1*6b24e03eSFabio Estevam // SPDX-License-Identifier: GPL-2.0+ 2*6b24e03eSFabio Estevam // 3*6b24e03eSFabio Estevam // Copyright 2012 Freescale Semiconductor, Inc. 4*6b24e03eSFabio Estevam // Copyright 2012 Linaro Ltd. 5c448303eSShawn Guo 6c448303eSShawn Guo #include <linux/module.h> 7c448303eSShawn Guo #include <linux/of.h> 8c448303eSShawn Guo #include <linux/of_platform.h> 9687b81d0SWolfram Sang #include <linux/i2c.h> 1081e8e492SRichard Zhao #include <linux/clk.h> 11c448303eSShawn Guo #include <sound/soc.h> 12c448303eSShawn Guo 13c448303eSShawn Guo #include "../codecs/sgtl5000.h" 14c448303eSShawn Guo #include "imx-audmux.h" 15c448303eSShawn Guo 16c448303eSShawn Guo #define DAI_NAME_SIZE 32 17c448303eSShawn Guo 18c448303eSShawn Guo struct imx_sgtl5000_data { 19c448303eSShawn Guo struct snd_soc_dai_link dai; 20c448303eSShawn Guo struct snd_soc_card card; 21c448303eSShawn Guo char codec_dai_name[DAI_NAME_SIZE]; 22c448303eSShawn Guo char platform_name[DAI_NAME_SIZE]; 2381e8e492SRichard Zhao struct clk *codec_clk; 24c448303eSShawn Guo unsigned int clk_frequency; 25c448303eSShawn Guo }; 26c448303eSShawn Guo 27c448303eSShawn Guo static int imx_sgtl5000_dai_init(struct snd_soc_pcm_runtime *rtd) 28c448303eSShawn Guo { 2947cf84e1SShawn Guo struct imx_sgtl5000_data *data = snd_soc_card_get_drvdata(rtd->card); 30c448303eSShawn Guo struct device *dev = rtd->card->dev; 31c448303eSShawn Guo int ret; 32c448303eSShawn Guo 33c448303eSShawn Guo ret = snd_soc_dai_set_sysclk(rtd->codec_dai, SGTL5000_SYSCLK, 34c448303eSShawn Guo data->clk_frequency, SND_SOC_CLOCK_IN); 35c448303eSShawn Guo if (ret) { 36c448303eSShawn Guo dev_err(dev, "could not set codec driver clock params\n"); 37c448303eSShawn Guo return ret; 38c448303eSShawn Guo } 39c448303eSShawn Guo 40c448303eSShawn Guo return 0; 41c448303eSShawn Guo } 42c448303eSShawn Guo 43172b4c5cSShawn Guo static const struct snd_soc_dapm_widget imx_sgtl5000_dapm_widgets[] = { 44172b4c5cSShawn Guo SND_SOC_DAPM_MIC("Mic Jack", NULL), 45172b4c5cSShawn Guo SND_SOC_DAPM_LINE("Line In Jack", NULL), 46172b4c5cSShawn Guo SND_SOC_DAPM_HP("Headphone Jack", NULL), 47172b4c5cSShawn Guo SND_SOC_DAPM_SPK("Line Out Jack", NULL), 48172b4c5cSShawn Guo SND_SOC_DAPM_SPK("Ext Spk", NULL), 49172b4c5cSShawn Guo }; 50172b4c5cSShawn Guo 51a0a3d518SBill Pemberton static int imx_sgtl5000_probe(struct platform_device *pdev) 52c448303eSShawn Guo { 53c448303eSShawn Guo struct device_node *np = pdev->dev.of_node; 54c448303eSShawn Guo struct device_node *ssi_np, *codec_np; 55c448303eSShawn Guo struct platform_device *ssi_pdev; 5681e8e492SRichard Zhao struct i2c_client *codec_dev; 5750d4a790SPhilipp Zabel struct imx_sgtl5000_data *data = NULL; 58c448303eSShawn Guo int int_port, ext_port; 59c448303eSShawn Guo int ret; 60c448303eSShawn Guo 61c448303eSShawn Guo ret = of_property_read_u32(np, "mux-int-port", &int_port); 62c448303eSShawn Guo if (ret) { 63c448303eSShawn Guo dev_err(&pdev->dev, "mux-int-port missing or invalid\n"); 64c448303eSShawn Guo return ret; 65c448303eSShawn Guo } 66c448303eSShawn Guo ret = of_property_read_u32(np, "mux-ext-port", &ext_port); 67c448303eSShawn Guo if (ret) { 68c448303eSShawn Guo dev_err(&pdev->dev, "mux-ext-port missing or invalid\n"); 69c448303eSShawn Guo return ret; 70c448303eSShawn Guo } 71c448303eSShawn Guo 72c448303eSShawn Guo /* 73c448303eSShawn Guo * The port numbering in the hardware manual starts at 1, while 74c448303eSShawn Guo * the audmux API expects it starts at 0. 75c448303eSShawn Guo */ 76c448303eSShawn Guo int_port--; 77c448303eSShawn Guo ext_port--; 78c448303eSShawn Guo ret = imx_audmux_v2_configure_port(int_port, 79c448303eSShawn Guo IMX_AUDMUX_V2_PTCR_SYN | 80c448303eSShawn Guo IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | 81c448303eSShawn Guo IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | 82c448303eSShawn Guo IMX_AUDMUX_V2_PTCR_TFSDIR | 83c448303eSShawn Guo IMX_AUDMUX_V2_PTCR_TCLKDIR, 84c448303eSShawn Guo IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); 85c448303eSShawn Guo if (ret) { 86c448303eSShawn Guo dev_err(&pdev->dev, "audmux internal port setup failed\n"); 87c448303eSShawn Guo return ret; 88c448303eSShawn Guo } 89db8b624dSJulia Lawall ret = imx_audmux_v2_configure_port(ext_port, 90ef3207c5SHui Wang IMX_AUDMUX_V2_PTCR_SYN, 91c448303eSShawn Guo IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); 92c448303eSShawn Guo if (ret) { 93c448303eSShawn Guo dev_err(&pdev->dev, "audmux external port setup failed\n"); 94c448303eSShawn Guo return ret; 95c448303eSShawn Guo } 96c448303eSShawn Guo 97c448303eSShawn Guo ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0); 98c448303eSShawn Guo codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0); 99c448303eSShawn Guo if (!ssi_np || !codec_np) { 100c448303eSShawn Guo dev_err(&pdev->dev, "phandle missing or invalid\n"); 101717071dcSRichard Zhao ret = -EINVAL; 102717071dcSRichard Zhao goto fail; 103c448303eSShawn Guo } 104c448303eSShawn Guo 105c448303eSShawn Guo ssi_pdev = of_find_device_by_node(ssi_np); 106c448303eSShawn Guo if (!ssi_pdev) { 107c448303eSShawn Guo dev_err(&pdev->dev, "failed to find SSI platform device\n"); 10828e5ca73SArnaud Patard (Rtp) ret = -EPROBE_DEFER; 109717071dcSRichard Zhao goto fail; 110c448303eSShawn Guo } 11181e8e492SRichard Zhao codec_dev = of_find_i2c_device_by_node(codec_np); 11281e8e492SRichard Zhao if (!codec_dev) { 11381e8e492SRichard Zhao dev_err(&pdev->dev, "failed to find codec platform device\n"); 11428e5ca73SArnaud Patard (Rtp) return -EPROBE_DEFER; 11581e8e492SRichard Zhao } 116c448303eSShawn Guo 117c448303eSShawn Guo data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 118717071dcSRichard Zhao if (!data) { 119717071dcSRichard Zhao ret = -ENOMEM; 120717071dcSRichard Zhao goto fail; 121717071dcSRichard Zhao } 122c448303eSShawn Guo 123a8b22c1cSPhilipp Zabel data->codec_clk = clk_get(&codec_dev->dev, NULL); 124f1269ae4SWei Yongjun if (IS_ERR(data->codec_clk)) { 125f1269ae4SWei Yongjun ret = PTR_ERR(data->codec_clk); 126717071dcSRichard Zhao goto fail; 127f1269ae4SWei Yongjun } 1289e13f345SFabio Estevam 12981e8e492SRichard Zhao data->clk_frequency = clk_get_rate(data->codec_clk); 130c448303eSShawn Guo 131c448303eSShawn Guo data->dai.name = "HiFi"; 132c448303eSShawn Guo data->dai.stream_name = "HiFi"; 133c448303eSShawn Guo data->dai.codec_dai_name = "sgtl5000"; 134c448303eSShawn Guo data->dai.codec_of_node = codec_np; 135fa659d83SShawn Guo data->dai.cpu_of_node = ssi_np; 136bd41bc96SShawn Guo data->dai.platform_of_node = ssi_np; 137c448303eSShawn Guo data->dai.init = &imx_sgtl5000_dai_init; 138c448303eSShawn Guo data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 139c448303eSShawn Guo SND_SOC_DAIFMT_CBM_CFM; 140c448303eSShawn Guo 141c448303eSShawn Guo data->card.dev = &pdev->dev; 142c448303eSShawn Guo ret = snd_soc_of_parse_card_name(&data->card, "model"); 143c448303eSShawn Guo if (ret) 1449e13f345SFabio Estevam goto fail; 145172b4c5cSShawn Guo ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); 146172b4c5cSShawn Guo if (ret) 1479e13f345SFabio Estevam goto fail; 148c448303eSShawn Guo data->card.num_links = 1; 14929df4306SLothar Waßmann data->card.owner = THIS_MODULE; 150c448303eSShawn Guo data->card.dai_link = &data->dai; 151172b4c5cSShawn Guo data->card.dapm_widgets = imx_sgtl5000_dapm_widgets; 152172b4c5cSShawn Guo data->card.num_dapm_widgets = ARRAY_SIZE(imx_sgtl5000_dapm_widgets); 153c448303eSShawn Guo 15447cf84e1SShawn Guo platform_set_drvdata(pdev, &data->card); 15547cf84e1SShawn Guo snd_soc_card_set_drvdata(&data->card, data); 15647cf84e1SShawn Guo 15701984a47SSachin Kamat ret = devm_snd_soc_register_card(&pdev->dev, &data->card); 158c448303eSShawn Guo if (ret) { 159c448303eSShawn Guo dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); 1609e13f345SFabio Estevam goto fail; 161c448303eSShawn Guo } 162c448303eSShawn Guo 1632fc059f2SFabio Estevam of_node_put(ssi_np); 1642fc059f2SFabio Estevam of_node_put(codec_np); 1652fc059f2SFabio Estevam 1662fc059f2SFabio Estevam return 0; 1672fc059f2SFabio Estevam 168717071dcSRichard Zhao fail: 16950d4a790SPhilipp Zabel if (data && !IS_ERR(data->codec_clk)) 170a8b22c1cSPhilipp Zabel clk_put(data->codec_clk); 171c448303eSShawn Guo of_node_put(ssi_np); 172c448303eSShawn Guo of_node_put(codec_np); 173c448303eSShawn Guo 174717071dcSRichard Zhao return ret; 175c448303eSShawn Guo } 176c448303eSShawn Guo 177a0a3d518SBill Pemberton static int imx_sgtl5000_remove(struct platform_device *pdev) 178c448303eSShawn Guo { 17947cf84e1SShawn Guo struct snd_soc_card *card = platform_get_drvdata(pdev); 18047cf84e1SShawn Guo struct imx_sgtl5000_data *data = snd_soc_card_get_drvdata(card); 181c448303eSShawn Guo 182a8b22c1cSPhilipp Zabel clk_put(data->codec_clk); 183c448303eSShawn Guo 184c448303eSShawn Guo return 0; 185c448303eSShawn Guo } 186c448303eSShawn Guo 187c448303eSShawn Guo static const struct of_device_id imx_sgtl5000_dt_ids[] = { 188c448303eSShawn Guo { .compatible = "fsl,imx-audio-sgtl5000", }, 189c448303eSShawn Guo { /* sentinel */ } 190c448303eSShawn Guo }; 191c448303eSShawn Guo MODULE_DEVICE_TABLE(of, imx_sgtl5000_dt_ids); 192c448303eSShawn Guo 193c448303eSShawn Guo static struct platform_driver imx_sgtl5000_driver = { 194c448303eSShawn Guo .driver = { 195c448303eSShawn Guo .name = "imx-sgtl5000", 1961abe729fSNicolin Chen .pm = &snd_soc_pm_ops, 197c448303eSShawn Guo .of_match_table = imx_sgtl5000_dt_ids, 198c448303eSShawn Guo }, 199c448303eSShawn Guo .probe = imx_sgtl5000_probe, 2004fa8dbc1SFabio Estevam .remove = imx_sgtl5000_remove, 201c448303eSShawn Guo }; 202c448303eSShawn Guo module_platform_driver(imx_sgtl5000_driver); 203c448303eSShawn Guo 204c448303eSShawn Guo MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>"); 205c448303eSShawn Guo MODULE_DESCRIPTION("Freescale i.MX SGTL5000 ASoC machine driver"); 206c448303eSShawn Guo MODULE_LICENSE("GPL v2"); 207c448303eSShawn Guo MODULE_ALIAS("platform:imx-sgtl5000"); 208