diff options
Diffstat (limited to 'bsp/meta-freescale-3rdparty/recipes-kernel/linux/linux-compulab-3.14.28/0015-ARM-i.MX6-ASoC-add-imx-wm8731-machine-driver.patch')
-rw-r--r-- | bsp/meta-freescale-3rdparty/recipes-kernel/linux/linux-compulab-3.14.28/0015-ARM-i.MX6-ASoC-add-imx-wm8731-machine-driver.patch | 573 |
1 files changed, 0 insertions, 573 deletions
diff --git a/bsp/meta-freescale-3rdparty/recipes-kernel/linux/linux-compulab-3.14.28/0015-ARM-i.MX6-ASoC-add-imx-wm8731-machine-driver.patch b/bsp/meta-freescale-3rdparty/recipes-kernel/linux/linux-compulab-3.14.28/0015-ARM-i.MX6-ASoC-add-imx-wm8731-machine-driver.patch deleted file mode 100644 index f3d4befd..00000000 --- a/bsp/meta-freescale-3rdparty/recipes-kernel/linux/linux-compulab-3.14.28/0015-ARM-i.MX6-ASoC-add-imx-wm8731-machine-driver.patch +++ /dev/null @@ -1,573 +0,0 @@ -From 5fbe55d2945782fee3f3238f20a8dcd0b9ba630c Mon Sep 17 00:00:00 2001 -From: Valentin Raevsky <valentin@compulab.co.il> -Date: Sun, 20 Jul 2014 11:10:12 +0300 -Subject: [PATCH 15/59] ARM: i.MX6: ASoC: add imx-wm8731 machine driver - -This is the initial imx-wm8731 device-tree-only machine driver working with -fsl_ssi driver. Works in the slave mode. - -Signed-off-by: Valentin Raevsky <valentin@compulab.co.il> ---- - sound/soc/fsl/Kconfig | 12 ++ - sound/soc/fsl/Makefile | 2 + - sound/soc/fsl/imx-wm8731.c | 505 ++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 519 insertions(+) - create mode 100644 sound/soc/fsl/imx-wm8731.c - -diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig -index 573dabb..bb229da 100644 ---- a/sound/soc/fsl/Kconfig -+++ b/sound/soc/fsl/Kconfig -@@ -252,6 +252,18 @@ config SND_SOC_IMX_CS42888 - Say Y if you want to add support for SoC audio on an i.MX board with - a cs42888 codec. - -+config SND_SOC_IMX_WM8731 -+ tristate "SoC Audio support for i.MX boards with wm8731" -+ depends on OF && I2C -+ select SND_SOC_WM8731 -+ select SND_SOC_IMX_PCM_DMA -+ select SND_SOC_IMX_AUDMUX -+ select SND_SOC_FSL_SSI -+ select SND_SOC_FSL_UTILS -+ help -+ Say Y if you want to add support for SoC audio on an i.MX board with -+ a wm8731 codec. -+ - config SND_SOC_IMX_WM8962 - tristate "SoC Audio support for i.MX boards with wm8962" - depends on OF && I2C -diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile -index 144cd69..65f8e6f 100644 ---- a/sound/soc/fsl/Makefile -+++ b/sound/soc/fsl/Makefile -@@ -55,6 +55,7 @@ snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o - snd-soc-wm1133-ev1-objs := wm1133-ev1.o - snd-soc-imx-cs42888-objs := imx-cs42888.o - snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o -+snd-soc-imx-wm8731-objs := imx-wm8731.o - snd-soc-imx-wm8962-objs := imx-wm8962.o - snd-soc-imx-spdif-objs := imx-spdif.o - snd-soc-imx-mc13783-objs := imx-mc13783.o -@@ -69,6 +70,7 @@ obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o - obj-$(CONFIG_SND_SOC_IMX_CS42888) += snd-soc-imx-cs42888.o - obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o - obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o -+obj-$(CONFIG_SND_SOC_IMX_WM8731) += snd-soc-imx-wm8731.o - obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o - obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o - obj-$(CONFIG_SND_SOC_IMX_HDMI) += snd-soc-imx-hdmi.o -diff --git a/sound/soc/fsl/imx-wm8731.c b/sound/soc/fsl/imx-wm8731.c -new file mode 100644 -index 0000000..ba1363f ---- /dev/null -+++ b/sound/soc/fsl/imx-wm8731.c -@@ -0,0 +1,505 @@ -+/* -+ * Copyright (C) 2014 Freescale Semiconductor, Inc. -+ * -+ * Based on imx-sgtl5000.c -+ * Copyright (C) 2012 Freescale Semiconductor, Inc. -+ * Copyright (C) 2012 Linaro Ltd. -+ * -+ * The code contained herein is licensed under the GNU General Public -+ * License. You may obtain a copy of the GNU General Public License -+ * Version 2 or later at the following locations: -+ * -+ * http://www.opensource.org/licenses/gpl-license.html -+ * http://www.gnu.org/copyleft/gpl.html -+ */ -+ -+#include <linux/module.h> -+#include <linux/of_platform.h> -+#include <linux/of_i2c.h> -+#include <linux/of_gpio.h> -+#include <linux/slab.h> -+#include <linux/gpio.h> -+#include <linux/clk.h> -+#include <sound/soc.h> -+#include <sound/jack.h> -+#include <sound/pcm_params.h> -+#include <sound/soc-dapm.h> -+#include <linux/pinctrl/consumer.h> -+ -+#include "../codecs/wm8731.h" -+#include "imx-audmux.h" -+#include "imx-ssi.h" -+ -+#define DAI_NAME_SIZE 32 -+ -+struct imx_wm8731_data { -+ struct snd_soc_dai_link dai; -+ struct snd_soc_card card; -+ char codec_dai_name[DAI_NAME_SIZE]; -+ char platform_name[DAI_NAME_SIZE]; -+ struct i2c_client *codec_dev; -+ /* audio_clocking_data */ -+ struct clk *pll; -+ struct clk *clock_root; -+ long sysclk; -+ long current_rate; -+ /* platfor data */ -+ unsigned int ssi_num; -+ unsigned int src_port; -+ unsigned int ext_port; -+}; -+ -+static int imx_wm8731_init(struct snd_soc_pcm_runtime *rtd); -+static int imx_hifi_hw_params_slv_mode(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params); -+static void imx_hifi_shutdown(struct snd_pcm_substream *substream); -+ -+struct imx_priv { -+ struct platform_device *pdev; -+ struct imx_wm8731_data *data; -+}; -+ -+static struct imx_priv card_priv; -+ -+static struct snd_soc_ops imx_hifi_ops = { -+ .shutdown = imx_hifi_shutdown, -+ .hw_params = imx_hifi_hw_params_slv_mode, -+}; -+ -+/* imx card dapm widgets */ -+static const struct snd_soc_dapm_widget imx_dapm_widgets[] = { -+ SND_SOC_DAPM_HP("Headphone Jack", NULL), -+ SND_SOC_DAPM_SPK("Ext Spk", NULL), -+ SND_SOC_DAPM_LINE("Line Jack", NULL), -+ SND_SOC_DAPM_MIC("Mic Jack", NULL), -+}; -+ -+/* imx machine connections to the codec pins */ -+static const struct snd_soc_dapm_route audio_map[] = { -+ { "Headphone Jack", NULL, "LHPOUT" }, -+ { "Headphone Jack", NULL, "RHPOUT" }, -+ -+ { "Ext Spk", NULL, "LOUT" }, -+ { "Ext Spk", NULL, "ROUT" }, -+ -+ { "LLINEIN", NULL, "Line Jack" }, -+ { "RLINEIN", NULL, "Line Jack" }, -+ -+ { "MICIN", NULL, "Mic Bias" }, -+ { "Mic Bias", NULL, "Mic Jack"}, -+}; -+ -+static int wm8731_slv_mode_init(struct imx_wm8731_data *data) -+{ -+ struct clk *new_parent; -+ struct clk *ssi_clk; -+ struct i2c_client *codec_dev = data->codec_dev; -+ -+ new_parent = devm_clk_get(&codec_dev->dev, "pll4"); -+ if (IS_ERR(new_parent)) { -+ pr_err("Could not get \"pll4\" clock \n"); -+ return PTR_ERR(new_parent); -+ } -+ -+ ssi_clk = devm_clk_get(&codec_dev->dev, "imx-ssi.1"); -+ if (IS_ERR(ssi_clk)) { -+ pr_err("Could not get \"imx-ssi.1\" clock \n"); -+ return PTR_ERR(ssi_clk); -+ } -+ -+ clk_set_parent(ssi_clk, new_parent); -+ -+ data->pll = new_parent; -+ data->clock_root = ssi_clk; -+ data->current_rate = 0; -+ -+ data->sysclk = 0; -+ -+ return 0; -+} -+ -+static int wm8731_slv_mode_clock_enable(int enable, struct imx_wm8731_data *data) -+{ -+ long pll_rate; -+ long rate_req; -+ long rate_avail; -+ -+ if (!enable) -+ return 0; -+ -+ if (data->sysclk == data->current_rate) -+ return 0; -+ -+ switch (data->sysclk) { -+ case 11289600: -+ pll_rate = 632217600; -+ break; -+ -+ case 12288000: -+ pll_rate = 688128000; -+ break; -+ -+ default: -+ return -EINVAL; -+ } -+ -+ rate_req = pll_rate; -+ rate_avail = clk_round_rate(data->pll, rate_req); -+ clk_set_rate(data->pll, rate_avail); -+ -+ rate_req = data->sysclk; -+ rate_avail = clk_round_rate(data->clock_root, -+ rate_req); -+ clk_set_rate(data->clock_root, rate_avail); -+ -+ pr_info("%s: \"imx-ssi.1\" rate = %ld (= %ld)\n", -+ __func__, rate_avail, rate_req); -+ -+ data->current_rate = data->sysclk; -+ -+ return 0; -+} -+ -+static int imx_hifi_hw_params_slv_mode(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params) -+{ -+ struct snd_soc_pcm_runtime *rtd = substream->private_data; -+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai; -+ struct snd_soc_dai *codec_dai = rtd->codec_dai; -+ struct snd_soc_card *card = codec_dai->codec->card; -+ struct imx_wm8731_data *data = snd_soc_card_get_drvdata(card); -+ -+ u32 dai_format, pll_out; -+ snd_pcm_format_t sample_format; -+ unsigned int channels; -+ unsigned int tx_mask, rx_mask; -+ unsigned int sampling_rate; -+ unsigned int div_2, div_psr, div_pm; -+ int ret; -+ -+ sampling_rate = params_rate(params); -+ sample_format = params_format(params); -+ -+ channels = params_channels(params); -+ printk("%s:%s sampling rate = %u channels = %u \n", __FUNCTION__, -+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "Playback" : "Capture"), -+ sampling_rate, channels); -+ -+ /* set CPU DAI configuration */ -+ switch (sampling_rate) { -+ case 8000: -+ case 32000: -+ case 48000: -+ case 96000: -+ data->sysclk = 12288000; -+ break; -+ -+ case 44100: -+ case 88200: -+ data->sysclk = 11289600; -+ break; -+ -+ default: -+ return -EINVAL; -+ } -+ -+ wm8731_slv_mode_clock_enable(1,data); -+ -+ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF | -+ SND_SOC_DAIFMT_CBS_CFS; -+ -+ ret = snd_soc_dai_set_fmt(cpu_dai, dai_format); -+ if (ret < 0) -+ return ret; -+ -+ /* set i.MX active slot mask */ -+ /* S[TR]CCR:DC */ -+ tx_mask = ~((1 << channels) - 1); -+ rx_mask = tx_mask; -+ snd_soc_dai_set_tdm_slot(cpu_dai, tx_mask, rx_mask, 2, 32); -+ -+ /* -+ * SSI sysclk divider: -+ * div_2: /1 or /2 -+ * div_psr: /1 or /8 -+ * div_pm: /1 .. /256 -+ */ -+ div_2 = 0; -+ div_psr = 0; -+ switch (sampling_rate) { -+ case 8000: -+ // 1x1x12 -+ div_pm = 11; -+ break; -+ case 32000: -+ // 1x1x3 -+ div_pm = 2; -+ break; -+ case 48000: -+ // 1x1x2 -+ div_pm = 1; -+ break; -+ case 96000: -+ // 1x1x1 -+ div_pm = 0; -+ break; -+ case 44100: -+ // 1x1x2 -+ div_pm = 1; -+ break; -+ case 88200: -+ // 1x1x1 -+ div_pm = 0; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ /* sync mode: a single clock controls both playback and capture */ -+ snd_soc_dai_set_clkdiv(cpu_dai, IMX_SSI_TX_DIV_2, (div_2 ? SSI_STCCR_DIV2 : 0)); -+ snd_soc_dai_set_clkdiv(cpu_dai, IMX_SSI_TX_DIV_PSR, (div_psr ? SSI_STCCR_PSR : 0)); -+ snd_soc_dai_set_clkdiv(cpu_dai, IMX_SSI_TX_DIV_PM, div_pm); -+ -+ /* set codec DAI configuration */ -+ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | -+ SND_SOC_DAIFMT_CBS_CFS; -+ -+ ret = snd_soc_dai_set_fmt(codec_dai, dai_format); -+ if (ret < 0) -+ return ret; -+ -+ ret = snd_soc_dai_set_sysclk(codec_dai, -+ WM8731_SYSCLK_MCLK, -+ data->sysclk, -+ SND_SOC_CLOCK_IN); -+ -+ if (ret < 0) { -+ pr_err("Failed to set codec master clock to %u: %d \n", -+ data->sysclk, ret); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static void imx_hifi_shutdown(struct snd_pcm_substream *substream) -+{ -+ struct snd_soc_pcm_runtime *rtd = substream->private_data; -+ struct snd_soc_dai *codec_dai = rtd->codec_dai; -+ struct snd_soc_card *card = codec_dai->codec->card; -+ struct imx_wm8731_data *data = snd_soc_card_get_drvdata(card); -+ -+ if (!codec_dai->active) -+ wm8731_slv_mode_clock_enable(0,data); -+ -+ return; -+} -+ -+static int imx_wm8731_init(struct snd_soc_pcm_runtime *rtd) -+{ -+ int ret = 0; -+ struct snd_soc_codec *codec = rtd->codec; -+ -+ /* Add imx specific widgets */ -+ ret = snd_soc_dapm_new_controls(&codec->dapm, imx_dapm_widgets, -+ ARRAY_SIZE(imx_dapm_widgets)); -+ if (ret) -+ goto out_retcode; -+ -+ /* Set up imx specific audio path audio_map */ -+ ret = snd_soc_dapm_add_routes(&codec->dapm, audio_map, ARRAY_SIZE(audio_map)); -+ if (ret) -+ goto out_retcode; -+ -+ ret = snd_soc_dapm_enable_pin(&codec->dapm, "Headphone Jack"); -+ if (ret) -+ goto out_retcode; -+ -+ ret = snd_soc_dapm_nc_pin(&codec->dapm, "Ext Spk"); -+ if (ret) -+ goto out_retcode; -+ -+out_retcode: -+ -+ if (ret) -+ pr_err("%s: failed with error code: %d \n", __FUNCTION__, ret); -+ else -+ pr_info("%s: success \n", __FUNCTION__); -+ -+ return ret; -+} -+ -+/** -+ * Configure AUDMUX interconnection between -+ * _slave (CPU side) and _master (codec size) -+ * -+ * When SSI operates in master mode, 5-wire interconnect with -+ * audio codec is required: -+ * TXC - BCLK -+ * TXD - DAC data -+ * RXD - ADC data -+ * TXFS - {DAC|ADC}LRC, i.e. word clock -+ * RXC - MCLK, i.e. oversampling clock -+ * Audmux is operated in asynchronous mode to enable 6-wire -+ * interface (as opposed to 4-wire interface in sync mode). -+ */ -+static int imx_audmux_config_slv_mode(int _slave, int _master) -+{ -+ unsigned int ptcr, pdcr; -+ int slave = _slave - 1; -+ int master = _master - 1; -+ -+ ptcr = IMX_AUDMUX_V2_PTCR_SYN | -+ IMX_AUDMUX_V2_PTCR_TFSDIR | -+ IMX_AUDMUX_V2_PTCR_TFSEL(slave) | -+ IMX_AUDMUX_V2_PTCR_RCLKDIR | -+ IMX_AUDMUX_V2_PTCR_RCSEL(slave | 0x8) | -+ IMX_AUDMUX_V2_PTCR_TCLKDIR | -+ IMX_AUDMUX_V2_PTCR_TCSEL(slave); -+ -+ pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(slave); -+ imx_audmux_v2_configure_port(master, ptcr, pdcr); -+ ptcr = ptcr & ~IMX_AUDMUX_V2_PTCR_SYN; -+ imx_audmux_v2_configure_port(master, ptcr, pdcr); -+ -+ ptcr = IMX_AUDMUX_V2_PTCR_SYN | -+ IMX_AUDMUX_V2_PTCR_RCLKDIR | -+ IMX_AUDMUX_V2_PTCR_RCSEL(master | 0x8) | -+ IMX_AUDMUX_V2_PTCR_TCLKDIR | -+ IMX_AUDMUX_V2_PTCR_TCSEL(master); -+ -+ pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(master); -+ imx_audmux_v2_configure_port(slave, ptcr, pdcr); -+ ptcr = ptcr & ~IMX_AUDMUX_V2_PTCR_SYN; -+ imx_audmux_v2_configure_port(slave, ptcr, pdcr); -+ -+ return 0; -+} -+ -+static int imx_wm8731_probe(struct platform_device *pdev) -+{ -+ struct device_node *ssi_np, *codec_np; -+ struct platform_device *ssi_pdev; -+ struct imx_priv *priv = &card_priv; -+ struct i2c_client *codec_dev; -+ struct imx_wm8731_data *data; -+ int ret; -+ -+ priv->pdev = pdev; -+ -+ ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0); -+ codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0); -+ if (!ssi_np || !codec_np) { -+ dev_err(&pdev->dev, "phandle missing or invalid\n"); -+ ret = -EINVAL; -+ goto fail; -+ } -+ -+ ssi_pdev = of_find_device_by_node(ssi_np); -+ if (!ssi_pdev) { -+ dev_err(&pdev->dev, "failed to find SSI platform device\n"); -+ ret = -EINVAL; -+ goto fail; -+ } -+ -+ codec_dev = of_find_i2c_device_by_node(codec_np); -+ if (!codec_dev || !codec_dev->driver) { -+ dev_err(&pdev->dev, "failed to find codec platform device\n"); -+ ret = -EINVAL; -+ goto fail; -+ } -+ -+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); -+ if (!data) { -+ ret = -ENOMEM; -+ goto fail; -+ } -+ -+ card_priv.data = data; -+ -+ data->codec_dev = codec_dev; -+ -+ data->dai.name = "HiFi"; -+ data->dai.stream_name = "HiFi"; -+ data->dai.codec_dai_name = "wm8731-hifi"; -+ data->dai.codec_of_node = codec_np; -+ data->dai.cpu_dai_name = dev_name(&ssi_pdev->dev); -+ data->dai.platform_of_node = ssi_np; -+ data->dai.ops = &imx_hifi_ops; -+ data->dai.init = &imx_wm8731_init; -+ -+ data->ssi_num = 2; /* 1-based */ -+ data->src_port = 2; -+ data->ext_port = 4; -+ -+ imx_audmux_config_slv_mode(data->src_port, data->ext_port); -+ -+ /* Slave Mode Init */ -+ wm8731_slv_mode_init(data); -+ -+ data->card.dev = &pdev->dev; -+ ret = snd_soc_of_parse_card_name(&data->card, "model"); -+ if (ret) -+ goto fail; -+ -+ ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); -+ if (ret) -+ goto fail; -+ -+ data->card.num_links = 1; -+ data->card.dai_link = &data->dai; -+ -+ data->card.dapm_widgets = imx_dapm_widgets; -+ data->card.num_dapm_widgets = ARRAY_SIZE(imx_dapm_widgets); -+ -+ platform_set_drvdata(pdev, &data->card); -+ snd_soc_card_set_drvdata(&data->card, data); -+ -+ ret = snd_soc_register_card(&data->card); -+ if (ret) { -+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); -+ goto fail; -+ } -+ -+ return 0; -+ -+fail: -+ -+ if (ssi_np) -+ of_node_put(ssi_np); -+ -+ if (codec_np) -+ of_node_put(codec_np); -+ -+ return ret; -+} -+ -+static int imx_wm8731_remove(struct platform_device *pdev) -+{ -+ struct snd_soc_card *card = platform_get_drvdata(pdev); -+ snd_soc_unregister_card(card); -+ -+ return 0; -+} -+ -+static const struct of_device_id imx_wm8731_dt_ids[] = { -+ { .compatible = "fsl,imx-audio-wm8731", }, -+ { /* sentinel */ } -+}; -+MODULE_DEVICE_TABLE(of, imx_wm8731_dt_ids); -+ -+static struct platform_driver imx_wm8731_driver = { -+ .driver = { -+ .name = "imx-wm8731", -+ .owner = THIS_MODULE, -+ .of_match_table = imx_wm8731_dt_ids, -+ }, -+ .probe = imx_wm8731_probe, -+ .remove = imx_wm8731_remove, -+}; -+module_platform_driver(imx_wm8731_driver); -+ -+MODULE_AUTHOR("Freescale Semiconductor, Inc."); -+MODULE_DESCRIPTION("Freescale i.MX WM8731 ASoC machine driver"); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:imx-wm8731"); --- -1.7.9.5 - |