1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) ST-Ericsson SA 2012 4 * 5 * Author: Ola Lilja <ola.o.lilja@stericsson.com>, 6 * Kristoffer Karlsson <kristoffer.karlsson@stericsson.com> 7 * for ST-Ericsson. 8 */ 9 10 #include <linux/module.h> 11 #include <linux/device.h> 12 #include <linux/io.h> 13 #include <linux/clk.h> 14 #include <linux/mutex.h> 15 16 #include <sound/soc.h> 17 #include <sound/soc-dapm.h> 18 #include <sound/pcm.h> 19 #include <sound/pcm_params.h> 20 21 #include "ux500_pcm.h" 22 #include "ux500_msp_dai.h" 23 #include "mop500_ab8500.h" 24 #include "../codecs/ab8500-codec.h" 25 26 #define TX_SLOT_MONO 0x0008 27 #define TX_SLOT_STEREO 0x000a 28 #define RX_SLOT_MONO 0x0001 29 #define RX_SLOT_STEREO 0x0003 30 #define TX_SLOT_8CH 0x00FF 31 #define RX_SLOT_8CH 0x00FF 32 33 #define DEF_TX_SLOTS TX_SLOT_STEREO 34 #define DEF_RX_SLOTS RX_SLOT_MONO 35 36 #define DRIVERMODE_NORMAL 0 37 #define DRIVERMODE_CODEC_ONLY 1 38 39 /* Slot configuration */ 40 static unsigned int tx_slots = DEF_TX_SLOTS; 41 static unsigned int rx_slots = DEF_RX_SLOTS; 42 43 /* Configuration consistency parameters */ 44 static DEFINE_MUTEX(mop500_ab8500_params_lock); 45 static unsigned long mop500_ab8500_usage; 46 static int mop500_ab8500_rate; 47 static int mop500_ab8500_channels; 48 49 /* Clocks */ 50 static const char * const enum_mclk[] = { 51 "SYSCLK", 52 "ULPCLK" 53 }; 54 enum mclk { 55 MCLK_SYSCLK, 56 MCLK_ULPCLK, 57 }; 58 59 static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_mclk, enum_mclk); 60 61 /* Private data for machine-part MOP500<->AB8500 */ 62 struct mop500_ab8500_drvdata { 63 /* Clocks */ 64 enum mclk mclk_sel; 65 struct clk *clk_ptr_intclk; 66 struct clk *clk_ptr_sysclk; 67 struct clk *clk_ptr_ulpclk; 68 }; 69 70 static inline const char *get_mclk_str(enum mclk mclk_sel) 71 { 72 switch (mclk_sel) { 73 case MCLK_SYSCLK: 74 return "SYSCLK"; 75 case MCLK_ULPCLK: 76 return "ULPCLK"; 77 default: 78 return "Unknown"; 79 } 80 } 81 82 static int mop500_ab8500_set_mclk(struct device *dev, 83 struct mop500_ab8500_drvdata *drvdata) 84 { 85 int status; 86 struct clk *clk_ptr; 87 88 if (IS_ERR(drvdata->clk_ptr_intclk)) { 89 dev_err(dev, 90 "%s: ERROR: intclk not initialized!\n", __func__); 91 return -EIO; 92 } 93 94 switch (drvdata->mclk_sel) { 95 case MCLK_SYSCLK: 96 clk_ptr = drvdata->clk_ptr_sysclk; 97 break; 98 case MCLK_ULPCLK: 99 clk_ptr = drvdata->clk_ptr_ulpclk; 100 break; 101 default: 102 return -EINVAL; 103 } 104 105 if (IS_ERR(clk_ptr)) { 106 dev_err(dev, "%s: ERROR: %s not initialized!\n", __func__, 107 get_mclk_str(drvdata->mclk_sel)); 108 return -EIO; 109 } 110 111 status = clk_set_parent(drvdata->clk_ptr_intclk, clk_ptr); 112 if (status) 113 dev_err(dev, 114 "%s: ERROR: Setting intclk parent to %s failed (ret = %d)!", 115 __func__, get_mclk_str(drvdata->mclk_sel), status); 116 else 117 dev_dbg(dev, 118 "%s: intclk parent changed to %s.\n", 119 __func__, get_mclk_str(drvdata->mclk_sel)); 120 121 return status; 122 } 123 124 /* 125 * Control-events 126 */ 127 128 static int mclk_input_control_get(struct snd_kcontrol *kcontrol, 129 struct snd_ctl_elem_value *ucontrol) 130 { 131 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 132 struct mop500_ab8500_drvdata *drvdata = 133 snd_soc_card_get_drvdata(card); 134 135 ucontrol->value.enumerated.item[0] = drvdata->mclk_sel; 136 137 return 0; 138 } 139 140 static int mclk_input_control_put(struct snd_kcontrol *kcontrol, 141 struct snd_ctl_elem_value *ucontrol) 142 { 143 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 144 struct mop500_ab8500_drvdata *drvdata = 145 snd_soc_card_get_drvdata(card); 146 unsigned int val = ucontrol->value.enumerated.item[0]; 147 148 if (val > (unsigned int)MCLK_ULPCLK) 149 return -EINVAL; 150 if (drvdata->mclk_sel == val) 151 return 0; 152 153 drvdata->mclk_sel = val; 154 155 return 1; 156 } 157 158 /* 159 * Controls 160 */ 161 162 static struct snd_kcontrol_new mop500_ab8500_ctrls[] = { 163 SOC_ENUM_EXT("Master Clock Select", 164 soc_enum_mclk, 165 mclk_input_control_get, mclk_input_control_put), 166 SOC_DAPM_PIN_SWITCH("Headset Left"), 167 SOC_DAPM_PIN_SWITCH("Headset Right"), 168 SOC_DAPM_PIN_SWITCH("Earpiece"), 169 SOC_DAPM_PIN_SWITCH("Speaker Left"), 170 SOC_DAPM_PIN_SWITCH("Speaker Right"), 171 SOC_DAPM_PIN_SWITCH("LineOut Left"), 172 SOC_DAPM_PIN_SWITCH("LineOut Right"), 173 SOC_DAPM_PIN_SWITCH("Vibra 1"), 174 SOC_DAPM_PIN_SWITCH("Vibra 2"), 175 SOC_DAPM_PIN_SWITCH("Mic 1"), 176 SOC_DAPM_PIN_SWITCH("Mic 2"), 177 SOC_DAPM_PIN_SWITCH("LineIn Left"), 178 SOC_DAPM_PIN_SWITCH("LineIn Right"), 179 SOC_DAPM_PIN_SWITCH("DMic 1"), 180 SOC_DAPM_PIN_SWITCH("DMic 2"), 181 SOC_DAPM_PIN_SWITCH("DMic 3"), 182 SOC_DAPM_PIN_SWITCH("DMic 4"), 183 SOC_DAPM_PIN_SWITCH("DMic 5"), 184 SOC_DAPM_PIN_SWITCH("DMic 6"), 185 }; 186 187 /* ASoC */ 188 189 static int mop500_ab8500_startup(struct snd_pcm_substream *substream) 190 { 191 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 192 193 /* Set audio-clock source */ 194 return mop500_ab8500_set_mclk(rtd->card->dev, 195 snd_soc_card_get_drvdata(rtd->card)); 196 } 197 198 static void mop500_ab8500_shutdown(struct snd_pcm_substream *substream) 199 { 200 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 201 struct device *dev = rtd->card->dev; 202 203 dev_dbg(dev, "%s: Enter\n", __func__); 204 205 /* Reset slots configuration to default(s) */ 206 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 207 tx_slots = DEF_TX_SLOTS; 208 else 209 rx_slots = DEF_RX_SLOTS; 210 } 211 212 static int mop500_ab8500_hw_params(struct snd_pcm_substream *substream, 213 struct snd_pcm_hw_params *params) 214 { 215 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 216 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); 217 struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); 218 struct device *dev = rtd->card->dev; 219 unsigned int fmt; 220 int channels, ret = 0, driver_mode, slots; 221 unsigned int sw_codec, sw_cpu; 222 bool is_playback; 223 224 dev_dbg(dev, "%s: Enter\n", __func__); 225 226 dev_dbg(dev, "%s: substream->pcm->name = %s\n" 227 "substream->pcm->id = %s.\n" 228 "substream->name = %s.\n" 229 "substream->number = %d.\n", 230 __func__, 231 substream->pcm->name, 232 substream->pcm->id, 233 substream->name, 234 substream->number); 235 236 /* Ensure configuration consistency between DAIs */ 237 mutex_lock(&mop500_ab8500_params_lock); 238 if (mop500_ab8500_usage) { 239 if (mop500_ab8500_rate != params_rate(params) || 240 mop500_ab8500_channels != params_channels(params)) { 241 mutex_unlock(&mop500_ab8500_params_lock); 242 return -EBUSY; 243 } 244 } else { 245 mop500_ab8500_rate = params_rate(params); 246 mop500_ab8500_channels = params_channels(params); 247 } 248 __set_bit(cpu_dai->id, &mop500_ab8500_usage); 249 mutex_unlock(&mop500_ab8500_params_lock); 250 251 channels = params_channels(params); 252 253 switch (params_format(params)) { 254 case SNDRV_PCM_FORMAT_S32_LE: 255 sw_cpu = 32; 256 break; 257 258 case SNDRV_PCM_FORMAT_S16_LE: 259 sw_cpu = 16; 260 break; 261 262 default: 263 return -EINVAL; 264 } 265 266 /* Setup codec depending on driver-mode */ 267 if (channels == 8) 268 driver_mode = DRIVERMODE_CODEC_ONLY; 269 else 270 driver_mode = DRIVERMODE_NORMAL; 271 dev_dbg(dev, "%s: Driver-mode: %s.\n", __func__, 272 (driver_mode == DRIVERMODE_NORMAL) ? "NORMAL" : "CODEC_ONLY"); 273 274 /* Setup format */ 275 276 if (driver_mode == DRIVERMODE_NORMAL) { 277 fmt = SND_SOC_DAIFMT_DSP_A | 278 SND_SOC_DAIFMT_CBM_CFM | 279 SND_SOC_DAIFMT_NB_NF | 280 SND_SOC_DAIFMT_CONT; 281 } else { 282 fmt = SND_SOC_DAIFMT_DSP_A | 283 SND_SOC_DAIFMT_CBM_CFM | 284 SND_SOC_DAIFMT_NB_NF | 285 SND_SOC_DAIFMT_GATED; 286 } 287 288 ret = snd_soc_runtime_set_dai_fmt(rtd, fmt); 289 if (ret) 290 return ret; 291 292 /* Setup TDM-slots */ 293 294 is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); 295 switch (channels) { 296 case 1: 297 slots = 16; 298 tx_slots = (is_playback) ? TX_SLOT_MONO : 0; 299 rx_slots = (is_playback) ? 0 : RX_SLOT_MONO; 300 break; 301 case 2: 302 slots = 16; 303 tx_slots = (is_playback) ? TX_SLOT_STEREO : 0; 304 rx_slots = (is_playback) ? 0 : RX_SLOT_STEREO; 305 break; 306 case 8: 307 slots = 16; 308 tx_slots = (is_playback) ? TX_SLOT_8CH : 0; 309 rx_slots = (is_playback) ? 0 : RX_SLOT_8CH; 310 break; 311 default: 312 return -EINVAL; 313 } 314 315 if (driver_mode == DRIVERMODE_NORMAL) 316 sw_codec = sw_cpu; 317 else 318 sw_codec = 20; 319 320 dev_dbg(dev, "%s: CPU-DAI TDM: TX=0x%04X RX=0x%04x\n", __func__, 321 tx_slots, rx_slots); 322 ret = snd_soc_dai_set_tdm_slot(cpu_dai, tx_slots, rx_slots, slots, 323 sw_cpu); 324 if (ret) 325 return ret; 326 327 dev_dbg(dev, "%s: CODEC-DAI TDM: TX=0x%04X RX=0x%04x\n", __func__, 328 tx_slots, rx_slots); 329 ret = snd_soc_dai_set_tdm_slot(codec_dai, tx_slots, rx_slots, slots, 330 sw_codec); 331 if (ret) 332 return ret; 333 334 return 0; 335 } 336 337 static int mop500_ab8500_hw_free(struct snd_pcm_substream *substream) 338 { 339 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 340 struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); 341 342 mutex_lock(&mop500_ab8500_params_lock); 343 __clear_bit(cpu_dai->id, &mop500_ab8500_usage); 344 mutex_unlock(&mop500_ab8500_params_lock); 345 346 return 0; 347 } 348 349 const struct snd_soc_ops mop500_ab8500_ops[] = { 350 { 351 .hw_params = mop500_ab8500_hw_params, 352 .hw_free = mop500_ab8500_hw_free, 353 .startup = mop500_ab8500_startup, 354 .shutdown = mop500_ab8500_shutdown, 355 } 356 }; 357 358 int mop500_ab8500_machine_init(struct snd_soc_pcm_runtime *rtd) 359 { 360 struct snd_soc_dapm_context *dapm = &rtd->card->dapm; 361 struct device *dev = rtd->card->dev; 362 struct mop500_ab8500_drvdata *drvdata; 363 int ret; 364 365 dev_dbg(dev, "%s Enter.\n", __func__); 366 367 /* Create driver private-data struct */ 368 drvdata = devm_kzalloc(dev, sizeof(struct mop500_ab8500_drvdata), 369 GFP_KERNEL); 370 371 if (!drvdata) 372 return -ENOMEM; 373 374 snd_soc_card_set_drvdata(rtd->card, drvdata); 375 376 /* Setup clocks */ 377 378 drvdata->clk_ptr_sysclk = clk_get(dev, "sysclk"); 379 if (IS_ERR(drvdata->clk_ptr_sysclk)) 380 dev_warn(dev, "%s: WARNING: clk_get failed for 'sysclk'!\n", 381 __func__); 382 drvdata->clk_ptr_ulpclk = clk_get(dev, "ulpclk"); 383 if (IS_ERR(drvdata->clk_ptr_ulpclk)) 384 dev_warn(dev, "%s: WARNING: clk_get failed for 'ulpclk'!\n", 385 __func__); 386 drvdata->clk_ptr_intclk = clk_get(dev, "intclk"); 387 if (IS_ERR(drvdata->clk_ptr_intclk)) 388 dev_warn(dev, "%s: WARNING: clk_get failed for 'intclk'!\n", 389 __func__); 390 391 /* Set intclk default parent to ulpclk */ 392 drvdata->mclk_sel = MCLK_ULPCLK; 393 ret = mop500_ab8500_set_mclk(dev, drvdata); 394 if (ret < 0) 395 dev_warn(dev, "%s: WARNING: mop500_ab8500_set_mclk!\n", 396 __func__); 397 398 drvdata->mclk_sel = MCLK_ULPCLK; 399 400 /* Add controls */ 401 ret = snd_soc_add_card_controls(rtd->card, mop500_ab8500_ctrls, 402 ARRAY_SIZE(mop500_ab8500_ctrls)); 403 if (ret < 0) { 404 pr_err("%s: Failed to add machine-controls (%d)!\n", 405 __func__, ret); 406 return ret; 407 } 408 409 ret = snd_soc_dapm_disable_pin(dapm, "Earpiece"); 410 ret |= snd_soc_dapm_disable_pin(dapm, "Speaker Left"); 411 ret |= snd_soc_dapm_disable_pin(dapm, "Speaker Right"); 412 ret |= snd_soc_dapm_disable_pin(dapm, "LineOut Left"); 413 ret |= snd_soc_dapm_disable_pin(dapm, "LineOut Right"); 414 ret |= snd_soc_dapm_disable_pin(dapm, "Vibra 1"); 415 ret |= snd_soc_dapm_disable_pin(dapm, "Vibra 2"); 416 ret |= snd_soc_dapm_disable_pin(dapm, "Mic 1"); 417 ret |= snd_soc_dapm_disable_pin(dapm, "Mic 2"); 418 ret |= snd_soc_dapm_disable_pin(dapm, "LineIn Left"); 419 ret |= snd_soc_dapm_disable_pin(dapm, "LineIn Right"); 420 ret |= snd_soc_dapm_disable_pin(dapm, "DMic 1"); 421 ret |= snd_soc_dapm_disable_pin(dapm, "DMic 2"); 422 ret |= snd_soc_dapm_disable_pin(dapm, "DMic 3"); 423 ret |= snd_soc_dapm_disable_pin(dapm, "DMic 4"); 424 ret |= snd_soc_dapm_disable_pin(dapm, "DMic 5"); 425 ret |= snd_soc_dapm_disable_pin(dapm, "DMic 6"); 426 427 return ret; 428 } 429 430 void mop500_ab8500_remove(struct snd_soc_card *card) 431 { 432 struct mop500_ab8500_drvdata *drvdata = snd_soc_card_get_drvdata(card); 433 434 clk_put(drvdata->clk_ptr_sysclk); 435 clk_put(drvdata->clk_ptr_ulpclk); 436 clk_put(drvdata->clk_ptr_intclk); 437 438 snd_soc_card_set_drvdata(card, drvdata); 439 } 440
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.