diff options
Diffstat (limited to 'sound/soc/codecs/cs42xx8.c')
-rw-r--r-- | sound/soc/codecs/cs42xx8.c | 152 |
1 files changed, 119 insertions, 33 deletions
diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c index 462341fef5a9..7cbbe64e3509 100644 --- a/sound/soc/codecs/cs42xx8.c +++ b/sound/soc/codecs/cs42xx8.c @@ -1,7 +1,7 @@ /* * Cirrus Logic CS42448/CS42888 Audio CODEC Digital Audio Interface (DAI) driver * - * Copyright (C) 2014 Freescale Semiconductor, Inc. + * Copyright (C) 2014-2016 Freescale Semiconductor, Inc. * * Author: Nicolin Chen <Guangyu.Chen@freescale.com> * @@ -14,6 +14,7 @@ #include <linux/delay.h> #include <linux/module.h> #include <linux/of_device.h> +#include <linux/of_gpio.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <sound/pcm_params.h> @@ -32,8 +33,7 @@ static const char *const cs42xx8_supply_names[CS42XX8_NUM_SUPPLIES] = { #define CS42XX8_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S20_3LE | \ - SNDRV_PCM_FMTBIT_S24_LE | \ - SNDRV_PCM_FMTBIT_S32_LE) + SNDRV_PCM_FMTBIT_S24_LE) /* codec private data */ struct cs42xx8_priv { @@ -45,6 +45,8 @@ struct cs42xx8_priv { bool slave_mode; unsigned long sysclk; u32 tx_channels; + int rate[2]; + int reset_gpio; }; /* -127.5dB to 0dB with step of 0.5dB */ @@ -128,7 +130,6 @@ static const struct snd_soc_dapm_widget cs42xx8_dapm_widgets[] = { SND_SOC_DAPM_INPUT("AIN2L"), SND_SOC_DAPM_INPUT("AIN2R"), - SND_SOC_DAPM_SUPPLY("PWR", CS42XX8_PWRCTL, 0, 1, NULL, 0), }; static const struct snd_soc_dapm_widget cs42xx8_adc3_dapm_widgets[] = { @@ -142,53 +143,43 @@ static const struct snd_soc_dapm_route cs42xx8_dapm_routes[] = { /* Playback */ { "AOUT1L", NULL, "DAC1" }, { "AOUT1R", NULL, "DAC1" }, - { "DAC1", NULL, "PWR" }, { "AOUT2L", NULL, "DAC2" }, { "AOUT2R", NULL, "DAC2" }, - { "DAC2", NULL, "PWR" }, { "AOUT3L", NULL, "DAC3" }, { "AOUT3R", NULL, "DAC3" }, - { "DAC3", NULL, "PWR" }, { "AOUT4L", NULL, "DAC4" }, { "AOUT4R", NULL, "DAC4" }, - { "DAC4", NULL, "PWR" }, /* Capture */ { "ADC1", NULL, "AIN1L" }, { "ADC1", NULL, "AIN1R" }, - { "ADC1", NULL, "PWR" }, { "ADC2", NULL, "AIN2L" }, { "ADC2", NULL, "AIN2R" }, - { "ADC2", NULL, "PWR" }, }; static const struct snd_soc_dapm_route cs42xx8_adc3_dapm_routes[] = { /* Capture */ { "ADC3", NULL, "AIN3L" }, { "ADC3", NULL, "AIN3R" }, - { "ADC3", NULL, "PWR" }, }; struct cs42xx8_ratios { - unsigned int ratio; - unsigned char speed; - unsigned char mclk; + unsigned int mfreq; + unsigned int min_mclk; + unsigned int max_mclk; + unsigned int ratio[3]; }; static const struct cs42xx8_ratios cs42xx8_ratios[] = { - { 64, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_256(4) }, - { 96, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_384(4) }, - { 128, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_512(4) }, - { 192, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_768(4) }, - { 256, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_256(1) }, - { 384, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_384(1) }, - { 512, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_512(1) }, - { 768, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_768(1) }, - { 1024, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_1024(1) } + { 0, 1029000, 12800000, {256, 128, 64} }, + { 2, 1536000, 19200000, {384, 192, 96} }, + { 4, 2048000, 25600000, {512, 256, 128} }, + { 6, 3072000, 38400000, {768, 384, 192} }, + { 8, 4096000, 51200000, {1024, 512, 256} }, }; static int cs42xx8_set_dai_sysclk(struct snd_soc_dai *codec_dai, @@ -255,15 +246,70 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; - u32 ratio = cs42xx8->sysclk / params_rate(params); + u32 rate = params_rate(params); + u32 ratio_tx, ratio_rx; + u32 rate_tx, rate_rx; + u32 fm_tx, fm_rx; u32 i, fm, val, mask; if (tx) cs42xx8->tx_channels = params_channels(params); - for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) { - if (cs42xx8_ratios[i].ratio == ratio) - break; + rate_tx = tx ? rate : cs42xx8->rate[0]; + rate_rx = tx ? cs42xx8->rate[1] : rate; + + ratio_tx = rate_tx > 0 ? cs42xx8->sysclk / rate_tx : 0; + ratio_rx = rate_rx > 0 ? cs42xx8->sysclk / rate_rx : 0; + + if (cs42xx8->slave_mode) { + fm_rx = CS42XX8_FM_AUTO; + fm_tx = CS42XX8_FM_AUTO; + } else { + if (rate_tx < 50000) + fm_tx = CS42XX8_FM_SINGLE; + else if (rate_tx > 50000 && rate_tx < 100000) + fm_tx = CS42XX8_FM_DOUBLE; + else if (rate_tx > 100000 && rate_tx < 200000) + fm_tx = CS42XX8_FM_QUAD; + else { + dev_err(codec->dev, "unsupported sample rate or rate combine\n"); + return -EINVAL; + } + + if (rate_rx < 50000) + fm_rx = CS42XX8_FM_SINGLE; + else if (rate_rx > 50000 && rate_rx < 100000) + fm_rx = CS42XX8_FM_DOUBLE; + else if (rate_rx > 100000 && rate_rx < 200000) + fm_rx = CS42XX8_FM_QUAD; + else { + dev_err(codec->dev, "unsupported sample rate or rate combine\n"); + return -EINVAL; + } + } + + fm = tx ? fm_tx : fm_rx; + + if (fm == CS42XX8_FM_AUTO) { + for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) { + if ((ratio_tx > 0 ? (cs42xx8_ratios[i].ratio[0] == ratio_tx || + cs42xx8_ratios[i].ratio[1] == ratio_tx || + cs42xx8_ratios[i].ratio[2] == ratio_tx) : true) && + (ratio_rx > 0 ? (cs42xx8_ratios[i].ratio[0] == ratio_rx || + cs42xx8_ratios[i].ratio[1] == ratio_rx || + cs42xx8_ratios[i].ratio[2] == ratio_rx) : true) && + cs42xx8->sysclk >= cs42xx8_ratios[i].min_mclk && + cs42xx8->sysclk <= cs42xx8_ratios[i].max_mclk) + break; + } + } else { + for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) { + if ((ratio_tx > 0 ? (cs42xx8_ratios[i].ratio[fm_tx] == ratio_tx) : true) && + (ratio_rx > 0 ? (cs42xx8_ratios[i].ratio[fm_rx] == ratio_rx) : true) && + cs42xx8->sysclk >= cs42xx8_ratios[i].min_mclk && + cs42xx8->sysclk <= cs42xx8_ratios[i].max_mclk) + break; + } } if (i == ARRAY_SIZE(cs42xx8_ratios)) { @@ -271,10 +317,10 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - mask = CS42XX8_FUNCMOD_MFREQ_MASK; - val = cs42xx8_ratios[i].mclk; + cs42xx8->rate[substream->stream] = rate; - fm = cs42xx8->slave_mode ? CS42XX8_FM_AUTO : cs42xx8_ratios[i].speed; + mask = CS42XX8_FUNCMOD_MFREQ_MASK; + val = cs42xx8_ratios[i].mfreq; regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD, CS42XX8_FUNCMOD_xC_FM_MASK(tx) | mask, @@ -283,6 +329,22 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream, return 0; } +static int cs42xx8_hw_free(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; + struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + + cs42xx8->rate[substream->stream] = 0; + + regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD, + CS42XX8_FUNCMOD_xC_FM_MASK(tx), + CS42XX8_FUNCMOD_xC_FM(tx, CS42XX8_FM_AUTO)); + return 0; +} + static int cs42xx8_digital_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; @@ -300,6 +362,7 @@ static const struct snd_soc_dai_ops cs42xx8_dai_ops = { .set_fmt = cs42xx8_set_dai_fmt, .set_sysclk = cs42xx8_set_dai_sysclk, .hw_params = cs42xx8_hw_params, + .hw_free = cs42xx8_hw_free, .digital_mute = cs42xx8_digital_mute, }; @@ -321,7 +384,6 @@ static struct snd_soc_dai_driver cs42xx8_dai = { }; static const struct reg_default cs42xx8_reg[] = { - { 0x01, 0x01 }, /* Chip I.D. and Revision Register */ { 0x02, 0x00 }, /* Power Control */ { 0x03, 0xF0 }, /* Functional Mode */ { 0x04, 0x46 }, /* Interface Formats */ @@ -403,7 +465,8 @@ static int cs42xx8_codec_probe(struct snd_soc_codec *codec) /* Mute all DAC channels */ regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE, CS42XX8_DACMUTE_ALL); - + regmap_update_bits(cs42xx8->regmap, CS42XX8_PWRCTL, + CS42XX8_PWRCTL_PDN_MASK, 0); return 0; } @@ -443,7 +506,8 @@ EXPORT_SYMBOL_GPL(cs42xx8_of_match); int cs42xx8_probe(struct device *dev, struct regmap *regmap) { - const struct of_device_id *of_id; + const struct of_device_id *of_id = of_match_device(cs42xx8_of_match, dev); + struct device_node *np = dev->of_node; struct cs42xx8_priv *cs42xx8; int ret, val, i; @@ -469,6 +533,17 @@ int cs42xx8_probe(struct device *dev, struct regmap *regmap) return -EINVAL; } + cs42xx8->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (gpio_is_valid(cs42xx8->reset_gpio)) { + ret = devm_gpio_request_one(dev, cs42xx8->reset_gpio, + GPIOF_OUT_INIT_LOW, "cs42xx8 reset"); + if (ret) { + dev_err(dev, "unable to get reset gpio\n"); + return ret; + } + gpio_set_value_cansleep(cs42xx8->reset_gpio, 1); + } + cs42xx8->clk = devm_clk_get(dev, "mclk"); if (IS_ERR(cs42xx8->clk)) { dev_err(dev, "failed to get the clock: %ld\n", @@ -558,6 +633,11 @@ static int cs42xx8_runtime_resume(struct device *dev) return ret; } + if (gpio_is_valid(cs42xx8->reset_gpio)) { + gpio_set_value_cansleep(cs42xx8->reset_gpio, 0); + gpio_set_value_cansleep(cs42xx8->reset_gpio, 1); + } + ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies), cs42xx8->supplies); if (ret) { @@ -565,9 +645,14 @@ static int cs42xx8_runtime_resume(struct device *dev) goto err_clk; } + regmap_update_bits(cs42xx8->regmap, CS42XX8_PWRCTL, + CS42XX8_PWRCTL_PDN_MASK, 1); /* Make sure hardware reset done */ msleep(5); + regmap_update_bits(cs42xx8->regmap, CS42XX8_PWRCTL, + CS42XX8_PWRCTL_PDN_MASK, 0); + regcache_cache_only(cs42xx8->regmap, false); regcache_mark_dirty(cs42xx8->regmap); @@ -604,6 +689,7 @@ static int cs42xx8_runtime_suspend(struct device *dev) #endif const struct dev_pm_ops cs42xx8_pm = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) SET_RUNTIME_PM_OPS(cs42xx8_runtime_suspend, cs42xx8_runtime_resume, NULL) }; EXPORT_SYMBOL_GPL(cs42xx8_pm); |