linux/sound/soc/intel/boards/cht_bsw_max98090_ti.c
<<
>>
Prefs
   1/*
   2 *  cht-bsw-max98090.c - ASoc Machine driver for Intel Cherryview-based
   3 *  platforms Cherrytrail and Braswell, with max98090 & TI codec.
   4 *
   5 *  Copyright (C) 2015 Intel Corp
   6 *  Author: Fang, Yang A <yang.a.fang@intel.com>
   7 *  This file is modified from cht_bsw_rt5645.c
   8 *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   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 as published by
  12 *  the Free Software Foundation; version 2 of the License.
  13 *
  14 *  This program is distributed in the hope that it will be useful, but
  15 *  WITHOUT ANY WARRANTY; without even the implied warranty of
  16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 *  General Public License for more details.
  18 *
  19 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  20 */
  21
  22#include <linux/module.h>
  23#include <linux/platform_device.h>
  24#include <linux/slab.h>
  25#include <linux/acpi.h>
  26#include <sound/pcm.h>
  27#include <sound/pcm_params.h>
  28#include <sound/soc.h>
  29#include <sound/jack.h>
  30#include "../../codecs/max98090.h"
  31#include "../atom/sst-atom-controls.h"
  32#include "../../codecs/ts3a227e.h"
  33
  34#define CHT_PLAT_CLK_3_HZ       19200000
  35#define CHT_CODEC_DAI   "HiFi"
  36
  37struct cht_mc_private {
  38        struct snd_soc_jack jack;
  39        bool ts3a227e_present;
  40};
  41
  42static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
  43{
  44        struct snd_soc_pcm_runtime *rtd;
  45
  46        list_for_each_entry(rtd, &card->rtd_list, list) {
  47                if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
  48                             strlen(CHT_CODEC_DAI)))
  49                        return rtd->codec_dai;
  50        }
  51        return NULL;
  52}
  53
  54static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
  55        SND_SOC_DAPM_HP("Headphone", NULL),
  56        SND_SOC_DAPM_MIC("Headset Mic", NULL),
  57        SND_SOC_DAPM_MIC("Int Mic", NULL),
  58        SND_SOC_DAPM_SPK("Ext Spk", NULL),
  59};
  60
  61static const struct snd_soc_dapm_route cht_audio_map[] = {
  62        {"IN34", NULL, "Headset Mic"},
  63        {"Headset Mic", NULL, "MICBIAS"},
  64        {"DMICL", NULL, "Int Mic"},
  65        {"Headphone", NULL, "HPL"},
  66        {"Headphone", NULL, "HPR"},
  67        {"Ext Spk", NULL, "SPKL"},
  68        {"Ext Spk", NULL, "SPKR"},
  69        {"HiFi Playback", NULL, "ssp2 Tx"},
  70        {"ssp2 Tx", NULL, "codec_out0"},
  71        {"ssp2 Tx", NULL, "codec_out1"},
  72        {"codec_in0", NULL, "ssp2 Rx" },
  73        {"codec_in1", NULL, "ssp2 Rx" },
  74        {"ssp2 Rx", NULL, "HiFi Capture"},
  75};
  76
  77static const struct snd_kcontrol_new cht_mc_controls[] = {
  78        SOC_DAPM_PIN_SWITCH("Headphone"),
  79        SOC_DAPM_PIN_SWITCH("Headset Mic"),
  80        SOC_DAPM_PIN_SWITCH("Int Mic"),
  81        SOC_DAPM_PIN_SWITCH("Ext Spk"),
  82};
  83
  84static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
  85                             struct snd_pcm_hw_params *params)
  86{
  87        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  88        struct snd_soc_dai *codec_dai = rtd->codec_dai;
  89        int ret;
  90
  91        ret = snd_soc_dai_set_sysclk(codec_dai, M98090_REG_SYSTEM_CLOCK,
  92                                     CHT_PLAT_CLK_3_HZ, SND_SOC_CLOCK_IN);
  93        if (ret < 0) {
  94                dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
  95                return ret;
  96        }
  97
  98        return 0;
  99}
 100
 101static int cht_ti_jack_event(struct notifier_block *nb,
 102                unsigned long event, void *data)
 103{
 104        struct snd_soc_jack *jack = (struct snd_soc_jack *)data;
 105        struct snd_soc_dapm_context *dapm = &jack->card->dapm;
 106
 107        if (event & SND_JACK_MICROPHONE) {
 108                snd_soc_dapm_force_enable_pin(dapm, "SHDN");
 109                snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
 110                snd_soc_dapm_sync(dapm);
 111        } else {
 112                snd_soc_dapm_disable_pin(dapm, "MICBIAS");
 113                snd_soc_dapm_disable_pin(dapm, "SHDN");
 114                snd_soc_dapm_sync(dapm);
 115        }
 116
 117        return 0;
 118}
 119
 120static struct notifier_block cht_jack_nb = {
 121        .notifier_call = cht_ti_jack_event,
 122};
 123
 124static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
 125{
 126        int ret;
 127        int jack_type;
 128        struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);
 129        struct snd_soc_jack *jack = &ctx->jack;
 130
 131        /**
 132        * TI supports 4 butons headset detection
 133        * KEY_MEDIA
 134        * KEY_VOICECOMMAND
 135        * KEY_VOLUMEUP
 136        * KEY_VOLUMEDOWN
 137        */
 138        if (ctx->ts3a227e_present)
 139                jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
 140                                        SND_JACK_BTN_0 | SND_JACK_BTN_1 |
 141                                        SND_JACK_BTN_2 | SND_JACK_BTN_3;
 142        else
 143                jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE;
 144
 145        ret = snd_soc_card_jack_new(runtime->card, "Headset Jack",
 146                                        jack_type, jack, NULL, 0);
 147
 148        if (ret) {
 149                dev_err(runtime->dev, "Headset Jack creation failed %d\n", ret);
 150                return ret;
 151        }
 152
 153        if (ctx->ts3a227e_present)
 154                snd_soc_jack_notifier_register(jack, &cht_jack_nb);
 155
 156        return ret;
 157}
 158
 159static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
 160                            struct snd_pcm_hw_params *params)
 161{
 162        struct snd_interval *rate = hw_param_interval(params,
 163                        SNDRV_PCM_HW_PARAM_RATE);
 164        struct snd_interval *channels = hw_param_interval(params,
 165                                                SNDRV_PCM_HW_PARAM_CHANNELS);
 166        int ret = 0;
 167        unsigned int fmt = 0;
 168
 169        ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 16);
 170        if (ret < 0) {
 171                dev_err(rtd->dev, "can't set cpu_dai slot fmt: %d\n", ret);
 172                return ret;
 173        }
 174
 175        fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF
 176                                | SND_SOC_DAIFMT_CBS_CFS;
 177
 178        ret = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt);
 179        if (ret < 0) {
 180                dev_err(rtd->dev, "can't set cpu_dai set fmt: %d\n", ret);
 181                return ret;
 182        }
 183
 184        /* The DSP will covert the FE rate to 48k, stereo, 24bits */
 185        rate->min = rate->max = 48000;
 186        channels->min = channels->max = 2;
 187
 188        /* set SSP2 to 24-bit */
 189        params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
 190        return 0;
 191}
 192
 193static int cht_aif1_startup(struct snd_pcm_substream *substream)
 194{
 195        return snd_pcm_hw_constraint_single(substream->runtime,
 196                        SNDRV_PCM_HW_PARAM_RATE, 48000);
 197}
 198
 199static int cht_max98090_headset_init(struct snd_soc_component *component)
 200{
 201        struct snd_soc_card *card = component->card;
 202        struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
 203
 204        return ts3a227e_enable_jack_detect(component, &ctx->jack);
 205}
 206
 207static struct snd_soc_ops cht_aif1_ops = {
 208        .startup = cht_aif1_startup,
 209};
 210
 211static struct snd_soc_ops cht_be_ssp2_ops = {
 212        .hw_params = cht_aif1_hw_params,
 213};
 214
 215static struct snd_soc_aux_dev cht_max98090_headset_dev = {
 216        .name = "Headset Chip",
 217        .init = cht_max98090_headset_init,
 218        .codec_name = "i2c-104C227E:00",
 219};
 220
 221static struct snd_soc_dai_link cht_dailink[] = {
 222        [MERR_DPCM_AUDIO] = {
 223                .name = "Audio Port",
 224                .stream_name = "Audio",
 225                .cpu_dai_name = "media-cpu-dai",
 226                .codec_dai_name = "snd-soc-dummy-dai",
 227                .codec_name = "snd-soc-dummy",
 228                .platform_name = "sst-mfld-platform",
 229                .nonatomic = true,
 230                .dynamic = 1,
 231                .dpcm_playback = 1,
 232                .dpcm_capture = 1,
 233                .ops = &cht_aif1_ops,
 234        },
 235        [MERR_DPCM_DEEP_BUFFER] = {
 236                .name = "Deep-Buffer Audio Port",
 237                .stream_name = "Deep-Buffer Audio",
 238                .cpu_dai_name = "deepbuffer-cpu-dai",
 239                .codec_dai_name = "snd-soc-dummy-dai",
 240                .codec_name = "snd-soc-dummy",
 241                .platform_name = "sst-mfld-platform",
 242                .nonatomic = true,
 243                .dynamic = 1,
 244                .dpcm_playback = 1,
 245                .ops = &cht_aif1_ops,
 246        },
 247        [MERR_DPCM_COMPR] = {
 248                .name = "Compressed Port",
 249                .stream_name = "Compress",
 250                .cpu_dai_name = "compress-cpu-dai",
 251                .codec_dai_name = "snd-soc-dummy-dai",
 252                .codec_name = "snd-soc-dummy",
 253                .platform_name = "sst-mfld-platform",
 254        },
 255        /* back ends */
 256        {
 257                .name = "SSP2-Codec",
 258                .id = 1,
 259                .cpu_dai_name = "ssp2-port",
 260                .platform_name = "sst-mfld-platform",
 261                .no_pcm = 1,
 262                .codec_dai_name = "HiFi",
 263                .codec_name = "i2c-193C9890:00",
 264                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
 265                                        | SND_SOC_DAIFMT_CBS_CFS,
 266                .init = cht_codec_init,
 267                .be_hw_params_fixup = cht_codec_fixup,
 268                .dpcm_playback = 1,
 269                .dpcm_capture = 1,
 270                .ops = &cht_be_ssp2_ops,
 271        },
 272};
 273
 274/* SoC card */
 275static struct snd_soc_card snd_soc_card_cht = {
 276        .name = "chtmax98090",
 277        .owner = THIS_MODULE,
 278        .dai_link = cht_dailink,
 279        .num_links = ARRAY_SIZE(cht_dailink),
 280        .aux_dev = &cht_max98090_headset_dev,
 281        .num_aux_devs = 1,
 282        .dapm_widgets = cht_dapm_widgets,
 283        .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
 284        .dapm_routes = cht_audio_map,
 285        .num_dapm_routes = ARRAY_SIZE(cht_audio_map),
 286        .controls = cht_mc_controls,
 287        .num_controls = ARRAY_SIZE(cht_mc_controls),
 288};
 289
 290static int snd_cht_mc_probe(struct platform_device *pdev)
 291{
 292        int ret_val = 0;
 293        struct cht_mc_private *drv;
 294
 295        drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
 296        if (!drv)
 297                return -ENOMEM;
 298
 299        drv->ts3a227e_present = acpi_dev_found("104C227E");
 300        if (!drv->ts3a227e_present) {
 301                /* no need probe TI jack detection chip */
 302                snd_soc_card_cht.aux_dev = NULL;
 303                snd_soc_card_cht.num_aux_devs = 0;
 304        }
 305
 306        /* register the soc card */
 307        snd_soc_card_cht.dev = &pdev->dev;
 308        snd_soc_card_set_drvdata(&snd_soc_card_cht, drv);
 309        ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
 310        if (ret_val) {
 311                dev_err(&pdev->dev,
 312                        "snd_soc_register_card failed %d\n", ret_val);
 313                return ret_val;
 314        }
 315        platform_set_drvdata(pdev, &snd_soc_card_cht);
 316        return ret_val;
 317}
 318
 319static struct platform_driver snd_cht_mc_driver = {
 320        .driver = {
 321                .name = "cht-bsw-max98090",
 322        },
 323        .probe = snd_cht_mc_probe,
 324};
 325
 326module_platform_driver(snd_cht_mc_driver)
 327
 328MODULE_DESCRIPTION("ASoC Intel(R) Braswell Machine driver");
 329MODULE_AUTHOR("Fang, Yang A <yang.a.fang@intel.com>");
 330MODULE_LICENSE("GPL v2");
 331MODULE_ALIAS("platform:cht-bsw-max98090");
 332