diff options
Diffstat (limited to 'sound/soc/codecs/cs42l51.c')
-rw-r--r-- | sound/soc/codecs/cs42l51.c | 295 |
1 files changed, 93 insertions, 202 deletions
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index dd9b8550c402..cb086eaf4e07 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c | |||
@@ -42,15 +42,14 @@ enum master_slave_mode { | |||
42 | }; | 42 | }; |
43 | 43 | ||
44 | struct cs42l51_private { | 44 | struct cs42l51_private { |
45 | enum snd_soc_control_type control_type; | ||
46 | void *control_data; | ||
45 | unsigned int mclk; | 47 | unsigned int mclk; |
46 | unsigned int audio_mode; /* The mode (I2S or left-justified) */ | 48 | unsigned int audio_mode; /* The mode (I2S or left-justified) */ |
47 | enum master_slave_mode func; | 49 | enum master_slave_mode func; |
48 | struct snd_soc_codec codec; | ||
49 | u8 reg_cache[CS42L51_NUMREGS]; | 50 | u8 reg_cache[CS42L51_NUMREGS]; |
50 | }; | 51 | }; |
51 | 52 | ||
52 | static struct snd_soc_codec *cs42l51_codec; | ||
53 | |||
54 | #define CS42L51_FORMATS ( \ | 53 | #define CS42L51_FORMATS ( \ |
55 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ | 54 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ |
56 | SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \ | 55 | SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \ |
@@ -75,134 +74,6 @@ static int cs42l51_fill_cache(struct snd_soc_codec *codec) | |||
75 | return 0; | 74 | return 0; |
76 | } | 75 | } |
77 | 76 | ||
78 | static int cs42l51_i2c_probe(struct i2c_client *i2c_client, | ||
79 | const struct i2c_device_id *id) | ||
80 | { | ||
81 | struct snd_soc_codec *codec; | ||
82 | struct cs42l51_private *cs42l51; | ||
83 | int ret = 0; | ||
84 | int reg; | ||
85 | |||
86 | if (cs42l51_codec) | ||
87 | return -EBUSY; | ||
88 | |||
89 | /* Verify that we have a CS42L51 */ | ||
90 | ret = i2c_smbus_read_byte_data(i2c_client, CS42L51_CHIP_REV_ID); | ||
91 | if (ret < 0) { | ||
92 | dev_err(&i2c_client->dev, "failed to read I2C\n"); | ||
93 | goto error; | ||
94 | } | ||
95 | |||
96 | if ((ret != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) && | ||
97 | (ret != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) { | ||
98 | dev_err(&i2c_client->dev, "Invalid chip id\n"); | ||
99 | ret = -ENODEV; | ||
100 | goto error; | ||
101 | } | ||
102 | |||
103 | dev_info(&i2c_client->dev, "found device cs42l51 rev %d\n", | ||
104 | ret & 7); | ||
105 | |||
106 | cs42l51 = kzalloc(sizeof(struct cs42l51_private), GFP_KERNEL); | ||
107 | if (!cs42l51) { | ||
108 | dev_err(&i2c_client->dev, "could not allocate codec\n"); | ||
109 | return -ENOMEM; | ||
110 | } | ||
111 | codec = &cs42l51->codec; | ||
112 | |||
113 | mutex_init(&codec->mutex); | ||
114 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
115 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
116 | |||
117 | codec->dev = &i2c_client->dev; | ||
118 | codec->name = "CS42L51"; | ||
119 | codec->owner = THIS_MODULE; | ||
120 | codec->dai = &cs42l51_dai; | ||
121 | codec->num_dai = 1; | ||
122 | snd_soc_codec_set_drvdata(codec, cs42l51); | ||
123 | |||
124 | codec->control_data = i2c_client; | ||
125 | codec->reg_cache = cs42l51->reg_cache; | ||
126 | codec->reg_cache_size = CS42L51_NUMREGS; | ||
127 | i2c_set_clientdata(i2c_client, codec); | ||
128 | |||
129 | ret = cs42l51_fill_cache(codec); | ||
130 | if (ret < 0) { | ||
131 | dev_err(&i2c_client->dev, "failed to fill register cache\n"); | ||
132 | goto error_alloc; | ||
133 | } | ||
134 | |||
135 | ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C); | ||
136 | if (ret < 0) { | ||
137 | dev_err(&i2c_client->dev, "Failed to set cache I/O: %d\n", ret); | ||
138 | goto error_alloc; | ||
139 | } | ||
140 | |||
141 | /* | ||
142 | * DAC configuration | ||
143 | * - Use signal processor | ||
144 | * - auto mute | ||
145 | * - vol changes immediate | ||
146 | * - no de-emphasize | ||
147 | */ | ||
148 | reg = CS42L51_DAC_CTL_DATA_SEL(1) | ||
149 | | CS42L51_DAC_CTL_AMUTE | CS42L51_DAC_CTL_DACSZ(0); | ||
150 | ret = snd_soc_write(codec, CS42L51_DAC_CTL, reg); | ||
151 | if (ret < 0) | ||
152 | goto error_alloc; | ||
153 | |||
154 | cs42l51_dai.dev = codec->dev; | ||
155 | cs42l51_codec = codec; | ||
156 | |||
157 | ret = snd_soc_register_codec(codec); | ||
158 | if (ret != 0) { | ||
159 | dev_err(codec->dev, "Failed to register codec: %d\n", ret); | ||
160 | goto error_alloc; | ||
161 | } | ||
162 | |||
163 | ret = snd_soc_register_dai(&cs42l51_dai); | ||
164 | if (ret < 0) { | ||
165 | dev_err(&i2c_client->dev, "failed to register DAIe\n"); | ||
166 | goto error_reg; | ||
167 | } | ||
168 | |||
169 | return 0; | ||
170 | |||
171 | error_reg: | ||
172 | snd_soc_unregister_codec(codec); | ||
173 | error_alloc: | ||
174 | kfree(cs42l51); | ||
175 | error: | ||
176 | return ret; | ||
177 | } | ||
178 | |||
179 | static int cs42l51_i2c_remove(struct i2c_client *client) | ||
180 | { | ||
181 | struct cs42l51_private *cs42l51 = i2c_get_clientdata(client); | ||
182 | snd_soc_unregister_dai(&cs42l51_dai); | ||
183 | snd_soc_unregister_codec(cs42l51_codec); | ||
184 | cs42l51_codec = NULL; | ||
185 | kfree(cs42l51); | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | |||
190 | static const struct i2c_device_id cs42l51_id[] = { | ||
191 | {"cs42l51", 0}, | ||
192 | {} | ||
193 | }; | ||
194 | MODULE_DEVICE_TABLE(i2c, cs42l51_id); | ||
195 | |||
196 | static struct i2c_driver cs42l51_i2c_driver = { | ||
197 | .driver = { | ||
198 | .name = "CS42L51 I2C", | ||
199 | .owner = THIS_MODULE, | ||
200 | }, | ||
201 | .id_table = cs42l51_id, | ||
202 | .probe = cs42l51_i2c_probe, | ||
203 | .remove = cs42l51_i2c_remove, | ||
204 | }; | ||
205 | |||
206 | static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol, | 77 | static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol, |
207 | struct snd_ctl_elem_value *ucontrol) | 78 | struct snd_ctl_elem_value *ucontrol) |
208 | { | 79 | { |
@@ -484,51 +355,8 @@ static int cs42l51_set_dai_sysclk(struct snd_soc_dai *codec_dai, | |||
484 | { | 355 | { |
485 | struct snd_soc_codec *codec = codec_dai->codec; | 356 | struct snd_soc_codec *codec = codec_dai->codec; |
486 | struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec); | 357 | struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec); |
487 | struct cs42l51_ratios *ratios = NULL; | ||
488 | int nr_ratios = 0; | ||
489 | unsigned int rates = 0; | ||
490 | unsigned int rate_min = -1; | ||
491 | unsigned int rate_max = 0; | ||
492 | int i; | ||
493 | 358 | ||
494 | cs42l51->mclk = freq; | 359 | cs42l51->mclk = freq; |
495 | |||
496 | switch (cs42l51->func) { | ||
497 | case MODE_MASTER: | ||
498 | return -EINVAL; | ||
499 | case MODE_SLAVE: | ||
500 | ratios = slave_ratios; | ||
501 | nr_ratios = ARRAY_SIZE(slave_ratios); | ||
502 | break; | ||
503 | case MODE_SLAVE_AUTO: | ||
504 | ratios = slave_auto_ratios; | ||
505 | nr_ratios = ARRAY_SIZE(slave_auto_ratios); | ||
506 | break; | ||
507 | } | ||
508 | |||
509 | for (i = 0; i < nr_ratios; i++) { | ||
510 | unsigned int rate = freq / ratios[i].ratio; | ||
511 | rates |= snd_pcm_rate_to_rate_bit(rate); | ||
512 | if (rate < rate_min) | ||
513 | rate_min = rate; | ||
514 | if (rate > rate_max) | ||
515 | rate_max = rate; | ||
516 | } | ||
517 | rates &= ~SNDRV_PCM_RATE_KNOT; | ||
518 | |||
519 | if (!rates) { | ||
520 | dev_err(codec->dev, "could not find a valid sample rate\n"); | ||
521 | return -EINVAL; | ||
522 | } | ||
523 | |||
524 | codec_dai->playback.rates = rates; | ||
525 | codec_dai->playback.rate_min = rate_min; | ||
526 | codec_dai->playback.rate_max = rate_max; | ||
527 | |||
528 | codec_dai->capture.rates = rates; | ||
529 | codec_dai->capture.rate_min = rate_min; | ||
530 | codec_dai->capture.rate_max = rate_max; | ||
531 | |||
532 | return 0; | 360 | return 0; |
533 | } | 361 | } |
534 | 362 | ||
@@ -537,8 +365,7 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream, | |||
537 | struct snd_soc_dai *dai) | 365 | struct snd_soc_dai *dai) |
538 | { | 366 | { |
539 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 367 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
540 | struct snd_soc_device *socdev = rtd->socdev; | 368 | struct snd_soc_codec *codec = rtd->codec; |
541 | struct snd_soc_codec *codec = socdev->card->codec; | ||
542 | struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec); | 369 | struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec); |
543 | int ret; | 370 | int ret; |
544 | unsigned int i; | 371 | unsigned int i; |
@@ -670,8 +497,8 @@ static struct snd_soc_dai_ops cs42l51_dai_ops = { | |||
670 | .digital_mute = cs42l51_dai_mute, | 497 | .digital_mute = cs42l51_dai_mute, |
671 | }; | 498 | }; |
672 | 499 | ||
673 | struct snd_soc_dai cs42l51_dai = { | 500 | static struct snd_soc_dai_driver cs42l51_dai = { |
674 | .name = "CS42L51 HiFi", | 501 | .name = "cs42l51-hifi", |
675 | .playback = { | 502 | .playback = { |
676 | .stream_name = "Playback", | 503 | .stream_name = "Playback", |
677 | .channels_min = 1, | 504 | .channels_min = 1, |
@@ -688,30 +515,39 @@ struct snd_soc_dai cs42l51_dai = { | |||
688 | }, | 515 | }, |
689 | .ops = &cs42l51_dai_ops, | 516 | .ops = &cs42l51_dai_ops, |
690 | }; | 517 | }; |
691 | EXPORT_SYMBOL_GPL(cs42l51_dai); | ||
692 | |||
693 | 518 | ||
694 | static int cs42l51_probe(struct platform_device *pdev) | 519 | static int cs42l51_probe(struct snd_soc_codec *codec) |
695 | { | 520 | { |
696 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 521 | struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec); |
697 | struct snd_soc_codec *codec; | 522 | int ret, reg; |
698 | int ret = 0; | ||
699 | 523 | ||
700 | if (!cs42l51_codec) { | 524 | codec->control_data = cs42l51->control_data; |
701 | dev_err(&pdev->dev, "CS42L51 codec not yet registered\n"); | ||
702 | return -EINVAL; | ||
703 | } | ||
704 | 525 | ||
705 | socdev->card->codec = cs42l51_codec; | 526 | ret = cs42l51_fill_cache(codec); |
706 | codec = socdev->card->codec; | 527 | if (ret < 0) { |
528 | dev_err(codec->dev, "failed to fill register cache\n"); | ||
529 | return ret; | ||
530 | } | ||
707 | 531 | ||
708 | /* Register PCMs */ | 532 | ret = snd_soc_codec_set_cache_io(codec, 8, 8, cs42l51->control_type); |
709 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
710 | if (ret < 0) { | 533 | if (ret < 0) { |
711 | dev_err(&pdev->dev, "failed to create PCMs\n"); | 534 | dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); |
712 | return ret; | 535 | return ret; |
713 | } | 536 | } |
714 | 537 | ||
538 | /* | ||
539 | * DAC configuration | ||
540 | * - Use signal processor | ||
541 | * - auto mute | ||
542 | * - vol changes immediate | ||
543 | * - no de-emphasize | ||
544 | */ | ||
545 | reg = CS42L51_DAC_CTL_DATA_SEL(1) | ||
546 | | CS42L51_DAC_CTL_AMUTE | CS42L51_DAC_CTL_DACSZ(0); | ||
547 | ret = snd_soc_write(codec, CS42L51_DAC_CTL, reg); | ||
548 | if (ret < 0) | ||
549 | return ret; | ||
550 | |||
715 | snd_soc_add_controls(codec, cs42l51_snd_controls, | 551 | snd_soc_add_controls(codec, cs42l51_snd_controls, |
716 | ARRAY_SIZE(cs42l51_snd_controls)); | 552 | ARRAY_SIZE(cs42l51_snd_controls)); |
717 | snd_soc_dapm_new_controls(codec, cs42l51_dapm_widgets, | 553 | snd_soc_dapm_new_controls(codec, cs42l51_dapm_widgets, |
@@ -722,22 +558,77 @@ static int cs42l51_probe(struct platform_device *pdev) | |||
722 | return 0; | 558 | return 0; |
723 | } | 559 | } |
724 | 560 | ||
561 | static struct snd_soc_codec_driver soc_codec_device_cs42l51 = { | ||
562 | .probe = cs42l51_probe, | ||
563 | .reg_cache_size = CS42L51_NUMREGS, | ||
564 | .reg_word_size = sizeof(u8), | ||
565 | }; | ||
725 | 566 | ||
726 | static int cs42l51_remove(struct platform_device *pdev) | 567 | static int cs42l51_i2c_probe(struct i2c_client *i2c_client, |
568 | const struct i2c_device_id *id) | ||
727 | { | 569 | { |
728 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 570 | struct cs42l51_private *cs42l51; |
571 | int ret; | ||
729 | 572 | ||
730 | snd_soc_free_pcms(socdev); | 573 | /* Verify that we have a CS42L51 */ |
731 | snd_soc_dapm_free(socdev); | 574 | ret = i2c_smbus_read_byte_data(i2c_client, CS42L51_CHIP_REV_ID); |
575 | if (ret < 0) { | ||
576 | dev_err(&i2c_client->dev, "failed to read I2C\n"); | ||
577 | goto error; | ||
578 | } | ||
579 | |||
580 | if ((ret != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) && | ||
581 | (ret != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) { | ||
582 | dev_err(&i2c_client->dev, "Invalid chip id\n"); | ||
583 | ret = -ENODEV; | ||
584 | goto error; | ||
585 | } | ||
586 | |||
587 | dev_info(&i2c_client->dev, "found device cs42l51 rev %d\n", | ||
588 | ret & 7); | ||
589 | |||
590 | cs42l51 = kzalloc(sizeof(struct cs42l51_private), GFP_KERNEL); | ||
591 | if (!cs42l51) { | ||
592 | dev_err(&i2c_client->dev, "could not allocate codec\n"); | ||
593 | return -ENOMEM; | ||
594 | } | ||
595 | |||
596 | i2c_set_clientdata(i2c_client, cs42l51); | ||
597 | cs42l51->control_data = i2c_client; | ||
598 | cs42l51->control_type = SND_SOC_I2C; | ||
732 | 599 | ||
600 | ret = snd_soc_register_codec(&i2c_client->dev, | ||
601 | &soc_codec_device_cs42l51, &cs42l51_dai, 1); | ||
602 | if (ret < 0) | ||
603 | kfree(cs42l51); | ||
604 | error: | ||
605 | return ret; | ||
606 | } | ||
607 | |||
608 | static int cs42l51_i2c_remove(struct i2c_client *client) | ||
609 | { | ||
610 | struct cs42l51_private *cs42l51 = i2c_get_clientdata(client); | ||
611 | |||
612 | snd_soc_unregister_codec(&client->dev); | ||
613 | kfree(cs42l51); | ||
733 | return 0; | 614 | return 0; |
734 | } | 615 | } |
735 | 616 | ||
736 | struct snd_soc_codec_device soc_codec_device_cs42l51 = { | 617 | static const struct i2c_device_id cs42l51_id[] = { |
737 | .probe = cs42l51_probe, | 618 | {"cs42l51", 0}, |
738 | .remove = cs42l51_remove | 619 | {} |
620 | }; | ||
621 | MODULE_DEVICE_TABLE(i2c, cs42l51_id); | ||
622 | |||
623 | static struct i2c_driver cs42l51_i2c_driver = { | ||
624 | .driver = { | ||
625 | .name = "cs42l51-codec", | ||
626 | .owner = THIS_MODULE, | ||
627 | }, | ||
628 | .id_table = cs42l51_id, | ||
629 | .probe = cs42l51_i2c_probe, | ||
630 | .remove = cs42l51_i2c_remove, | ||
739 | }; | 631 | }; |
740 | EXPORT_SYMBOL_GPL(soc_codec_device_cs42l51); | ||
741 | 632 | ||
742 | static int __init cs42l51_init(void) | 633 | static int __init cs42l51_init(void) |
743 | { | 634 | { |
@@ -758,6 +649,6 @@ static void __exit cs42l51_exit(void) | |||
758 | } | 649 | } |
759 | module_exit(cs42l51_exit); | 650 | module_exit(cs42l51_exit); |
760 | 651 | ||
761 | MODULE_AUTHOR("Arnaud Patard <apatard@mandriva.com>"); | 652 | MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>"); |
762 | MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver"); | 653 | MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver"); |
763 | MODULE_LICENSE("GPL"); | 654 | MODULE_LICENSE("GPL"); |