diff options
author | Lars-Peter Clausen <lars@metafoo.de> | 2011-05-08 12:24:43 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2011-05-08 19:45:01 -0400 |
commit | b1f7b2b56b98d9eedc6a1b127d5bc5e9c51d2f73 (patch) | |
tree | 8b5d21531d874f2f6dcef4162c2e4262c98fa6a3 /sound/soc/codecs/ssm2602.c | |
parent | f6c1f2d5e5d50366910fd687e88d07ebaabe00ab (diff) |
ASoC: SSM2602: Add SSM2604 support
The SSM2604 is basically a lightweight variant of the SSM2602 with a compatible
register layout. Thus we can easily support both devices by the same driver,
by providing a slightly set of controls, widgets and routes.
Compared to the SSM2602 the SSM2604 has no microphone input and no headphone
output.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Acked-by: Liam Girdwood <lrg@ti.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/codecs/ssm2602.c')
-rw-r--r-- | sound/soc/codecs/ssm2602.c | 169 |
1 files changed, 116 insertions, 53 deletions
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index e1ebf3d04935..b0306cf09dfe 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c | |||
@@ -45,12 +45,19 @@ | |||
45 | 45 | ||
46 | #define SSM2602_VERSION "0.1" | 46 | #define SSM2602_VERSION "0.1" |
47 | 47 | ||
48 | enum ssm2602_type { | ||
49 | SSM2602, | ||
50 | SSM2604, | ||
51 | }; | ||
52 | |||
48 | /* codec private data */ | 53 | /* codec private data */ |
49 | struct ssm2602_priv { | 54 | struct ssm2602_priv { |
50 | unsigned int sysclk; | 55 | unsigned int sysclk; |
51 | enum snd_soc_control_type control_type; | 56 | enum snd_soc_control_type control_type; |
52 | struct snd_pcm_substream *master_substream; | 57 | struct snd_pcm_substream *master_substream; |
53 | struct snd_pcm_substream *slave_substream; | 58 | struct snd_pcm_substream *slave_substream; |
59 | |||
60 | enum ssm2602_type type; | ||
54 | }; | 61 | }; |
55 | 62 | ||
56 | /* | 63 | /* |
@@ -80,90 +87,97 @@ static const struct soc_enum ssm2602_enum[] = { | |||
80 | SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, 4, ssm2602_deemph), | 87 | SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, 4, ssm2602_deemph), |
81 | }; | 88 | }; |
82 | 89 | ||
83 | static const struct snd_kcontrol_new ssm2602_snd_controls[] = { | 90 | static const struct snd_kcontrol_new ssm260x_snd_controls[] = { |
91 | SOC_DOUBLE_R("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 31, 0), | ||
92 | SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1), | ||
84 | 93 | ||
94 | SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1), | ||
95 | SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0), | ||
96 | |||
97 | SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]), | ||
98 | }; | ||
99 | |||
100 | static const struct snd_kcontrol_new ssm2602_snd_controls[] = { | ||
85 | SOC_DOUBLE_R("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V, | 101 | SOC_DOUBLE_R("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V, |
86 | 0, 127, 0), | 102 | 0, 127, 0), |
87 | SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V, | 103 | SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V, |
88 | 7, 1, 0), | 104 | 7, 1, 0), |
89 | 105 | ||
90 | SOC_DOUBLE_R("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 31, 0), | 106 | SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1), |
91 | SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1), | ||
92 | 107 | ||
93 | SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0), | 108 | SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0), |
94 | SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0), | 109 | SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0), |
95 | SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1), | 110 | SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1), |
96 | |||
97 | SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1), | ||
98 | |||
99 | SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1), | ||
100 | SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0), | ||
101 | |||
102 | SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]), | ||
103 | }; | 111 | }; |
104 | 112 | ||
105 | /* Output Mixer */ | 113 | /* Output Mixer */ |
106 | static const struct snd_kcontrol_new ssm2602_output_mixer_controls[] = { | 114 | static const struct snd_kcontrol_new ssm260x_output_mixer_controls[] = { |
107 | SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0), | 115 | SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0), |
108 | SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0), | ||
109 | SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0), | 116 | SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0), |
117 | SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0), | ||
110 | }; | 118 | }; |
111 | 119 | ||
112 | /* Input mux */ | 120 | /* Input mux */ |
113 | static const struct snd_kcontrol_new ssm2602_input_mux_controls = | 121 | static const struct snd_kcontrol_new ssm2602_input_mux_controls = |
114 | SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]); | 122 | SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]); |
115 | 123 | ||
116 | static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = { | 124 | static const struct snd_soc_dapm_widget ssm260x_dapm_widgets[] = { |
117 | SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1, | ||
118 | &ssm2602_output_mixer_controls[0], | ||
119 | ARRAY_SIZE(ssm2602_output_mixer_controls)), | ||
120 | SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1), | 125 | SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1), |
126 | SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1), | ||
127 | SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0), | ||
128 | |||
121 | SND_SOC_DAPM_OUTPUT("LOUT"), | 129 | SND_SOC_DAPM_OUTPUT("LOUT"), |
122 | SND_SOC_DAPM_OUTPUT("LHPOUT"), | ||
123 | SND_SOC_DAPM_OUTPUT("ROUT"), | 130 | SND_SOC_DAPM_OUTPUT("ROUT"), |
124 | SND_SOC_DAPM_OUTPUT("RHPOUT"), | 131 | SND_SOC_DAPM_INPUT("RLINEIN"), |
125 | SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1), | 132 | SND_SOC_DAPM_INPUT("LLINEIN"), |
133 | }; | ||
134 | |||
135 | static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = { | ||
136 | SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1, | ||
137 | ssm260x_output_mixer_controls, | ||
138 | ARRAY_SIZE(ssm260x_output_mixer_controls)), | ||
139 | |||
126 | SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls), | 140 | SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls), |
127 | SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0), | ||
128 | SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1), | 141 | SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1), |
142 | |||
143 | SND_SOC_DAPM_OUTPUT("LHPOUT"), | ||
144 | SND_SOC_DAPM_OUTPUT("RHPOUT"), | ||
129 | SND_SOC_DAPM_INPUT("MICIN"), | 145 | SND_SOC_DAPM_INPUT("MICIN"), |
130 | SND_SOC_DAPM_INPUT("RLINEIN"), | ||
131 | SND_SOC_DAPM_INPUT("LLINEIN"), | ||
132 | }; | 146 | }; |
133 | 147 | ||
134 | static const struct snd_soc_dapm_route audio_conn[] = { | 148 | static const struct snd_soc_dapm_widget ssm2604_dapm_widgets[] = { |
135 | /* output mixer */ | 149 | SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, |
150 | ssm260x_output_mixer_controls, | ||
151 | ARRAY_SIZE(ssm260x_output_mixer_controls) - 1), /* Last element is the mic */ | ||
152 | }; | ||
153 | |||
154 | static const struct snd_soc_dapm_route ssm260x_routes[] = { | ||
136 | {"Output Mixer", "Line Bypass Switch", "Line Input"}, | 155 | {"Output Mixer", "Line Bypass Switch", "Line Input"}, |
137 | {"Output Mixer", "HiFi Playback Switch", "DAC"}, | 156 | {"Output Mixer", "HiFi Playback Switch", "DAC"}, |
157 | |||
158 | {"ROUT", NULL, "Output Mixer"}, | ||
159 | {"LOUT", NULL, "Output Mixer"}, | ||
160 | |||
161 | {"Line Input", NULL, "LLINEIN"}, | ||
162 | {"Line Input", NULL, "RLINEIN"}, | ||
163 | }; | ||
164 | |||
165 | static const struct snd_soc_dapm_route ssm2602_routes[] = { | ||
138 | {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"}, | 166 | {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"}, |
139 | 167 | ||
140 | /* outputs */ | ||
141 | {"RHPOUT", NULL, "Output Mixer"}, | 168 | {"RHPOUT", NULL, "Output Mixer"}, |
142 | {"ROUT", NULL, "Output Mixer"}, | ||
143 | {"LHPOUT", NULL, "Output Mixer"}, | 169 | {"LHPOUT", NULL, "Output Mixer"}, |
144 | {"LOUT", NULL, "Output Mixer"}, | ||
145 | 170 | ||
146 | /* input mux */ | ||
147 | {"Input Mux", "Line", "Line Input"}, | 171 | {"Input Mux", "Line", "Line Input"}, |
148 | {"Input Mux", "Mic", "Mic Bias"}, | 172 | {"Input Mux", "Mic", "Mic Bias"}, |
149 | {"ADC", NULL, "Input Mux"}, | 173 | {"ADC", NULL, "Input Mux"}, |
150 | 174 | ||
151 | /* inputs */ | ||
152 | {"Line Input", NULL, "LLINEIN"}, | ||
153 | {"Line Input", NULL, "RLINEIN"}, | ||
154 | {"Mic Bias", NULL, "MICIN"}, | 175 | {"Mic Bias", NULL, "MICIN"}, |
155 | }; | 176 | }; |
156 | 177 | ||
157 | static int ssm2602_add_widgets(struct snd_soc_codec *codec) | 178 | static const struct snd_soc_dapm_route ssm2604_routes[] = { |
158 | { | 179 | {"ADC", NULL, "Line Input"}, |
159 | struct snd_soc_dapm_context *dapm = &codec->dapm; | 180 | }; |
160 | |||
161 | snd_soc_dapm_new_controls(dapm, ssm2602_dapm_widgets, | ||
162 | ARRAY_SIZE(ssm2602_dapm_widgets)); | ||
163 | snd_soc_dapm_add_routes(dapm, audio_conn, ARRAY_SIZE(audio_conn)); | ||
164 | |||
165 | return 0; | ||
166 | } | ||
167 | 181 | ||
168 | struct ssm2602_coeff { | 182 | struct ssm2602_coeff { |
169 | u32 mclk; | 183 | u32 mclk; |
@@ -492,8 +506,46 @@ static int ssm2602_resume(struct snd_soc_codec *codec) | |||
492 | 506 | ||
493 | static int ssm2602_probe(struct snd_soc_codec *codec) | 507 | static int ssm2602_probe(struct snd_soc_codec *codec) |
494 | { | 508 | { |
509 | struct snd_soc_dapm_context *dapm = &codec->dapm; | ||
510 | int ret, reg; | ||
511 | |||
512 | reg = snd_soc_read(codec, SSM2602_LOUT1V); | ||
513 | snd_soc_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH); | ||
514 | reg = snd_soc_read(codec, SSM2602_ROUT1V); | ||
515 | snd_soc_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH); | ||
516 | |||
517 | ret = snd_soc_add_controls(codec, ssm2602_snd_controls, | ||
518 | ARRAY_SIZE(ssm2602_snd_controls)); | ||
519 | if (ret) | ||
520 | return ret; | ||
521 | |||
522 | ret = snd_soc_dapm_new_controls(dapm, ssm2602_dapm_widgets, | ||
523 | ARRAY_SIZE(ssm2602_dapm_widgets)); | ||
524 | if (ret) | ||
525 | return ret; | ||
526 | |||
527 | return snd_soc_dapm_add_routes(dapm, ssm2602_routes, | ||
528 | ARRAY_SIZE(ssm2602_routes)); | ||
529 | } | ||
530 | |||
531 | static int ssm2604_probe(struct snd_soc_codec *codec) | ||
532 | { | ||
533 | struct snd_soc_dapm_context *dapm = &codec->dapm; | ||
534 | int ret; | ||
535 | |||
536 | ret = snd_soc_dapm_new_controls(dapm, ssm2604_dapm_widgets, | ||
537 | ARRAY_SIZE(ssm2604_dapm_widgets)); | ||
538 | if (ret) | ||
539 | return ret; | ||
540 | |||
541 | return snd_soc_dapm_add_routes(dapm, ssm2604_routes, | ||
542 | ARRAY_SIZE(ssm2604_routes)); | ||
543 | } | ||
544 | |||
545 | static int ssm260x_probe(struct snd_soc_codec *codec) | ||
546 | { | ||
495 | struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); | 547 | struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); |
496 | int ret = 0, reg; | 548 | int ret, reg; |
497 | 549 | ||
498 | pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION); | 550 | pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION); |
499 | 551 | ||
@@ -514,19 +566,20 @@ static int ssm2602_probe(struct snd_soc_codec *codec) | |||
514 | snd_soc_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH); | 566 | snd_soc_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH); |
515 | reg = snd_soc_read(codec, SSM2602_RINVOL); | 567 | reg = snd_soc_read(codec, SSM2602_RINVOL); |
516 | snd_soc_write(codec, SSM2602_RINVOL, reg | RINVOL_RLIN_BOTH); | 568 | snd_soc_write(codec, SSM2602_RINVOL, reg | RINVOL_RLIN_BOTH); |
517 | reg = snd_soc_read(codec, SSM2602_LOUT1V); | ||
518 | snd_soc_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH); | ||
519 | reg = snd_soc_read(codec, SSM2602_ROUT1V); | ||
520 | snd_soc_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH); | ||
521 | /*select Line in as default input*/ | 569 | /*select Line in as default input*/ |
522 | snd_soc_write(codec, SSM2602_APANA, APANA_SELECT_DAC | | 570 | snd_soc_write(codec, SSM2602_APANA, APANA_SELECT_DAC | |
523 | APANA_ENABLE_MIC_BOOST); | 571 | APANA_ENABLE_MIC_BOOST); |
524 | 572 | ||
525 | snd_soc_add_controls(codec, ssm2602_snd_controls, | 573 | switch (ssm2602->type) { |
526 | ARRAY_SIZE(ssm2602_snd_controls)); | 574 | case SSM2602: |
527 | ssm2602_add_widgets(codec); | 575 | ret = ssm2602_probe(codec); |
576 | break; | ||
577 | case SSM2604: | ||
578 | ret = ssm2604_probe(codec); | ||
579 | break; | ||
580 | } | ||
528 | 581 | ||
529 | return 0; | 582 | return ret; |
530 | } | 583 | } |
531 | 584 | ||
532 | /* remove everything here */ | 585 | /* remove everything here */ |
@@ -537,7 +590,7 @@ static int ssm2602_remove(struct snd_soc_codec *codec) | |||
537 | } | 590 | } |
538 | 591 | ||
539 | static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = { | 592 | static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = { |
540 | .probe = ssm2602_probe, | 593 | .probe = ssm260x_probe, |
541 | .remove = ssm2602_remove, | 594 | .remove = ssm2602_remove, |
542 | .suspend = ssm2602_suspend, | 595 | .suspend = ssm2602_suspend, |
543 | .resume = ssm2602_resume, | 596 | .resume = ssm2602_resume, |
@@ -545,6 +598,13 @@ static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = { | |||
545 | .reg_cache_size = ARRAY_SIZE(ssm2602_reg), | 598 | .reg_cache_size = ARRAY_SIZE(ssm2602_reg), |
546 | .reg_word_size = sizeof(u16), | 599 | .reg_word_size = sizeof(u16), |
547 | .reg_cache_default = ssm2602_reg, | 600 | .reg_cache_default = ssm2602_reg, |
601 | |||
602 | .controls = ssm260x_snd_controls, | ||
603 | .num_controls = ARRAY_SIZE(ssm260x_snd_controls), | ||
604 | .dapm_widgets = ssm260x_dapm_widgets, | ||
605 | .num_dapm_widgets = ARRAY_SIZE(ssm260x_dapm_widgets), | ||
606 | .dapm_routes = ssm260x_routes, | ||
607 | .num_dapm_routes = ARRAY_SIZE(ssm260x_routes), | ||
548 | }; | 608 | }; |
549 | 609 | ||
550 | #if defined(CONFIG_SPI_MASTER) | 610 | #if defined(CONFIG_SPI_MASTER) |
@@ -559,6 +619,7 @@ static int __devinit ssm2602_spi_probe(struct spi_device *spi) | |||
559 | 619 | ||
560 | spi_set_drvdata(spi, ssm2602); | 620 | spi_set_drvdata(spi, ssm2602); |
561 | ssm2602->control_type = SND_SOC_SPI; | 621 | ssm2602->control_type = SND_SOC_SPI; |
622 | ssm2602->type = SSM2602; | ||
562 | 623 | ||
563 | ret = snd_soc_register_codec(&spi->dev, | 624 | ret = snd_soc_register_codec(&spi->dev, |
564 | &soc_codec_dev_ssm2602, &ssm2602_dai, 1); | 625 | &soc_codec_dev_ssm2602, &ssm2602_dai, 1); |
@@ -603,6 +664,7 @@ static int __devinit ssm2602_i2c_probe(struct i2c_client *i2c, | |||
603 | 664 | ||
604 | i2c_set_clientdata(i2c, ssm2602); | 665 | i2c_set_clientdata(i2c, ssm2602); |
605 | ssm2602->control_type = SND_SOC_I2C; | 666 | ssm2602->control_type = SND_SOC_I2C; |
667 | ssm2602->type = id->driver_data; | ||
606 | 668 | ||
607 | ret = snd_soc_register_codec(&i2c->dev, | 669 | ret = snd_soc_register_codec(&i2c->dev, |
608 | &soc_codec_dev_ssm2602, &ssm2602_dai, 1); | 670 | &soc_codec_dev_ssm2602, &ssm2602_dai, 1); |
@@ -619,7 +681,8 @@ static int __devexit ssm2602_i2c_remove(struct i2c_client *client) | |||
619 | } | 681 | } |
620 | 682 | ||
621 | static const struct i2c_device_id ssm2602_i2c_id[] = { | 683 | static const struct i2c_device_id ssm2602_i2c_id[] = { |
622 | { "ssm2602", 0 }, | 684 | { "ssm2602", SSM2602 }, |
685 | { "ssm2604", SSM2604 }, | ||
623 | { } | 686 | { } |
624 | }; | 687 | }; |
625 | MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id); | 688 | MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id); |
@@ -669,6 +732,6 @@ static void __exit ssm2602_exit(void) | |||
669 | } | 732 | } |
670 | module_exit(ssm2602_exit); | 733 | module_exit(ssm2602_exit); |
671 | 734 | ||
672 | MODULE_DESCRIPTION("ASoC ssm2602 driver"); | 735 | MODULE_DESCRIPTION("ASoC SSM2602/SSM2604 driver"); |
673 | MODULE_AUTHOR("Cliff Cai"); | 736 | MODULE_AUTHOR("Cliff Cai"); |
674 | MODULE_LICENSE("GPL"); | 737 | MODULE_LICENSE("GPL"); |