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