aboutsummaryrefslogtreecommitdiff
path: root/sound/soc/pxa/zylonite.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/pxa/zylonite.c')
-rw-r--r--sound/soc/pxa/zylonite.c132
1 files changed, 102 insertions, 30 deletions
diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c
index f8e9ecd589d..9a386b4c4ed 100644
--- a/sound/soc/pxa/zylonite.c
+++ b/sound/soc/pxa/zylonite.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
+#include <linux/clk.h>
#include <linux/i2c.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -26,6 +27,17 @@
#include "pxa2xx-ac97.h"
#include "pxa-ssp.h"
+/*
+ * There is a physical switch SW15 on the board which changes the MCLK
+ * for the WM9713 between the standard AC97 master clock and the
+ * output of the CLK_POUT signal from the PXA.
+ */
+static int clk_pout;
+module_param(clk_pout, int, 0);
+MODULE_PARM_DESC(clk_pout, "Use CLK_POUT as WM9713 MCLK (SW15 on board).");
+
+static struct clk *pout;
+
static struct snd_soc_card zylonite;
static const struct snd_soc_dapm_widget zylonite_dapm_widgets[] = {
@@ -61,10 +73,8 @@ static const struct snd_soc_dapm_route audio_map[] = {
static int zylonite_wm9713_init(struct snd_soc_codec *codec)
{
- /* Currently we only support use of the AC97 clock here. If
- * CLK_POUT is selected by SW15 then the clock API will need
- * to be used to request and enable it here.
- */
+ if (clk_pout)
+ snd_soc_dai_set_pll(&codec->dai[0], 0, clk_get_rate(pout), 0);
snd_soc_dapm_new_controls(codec, zylonite_dapm_widgets,
ARRAY_SIZE(zylonite_dapm_widgets));
@@ -86,40 +96,35 @@ static int zylonite_voice_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
unsigned int pll_out = 0;
- unsigned int acds = 0;
unsigned int wm9713_div = 0;
int ret = 0;
+ int rate = params_rate(params);
+ int width = snd_pcm_format_physical_width(params_format(params));
- switch (params_rate(params)) {
+ /* Only support ratios that we can generate neatly from the AC97
+ * based master clock - in particular, this excludes 44.1kHz.
+ * In most applications the voice DAC will be used for telephony
+ * data so multiples of 8kHz will be the common case.
+ */
+ switch (rate) {
case 8000:
wm9713_div = 12;
- pll_out = 2048000;
break;
case 16000:
wm9713_div = 6;
- pll_out = 4096000;
break;
case 48000:
- default:
wm9713_div = 2;
- pll_out = 12288000;
- acds = 1;
break;
+ default:
+ /* Don't support OSS emulation */
+ return -EINVAL;
}
- ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
- if (ret < 0)
- return ret;
+ /* Add 1 to the width for the leading clock cycle */
+ pll_out = rate * (width + 1) * 8;
- ret = snd_soc_dai_set_tdm_slot(cpu_dai,
- params_channels(params),
- params_channels(params));
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, 1);
if (ret < 0)
return ret;
@@ -127,19 +132,22 @@ static int zylonite_voice_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
- ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_AUDIO_DIV_ACDS, acds);
+ if (clk_pout)
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_PLL_DIV,
+ WM9713_PCMDIV(wm9713_div));
+ else
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_DIV,
+ WM9713_PCMDIV(wm9713_div));
if (ret < 0)
return ret;
- ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, 1);
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
- /* Note that if the PLL is in use the WM9713_PCMCLK_PLL_DIV needs
- * to be set instead.
- */
- ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_DIV,
- WM9713_PCMDIV(wm9713_div));
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
@@ -173,8 +181,72 @@ static struct snd_soc_dai_link zylonite_dai[] = {
},
};
+static int zylonite_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ if (clk_pout) {
+ pout = clk_get(NULL, "CLK_POUT");
+ if (IS_ERR(pout)) {
+ dev_err(&pdev->dev, "Unable to obtain CLK_POUT: %ld\n",
+ PTR_ERR(pout));
+ return PTR_ERR(pout);
+ }
+
+ ret = clk_enable(pout);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Unable to enable CLK_POUT: %d\n",
+ ret);
+ clk_put(pout);
+ return ret;
+ }
+
+ dev_dbg(&pdev->dev, "MCLK enabled at %luHz\n",
+ clk_get_rate(pout));
+ }
+
+ return 0;
+}
+
+static int zylonite_remove(struct platform_device *pdev)
+{
+ if (clk_pout) {
+ clk_disable(pout);
+ clk_put(pout);
+ }
+
+ return 0;
+}
+
+static int zylonite_suspend_post(struct platform_device *pdev,
+ pm_message_t state)
+{
+ if (clk_pout)
+ clk_disable(pout);
+
+ return 0;
+}
+
+static int zylonite_resume_pre(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ if (clk_pout) {
+ ret = clk_enable(pout);
+ if (ret != 0)
+ dev_err(&pdev->dev, "Unable to enable CLK_POUT: %d\n",
+ ret);
+ }
+
+ return ret;
+}
+
static struct snd_soc_card zylonite = {
.name = "Zylonite",
+ .probe = &zylonite_probe,
+ .remove = &zylonite_remove,
+ .suspend_post = &zylonite_suspend_post,
+ .resume_pre = &zylonite_resume_pre,
.platform = &pxa2xx_soc_platform,
.dai_link = zylonite_dai,
.num_links = ARRAY_SIZE(zylonite_dai),