1c448303eSShawn Guo /* 2c448303eSShawn Guo * Copyright 2012 Freescale Semiconductor, Inc. 3c448303eSShawn Guo * Copyright 2012 Linaro Ltd. 4c448303eSShawn Guo * 5c448303eSShawn Guo * The code contained herein is licensed under the GNU General Public 6c448303eSShawn Guo * License. You may obtain a copy of the GNU General Public License 7c448303eSShawn Guo * Version 2 or later at the following locations: 8c448303eSShawn Guo * 9c448303eSShawn Guo * http://www.opensource.org/licenses/gpl-license.html 10c448303eSShawn Guo * http://www.gnu.org/copyleft/gpl.html 11c448303eSShawn Guo */ 12c448303eSShawn Guo 13c448303eSShawn Guo #include <linux/module.h> 14c448303eSShawn Guo #include <linux/of.h> 15c448303eSShawn Guo #include <linux/of_platform.h> 16687b81d0SWolfram Sang #include <linux/i2c.h> 1781e8e492SRichard Zhao #include <linux/clk.h> 18c448303eSShawn Guo #include <sound/soc.h> 19c448303eSShawn Guo 20c448303eSShawn Guo #include "../codecs/sgtl5000.h" 21c448303eSShawn Guo #include "imx-audmux.h" 22c448303eSShawn Guo 23c448303eSShawn Guo #define DAI_NAME_SIZE 32 24c448303eSShawn Guo 25c448303eSShawn Guo struct imx_sgtl5000_data { 26c448303eSShawn Guo struct snd_soc_dai_link dai; 27c448303eSShawn Guo struct snd_soc_card card; 28c448303eSShawn Guo char codec_dai_name[DAI_NAME_SIZE]; 29c448303eSShawn Guo char platform_name[DAI_NAME_SIZE]; 3081e8e492SRichard Zhao struct clk *codec_clk; 31c448303eSShawn Guo unsigned int clk_frequency; 32c448303eSShawn Guo }; 33c448303eSShawn Guo 34c448303eSShawn Guo static int imx_sgtl5000_dai_init(struct snd_soc_pcm_runtime *rtd) 35c448303eSShawn Guo { 36*47cf84e1SShawn Guo struct imx_sgtl5000_data *data = snd_soc_card_get_drvdata(rtd->card); 37c448303eSShawn Guo struct device *dev = rtd->card->dev; 38c448303eSShawn Guo int ret; 39c448303eSShawn Guo 40c448303eSShawn Guo ret = snd_soc_dai_set_sysclk(rtd->codec_dai, SGTL5000_SYSCLK, 41c448303eSShawn Guo data->clk_frequency, SND_SOC_CLOCK_IN); 42c448303eSShawn Guo if (ret) { 43c448303eSShawn Guo dev_err(dev, "could not set codec driver clock params\n"); 44c448303eSShawn Guo return ret; 45c448303eSShawn Guo } 46c448303eSShawn Guo 47c448303eSShawn Guo return 0; 48c448303eSShawn Guo } 49c448303eSShawn Guo 50172b4c5cSShawn Guo static const struct snd_soc_dapm_widget imx_sgtl5000_dapm_widgets[] = { 51172b4c5cSShawn Guo SND_SOC_DAPM_MIC("Mic Jack", NULL), 52172b4c5cSShawn Guo SND_SOC_DAPM_LINE("Line In Jack", NULL), 53172b4c5cSShawn Guo SND_SOC_DAPM_HP("Headphone Jack", NULL), 54172b4c5cSShawn Guo SND_SOC_DAPM_SPK("Line Out Jack", NULL), 55172b4c5cSShawn Guo SND_SOC_DAPM_SPK("Ext Spk", NULL), 56172b4c5cSShawn Guo }; 57172b4c5cSShawn Guo 58a0a3d518SBill Pemberton static int imx_sgtl5000_probe(struct platform_device *pdev) 59c448303eSShawn Guo { 60c448303eSShawn Guo struct device_node *np = pdev->dev.of_node; 61c448303eSShawn Guo struct device_node *ssi_np, *codec_np; 62c448303eSShawn Guo struct platform_device *ssi_pdev; 6381e8e492SRichard Zhao struct i2c_client *codec_dev; 6450d4a790SPhilipp Zabel struct imx_sgtl5000_data *data = NULL; 65c448303eSShawn Guo int int_port, ext_port; 66c448303eSShawn Guo int ret; 67c448303eSShawn Guo 68c448303eSShawn Guo ret = of_property_read_u32(np, "mux-int-port", &int_port); 69c448303eSShawn Guo if (ret) { 70c448303eSShawn Guo dev_err(&pdev->dev, "mux-int-port missing or invalid\n"); 71c448303eSShawn Guo return ret; 72c448303eSShawn Guo } 73c448303eSShawn Guo ret = of_property_read_u32(np, "mux-ext-port", &ext_port); 74c448303eSShawn Guo if (ret) { 75c448303eSShawn Guo dev_err(&pdev->dev, "mux-ext-port missing or invalid\n"); 76c448303eSShawn Guo return ret; 77c448303eSShawn Guo } 78c448303eSShawn Guo 79c448303eSShawn Guo /* 80c448303eSShawn Guo * The port numbering in the hardware manual starts at 1, while 81c448303eSShawn Guo * the audmux API expects it starts at 0. 82c448303eSShawn Guo */ 83c448303eSShawn Guo int_port--; 84c448303eSShawn Guo ext_port--; 85c448303eSShawn Guo ret = imx_audmux_v2_configure_port(int_port, 86c448303eSShawn Guo IMX_AUDMUX_V2_PTCR_SYN | 87c448303eSShawn Guo IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | 88c448303eSShawn Guo IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | 89c448303eSShawn Guo IMX_AUDMUX_V2_PTCR_TFSDIR | 90c448303eSShawn Guo IMX_AUDMUX_V2_PTCR_TCLKDIR, 91c448303eSShawn Guo IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); 92c448303eSShawn Guo if (ret) { 93c448303eSShawn Guo dev_err(&pdev->dev, "audmux internal port setup failed\n"); 94c448303eSShawn Guo return ret; 95c448303eSShawn Guo } 96db8b624dSJulia Lawall ret = imx_audmux_v2_configure_port(ext_port, 97ef3207c5SHui Wang IMX_AUDMUX_V2_PTCR_SYN, 98c448303eSShawn Guo IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); 99c448303eSShawn Guo if (ret) { 100c448303eSShawn Guo dev_err(&pdev->dev, "audmux external port setup failed\n"); 101c448303eSShawn Guo return ret; 102c448303eSShawn Guo } 103c448303eSShawn Guo 104c448303eSShawn Guo ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0); 105c448303eSShawn Guo codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0); 106c448303eSShawn Guo if (!ssi_np || !codec_np) { 107c448303eSShawn Guo dev_err(&pdev->dev, "phandle missing or invalid\n"); 108717071dcSRichard Zhao ret = -EINVAL; 109717071dcSRichard Zhao goto fail; 110c448303eSShawn Guo } 111c448303eSShawn Guo 112c448303eSShawn Guo ssi_pdev = of_find_device_by_node(ssi_np); 113c448303eSShawn Guo if (!ssi_pdev) { 114c448303eSShawn Guo dev_err(&pdev->dev, "failed to find SSI platform device\n"); 11528e5ca73SArnaud Patard (Rtp) ret = -EPROBE_DEFER; 116717071dcSRichard Zhao goto fail; 117c448303eSShawn Guo } 11881e8e492SRichard Zhao codec_dev = of_find_i2c_device_by_node(codec_np); 11981e8e492SRichard Zhao if (!codec_dev) { 12081e8e492SRichard Zhao dev_err(&pdev->dev, "failed to find codec platform device\n"); 12128e5ca73SArnaud Patard (Rtp) return -EPROBE_DEFER; 12281e8e492SRichard Zhao } 123c448303eSShawn Guo 124c448303eSShawn Guo data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 125717071dcSRichard Zhao if (!data) { 126717071dcSRichard Zhao ret = -ENOMEM; 127717071dcSRichard Zhao goto fail; 128717071dcSRichard Zhao } 129c448303eSShawn Guo 130a8b22c1cSPhilipp Zabel data->codec_clk = clk_get(&codec_dev->dev, NULL); 131f1269ae4SWei Yongjun if (IS_ERR(data->codec_clk)) { 132f1269ae4SWei Yongjun ret = PTR_ERR(data->codec_clk); 133717071dcSRichard Zhao goto fail; 134f1269ae4SWei Yongjun } 1359e13f345SFabio Estevam 13681e8e492SRichard Zhao data->clk_frequency = clk_get_rate(data->codec_clk); 137c448303eSShawn Guo 138c448303eSShawn Guo data->dai.name = "HiFi"; 139c448303eSShawn Guo data->dai.stream_name = "HiFi"; 140c448303eSShawn Guo data->dai.codec_dai_name = "sgtl5000"; 141c448303eSShawn Guo data->dai.codec_of_node = codec_np; 142fa659d83SShawn Guo data->dai.cpu_of_node = ssi_np; 143bd41bc96SShawn Guo data->dai.platform_of_node = ssi_np; 144c448303eSShawn Guo data->dai.init = &imx_sgtl5000_dai_init; 145c448303eSShawn Guo data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 146c448303eSShawn Guo SND_SOC_DAIFMT_CBM_CFM; 147c448303eSShawn Guo 148c448303eSShawn Guo data->card.dev = &pdev->dev; 149c448303eSShawn Guo ret = snd_soc_of_parse_card_name(&data->card, "model"); 150c448303eSShawn Guo if (ret) 1519e13f345SFabio Estevam goto fail; 152172b4c5cSShawn Guo ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); 153172b4c5cSShawn Guo if (ret) 1549e13f345SFabio Estevam goto fail; 155c448303eSShawn Guo data->card.num_links = 1; 15629df4306SLothar Waßmann data->card.owner = THIS_MODULE; 157c448303eSShawn Guo data->card.dai_link = &data->dai; 158172b4c5cSShawn Guo data->card.dapm_widgets = imx_sgtl5000_dapm_widgets; 159172b4c5cSShawn Guo data->card.num_dapm_widgets = ARRAY_SIZE(imx_sgtl5000_dapm_widgets); 160c448303eSShawn Guo 161*47cf84e1SShawn Guo platform_set_drvdata(pdev, &data->card); 162*47cf84e1SShawn Guo snd_soc_card_set_drvdata(&data->card, data); 163*47cf84e1SShawn Guo 16401984a47SSachin Kamat ret = devm_snd_soc_register_card(&pdev->dev, &data->card); 165c448303eSShawn Guo if (ret) { 166c448303eSShawn Guo dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); 1679e13f345SFabio Estevam goto fail; 168c448303eSShawn Guo } 169c448303eSShawn Guo 1702fc059f2SFabio Estevam of_node_put(ssi_np); 1712fc059f2SFabio Estevam of_node_put(codec_np); 1722fc059f2SFabio Estevam 1732fc059f2SFabio Estevam return 0; 1742fc059f2SFabio Estevam 175717071dcSRichard Zhao fail: 17650d4a790SPhilipp Zabel if (data && !IS_ERR(data->codec_clk)) 177a8b22c1cSPhilipp Zabel clk_put(data->codec_clk); 178717071dcSRichard Zhao if (ssi_np) 179c448303eSShawn Guo of_node_put(ssi_np); 180717071dcSRichard Zhao if (codec_np) 181c448303eSShawn Guo of_node_put(codec_np); 182c448303eSShawn Guo 183717071dcSRichard Zhao return ret; 184c448303eSShawn Guo } 185c448303eSShawn Guo 186a0a3d518SBill Pemberton static int imx_sgtl5000_remove(struct platform_device *pdev) 187c448303eSShawn Guo { 188*47cf84e1SShawn Guo struct snd_soc_card *card = platform_get_drvdata(pdev); 189*47cf84e1SShawn Guo struct imx_sgtl5000_data *data = snd_soc_card_get_drvdata(card); 190c448303eSShawn Guo 191a8b22c1cSPhilipp Zabel clk_put(data->codec_clk); 192c448303eSShawn Guo 193c448303eSShawn Guo return 0; 194c448303eSShawn Guo } 195c448303eSShawn Guo 196c448303eSShawn Guo static const struct of_device_id imx_sgtl5000_dt_ids[] = { 197c448303eSShawn Guo { .compatible = "fsl,imx-audio-sgtl5000", }, 198c448303eSShawn Guo { /* sentinel */ } 199c448303eSShawn Guo }; 200c448303eSShawn Guo MODULE_DEVICE_TABLE(of, imx_sgtl5000_dt_ids); 201c448303eSShawn Guo 202c448303eSShawn Guo static struct platform_driver imx_sgtl5000_driver = { 203c448303eSShawn Guo .driver = { 204c448303eSShawn Guo .name = "imx-sgtl5000", 205c448303eSShawn Guo .owner = THIS_MODULE, 2061abe729fSNicolin Chen .pm = &snd_soc_pm_ops, 207c448303eSShawn Guo .of_match_table = imx_sgtl5000_dt_ids, 208c448303eSShawn Guo }, 209c448303eSShawn Guo .probe = imx_sgtl5000_probe, 2104fa8dbc1SFabio Estevam .remove = imx_sgtl5000_remove, 211c448303eSShawn Guo }; 212c448303eSShawn Guo module_platform_driver(imx_sgtl5000_driver); 213c448303eSShawn Guo 214c448303eSShawn Guo MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>"); 215c448303eSShawn Guo MODULE_DESCRIPTION("Freescale i.MX SGTL5000 ASoC machine driver"); 216c448303eSShawn Guo MODULE_LICENSE("GPL v2"); 217c448303eSShawn Guo MODULE_ALIAS("platform:imx-sgtl5000"); 218