1/* 2 * Intel Baytrail SST RT5640 machine driver 3 * Copyright (c) 2014, Intel Corporation. 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms and conditions of the GNU General Public License, 7 * version 2, as published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 * more details. 13 */ 14 15#include <linux/init.h> 16#include <linux/module.h> 17#include <linux/platform_device.h> 18#include <linux/acpi.h> 19#include <linux/device.h> 20#include <linux/dmi.h> 21#include <linux/slab.h> 22#include <sound/pcm.h> 23#include <sound/pcm_params.h> 24#include <sound/soc.h> 25#include <sound/jack.h> 26#include "../../codecs/rt5640.h" 27 28#include "../common/sst-dsp.h" 29 30static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = { 31 SND_SOC_DAPM_HP("Headphone", NULL), 32 SND_SOC_DAPM_MIC("Headset Mic", NULL), 33 SND_SOC_DAPM_MIC("Internal Mic", NULL), 34 SND_SOC_DAPM_SPK("Speaker", NULL), 35}; 36 37static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = { 38 {"Headset Mic", NULL, "MICBIAS1"}, 39 {"IN2P", NULL, "Headset Mic"}, 40 {"Headphone", NULL, "HPOL"}, 41 {"Headphone", NULL, "HPOR"}, 42 {"Speaker", NULL, "SPOLP"}, 43 {"Speaker", NULL, "SPOLN"}, 44 {"Speaker", NULL, "SPORP"}, 45 {"Speaker", NULL, "SPORN"}, 46}; 47 48static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic1_map[] = { 49 {"DMIC1", NULL, "Internal Mic"}, 50}; 51 52static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic2_map[] = { 53 {"DMIC2", NULL, "Internal Mic"}, 54}; 55 56static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = { 57 {"Internal Mic", NULL, "MICBIAS1"}, 58 {"IN1P", NULL, "Internal Mic"}, 59}; 60 61enum { 62 BYT_RT5640_DMIC1_MAP, 63 BYT_RT5640_DMIC2_MAP, 64 BYT_RT5640_IN1_MAP, 65}; 66 67#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff) 68#define BYT_RT5640_DMIC_EN BIT(16) 69 70static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP | 71 BYT_RT5640_DMIC_EN; 72 73static const struct snd_kcontrol_new byt_rt5640_controls[] = { 74 SOC_DAPM_PIN_SWITCH("Headphone"), 75 SOC_DAPM_PIN_SWITCH("Headset Mic"), 76 SOC_DAPM_PIN_SWITCH("Internal Mic"), 77 SOC_DAPM_PIN_SWITCH("Speaker"), 78}; 79 80static int byt_rt5640_hw_params(struct snd_pcm_substream *substream, 81 struct snd_pcm_hw_params *params) 82{ 83 struct snd_soc_pcm_runtime *rtd = substream->private_data; 84 struct snd_soc_dai *codec_dai = rtd->codec_dai; 85 int ret; 86 87 ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, 88 params_rate(params) * 256, 89 SND_SOC_CLOCK_IN); 90 if (ret < 0) { 91 dev_err(codec_dai->dev, "can't set codec clock %d\n", ret); 92 return ret; 93 } 94 ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1, 95 params_rate(params) * 64, 96 params_rate(params) * 256); 97 if (ret < 0) { 98 dev_err(codec_dai->dev, "can't set codec pll: %d\n", ret); 99 return ret; 100 } 101 return 0; 102} 103 104static int byt_rt5640_quirk_cb(const struct dmi_system_id *id) 105{ 106 byt_rt5640_quirk = (unsigned long)id->driver_data; 107 return 1; 108} 109 110static const struct dmi_system_id byt_rt5640_quirk_table[] = { 111 { 112 .callback = byt_rt5640_quirk_cb, 113 .matches = { 114 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 115 DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"), 116 }, 117 .driver_data = (unsigned long *)BYT_RT5640_IN1_MAP, 118 }, 119 { 120 .callback = byt_rt5640_quirk_cb, 121 .matches = { 122 DMI_MATCH(DMI_SYS_VENDOR, "DellInc."), 123 DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"), 124 }, 125 .driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP | 126 BYT_RT5640_DMIC_EN), 127 }, 128 {} 129}; 130 131static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) 132{ 133 int ret; 134 struct snd_soc_codec *codec = runtime->codec; 135 struct snd_soc_card *card = runtime->card; 136 const struct snd_soc_dapm_route *custom_map; 137 int num_routes; 138 139 card->dapm.idle_bias_off = true; 140 141 ret = snd_soc_add_card_controls(card, byt_rt5640_controls, 142 ARRAY_SIZE(byt_rt5640_controls)); 143 if (ret) { 144 dev_err(card->dev, "unable to add card controls\n"); 145 return ret; 146 } 147 148 dmi_check_system(byt_rt5640_quirk_table); 149 switch (BYT_RT5640_MAP(byt_rt5640_quirk)) { 150 case BYT_RT5640_IN1_MAP: 151 custom_map = byt_rt5640_intmic_in1_map; 152 num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map); 153 break; 154 case BYT_RT5640_DMIC2_MAP: 155 custom_map = byt_rt5640_intmic_dmic2_map; 156 num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic2_map); 157 break; 158 default: 159 custom_map = byt_rt5640_intmic_dmic1_map; 160 num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic1_map); 161 } 162 163 ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes); 164 if (ret) 165 return ret; 166 167 if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) { 168 ret = rt5640_dmic_enable(codec, 0, 0); 169 if (ret) 170 return ret; 171 } 172 173 snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone"); 174 snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker"); 175 176 return ret; 177} 178 179static struct snd_soc_ops byt_rt5640_ops = { 180 .hw_params = byt_rt5640_hw_params, 181}; 182 183static struct snd_soc_dai_link byt_rt5640_dais[] = { 184 { 185 .name = "Baytrail Audio", 186 .stream_name = "Audio", 187 .cpu_dai_name = "baytrail-pcm-audio", 188 .codec_dai_name = "rt5640-aif1", 189 .codec_name = "i2c-10EC5640:00", 190 .platform_name = "baytrail-pcm-audio", 191 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 192 SND_SOC_DAIFMT_CBS_CFS, 193 .init = byt_rt5640_init, 194 .ops = &byt_rt5640_ops, 195 }, 196}; 197 198static struct snd_soc_card byt_rt5640_card = { 199 .name = "byt-rt5640", 200 .dai_link = byt_rt5640_dais, 201 .num_links = ARRAY_SIZE(byt_rt5640_dais), 202 .dapm_widgets = byt_rt5640_widgets, 203 .num_dapm_widgets = ARRAY_SIZE(byt_rt5640_widgets), 204 .dapm_routes = byt_rt5640_audio_map, 205 .num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map), 206 .fully_routed = true, 207}; 208 209static int byt_rt5640_probe(struct platform_device *pdev) 210{ 211 struct snd_soc_card *card = &byt_rt5640_card; 212 213 card->dev = &pdev->dev; 214 return devm_snd_soc_register_card(&pdev->dev, card); 215} 216 217static struct platform_driver byt_rt5640_audio = { 218 .probe = byt_rt5640_probe, 219 .driver = { 220 .name = "byt-rt5640", 221 .pm = &snd_soc_pm_ops, 222 }, 223}; 224module_platform_driver(byt_rt5640_audio) 225 226MODULE_DESCRIPTION("ASoC Intel(R) Baytrail Machine driver"); 227MODULE_AUTHOR("Omair Md Abdullah, Jarkko Nikula"); 228MODULE_LICENSE("GPL v2"); 229MODULE_ALIAS("platform:byt-rt5640"); 230