From 02a70e6f54146d30ca123a93cdafcecc0cb77d82 Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Mon, 26 Nov 2012 16:38:12 +0800 Subject: [PATCH 01/14] ASoC: sama5d3-wm8904: add audio support Signed-off-by: Bo Shen --- sound/soc/atmel/Kconfig | 11 ++ sound/soc/atmel/Makefile | 3 + sound/soc/atmel/sama5d3_wm8904.c | 279 +++++++++++++++++++++++++++++++ 3 files changed, 293 insertions(+) create mode 100644 sound/soc/atmel/sama5d3_wm8904.c diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 4789619a52d86d..e81787b435917d 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -61,3 +61,14 @@ config SND_AT91_SOC_AFEB9260 select SND_SOC_TLV320AIC23_I2C help Say Y here to support sound on AFEB9260 board. + +config SND_AT91_SOC_SAMA5D3_WM8904 + tristate "SoC Audio support for WM8904 based SAMA5D3-EK board" + depends on ATMEL_SSC && SND_ATMEL_SOC + select SND_ATMEL_SOC_DMA + select SND_ATMEL_SOC_SSC + select SND_SOC_WM8904 + select SND_SOC_DMAENGINE_PCM + help + Say Y if you want to add support for audio SoC on an + SAMA5D3-EK board which uses WM8904 audio codec. diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index 5baabc8bde3abf..acf60e9058fe8d 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile @@ -13,8 +13,11 @@ obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o snd-atmel-soc-wm8904-objs := atmel_wm8904.o snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o +snd-soc-sama5d3-wm8904-objs := sama5d3_wm8904.o obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o +obj-$(CONFIG_SND_AT91_SOC_SAMA5D3_WM8904) += snd-soc-sama5d3-wm8904.o + obj-$(CONFIG_SND_AT91_SOC_AFEB9260) += snd-soc-afeb9260.o diff --git a/sound/soc/atmel/sama5d3_wm8904.c b/sound/soc/atmel/sama5d3_wm8904.c new file mode 100644 index 00000000000000..bd4ab4e2a38f24 --- /dev/null +++ b/sound/soc/atmel/sama5d3_wm8904.c @@ -0,0 +1,279 @@ +/* + * sama5d3ek_wm8904 - SoC audio for SAMA5D3EK based boards + * which use WM8904 as codec. + * + * Copyright (C) 2012 Atmel + * + * Author: Bo Shen + * Based on sam9g20_wm8731.c by Sedji Gaouaou + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "../codecs/wm8904.h" +#include "atmel_ssc_dai.h" + +#define MCLK_RATE 32768 + +static struct clk *mclk; + +static const struct snd_soc_dapm_route intercon[] = { + { "MICBIAS", NULL, "IN1L" }, + { "Left Capture Mux", NULL, "MICBIAS" }, +}; + +static int sama5d3_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret; + + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) { + pr_err("%s - Failed to set CODEC DAI format.", __func__); + return ret; + } + + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) { + pr_err("%s - Failed to set CPU DAI format.", __func__); + return ret; + } + + ret = snd_soc_dai_set_pll(codec_dai, WM8904_FLL_MCLK, WM8904_FLL_MCLK, + 32768, params_rate(params) * 256); + if (ret < 0) { + pr_err("%s - Failed to set CODEC PLL.", __func__); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, WM8904_CLK_FLL, + 12000000, SND_SOC_CLOCK_IN); + if (ret < 0) { + pr_err("%s - Failed to set WM8904 SYSCLK\n", __func__); + return ret; + } + + return 0; +} + +static struct snd_soc_ops sama5d3_soc_ops = { + .hw_params = sama5d3_hw_params, +}; + +static int sama5d3_wm8904_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + + pr_debug("ASoC: sama5d3_wm8904_init() called\n"); + + snd_soc_dapm_nc_pin(dapm, "IN1R"); + snd_soc_dapm_nc_pin(dapm, "IN3L"); + snd_soc_dapm_nc_pin(dapm, "IN3R"); + + snd_soc_dapm_nc_pin(dapm, "LINEOUTL"); + snd_soc_dapm_nc_pin(dapm, "LINEOUTR"); + + snd_soc_dapm_enable_pin(dapm, "HPOUTL"); + snd_soc_dapm_enable_pin(dapm, "HPOUTR"); + snd_soc_dapm_enable_pin(dapm, "IN1L"); + snd_soc_dapm_enable_pin(dapm, "IN2L"); + snd_soc_dapm_enable_pin(dapm, "IN2R"); + snd_soc_dapm_enable_pin(dapm, "MICBIAS"); + + snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); + + snd_soc_dapm_sync(dapm); + + return 0; +} + +int sama5d3ek_snd_suspend_pre(struct snd_soc_card *card) +{ + clk_disable(mclk); + return 0; +} + +int sama5d3ek_snd_resume_pre(struct snd_soc_card *card) +{ + clk_enable(mclk); + return 0; +} + +static struct snd_soc_dai_link sama5d3ek_dai = { + .name = "WM8904", + .stream_name = "WM8904 PCM", + .codec_dai_name = "wm8904-hifi", + .init = sama5d3_wm8904_init, + .ops = &sama5d3_soc_ops, +}; + +static struct snd_soc_card snd_soc_sama5d3ek = { + .name = "WM8904 @ SAMA5D3", + .dai_link = &sama5d3ek_dai, + .num_links = 1, + .suspend_pre = sama5d3ek_snd_suspend_pre, + .resume_pre = sama5d3ek_snd_resume_pre, +}; + +static int sama5d3ek_wm8904_dt_init(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *codec_np, *cpu_np; + struct snd_soc_card *card = &snd_soc_sama5d3ek; + int ret; + + if (!np) + return -1; + + ret = snd_soc_of_parse_card_name(card, "atmel,model"); + if (ret) { + dev_err(&pdev->dev, "failed to parse card name\n"); + return ret; + } + + ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing"); + if (ret) { + dev_err(&pdev->dev, "failed to parse audio routing\n"); + return ret; + } + + cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0); + if (!cpu_np) { + dev_err(&pdev->dev, "failed to get dai and pcm info\n"); + ret = -EINVAL; + return ret; + } + sama5d3ek_dai.cpu_of_node = cpu_np; + sama5d3ek_dai.platform_of_node = cpu_np; + of_node_put(cpu_np); + + codec_np = of_parse_phandle(np, "atmel,audio-codec", 0); + if (!codec_np) { + dev_err(&pdev->dev, "failed to get codec info\n"); + ret = -EINVAL; + return ret; + } + sama5d3ek_dai.codec_of_node = codec_np; + of_node_put(codec_np); + + return 0; +} + +static int sama5d3ek_wm8904_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &snd_soc_sama5d3ek; + struct clk *clk_src; + struct pinctrl *pinctrl; + int ret; + + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) { + dev_err(&pdev->dev, "failed to request pinctrl\n"); + return PTR_ERR(pinctrl); + } + + ret = atmel_ssc_set_audio(0); + if (ret != 0) { + dev_err(&pdev->dev, "failed to set SSC 0 for audio\n"); + return ret; + } + + card->dev = &pdev->dev; + ret = sama5d3ek_wm8904_dt_init(pdev); + if (ret) { + dev_err(&pdev->dev, "failed to init dt info\n"); + goto err_set_audio; + } + + mclk = clk_get(NULL, "pck0"); + if (IS_ERR(mclk)) { + dev_err(&pdev->dev, "failed to get pck0\n"); + ret = PTR_ERR(mclk); + goto err_set_audio; + } + + clk_src = clk_get(NULL, "clk32k"); + if (IS_ERR(clk_src)) { + dev_err(&pdev->dev, "failed to get clk32k\n"); + ret = PTR_ERR(clk_src); + goto err_set_audio; + } + + ret = clk_set_parent(mclk, clk_src); + clk_put(clk_src); + if (ret != 0) { + dev_err(&pdev->dev, "failed to set MCLK parent\n"); + goto err_set_audio; + } + + dev_info(&pdev->dev, "setting pck0 to %dHz\n", MCLK_RATE); + + clk_set_rate(mclk, MCLK_RATE); + clk_enable(mclk); + + snd_soc_register_card(card); + + return 0; + +err_set_audio: + atmel_ssc_put_audio(0); + return ret; +} + +static int sama5d3ek_wm8904_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + atmel_ssc_put_audio(0); + snd_soc_unregister_card(card); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id sama5d3ek_wm8904_dt_ids[] = { + { .compatible = "atmel,sama5d3ek-wm8904", }, + { } +}; +#endif + +static struct platform_driver sama5d3ek_wm8904_driver = { + .driver = { + .name = "sama5d3ek-audio", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(sama5d3ek_wm8904_dt_ids), + }, + .probe = sama5d3ek_wm8904_probe, + .remove = sama5d3ek_wm8904_remove, +}; + +module_platform_driver(sama5d3ek_wm8904_driver); + +/* Module information */ +MODULE_AUTHOR("Bo Shen "); +MODULE_DESCRIPTION("ALSA SoC machine driver for AT91SAMA5D3 - WM8904"); +MODULE_LICENSE("GPL"); From 015f6c6beecc76f7e8cd4f1ba4804e6792053801 Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Fri, 18 Jan 2013 14:52:56 +0800 Subject: [PATCH 02/14] ASoC: sama5-wm8904: setting dai format through dai link Signed-off-by: Bo Shen --- sound/soc/atmel/sama5d3_wm8904.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/sound/soc/atmel/sama5d3_wm8904.c b/sound/soc/atmel/sama5d3_wm8904.c index bd4ab4e2a38f24..20c8f547f4e859 100644 --- a/sound/soc/atmel/sama5d3_wm8904.c +++ b/sound/soc/atmel/sama5d3_wm8904.c @@ -45,23 +45,8 @@ static int sama5d3_hw_params(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_dai *cpu_dai = rtd->cpu_dai; int ret; - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) { - pr_err("%s - Failed to set CODEC DAI format.", __func__); - return ret; - } - - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) { - pr_err("%s - Failed to set CPU DAI format.", __func__); - return ret; - } - ret = snd_soc_dai_set_pll(codec_dai, WM8904_FLL_MCLK, WM8904_FLL_MCLK, 32768, params_rate(params) * 256); if (ret < 0) { @@ -128,6 +113,9 @@ static struct snd_soc_dai_link sama5d3ek_dai = { .stream_name = "WM8904 PCM", .codec_dai_name = "wm8904-hifi", .init = sama5d3_wm8904_init, + .dai_fmt = SND_SOC_DAIFMT_I2S + | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM, .ops = &sama5d3_soc_ops, }; From 8aa7dd17d7a3bded05913417ad55ca65918b3b88 Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Fri, 18 Jan 2013 15:51:53 +0800 Subject: [PATCH 03/14] ASoC: sama5_wm8904: using dapm widget directly Signed-off-by: Bo Shen --- sound/soc/atmel/sama5d3_wm8904.c | 39 ++++++-------------------------- 1 file changed, 7 insertions(+), 32 deletions(-) diff --git a/sound/soc/atmel/sama5d3_wm8904.c b/sound/soc/atmel/sama5d3_wm8904.c index 20c8f547f4e859..ba8cd307f4fa69 100644 --- a/sound/soc/atmel/sama5d3_wm8904.c +++ b/sound/soc/atmel/sama5d3_wm8904.c @@ -35,9 +35,10 @@ static struct clk *mclk; -static const struct snd_soc_dapm_route intercon[] = { - { "MICBIAS", NULL, "IN1L" }, - { "Left Capture Mux", NULL, "MICBIAS" }, +static const struct snd_soc_dapm_widget sama5ek_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic", NULL), + SND_SOC_DAPM_LINE("Line In Jack", NULL), }; static int sama5d3_hw_params(struct snd_pcm_substream *substream, @@ -68,34 +69,6 @@ static struct snd_soc_ops sama5d3_soc_ops = { .hw_params = sama5d3_hw_params, }; -static int sama5d3_wm8904_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - - pr_debug("ASoC: sama5d3_wm8904_init() called\n"); - - snd_soc_dapm_nc_pin(dapm, "IN1R"); - snd_soc_dapm_nc_pin(dapm, "IN3L"); - snd_soc_dapm_nc_pin(dapm, "IN3R"); - - snd_soc_dapm_nc_pin(dapm, "LINEOUTL"); - snd_soc_dapm_nc_pin(dapm, "LINEOUTR"); - - snd_soc_dapm_enable_pin(dapm, "HPOUTL"); - snd_soc_dapm_enable_pin(dapm, "HPOUTR"); - snd_soc_dapm_enable_pin(dapm, "IN1L"); - snd_soc_dapm_enable_pin(dapm, "IN2L"); - snd_soc_dapm_enable_pin(dapm, "IN2R"); - snd_soc_dapm_enable_pin(dapm, "MICBIAS"); - - snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); - - snd_soc_dapm_sync(dapm); - - return 0; -} - int sama5d3ek_snd_suspend_pre(struct snd_soc_card *card) { clk_disable(mclk); @@ -112,7 +85,6 @@ static struct snd_soc_dai_link sama5d3ek_dai = { .name = "WM8904", .stream_name = "WM8904 PCM", .codec_dai_name = "wm8904-hifi", - .init = sama5d3_wm8904_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM, @@ -125,6 +97,9 @@ static struct snd_soc_card snd_soc_sama5d3ek = { .num_links = 1, .suspend_pre = sama5d3ek_snd_suspend_pre, .resume_pre = sama5d3ek_snd_resume_pre, + .dapm_widgets = sama5ek_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sama5ek_dapm_widgets), + .fully_routed = true, }; static int sama5d3ek_wm8904_dt_init(struct platform_device *pdev) From b290625e8da67ef5d1ad1c1339a286eac1685146 Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Tue, 29 Jan 2013 13:49:10 +0800 Subject: [PATCH 04/14] ASoC: sama5_wm8904: disable clock when remove Signed-off-by: Bo Shen --- sound/soc/atmel/sama5d3_wm8904.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/atmel/sama5d3_wm8904.c b/sound/soc/atmel/sama5d3_wm8904.c index ba8cd307f4fa69..a52b611b9c5a24 100644 --- a/sound/soc/atmel/sama5d3_wm8904.c +++ b/sound/soc/atmel/sama5d3_wm8904.c @@ -211,8 +211,9 @@ static int sama5d3ek_wm8904_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); - atmel_ssc_put_audio(0); + clk_disable(mclk); snd_soc_unregister_card(card); + atmel_ssc_put_audio(0); return 0; } From 4edd89baa1646b0b1774d2d2cf44947e0b3ec117 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Tue, 19 Feb 2013 18:06:51 +0800 Subject: [PATCH 05/14] ARM: at91/trivial: fix alsa driver module description with correct sama5d3 name --- sound/soc/atmel/sama5d3_wm8904.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/atmel/sama5d3_wm8904.c b/sound/soc/atmel/sama5d3_wm8904.c index a52b611b9c5a24..d0675d9e2a6ee1 100644 --- a/sound/soc/atmel/sama5d3_wm8904.c +++ b/sound/soc/atmel/sama5d3_wm8904.c @@ -239,5 +239,5 @@ module_platform_driver(sama5d3ek_wm8904_driver); /* Module information */ MODULE_AUTHOR("Bo Shen "); -MODULE_DESCRIPTION("ALSA SoC machine driver for AT91SAMA5D3 - WM8904"); +MODULE_DESCRIPTION("ALSA SoC machine driver for SAMA5D3 - WM8904"); MODULE_LICENSE("GPL"); From 9fb589e6cce29492d7f4810eb44e8c79efaa1849 Mon Sep 17 00:00:00 2001 From: Alexander Morozov Date: Wed, 17 Jul 2013 20:17:50 +0400 Subject: [PATCH 06/14] sound: codecs: add devicetree support for WM8904 --- sound/soc/codecs/wm8904.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index afbcce82497327..8372e016686532 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -2259,10 +2259,19 @@ static const struct i2c_device_id wm8904_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, wm8904_i2c_id); +static const struct of_device_id wm8904_of_match[] = { + { .compatible = "wlf,wm8904", }, + { .compatible = "wlf,wm8912", }, + { .compatible = "wlf,wm8918", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8904_of_match); + static struct i2c_driver wm8904_i2c_driver = { .driver = { .name = "wm8904", .owner = THIS_MODULE, + .of_match_table = wm8904_of_match, }, .probe = wm8904_i2c_probe, .remove = wm8904_i2c_remove, From 40142888d09ca666eb2655a0f548ebbbb45bbff6 Mon Sep 17 00:00:00 2001 From: Alexander Morozov Date: Thu, 7 Aug 2014 18:16:27 +0400 Subject: [PATCH 07/14] ASoC: sama5_wm8904: remove unneeded include The include is not required and results in failed compilation. --- sound/soc/atmel/sama5d3_wm8904.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/atmel/sama5d3_wm8904.c b/sound/soc/atmel/sama5d3_wm8904.c index d0675d9e2a6ee1..fcdcf4195e4bdf 100644 --- a/sound/soc/atmel/sama5d3_wm8904.c +++ b/sound/soc/atmel/sama5d3_wm8904.c @@ -26,7 +26,6 @@ #include #include -#include #include "../codecs/wm8904.h" #include "atmel_ssc_dai.h" From af47690bb89ac00c8467373cb6c2fac1759d6978 Mon Sep 17 00:00:00 2001 From: Alexander Morozov Date: Wed, 17 Jul 2013 20:19:04 +0400 Subject: [PATCH 08/14] ARM: at91sama5d3ek/dt: fix sound SOC DT entry --- arch/arm/boot/dts/sama5d35ek.dts | 4 ++++ arch/arm/boot/dts/sama5d3xmb.dtsi | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/sama5d35ek.dts b/arch/arm/boot/dts/sama5d35ek.dts index 9089c7c6cea863..d58d2f4998e9ba 100644 --- a/arch/arm/boot/dts/sama5d35ek.dts +++ b/arch/arm/boot/dts/sama5d35ek.dts @@ -20,6 +20,10 @@ status = "okay"; }; + ssc0: ssc@f0008000 { + status = "okay"; + }; + can0: can@f000c000 { status = "okay"; }; diff --git a/arch/arm/boot/dts/sama5d3xmb.dtsi b/arch/arm/boot/dts/sama5d3xmb.dtsi index b8c6f20e780c99..3bfc9b0666df82 100644 --- a/arch/arm/boot/dts/sama5d3xmb.dtsi +++ b/arch/arm/boot/dts/sama5d3xmb.dtsi @@ -43,10 +43,11 @@ */ i2c0: i2c@f0014000 { wm8904: wm8904@1a { - compatible = "wm8904"; + compatible = "wlf,wm8904"; reg = <0x1a>; clocks = <&pck0>; clock-names = "mclk"; + status = "okay"; }; }; From fccca8441226c8d48c9e1140ccb17ddecdbab69e Mon Sep 17 00:00:00 2001 From: Alexander Morozov Date: Tue, 18 Jun 2013 14:57:13 +0400 Subject: [PATCH 09/14] ARM: at91sam9260/dt: add I2C pinctl definitions This patch adds missing devicetree definitions for IO pins of I2C bus. Without them transfers on I2C just timed out. Definitions affect boards based on at91sam9260 and at91sam9g20 CPUs. --- arch/arm/boot/dts/at91sam9260.dtsi | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm/boot/dts/at91sam9260.dtsi b/arch/arm/boot/dts/at91sam9260.dtsi index c0e0eae16a279f..e0ee5fcefbafda 100644 --- a/arch/arm/boot/dts/at91sam9260.dtsi +++ b/arch/arm/boot/dts/at91sam9260.dtsi @@ -435,6 +435,14 @@ }; }; + i2c0 { + pinctrl_i2c0: i2c0-0 { + atmel,pins = + <0 23 0x1 0x0 /* PA23 periph A I2C0 data */ + 0 24 0x1 0x0>; /* PA24 periph A I2C0 clock */ + }; + }; + pioA: gpio@fffff400 { compatible = "atmel,at91rm9200-gpio"; reg = <0xfffff400 0x200>; @@ -563,6 +571,8 @@ interrupts = <11 IRQ_TYPE_LEVEL_HIGH 6>; #address-cells = <1>; #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c0>; status = "disabled"; }; From 6055bb4627c0c3973bbbf1fe2dc982d066cbd842 Mon Sep 17 00:00:00 2001 From: Alexander Morozov Date: Wed, 27 Feb 2013 14:58:33 +0400 Subject: [PATCH 10/14] ARM: at91sam9x5/dt: usart3 definitions This patch adds missing usart3 structures in device tree description of at91sam9x5 SoC family. --- arch/arm/boot/dts/at91sam9x5.dtsi | 40 +++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi index 6b113fb72af1c8..59a6239c0169fb 100644 --- a/arch/arm/boot/dts/at91sam9x5.dtsi +++ b/arch/arm/boot/dts/at91sam9x5.dtsi @@ -26,6 +26,7 @@ serial1 = &usart0; serial2 = &usart1; serial3 = &usart2; + serial4 = &usart3; gpio0 = &pioA; gpio1 = &pioB; gpio2 = &pioC; @@ -277,6 +278,11 @@ reg = <7>; }; + usart3_clk: usart3_clk { + #clock-cells = <0>; + reg = <8>; + }; + twi0_clk: twi0_clk { reg = <9>; #clock-cells = <0>; @@ -570,6 +576,29 @@ }; }; + usart3 { + pinctrl_usart3: usart3-0 { + atmel,pins = + ; /* PC23 periph B */ + }; + + pinctrl_usart3_rts: usart3_rts-0 { + atmel,pins = + ; /* PC24 periph B */ + }; + + pinctrl_usart3_cts: usart3_cts-0 { + atmel,pins = + ; /* PC25 periph B */ + }; + + pinctrl_usart3_sck: usart3_sck-0 { + atmel,pins = + ; /* PC26 periph B */ + }; + }; + uart0 { pinctrl_uart0: uart0-0 { atmel,pins = @@ -983,6 +1012,17 @@ status = "disabled"; }; + usart3: serial@f8028000 { + compatible = "atmel,at91sam9260-usart"; + reg = <0xf8028000 0x200>; + interrupts = <8 4 5>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usart3>; + clocks = <&usart3_clk>; + clock-names = "usart"; + status = "disabled"; + }; + i2c0: i2c@f8010000 { compatible = "atmel,at91sam9x5-i2c"; reg = <0xf8010000 0x100>; From 182a4b0e56bfffbed4b458e59c6d0a7bb282832f Mon Sep 17 00:00:00 2001 From: Alexander Morozov Date: Fri, 21 Jun 2013 18:37:49 +0400 Subject: [PATCH 11/14] ARM: at91sam9x5/dt: add DMAs for usarts This patch adds missing DMA definitions in devicetree for at91sam9x5 SoC family. --- arch/arm/boot/dts/at91sam9x5.dtsi | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi index 59a6239c0169fb..60cc64a4f91f51 100644 --- a/arch/arm/boot/dts/at91sam9x5.dtsi +++ b/arch/arm/boot/dts/at91sam9x5.dtsi @@ -973,6 +973,9 @@ reg = <0xfffff200 0x200>; interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>; pinctrl-names = "default"; + dmas = <&dma1 1 0x8>, + <&dma1 1 0x209>; + dma-names = "tx", "rx"; pinctrl-0 = <&pinctrl_dbgu>; clocks = <&mck>; clock-names = "usart"; @@ -983,6 +986,9 @@ compatible = "atmel,at91sam9260-usart"; reg = <0xf801c000 0x200>; interrupts = <5 IRQ_TYPE_LEVEL_HIGH 5>; + dmas = <&dma0 1 0x3>, + <&dma0 1 0x204>; + dma-names = "tx", "rx"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_usart0>; clocks = <&usart0_clk>; @@ -994,6 +1000,9 @@ compatible = "atmel,at91sam9260-usart"; reg = <0xf8020000 0x200>; interrupts = <6 IRQ_TYPE_LEVEL_HIGH 5>; + dmas = <&dma0 1 0x5>, + <&dma0 1 0x204>; + dma-names = "tx", "rx"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_usart1>; clocks = <&usart1_clk>; @@ -1005,6 +1014,9 @@ compatible = "atmel,at91sam9260-usart"; reg = <0xf8024000 0x200>; interrupts = <7 IRQ_TYPE_LEVEL_HIGH 5>; + dmas = <&dma1 1 0xc>, + <&dma1 1 0x20d>; + dma-names = "tx", "rx"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_usart2>; clocks = <&usart2_clk>; @@ -1016,6 +1028,9 @@ compatible = "atmel,at91sam9260-usart"; reg = <0xf8028000 0x200>; interrupts = <8 4 5>; + dmas = <&dma1 1 0xe>, + <&dma1 1 0x20f>; + dma-names = "tx", "rx"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_usart3>; clocks = <&usart3_clk>; From b64da31a87e7ad92aa7d59aa9726c5c290a4f2b7 Mon Sep 17 00:00:00 2001 From: Alexander Morozov Date: Fri, 8 Aug 2014 20:30:38 +0400 Subject: [PATCH 12/14] at91: clk: fix bug in divisor calculation In at91sam9x5/sama5 family peripheral clock is produced by dividing MCK by 2^div, where div=0..3. This patch fixes off-by-one error when div is set to 4 when max frequency specified in devicetree is lower than MCK / 2^3. This may happen only when system is misconfigured, but we still shouldn't write illegal values to hardware, so div is set as to 3 and warning is printed that the desired frequency is out of range. --- drivers/clk/at91/clk-peripheral.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c index 597fed423d7d31..55622f544adee1 100644 --- a/drivers/clk/at91/clk-peripheral.c +++ b/drivers/clk/at91/clk-peripheral.c @@ -151,6 +151,14 @@ static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph) if (parent_rate >> shift <= periph->range.max) break; } + + if (shift == PERIPHERAL_MAX_SHIFT) { + shift--; + printk(KERN_WARNING "clk: setting frequency of clock " + "per_id=%d to value %lu above upper limit %lu\n", + periph->id, parent_rate >> shift, + periph->range.max); + } } periph->auto_div = false; From 27ed119a31ce9ad41f62d85861d20894e81ba452 Mon Sep 17 00:00:00 2001 From: Alexander Grishin Date: Mon, 24 Mar 2014 15:57:59 +0400 Subject: [PATCH 13/14] serial: at91: fix stall in DMA RX path 1. Data is transferred from DMA buffer to TTY layer by a tasklet. Tasklet is scheduled when a part of sg buffer fills up, or when USART hardware detects end of transfer by timeout. If USART timeout happend after DMA interrupt and tasklet hasn't been run yet, tasklet runs only once and transfers contents of the filled buffer. If there is some data in new sg buffer, it's processed only next time applet is run. If serial port is used for message-based protocol, communication may effectively halt. In this patch tasklet runs until all currently available data from DMA buffer is transferred. 2. Fix bug in tasklet where under special circumstances whole DMA rx ring buffer is dumped into TTY layer regardless of data validity. Also-By: Sergey Sharapov Also-By: Alexander Morozov --- drivers/tty/serial/atmel_serial.c | 72 ++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 26 deletions(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 53eeea13ff165f..482d180ae41f3e 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -825,40 +825,57 @@ static void atmel_rx_from_dma(struct uart_port *port) struct dma_chan *chan = atmel_port->chan_rx; struct dma_tx_state state; enum dma_status dmastat; - size_t pending, count; + size_t pending, count, sg_len; /* Reset the UART timeout early so that we don't miss one */ UART_PUT_CR(port, ATMEL_US_STTTO); - dmastat = dmaengine_tx_status(chan, - atmel_port->cookie_rx, - &state); - /* Restart a new tasklet if DMA status is error */ - if (dmastat == DMA_ERROR) { - dev_dbg(port->dev, "Get residue error, restart tasklet\n"); - UART_PUT_IER(port, ATMEL_US_TIMEOUT); - tasklet_schedule(&atmel_port->tasklet); - return; - } - /* current transfer size should no larger than dma buffer */ - pending = sg_dma_len(&atmel_port->sg_rx) - state.residue; - BUG_ON(pending > sg_dma_len(&atmel_port->sg_rx)); - /* - * This will take the chars we have so far, - * ring->head will record the transfer size, only new bytes come - * will insert into the framework. + sg_len = sg_dma_len(&atmel_port->sg_rx); + + /* Loop untill all data are transferred from the ring buffer. + * dmaengine_tx_status() calculates residue taking in account only + * state of oldest sg buffer, so we should retry in case there is data + * in the new one. */ - if (pending > ring->head) { - count = pending - ring->head; + while (1) { + dmastat = dmaengine_tx_status(chan, + atmel_port->cookie_rx, + &state); + /* Restart a new tasklet if DMA status is error */ + if (dmastat == DMA_ERROR) { + dev_dbg(port->dev, + "Get residue error, restart tasklet\n"); + UART_PUT_IER(port, ATMEL_US_TIMEOUT); + tasklet_schedule(&atmel_port->tasklet); + return; + } + /* current transfer size should no larger than dma buffer */ + pending = sg_len - state.residue; + BUG_ON(pending > sg_len); - atmel_flip_buffer_rx_dma(port, ring->buf + ring->head, count); + /* DMA has filled ring buffer up to `pending`, and data + * up to `ring->head` was already transferred to TTY layer. + * + * Sometimes we have already rewound ring->head to 0, but + * `pending` is still at `sg_len` if no bytes were transferred. + * Second subcondition deals with this case. + */ + if ((pending > ring->head) && + (pending - ring->head != sg_len)) { + count = pending - ring->head; + + atmel_flip_buffer_rx_dma(port, ring->buf + ring->head, + count); - ring->head += count; - if (ring->head == sg_dma_len(&atmel_port->sg_rx)) - ring->head = 0; + ring->head += count; + if (ring->head == sg_len) + ring->head = 0; - port->icount.rx += count; + port->icount.rx += count; + } else { + break; + } } UART_PUT_IER(port, ATMEL_US_TIMEOUT); @@ -874,6 +891,9 @@ static int atmel_prepare_rx_dma(struct uart_port *port) int ret, nent; ring = &atmel_port->rx_ring; + ring->head = 0; + + port = &(atmel_port->uart); dma_cap_zero(mask); dma_cap_set(DMA_CYCLIC, mask); @@ -887,7 +907,7 @@ static int atmel_prepare_rx_dma(struct uart_port *port) spin_lock_init(&atmel_port->lock_rx); sg_init_table(&atmel_port->sg_rx, 1); /* UART circular rx buffer is an aligned page. */ - BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK); + BUG_ON((int)ring->buf & ~PAGE_MASK); sg_set_page(&atmel_port->sg_rx, virt_to_page(ring->buf), ATMEL_SERIAL_RINGSIZE, From 951cf5dc5b570641232cba1d8cef835296a3ffec Mon Sep 17 00:00:00 2001 From: Alexander Morozov Date: Mon, 25 Aug 2014 13:57:32 +0400 Subject: [PATCH 14/14] SPI: at91: free correct GPIO This patch fixed incorrect release of GPIO CS pin. Sometimes module cleanup code tried to free invalid GPIO pin, definetely not the one that was allocated. Because of this GPIO couldn't be requested on subsequent module insertion. --- drivers/spi/spi-atmel.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 079e6b1b0cdb6f..4feb78614b777c 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -1250,13 +1250,12 @@ static int atmel_spi_transfer_one_message(struct spi_master *master, static void atmel_spi_cleanup(struct spi_device *spi) { struct atmel_spi_device *asd = spi->controller_state; - unsigned gpio = (unsigned) spi->controller_data; if (!asd) return; spi->controller_state = NULL; - gpio_free(gpio); + gpio_free(asd->npcs_pin); kfree(asd); }