0001
0002
0003
0004
0005
0006 #include <linux/gpio.h>
0007 #include <linux/module.h>
0008 #include <linux/of.h>
0009 #include <linux/of_platform.h>
0010 #include <linux/i2c.h>
0011 #include <linux/of_gpio.h>
0012 #include <sound/soc.h>
0013 #include <sound/jack.h>
0014
0015 #include "imx-audmux.h"
0016
0017 #define DAI_NAME_SIZE 32
0018 #define MUX_PORT_MAX 7
0019
0020 struct imx_es8328_data {
0021 struct device *dev;
0022 struct snd_soc_dai_link dai;
0023 struct snd_soc_card card;
0024 char codec_dai_name[DAI_NAME_SIZE];
0025 char platform_name[DAI_NAME_SIZE];
0026 int jack_gpio;
0027 };
0028
0029 static struct snd_soc_jack_gpio headset_jack_gpios[] = {
0030 {
0031 .gpio = -1,
0032 .name = "headset-gpio",
0033 .report = SND_JACK_HEADSET,
0034 .invert = 0,
0035 .debounce_time = 200,
0036 },
0037 };
0038
0039 static struct snd_soc_jack headset_jack;
0040
0041 static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd)
0042 {
0043 struct imx_es8328_data *data = container_of(rtd->card,
0044 struct imx_es8328_data, card);
0045 int ret = 0;
0046
0047
0048 if (gpio_is_valid(data->jack_gpio)) {
0049 ret = snd_soc_card_jack_new(rtd->card, "Headphone",
0050 SND_JACK_HEADPHONE | SND_JACK_BTN_0,
0051 &headset_jack);
0052 if (ret)
0053 return ret;
0054
0055 headset_jack_gpios[0].gpio = data->jack_gpio;
0056 ret = snd_soc_jack_add_gpios(&headset_jack,
0057 ARRAY_SIZE(headset_jack_gpios),
0058 headset_jack_gpios);
0059 }
0060
0061 return ret;
0062 }
0063
0064 static const struct snd_soc_dapm_widget imx_es8328_dapm_widgets[] = {
0065 SND_SOC_DAPM_MIC("Mic Jack", NULL),
0066 SND_SOC_DAPM_HP("Headphone", NULL),
0067 SND_SOC_DAPM_SPK("Speaker", NULL),
0068 SND_SOC_DAPM_REGULATOR_SUPPLY("audio-amp", 1, 0),
0069 };
0070
0071 static int imx_es8328_probe(struct platform_device *pdev)
0072 {
0073 struct device_node *np = pdev->dev.of_node;
0074 struct device_node *ssi_np = NULL, *codec_np = NULL;
0075 struct platform_device *ssi_pdev;
0076 struct imx_es8328_data *data;
0077 struct snd_soc_dai_link_component *comp;
0078 u32 int_port, ext_port;
0079 int ret;
0080 struct device *dev = &pdev->dev;
0081
0082 ret = of_property_read_u32(np, "mux-int-port", &int_port);
0083 if (ret) {
0084 dev_err(dev, "mux-int-port missing or invalid\n");
0085 goto fail;
0086 }
0087 if (int_port > MUX_PORT_MAX || int_port == 0) {
0088 dev_err(dev, "mux-int-port: hardware only has %d mux ports\n",
0089 MUX_PORT_MAX);
0090 ret = -EINVAL;
0091 goto fail;
0092 }
0093
0094 ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
0095 if (ret) {
0096 dev_err(dev, "mux-ext-port missing or invalid\n");
0097 goto fail;
0098 }
0099 if (ext_port > MUX_PORT_MAX || ext_port == 0) {
0100 dev_err(dev, "mux-ext-port: hardware only has %d mux ports\n",
0101 MUX_PORT_MAX);
0102 ret = -EINVAL;
0103 goto fail;
0104 }
0105
0106
0107
0108
0109
0110 int_port--;
0111 ext_port--;
0112 ret = imx_audmux_v2_configure_port(int_port,
0113 IMX_AUDMUX_V2_PTCR_SYN |
0114 IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
0115 IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
0116 IMX_AUDMUX_V2_PTCR_TFSDIR |
0117 IMX_AUDMUX_V2_PTCR_TCLKDIR,
0118 IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
0119 if (ret) {
0120 dev_err(dev, "audmux internal port setup failed\n");
0121 return ret;
0122 }
0123 ret = imx_audmux_v2_configure_port(ext_port,
0124 IMX_AUDMUX_V2_PTCR_SYN,
0125 IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
0126 if (ret) {
0127 dev_err(dev, "audmux external port setup failed\n");
0128 return ret;
0129 }
0130
0131 ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0);
0132 codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
0133 if (!ssi_np || !codec_np) {
0134 dev_err(dev, "phandle missing or invalid\n");
0135 ret = -EINVAL;
0136 goto fail;
0137 }
0138
0139 ssi_pdev = of_find_device_by_node(ssi_np);
0140 if (!ssi_pdev) {
0141 dev_err(dev, "failed to find SSI platform device\n");
0142 ret = -EINVAL;
0143 goto fail;
0144 }
0145
0146 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
0147 if (!data) {
0148 ret = -ENOMEM;
0149 goto put_device;
0150 }
0151
0152 comp = devm_kzalloc(dev, 3 * sizeof(*comp), GFP_KERNEL);
0153 if (!comp) {
0154 ret = -ENOMEM;
0155 goto put_device;
0156 }
0157
0158 data->dev = dev;
0159
0160 data->jack_gpio = of_get_named_gpio(pdev->dev.of_node, "jack-gpio", 0);
0161
0162 data->dai.cpus = &comp[0];
0163 data->dai.codecs = &comp[1];
0164 data->dai.platforms = &comp[2];
0165
0166 data->dai.num_cpus = 1;
0167 data->dai.num_codecs = 1;
0168 data->dai.num_platforms = 1;
0169
0170 data->dai.name = "hifi";
0171 data->dai.stream_name = "hifi";
0172 data->dai.codecs->dai_name = "es8328-hifi-analog";
0173 data->dai.codecs->of_node = codec_np;
0174 data->dai.cpus->of_node = ssi_np;
0175 data->dai.platforms->of_node = ssi_np;
0176 data->dai.init = &imx_es8328_dai_init;
0177 data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
0178 SND_SOC_DAIFMT_CBP_CFP;
0179
0180 data->card.dev = dev;
0181 data->card.dapm_widgets = imx_es8328_dapm_widgets;
0182 data->card.num_dapm_widgets = ARRAY_SIZE(imx_es8328_dapm_widgets);
0183 ret = snd_soc_of_parse_card_name(&data->card, "model");
0184 if (ret) {
0185 dev_err(dev, "Unable to parse card name\n");
0186 goto put_device;
0187 }
0188 ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
0189 if (ret) {
0190 dev_err(dev, "Unable to parse routing: %d\n", ret);
0191 goto put_device;
0192 }
0193 data->card.num_links = 1;
0194 data->card.owner = THIS_MODULE;
0195 data->card.dai_link = &data->dai;
0196
0197 ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
0198 if (ret) {
0199 dev_err(dev, "Unable to register: %d\n", ret);
0200 goto put_device;
0201 }
0202
0203 platform_set_drvdata(pdev, data);
0204 put_device:
0205 put_device(&ssi_pdev->dev);
0206 fail:
0207 of_node_put(ssi_np);
0208 of_node_put(codec_np);
0209
0210 return ret;
0211 }
0212
0213 static const struct of_device_id imx_es8328_dt_ids[] = {
0214 { .compatible = "fsl,imx-audio-es8328", },
0215 { }
0216 };
0217 MODULE_DEVICE_TABLE(of, imx_es8328_dt_ids);
0218
0219 static struct platform_driver imx_es8328_driver = {
0220 .driver = {
0221 .name = "imx-es8328",
0222 .of_match_table = imx_es8328_dt_ids,
0223 },
0224 .probe = imx_es8328_probe,
0225 };
0226 module_platform_driver(imx_es8328_driver);
0227
0228 MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
0229 MODULE_DESCRIPTION("Kosagi i.MX6 ES8328 ASoC machine driver");
0230 MODULE_LICENSE("GPL v2");
0231 MODULE_ALIAS("platform:imx-audio-es8328");