[alsa-devel] [PATCH v10 0/7] ASoC: fsl-ssi: ac97-slave support
Hi,
this series implements ac97-slave support for fsl-ssi and phycore ac97 DT support.
Since v9 there were a lot of changes. I dropped the board specific ac97-link reset functions and added generic functions to soc-core. It uses devicetree parsed pinctrl and gpios. The audmux setup was removed from phycore-ac97-dt. The initial setup can now be defined in DT.
Regards,
Markus
Markus Pargmann (7): ASoC: core: Generic ac97 link reset functions ASoC: codec: wm9712 simple DT bindings ASoC: imx-audmux: Read default configuration from devicetree ASoC: fsl-ssi: Add support for imx-pcm-fiq ASoC: fsl-ssi: Use generic DMA bindings if possible ASoC: fsl-ssi: ac97-slave support ASoC: Add phycore-ac97-dt driver
.../devicetree/bindings/sound/fsl,ssi.txt | 12 + .../devicetree/bindings/sound/imx-audmux.txt | 9 + .../bindings/sound/phytec,phycore-ac97.txt | 16 + .../devicetree/bindings/sound/soc-ac97link.txt | 28 ++ Documentation/devicetree/bindings/sound/wm9712.txt | 10 + include/sound/soc.h | 2 + sound/soc/codecs/wm9712.c | 9 + sound/soc/fsl/Kconfig | 15 +- sound/soc/fsl/Makefile | 2 + sound/soc/fsl/fsl_ssi.c | 431 +++++++++++++++++---- sound/soc/fsl/imx-audmux.c | 62 +++ sound/soc/fsl/phycore-ac97-dt.c | 124 ++++++ sound/soc/fsl/phycore-ac97.c | 13 +- sound/soc/soc-core.c | 153 ++++++++ 14 files changed, 789 insertions(+), 97 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/phytec,phycore-ac97.txt create mode 100644 Documentation/devicetree/bindings/sound/soc-ac97link.txt create mode 100644 Documentation/devicetree/bindings/sound/wm9712.txt create mode 100644 sound/soc/fsl/phycore-ac97-dt.c
This patch adds generic ac97 reset functions using pincontrol and gpio parsed from devicetree.
Signed-off-by: Markus Pargmann mpa@pengutronix.de --- .../devicetree/bindings/sound/soc-ac97link.txt | 28 ++++ include/sound/soc.h | 2 + sound/soc/soc-core.c | 153 +++++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/soc-ac97link.txt
diff --git a/Documentation/devicetree/bindings/sound/soc-ac97link.txt b/Documentation/devicetree/bindings/sound/soc-ac97link.txt new file mode 100644 index 0000000..80152a8 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/soc-ac97link.txt @@ -0,0 +1,28 @@ +AC97 link bindings + +These bindings can be included within any other device node. + +Required properties: + - pinctrl-names: Has to contain following states to setup the correct + pinmuxing for the used gpios: + "ac97-running": AC97-link is active + "ac97-reset": AC97-link reset state + "ac97-warm-reset": AC97-link warm reset state + - ac97-gpios: List of gpio phandles with args in the order ac97-sync, + ac97-sdata, ac97-reset + + +Example: + +ssi { + ... + + pinctrl-names = "default", "ac97-running", "ac97-reset", "ac97-warm-reset"; + pinctrl-0 = <&ac97link_running>; + pinctrl-1 = <&ac97link_running>; + pinctrl-2 = <&ac97link_reset>; + pinctrl-3 = <&ac97link_warm_reset>; + ac97-gpios = <&gpio3 20 0 &gpio3 22 0 &gpio3 28 0>; + + ... +}; diff --git a/include/sound/soc.h b/include/sound/soc.h index 6eabee7..c0ac3bc 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -468,6 +468,8 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops); +int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, + struct platform_device *pdev);
/* *Controls diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 0ec070c..6010932 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -30,9 +30,12 @@ #include <linux/bitops.h> #include <linux/debugfs.h> #include <linux/platform_device.h> +#include <linux/pinctrl/consumer.h> #include <linux/ctype.h> #include <linux/slab.h> #include <linux/of.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> #include <sound/ac97_codec.h> #include <sound/core.h> #include <sound/jack.h> @@ -69,6 +72,16 @@ static int pmdown_time = 5000; module_param(pmdown_time, int, 0); MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
+struct snd_ac97_reset_cfg { + struct pinctrl *pctl; + struct pinctrl_state *pstate_reset; + struct pinctrl_state *pstate_warm_reset; + struct pinctrl_state *pstate_run; + int gpio_sdata; + int gpio_sync; + int gpio_reset; +}; + /* returns the minimum number of bytes needed to represent * a particular given value */ static int min_bytes_needed(unsigned long val) @@ -2080,6 +2093,117 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, } EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
+static struct snd_ac97_reset_cfg snd_ac97_rst_cfg; + +static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97) +{ + struct pinctrl *pctl = snd_ac97_rst_cfg.pctl; + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1); + + udelay(10); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0); + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run); + msleep(2); +} + +static void snd_soc_ac97_reset(struct snd_ac97 *ac97) +{ + struct pinctrl *pctl = snd_ac97_rst_cfg.pctl; + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0); + gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0); + gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0); + + udelay(10); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1); + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run); + msleep(2); +} + +static int snd_soc_ac97_parse_pinctl(struct device *dev, + struct snd_ac97_reset_cfg *cfg) +{ + struct pinctrl *p; + struct pinctrl_state *state; + int gpio; + int ret; + + p = devm_pinctrl_get(dev); + if (IS_ERR(p)) { + dev_err(dev, "Failed to get pinctrl\n"); + return PTR_RET(p); + } + cfg->pctl = p; + + state = pinctrl_lookup_state(p, "ac97-reset"); + if (IS_ERR(state)) { + dev_err(dev, "Can't find pinctrl state ac97-reset\n"); + return PTR_RET(state); + } + cfg->pstate_reset = state; + + state = pinctrl_lookup_state(p, "ac97-warm-reset"); + if (IS_ERR(state)) { + dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n"); + return PTR_RET(state); + } + cfg->pstate_warm_reset = state; + + state = pinctrl_lookup_state(p, "ac97-running"); + if (IS_ERR(state)) { + dev_err(dev, "Can't find pinctrl state ac97-running\n"); + return PTR_RET(state); + } + cfg->pstate_run = state; + + gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0); + if (gpio < 0) { + dev_err(dev, "Can't find ac97-sync gpio\n"); + return gpio; + } + ret = devm_gpio_request(dev, gpio, "AC97 link sync"); + if (ret) { + dev_err(dev, "Failed requesting ac97-sync gpio\n"); + return ret; + } + cfg->gpio_sync = gpio; + + gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1); + if (gpio < 0) { + dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio); + return gpio; + } + ret = devm_gpio_request(dev, gpio, "AC97 link sdata"); + if (ret) { + dev_err(dev, "Failed requesting ac97-sdata gpio\n"); + return ret; + } + cfg->gpio_sdata = gpio; + + gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2); + if (gpio < 0) { + dev_err(dev, "Can't find ac97-reset gpio\n"); + return gpio; + } + ret = devm_gpio_request(dev, gpio, "AC97 link reset"); + if (ret) { + dev_err(dev, "Failed requesting ac97-reset gpio\n"); + return ret; + } + cfg->gpio_reset = gpio; + + return 0; +} + struct snd_ac97_bus_ops *soc_ac97_ops; EXPORT_SYMBOL_GPL(soc_ac97_ops);
@@ -2098,6 +2222,35 @@ int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops) EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops);
/** + * snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions + * + * This function sets the reset and warm_reset properties of ops and parses + * the device node of pdev to get pinctrl states and gpio numbers to use. + */ +int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct snd_ac97_reset_cfg cfg; + int ret; + + ret = snd_soc_ac97_parse_pinctl(dev, &cfg); + if (ret) + return ret; + + ret = snd_soc_set_ac97_ops(ops); + if (ret) + return ret; + + ops->warm_reset = snd_soc_ac97_warm_reset; + ops->reset = snd_soc_ac97_reset; + + snd_ac97_rst_cfg = cfg; + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset); + +/** * snd_soc_free_ac97_codec - free AC97 codec device * @codec: audio codec *
On Sat, Jul 27, 2013 at 01:31:50PM +0200, Markus Pargmann wrote:
This is a really nice approach to the problem, just one thing I'd like to see changed in the bindings:
+ssi {
- ...
- pinctrl-names = "default", "ac97-running", "ac97-reset", "ac97-warm-reset";
I'd like to see some documentation of this "default" state - I'd expect this corresponds to the bus being idle and waiting for a wakeup from the CODEC which is definitely a useful state to have for power optimisation so it'd be good to bake it into the bindings.
Alternatively it could be the bus being totally idle with no possibility of wakeup in which case it'd be good to say that too then someone can add the waiting for wakeup state later.
Please also note the new list devicetree@vger.kernel.org.
On Sat, Jul 27, 2013 at 01:05:22PM +0100, Mark Brown wrote:
On Sat, Jul 27, 2013 at 01:31:50PM +0200, Markus Pargmann wrote:
This is a really nice approach to the problem, just one thing I'd like to see changed in the bindings:
+ssi {
- ...
- pinctrl-names = "default", "ac97-running", "ac97-reset", "ac97-warm-reset";
I'd like to see some documentation of this "default" state - I'd expect this corresponds to the bus being idle and waiting for a wakeup from the CODEC which is definitely a useful state to have for power optimisation so it'd be good to bake it into the bindings.
Alternatively it could be the bus being totally idle with no possibility of wakeup in which case it'd be good to say that too then someone can add the waiting for wakeup state later.
The "default" state is actually just the default pin state that is set before the driver is probed. I will remove it from the example. I didn't add a low power state although it might be usefull and is defined by the standard.
Please also note the new list devicetree@vger.kernel.org.
Yes thanks.
Regards,
Markus
Signed-off-by: Markus Pargmann mpa@pengutronix.de --- Documentation/devicetree/bindings/sound/wm9712.txt | 10 ++++++++++ sound/soc/codecs/wm9712.c | 9 +++++++++ 2 files changed, 19 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/wm9712.txt
diff --git a/Documentation/devicetree/bindings/sound/wm9712.txt b/Documentation/devicetree/bindings/sound/wm9712.txt new file mode 100644 index 0000000..d29f673 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wm9712.txt @@ -0,0 +1,10 @@ +WM9712 audio CODEC + +Required properties: + - compatible : "wlf,wm9712" + +Example: + +wm9712: codec { + compatible = "wlf,wm9712"; +}; diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index c5eb746..d619cdc 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -697,10 +697,19 @@ static int wm9712_remove(struct platform_device *pdev) return 0; }
+static const struct of_device_id wm9712_codec_of_dev_id[] = { + { + .compatible = "wlf,wm9712", + }, { + /* sentinel */ + } +}; + static struct platform_driver wm9712_codec_driver = { .driver = { .name = "wm9712-codec", .owner = THIS_MODULE, + .of_match_table = wm9712_codec_of_dev_id, },
.probe = wm9712_probe,
On Sat, Jul 27, 2013 at 01:31:51PM +0200, Markus Pargmann wrote:
+static const struct of_device_id wm9712_codec_of_dev_id[] = {
- {
.compatible = "wlf,wm9712",
- }, {
/* sentinel */
- }
+};
static struct platform_driver wm9712_codec_driver = { .driver = { .name = "wm9712-codec", .owner = THIS_MODULE,
.of_match_table = wm9712_codec_of_dev_id,
},
.probe = wm9712_probe,
AC'97 is an enumerable bus, we shouldn't need this...
On Sat, Jul 27, 2013 at 12:59:50PM +0100, Mark Brown wrote:
On Sat, Jul 27, 2013 at 01:31:51PM +0200, Markus Pargmann wrote:
+static const struct of_device_id wm9712_codec_of_dev_id[] = {
- {
.compatible = "wlf,wm9712",
- }, {
/* sentinel */
- }
+};
static struct platform_driver wm9712_codec_driver = { .driver = { .name = "wm9712-codec", .owner = THIS_MODULE,
.of_match_table = wm9712_codec_of_dev_id,
},
.probe = wm9712_probe,
AC'97 is an enumerable bus, we shouldn't need this...
What do you mean exactly? Is there a way to automaticaly discover connected codecs and register them?
Regards,
Markus
On Sat, Jul 27, 2013 at 03:44:52PM +0200, Markus Pargmann wrote:
On Sat, Jul 27, 2013 at 12:59:50PM +0100, Mark Brown wrote:
AC'97 is an enumerable bus, we shouldn't need this...
What do you mean exactly? Is there a way to automaticaly discover connected codecs and register them?
Yes, of coure - just read the ID registers. Look at how the non-ASoC AC'97 code works.
Adds a function to parse a default port configuration from devicetree.
Signed-off-by: Markus Pargmann mpa@pengutronix.de --- .../devicetree/bindings/sound/imx-audmux.txt | 9 ++++ sound/soc/fsl/imx-audmux.c | 62 ++++++++++++++++++++++ 2 files changed, 71 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/imx-audmux.txt b/Documentation/devicetree/bindings/sound/imx-audmux.txt index 215aa98..f88a00e 100644 --- a/Documentation/devicetree/bindings/sound/imx-audmux.txt +++ b/Documentation/devicetree/bindings/sound/imx-audmux.txt @@ -5,6 +5,15 @@ Required properties: or "fsl,imx31-audmux" for the version firstly used on i.MX31. - reg : Should contain AUDMUX registers location and length
+An initial configuration can be setup using child nodes. + +Required properties of optional child nodes: +- fsl,audmux-port : Integer of the audmux port that is configured by this + child node. +- fsl,port-config : List of configuration options for the specific port. For + imx31-audmux and above, it is a list of tuples <ptcr pdcr>. For + imx21-audmux it is a list of pcr values. + Example:
audmux@021d8000 { diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c index 1a5da1e..103d1b0 100644 --- a/sound/soc/fsl/imx-audmux.c +++ b/sound/soc/fsl/imx-audmux.c @@ -251,6 +251,66 @@ int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr, } EXPORT_SYMBOL_GPL(imx_audmux_v2_configure_port);
+static int imx_audmux_parse_dt_defaults(struct platform_device *pdev, + struct device_node *of_node) +{ + struct device_node *child; + + for_each_available_child_of_node(of_node, child) { + unsigned int port; + unsigned int ptcr = 0; + unsigned int pdcr = 0; + unsigned int pcr = 0; + unsigned int val; + int ret; + int i = 0; + + ret = of_property_read_u32(child, "fsl,audmux-port", &port); + if (ret) { + dev_warn(&pdev->dev, "Failed to get fsl,audmux-port of child node "%s"\n", + child->full_name); + continue; + } + if (!of_property_read_bool(child, "fsl,port-config")) { + dev_warn(&pdev->dev, "child node "%s" does not have property fsl,port-config\n", + child->full_name); + continue; + } + + for (i = 0; (ret = of_property_read_u32_index(child, + "fsl,port-config\n", i, &val)) == 0; + ++i) { + if (audmux_type == IMX31_AUDMUX) { + if (i % 2) + pdcr |= val; + else + ptcr |= val; + } else { + pcr |= val; + } + } + + if (ret != -ENODATA) { + dev_err(&pdev->dev, "Failed to read u32 at index %d of child %s\n", + i, child->full_name); + continue; + } + + if (audmux_type == IMX31_AUDMUX) { + if (i % 2) { + dev_err(&pdev->dev, "One pdcr value is missing in child node %s\n", + child->full_name); + continue; + } + imx_audmux_v2_configure_port(port, ptcr, pdcr); + } else { + imx_audmux_v1_configure_port(port, pcr); + } + } + + return 0; +} + static int imx_audmux_probe(struct platform_device *pdev) { struct resource *res; @@ -275,6 +335,8 @@ static int imx_audmux_probe(struct platform_device *pdev) if (audmux_type == IMX31_AUDMUX) audmux_debugfs_init();
+ imx_audmux_parse_dt_defaults(pdev, pdev->dev.of_node); + return 0; }
On Sat, Jul 27, 2013 at 01:31:52PM +0200, Markus Pargmann wrote:
Adds a function to parse a default port configuration from devicetree.
Applied, thanks - this is a really nice improvement.
On Tue, Aug 06, 2013 at 05:57:46PM +0100, Mark Brown wrote:
On Sat, Jul 27, 2013 at 01:31:52PM +0200, Markus Pargmann wrote:
Adds a function to parse a default port configuration from devicetree.
Applied, thanks - this is a really nice improvement.
Sorry, but I noticed two bugs in this patch, I will send a fixup.
Regards,
Markus
Signed-off-by: Markus Pargmann mpa@pengutronix.de --- sound/soc/fsl/imx-audmux.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c index f37cd9c..b21b85f 100644 --- a/sound/soc/fsl/imx-audmux.c +++ b/sound/soc/fsl/imx-audmux.c @@ -270,7 +270,7 @@ static int imx_audmux_parse_dt_defaults(struct platform_device *pdev, }
for (i = 0; (ret = of_property_read_u32_index(child, - "fsl,port-config\n", i, &val)) == 0; + "fsl,port-config", i, &val)) == 0; ++i) { if (audmux_type == IMX31_AUDMUX) { if (i % 2) @@ -282,7 +282,7 @@ static int imx_audmux_parse_dt_defaults(struct platform_device *pdev, } }
- if (ret != -ENODATA) { + if (ret != -EOVERFLOW) { dev_err(&pdev->dev, "Failed to read u32 at index %d of child %s\n", i, child->full_name); continue;
On Sat, Aug 10, 2013 at 02:55:57PM +0200, Markus Pargmann wrote:
Signed-off-by: Markus Pargmann mpa@pengutronix.de
Applied, thanks. This is two changes so would've been better as two patches.
Add support for non-dma pcm for imx platforms with imx-pcm-fiq support. Instead of imx-pcm-audio, in this case imx-pcm-fiq-audio device is added and the SIER flags are set differently.
We need imx-pcm-fiq for some boards that use an incompatible codec. imx-pcm-fiq handles those codecs differently and allows to operate with them. DMA is not possible because some data sent by the codecs, e.g. wm9712, is not in the datastream. Also some data is mixed up in the fifos, so that we need to sort them out manually.
Signed-off-by: Markus Pargmann mpa@pengutronix.de Tested-by: Shawn Guo shawn.guo@linaro.org --- .../devicetree/bindings/sound/fsl,ssi.txt | 4 ++ sound/soc/fsl/fsl_ssi.c | 79 ++++++++++++++++++---- 2 files changed, 71 insertions(+), 12 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/fsl,ssi.txt b/Documentation/devicetree/bindings/sound/fsl,ssi.txt index 5ff76c9..e45cbce 100644 --- a/Documentation/devicetree/bindings/sound/fsl,ssi.txt +++ b/Documentation/devicetree/bindings/sound/fsl,ssi.txt @@ -47,6 +47,10 @@ Optional properties: - codec-handle: Phandle to a 'codec' node that defines an audio codec connected to this SSI. This node is typically a child of an I2C or other control node. +- fsl,fiq-stream-filter: Bool property. Disabled DMA and use FIQ instead to + filter the codec stream. This is necessary for some boards + where an incompatible codec is connected to this SSI, e.g. + on pca100 and pcm043.
Child 'codec' node required properties: - compatible: Compatible list, contains the name of the codec diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 4d78df7..8b075ef 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -8,6 +8,26 @@ * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of any * kind, whether express or implied. + * + * + * Some notes why imx-pcm-fiq is used instead of DMA on some boards: + * + * The i.MX SSI core has some nasty limitations in AC97 mode. While most + * sane processor vendors have a FIFO per AC97 slot, the i.MX has only + * one FIFO which combines all valid receive slots. We cannot even select + * which slots we want to receive. The WM9712 with which this driver + * was developed with always sends GPIO status data in slot 12 which + * we receive in our (PCM-) data stream. The only chance we have is to + * manually skip this data in the FIQ handler. With sampling rates different + * from 48000Hz not every frame has valid receive data, so the ratio + * between pcm data and GPIO status data changes. Our FIQ handler is not + * able to handle this, hence this driver only works with 48000Hz sampling + * rate. + * Reading and writing AC97 registers is another challenge. The core + * provides us status bits when the read register is updated with *another* + * value. When we read the same register two times (and the register still + * contains the same value) these status bits are not set. We work + * around this by not polling these bits but only wait a fixed delay. */
#include <linux/init.h> @@ -121,11 +141,13 @@ struct fsl_ssi_private {
bool new_binding; bool ssi_on_imx; + bool use_dma; struct clk *clk; struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_rx; struct imx_dma_data filter_data_tx; struct imx_dma_data filter_data_rx; + struct imx_pcm_fiq_params fiq_params;
struct { unsigned int rfrc; @@ -355,7 +377,12 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, */
/* Enable the interrupts and DMA requests */ - write_ssi(SIER_FLAGS, &ssi->sier); + if (ssi_private->use_dma) + write_ssi(SIER_FLAGS, &ssi->sier); + else + write_ssi(CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TFE0_EN | + CCSR_SSI_SIER_RIE | + CCSR_SSI_SIER_RFF0_EN, &ssi->sier);
/* * Set the watermark for transmit FIFI 0 and receive FIFO 0. We @@ -543,7 +570,7 @@ static int fsl_ssi_dai_probe(struct snd_soc_dai *dai) { struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(dai);
- if (ssi_private->ssi_on_imx) { + if (ssi_private->ssi_on_imx && ssi_private->use_dma) { dai->playback_dma_data = &ssi_private->dma_params_tx; dai->capture_dma_data = &ssi_private->dma_params_rx; } @@ -683,6 +710,9 @@ static int fsl_ssi_probe(struct platform_device *pdev)
strcpy(ssi_private->name, p);
+ ssi_private->use_dma = !of_property_read_bool(np, + "fsl,fiq-stream-filter"); + /* Initialize this copy of the CPU DAI driver structure */ memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template, sizeof(fsl_ssi_dai_template)); @@ -707,12 +737,16 @@ static int fsl_ssi_probe(struct platform_device *pdev) return -ENXIO; }
- /* The 'name' should not have any slashes in it. */ - ret = devm_request_irq(&pdev->dev, ssi_private->irq, fsl_ssi_isr, 0, - ssi_private->name, ssi_private); - if (ret < 0) { - dev_err(&pdev->dev, "could not claim irq %u\n", ssi_private->irq); - goto error_irqmap; + if (ssi_private->use_dma) { + /* The 'name' should not have any slashes in it. */ + ret = devm_request_irq(&pdev->dev, ssi_private->irq, + fsl_ssi_isr, 0, ssi_private->name, + ssi_private); + if (ret < 0) { + dev_err(&pdev->dev, "could not claim irq %u\n", + ssi_private->irq); + goto error_irqmap; + } }
/* Are the RX and the TX clocks locked? */ @@ -766,7 +800,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) */ ret = of_property_read_u32_array(pdev->dev.of_node, "fsl,ssi-dma-events", dma_events, 2); - if (ret) { + if (ret && !ssi_private->use_dma) { dev_err(&pdev->dev, "could not get dma events\n"); goto error_clk; } @@ -805,9 +839,30 @@ static int fsl_ssi_probe(struct platform_device *pdev) }
if (ssi_private->ssi_on_imx) { - ret = imx_pcm_dma_init(pdev); - if (ret) - goto error_dev; + if (!ssi_private->use_dma) { + + /* + * Some boards use an incompatible codec. To get it + * working, we are using imx-fiq-pcm-audio, that + * can handle those codecs. DMA is not possible in this + * situation. + */ + + ssi_private->fiq_params.irq = ssi_private->irq; + ssi_private->fiq_params.base = ssi_private->ssi; + ssi_private->fiq_params.dma_params_rx = + &ssi_private->dma_params_rx; + ssi_private->fiq_params.dma_params_tx = + &ssi_private->dma_params_tx; + + ret = imx_pcm_fiq_init(pdev, &ssi_private->fiq_params); + if (ret) + goto error_dev; + } else { + ret = imx_pcm_dma_init(pdev); + if (ret) + goto error_dev; + } }
/*
On Sat, Jul 27, 2013 at 01:31:53PM +0200, Markus Pargmann wrote:
Add support for non-dma pcm for imx platforms with imx-pcm-fiq support. Instead of imx-pcm-audio, in this case imx-pcm-fiq-audio device is added and the SIER flags are set differently.
Applied, thanks. Ideally we'd have a quirk infrastructure to figure out if this is required but it really doesn't seem worth it.
There may be some platforms using fsl-ssi that do not have a DMA driver with generic DMA bindings. So this patch adds support for the generic DMA bindings, while still accepting the old "fsl,dma-events" property if "dmas" is not found.
Signed-off-by: Markus Pargmann mpa@pengutronix.de Tested-by: Shawn Guo shawn.guo@linaro.org --- Documentation/devicetree/bindings/sound/fsl,ssi.txt | 4 ++++ sound/soc/fsl/fsl_ssi.c | 20 ++++++++++++-------- 2 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/fsl,ssi.txt b/Documentation/devicetree/bindings/sound/fsl,ssi.txt index e45cbce..088a2c0 100644 --- a/Documentation/devicetree/bindings/sound/fsl,ssi.txt +++ b/Documentation/devicetree/bindings/sound/fsl,ssi.txt @@ -51,6 +51,10 @@ Optional properties: filter the codec stream. This is necessary for some boards where an incompatible codec is connected to this SSI, e.g. on pca100 and pcm043. +- dmas: Generic dma devicetree binding as described in + Documentation/devicetree/bindings/dma/dma.txt. +- dma-names: Two dmas have to be defined, "tx" and "rx", if fsl,imx-fiq + is not defined.
Child 'codec' node required properties: - compatible: Compatible list, contains the name of the codec diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 8b075ef..0c072ff 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -794,15 +794,19 @@ static int fsl_ssi_probe(struct platform_device *pdev) &ssi_private->filter_data_tx; ssi_private->dma_params_rx.filter_data = &ssi_private->filter_data_rx; - /* - * TODO: This is a temporary solution and should be changed - * to use generic DMA binding later when the helplers get in. - */ - ret = of_property_read_u32_array(pdev->dev.of_node, + if (!of_property_read_bool(pdev->dev.of_node, "dmas") && + ssi_private->use_dma) { + /* + * FIXME: This is a temporary solution until all + * necessary dma drivers support the generic dma + * bindings. + */ + ret = of_property_read_u32_array(pdev->dev.of_node, "fsl,ssi-dma-events", dma_events, 2); - if (ret && !ssi_private->use_dma) { - dev_err(&pdev->dev, "could not get dma events\n"); - goto error_clk; + if (ret && ssi_private->use_dma) { + dev_err(&pdev->dev, "could not get dma events but fsl-ssi is configured to use DMA\n"); + goto error_clk; + } }
shared = of_device_is_compatible(of_get_parent(np),
On Sat, Jul 27, 2013 at 01:31:54PM +0200, Markus Pargmann wrote:
There may be some platforms using fsl-ssi that do not have a DMA driver with generic DMA bindings. So this patch adds support for the generic DMA bindings, while still accepting the old "fsl,dma-events" property if "dmas" is not found.
Applied, thanks.
This patch adds ac97-slave support.
For ac97, the registers have to be setup earlier than for other ssi modes because there is some communication with the external device before streaming. So this patch introduces a fsl_ssi_setup function to setup the registers for different ssi operation modes seperately.
This patch was tested with imx27-pca100.
Signed-off-by: Markus Pargmann mpa@pengutronix.de Tested-by: Shawn Guo shawn.guo@linaro.org --- .../devicetree/bindings/sound/fsl,ssi.txt | 4 + sound/soc/fsl/fsl_ssi.c | 346 ++++++++++++++++----- 2 files changed, 280 insertions(+), 70 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/fsl,ssi.txt b/Documentation/devicetree/bindings/sound/fsl,ssi.txt index 088a2c0..4303b6a 100644 --- a/Documentation/devicetree/bindings/sound/fsl,ssi.txt +++ b/Documentation/devicetree/bindings/sound/fsl,ssi.txt @@ -43,6 +43,10 @@ Required properties: together. This would still allow different sample sizes, but not different sample rates.
+Required are also ac97 link bindings if ac97 is used. See +Documentation/devicetree/bindings/sound/soc-ac97link.txt for the necessary +bindings. + Optional properties: - codec-handle: Phandle to a 'codec' node that defines an audio codec connected to this SSI. This node is typically diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 0c072ff..827af75 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -141,6 +141,7 @@ struct fsl_ssi_private {
bool new_binding; bool ssi_on_imx; + bool imx_ac97; bool use_dma; struct clk *clk; struct snd_dmaengine_dai_dma_data dma_params_tx; @@ -320,6 +321,124 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) return ret; }
+static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private) +{ + struct ccsr_ssi __iomem *ssi = ssi_private->ssi; + u8 i2s_mode; + u8 wm; + int synchronous = ssi_private->cpu_dai_drv.symmetric_rates; + + if (ssi_private->imx_ac97) + i2s_mode = CCSR_SSI_SCR_I2S_MODE_NORMAL | CCSR_SSI_SCR_NET; + else + i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE; + + /* + * Section 16.5 of the MPC8610 reference manual says that the SSI needs + * to be disabled before updating the registers we set here. + */ + write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0); + + /* + * Program the SSI into I2S Slave Non-Network Synchronous mode. Also + * enable the transmit and receive FIFO. + * + * FIXME: Little-endian samples require a different shift dir + */ + write_ssi_mask(&ssi->scr, + CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN, + CCSR_SSI_SCR_TFR_CLK_DIS | + i2s_mode | + (synchronous ? CCSR_SSI_SCR_SYN : 0)); + + write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 | + CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS | + CCSR_SSI_STCR_TSCKP, &ssi->stcr); + + write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 | + CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS | + CCSR_SSI_SRCR_RSCKP, &ssi->srcr); + /* + * The DC and PM bits are only used if the SSI is the clock master. + */ + + /* + * Set the watermark for transmit FIFI 0 and receive FIFO 0. We don't + * use FIFO 1. We program the transmit water to signal a DMA transfer + * if there are only two (or fewer) elements left in the FIFO. Two + * elements equals one frame (left channel, right channel). This value, + * however, depends on the depth of the transmit buffer. + * + * We set the watermark on the same level as the DMA burstsize. For + * fiq it is probably better to use the biggest possible watermark + * size. + */ + if (ssi_private->use_dma) + wm = ssi_private->fifo_depth - 2; + else + wm = ssi_private->fifo_depth; + + write_ssi(CCSR_SSI_SFCSR_TFWM0(wm) | CCSR_SSI_SFCSR_RFWM0(wm) | + CCSR_SSI_SFCSR_TFWM1(wm) | CCSR_SSI_SFCSR_RFWM1(wm), + &ssi->sfcsr); + + /* + * For non-ac97 setups, we keep the SSI disabled because if we enable + * it, then the DMA controller will start. It's not supposed to start + * until the SCR.TE (or SCR.RE) bit is set, but it does anyway. The DMA + * controller will transfer one "BWC" of data (i.e. the amount of data + * that the MR.BWC bits are set to). The reason this is bad is because + * at this point, the PCM driver has not finished initializing the DMA + * controller. + */ + + + /* + * For ac97 interrupts are enabled with the startup of the substream + * because it is also running without an active substream. Normally SSI + * is only enabled when there is a substream. + */ + if (!ssi_private->imx_ac97) { + /* Enable the interrupts and DMA requests */ + if (ssi_private->use_dma) + write_ssi(SIER_FLAGS, &ssi->sier); + else + write_ssi(CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TFE0_EN | + CCSR_SSI_SIER_RIE | + CCSR_SSI_SIER_RFF0_EN, &ssi->sier); + } else { + /* + * Setup the clock control register + */ + write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13), + &ssi->stccr); + write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13), + &ssi->srccr); + + /* + * Enable AC97 mode and startup the SSI + */ + write_ssi(CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV, + &ssi->sacnt); + write_ssi(0xff, &ssi->saccdis); + write_ssi(0x300, &ssi->saccen); + + /* + * Enable SSI + */ + write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN); + write_ssi(CCSR_SSI_SOR_WAIT(3), &ssi->sor); + + /* + * Enable Transmit and Receive + */ + write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE); + } + + return 0; +} + + /** * fsl_ssi_startup: create a new substream * @@ -341,75 +460,14 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, * and initialize the SSI registers. */ if (!ssi_private->first_stream) { - struct ccsr_ssi __iomem *ssi = ssi_private->ssi; - ssi_private->first_stream = substream;
/* - * Section 16.5 of the MPC8610 reference manual says that the - * SSI needs to be disabled before updating the registers we set - * here. - */ - write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0); - - /* - * Program the SSI into I2S Slave Non-Network Synchronous mode. - * Also enable the transmit and receive FIFO. - * - * FIXME: Little-endian samples require a different shift dir - */ - write_ssi_mask(&ssi->scr, - CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN, - CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE - | (synchronous ? CCSR_SSI_SCR_SYN : 0)); - - write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 | - CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS | - CCSR_SSI_STCR_TSCKP, &ssi->stcr); - - write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 | - CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS | - CCSR_SSI_SRCR_RSCKP, &ssi->srcr); - - /* - * The DC and PM bits are only used if the SSI is the clock - * master. - */ - - /* Enable the interrupts and DMA requests */ - if (ssi_private->use_dma) - write_ssi(SIER_FLAGS, &ssi->sier); - else - write_ssi(CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TFE0_EN | - CCSR_SSI_SIER_RIE | - CCSR_SSI_SIER_RFF0_EN, &ssi->sier); - - /* - * Set the watermark for transmit FIFI 0 and receive FIFO 0. We - * don't use FIFO 1. We program the transmit water to signal a - * DMA transfer if there are only two (or fewer) elements left - * in the FIFO. Two elements equals one frame (left channel, - * right channel). This value, however, depends on the depth of - * the transmit buffer. - * - * We program the receive FIFO to notify us if at least two - * elements (one frame) have been written to the FIFO. We could - * make this value larger (and maybe we should), but this way - * data will be written to memory as soon as it's available. - */ - write_ssi(CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) | - CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2), - &ssi->sfcsr); - - /* - * We keep the SSI disabled because if we enable it, then the - * DMA controller will start. It's not supposed to start until - * the SCR.TE (or SCR.RE) bit is set, but it does anyway. The - * DMA controller will transfer one "BWC" of data (i.e. the - * amount of data that the MR.BWC bits are set to). The reason - * this is bad is because at this point, the PCM driver has not - * finished initializing the DMA controller. + * fsl_ssi_setup was already called by ac97_init earlier if + * the driver is in ac97 mode. */ + if (!ssi_private->imx_ac97) + fsl_ssi_setup(ssi_private); } else { if (synchronous) { struct snd_pcm_runtime *first_runtime = @@ -538,7 +596,8 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, else write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_RE, 0);
- if ((read_ssi(&ssi->scr) & (CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0) + if (!ssi_private->imx_ac97 && (read_ssi(&ssi->scr) & + (CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0) write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0); break;
@@ -608,6 +667,133 @@ static const struct snd_soc_component_driver fsl_ssi_component = { .name = "fsl-ssi", };
+/** + * fsl_ssi_ac97_trigger: start and stop the AC97 receive/transmit. + * + * This function is called by ALSA to start, stop, pause, and resume the + * transfer of data. + */ +static int fsl_ssi_ac97_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata( + rtd->cpu_dai); + struct ccsr_ssi __iomem *ssi = ssi_private->ssi; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + write_ssi_mask(&ssi->sier, 0, CCSR_SSI_SIER_TIE | + CCSR_SSI_SIER_TFE0_EN); + else + write_ssi_mask(&ssi->sier, 0, CCSR_SSI_SIER_RIE | + CCSR_SSI_SIER_RFF0_EN); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + write_ssi_mask(&ssi->sier, CCSR_SSI_SIER_TIE | + CCSR_SSI_SIER_TFE0_EN, 0); + else + write_ssi_mask(&ssi->sier, CCSR_SSI_SIER_RIE | + CCSR_SSI_SIER_RFF0_EN, 0); + break; + + default: + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + write_ssi(CCSR_SSI_SOR_TX_CLR, &ssi->sor); + else + write_ssi(CCSR_SSI_SOR_RX_CLR, &ssi->sor); + + return 0; +} + +static const struct snd_soc_dai_ops fsl_ssi_ac97_dai_ops = { + .startup = fsl_ssi_startup, + .shutdown = fsl_ssi_shutdown, + .trigger = fsl_ssi_ac97_trigger, +}; + +static struct snd_soc_dai_driver fsl_ssi_ac97_dai = { + .ac97_control = 1, + .playback = { + .stream_name = "AC97 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "AC97 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &fsl_ssi_ac97_dai_ops, +}; + + +static struct fsl_ssi_private *fsl_ac97_data; + +static void fsl_ssi_ac97_init(void) +{ + fsl_ssi_setup(fsl_ac97_data); +} + +void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, + unsigned short val) +{ + struct ccsr_ssi *ssi = fsl_ac97_data->ssi; + unsigned int lreg; + unsigned int lval; + + if (reg > 0x7f) + return; + + + lreg = reg << 12; + write_ssi(lreg, &ssi->sacadd); + + lval = val << 4; + write_ssi(lval , &ssi->sacdat); + + write_ssi_mask(&ssi->sacnt, CCSR_SSI_SACNT_RDWR_MASK, + CCSR_SSI_SACNT_WR); + udelay(100); +} + +unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, + unsigned short reg) +{ + struct ccsr_ssi *ssi = fsl_ac97_data->ssi; + + unsigned short val = -1; + unsigned int lreg; + + lreg = (reg & 0x7f) << 12; + write_ssi(lreg, &ssi->sacadd); + write_ssi_mask(&ssi->sacnt, CCSR_SSI_SACNT_RDWR_MASK, + CCSR_SSI_SACNT_RD); + + udelay(100); + + val = (read_ssi(&ssi->sacdat) >> 4) & 0xffff; + + return val; +} + +static struct snd_ac97_bus_ops fsl_ssi_ac97_ops = { + .read = fsl_ssi_ac97_read, + .write = fsl_ssi_ac97_write, +}; + /* Show the statistics of a flag only if its interrupt is enabled. The * compiler will optimze this code to a no-op if the interrupt is not * enabled. @@ -684,6 +870,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) struct resource res; char name[64]; bool shared; + bool ac97 = false;
/* SSIs that are not connected on the board should have a * status = "disabled" @@ -694,7 +881,13 @@ static int fsl_ssi_probe(struct platform_device *pdev)
/* We only support the SSI in "I2S Slave" mode */ sprop = of_get_property(np, "fsl,mode", NULL); - if (!sprop || strcmp(sprop, "i2s-slave")) { + if (!sprop) { + dev_err(&pdev->dev, "fsl,mode property is necessary\n"); + return -EINVAL; + } + if (!strcmp(sprop, "ac97-slave")) { + ac97 = true; + } else if (strcmp(sprop, "i2s-slave")) { dev_notice(&pdev->dev, "mode %s is unsupported\n", sprop); return -ENODEV; } @@ -713,9 +906,19 @@ static int fsl_ssi_probe(struct platform_device *pdev) ssi_private->use_dma = !of_property_read_bool(np, "fsl,fiq-stream-filter");
- /* Initialize this copy of the CPU DAI driver structure */ - memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template, - sizeof(fsl_ssi_dai_template)); + if (ac97) { + memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_ac97_dai, + sizeof(fsl_ssi_ac97_dai)); + + fsl_ac97_data = ssi_private; + ssi_private->imx_ac97 = true; + + snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev); + } else { + /* Initialize this copy of the CPU DAI driver structure */ + memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template, + sizeof(fsl_ssi_dai_template)); + } ssi_private->cpu_dai_drv.name = ssi_private->name;
/* Get the addresses and IRQ */ @@ -901,6 +1104,9 @@ static int fsl_ssi_probe(struct platform_device *pdev) }
done: + if (ssi_private->imx_ac97) + fsl_ssi_ac97_init(); + return 0;
error_dai:
Add devicetree support for phycore-ac97 driver in a seperated driver for DT loading. The seperation reduces the confusion with the old style initialization of this driver via late_initcall. Also this driver is using fsl-ssi instead of imx-ssi.
platform_of_node and cpu_of_node are set according to the fsl,audmux phandle.
This patch adds handling of ac97 reset functions according to fsl ac97 support. They are setup from here to avoid board specific code in the generic fsl-ssi driver.
Signed-off-by: Markus Pargmann mpa@pengutronix.de Tested-by: Shawn Guo shawn.guo@linaro.org --- .../bindings/sound/phytec,phycore-ac97.txt | 16 +++ sound/soc/fsl/Kconfig | 15 ++- sound/soc/fsl/Makefile | 2 + sound/soc/fsl/phycore-ac97-dt.c | 124 +++++++++++++++++++++ sound/soc/fsl/phycore-ac97.c | 13 +-- 5 files changed, 156 insertions(+), 14 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/phytec,phycore-ac97.txt create mode 100644 sound/soc/fsl/phycore-ac97-dt.c
diff --git a/Documentation/devicetree/bindings/sound/phytec,phycore-ac97.txt b/Documentation/devicetree/bindings/sound/phytec,phycore-ac97.txt new file mode 100644 index 0000000..b3ce9cb --- /dev/null +++ b/Documentation/devicetree/bindings/sound/phytec,phycore-ac97.txt @@ -0,0 +1,16 @@ +Phytec phycore AC97 + +Required properties: +- compatible: "phytec,phycore-ac97" +- phytec,ssi: A phandle to the ssi device that is connected to ac97. +- phytec,audmux: A phandle to the audmux device. +- audio-codec: phandle to the codec device node. + +Example: + +sound { + compatible = "phytec,phycore-ac97"; + phytec,ssi = <&ssi1>; + phytec,audmux = <&audmux>; + audio-codec = <&codec>; +}; diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 3a79d01..5f992b3 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -142,8 +142,8 @@ config SND_SOC_MX27VIS_AIC32X4 board with TLV320AIC32X4 codec.
config SND_SOC_PHYCORE_AC97 - tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards" - depends on MACH_PCM043 || MACH_PCA100 + tristate "SoC Audio support for Phytec phyCORE boards" + depends on MACH_PCM043 select SND_SOC_AC97_BUS select SND_SOC_WM9712 select SND_SOC_IMX_PCM_FIQ @@ -153,6 +153,17 @@ config SND_SOC_PHYCORE_AC97 Say Y if you want to add support for SoC audio on Phytec phyCORE and phyCARD boards in AC97 mode
+config SND_SOC_PHYCORE_AC97_DT + tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards (devicetree only)" + select SND_SOC_AC97_BUS + select SND_SOC_WM9712 + select SND_SOC_IMX_PCM_FIQ + select SND_SOC_IMX_AUDMUX + select SND_SOC_FSL_SSI + help + Say Y if you want to add support for SoC audio on Phytec phyCORE + and phyCARD boards in AC97 mode when using devicetree. + config SND_SOC_EUKREA_TLV320 tristate "Eukrea TLV320" depends on MACH_EUKREA_MBIMX27_BASEBOARD \ diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index d4b4aa8..31bdd69 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o # i.MX Machine Support snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o snd-soc-phycore-ac97-objs := phycore-ac97.o +snd-soc-phycore-ac97-dt-objs := phycore-ac97-dt.o snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o snd-soc-wm1133-ev1-objs := wm1133-ev1.o snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o @@ -47,6 +48,7 @@ snd-soc-imx-mc13783-objs := imx-mc13783.o
obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o +obj-$(CONFIG_SND_SOC_PHYCORE_AC97_DT) += snd-soc-phycore-ac97-dt.o obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o diff --git a/sound/soc/fsl/phycore-ac97-dt.c b/sound/soc/fsl/phycore-ac97-dt.c new file mode 100644 index 0000000..46b3423 --- /dev/null +++ b/sound/soc/fsl/phycore-ac97-dt.c @@ -0,0 +1,124 @@ +/* + * phycore-ac97-dt.c -- SoC audio for imx_phycore in AC97 mode + * + * Copyright 2013 Markus Pargmann, Pengutronix mpa@pengutronix.de + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> + +#include "imx-audmux.h" +#include "fsl_ssi.h" + +#define DRV_NAME "phycore-ac97-dt-driver" + +struct phycore_ac97_data { + struct snd_soc_card card; + struct device_node *cpu_np; + struct device_node *codec_np; +}; + +static struct snd_soc_dai_link imx_phycore_dai_ac97[] = { + { + .name = "HiFi", + .stream_name = "HiFi", + .codec_dai_name = "wm9712-hifi", + }, +}; + +static struct snd_soc_card imx_phycore = { + .name = "PhyCORE-ac97-audio", + .owner = THIS_MODULE, + .dai_link = imx_phycore_dai_ac97, + .num_links = ARRAY_SIZE(imx_phycore_dai_ac97), +}; + +static const struct of_device_id imx_phycore_ac97_of_dev_id[] = { + { + .compatible = "phytec,phycore-ac97", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, imx_phycore_ac97_of_dev_id); + +static int imx_phycore_ac97_probe(struct platform_device *pdev) +{ + int ret; + struct phycore_ac97_data *priv; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + + if (!priv) + return -ENOMEM; + + imx_phycore.dev = &pdev->dev; + + priv->cpu_np = of_parse_phandle(pdev->dev.of_node, "phytec,ssi", 0); + if (!priv->cpu_np) { + dev_err(&pdev->dev, "No valid ssi phandle found\n"); + return -EINVAL; + } + + priv->codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0); + if (!priv->codec_np) { + dev_err(&pdev->dev, "No valid codec phandle found\n"); + of_node_put(priv->cpu_np); + return -EINVAL; + } + + imx_phycore_dai_ac97[0].cpu_of_node = priv->cpu_np; + imx_phycore_dai_ac97[0].platform_of_node = priv->cpu_np; + imx_phycore_dai_ac97[0].codec_of_node = priv->codec_np; + + ret = snd_soc_register_card(&imx_phycore); + if (ret) { + dev_err(&pdev->dev, "ASoC: soc card registration failed\n"); + of_node_put(priv->cpu_np); + of_node_put(priv->codec_np); + return ret; + } + + dev_set_drvdata(&pdev->dev, priv); + + return ret; +} + +static int imx_phycore_ac97_remove(struct platform_device *pdev) +{ + struct phycore_ac97_data *priv = dev_get_drvdata(&pdev->dev); + + snd_soc_unregister_card(&imx_phycore); + + of_node_put(priv->cpu_np); + of_node_put(priv->codec_np); + + return 0; +} + +static struct platform_driver imx_phycore_ac97_driver = { + .probe = imx_phycore_ac97_probe, + .remove = imx_phycore_ac97_remove, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = imx_phycore_ac97_of_dev_id, + }, +}; + +module_platform_driver(imx_phycore_ac97_driver); + +MODULE_AUTHOR("Markus Pargmann mpa@pengutronix.de"); +MODULE_DESCRIPTION(DRV_NAME ": PhyCORE ALSA SoC DT driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/phycore-ac97.c b/sound/soc/fsl/phycore-ac97.c index ae403c2..4126edc 100644 --- a/sound/soc/fsl/phycore-ac97.c +++ b/sound/soc/fsl/phycore-ac97.c @@ -52,18 +52,7 @@ static int __init imx_phycore_init(void) { int ret;
- if (machine_is_pca100()) { - imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, - IMX_AUDMUX_V1_PCR_SYN | /* 4wire mode */ - IMX_AUDMUX_V1_PCR_TFCSEL(3) | - IMX_AUDMUX_V1_PCR_TCLKDIR | /* clock is output */ - IMX_AUDMUX_V1_PCR_RXDSEL(3)); - imx_audmux_v1_configure_port(3, - IMX_AUDMUX_V1_PCR_SYN | /* 4wire mode */ - IMX_AUDMUX_V1_PCR_TFCSEL(0) | - IMX_AUDMUX_V1_PCR_TFSDIR | - IMX_AUDMUX_V1_PCR_RXDSEL(0)); - } else if (machine_is_pcm043()) { + if (machine_is_pcm043()) { imx_audmux_v2_configure_port(3, IMX_AUDMUX_V2_PTCR_SYN | /* 4wire mode */ IMX_AUDMUX_V2_PTCR_TFSEL(0) |
participants (2)
-
Mark Brown
-
Markus Pargmann