1 /* 2 * Copyright (c) 2015 The Linux Foundation. All rights reserved. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 and 6 * only version 2 as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 */ 14 15 #include <linux/device.h> 16 #include <linux/module.h> 17 #include <linux/kernel.h> 18 #include <linux/io.h> 19 #include <linux/of.h> 20 #include <linux/clk.h> 21 #include <linux/platform_device.h> 22 #include <sound/pcm.h> 23 #include <sound/pcm_params.h> 24 #include <sound/jack.h> 25 #include <sound/soc.h> 26 #include <uapi/linux/input-event-codes.h> 27 #include <dt-bindings/sound/apq8016-lpass.h> 28 29 struct apq8016_sbc_data { 30 void __iomem *mic_iomux; 31 void __iomem *spkr_iomux; 32 struct snd_soc_jack jack; 33 bool jack_setup; 34 struct snd_soc_dai_link dai_link[]; /* dynamically allocated */ 35 }; 36 37 #define MIC_CTRL_TER_WS_SLAVE_SEL BIT(21) 38 #define MIC_CTRL_QUA_WS_SLAVE_SEL_10 BIT(17) 39 #define MIC_CTRL_TLMM_SCLK_EN BIT(1) 40 #define SPKR_CTL_PRI_WS_SLAVE_SEL_11 (BIT(17) | BIT(16)) 41 #define DEFAULT_MCLK_RATE 9600000 42 43 static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) 44 { 45 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 46 struct snd_soc_component *component; 47 struct snd_soc_dai_link *dai_link = rtd->dai_link; 48 struct snd_soc_card *card = rtd->card; 49 struct apq8016_sbc_data *pdata = snd_soc_card_get_drvdata(card); 50 int i, rval; 51 52 switch (cpu_dai->id) { 53 case MI2S_PRIMARY: 54 writel(readl(pdata->spkr_iomux) | SPKR_CTL_PRI_WS_SLAVE_SEL_11, 55 pdata->spkr_iomux); 56 break; 57 58 case MI2S_QUATERNARY: 59 /* Configure the Quat MI2S to TLMM */ 60 writel(readl(pdata->mic_iomux) | MIC_CTRL_QUA_WS_SLAVE_SEL_10 | 61 MIC_CTRL_TLMM_SCLK_EN, 62 pdata->mic_iomux); 63 break; 64 case MI2S_TERTIARY: 65 writel(readl(pdata->mic_iomux) | MIC_CTRL_TER_WS_SLAVE_SEL | 66 MIC_CTRL_TLMM_SCLK_EN, 67 pdata->mic_iomux); 68 69 break; 70 71 default: 72 dev_err(card->dev, "unsupported cpu dai configuration\n"); 73 return -EINVAL; 74 75 } 76 77 if (!pdata->jack_setup) { 78 struct snd_jack *jack; 79 80 rval = snd_soc_card_jack_new(card, "Headset Jack", 81 SND_JACK_HEADSET | 82 SND_JACK_HEADPHONE | 83 SND_JACK_BTN_0 | SND_JACK_BTN_1 | 84 SND_JACK_BTN_2 | SND_JACK_BTN_3 | 85 SND_JACK_BTN_4, 86 &pdata->jack, NULL, 0); 87 88 if (rval < 0) { 89 dev_err(card->dev, "Unable to add Headphone Jack\n"); 90 return rval; 91 } 92 93 jack = pdata->jack.jack; 94 95 snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); 96 snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); 97 snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP); 98 snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); 99 pdata->jack_setup = true; 100 } 101 102 for (i = 0 ; i < dai_link->num_codecs; i++) { 103 struct snd_soc_dai *dai = rtd->codec_dais[i]; 104 105 component = dai->component; 106 /* Set default mclk for internal codec */ 107 rval = snd_soc_component_set_sysclk(component, 0, 0, DEFAULT_MCLK_RATE, 108 SND_SOC_CLOCK_IN); 109 if (rval != 0 && rval != -ENOTSUPP) { 110 dev_warn(card->dev, "Failed to set mclk: %d\n", rval); 111 return rval; 112 } 113 rval = snd_soc_component_set_jack(component, &pdata->jack, NULL); 114 if (rval != 0 && rval != -ENOTSUPP) { 115 dev_warn(card->dev, "Failed to set jack: %d\n", rval); 116 return rval; 117 } 118 } 119 120 return 0; 121 } 122 123 static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card) 124 { 125 struct device *dev = card->dev; 126 struct snd_soc_dai_link *link; 127 struct device_node *np, *codec, *cpu, *node = dev->of_node; 128 struct apq8016_sbc_data *data; 129 int ret, num_links; 130 131 ret = snd_soc_of_parse_card_name(card, "qcom,model"); 132 if (ret) { 133 dev_err(dev, "Error parsing card name: %d\n", ret); 134 return ERR_PTR(ret); 135 } 136 137 /* DAPM routes */ 138 if (of_property_read_bool(node, "qcom,audio-routing")) { 139 ret = snd_soc_of_parse_audio_routing(card, 140 "qcom,audio-routing"); 141 if (ret) 142 return ERR_PTR(ret); 143 } 144 145 146 /* Populate links */ 147 num_links = of_get_child_count(node); 148 149 /* Allocate the private data and the DAI link array */ 150 data = devm_kzalloc(dev, 151 struct_size(data, dai_link, num_links), 152 GFP_KERNEL); 153 if (!data) 154 return ERR_PTR(-ENOMEM); 155 156 card->dai_link = &data->dai_link[0]; 157 card->num_links = num_links; 158 159 link = data->dai_link; 160 161 for_each_child_of_node(node, np) { 162 cpu = of_get_child_by_name(np, "cpu"); 163 codec = of_get_child_by_name(np, "codec"); 164 165 if (!cpu || !codec) { 166 dev_err(dev, "Can't find cpu/codec DT node\n"); 167 return ERR_PTR(-EINVAL); 168 } 169 170 link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0); 171 if (!link->cpu_of_node) { 172 dev_err(card->dev, "error getting cpu phandle\n"); 173 return ERR_PTR(-EINVAL); 174 } 175 176 ret = snd_soc_of_get_dai_name(cpu, &link->cpu_dai_name); 177 if (ret) { 178 dev_err(card->dev, "error getting cpu dai name\n"); 179 return ERR_PTR(ret); 180 } 181 182 ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); 183 184 if (ret < 0) { 185 dev_err(card->dev, "error getting codec dai name\n"); 186 return ERR_PTR(ret); 187 } 188 189 link->platform_of_node = link->cpu_of_node; 190 ret = of_property_read_string(np, "link-name", &link->name); 191 if (ret) { 192 dev_err(card->dev, "error getting codec dai_link name\n"); 193 return ERR_PTR(ret); 194 } 195 196 link->stream_name = link->name; 197 link->init = apq8016_sbc_dai_init; 198 link++; 199 } 200 201 return data; 202 } 203 204 static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = { 205 206 SND_SOC_DAPM_MIC("Handset Mic", NULL), 207 SND_SOC_DAPM_MIC("Headset Mic", NULL), 208 SND_SOC_DAPM_MIC("Secondary Mic", NULL), 209 SND_SOC_DAPM_MIC("Digital Mic1", NULL), 210 SND_SOC_DAPM_MIC("Digital Mic2", NULL), 211 }; 212 213 static int apq8016_sbc_platform_probe(struct platform_device *pdev) 214 { 215 struct device *dev = &pdev->dev; 216 struct snd_soc_card *card; 217 struct apq8016_sbc_data *data; 218 struct resource *res; 219 220 card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); 221 if (!card) 222 return -ENOMEM; 223 224 card->dev = dev; 225 card->dapm_widgets = apq8016_sbc_dapm_widgets; 226 card->num_dapm_widgets = ARRAY_SIZE(apq8016_sbc_dapm_widgets); 227 data = apq8016_sbc_parse_of(card); 228 if (IS_ERR(data)) { 229 dev_err(&pdev->dev, "Error resolving dai links: %ld\n", 230 PTR_ERR(data)); 231 return PTR_ERR(data); 232 } 233 234 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mic-iomux"); 235 data->mic_iomux = devm_ioremap_resource(dev, res); 236 if (IS_ERR(data->mic_iomux)) 237 return PTR_ERR(data->mic_iomux); 238 239 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "spkr-iomux"); 240 data->spkr_iomux = devm_ioremap_resource(dev, res); 241 if (IS_ERR(data->spkr_iomux)) 242 return PTR_ERR(data->spkr_iomux); 243 244 snd_soc_card_set_drvdata(card, data); 245 246 return devm_snd_soc_register_card(&pdev->dev, card); 247 } 248 249 static const struct of_device_id apq8016_sbc_device_id[] = { 250 { .compatible = "qcom,apq8016-sbc-sndcard" }, 251 {}, 252 }; 253 MODULE_DEVICE_TABLE(of, apq8016_sbc_device_id); 254 255 static struct platform_driver apq8016_sbc_platform_driver = { 256 .driver = { 257 .name = "qcom-apq8016-sbc", 258 .of_match_table = of_match_ptr(apq8016_sbc_device_id), 259 }, 260 .probe = apq8016_sbc_platform_probe, 261 }; 262 module_platform_driver(apq8016_sbc_platform_driver); 263 264 MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org"); 265 MODULE_DESCRIPTION("APQ8016 ASoC Machine Driver"); 266 MODULE_LICENSE("GPL v2"); 267