1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright 2011 Freescale Semiconductor, Inc. 4 */ 5 6 #include <linux/module.h> 7 #include <linux/device.h> 8 #include <linux/of.h> 9 #include <sound/core.h> 10 #include <sound/pcm.h> 11 #include <sound/soc.h> 12 #include <sound/jack.h> 13 #include <sound/soc-dapm.h> 14 15 #include "../codecs/sgtl5000.h" 16 #include "mxs-saif.h" 17 18 static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream, 19 struct snd_pcm_hw_params *params) 20 { 21 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 22 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); 23 struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); 24 unsigned int rate = params_rate(params); 25 u32 mclk; 26 int ret; 27 28 /* sgtl5000 does not support 512*rate when in 96000 fs */ 29 switch (rate) { 30 case 96000: 31 mclk = 256 * rate; 32 break; 33 default: 34 mclk = 512 * rate; 35 break; 36 } 37 38 /* Set SGTL5000's SYSCLK (provided by SAIF MCLK) */ 39 ret = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, mclk, 0); 40 if (ret) { 41 dev_err(codec_dai->dev, "Failed to set sysclk to %u.%03uMHz\n", 42 mclk / 1000000, mclk / 1000 % 1000); 43 return ret; 44 } 45 46 /* The SAIF MCLK should be the same as SGTL5000_SYSCLK */ 47 ret = snd_soc_dai_set_sysclk(cpu_dai, MXS_SAIF_MCLK, mclk, 0); 48 if (ret) { 49 dev_err(cpu_dai->dev, "Failed to set sysclk to %u.%03uMHz\n", 50 mclk / 1000000, mclk / 1000 % 1000); 51 return ret; 52 } 53 54 return 0; 55 } 56 57 static const struct snd_soc_ops mxs_sgtl5000_hifi_ops = { 58 .hw_params = mxs_sgtl5000_hw_params, 59 }; 60 61 #define MXS_SGTL5000_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \ 62 SND_SOC_DAIFMT_CBS_CFS) 63 64 65 SND_SOC_DAILINK_DEFS(hifi_tx, 66 DAILINK_COMP_ARRAY(COMP_EMPTY()), 67 DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "sgtl5000")), 68 DAILINK_COMP_ARRAY(COMP_EMPTY())); 69 70 SND_SOC_DAILINK_DEFS(hifi_rx, 71 DAILINK_COMP_ARRAY(COMP_EMPTY()), 72 DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "sgtl5000")), 73 DAILINK_COMP_ARRAY(COMP_EMPTY())); 74 75 static struct snd_soc_dai_link mxs_sgtl5000_dai[] = { 76 { 77 .name = "HiFi Tx", 78 .stream_name = "HiFi Playback", 79 .dai_fmt = MXS_SGTL5000_DAI_FMT, 80 .ops = &mxs_sgtl5000_hifi_ops, 81 .playback_only = true, 82 SND_SOC_DAILINK_REG(hifi_tx), 83 }, { 84 .name = "HiFi Rx", 85 .stream_name = "HiFi Capture", 86 .dai_fmt = MXS_SGTL5000_DAI_FMT, 87 .ops = &mxs_sgtl5000_hifi_ops, 88 .capture_only = true, 89 SND_SOC_DAILINK_REG(hifi_rx), 90 }, 91 }; 92 93 static const struct snd_soc_dapm_widget mxs_sgtl5000_dapm_widgets[] = { 94 SND_SOC_DAPM_MIC("Mic Jack", NULL), 95 SND_SOC_DAPM_LINE("Line In Jack", NULL), 96 SND_SOC_DAPM_HP("Headphone Jack", NULL), 97 SND_SOC_DAPM_SPK("Line Out Jack", NULL), 98 SND_SOC_DAPM_SPK("Ext Spk", NULL), 99 }; 100 101 static struct snd_soc_card mxs_sgtl5000 = { 102 .name = "mxs_sgtl5000", 103 .owner = THIS_MODULE, 104 .dai_link = mxs_sgtl5000_dai, 105 .num_links = ARRAY_SIZE(mxs_sgtl5000_dai), 106 }; 107 108 static int mxs_sgtl5000_probe(struct platform_device *pdev) 109 { 110 struct snd_soc_card *card = &mxs_sgtl5000; 111 int ret, i; 112 struct device_node *np = pdev->dev.of_node; 113 struct device_node *saif_np[2], *codec_np; 114 115 saif_np[0] = of_parse_phandle(np, "saif-controllers", 0); 116 saif_np[1] = of_parse_phandle(np, "saif-controllers", 1); 117 codec_np = of_parse_phandle(np, "audio-codec", 0); 118 if (!saif_np[0] || !saif_np[1] || !codec_np) { 119 dev_err(&pdev->dev, "phandle missing or invalid\n"); 120 of_node_put(codec_np); 121 of_node_put(saif_np[0]); 122 of_node_put(saif_np[1]); 123 return -EINVAL; 124 } 125 126 for (i = 0; i < 2; i++) { 127 mxs_sgtl5000_dai[i].codecs->name = NULL; 128 mxs_sgtl5000_dai[i].codecs->of_node = codec_np; 129 mxs_sgtl5000_dai[i].cpus->dai_name = NULL; 130 mxs_sgtl5000_dai[i].cpus->of_node = saif_np[i]; 131 mxs_sgtl5000_dai[i].platforms->name = NULL; 132 mxs_sgtl5000_dai[i].platforms->of_node = saif_np[i]; 133 } 134 135 of_node_put(codec_np); 136 of_node_put(saif_np[0]); 137 of_node_put(saif_np[1]); 138 139 /* 140 * Set an init clock(11.28Mhz) for sgtl5000 initialization(i2c r/w). 141 * The Sgtl5000 sysclk is derived from saif0 mclk and it's range 142 * should be >= 8MHz and <= 27M. 143 */ 144 ret = mxs_saif_get_mclk(0, 44100 * 256, 44100); 145 if (ret) { 146 dev_err(&pdev->dev, "failed to get mclk\n"); 147 return ret; 148 } 149 150 card->dev = &pdev->dev; 151 152 if (of_property_present(np, "audio-routing")) { 153 card->dapm_widgets = mxs_sgtl5000_dapm_widgets; 154 card->num_dapm_widgets = ARRAY_SIZE(mxs_sgtl5000_dapm_widgets); 155 156 ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); 157 if (ret) { 158 dev_err(&pdev->dev, "failed to parse audio-routing (%d)\n", 159 ret); 160 return ret; 161 } 162 } 163 164 ret = devm_snd_soc_register_card(&pdev->dev, card); 165 if (ret) 166 return dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); 167 168 return 0; 169 } 170 171 static void mxs_sgtl5000_remove(struct platform_device *pdev) 172 { 173 mxs_saif_put_mclk(0); 174 } 175 176 static const struct of_device_id mxs_sgtl5000_dt_ids[] = { 177 { .compatible = "fsl,mxs-audio-sgtl5000", }, 178 { /* sentinel */ } 179 }; 180 MODULE_DEVICE_TABLE(of, mxs_sgtl5000_dt_ids); 181 182 static struct platform_driver mxs_sgtl5000_audio_driver = { 183 .driver = { 184 .name = "mxs-sgtl5000", 185 .of_match_table = mxs_sgtl5000_dt_ids, 186 }, 187 .probe = mxs_sgtl5000_probe, 188 .remove = mxs_sgtl5000_remove, 189 }; 190 191 module_platform_driver(mxs_sgtl5000_audio_driver); 192 193 MODULE_AUTHOR("Freescale Semiconductor, Inc."); 194 MODULE_DESCRIPTION("MXS ALSA SoC Machine driver"); 195 MODULE_LICENSE("GPL"); 196 MODULE_ALIAS("platform:mxs-sgtl5000"); 197