linux/sound/soc/fsl/imx-es8328.c
<<
>>
Prefs
   1/*
   2 * Copyright 2012 Freescale Semiconductor, Inc.
   3 * Copyright 2012 Linaro Ltd.
   4 *
   5 * The code contained herein is licensed under the GNU General Public
   6 * License. You may obtain a copy of the GNU General Public License
   7 * Version 2 or later at the following locations:
   8 *
   9 * http://www.opensource.org/licenses/gpl-license.html
  10 * http://www.gnu.org/copyleft/gpl.html
  11 */
  12
  13#include <linux/gpio.h>
  14#include <linux/module.h>
  15#include <linux/of.h>
  16#include <linux/of_platform.h>
  17#include <linux/i2c.h>
  18#include <linux/of_gpio.h>
  19#include <sound/soc.h>
  20#include <sound/jack.h>
  21
  22#include "imx-audmux.h"
  23
  24#define DAI_NAME_SIZE   32
  25#define MUX_PORT_MAX    7
  26
  27struct imx_es8328_data {
  28        struct device *dev;
  29        struct snd_soc_dai_link dai;
  30        struct snd_soc_card card;
  31        char codec_dai_name[DAI_NAME_SIZE];
  32        char platform_name[DAI_NAME_SIZE];
  33        int jack_gpio;
  34};
  35
  36static struct snd_soc_jack_gpio headset_jack_gpios[] = {
  37        {
  38                .gpio = -1,
  39                .name = "headset-gpio",
  40                .report = SND_JACK_HEADSET,
  41                .invert = 0,
  42                .debounce_time = 200,
  43        },
  44};
  45
  46static struct snd_soc_jack headset_jack;
  47
  48static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd)
  49{
  50        struct imx_es8328_data *data = container_of(rtd->card,
  51                                        struct imx_es8328_data, card);
  52        int ret = 0;
  53
  54        /* Headphone jack detection */
  55        if (gpio_is_valid(data->jack_gpio)) {
  56                ret = snd_soc_card_jack_new(rtd->card, "Headphone",
  57                                            SND_JACK_HEADPHONE | SND_JACK_BTN_0,
  58                                            &headset_jack, NULL, 0);
  59                if (ret)
  60                        return ret;
  61
  62                headset_jack_gpios[0].gpio = data->jack_gpio;
  63                ret = snd_soc_jack_add_gpios(&headset_jack,
  64                                             ARRAY_SIZE(headset_jack_gpios),
  65                                             headset_jack_gpios);
  66        }
  67
  68        return ret;
  69}
  70
  71static const struct snd_soc_dapm_widget imx_es8328_dapm_widgets[] = {
  72        SND_SOC_DAPM_MIC("Mic Jack", NULL),
  73        SND_SOC_DAPM_HP("Headphone", NULL),
  74        SND_SOC_DAPM_SPK("Speaker", NULL),
  75        SND_SOC_DAPM_REGULATOR_SUPPLY("audio-amp", 1, 0),
  76};
  77
  78static int imx_es8328_probe(struct platform_device *pdev)
  79{
  80        struct device_node *np = pdev->dev.of_node;
  81        struct device_node *ssi_np = NULL, *codec_np = NULL;
  82        struct platform_device *ssi_pdev;
  83        struct imx_es8328_data *data;
  84        u32 int_port, ext_port;
  85        int ret;
  86        struct device *dev = &pdev->dev;
  87
  88        ret = of_property_read_u32(np, "mux-int-port", &int_port);
  89        if (ret) {
  90                dev_err(dev, "mux-int-port missing or invalid\n");
  91                goto fail;
  92        }
  93        if (int_port > MUX_PORT_MAX || int_port == 0) {
  94                dev_err(dev, "mux-int-port: hardware only has %d mux ports\n",
  95                        MUX_PORT_MAX);
  96                goto fail;
  97        }
  98
  99        ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
 100        if (ret) {
 101                dev_err(dev, "mux-ext-port missing or invalid\n");
 102                goto fail;
 103        }
 104        if (ext_port > MUX_PORT_MAX || ext_port == 0) {
 105                dev_err(dev, "mux-ext-port: hardware only has %d mux ports\n",
 106                        MUX_PORT_MAX);
 107                ret = -EINVAL;
 108                goto fail;
 109        }
 110
 111        /*
 112         * The port numbering in the hardware manual starts at 1, while
 113         * the audmux API expects it starts at 0.
 114         */
 115        int_port--;
 116        ext_port--;
 117        ret = imx_audmux_v2_configure_port(int_port,
 118                        IMX_AUDMUX_V2_PTCR_SYN |
 119                        IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
 120                        IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
 121                        IMX_AUDMUX_V2_PTCR_TFSDIR |
 122                        IMX_AUDMUX_V2_PTCR_TCLKDIR,
 123                        IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
 124        if (ret) {
 125                dev_err(dev, "audmux internal port setup failed\n");
 126                return ret;
 127        }
 128        ret = imx_audmux_v2_configure_port(ext_port,
 129                        IMX_AUDMUX_V2_PTCR_SYN,
 130                        IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
 131        if (ret) {
 132                dev_err(dev, "audmux external port setup failed\n");
 133                return ret;
 134        }
 135
 136        ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0);
 137        codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
 138        if (!ssi_np || !codec_np) {
 139                dev_err(dev, "phandle missing or invalid\n");
 140                ret = -EINVAL;
 141                goto fail;
 142        }
 143
 144        ssi_pdev = of_find_device_by_node(ssi_np);
 145        if (!ssi_pdev) {
 146                dev_err(dev, "failed to find SSI platform device\n");
 147                ret = -EINVAL;
 148                goto fail;
 149        }
 150
 151        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
 152        if (!data) {
 153                ret = -ENOMEM;
 154                goto fail;
 155        }
 156
 157        data->dev = dev;
 158
 159        data->jack_gpio = of_get_named_gpio(pdev->dev.of_node, "jack-gpio", 0);
 160
 161        data->dai.name = "hifi";
 162        data->dai.stream_name = "hifi";
 163        data->dai.codec_dai_name = "es8328-hifi-analog";
 164        data->dai.codec_of_node = codec_np;
 165        data->dai.cpu_of_node = ssi_np;
 166        data->dai.platform_of_node = ssi_np;
 167        data->dai.init = &imx_es8328_dai_init;
 168        data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 169                            SND_SOC_DAIFMT_CBM_CFM;
 170
 171        data->card.dev = dev;
 172        data->card.dapm_widgets = imx_es8328_dapm_widgets;
 173        data->card.num_dapm_widgets = ARRAY_SIZE(imx_es8328_dapm_widgets);
 174        ret = snd_soc_of_parse_card_name(&data->card, "model");
 175        if (ret) {
 176                dev_err(dev, "Unable to parse card name\n");
 177                goto fail;
 178        }
 179        ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
 180        if (ret) {
 181                dev_err(dev, "Unable to parse routing: %d\n", ret);
 182                goto fail;
 183        }
 184        data->card.num_links = 1;
 185        data->card.owner = THIS_MODULE;
 186        data->card.dai_link = &data->dai;
 187
 188        ret = snd_soc_register_card(&data->card);
 189        if (ret) {
 190                dev_err(dev, "Unable to register: %d\n", ret);
 191                goto fail;
 192        }
 193
 194        platform_set_drvdata(pdev, data);
 195fail:
 196        of_node_put(ssi_np);
 197        of_node_put(codec_np);
 198
 199        return ret;
 200}
 201
 202static int imx_es8328_remove(struct platform_device *pdev)
 203{
 204        struct imx_es8328_data *data = platform_get_drvdata(pdev);
 205
 206        snd_soc_jack_free_gpios(&headset_jack, ARRAY_SIZE(headset_jack_gpios),
 207                                headset_jack_gpios);
 208
 209        snd_soc_unregister_card(&data->card);
 210
 211        return 0;
 212}
 213
 214static const struct of_device_id imx_es8328_dt_ids[] = {
 215        { .compatible = "fsl,imx-audio-es8328", },
 216        { /* sentinel */ }
 217};
 218MODULE_DEVICE_TABLE(of, imx_es8328_dt_ids);
 219
 220static struct platform_driver imx_es8328_driver = {
 221        .driver = {
 222                .name = "imx-es8328",
 223                .of_match_table = imx_es8328_dt_ids,
 224        },
 225        .probe = imx_es8328_probe,
 226        .remove = imx_es8328_remove,
 227};
 228module_platform_driver(imx_es8328_driver);
 229
 230MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
 231MODULE_DESCRIPTION("Kosagi i.MX6 ES8328 ASoC machine driver");
 232MODULE_LICENSE("GPL v2");
 233MODULE_ALIAS("platform:imx-audio-es8328");
 234