diff options
author | Andrew F. Davis | 2013-10-25 08:29:39 -0500 |
---|---|---|
committer | Andrew F. Davis | 2018-04-21 19:27:07 -0500 |
commit | bb503f36fef8523ccd03b6d00c022a6d3a62111c (patch) | |
tree | 5abf8bdb38cd7b1016c52e02fc0474e931c98b81 | |
parent | e5bb2a46a4129192110f5fa8f6d77724a52c4537 (diff) | |
download | afd-analog-tlv320aic325x.tar.gz afd-analog-tlv320aic325x.tar.xz afd-analog-tlv320aic325x.zip |
ASoC: Add TLV320AIC3254 MiniDSP supporttlv320aic325x
Signed-off-by: Andrew F. Davis <afd@ti.com>
-rw-r--r-- | sound/soc/codecs/tlv320aic325x.c | 475 |
1 files changed, 471 insertions, 4 deletions
diff --git a/sound/soc/codecs/tlv320aic325x.c b/sound/soc/codecs/tlv320aic325x.c index 07e238bb6055..e2aed8f12a51 100644 --- a/sound/soc/codecs/tlv320aic325x.c +++ b/sound/soc/codecs/tlv320aic325x.c | |||
@@ -33,6 +33,9 @@ | |||
33 | #include <sound/tlv.h> | 33 | #include <sound/tlv.h> |
34 | #include <sound/jack.h> | 34 | #include <sound/jack.h> |
35 | 35 | ||
36 | #include "ti-minidsp/aic3xxx_cfw.h" | ||
37 | #include "ti-minidsp/aic3xxx_cfw_ops.h" | ||
38 | |||
36 | #include "tlv320aic325x.h" | 39 | #include "tlv320aic325x.h" |
37 | 40 | ||
38 | struct aic325x_priv { | 41 | struct aic325x_priv { |
@@ -40,6 +43,11 @@ struct aic325x_priv { | |||
40 | bool master; | 43 | bool master; |
41 | struct snd_soc_codec *codec; | 44 | struct snd_soc_codec *codec; |
42 | 45 | ||
46 | struct cfw_state cfw_ps; | ||
47 | struct firmware *cur_fw; | ||
48 | |||
49 | int dsp_runstate; | ||
50 | |||
43 | struct regmap *regmap; | 51 | struct regmap *regmap; |
44 | }; | 52 | }; |
45 | 53 | ||
@@ -201,6 +209,25 @@ static const struct snd_kcontrol_new adcl_mux = | |||
201 | static const struct snd_kcontrol_new adcr_mux = | 209 | static const struct snd_kcontrol_new adcr_mux = |
202 | SOC_DAPM_ENUM("Right ADC Route", adcr_enum); | 210 | SOC_DAPM_ENUM("Right ADC Route", adcr_enum); |
203 | 211 | ||
212 | static int aic3xxx_wait_bits(struct snd_soc_codec *codec, unsigned int reg, | ||
213 | unsigned char mask, unsigned char val) | ||
214 | { | ||
215 | unsigned int status; | ||
216 | unsigned int counter; | ||
217 | int timeout = AIC325x_TIME_DELAY * AIC325x_DELAY_COUNTER; | ||
218 | |||
219 | for (counter = 0; counter < AIC325x_DELAY_COUNTER; counter++) { | ||
220 | status = snd_soc_read(codec, reg); | ||
221 | if ((status & mask) == val) | ||
222 | return 0; | ||
223 | usleep_range(AIC325x_TIME_DELAY, AIC325x_TIME_DELAY + 100); | ||
224 | } | ||
225 | |||
226 | dev_err(codec->dev, "wait_bits timedout (%d microsecs). page %d reg %d lastval 0x%x\n", timeout, reg / 128, reg % 128, status); | ||
227 | |||
228 | return -1; | ||
229 | } | ||
230 | |||
204 | static int pll_power_on_event(struct snd_soc_dapm_widget *w, | 231 | static int pll_power_on_event(struct snd_soc_dapm_widget *w, |
205 | struct snd_kcontrol *kcontrol, int event) | 232 | struct snd_kcontrol *kcontrol, int event) |
206 | { | 233 | { |
@@ -209,6 +236,229 @@ static int pll_power_on_event(struct snd_soc_dapm_widget *w, | |||
209 | return 0; | 236 | return 0; |
210 | } | 237 | } |
211 | 238 | ||
239 | static int aic3256_get_runstate(struct snd_soc_codec *codec) | ||
240 | { | ||
241 | unsigned int dac, adc; | ||
242 | u8 state = 0; | ||
243 | |||
244 | dac = snd_soc_read(codec, AIC325x_DACFLAG1); | ||
245 | |||
246 | if (dac & BIT(7)) | ||
247 | state |= AIC3XXX_COPS_MDSP_D_L; | ||
248 | |||
249 | if (dac & BIT(3)) | ||
250 | state |= AIC3XXX_COPS_MDSP_D_R; | ||
251 | |||
252 | adc = snd_soc_read(codec, AIC325x_ADCFLAG); | ||
253 | |||
254 | if (adc & BIT(6)) | ||
255 | state |= AIC3XXX_COPS_MDSP_A_L; | ||
256 | |||
257 | if (adc & BIT(2)) | ||
258 | state |= AIC3XXX_COPS_MDSP_A_R; | ||
259 | |||
260 | return state; | ||
261 | } | ||
262 | |||
263 | static int aic3256_dsp_pwrdwn_status(struct snd_soc_codec *codec) | ||
264 | { | ||
265 | snd_soc_update_bits(codec, AIC325x_ADCSETUP, 0xc0, 0); | ||
266 | snd_soc_update_bits(codec, AIC325x_DACSETUP, 0xc0, 0); | ||
267 | |||
268 | if (aic3xxx_wait_bits(codec, AIC325x_ADCFLAG, AIC325x_ADC_POWER_MASK, 0)) | ||
269 | goto err; | ||
270 | |||
271 | if (aic3xxx_wait_bits(codec, AIC325x_DACFLAG1, AIC325x_DAC_POWER_MASK, 0)) | ||
272 | goto err; | ||
273 | |||
274 | return 0; | ||
275 | |||
276 | err: | ||
277 | dev_err(codec->dev, "DAC/ADC Power down timedout\n"); | ||
278 | |||
279 | return -EINVAL; | ||
280 | } | ||
281 | |||
282 | static int aic3256_dsp_pwrup(struct snd_soc_codec *codec, int state) | ||
283 | { | ||
284 | int adc_reg_mask = 0; | ||
285 | int adc_power = 0; | ||
286 | int dac_reg_mask = 0; | ||
287 | int dac_power = 0; | ||
288 | int ret_wbits; | ||
289 | |||
290 | if (state & AIC3XXX_COPS_MDSP_A_L) { | ||
291 | adc_reg_mask |= 0x80; | ||
292 | adc_power |= AIC325x_LADC_POWER; | ||
293 | } | ||
294 | |||
295 | if (state & AIC3XXX_COPS_MDSP_A_R) { | ||
296 | adc_reg_mask |= 0x40; | ||
297 | adc_power |= AIC325x_RADC_POWER; | ||
298 | } | ||
299 | |||
300 | if (state & AIC3XXX_COPS_MDSP_A) | ||
301 | snd_soc_update_bits(codec, AIC325x_ADCSETUP, 0xc0, adc_reg_mask); | ||
302 | |||
303 | if (state & AIC3XXX_COPS_MDSP_D_L) { | ||
304 | dac_reg_mask |= 0x80; | ||
305 | dac_power |= AIC325x_LDAC_POWER; | ||
306 | } | ||
307 | |||
308 | if (state & AIC3XXX_COPS_MDSP_D_R) { | ||
309 | dac_reg_mask |= 0x40; | ||
310 | dac_power |= AIC325x_RDAC_POWER; | ||
311 | } | ||
312 | |||
313 | if (state & AIC3XXX_COPS_MDSP_D) | ||
314 | snd_soc_update_bits(codec, AIC325x_DACSETUP, 0xc0, dac_reg_mask); | ||
315 | |||
316 | if (state & AIC3XXX_COPS_MDSP_A) { | ||
317 | ret_wbits = aic3xxx_wait_bits(codec, AIC325x_ADCFLAG, AIC325x_ADC_POWER_MASK, adc_power); | ||
318 | if (ret_wbits) | ||
319 | dev_err(codec->dev, "ADC Power down timedout\n"); | ||
320 | } | ||
321 | |||
322 | if (state & AIC3XXX_COPS_MDSP_D) { | ||
323 | ret_wbits = aic3xxx_wait_bits(codec, AIC325x_DACFLAG1, AIC325x_DAC_POWER_MASK, dac_power); | ||
324 | if (ret_wbits) | ||
325 | dev_err(codec->dev, "ADC Power down timedout\n"); | ||
326 | } | ||
327 | |||
328 | return 0; | ||
329 | } | ||
330 | |||
331 | static int aic3256_restart_dsps_sync(struct snd_soc_codec *codec, int run_state) | ||
332 | { | ||
333 | aic3256_dsp_pwrdwn_status(codec); | ||
334 | aic3256_dsp_pwrup(codec, run_state); | ||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | /** | ||
339 | *aic325x_dac_event: Headset popup reduction and powering up dsps together | ||
340 | * when they are in sync mode | ||
341 | * @w: pointer variable to dapm_widget | ||
342 | * @kcontrol: pointer to sound control | ||
343 | * @event: event element information | ||
344 | * | ||
345 | * Returns 0 for success. | ||
346 | */ | ||
347 | static int aic325x_dac_event(struct snd_soc_dapm_widget *w, | ||
348 | struct snd_kcontrol *kcontrol, int event) | ||
349 | { | ||
350 | int reg_mask = 0; | ||
351 | int ret_wbits = 0; | ||
352 | int run_state_mask; | ||
353 | int sync_needed = 0, non_sync_state = 0; | ||
354 | int other_dsp = 0, run_state = 0; | ||
355 | struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); | ||
356 | struct aic325x_priv *aic325x = snd_soc_codec_get_drvdata(codec); | ||
357 | |||
358 | printk("dac event *******\n"); | ||
359 | |||
360 | if (w->shift == 7) { | ||
361 | reg_mask = AIC325x_LDAC_POWER; | ||
362 | run_state_mask = AIC3XXX_COPS_MDSP_D_L; | ||
363 | } | ||
364 | if (w->shift == 6) { | ||
365 | reg_mask = AIC325x_RDAC_POWER; | ||
366 | run_state_mask = AIC3XXX_COPS_MDSP_D_R; | ||
367 | } | ||
368 | |||
369 | switch (event) { | ||
370 | case SND_SOC_DAPM_POST_PMU: | ||
371 | ret_wbits = aic3xxx_wait_bits(codec, AIC325x_DACFLAG1, reg_mask, reg_mask); | ||
372 | sync_needed = snd_soc_read(codec, AIC325x_DAC_PRB); | ||
373 | non_sync_state = !(aic325x->dsp_runstate & AIC3XXX_COPS_MDSP_ALL); | ||
374 | other_dsp = aic325x->dsp_runstate & AIC3XXX_COPS_MDSP_A; | ||
375 | if (sync_needed && non_sync_state && other_dsp) { | ||
376 | run_state = aic3256_get_runstate(aic325x->codec); | ||
377 | aic3256_dsp_pwrdwn_status(aic325x->codec); | ||
378 | aic3256_dsp_pwrup(aic325x->codec, run_state); | ||
379 | } | ||
380 | aic325x->dsp_runstate |= run_state_mask; | ||
381 | if (ret_wbits) { | ||
382 | dev_err(codec->dev, "DAC_post_pmu timed out\n"); | ||
383 | return -1; | ||
384 | } | ||
385 | break; | ||
386 | case SND_SOC_DAPM_POST_PMD: | ||
387 | ret_wbits = aic3xxx_wait_bits(codec, AIC325x_DACFLAG1, reg_mask, 0); | ||
388 | aic325x->dsp_runstate = (aic325x->dsp_runstate & ~run_state_mask); | ||
389 | if (ret_wbits) { | ||
390 | dev_err(codec->dev, "DAC_post_pmd timed out\n"); | ||
391 | return -1; | ||
392 | } | ||
393 | break; | ||
394 | default: | ||
395 | BUG(); | ||
396 | return -EINVAL; | ||
397 | } | ||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | /** | ||
402 | * aic325x_adc_event: To get DSP run state to perform synchronization | ||
403 | * @w: pointer variable to dapm_widget | ||
404 | * @kcontrol: pointer to sound control | ||
405 | * @event: event element information | ||
406 | * | ||
407 | * Returns 0 for success. | ||
408 | */ | ||
409 | static int aic325x_adc_event(struct snd_soc_dapm_widget *w, | ||
410 | struct snd_kcontrol *kcontrol, int event) | ||
411 | { | ||
412 | int run_state = 0; | ||
413 | int non_sync_state = 0, sync_needed = 0; | ||
414 | int other_dsp = 0; | ||
415 | int run_state_mask = 0; | ||
416 | struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); | ||
417 | struct aic325x_priv *aic3256 = snd_soc_codec_get_drvdata(codec); | ||
418 | int reg_mask = 0; | ||
419 | int ret_wbits = 0; | ||
420 | |||
421 | if (w->shift == 7) { | ||
422 | reg_mask = AIC325x_LADC_POWER; | ||
423 | run_state_mask = AIC3XXX_COPS_MDSP_A_L; | ||
424 | } | ||
425 | if (w->shift == 6) { | ||
426 | reg_mask = AIC325x_RADC_POWER; | ||
427 | run_state_mask = AIC3XXX_COPS_MDSP_A_R; | ||
428 | } | ||
429 | |||
430 | switch (event) { | ||
431 | case SND_SOC_DAPM_POST_PMU: | ||
432 | ret_wbits = aic3xxx_wait_bits(codec, AIC325x_ADCFLAG , reg_mask, reg_mask); | ||
433 | sync_needed = snd_soc_read(codec, AIC325x_DAC_PRB); | ||
434 | non_sync_state = !(aic3256->dsp_runstate & AIC3XXX_COPS_MDSP_ALL); | ||
435 | other_dsp = aic3256->dsp_runstate & AIC3XXX_COPS_MDSP_D; | ||
436 | if (sync_needed && non_sync_state && other_dsp) { | ||
437 | run_state = aic3256_get_runstate(aic3256->codec); | ||
438 | aic3256_dsp_pwrdwn_status(aic3256->codec); | ||
439 | aic3256_dsp_pwrup(aic3256->codec, run_state); | ||
440 | } | ||
441 | aic3256->dsp_runstate |= run_state_mask; | ||
442 | if (ret_wbits) { | ||
443 | dev_err(codec->dev, "ADC POST_PMU timedout\n"); | ||
444 | return -1; | ||
445 | } | ||
446 | break; | ||
447 | case SND_SOC_DAPM_POST_PMD: | ||
448 | ret_wbits = aic3xxx_wait_bits(codec, AIC325x_ADCFLAG, reg_mask, 0); | ||
449 | aic3256->dsp_runstate = (aic3256->dsp_runstate & ~run_state_mask); | ||
450 | if (ret_wbits) { | ||
451 | dev_err(codec->dev, "ADC POST_PMD timedout\n"); | ||
452 | return -1; | ||
453 | } | ||
454 | break; | ||
455 | default: | ||
456 | BUG(); | ||
457 | return -EINVAL; | ||
458 | } | ||
459 | return 0; | ||
460 | } | ||
461 | |||
212 | static const struct snd_soc_dapm_widget aic325x_dapm_widgets[] = { | 462 | static const struct snd_soc_dapm_widget aic325x_dapm_widgets[] = { |
213 | SND_SOC_DAPM_AIF_IN("ASIIN", "ASI Playback", 0, SND_SOC_NOPM, 0, 0), | 463 | SND_SOC_DAPM_AIF_IN("ASIIN", "ASI Playback", 0, SND_SOC_NOPM, 0, 0), |
214 | SND_SOC_DAPM_PGA("ASILIN", SND_SOC_NOPM, 0, 0, NULL, 0), | 464 | SND_SOC_DAPM_PGA("ASILIN", SND_SOC_NOPM, 0, 0, NULL, 0), |
@@ -217,8 +467,8 @@ static const struct snd_soc_dapm_widget aic325x_dapm_widgets[] = { | |||
217 | SND_SOC_DAPM_MUX("ASIIn Left Route", SND_SOC_NOPM, 0, 0, &asilin_control), | 467 | SND_SOC_DAPM_MUX("ASIIn Left Route", SND_SOC_NOPM, 0, 0, &asilin_control), |
218 | SND_SOC_DAPM_MUX("ASIIn Right Route", SND_SOC_NOPM, 0, 0, &asirin_control), | 468 | SND_SOC_DAPM_MUX("ASIIn Right Route", SND_SOC_NOPM, 0, 0, &asirin_control), |
219 | SND_SOC_DAPM_PGA("ASI IN Port", SND_SOC_NOPM, 0, 0, NULL, 0), | 469 | SND_SOC_DAPM_PGA("ASI IN Port", SND_SOC_NOPM, 0, 0, NULL, 0), |
220 | SND_SOC_DAPM_DAC("Left DAC", NULL, AIC325x_DACSETUP, 7, 0), | 470 | SND_SOC_DAPM_DAC_E("Left DAC", NULL, AIC325x_DACSETUP, 7, 0, aic325x_dac_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), |
221 | SND_SOC_DAPM_DAC("Right DAC", NULL, AIC325x_DACSETUP, 6, 0), | 471 | SND_SOC_DAPM_DAC_E("Right DAC", NULL, AIC325x_DACSETUP, 6, 0, aic325x_dac_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), |
222 | 472 | ||
223 | SND_SOC_DAPM_MIXER("HPL Output Mixer", SND_SOC_NOPM, 0, 0, hpl_output_mixer_controls, ARRAY_SIZE(hpl_output_mixer_controls)), | 473 | SND_SOC_DAPM_MIXER("HPL Output Mixer", SND_SOC_NOPM, 0, 0, hpl_output_mixer_controls, ARRAY_SIZE(hpl_output_mixer_controls)), |
224 | SND_SOC_DAPM_MIXER("HPR Output Mixer", SND_SOC_NOPM, 0, 0, hpr_output_mixer_controls, ARRAY_SIZE(hpr_output_mixer_controls)), | 474 | SND_SOC_DAPM_MIXER("HPR Output Mixer", SND_SOC_NOPM, 0, 0, hpr_output_mixer_controls, ARRAY_SIZE(hpr_output_mixer_controls)), |
@@ -271,8 +521,8 @@ static const struct snd_soc_dapm_widget aic325x_dapm_widgets[] = { | |||
271 | SND_SOC_DAPM_MUX("Left ADC Route", SND_SOC_NOPM, 0, 0, &adcl_mux), | 521 | SND_SOC_DAPM_MUX("Left ADC Route", SND_SOC_NOPM, 0, 0, &adcl_mux), |
272 | SND_SOC_DAPM_MUX("Right ADC Route", SND_SOC_NOPM, 0, 0, &adcr_mux), | 522 | SND_SOC_DAPM_MUX("Right ADC Route", SND_SOC_NOPM, 0, 0, &adcr_mux), |
273 | 523 | ||
274 | SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC325x_ADCSETUP, 7, 0), | 524 | SND_SOC_DAPM_ADC_E("Left ADC", "Left Capture", AIC325x_ADCSETUP, 7, 0, aic325x_adc_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), |
275 | SND_SOC_DAPM_ADC("Right ADC", "Right Capture", AIC325x_ADCSETUP, 6, 0), | 525 | SND_SOC_DAPM_ADC_E("Right ADC", "Right Capture", AIC325x_ADCSETUP, 6, 0, aic325x_adc_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), |
276 | 526 | ||
277 | SND_SOC_DAPM_MICBIAS("Mic Bias", AIC325x_MICBIAS_CTRL, 6, 0), | 527 | SND_SOC_DAPM_MICBIAS("Mic Bias", AIC325x_MICBIAS_CTRL, 6, 0), |
278 | 528 | ||
@@ -405,6 +655,160 @@ static const struct snd_soc_dapm_route aic325x_dapm_routes[] = { | |||
405 | {"Right ADC", NULL, "MADC_DIV"}, | 655 | {"Right ADC", NULL, "MADC_DIV"}, |
406 | }; | 656 | }; |
407 | 657 | ||
658 | /** | ||
659 | * Methods for CFW Operations | ||
660 | * | ||
661 | * Due to incompatibilities between structures used by regmap and CFW | ||
662 | * we need to transform the register format before linking to | ||
663 | * CFW operations. | ||
664 | */ | ||
665 | static unsigned int aic3256_ops_cfw2reg(unsigned int reg) | ||
666 | { | ||
667 | union cfw_register *c = (union cfw_register *)® | ||
668 | |||
669 | if (c->book) | ||
670 | pr_warn("Book not 0\n"); | ||
671 | |||
672 | return AIC325x_REG(c->page, c->offset); | ||
673 | } | ||
674 | |||
675 | static int aic3256_ops_reg_read(struct snd_soc_codec *codec, unsigned int reg) | ||
676 | { | ||
677 | unsigned int val = snd_soc_read(codec, aic3256_ops_cfw2reg(reg)); | ||
678 | |||
679 | printk(" ** read page %d, reg %d: 0x%08x\n", aic3256_ops_cfw2reg(reg) / 128, aic3256_ops_cfw2reg(reg) % 128, val); | ||
680 | |||
681 | return snd_soc_read(codec, aic3256_ops_cfw2reg(reg)); | ||
682 | } | ||
683 | |||
684 | static int aic3256_ops_reg_write(struct snd_soc_codec *codec, unsigned int reg, | ||
685 | unsigned char val) | ||
686 | { | ||
687 | printk(" ** write page %d, reg %d: 0x%08x\n", aic3256_ops_cfw2reg(reg) / 128, aic3256_ops_cfw2reg(reg) % 128, val); | ||
688 | |||
689 | return snd_soc_write(codec, aic3256_ops_cfw2reg(reg), val); | ||
690 | } | ||
691 | |||
692 | static int aic3256_ops_set_bits(struct snd_soc_codec *codec, unsigned int reg, | ||
693 | unsigned char mask, unsigned char val) | ||
694 | { | ||
695 | printk(" ** setbits page %d, reg %d: 0x%08x 0x%08x\n", aic3256_ops_cfw2reg(reg) / 128, aic3256_ops_cfw2reg(reg) % 128, mask, val); | ||
696 | |||
697 | return snd_soc_update_bits(codec, aic3256_ops_cfw2reg(reg), mask, val); | ||
698 | } | ||
699 | |||
700 | static int aic3256_ops_bulk_read(struct snd_soc_codec *codec, unsigned int reg, | ||
701 | int count, u8 *buf) | ||
702 | { | ||
703 | struct aic325x_priv *aic325x = snd_soc_codec_get_drvdata(codec); | ||
704 | |||
705 | printk(" ** bulk read page %d, reg %d: %d count\n", aic3256_ops_cfw2reg(reg) / 128, aic3256_ops_cfw2reg(reg) % 128, count); | ||
706 | |||
707 | return regmap_bulk_read(aic325x->regmap, aic3256_ops_cfw2reg(reg), buf, count); | ||
708 | } | ||
709 | |||
710 | static int aic3256_ops_bulk_write(struct snd_soc_codec *codec, unsigned int reg, | ||
711 | int count, const u8 *buf) | ||
712 | { | ||
713 | struct aic325x_priv *aic325x = snd_soc_codec_get_drvdata(codec); | ||
714 | |||
715 | printk(" ** bulk write page %d, reg %d: %d count\n", aic3256_ops_cfw2reg(reg) / 128, aic3256_ops_cfw2reg(reg) % 128, count); | ||
716 | |||
717 | return regmap_bulk_write(aic325x->regmap, aic3256_ops_cfw2reg(reg), buf, count); | ||
718 | } | ||
719 | |||
720 | int aic3256_ops_lock(struct snd_soc_codec *codec) | ||
721 | { | ||
722 | // mutex_lock(&codec->mutex); | ||
723 | /* Reading the run state of adc and dac */ | ||
724 | return aic3256_get_runstate(codec); | ||
725 | } | ||
726 | |||
727 | int aic3256_ops_unlock(struct snd_soc_codec *codec) | ||
728 | { | ||
729 | /*Releasing the lock of mutex */ | ||
730 | // mutex_unlock(&codec->mutex); | ||
731 | return 0; | ||
732 | } | ||
733 | |||
734 | int aic3256_ops_stop(struct snd_soc_codec *codec, int mask) | ||
735 | { | ||
736 | int run_state = 0; | ||
737 | |||
738 | run_state = aic3256_get_runstate(codec); | ||
739 | |||
740 | if (mask & AIC3XXX_COPS_MDSP_A) /* power-down ADCs */ | ||
741 | snd_soc_update_bits(codec, AIC325x_ADCSETUP, 0xc0, 0); | ||
742 | |||
743 | if (mask & AIC3XXX_COPS_MDSP_D) /* power-down DACs */ | ||
744 | snd_soc_update_bits(codec, AIC325x_DACSETUP, 0xc0, 0); | ||
745 | |||
746 | if ((mask & AIC3XXX_COPS_MDSP_A) && aic3xxx_wait_bits(codec, AIC325x_ADCFLAG, AIC325x_ADC_POWER_MASK, 0)) | ||
747 | goto err; | ||
748 | |||
749 | if ((mask & AIC3XXX_COPS_MDSP_D) && aic3xxx_wait_bits(codec, AIC325x_DACFLAG1, AIC325x_DAC_POWER_MASK, 0)) | ||
750 | goto err; | ||
751 | |||
752 | return run_state; | ||
753 | |||
754 | err: | ||
755 | dev_err(codec->dev, "Unable to turn off ADCs or DACs\n"); | ||
756 | return -EINVAL; | ||
757 | } | ||
758 | |||
759 | static int aic3256_ops_restore(struct snd_soc_codec *codec, int run_state) | ||
760 | { | ||
761 | int sync_state; | ||
762 | |||
763 | /* This is for read the sync mode register state */ | ||
764 | sync_state = snd_soc_read(codec, AIC325x_DAC_PRB); | ||
765 | /* checking whether the sync mode has been set or not and checking the current state */ | ||
766 | if ((run_state & AIC3XXX_COPS_MDSP_ALL) && (sync_state & 0x80)) | ||
767 | aic3256_restart_dsps_sync(codec, run_state); | ||
768 | else | ||
769 | aic3256_dsp_pwrup(codec, run_state); | ||
770 | |||
771 | return 0; | ||
772 | } | ||
773 | |||
774 | static const int sbuf[][2] = { | ||
775 | { AIC3XXX_ABUF_MDSP_A, AIC325x_ADC_ADAPTIVE_CRAM_REG }, | ||
776 | { AIC3XXX_ABUF_MDSP_D1, AIC325x_DAC_ADAPTIVE_CRAM_REG }, | ||
777 | /* { AIC3XXX_ABUF_MDSP_D2, AIC325x_DAC_ADAPTIVE_BANK2_REG }, */ | ||
778 | }; | ||
779 | |||
780 | int aic3256_ops_adaptivebuffer_swap(struct snd_soc_codec *codec, int mask) | ||
781 | { | ||
782 | int i; | ||
783 | |||
784 | for (i = 0; i < ARRAY_SIZE(sbuf); i++) { | ||
785 | if (!(mask & sbuf[i][0])) | ||
786 | continue; | ||
787 | snd_soc_update_bits(codec, sbuf[i][1], 0x1, 0x1); | ||
788 | if (aic3xxx_wait_bits(codec, sbuf[i][1], 0x1, 0)) | ||
789 | goto err; | ||
790 | } | ||
791 | |||
792 | return 0; | ||
793 | |||
794 | err: | ||
795 | dev_err(codec->dev, "miniDSP buffer swap failure\n"); | ||
796 | return -EINVAL; | ||
797 | } | ||
798 | |||
799 | static const struct aic3xxx_codec_ops aic3256_cfw_codec_ops = { | ||
800 | .reg_read = aic3256_ops_reg_read, | ||
801 | .reg_write = aic3256_ops_reg_write, | ||
802 | .set_bits = aic3256_ops_set_bits, | ||
803 | .bulk_read = aic3256_ops_bulk_read, | ||
804 | .bulk_write = aic3256_ops_bulk_write, | ||
805 | .lock = aic3256_ops_lock, | ||
806 | .unlock = aic3256_ops_unlock, | ||
807 | .stop = aic3256_ops_stop, | ||
808 | .restore = aic3256_ops_restore, | ||
809 | .bswap = aic3256_ops_adaptivebuffer_swap, | ||
810 | }; | ||
811 | |||
408 | struct AIC325x_rate_divs { | 812 | struct AIC325x_rate_divs { |
409 | u32 mclk; | 813 | u32 mclk; |
410 | u32 rate; | 814 | u32 rate; |
@@ -653,6 +1057,15 @@ static int aic325x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) | |||
653 | return 0; | 1057 | return 0; |
654 | } | 1058 | } |
655 | 1059 | ||
1060 | static int aic325x_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, | ||
1061 | unsigned int freq_in, unsigned int freq_out) | ||
1062 | { | ||
1063 | struct snd_soc_codec *codec = dai->codec; | ||
1064 | struct aic325x_priv *aic325x = snd_soc_codec_get_drvdata(codec); | ||
1065 | |||
1066 | return aic3xxx_cfw_set_pll(&aic325x->cfw_ps, dai->id); | ||
1067 | } | ||
1068 | |||
656 | static int aic325x_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) | 1069 | static int aic325x_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) |
657 | { | 1070 | { |
658 | // struct aic325x_priv *aic325x = snd_soc_codec_get_drvdata(codec); | 1071 | // struct aic325x_priv *aic325x = snd_soc_codec_get_drvdata(codec); |
@@ -680,6 +1093,7 @@ struct snd_soc_dai_ops aic325x_dai_ops = { | |||
680 | .hw_params = aic325x_hw_params, | 1093 | .hw_params = aic325x_hw_params, |
681 | .set_sysclk = aic325x_set_dai_sysclk, | 1094 | .set_sysclk = aic325x_set_dai_sysclk, |
682 | .set_fmt = aic325x_set_dai_fmt, | 1095 | .set_fmt = aic325x_set_dai_fmt, |
1096 | .set_pll = aic325x_dai_set_pll, | ||
683 | }; | 1097 | }; |
684 | 1098 | ||
685 | static struct snd_soc_dai_driver tlv320aic325x_dai_driver[] = { | 1099 | static struct snd_soc_dai_driver tlv320aic325x_dai_driver[] = { |
@@ -703,6 +1117,41 @@ static struct snd_soc_dai_driver tlv320aic325x_dai_driver[] = { | |||
703 | }, | 1117 | }, |
704 | }; | 1118 | }; |
705 | 1119 | ||
1120 | static void aic3256_firmware_load(const struct firmware *fw, void *context) | ||
1121 | { | ||
1122 | struct snd_soc_codec *codec = context; | ||
1123 | struct aic325x_priv *aic325x = snd_soc_codec_get_drvdata(codec); | ||
1124 | int ret = 0; | ||
1125 | |||
1126 | if (aic325x->cur_fw) { | ||
1127 | release_firmware(aic325x->cur_fw); | ||
1128 | aic325x->cur_fw = NULL; | ||
1129 | } | ||
1130 | |||
1131 | if (!fw) { | ||
1132 | dev_err(codec->dev, "Codec Firmware failed\n"); | ||
1133 | return; | ||
1134 | } | ||
1135 | |||
1136 | dev_dbg(codec->dev, "Firmware binary load\n"); | ||
1137 | aic325x->cur_fw = (void *)fw; | ||
1138 | ret = aic3xxx_cfw_load(&aic325x->cfw_ps, (void *)fw->data, fw->size); | ||
1139 | if (ret < 0) { | ||
1140 | dev_err(codec->dev, "Firmware binary load failed\n"); | ||
1141 | release_firmware(aic325x->cur_fw); | ||
1142 | aic325x->cur_fw = NULL; | ||
1143 | fw = NULL; | ||
1144 | } | ||
1145 | |||
1146 | if (ret >= 0) { | ||
1147 | /* init function for transition */ | ||
1148 | aic3xxx_cfw_transition(&aic325x->cfw_ps, "INIT"); | ||
1149 | aic3xxx_cfw_add_modes(codec, &aic325x->cfw_ps); | ||
1150 | aic3xxx_cfw_add_controls(codec, &aic325x->cfw_ps); | ||
1151 | aic3xxx_cfw_setmode_cfg(&aic325x->cfw_ps, 0, 0); | ||
1152 | } | ||
1153 | } | ||
1154 | |||
706 | static int aic325x_codec_probe(struct snd_soc_codec *codec) | 1155 | static int aic325x_codec_probe(struct snd_soc_codec *codec) |
707 | { | 1156 | { |
708 | struct aic325x_priv *aic325x = snd_soc_codec_get_drvdata(codec); | 1157 | struct aic325x_priv *aic325x = snd_soc_codec_get_drvdata(codec); |
@@ -711,10 +1160,24 @@ static int aic325x_codec_probe(struct snd_soc_codec *codec) | |||
711 | 1160 | ||
712 | aic325x->codec = codec; | 1161 | aic325x->codec = codec; |
713 | 1162 | ||
1163 | aic325x->cur_fw = NULL; | ||
1164 | aic3xxx_cfw_init(aic325x->codec, &aic325x->cfw_ps, &aic3256_cfw_codec_ops); | ||
1165 | aic325x->dsp_runstate = 0; | ||
1166 | |||
714 | aic325x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 1167 | aic325x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
715 | 1168 | ||
716 | snd_soc_write(codec, AIC325x_LDO_CTRL, 0x01); /* FIXME: make conditional on regulator */ | 1169 | snd_soc_write(codec, AIC325x_LDO_CTRL, 0x01); /* FIXME: make conditional on regulator */ |
717 | 1170 | ||
1171 | /* firmware load */ | ||
1172 | ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, | ||
1173 | "tlv320aic3254_fw_v1.bin", | ||
1174 | codec->dev, GFP_KERNEL, codec, | ||
1175 | aic3256_firmware_load); | ||
1176 | if (ret < 0) { | ||
1177 | dev_err(codec->dev, "Firmware request failed\n"); | ||
1178 | return ret; | ||
1179 | } | ||
1180 | |||
718 | return 0; | 1181 | return 0; |
719 | } | 1182 | } |
720 | 1183 | ||
@@ -725,6 +1188,10 @@ static int aic325x_codec_remove(struct snd_soc_codec *codec) | |||
725 | 1188 | ||
726 | aic325x_set_bias_level(codec, SND_SOC_BIAS_OFF); | 1189 | aic325x_set_bias_level(codec, SND_SOC_BIAS_OFF); |
727 | 1190 | ||
1191 | /* Release firmware if any */ | ||
1192 | if (aic325x->cur_fw) | ||
1193 | release_firmware(aic325x->cur_fw); | ||
1194 | |||
728 | return 0; | 1195 | return 0; |
729 | } | 1196 | } |
730 | 1197 | ||