Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 //
0003 // Copyright 2012 Freescale Semiconductor, Inc.
0004 // Copyright 2012 Linaro Ltd.
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     /* Headphone jack detection */
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      * The port numbering in the hardware manual starts at 1, while
0108      * the audmux API expects it starts at 0.
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     { /* sentinel */ }
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");