smartq_wm8987.c (5490B)
1// SPDX-License-Identifier: GPL-2.0+ 2// 3// Copyright 2010 Maurus Cuelenaere <mcuelenaere@gmail.com> 4// 5// Based on smdk6410_wm8987.c 6// Copyright 2007 Wolfson Microelectronics PLC. - linux@wolfsonmicro.com 7// Graeme Gregory - graeme.gregory@wolfsonmicro.com 8 9#include <linux/gpio/consumer.h> 10#include <linux/module.h> 11 12#include <sound/soc.h> 13#include <sound/jack.h> 14 15#include "i2s.h" 16#include "../codecs/wm8750.h" 17 18/* 19 * WM8987 is register compatible with WM8750, so using that as base driver. 20 */ 21 22static struct snd_soc_card snd_soc_smartq; 23 24static int smartq_hifi_hw_params(struct snd_pcm_substream *substream, 25 struct snd_pcm_hw_params *params) 26{ 27 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 28 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 29 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 30 unsigned int clk = 0; 31 int ret; 32 33 switch (params_rate(params)) { 34 case 8000: 35 case 16000: 36 case 32000: 37 case 48000: 38 case 96000: 39 clk = 12288000; 40 break; 41 case 11025: 42 case 22050: 43 case 44100: 44 case 88200: 45 clk = 11289600; 46 break; 47 } 48 49 /* Use PCLK for I2S signal generation */ 50 ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0, 51 0, SND_SOC_CLOCK_IN); 52 if (ret < 0) 53 return ret; 54 55 /* Gate the RCLK output on PAD */ 56 ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK, 57 0, SND_SOC_CLOCK_IN); 58 if (ret < 0) 59 return ret; 60 61 /* set the codec system clock for DAC and ADC */ 62 ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk, 63 SND_SOC_CLOCK_IN); 64 if (ret < 0) 65 return ret; 66 67 return 0; 68} 69 70/* 71 * SmartQ WM8987 HiFi DAI operations. 72 */ 73static const struct snd_soc_ops smartq_hifi_ops = { 74 .hw_params = smartq_hifi_hw_params, 75}; 76 77static struct snd_soc_jack smartq_jack; 78 79static struct snd_soc_jack_pin smartq_jack_pins[] = { 80 /* Disable speaker when headphone is plugged in */ 81 { 82 .pin = "Internal Speaker", 83 .mask = SND_JACK_HEADPHONE, 84 }, 85}; 86 87static struct snd_soc_jack_gpio smartq_jack_gpios[] = { 88 { 89 .gpio = -1, 90 .name = "headphone detect", 91 .report = SND_JACK_HEADPHONE, 92 .debounce_time = 200, 93 }, 94}; 95 96static const struct snd_kcontrol_new wm8987_smartq_controls[] = { 97 SOC_DAPM_PIN_SWITCH("Internal Speaker"), 98 SOC_DAPM_PIN_SWITCH("Headphone Jack"), 99 SOC_DAPM_PIN_SWITCH("Internal Mic"), 100}; 101 102static int smartq_speaker_event(struct snd_soc_dapm_widget *w, 103 struct snd_kcontrol *k, 104 int event) 105{ 106 struct gpio_desc *gpio = snd_soc_card_get_drvdata(&snd_soc_smartq); 107 108 gpiod_set_value(gpio, SND_SOC_DAPM_EVENT_OFF(event)); 109 110 return 0; 111} 112 113static const struct snd_soc_dapm_widget wm8987_dapm_widgets[] = { 114 SND_SOC_DAPM_SPK("Internal Speaker", smartq_speaker_event), 115 SND_SOC_DAPM_HP("Headphone Jack", NULL), 116 SND_SOC_DAPM_MIC("Internal Mic", NULL), 117}; 118 119static const struct snd_soc_dapm_route audio_map[] = { 120 {"Headphone Jack", NULL, "LOUT2"}, 121 {"Headphone Jack", NULL, "ROUT2"}, 122 123 {"Internal Speaker", NULL, "LOUT2"}, 124 {"Internal Speaker", NULL, "ROUT2"}, 125 126 {"Mic Bias", NULL, "Internal Mic"}, 127 {"LINPUT2", NULL, "Mic Bias"}, 128}; 129 130static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd) 131{ 132 struct snd_soc_dapm_context *dapm = &rtd->card->dapm; 133 int err = 0; 134 135 /* set endpoints to not connected */ 136 snd_soc_dapm_nc_pin(dapm, "LINPUT1"); 137 snd_soc_dapm_nc_pin(dapm, "RINPUT1"); 138 snd_soc_dapm_nc_pin(dapm, "OUT3"); 139 snd_soc_dapm_nc_pin(dapm, "ROUT1"); 140 141 /* Headphone jack detection */ 142 err = snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack", 143 SND_JACK_HEADPHONE, &smartq_jack, 144 smartq_jack_pins, 145 ARRAY_SIZE(smartq_jack_pins)); 146 if (err) 147 return err; 148 149 err = snd_soc_jack_add_gpios(&smartq_jack, 150 ARRAY_SIZE(smartq_jack_gpios), 151 smartq_jack_gpios); 152 153 return err; 154} 155 156SND_SOC_DAILINK_DEFS(wm8987, 157 DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")), 158 DAILINK_COMP_ARRAY(COMP_CODEC("wm8750.0-0x1a", "wm8750-hifi")), 159 DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0"))); 160 161static struct snd_soc_dai_link smartq_dai[] = { 162 { 163 .name = "wm8987", 164 .stream_name = "SmartQ Hi-Fi", 165 .init = smartq_wm8987_init, 166 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 167 SND_SOC_DAIFMT_CBS_CFS, 168 .ops = &smartq_hifi_ops, 169 SND_SOC_DAILINK_REG(wm8987), 170 }, 171}; 172 173static struct snd_soc_card snd_soc_smartq = { 174 .name = "SmartQ", 175 .owner = THIS_MODULE, 176 .dai_link = smartq_dai, 177 .num_links = ARRAY_SIZE(smartq_dai), 178 179 .dapm_widgets = wm8987_dapm_widgets, 180 .num_dapm_widgets = ARRAY_SIZE(wm8987_dapm_widgets), 181 .dapm_routes = audio_map, 182 .num_dapm_routes = ARRAY_SIZE(audio_map), 183 .controls = wm8987_smartq_controls, 184 .num_controls = ARRAY_SIZE(wm8987_smartq_controls), 185}; 186 187static int smartq_probe(struct platform_device *pdev) 188{ 189 struct gpio_desc *gpio; 190 int ret; 191 192 platform_set_drvdata(pdev, &snd_soc_smartq); 193 194 /* Initialise GPIOs used by amplifiers */ 195 gpio = devm_gpiod_get(&pdev->dev, "amplifiers shutdown", 196 GPIOD_OUT_HIGH); 197 if (IS_ERR(gpio)) { 198 dev_err(&pdev->dev, "Failed to register GPK12\n"); 199 ret = PTR_ERR(gpio); 200 goto out; 201 } 202 snd_soc_card_set_drvdata(&snd_soc_smartq, gpio); 203 204 ret = devm_snd_soc_register_card(&pdev->dev, &snd_soc_smartq); 205 if (ret) 206 dev_err(&pdev->dev, "Failed to register card\n"); 207 208out: 209 return ret; 210} 211 212static struct platform_driver smartq_driver = { 213 .driver = { 214 .name = "smartq-audio", 215 }, 216 .probe = smartq_probe, 217}; 218 219module_platform_driver(smartq_driver); 220 221/* Module information */ 222MODULE_AUTHOR("Maurus Cuelenaere <mcuelenaere@gmail.com>"); 223MODULE_DESCRIPTION("ALSA SoC SmartQ WM8987"); 224MODULE_LICENSE("GPL");