[alsa-devel] [PATCH 01/12] ASoC: Create codec DAPM widgets before calling the codecs probe function
This allows to create DAPM routes depending on those widgets in the codecs probe function. This is helpful when supporting similar codecs with minor differences in the DAPM routing with the same driver.
Something similar has already been done for cards in commit a841ebb9(ASoC: Create card DAPM widgets early so they can be used in callbacks)
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/soc-core.c | 7 ++++--- 1 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 133edeb..a477e21 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1495,6 +1495,10 @@ static int soc_probe_codec(struct snd_soc_card *card,
soc_init_codec_debugfs(codec);
+ if (driver->dapm_widgets) + snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets, + driver->num_dapm_widgets); + if (driver->probe) { ret = driver->probe(codec); if (ret < 0) { @@ -1508,9 +1512,6 @@ static int soc_probe_codec(struct snd_soc_card *card, if (driver->controls) snd_soc_add_controls(codec, driver->controls, driver->num_controls); - if (driver->dapm_widgets) - snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets, - driver->num_dapm_widgets); if (driver->dapm_routes) snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes, driver->num_dapm_routes);
Drop unused field from the coeff struct, precalculate the srate register at compile-time and cleanup up the naming.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/codecs/ssm2602.c | 75 ++++++++++++++++++++----------------------- 1 files changed, 35 insertions(+), 40 deletions(-)
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index 7e21949..d536820 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -167,83 +167,78 @@ static int ssm2602_add_widgets(struct snd_soc_codec *codec) return 0; }
-struct _coeff_div { +struct ssm2602_coeff { u32 mclk; u32 rate; - u16 fs; - u8 sr:4; - u8 bosr:1; - u8 usb:1; + u8 srate; };
-/* codec mclk clock divider coefficients */ -static const struct _coeff_div coeff_div[] = { +#define SSM2602_COEFF_SRATE(sr, bosr, usb) (((sr) << 2) | ((bosr) << 1) | (usb)) + +/* codec mclk clock coefficients */ +static const struct ssm2602_coeff ssm2602_coeff_table[] = { /* 48k */ - {12288000, 48000, 256, 0x0, 0x0, 0x0}, - {18432000, 48000, 384, 0x0, 0x1, 0x0}, - {12000000, 48000, 250, 0x0, 0x0, 0x1}, + {12288000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x0)}, + {18432000, 48000, SSM2602_COEFF_SRATE(0x0, 0x1, 0x0)}, + {12000000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x1)},
/* 32k */ - {12288000, 32000, 384, 0x6, 0x0, 0x0}, - {18432000, 32000, 576, 0x6, 0x1, 0x0}, - {12000000, 32000, 375, 0x6, 0x0, 0x1}, + {12288000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x0)}, + {18432000, 32000, SSM2602_COEFF_SRATE(0x6, 0x1, 0x0)}, + {12000000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x1)},
/* 8k */ - {12288000, 8000, 1536, 0x3, 0x0, 0x0}, - {18432000, 8000, 2304, 0x3, 0x1, 0x0}, - {11289600, 8000, 1408, 0xb, 0x0, 0x0}, - {16934400, 8000, 2112, 0xb, 0x1, 0x0}, - {12000000, 8000, 1500, 0x3, 0x0, 0x1}, + {12288000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x0)}, + {18432000, 8000, SSM2602_COEFF_SRATE(0x3, 0x1, 0x0)}, + {11289600, 8000, SSM2602_COEFF_SRATE(0xb, 0x0, 0x0)}, + {16934400, 8000, SSM2602_COEFF_SRATE(0xb, 0x1, 0x0)}, + {12000000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x1)},
/* 96k */ - {12288000, 96000, 128, 0x7, 0x0, 0x0}, - {18432000, 96000, 192, 0x7, 0x1, 0x0}, - {12000000, 96000, 125, 0x7, 0x0, 0x1}, + {12288000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x0)}, + {18432000, 96000, SSM2602_COEFF_SRATE(0x7, 0x1, 0x0)}, + {12000000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x1)},
/* 44.1k */ - {11289600, 44100, 256, 0x8, 0x0, 0x0}, - {16934400, 44100, 384, 0x8, 0x1, 0x0}, - {12000000, 44100, 272, 0x8, 0x1, 0x1}, + {11289600, 44100, SSM2602_COEFF_SRATE(0x8, 0x0, 0x0)}, + {16934400, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x0)}, + {12000000, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x1)},
/* 88.2k */ - {11289600, 88200, 128, 0xf, 0x0, 0x0}, - {16934400, 88200, 192, 0xf, 0x1, 0x0}, - {12000000, 88200, 136, 0xf, 0x1, 0x1}, + {11289600, 88200, SSM2602_COEFF_SRATE(0xf, 0x0, 0x0)}, + {16934400, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x0)}, + {12000000, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x1)}, };
-static inline int get_coeff(int mclk, int rate) +static inline int ssm2602_get_coeff(int mclk, int rate) { int i;
- for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { - if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) - return i; + for (i = 0; i < ARRAY_SIZE(ssm2602_coeff_table); i++) { + if (ssm2602_coeff_table[i].rate == rate && + ssm2602_coeff_table[i].mclk == mclk) + return ssm2602_coeff_table[i].srate; } - return i; + return -EINVAL; }
static int ssm2602_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - u16 srate; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); u16 iface = snd_soc_read(codec, SSM2602_IFACE) & 0xfff3; - int i = get_coeff(ssm2602->sysclk, params_rate(params)); + int srate = ssm2602_get_coeff(ssm2602->sysclk, params_rate(params));
if (substream == ssm2602->slave_substream) { dev_dbg(codec->dev, "Ignoring hw_params for slave substream\n"); return 0; }
- /*no match is found*/ - if (i == ARRAY_SIZE(coeff_div)) - return -EINVAL; - - srate = (coeff_div[i].sr << 2) | - (coeff_div[i].bosr << 1) | coeff_div[i].usb; + if (srate < 0) + return srate;
snd_soc_write(codec, SSM2602_ACTIVE, 0); snd_soc_write(codec, SSM2602_SRATE, srate);
There are currently two controls which allow selecting the capture source, one as a normal control, the other as part of a DAPM_MUX widget. Remove the normal control.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/codecs/ssm2602.c | 2 -- 1 files changed, 0 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index d536820..02390dd 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -99,8 +99,6 @@ SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1), SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1), SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0),
-SOC_ENUM("Capture Source", ssm2602_enum[0]), - SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]), };
Annotate the i2c probe and remove functions with __devinit and __devexit.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/codecs/ssm2602.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index 02390dd..6444d34 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -594,7 +594,7 @@ static struct spi_driver ssm2602_spi_driver = { * low = 0x1a * high = 0x1b */ -static int ssm2602_i2c_probe(struct i2c_client *i2c, +static int __devinit ssm2602_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct ssm2602_priv *ssm2602; @@ -614,7 +614,7 @@ static int ssm2602_i2c_probe(struct i2c_client *i2c, return ret; }
-static int ssm2602_i2c_remove(struct i2c_client *client) +static int __devexit ssm2602_i2c_remove(struct i2c_client *client) { snd_soc_unregister_codec(&client->dev); kfree(i2c_get_clientdata(client)); @@ -634,7 +634,7 @@ static struct i2c_driver ssm2602_i2c_driver = { .owner = THIS_MODULE, }, .probe = ssm2602_i2c_probe, - .remove = ssm2602_i2c_remove, + .remove = __devexit_p(ssm2602_i2c_remove), .id_table = ssm2602_i2c_id, }; #endif
Those are leftovers from a pre-multicomponent era.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/codecs/ssm2602.h | 6 ------ 1 files changed, 0 insertions(+), 6 deletions(-)
diff --git a/sound/soc/codecs/ssm2602.h b/sound/soc/codecs/ssm2602.h index 42a47d0..b98c691 100644 --- a/sound/soc/codecs/ssm2602.h +++ b/sound/soc/codecs/ssm2602.h @@ -117,11 +117,5 @@ #define SSM2602_CACHEREGNUM 10
#define SSM2602_SYSCLK 0 -#define SSM2602_DAI 0 - -struct ssm2602_setup_data { - int i2c_bus; - unsigned short i2c_address; -};
#endif
reg_cache_size is supposed to be the number of elements in the register cache, not the size in bytes.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/codecs/ssm2602.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index 6444d34..40d3474 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -545,7 +545,7 @@ static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = { .suspend = ssm2602_suspend, .resume = ssm2602_resume, .set_bias_level = ssm2602_set_bias_level, - .reg_cache_size = sizeof(ssm2602_reg), + .reg_cache_size = ARRAY_SIZE(ssm2602_reg), .reg_word_size = sizeof(u16), .reg_cache_default = ssm2602_reg, };
It is not required to have the codec powered at this stage and DAPM will power the ADC and DAC down again after probe has run anyway. Thus we avoid some unnecessary writes by this change.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/codecs/ssm2602.c | 5 +---- 1 files changed, 1 insertions(+), 4 deletions(-)
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index 40d3474..9ce792b 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -61,7 +61,7 @@ struct ssm2602_priv { */ static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = { 0x0017, 0x0017, 0x0079, 0x0079, - 0x0000, 0x0000, 0x0000, 0x000a, + 0x0000, 0x0000, 0x009f, 0x000a, 0x0000, 0x0000 };
@@ -509,8 +509,6 @@ static int ssm2602_probe(struct snd_soc_codec *codec) return ret; }
- /*power on device*/ - snd_soc_write(codec, SSM2602_ACTIVE, 0); /* set the update bits */ reg = snd_soc_read(codec, SSM2602_LINVOL); snd_soc_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH); @@ -523,7 +521,6 @@ static int ssm2602_probe(struct snd_soc_codec *codec) /*select Line in as default input*/ snd_soc_write(codec, SSM2602_APANA, APANA_SELECT_DAC | APANA_ENABLE_MIC_BOOST); - snd_soc_write(codec, SSM2602_PWR, 0);
snd_soc_add_controls(codec, ssm2602_snd_controls, ARRAY_SIZE(ssm2602_snd_controls));
On Thu, May 05, 2011 at 04:59:15PM +0200, Lars-Peter Clausen wrote:
static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = { 0x0017, 0x0017, 0x0079, 0x0079,
- 0x0000, 0x0000, 0x0000, 0x000a,
- 0x0000, 0x0000, 0x009f, 0x000a, 0x0000, 0x0000
This should reflect the power on reset defaults of the device. If this is not the case we should fix that, but it's a separate issue to whatever the settings are.
The 'Mic Boost2' control's shift was off by one and thus was not working.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/codecs/ssm2602.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index 9ce792b..c6dc0da 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -91,7 +91,7 @@ SOC_DOUBLE_R("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 31, 0), SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1),
SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0), -SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 7, 1, 0), +SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0), SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1),
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 --- 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 c6dc0da..b36bfd1 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -45,12 +45,19 @@
#define SSM2602_VERSION "0.1"
+enum ssm2602_type { + SSM2602, + SSM2604, +}; + /* codec private data */ struct ssm2602_priv { unsigned int sysclk; enum snd_soc_control_type control_type; struct snd_pcm_substream *master_substream; struct snd_pcm_substream *slave_substream; + + enum ssm2602_type type; };
/* @@ -80,90 +87,97 @@ static const struct soc_enum ssm2602_enum[] = { SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, 4, ssm2602_deemph), };
-static const struct snd_kcontrol_new ssm2602_snd_controls[] = { +static const struct snd_kcontrol_new ssm260x_snd_controls[] = { +SOC_DOUBLE_R("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 31, 0), +SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1),
+SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1), +SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0), + +SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]), +}; + +static const struct snd_kcontrol_new ssm2602_snd_controls[] = { SOC_DOUBLE_R("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V, 0, 127, 0), SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V, 7, 1, 0),
-SOC_DOUBLE_R("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 31, 0), -SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1), +SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1),
SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0), SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0), SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1), - -SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1), - -SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1), -SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0), - -SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]), };
/* Output Mixer */ -static const struct snd_kcontrol_new ssm2602_output_mixer_controls[] = { +static const struct snd_kcontrol_new ssm260x_output_mixer_controls[] = { SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0), -SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0), SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0), +SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0), };
/* Input mux */ static const struct snd_kcontrol_new ssm2602_input_mux_controls = SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]);
-static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = { -SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1, - &ssm2602_output_mixer_controls[0], - ARRAY_SIZE(ssm2602_output_mixer_controls)), +static const struct snd_soc_dapm_widget ssm260x_dapm_widgets[] = { SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1), +SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1), +SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0), + SND_SOC_DAPM_OUTPUT("LOUT"), -SND_SOC_DAPM_OUTPUT("LHPOUT"), SND_SOC_DAPM_OUTPUT("ROUT"), -SND_SOC_DAPM_OUTPUT("RHPOUT"), -SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1), +SND_SOC_DAPM_INPUT("RLINEIN"), +SND_SOC_DAPM_INPUT("LLINEIN"), +}; + +static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = { +SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1, + ssm260x_output_mixer_controls, + ARRAY_SIZE(ssm260x_output_mixer_controls)), + SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls), -SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0), SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1), + +SND_SOC_DAPM_OUTPUT("LHPOUT"), +SND_SOC_DAPM_OUTPUT("RHPOUT"), SND_SOC_DAPM_INPUT("MICIN"), -SND_SOC_DAPM_INPUT("RLINEIN"), -SND_SOC_DAPM_INPUT("LLINEIN"), };
-static const struct snd_soc_dapm_route audio_conn[] = { - /* output mixer */ +static const struct snd_soc_dapm_widget ssm2604_dapm_widgets[] = { +SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, + ssm260x_output_mixer_controls, + ARRAY_SIZE(ssm260x_output_mixer_controls) - 1), /* Last element is the mic */ +}; + +static const struct snd_soc_dapm_route ssm260x_routes[] = { {"Output Mixer", "Line Bypass Switch", "Line Input"}, {"Output Mixer", "HiFi Playback Switch", "DAC"}, + + {"ROUT", NULL, "Output Mixer"}, + {"LOUT", NULL, "Output Mixer"}, + + {"Line Input", NULL, "LLINEIN"}, + {"Line Input", NULL, "RLINEIN"}, +}; + +static const struct snd_soc_dapm_route ssm2602_routes[] = { {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
- /* outputs */ {"RHPOUT", NULL, "Output Mixer"}, - {"ROUT", NULL, "Output Mixer"}, {"LHPOUT", NULL, "Output Mixer"}, - {"LOUT", NULL, "Output Mixer"},
- /* input mux */ {"Input Mux", "Line", "Line Input"}, {"Input Mux", "Mic", "Mic Bias"}, {"ADC", NULL, "Input Mux"},
- /* inputs */ - {"Line Input", NULL, "LLINEIN"}, - {"Line Input", NULL, "RLINEIN"}, {"Mic Bias", NULL, "MICIN"}, };
-static int ssm2602_add_widgets(struct snd_soc_codec *codec) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_new_controls(dapm, ssm2602_dapm_widgets, - ARRAY_SIZE(ssm2602_dapm_widgets)); - snd_soc_dapm_add_routes(dapm, audio_conn, ARRAY_SIZE(audio_conn)); - - return 0; -} +static const struct snd_soc_dapm_route ssm2604_routes[] = { + {"ADC", NULL, "Line Input"}, +};
struct ssm2602_coeff { u32 mclk; @@ -492,8 +506,46 @@ static int ssm2602_resume(struct snd_soc_codec *codec)
static int ssm2602_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret, reg; + + reg = snd_soc_read(codec, SSM2602_LOUT1V); + snd_soc_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH); + reg = snd_soc_read(codec, SSM2602_ROUT1V); + snd_soc_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH); + + ret = snd_soc_add_controls(codec, ssm2602_snd_controls, + ARRAY_SIZE(ssm2602_snd_controls)); + if (ret) + return ret; + + ret = snd_soc_dapm_new_controls(dapm, ssm2602_dapm_widgets, + ARRAY_SIZE(ssm2602_dapm_widgets)); + if (ret) + return ret; + + return snd_soc_dapm_add_routes(dapm, ssm2602_routes, + ARRAY_SIZE(ssm2602_routes)); +} + +static int ssm2604_probe(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret; + + ret = snd_soc_dapm_new_controls(dapm, ssm2604_dapm_widgets, + ARRAY_SIZE(ssm2604_dapm_widgets)); + if (ret) + return ret; + + return snd_soc_dapm_add_routes(dapm, ssm2604_routes, + ARRAY_SIZE(ssm2604_routes)); +} + +static int ssm260x_probe(struct snd_soc_codec *codec) +{ struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); - int ret = 0, reg; + int ret, reg;
pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION);
@@ -514,19 +566,20 @@ static int ssm2602_probe(struct snd_soc_codec *codec) snd_soc_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH); reg = snd_soc_read(codec, SSM2602_RINVOL); snd_soc_write(codec, SSM2602_RINVOL, reg | RINVOL_RLIN_BOTH); - reg = snd_soc_read(codec, SSM2602_LOUT1V); - snd_soc_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH); - reg = snd_soc_read(codec, SSM2602_ROUT1V); - snd_soc_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH); /*select Line in as default input*/ snd_soc_write(codec, SSM2602_APANA, APANA_SELECT_DAC | APANA_ENABLE_MIC_BOOST);
- snd_soc_add_controls(codec, ssm2602_snd_controls, - ARRAY_SIZE(ssm2602_snd_controls)); - ssm2602_add_widgets(codec); + switch (ssm2602->type) { + case SSM2602: + ret = ssm2602_probe(codec); + break; + case SSM2604: + ret = ssm2604_probe(codec); + break; + }
- return 0; + return ret; }
/* remove everything here */ @@ -537,7 +590,7 @@ static int ssm2602_remove(struct snd_soc_codec *codec) }
static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = { - .probe = ssm2602_probe, + .probe = ssm260x_probe, .remove = ssm2602_remove, .suspend = ssm2602_suspend, .resume = ssm2602_resume, @@ -545,6 +598,13 @@ static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = { .reg_cache_size = ARRAY_SIZE(ssm2602_reg), .reg_word_size = sizeof(u16), .reg_cache_default = ssm2602_reg, + + .controls = ssm260x_snd_controls, + .num_controls = ARRAY_SIZE(ssm260x_snd_controls), + .dapm_widgets = ssm260x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ssm260x_dapm_widgets), + .dapm_routes = ssm260x_routes, + .num_dapm_routes = ARRAY_SIZE(ssm260x_routes), };
#if defined(CONFIG_SPI_MASTER) @@ -559,6 +619,7 @@ static int __devinit ssm2602_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, ssm2602); ssm2602->control_type = SND_SOC_SPI; + ssm2602->type = SSM2602;
ret = snd_soc_register_codec(&spi->dev, &soc_codec_dev_ssm2602, &ssm2602_dai, 1); @@ -603,6 +664,7 @@ static int __devinit ssm2602_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, ssm2602); ssm2602->control_type = SND_SOC_I2C; + ssm2602->type = id->driver_data;
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_ssm2602, &ssm2602_dai, 1); @@ -619,7 +681,8 @@ static int __devexit ssm2602_i2c_remove(struct i2c_client *client) }
static const struct i2c_device_id ssm2602_i2c_id[] = { - { "ssm2602", 0 }, + { "ssm2602", SSM2602 }, + { "ssm2604", SSM2604 }, { } }; MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id); @@ -669,6 +732,6 @@ static void __exit ssm2602_exit(void) } module_exit(ssm2602_exit);
-MODULE_DESCRIPTION("ASoC ssm2602 driver"); +MODULE_DESCRIPTION("ASoC SSM2602/SSM2604 driver"); MODULE_AUTHOR("Cliff Cai"); MODULE_LICENSE("GPL");
The SSM2603 is mostly register compatible with the SSM2602 and can be supported by the current driver without any changes.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/codecs/ssm2602.c | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index b36bfd1..ca57d93 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -682,6 +682,7 @@ static int __devexit ssm2602_i2c_remove(struct i2c_client *client)
static const struct i2c_device_id ssm2602_i2c_id[] = { { "ssm2602", SSM2602 }, + { "ssm2603", SSM2602 }, { "ssm2604", SSM2604 }, { } }; @@ -732,6 +733,6 @@ static void __exit ssm2602_exit(void) } module_exit(ssm2602_exit);
-MODULE_DESCRIPTION("ASoC SSM2602/SSM2604 driver"); +MODULE_DESCRIPTION("ASoC SSM2602/SSM2603/SSM2604 driver"); MODULE_AUTHOR("Cliff Cai"); MODULE_LICENSE("GPL");
Model the power supply for the digital core as a DAPM_SUPPLY widget. This allows to cleanup the code a bit.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/codecs/ssm2602.c | 25 ++++++------------------- 1 files changed, 6 insertions(+), 19 deletions(-)
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index ca57d93..c3d5936 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -126,6 +126,8 @@ SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1), SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1), SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0),
+SND_SOC_DAPM_SUPPLY("Digital Core Power", SSM2602_ACTIVE, 0, 0, 0, 0), + SND_SOC_DAPM_OUTPUT("LOUT"), SND_SOC_DAPM_OUTPUT("ROUT"), SND_SOC_DAPM_INPUT("RLINEIN"), @@ -152,6 +154,9 @@ SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, };
static const struct snd_soc_dapm_route ssm260x_routes[] = { + {"DAC", NULL, "Digital Core Power"}, + {"ADC", NULL, "Digital Core Power"}, + {"Output Mixer", "Line Bypass Switch", "Line Input"}, {"Output Mixer", "HiFi Playback Switch", "DAC"},
@@ -252,7 +257,6 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream, if (srate < 0) return srate;
- snd_soc_write(codec, SSM2602_ACTIVE, 0); snd_soc_write(codec, SSM2602_SRATE, srate);
/* bit size */ @@ -270,7 +274,6 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream, break; } snd_soc_write(codec, SSM2602_IFACE, iface); - snd_soc_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC); return 0; }
@@ -312,17 +315,6 @@ static int ssm2602_startup(struct snd_pcm_substream *substream, return 0; }
-static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; - /* set active */ - snd_soc_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC); - - return 0; -} - static void ssm2602_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -330,16 +322,13 @@ static void ssm2602_shutdown(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = rtd->codec; struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
- /* deactivate */ - if (!codec->active) - snd_soc_write(codec, SSM2602_ACTIVE, 0); - if (ssm2602->master_substream == substream) ssm2602->master_substream = ssm2602->slave_substream;
ssm2602->slave_substream = NULL; }
+ static int ssm2602_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; @@ -446,7 +435,6 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_OFF: /* everything off, dac mute, inactive */ - snd_soc_write(codec, SSM2602_ACTIVE, 0); snd_soc_write(codec, SSM2602_PWR, 0xffff); break;
@@ -464,7 +452,6 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
static struct snd_soc_dai_ops ssm2602_dai_ops = { .startup = ssm2602_startup, - .prepare = ssm2602_pcm_prepare, .hw_params = ssm2602_hw_params, .shutdown = ssm2602_shutdown, .digital_mute = ssm2602_mute,
Also fix the maximum value for the capture volume control.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/codecs/ssm2602.c | 21 ++++++++++++++++----- 1 files changed, 16 insertions(+), 5 deletions(-)
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index c3d5936..8ed74e6 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -40,6 +40,7 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/initval.h> +#include <sound/tlv.h>
#include "ssm2602.h"
@@ -87,8 +88,18 @@ static const struct soc_enum ssm2602_enum[] = { SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, 4, ssm2602_deemph), };
+static const unsigned int ssm260x_outmix_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 47, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0), + 48, 127, TLV_DB_SCALE_ITEM(-7400, 100, 0), +}; + +static const DECLARE_TLV_DB_SCALE(ssm260x_inpga_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(ssm260x_sidetone_tlv, -1500, 300, 0); + static const struct snd_kcontrol_new ssm260x_snd_controls[] = { -SOC_DOUBLE_R("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 31, 0), +SOC_DOUBLE_R_TLV("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 45, 0, + ssm260x_inpga_tlv), SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1),
SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1), @@ -98,12 +109,12 @@ SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]), };
static const struct snd_kcontrol_new ssm2602_snd_controls[] = { -SOC_DOUBLE_R("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V, - 0, 127, 0), +SOC_DOUBLE_R_TLV("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V, + 0, 127, 0, ssm260x_outmix_tlv), SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V, 7, 1, 0), - -SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1), +SOC_SINGLE_TLV("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1, + ssm260x_sidetone_tlv),
SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0), SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0),
not to bug you too much, but could you resend the series with the updated device-drivers-devel ml ? will make it easier for me to review ... -mike
On Thu, May 5, 2011 at 15:08, Mike Frysinger wrote:
not to bug you too much, but could you resend the series with the updated device-drivers-devel ml ? Â will make it easier for me to review ...
sorry, i meant just fix the d-d-d ml e-mail rather than only send there. but whatever, i'll manually merge the e-mail threads once i get back to the US, so dont worry about it. -mike
On Thu, 2011-05-05 at 16:59 +0200, Lars-Peter Clausen wrote:
This allows to create DAPM routes depending on those widgets in the codecs probe function. This is helpful when supporting similar codecs with minor differences in the DAPM routing with the same driver.
Something similar has already been done for cards in commit a841ebb9(ASoC: Create card DAPM widgets early so they can be used in callbacks)
Signed-off-by: Lars-Peter Clausen lars@metafoo.de
These look all OK but I'm not fully following your commit message above with moving the call to snd_soc_dapm_new_controls() below. Could you explain why moving the call fixes your issue.
sound/soc/soc-core.c | 7 ++++--- 1 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 133edeb..a477e21 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1495,6 +1495,10 @@ static int soc_probe_codec(struct snd_soc_card *card,
soc_init_codec_debugfs(codec);
- if (driver->dapm_widgets)
snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets,
driver->num_dapm_widgets);
- if (driver->probe) { ret = driver->probe(codec); if (ret < 0) {
@@ -1508,9 +1512,6 @@ static int soc_probe_codec(struct snd_soc_card *card, if (driver->controls) snd_soc_add_controls(codec, driver->controls, driver->num_controls);
- if (driver->dapm_widgets)
snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets,
if (driver->dapm_routes) snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes, driver->num_dapm_routes);driver->num_dapm_widgets);
On Sat, May 07, 2011 at 07:11:00PM +0100, Liam Girdwood wrote:
These look all OK but I'm not fully following your commit message above with moving the call to snd_soc_dapm_new_controls() below. Could you explain why moving the call fixes your issue.
It means the widgets are present before the codec initialisation callbacks get run so the callbacks can use them if they wish to.
On Sat, 2011-05-07 at 19:21 +0100, Mark Brown wrote:
On Sat, May 07, 2011 at 07:11:00PM +0100, Liam Girdwood wrote:
These look all OK but I'm not fully following your commit message above with moving the call to snd_soc_dapm_new_controls() below. Could you explain why moving the call fixes your issue.
It means the widgets are present before the codec initialisation callbacks get run so the callbacks can use them if they wish to.
Ok, cool. This is a better commit message.
All.
Acked-by: Liam Girdwood lrg@ti.com
On Sun, May 08, 2011 at 12:16:01PM +0100, Liam Girdwood wrote:
Ok, cool. This is a better commit message.
All.
Acked-by: Liam Girdwood lrg@ti.com
I tried to apply these but probably due to patch 7 not being applied most of the patches after it failed. Please resubmit anything that hasn't turned up in my tree.
Please also remember to pull bug fixes to the front of the patch series so they can be sent to Linus without getting tied in with random new feature additions and ensure that your commit messages have lines less than 80 characters long.
Some of the values in the default register cache did not represent the codecs state after reset. This patch fixes it.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/codecs/ssm2602.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index 30229cf..a09d66c 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -60,8 +60,8 @@ struct ssm2602_priv { * There is no point in caching the reset register */ static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = { - 0x0017, 0x0017, 0x0079, 0x0079, - 0x0000, 0x0000, 0x0000, 0x000a, + 0x0097, 0x0097, 0x0079, 0x0079, + 0x000a, 0x0008, 0x009f, 0x000a, 0x0000, 0x0000 };
On Sun, 2011-05-08 at 09:24 -0700, Lars-Peter Clausen wrote:
Some of the values in the default register cache did not represent the codecs state after reset. This patch fixes it.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de
sound/soc/codecs/ssm2602.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index 30229cf..a09d66c 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -60,8 +60,8 @@ struct ssm2602_priv {
- There is no point in caching the reset register
*/ static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = {
- 0x0017, 0x0017, 0x0079, 0x0079,
- 0x0000, 0x0000, 0x0000, 0x000a,
- 0x0097, 0x0097, 0x0079, 0x0079,
- 0x000a, 0x0008, 0x009f, 0x000a, 0x0000, 0x0000
};
All
Acked-by: Liam Girdwood lrg@ti.com
On Sun, May 08, 2011 at 09:24:41AM -0700, Lars-Peter Clausen wrote:
Some of the values in the default register cache did not represent the codecs state after reset. This patch fixes it.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de
Applied all, thanks.
It is not required to have the codec powered at this stage and DAPM will power the ADC and DAC down again after probe has run anyway. Thus we avoid some unnecessary writes by this change.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/codecs/ssm2602.c | 3 --- 1 files changed, 0 insertions(+), 3 deletions(-)
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index a09d66c..e1ebf3d 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -509,8 +509,6 @@ static int ssm2602_probe(struct snd_soc_codec *codec) return ret; }
- /*power on device*/ - snd_soc_write(codec, SSM2602_ACTIVE, 0); /* set the update bits */ reg = snd_soc_read(codec, SSM2602_LINVOL); snd_soc_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH); @@ -523,7 +521,6 @@ static int ssm2602_probe(struct snd_soc_codec *codec) /*select Line in as default input*/ snd_soc_write(codec, SSM2602_APANA, APANA_SELECT_DAC | APANA_ENABLE_MIC_BOOST); - snd_soc_write(codec, SSM2602_PWR, 0);
snd_soc_add_controls(codec, ssm2602_snd_controls, ARRAY_SIZE(ssm2602_snd_controls));
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 --- 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 e1ebf3d..b0306cf 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -45,12 +45,19 @@
#define SSM2602_VERSION "0.1"
+enum ssm2602_type { + SSM2602, + SSM2604, +}; + /* codec private data */ struct ssm2602_priv { unsigned int sysclk; enum snd_soc_control_type control_type; struct snd_pcm_substream *master_substream; struct snd_pcm_substream *slave_substream; + + enum ssm2602_type type; };
/* @@ -80,90 +87,97 @@ static const struct soc_enum ssm2602_enum[] = { SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, 4, ssm2602_deemph), };
-static const struct snd_kcontrol_new ssm2602_snd_controls[] = { +static const struct snd_kcontrol_new ssm260x_snd_controls[] = { +SOC_DOUBLE_R("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 31, 0), +SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1),
+SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1), +SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0), + +SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]), +}; + +static const struct snd_kcontrol_new ssm2602_snd_controls[] = { SOC_DOUBLE_R("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V, 0, 127, 0), SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V, 7, 1, 0),
-SOC_DOUBLE_R("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 31, 0), -SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1), +SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1),
SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0), SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0), SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1), - -SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1), - -SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1), -SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0), - -SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]), };
/* Output Mixer */ -static const struct snd_kcontrol_new ssm2602_output_mixer_controls[] = { +static const struct snd_kcontrol_new ssm260x_output_mixer_controls[] = { SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0), -SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0), SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0), +SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0), };
/* Input mux */ static const struct snd_kcontrol_new ssm2602_input_mux_controls = SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]);
-static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = { -SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1, - &ssm2602_output_mixer_controls[0], - ARRAY_SIZE(ssm2602_output_mixer_controls)), +static const struct snd_soc_dapm_widget ssm260x_dapm_widgets[] = { SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1), +SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1), +SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0), + SND_SOC_DAPM_OUTPUT("LOUT"), -SND_SOC_DAPM_OUTPUT("LHPOUT"), SND_SOC_DAPM_OUTPUT("ROUT"), -SND_SOC_DAPM_OUTPUT("RHPOUT"), -SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1), +SND_SOC_DAPM_INPUT("RLINEIN"), +SND_SOC_DAPM_INPUT("LLINEIN"), +}; + +static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = { +SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1, + ssm260x_output_mixer_controls, + ARRAY_SIZE(ssm260x_output_mixer_controls)), + SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls), -SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0), SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1), + +SND_SOC_DAPM_OUTPUT("LHPOUT"), +SND_SOC_DAPM_OUTPUT("RHPOUT"), SND_SOC_DAPM_INPUT("MICIN"), -SND_SOC_DAPM_INPUT("RLINEIN"), -SND_SOC_DAPM_INPUT("LLINEIN"), };
-static const struct snd_soc_dapm_route audio_conn[] = { - /* output mixer */ +static const struct snd_soc_dapm_widget ssm2604_dapm_widgets[] = { +SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, + ssm260x_output_mixer_controls, + ARRAY_SIZE(ssm260x_output_mixer_controls) - 1), /* Last element is the mic */ +}; + +static const struct snd_soc_dapm_route ssm260x_routes[] = { {"Output Mixer", "Line Bypass Switch", "Line Input"}, {"Output Mixer", "HiFi Playback Switch", "DAC"}, + + {"ROUT", NULL, "Output Mixer"}, + {"LOUT", NULL, "Output Mixer"}, + + {"Line Input", NULL, "LLINEIN"}, + {"Line Input", NULL, "RLINEIN"}, +}; + +static const struct snd_soc_dapm_route ssm2602_routes[] = { {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
- /* outputs */ {"RHPOUT", NULL, "Output Mixer"}, - {"ROUT", NULL, "Output Mixer"}, {"LHPOUT", NULL, "Output Mixer"}, - {"LOUT", NULL, "Output Mixer"},
- /* input mux */ {"Input Mux", "Line", "Line Input"}, {"Input Mux", "Mic", "Mic Bias"}, {"ADC", NULL, "Input Mux"},
- /* inputs */ - {"Line Input", NULL, "LLINEIN"}, - {"Line Input", NULL, "RLINEIN"}, {"Mic Bias", NULL, "MICIN"}, };
-static int ssm2602_add_widgets(struct snd_soc_codec *codec) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_new_controls(dapm, ssm2602_dapm_widgets, - ARRAY_SIZE(ssm2602_dapm_widgets)); - snd_soc_dapm_add_routes(dapm, audio_conn, ARRAY_SIZE(audio_conn)); - - return 0; -} +static const struct snd_soc_dapm_route ssm2604_routes[] = { + {"ADC", NULL, "Line Input"}, +};
struct ssm2602_coeff { u32 mclk; @@ -492,8 +506,46 @@ static int ssm2602_resume(struct snd_soc_codec *codec)
static int ssm2602_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret, reg; + + reg = snd_soc_read(codec, SSM2602_LOUT1V); + snd_soc_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH); + reg = snd_soc_read(codec, SSM2602_ROUT1V); + snd_soc_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH); + + ret = snd_soc_add_controls(codec, ssm2602_snd_controls, + ARRAY_SIZE(ssm2602_snd_controls)); + if (ret) + return ret; + + ret = snd_soc_dapm_new_controls(dapm, ssm2602_dapm_widgets, + ARRAY_SIZE(ssm2602_dapm_widgets)); + if (ret) + return ret; + + return snd_soc_dapm_add_routes(dapm, ssm2602_routes, + ARRAY_SIZE(ssm2602_routes)); +} + +static int ssm2604_probe(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret; + + ret = snd_soc_dapm_new_controls(dapm, ssm2604_dapm_widgets, + ARRAY_SIZE(ssm2604_dapm_widgets)); + if (ret) + return ret; + + return snd_soc_dapm_add_routes(dapm, ssm2604_routes, + ARRAY_SIZE(ssm2604_routes)); +} + +static int ssm260x_probe(struct snd_soc_codec *codec) +{ struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); - int ret = 0, reg; + int ret, reg;
pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION);
@@ -514,19 +566,20 @@ static int ssm2602_probe(struct snd_soc_codec *codec) snd_soc_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH); reg = snd_soc_read(codec, SSM2602_RINVOL); snd_soc_write(codec, SSM2602_RINVOL, reg | RINVOL_RLIN_BOTH); - reg = snd_soc_read(codec, SSM2602_LOUT1V); - snd_soc_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH); - reg = snd_soc_read(codec, SSM2602_ROUT1V); - snd_soc_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH); /*select Line in as default input*/ snd_soc_write(codec, SSM2602_APANA, APANA_SELECT_DAC | APANA_ENABLE_MIC_BOOST);
- snd_soc_add_controls(codec, ssm2602_snd_controls, - ARRAY_SIZE(ssm2602_snd_controls)); - ssm2602_add_widgets(codec); + switch (ssm2602->type) { + case SSM2602: + ret = ssm2602_probe(codec); + break; + case SSM2604: + ret = ssm2604_probe(codec); + break; + }
- return 0; + return ret; }
/* remove everything here */ @@ -537,7 +590,7 @@ static int ssm2602_remove(struct snd_soc_codec *codec) }
static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = { - .probe = ssm2602_probe, + .probe = ssm260x_probe, .remove = ssm2602_remove, .suspend = ssm2602_suspend, .resume = ssm2602_resume, @@ -545,6 +598,13 @@ static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = { .reg_cache_size = ARRAY_SIZE(ssm2602_reg), .reg_word_size = sizeof(u16), .reg_cache_default = ssm2602_reg, + + .controls = ssm260x_snd_controls, + .num_controls = ARRAY_SIZE(ssm260x_snd_controls), + .dapm_widgets = ssm260x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ssm260x_dapm_widgets), + .dapm_routes = ssm260x_routes, + .num_dapm_routes = ARRAY_SIZE(ssm260x_routes), };
#if defined(CONFIG_SPI_MASTER) @@ -559,6 +619,7 @@ static int __devinit ssm2602_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, ssm2602); ssm2602->control_type = SND_SOC_SPI; + ssm2602->type = SSM2602;
ret = snd_soc_register_codec(&spi->dev, &soc_codec_dev_ssm2602, &ssm2602_dai, 1); @@ -603,6 +664,7 @@ static int __devinit ssm2602_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, ssm2602); ssm2602->control_type = SND_SOC_I2C; + ssm2602->type = id->driver_data;
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_ssm2602, &ssm2602_dai, 1); @@ -619,7 +681,8 @@ static int __devexit ssm2602_i2c_remove(struct i2c_client *client) }
static const struct i2c_device_id ssm2602_i2c_id[] = { - { "ssm2602", 0 }, + { "ssm2602", SSM2602 }, + { "ssm2604", SSM2604 }, { } }; MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id); @@ -669,6 +732,6 @@ static void __exit ssm2602_exit(void) } module_exit(ssm2602_exit);
-MODULE_DESCRIPTION("ASoC ssm2602 driver"); +MODULE_DESCRIPTION("ASoC SSM2602/SSM2604 driver"); MODULE_AUTHOR("Cliff Cai"); MODULE_LICENSE("GPL");
The SSM2603 is mostly register compatible with the SSM2602 and can be supported by the current driver without any changes.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/codecs/ssm2602.c | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index b0306cf..d828721 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -682,6 +682,7 @@ static int __devexit ssm2602_i2c_remove(struct i2c_client *client)
static const struct i2c_device_id ssm2602_i2c_id[] = { { "ssm2602", SSM2602 }, + { "ssm2603", SSM2602 }, { "ssm2604", SSM2604 }, { } }; @@ -732,6 +733,6 @@ static void __exit ssm2602_exit(void) } module_exit(ssm2602_exit);
-MODULE_DESCRIPTION("ASoC SSM2602/SSM2604 driver"); +MODULE_DESCRIPTION("ASoC SSM2602/SSM2603/SSM2604 driver"); MODULE_AUTHOR("Cliff Cai"); MODULE_LICENSE("GPL");
Model the power supply for the digital core as a DAPM_SUPPLY widget. This allows to cleanup the code a bit.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/codecs/ssm2602.c | 25 ++++++------------------- 1 files changed, 6 insertions(+), 19 deletions(-)
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index d828721..763d392 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -126,6 +126,8 @@ SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1), SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1), SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0),
+SND_SOC_DAPM_SUPPLY("Digital Core Power", SSM2602_ACTIVE, 0, 0, 0, 0), + SND_SOC_DAPM_OUTPUT("LOUT"), SND_SOC_DAPM_OUTPUT("ROUT"), SND_SOC_DAPM_INPUT("RLINEIN"), @@ -152,6 +154,9 @@ SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, };
static const struct snd_soc_dapm_route ssm260x_routes[] = { + {"DAC", NULL, "Digital Core Power"}, + {"ADC", NULL, "Digital Core Power"}, + {"Output Mixer", "Line Bypass Switch", "Line Input"}, {"Output Mixer", "HiFi Playback Switch", "DAC"},
@@ -252,7 +257,6 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream, if (srate < 0) return srate;
- snd_soc_write(codec, SSM2602_ACTIVE, 0); snd_soc_write(codec, SSM2602_SRATE, srate);
/* bit size */ @@ -270,7 +274,6 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream, break; } snd_soc_write(codec, SSM2602_IFACE, iface); - snd_soc_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC); return 0; }
@@ -312,17 +315,6 @@ static int ssm2602_startup(struct snd_pcm_substream *substream, return 0; }
-static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; - /* set active */ - snd_soc_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC); - - return 0; -} - static void ssm2602_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -330,16 +322,13 @@ static void ssm2602_shutdown(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = rtd->codec; struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
- /* deactivate */ - if (!codec->active) - snd_soc_write(codec, SSM2602_ACTIVE, 0); - if (ssm2602->master_substream == substream) ssm2602->master_substream = ssm2602->slave_substream;
ssm2602->slave_substream = NULL; }
+ static int ssm2602_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; @@ -446,7 +435,6 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_OFF: /* everything off, dac mute, inactive */ - snd_soc_write(codec, SSM2602_ACTIVE, 0); snd_soc_write(codec, SSM2602_PWR, 0xffff); break;
@@ -464,7 +452,6 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
static struct snd_soc_dai_ops ssm2602_dai_ops = { .startup = ssm2602_startup, - .prepare = ssm2602_pcm_prepare, .hw_params = ssm2602_hw_params, .shutdown = ssm2602_shutdown, .digital_mute = ssm2602_mute,
Also fix the maximum value for the capture volume control.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/codecs/ssm2602.c | 21 ++++++++++++++++----- 1 files changed, 16 insertions(+), 5 deletions(-)
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index 763d392..70099c9 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -40,6 +40,7 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/initval.h> +#include <sound/tlv.h>
#include "ssm2602.h"
@@ -87,8 +88,18 @@ static const struct soc_enum ssm2602_enum[] = { SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, 4, ssm2602_deemph), };
+static const unsigned int ssm260x_outmix_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 47, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0), + 48, 127, TLV_DB_SCALE_ITEM(-7400, 100, 0), +}; + +static const DECLARE_TLV_DB_SCALE(ssm260x_inpga_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(ssm260x_sidetone_tlv, -1500, 300, 0); + static const struct snd_kcontrol_new ssm260x_snd_controls[] = { -SOC_DOUBLE_R("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 31, 0), +SOC_DOUBLE_R_TLV("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 45, 0, + ssm260x_inpga_tlv), SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1),
SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1), @@ -98,12 +109,12 @@ SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]), };
static const struct snd_kcontrol_new ssm2602_snd_controls[] = { -SOC_DOUBLE_R("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V, - 0, 127, 0), +SOC_DOUBLE_R_TLV("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V, + 0, 127, 0, ssm260x_outmix_tlv), SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V, 7, 1, 0), - -SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1), +SOC_SINGLE_TLV("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1, + ssm260x_sidetone_tlv),
SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0), SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0),
participants (4)
-
Lars-Peter Clausen
-
Liam Girdwood
-
Mark Brown
-
Mike Frysinger