From c910cc36e7b73099b6710bf3179e5667cd241a81 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Mon, 29 Sep 2014 10:56:03 +0800 Subject: [PATCH] MLK-9723-4: ASoC: fsl_mqs: add mqs codec driver Implement codec driver for mqs. mqs is a very simple IP. which support: Word length: 16bit. DAI format: Left-Justified, slave mode. Signed-off-by: Shengjiu Wang (cherry picked from commit 9da6bdd2072b850e9bb910512123eff7d80a0e2f) --- .../devicetree/bindings/sound/fsl,mqs.txt | 22 ++ sound/soc/codecs/Kconfig | 3 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/fsl_mqs.c | 229 ++++++++++++++++++ 4 files changed, 256 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/fsl,mqs.txt create mode 100644 sound/soc/codecs/fsl_mqs.c diff --git a/Documentation/devicetree/bindings/sound/fsl,mqs.txt b/Documentation/devicetree/bindings/sound/fsl,mqs.txt new file mode 100644 index 000000000000..7c288461e9ce --- /dev/null +++ b/Documentation/devicetree/bindings/sound/fsl,mqs.txt @@ -0,0 +1,22 @@ +fsl,mqs audio CODEC + +Required properties: + + - compatible : must contain one of "fsl,imx6sx-mqs" and "fsl,codec-mqs" + + - clocks : a list of phandles + clock-specifiers, one for each entry in + clock-names + + - clock-names : must contain "mclk" + + - gpr : the gpr node. + +Example: + +mqs: mqs { + compatible = "fsl,imx6sx-mqs"; + gpr = <&gpr>; + clocks = <&clks IMX6SX_CLK_SAI1>; + clock-names = "mclk"; + status = "disabled"; +}; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index a79c0d141f90..86513b0b1659 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -293,6 +293,9 @@ config SND_SOC_BT_SCO config SND_SOC_DMIC tristate +config SND_SOC_FSL_MQS + tristate + config SND_SOC_ISABELLE tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index cfe5d634c812..2deeb147067d 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -32,6 +32,7 @@ snd-soc-da732x-objs := da732x.o snd-soc-da9055-objs := da9055.o snd-soc-bt-sco-objs := bt-sco.o snd-soc-dmic-objs := dmic.o +snd-soc-fsl-mqs-objs := fsl_mqs.o snd-soc-isabelle-objs := isabelle.o snd-soc-jz4740-codec-objs := jz4740.o snd-soc-l3-objs := l3.o @@ -167,6 +168,7 @@ obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o +obj-$(CONFIG_SND_SOC_FSL_MQS) += snd-soc-fsl-mqs.o obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o diff --git a/sound/soc/codecs/fsl_mqs.c b/sound/soc/codecs/fsl_mqs.c new file mode 100644 index 000000000000..244f422d4898 --- /dev/null +++ b/sound/soc/codecs/fsl_mqs.c @@ -0,0 +1,229 @@ +/* + * ALSA SoC IMX MQS driver + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* codec private data */ +struct fsl_mqs { + struct platform_device *pdev; + struct regmap *gpr; + struct clk *mclk; + + unsigned long mclk_rate; + + int sysclk_rate; + int bclk; + int lrclk; + char name[32]; +}; + +#define FSL_MQS_RATES SNDRV_PCM_RATE_8000_192000 +#define FSL_MQS_FORMATS SNDRV_PCM_FMTBIT_S16_LE + +static int fsl_mqs_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct fsl_mqs *mqs_priv = snd_soc_codec_get_drvdata(codec); + int div, res; + + mqs_priv->bclk = snd_soc_params_to_bclk(params); + mqs_priv->lrclk = params_rate(params); + + /* + * mclk_rate / (oversample(32,64) * FS * 2 * divider ) = repeat_rate; + * if repeat_rate is 8, mqs can achieve better quality. + * oversample rate is fix to 32 currently. + */ + div = mqs_priv->mclk_rate / (32 * 2 * mqs_priv->lrclk * 8); + res = mqs_priv->mclk_rate % (32 * 2 * mqs_priv->lrclk * 8); + + if (res == 0 && div > 0 && div <= 256) { + regmap_update_bits(mqs_priv->gpr, IOMUXC_GPR2, + IMX6SX_GPR2_MQS_CLK_DIV_MASK, (div-1) << IMX6SX_GPR2_MQS_CLK_DIV_SHIFT); + regmap_update_bits(mqs_priv->gpr, IOMUXC_GPR2, + IMX6SX_GPR2_MQS_OVERSAMPLE_MASK, 0 << IMX6SX_GPR2_MQS_OVERSAMPLE_SHIFT); + } else + dev_err(&mqs_priv->pdev->dev, "can't get proper divider\n"); + + return 0; +} + +static int fsl_mqs_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int fsl_mqs_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct fsl_mqs *mqs_priv = snd_soc_codec_get_drvdata(codec); + + mqs_priv->sysclk_rate = freq; + + return 0; +} + +static int fsl_mqs_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct fsl_mqs *mqs_priv = snd_soc_codec_get_drvdata(codec); + + regmap_update_bits(mqs_priv->gpr, IOMUXC_GPR2, IMX6SX_GPR2_MQS_EN_MASK, + 1 << IMX6SX_GPR2_MQS_EN_SHIFT); + return 0; +} + +static void fsl_mqs_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct fsl_mqs *mqs_priv = snd_soc_codec_get_drvdata(codec); + + regmap_update_bits(mqs_priv->gpr, IOMUXC_GPR2, + IMX6SX_GPR2_MQS_EN_MASK, 0); + +} + + +static struct snd_soc_codec_driver soc_codec_fsl_mqs; + +static const struct snd_soc_dai_ops fsl_mqs_dai_ops = { + .startup = fsl_mqs_startup, + .shutdown = fsl_mqs_shutdown, + .hw_params = fsl_mqs_hw_params, + .set_fmt = fsl_mqs_set_dai_fmt, + .set_sysclk = fsl_mqs_set_dai_sysclk, +}; + +static struct snd_soc_dai_driver fsl_mqs_dai = { + .name = "fsl-mqs-dai", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = FSL_MQS_RATES, + .formats = FSL_MQS_FORMATS, + }, + .ops = &fsl_mqs_dai_ops, +}; + +static int fsl_mqs_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *gpr_np; + struct fsl_mqs *mqs_priv; + int ret = 0; + + mqs_priv = devm_kzalloc(&pdev->dev, sizeof(*mqs_priv), GFP_KERNEL); + if (!mqs_priv) + return -ENOMEM; + + mqs_priv->pdev = pdev; + strcpy(mqs_priv->name, np->name); + + gpr_np = of_parse_phandle(np, "gpr", 0); + if (IS_ERR(gpr_np)) { + dev_err(&pdev->dev, "failed to get gpr node by phandle\n"); + ret = PTR_ERR(gpr_np); + goto out; + } + + mqs_priv->gpr = syscon_node_to_regmap(gpr_np); + if (IS_ERR(mqs_priv->gpr)) { + dev_err(&pdev->dev, "failed to get gpr regmap\n"); + ret = PTR_ERR(mqs_priv->gpr); + goto out; + } + + mqs_priv->mclk = devm_clk_get(&pdev->dev, "mclk"); + if (IS_ERR(mqs_priv->mclk)) { + dev_err(&pdev->dev, "failed to get the clock: %ld\n", + PTR_ERR(mqs_priv->mclk)); + goto out; + } + + mqs_priv->mclk_rate = clk_get_rate(mqs_priv->mclk); + + dev_set_drvdata(&pdev->dev, mqs_priv); + + return snd_soc_register_codec(&pdev->dev, &soc_codec_fsl_mqs, + &fsl_mqs_dai, 1); + +out: + if (!IS_ERR(gpr_np)) + of_node_put(gpr_np); + + return ret; +} + +static int fsl_mqs_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static const struct of_device_id fsl_mqs_dt_ids[] = { + { .compatible = "fsl,imx6sx-mqs", }, + {} +}; +MODULE_DEVICE_TABLE(of, fsl_mqs_dt_ids); + + +static struct platform_driver fsl_mqs_driver = { + .probe = fsl_mqs_probe, + .remove = fsl_mqs_remove, + .driver = { + .name = "fsl-mqs", + .owner = THIS_MODULE, + .of_match_table = fsl_mqs_dt_ids, + }, +}; + +module_platform_driver(fsl_mqs_driver); + +MODULE_AUTHOR("shengjiu wang "); +MODULE_DESCRIPTION("MQS dummy codec driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform: fsl-mqs"); -- 2.39.2