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> 16*687b81d0SWolfram 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 { 36c448303eSShawn Guo struct imx_sgtl5000_data *data = container_of(rtd->card, 37c448303eSShawn Guo struct imx_sgtl5000_data, card); 38c448303eSShawn Guo struct device *dev = rtd->card->dev; 39c448303eSShawn Guo int ret; 40c448303eSShawn Guo 41c448303eSShawn Guo ret = snd_soc_dai_set_sysclk(rtd->codec_dai, SGTL5000_SYSCLK, 42c448303eSShawn Guo data->clk_frequency, SND_SOC_CLOCK_IN); 43c448303eSShawn Guo if (ret) { 44c448303eSShawn Guo dev_err(dev, "could not set codec driver clock params\n"); 45c448303eSShawn Guo return ret; 46c448303eSShawn Guo } 47c448303eSShawn Guo 48c448303eSShawn Guo return 0; 49c448303eSShawn Guo } 50c448303eSShawn Guo 51172b4c5cSShawn Guo static const struct snd_soc_dapm_widget imx_sgtl5000_dapm_widgets[] = { 52172b4c5cSShawn Guo SND_SOC_DAPM_MIC("Mic Jack", NULL), 53172b4c5cSShawn Guo SND_SOC_DAPM_LINE("Line In Jack", NULL), 54172b4c5cSShawn Guo SND_SOC_DAPM_HP("Headphone Jack", NULL), 55172b4c5cSShawn Guo SND_SOC_DAPM_SPK("Line Out Jack", NULL), 56172b4c5cSShawn Guo SND_SOC_DAPM_SPK("Ext Spk", NULL), 57172b4c5cSShawn Guo }; 58172b4c5cSShawn Guo 59a0a3d518SBill Pemberton static int imx_sgtl5000_probe(struct platform_device *pdev) 60c448303eSShawn Guo { 61c448303eSShawn Guo struct device_node *np = pdev->dev.of_node; 62c448303eSShawn Guo struct device_node *ssi_np, *codec_np; 63c448303eSShawn Guo struct platform_device *ssi_pdev; 6481e8e492SRichard Zhao struct i2c_client *codec_dev; 65c448303eSShawn Guo struct imx_sgtl5000_data *data; 66c448303eSShawn Guo int int_port, ext_port; 67c448303eSShawn Guo int ret; 68c448303eSShawn Guo 69c448303eSShawn Guo ret = of_property_read_u32(np, "mux-int-port", &int_port); 70c448303eSShawn Guo if (ret) { 71c448303eSShawn Guo dev_err(&pdev->dev, "mux-int-port missing or invalid\n"); 72c448303eSShawn Guo return ret; 73c448303eSShawn Guo } 74c448303eSShawn Guo ret = of_property_read_u32(np, "mux-ext-port", &ext_port); 75c448303eSShawn Guo if (ret) { 76c448303eSShawn Guo dev_err(&pdev->dev, "mux-ext-port missing or invalid\n"); 77c448303eSShawn Guo return ret; 78c448303eSShawn Guo } 79c448303eSShawn Guo 80c448303eSShawn Guo /* 81c448303eSShawn Guo * The port numbering in the hardware manual starts at 1, while 82c448303eSShawn Guo * the audmux API expects it starts at 0. 83c448303eSShawn Guo */ 84c448303eSShawn Guo int_port--; 85c448303eSShawn Guo ext_port--; 86c448303eSShawn Guo ret = imx_audmux_v2_configure_port(int_port, 87c448303eSShawn Guo IMX_AUDMUX_V2_PTCR_SYN | 88c448303eSShawn Guo IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | 89c448303eSShawn Guo IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | 90c448303eSShawn Guo IMX_AUDMUX_V2_PTCR_TFSDIR | 91c448303eSShawn Guo IMX_AUDMUX_V2_PTCR_TCLKDIR, 92c448303eSShawn Guo IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); 93c448303eSShawn Guo if (ret) { 94c448303eSShawn Guo dev_err(&pdev->dev, "audmux internal port setup failed\n"); 95c448303eSShawn Guo return ret; 96c448303eSShawn Guo } 97db8b624dSJulia Lawall ret = imx_audmux_v2_configure_port(ext_port, 98ef3207c5SHui Wang IMX_AUDMUX_V2_PTCR_SYN, 99c448303eSShawn Guo IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); 100c448303eSShawn Guo if (ret) { 101c448303eSShawn Guo dev_err(&pdev->dev, "audmux external port setup failed\n"); 102c448303eSShawn Guo return ret; 103c448303eSShawn Guo } 104c448303eSShawn Guo 105c448303eSShawn Guo ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0); 106c448303eSShawn Guo codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0); 107c448303eSShawn Guo if (!ssi_np || !codec_np) { 108c448303eSShawn Guo dev_err(&pdev->dev, "phandle missing or invalid\n"); 109717071dcSRichard Zhao ret = -EINVAL; 110717071dcSRichard Zhao goto fail; 111c448303eSShawn Guo } 112c448303eSShawn Guo 113c448303eSShawn Guo ssi_pdev = of_find_device_by_node(ssi_np); 114c448303eSShawn Guo if (!ssi_pdev) { 115c448303eSShawn Guo dev_err(&pdev->dev, "failed to find SSI platform device\n"); 11628e5ca73SArnaud Patard (Rtp) ret = -EPROBE_DEFER; 117717071dcSRichard Zhao goto fail; 118c448303eSShawn Guo } 11981e8e492SRichard Zhao codec_dev = of_find_i2c_device_by_node(codec_np); 12081e8e492SRichard Zhao if (!codec_dev) { 12181e8e492SRichard Zhao dev_err(&pdev->dev, "failed to find codec platform device\n"); 12228e5ca73SArnaud Patard (Rtp) return -EPROBE_DEFER; 12381e8e492SRichard Zhao } 124c448303eSShawn Guo 125c448303eSShawn Guo data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 126717071dcSRichard Zhao if (!data) { 127717071dcSRichard Zhao ret = -ENOMEM; 128717071dcSRichard Zhao goto fail; 129717071dcSRichard Zhao } 130c448303eSShawn Guo 131b9840124SFabio Estevam data->codec_clk = devm_clk_get(&codec_dev->dev, NULL); 1329e13f345SFabio Estevam if (IS_ERR(data->codec_clk)) 133717071dcSRichard Zhao goto fail; 1349e13f345SFabio Estevam 13581e8e492SRichard Zhao data->clk_frequency = clk_get_rate(data->codec_clk); 136c448303eSShawn Guo 137c448303eSShawn Guo data->dai.name = "HiFi"; 138c448303eSShawn Guo data->dai.stream_name = "HiFi"; 139c448303eSShawn Guo data->dai.codec_dai_name = "sgtl5000"; 140c448303eSShawn Guo data->dai.codec_of_node = codec_np; 141fa659d83SShawn Guo data->dai.cpu_of_node = ssi_np; 142bd41bc96SShawn Guo data->dai.platform_of_node = ssi_np; 143c448303eSShawn Guo data->dai.init = &imx_sgtl5000_dai_init; 144c448303eSShawn Guo data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 145c448303eSShawn Guo SND_SOC_DAIFMT_CBM_CFM; 146c448303eSShawn Guo 147c448303eSShawn Guo data->card.dev = &pdev->dev; 148c448303eSShawn Guo ret = snd_soc_of_parse_card_name(&data->card, "model"); 149c448303eSShawn Guo if (ret) 1509e13f345SFabio Estevam goto fail; 151172b4c5cSShawn Guo ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); 152172b4c5cSShawn Guo if (ret) 1539e13f345SFabio Estevam goto fail; 154c448303eSShawn Guo data->card.num_links = 1; 15529df4306SLothar Waßmann data->card.owner = THIS_MODULE; 156c448303eSShawn Guo data->card.dai_link = &data->dai; 157172b4c5cSShawn Guo data->card.dapm_widgets = imx_sgtl5000_dapm_widgets; 158172b4c5cSShawn Guo data->card.num_dapm_widgets = ARRAY_SIZE(imx_sgtl5000_dapm_widgets); 159c448303eSShawn Guo 160c448303eSShawn Guo ret = snd_soc_register_card(&data->card); 161c448303eSShawn Guo if (ret) { 162c448303eSShawn Guo dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); 1639e13f345SFabio Estevam goto fail; 164c448303eSShawn Guo } 165c448303eSShawn Guo 166c448303eSShawn Guo platform_set_drvdata(pdev, data); 1672fc059f2SFabio Estevam of_node_put(ssi_np); 1682fc059f2SFabio Estevam of_node_put(codec_np); 1692fc059f2SFabio Estevam 1702fc059f2SFabio Estevam return 0; 1712fc059f2SFabio Estevam 172717071dcSRichard Zhao fail: 173717071dcSRichard Zhao if (ssi_np) 174c448303eSShawn Guo of_node_put(ssi_np); 175717071dcSRichard Zhao if (codec_np) 176c448303eSShawn Guo of_node_put(codec_np); 177c448303eSShawn Guo 178717071dcSRichard Zhao return ret; 179c448303eSShawn Guo } 180c448303eSShawn Guo 181a0a3d518SBill Pemberton static int imx_sgtl5000_remove(struct platform_device *pdev) 182c448303eSShawn Guo { 183c448303eSShawn Guo struct imx_sgtl5000_data *data = platform_get_drvdata(pdev); 184c448303eSShawn Guo 185c448303eSShawn Guo snd_soc_unregister_card(&data->card); 186c448303eSShawn Guo 187c448303eSShawn Guo return 0; 188c448303eSShawn Guo } 189c448303eSShawn Guo 190c448303eSShawn Guo static const struct of_device_id imx_sgtl5000_dt_ids[] = { 191c448303eSShawn Guo { .compatible = "fsl,imx-audio-sgtl5000", }, 192c448303eSShawn Guo { /* sentinel */ } 193c448303eSShawn Guo }; 194c448303eSShawn Guo MODULE_DEVICE_TABLE(of, imx_sgtl5000_dt_ids); 195c448303eSShawn Guo 196c448303eSShawn Guo static struct platform_driver imx_sgtl5000_driver = { 197c448303eSShawn Guo .driver = { 198c448303eSShawn Guo .name = "imx-sgtl5000", 199c448303eSShawn Guo .owner = THIS_MODULE, 200c448303eSShawn Guo .of_match_table = imx_sgtl5000_dt_ids, 201c448303eSShawn Guo }, 202c448303eSShawn Guo .probe = imx_sgtl5000_probe, 203a0a3d518SBill Pemberton .remove = imx_sgtl5000_remove, 204c448303eSShawn Guo }; 205c448303eSShawn Guo module_platform_driver(imx_sgtl5000_driver); 206c448303eSShawn Guo 207c448303eSShawn Guo MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>"); 208c448303eSShawn Guo MODULE_DESCRIPTION("Freescale i.MX SGTL5000 ASoC machine driver"); 209c448303eSShawn Guo MODULE_LICENSE("GPL v2"); 210c448303eSShawn Guo MODULE_ALIAS("platform:imx-sgtl5000"); 211