-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ASoC: fsl: add imx-es8328 machine driver
This adds an initial machine driver for the ES8328 audio codec on Freescale boards. The driver supports headphones and an audio regulator for an onboard speaker amp. Signed-off-by: Sean Cross <xobs@kosagi.com> Signed-off-by: Mark Brown <broonie@linaro.org>
- Loading branch information
Sean Cross
authored and
Mark Brown
committed
Aug 16, 2014
1 parent
e1a6537
commit 7e7292d
Showing
4 changed files
with
308 additions
and
0 deletions.
There are no files selected for viewing
60 changes: 60 additions & 0 deletions
60
Documentation/devicetree/bindings/sound/imx-audio-es8328.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
Freescale i.MX audio complex with ES8328 codec | ||
|
||
Required properties: | ||
- compatible : "fsl,imx-audio-es8328" | ||
- model : The user-visible name of this sound complex | ||
- ssi-controller : The phandle of the i.MX SSI controller | ||
- jack-gpio : Optional GPIO for headphone jack | ||
- audio-amp-supply : Power regulator for speaker amps | ||
- audio-codec : The phandle of the ES8328 audio codec | ||
- audio-routing : A list of the connections between audio components. | ||
Each entry is a pair of strings, the first being the | ||
connection's sink, the second being the connection's | ||
source. Valid names could be power supplies, ES8328 | ||
pins, and the jacks on the board: | ||
|
||
Power supplies: | ||
* audio-amp | ||
|
||
ES8328 pins: | ||
* LOUT1 | ||
* LOUT2 | ||
* ROUT1 | ||
* ROUT2 | ||
* LINPUT1 | ||
* LINPUT2 | ||
* RINPUT1 | ||
* RINPUT2 | ||
* Mic PGA | ||
|
||
Board connectors: | ||
* Headphone | ||
* Speaker | ||
* Mic Jack | ||
- mux-int-port : The internal port of the i.MX audio muxer (AUDMUX) | ||
- mux-ext-port : The external port of the i.MX audio muxer (AUDMIX) | ||
|
||
Note: The AUDMUX port numbering should start at 1, which is consistent with | ||
hardware manual. | ||
|
||
Example: | ||
|
||
sound { | ||
compatible = "fsl,imx-audio-es8328"; | ||
model = "imx-audio-es8328"; | ||
ssi-controller = <&ssi1>; | ||
audio-codec = <&codec>; | ||
jack-gpio = <&gpio5 15 0>; | ||
audio-amp-supply = <®_audio_amp>; | ||
audio-routing = | ||
"Speaker", "LOUT2", | ||
"Speaker", "ROUT2", | ||
"Speaker", "audio-amp", | ||
"Headphone", "ROUT1", | ||
"Headphone", "LOUT1", | ||
"LINPUT1", "Mic Jack", | ||
"RINPUT1", "Mic Jack", | ||
"Mic Jack", "Mic Bias"; | ||
mux-int-port = <1>; | ||
mux-ext-port = <3>; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
/* | ||
* Copyright 2012 Freescale Semiconductor, Inc. | ||
* Copyright 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/gpio.h> | ||
#include <linux/module.h> | ||
#include <linux/of.h> | ||
#include <linux/of_platform.h> | ||
#include <linux/i2c.h> | ||
#include <linux/of_gpio.h> | ||
#include <sound/soc.h> | ||
#include <sound/jack.h> | ||
|
||
#include "imx-audmux.h" | ||
|
||
#define DAI_NAME_SIZE 32 | ||
#define MUX_PORT_MAX 7 | ||
|
||
struct imx_es8328_data { | ||
struct device *dev; | ||
struct snd_soc_dai_link dai; | ||
struct snd_soc_card card; | ||
char codec_dai_name[DAI_NAME_SIZE]; | ||
char platform_name[DAI_NAME_SIZE]; | ||
int jack_gpio; | ||
}; | ||
|
||
static struct snd_soc_jack_gpio headset_jack_gpios[] = { | ||
{ | ||
.gpio = -1, | ||
.name = "headset-gpio", | ||
.report = SND_JACK_HEADSET, | ||
.invert = 0, | ||
.debounce_time = 200, | ||
}, | ||
}; | ||
|
||
static struct snd_soc_jack headset_jack; | ||
|
||
static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd) | ||
{ | ||
struct imx_es8328_data *data = container_of(rtd->card, | ||
struct imx_es8328_data, card); | ||
int ret = 0; | ||
|
||
/* Headphone jack detection */ | ||
if (gpio_is_valid(data->jack_gpio)) { | ||
ret = snd_soc_jack_new(rtd->codec, "Headphone", | ||
SND_JACK_HEADPHONE | SND_JACK_BTN_0, | ||
&headset_jack); | ||
if (ret) | ||
return ret; | ||
|
||
headset_jack_gpios[0].gpio = data->jack_gpio; | ||
ret = snd_soc_jack_add_gpios(&headset_jack, | ||
ARRAY_SIZE(headset_jack_gpios), | ||
headset_jack_gpios); | ||
} | ||
|
||
return ret; | ||
} | ||
|
||
static const struct snd_soc_dapm_widget imx_es8328_dapm_widgets[] = { | ||
SND_SOC_DAPM_MIC("Mic Jack", NULL), | ||
SND_SOC_DAPM_HP("Headphone", NULL), | ||
SND_SOC_DAPM_SPK("Speaker", NULL), | ||
SND_SOC_DAPM_REGULATOR_SUPPLY("audio-amp", 1, 0), | ||
}; | ||
|
||
static int imx_es8328_probe(struct platform_device *pdev) | ||
{ | ||
struct device_node *np = pdev->dev.of_node; | ||
struct device_node *ssi_np, *codec_np; | ||
struct platform_device *ssi_pdev; | ||
struct imx_es8328_data *data; | ||
u32 int_port, ext_port; | ||
int ret; | ||
struct device *dev = &pdev->dev; | ||
|
||
ret = of_property_read_u32(np, "mux-int-port", &int_port); | ||
if (ret) { | ||
dev_err(dev, "mux-int-port missing or invalid\n"); | ||
goto fail; | ||
} | ||
if (int_port > MUX_PORT_MAX || int_port == 0) { | ||
dev_err(dev, "mux-int-port: hardware only has %d mux ports\n", | ||
MUX_PORT_MAX); | ||
goto fail; | ||
} | ||
|
||
ret = of_property_read_u32(np, "mux-ext-port", &ext_port); | ||
if (ret) { | ||
dev_err(dev, "mux-ext-port missing or invalid\n"); | ||
goto fail; | ||
} | ||
if (ext_port > MUX_PORT_MAX || ext_port == 0) { | ||
dev_err(dev, "mux-ext-port: hardware only has %d mux ports\n", | ||
MUX_PORT_MAX); | ||
goto fail; | ||
} | ||
|
||
/* | ||
* The port numbering in the hardware manual starts at 1, while | ||
* the audmux API expects it starts at 0. | ||
*/ | ||
int_port--; | ||
ext_port--; | ||
ret = imx_audmux_v2_configure_port(int_port, | ||
IMX_AUDMUX_V2_PTCR_SYN | | ||
IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | | ||
IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | | ||
IMX_AUDMUX_V2_PTCR_TFSDIR | | ||
IMX_AUDMUX_V2_PTCR_TCLKDIR, | ||
IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); | ||
if (ret) { | ||
dev_err(dev, "audmux internal port setup failed\n"); | ||
return ret; | ||
} | ||
ret = imx_audmux_v2_configure_port(ext_port, | ||
IMX_AUDMUX_V2_PTCR_SYN, | ||
IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); | ||
if (ret) { | ||
dev_err(dev, "audmux external port setup failed\n"); | ||
return ret; | ||
} | ||
|
||
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(dev, "phandle missing or invalid\n"); | ||
ret = -EINVAL; | ||
goto fail; | ||
} | ||
|
||
ssi_pdev = of_find_device_by_node(ssi_np); | ||
if (!ssi_pdev) { | ||
dev_err(dev, "failed to find SSI platform device\n"); | ||
ret = -EINVAL; | ||
goto fail; | ||
} | ||
|
||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); | ||
if (!data) { | ||
ret = -ENOMEM; | ||
goto fail; | ||
} | ||
|
||
data->dev = dev; | ||
|
||
data->jack_gpio = of_get_named_gpio(pdev->dev.of_node, "jack-gpio", 0); | ||
|
||
data->dai.name = "hifi"; | ||
data->dai.stream_name = "hifi"; | ||
data->dai.codec_dai_name = "es8328-hifi-analog"; | ||
data->dai.codec_of_node = codec_np; | ||
data->dai.cpu_of_node = ssi_np; | ||
data->dai.platform_of_node = ssi_np; | ||
data->dai.init = &imx_es8328_dai_init; | ||
data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | ||
SND_SOC_DAIFMT_CBM_CFM; | ||
|
||
data->card.dev = dev; | ||
data->card.dapm_widgets = imx_es8328_dapm_widgets; | ||
data->card.num_dapm_widgets = ARRAY_SIZE(imx_es8328_dapm_widgets); | ||
ret = snd_soc_of_parse_card_name(&data->card, "model"); | ||
if (ret) { | ||
dev_err(dev, "Unable to parse card name\n"); | ||
goto fail; | ||
} | ||
ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); | ||
if (ret) { | ||
dev_err(dev, "Unable to parse routing: %d\n", ret); | ||
goto fail; | ||
} | ||
data->card.num_links = 1; | ||
data->card.owner = THIS_MODULE; | ||
data->card.dai_link = &data->dai; | ||
|
||
ret = snd_soc_register_card(&data->card); | ||
if (ret) { | ||
dev_err(dev, "Unable to register: %d\n", ret); | ||
goto fail; | ||
} | ||
|
||
platform_set_drvdata(pdev, data); | ||
fail: | ||
of_node_put(ssi_np); | ||
of_node_put(codec_np); | ||
|
||
return ret; | ||
} | ||
|
||
static int imx_es8328_remove(struct platform_device *pdev) | ||
{ | ||
struct imx_es8328_data *data = platform_get_drvdata(pdev); | ||
|
||
snd_soc_jack_free_gpios(&headset_jack, ARRAY_SIZE(headset_jack_gpios), | ||
headset_jack_gpios); | ||
|
||
snd_soc_unregister_card(&data->card); | ||
|
||
return 0; | ||
} | ||
|
||
static const struct of_device_id imx_es8328_dt_ids[] = { | ||
{ .compatible = "fsl,imx-audio-es8328", }, | ||
{ /* sentinel */ } | ||
}; | ||
MODULE_DEVICE_TABLE(of, imx_es8328_dt_ids); | ||
|
||
static struct platform_driver imx_es8328_driver = { | ||
.driver = { | ||
.name = "imx-es8328", | ||
.of_match_table = imx_es8328_dt_ids, | ||
}, | ||
.probe = imx_es8328_probe, | ||
.remove = imx_es8328_remove, | ||
}; | ||
module_platform_driver(imx_es8328_driver); | ||
|
||
MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>"); | ||
MODULE_DESCRIPTION("Kosagi i.MX6 ES8328 ASoC machine driver"); | ||
MODULE_LICENSE("GPL v2"); | ||
MODULE_ALIAS("platform:imx-audio-es8328"); |