0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/acpi.h>
0009 #include <linux/dmi.h>
0010 #include <linux/gpio/consumer.h>
0011 #include <linux/gpio/machine.h>
0012 #include <linux/module.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/slab.h>
0015 #include <sound/pcm.h>
0016 #include <sound/pcm_params.h>
0017 #include <sound/soc.h>
0018 #include <sound/soc-acpi.h>
0019 #include "../../codecs/wm8804.h"
0020
0021 struct sof_card_private {
0022 struct gpio_desc *gpio_44;
0023 struct gpio_desc *gpio_48;
0024 int sample_rate;
0025 };
0026
0027 #define SOF_WM8804_UP2_QUIRK BIT(0)
0028
0029 static unsigned long sof_wm8804_quirk;
0030
0031 static int sof_wm8804_quirk_cb(const struct dmi_system_id *id)
0032 {
0033 sof_wm8804_quirk = (unsigned long)id->driver_data;
0034 return 1;
0035 }
0036
0037 static const struct dmi_system_id sof_wm8804_quirk_table[] = {
0038 {
0039 .callback = sof_wm8804_quirk_cb,
0040 .matches = {
0041 DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
0042 DMI_MATCH(DMI_PRODUCT_NAME, "UP-APL01"),
0043 },
0044 .driver_data = (void *)SOF_WM8804_UP2_QUIRK,
0045 },
0046 {}
0047 };
0048
0049 static int sof_wm8804_hw_params(struct snd_pcm_substream *substream,
0050 struct snd_pcm_hw_params *params)
0051 {
0052 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0053 struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
0054 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
0055 struct snd_soc_component *codec = codec_dai->component;
0056 const int sysclk = 27000000;
0057 int samplerate;
0058 long mclk_freq;
0059 int mclk_div;
0060 int sampling_freq;
0061 bool clk_44;
0062 int ret;
0063
0064 samplerate = params_rate(params);
0065 if (samplerate == ctx->sample_rate)
0066 return 0;
0067
0068 ctx->sample_rate = 0;
0069
0070 if (samplerate <= 96000) {
0071 mclk_freq = samplerate * 256;
0072 mclk_div = WM8804_MCLKDIV_256FS;
0073 } else {
0074 mclk_freq = samplerate * 128;
0075 mclk_div = WM8804_MCLKDIV_128FS;
0076 }
0077
0078 switch (samplerate) {
0079 case 32000:
0080 sampling_freq = 0x03;
0081 break;
0082 case 44100:
0083 sampling_freq = 0x00;
0084 break;
0085 case 48000:
0086 sampling_freq = 0x02;
0087 break;
0088 case 88200:
0089 sampling_freq = 0x08;
0090 break;
0091 case 96000:
0092 sampling_freq = 0x0a;
0093 break;
0094 case 176400:
0095 sampling_freq = 0x0c;
0096 break;
0097 case 192000:
0098 sampling_freq = 0x0e;
0099 break;
0100 default:
0101 dev_err(rtd->card->dev,
0102 "unsupported samplerate %d\n", samplerate);
0103 return -EINVAL;
0104 }
0105
0106 if (samplerate % 16000)
0107 clk_44 = true;
0108 else
0109 clk_44 = false;
0110
0111 if (!(IS_ERR_OR_NULL(ctx->gpio_44) ||
0112 IS_ERR_OR_NULL(ctx->gpio_48))) {
0113
0114
0115
0116
0117 if (clk_44) {
0118 gpiod_set_value_cansleep(ctx->gpio_48, !clk_44);
0119 gpiod_set_value_cansleep(ctx->gpio_44, clk_44);
0120 } else {
0121 gpiod_set_value_cansleep(ctx->gpio_44, clk_44);
0122 gpiod_set_value_cansleep(ctx->gpio_48, !clk_44);
0123 }
0124 }
0125
0126 snd_soc_dai_set_clkdiv(codec_dai, WM8804_MCLK_DIV, mclk_div);
0127 ret = snd_soc_dai_set_pll(codec_dai, 0, 0, sysclk, mclk_freq);
0128 if (ret < 0) {
0129 dev_err(rtd->card->dev, "Failed to set WM8804 PLL\n");
0130 return ret;
0131 }
0132
0133 ret = snd_soc_dai_set_sysclk(codec_dai, WM8804_TX_CLKSRC_PLL,
0134 sysclk, SND_SOC_CLOCK_OUT);
0135 if (ret < 0) {
0136 dev_err(rtd->card->dev,
0137 "Failed to set WM8804 SYSCLK: %d\n", ret);
0138 return ret;
0139 }
0140
0141
0142 snd_soc_component_update_bits(codec, WM8804_SPDTX4, 0x0f,
0143 sampling_freq);
0144
0145 ctx->sample_rate = samplerate;
0146
0147 return 0;
0148 }
0149
0150
0151 static struct snd_soc_ops sof_wm8804_ops = {
0152 .hw_params = sof_wm8804_hw_params,
0153 };
0154
0155 SND_SOC_DAILINK_DEF(ssp5_pin,
0156 DAILINK_COMP_ARRAY(COMP_CPU("SSP5 Pin")));
0157
0158 SND_SOC_DAILINK_DEF(ssp5_codec,
0159 DAILINK_COMP_ARRAY(COMP_CODEC("i2c-1AEC8804:00", "wm8804-spdif")));
0160
0161 SND_SOC_DAILINK_DEF(platform,
0162 DAILINK_COMP_ARRAY(COMP_PLATFORM("0000:00:0e.0")));
0163
0164 static struct snd_soc_dai_link dailink[] = {
0165
0166 {
0167 .name = "SSP5-Codec",
0168 .id = 0,
0169 .no_pcm = 1,
0170 .dpcm_playback = 1,
0171 .dpcm_capture = 1,
0172 .ops = &sof_wm8804_ops,
0173 SND_SOC_DAILINK_REG(ssp5_pin, ssp5_codec, platform),
0174 },
0175 };
0176
0177
0178 static struct snd_soc_card sof_wm8804_card = {
0179 .name = "wm8804",
0180 .owner = THIS_MODULE,
0181 .dai_link = dailink,
0182 .num_links = ARRAY_SIZE(dailink),
0183 };
0184
0185
0186 static char codec_name[SND_ACPI_I2C_ID_LEN];
0187
0188
0189
0190
0191
0192
0193
0194
0195
0196
0197
0198 static struct gpiod_lookup_table up2_gpios_table = {
0199
0200 .table = {
0201 GPIO_LOOKUP("INT3452:01", 73, "BCM-GPIO5", GPIO_ACTIVE_HIGH),
0202 GPIO_LOOKUP("INT3452:01", 74, "BCM-GPIO6", GPIO_ACTIVE_HIGH),
0203 { },
0204 },
0205 };
0206
0207 static int sof_wm8804_probe(struct platform_device *pdev)
0208 {
0209 struct snd_soc_card *card;
0210 struct snd_soc_acpi_mach *mach;
0211 struct sof_card_private *ctx;
0212 struct acpi_device *adev;
0213 int dai_index = 0;
0214 int ret;
0215 int i;
0216
0217 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
0218 if (!ctx)
0219 return -ENOMEM;
0220
0221 mach = pdev->dev.platform_data;
0222 card = &sof_wm8804_card;
0223 card->dev = &pdev->dev;
0224
0225 dmi_check_system(sof_wm8804_quirk_table);
0226
0227 if (sof_wm8804_quirk & SOF_WM8804_UP2_QUIRK) {
0228 up2_gpios_table.dev_id = dev_name(&pdev->dev);
0229 gpiod_add_lookup_table(&up2_gpios_table);
0230
0231
0232
0233
0234
0235
0236
0237
0238 ctx->gpio_44 = devm_gpiod_get(&pdev->dev, "BCM-GPIO5",
0239 GPIOD_OUT_LOW);
0240 if (IS_ERR(ctx->gpio_44)) {
0241 ret = PTR_ERR(ctx->gpio_44);
0242 dev_err(&pdev->dev,
0243 "could not get BCM-GPIO5: %d\n",
0244 ret);
0245 return ret;
0246 }
0247
0248 ctx->gpio_48 = devm_gpiod_get(&pdev->dev, "BCM-GPIO6",
0249 GPIOD_OUT_LOW);
0250 if (IS_ERR(ctx->gpio_48)) {
0251 ret = PTR_ERR(ctx->gpio_48);
0252 dev_err(&pdev->dev,
0253 "could not get BCM-GPIO6: %d\n",
0254 ret);
0255 return ret;
0256 }
0257 }
0258
0259
0260 for (i = 0; i < ARRAY_SIZE(dailink); i++) {
0261 if (!strcmp(dailink[i].codecs->name, "i2c-1AEC8804:00")) {
0262 dai_index = i;
0263 break;
0264 }
0265 }
0266
0267
0268 adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1);
0269 if (adev) {
0270 snprintf(codec_name, sizeof(codec_name),
0271 "%s%s", "i2c-", acpi_dev_name(adev));
0272 put_device(&adev->dev);
0273 dailink[dai_index].codecs->name = codec_name;
0274 }
0275
0276 snd_soc_card_set_drvdata(card, ctx);
0277
0278 return devm_snd_soc_register_card(&pdev->dev, card);
0279 }
0280
0281 static int sof_wm8804_remove(struct platform_device *pdev)
0282 {
0283 if (sof_wm8804_quirk & SOF_WM8804_UP2_QUIRK)
0284 gpiod_remove_lookup_table(&up2_gpios_table);
0285 return 0;
0286 }
0287
0288 static struct platform_driver sof_wm8804_driver = {
0289 .driver = {
0290 .name = "sof-wm8804",
0291 .pm = &snd_soc_pm_ops,
0292 },
0293 .probe = sof_wm8804_probe,
0294 .remove = sof_wm8804_remove,
0295 };
0296 module_platform_driver(sof_wm8804_driver);
0297
0298 MODULE_DESCRIPTION("ASoC Intel(R) SOF + WM8804 Machine driver");
0299 MODULE_AUTHOR("Pierre-Louis Bossart");
0300 MODULE_LICENSE("GPL v2");
0301 MODULE_ALIAS("platform:sof-wm8804");