linux/sound/soc/qcom/common.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2// Copyright (c) 2018, Linaro Limited.
   3// Copyright (c) 2018, The Linux Foundation. All rights reserved.
   4
   5#include <dt-bindings/sound/qcom,q6afe.h>
   6#include <linux/module.h>
   7#include <sound/jack.h>
   8#include <linux/input-event-codes.h>
   9#include "common.h"
  10
  11#define NAME_SIZE       32
  12
  13static const struct snd_soc_dapm_widget qcom_jack_snd_widgets[] = {
  14        SND_SOC_DAPM_HP("Headphone Jack", NULL),
  15        SND_SOC_DAPM_MIC("Mic Jack", NULL),
  16        SND_SOC_DAPM_SPK("DP0 Jack", NULL),
  17        SND_SOC_DAPM_SPK("DP1 Jack", NULL),
  18        SND_SOC_DAPM_SPK("DP2 Jack", NULL),
  19        SND_SOC_DAPM_SPK("DP3 Jack", NULL),
  20        SND_SOC_DAPM_SPK("DP4 Jack", NULL),
  21        SND_SOC_DAPM_SPK("DP5 Jack", NULL),
  22        SND_SOC_DAPM_SPK("DP6 Jack", NULL),
  23        SND_SOC_DAPM_SPK("DP7 Jack", NULL),
  24};
  25
  26int qcom_snd_parse_of(struct snd_soc_card *card)
  27{
  28        struct device_node *np;
  29        struct device_node *codec = NULL;
  30        struct device_node *platform = NULL;
  31        struct device_node *cpu = NULL;
  32        struct device *dev = card->dev;
  33        struct snd_soc_dai_link *link;
  34        struct of_phandle_args args;
  35        struct snd_soc_dai_link_component *dlc;
  36        int ret, num_links;
  37
  38        ret = snd_soc_of_parse_card_name(card, "model");
  39        if (ret == 0 && !card->name)
  40                /* Deprecated, only for compatibility with old device trees */
  41                ret = snd_soc_of_parse_card_name(card, "qcom,model");
  42        if (ret) {
  43                dev_err(dev, "Error parsing card name: %d\n", ret);
  44                return ret;
  45        }
  46
  47        if (of_property_present(dev->of_node, "widgets")) {
  48                ret = snd_soc_of_parse_audio_simple_widgets(card, "widgets");
  49                if (ret)
  50                        return ret;
  51        }
  52
  53        /* DAPM routes */
  54        if (of_property_present(dev->of_node, "audio-routing")) {
  55                ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
  56                if (ret)
  57                        return ret;
  58        }
  59        /* Deprecated, only for compatibility with old device trees */
  60        if (of_property_present(dev->of_node, "qcom,audio-routing")) {
  61                ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing");
  62                if (ret)
  63                        return ret;
  64        }
  65
  66        ret = snd_soc_of_parse_pin_switches(card, "pin-switches");
  67        if (ret)
  68                return ret;
  69
  70        ret = snd_soc_of_parse_aux_devs(card, "aux-devs");
  71        if (ret)
  72                return ret;
  73
  74        /* Populate links */
  75        num_links = of_get_available_child_count(dev->of_node);
  76
  77        /* Allocate the DAI link array */
  78        card->dai_link = devm_kcalloc(dev, num_links, sizeof(*link), GFP_KERNEL);
  79        if (!card->dai_link)
  80                return -ENOMEM;
  81
  82        card->num_links = num_links;
  83        link = card->dai_link;
  84
  85        for_each_available_child_of_node(dev->of_node, np) {
  86                dlc = devm_kcalloc(dev, 2, sizeof(*dlc), GFP_KERNEL);
  87                if (!dlc) {
  88                        ret = -ENOMEM;
  89                        goto err_put_np;
  90                }
  91
  92                link->cpus      = &dlc[0];
  93                link->platforms = &dlc[1];
  94
  95                link->num_cpus          = 1;
  96                link->num_platforms     = 1;
  97
  98                ret = of_property_read_string(np, "link-name", &link->name);
  99                if (ret) {
 100                        dev_err(card->dev, "error getting codec dai_link name\n");
 101                        goto err_put_np;
 102                }
 103
 104                cpu = of_get_child_by_name(np, "cpu");
 105                platform = of_get_child_by_name(np, "platform");
 106                codec = of_get_child_by_name(np, "codec");
 107
 108                if (!cpu) {
 109                        dev_err(dev, "%s: Can't find cpu DT node\n", link->name);
 110                        ret = -EINVAL;
 111                        goto err;
 112                }
 113
 114                ret = snd_soc_of_get_dlc(cpu, &args, link->cpus, 0);
 115                if (ret) {
 116                        dev_err_probe(card->dev, ret,
 117                                      "%s: error getting cpu dai name\n", link->name);
 118                        goto err;
 119                }
 120
 121                link->id = args.args[0];
 122
 123                if (platform) {
 124                        link->platforms->of_node = of_parse_phandle(platform,
 125                                        "sound-dai",
 126                                        0);
 127                        if (!link->platforms->of_node) {
 128                                dev_err(card->dev, "%s: platform dai not found\n", link->name);
 129                                ret = -EINVAL;
 130                                goto err;
 131                        }
 132                } else {
 133                        link->platforms->of_node = link->cpus->of_node;
 134                }
 135
 136                if (codec) {
 137                        ret = snd_soc_of_get_dai_link_codecs(dev, codec, link);
 138                        if (ret < 0) {
 139                                dev_err_probe(card->dev, ret,
 140                                              "%s: codec dai not found\n", link->name);
 141                                goto err;
 142                        }
 143
 144                        if (platform) {
 145                                /* DPCM backend */
 146                                link->no_pcm = 1;
 147                                link->ignore_pmdown_time = 1;
 148                        }
 149                } else {
 150                        /* DPCM frontend */
 151                        link->codecs     = &snd_soc_dummy_dlc;
 152                        link->num_codecs = 1;
 153                        link->dynamic = 1;
 154                }
 155
 156                if (platform || !codec) {
 157                        /* DPCM */
 158                        link->ignore_suspend = 1;
 159                        link->nonatomic = 1;
 160                }
 161
 162                link->stream_name = link->name;
 163                link++;
 164
 165                of_node_put(cpu);
 166                of_node_put(codec);
 167                of_node_put(platform);
 168        }
 169
 170        if (!card->dapm_widgets) {
 171                card->dapm_widgets = qcom_jack_snd_widgets;
 172                card->num_dapm_widgets = ARRAY_SIZE(qcom_jack_snd_widgets);
 173        }
 174
 175        return 0;
 176err:
 177        of_node_put(cpu);
 178        of_node_put(codec);
 179        of_node_put(platform);
 180err_put_np:
 181        of_node_put(np);
 182        return ret;
 183}
 184EXPORT_SYMBOL_GPL(qcom_snd_parse_of);
 185
 186static struct snd_soc_jack_pin qcom_headset_jack_pins[] = {
 187        /* Headset */
 188        {
 189                .pin = "Mic Jack",
 190                .mask = SND_JACK_MICROPHONE,
 191        },
 192        {
 193                .pin = "Headphone Jack",
 194                .mask = SND_JACK_HEADPHONE,
 195        },
 196};
 197
 198int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd,
 199                            struct snd_soc_jack *jack, bool *jack_setup)
 200{
 201        struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
 202        struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
 203        struct snd_soc_card *card = rtd->card;
 204        int rval, i;
 205
 206        if (!*jack_setup) {
 207                rval = snd_soc_card_jack_new_pins(card, "Headset Jack",
 208                                             SND_JACK_HEADSET | SND_JACK_LINEOUT |
 209                                             SND_JACK_MECHANICAL |
 210                                             SND_JACK_BTN_0 | SND_JACK_BTN_1 |
 211                                             SND_JACK_BTN_2 | SND_JACK_BTN_3 |
 212                                             SND_JACK_BTN_4 | SND_JACK_BTN_5,
 213                                             jack, qcom_headset_jack_pins,
 214                                             ARRAY_SIZE(qcom_headset_jack_pins));
 215
 216                if (rval < 0) {
 217                        dev_err(card->dev, "Unable to add Headphone Jack\n");
 218                        return rval;
 219                }
 220
 221                snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_MEDIA);
 222                snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
 223                snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
 224                snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
 225                *jack_setup = true;
 226        }
 227
 228        switch (cpu_dai->id) {
 229        case TX_CODEC_DMA_TX_0:
 230        case TX_CODEC_DMA_TX_1:
 231        case TX_CODEC_DMA_TX_2:
 232        case TX_CODEC_DMA_TX_3:
 233                for_each_rtd_codec_dais(rtd, i, codec_dai) {
 234                        rval = snd_soc_component_set_jack(codec_dai->component,
 235                                                          jack, NULL);
 236                        if (rval != 0 && rval != -ENOTSUPP) {
 237                                dev_warn(card->dev, "Failed to set jack: %d\n", rval);
 238                                return rval;
 239                        }
 240                }
 241
 242                break;
 243        default:
 244                break;
 245        }
 246
 247
 248        return 0;
 249}
 250EXPORT_SYMBOL_GPL(qcom_snd_wcd_jack_setup);
 251
 252int qcom_snd_dp_jack_setup(struct snd_soc_pcm_runtime *rtd,
 253                           struct snd_soc_jack *dp_jack, int dp_pcm_id)
 254{
 255        struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
 256        struct snd_soc_card *card = rtd->card;
 257        char jack_name[NAME_SIZE];
 258        int rval, i;
 259
 260        snprintf(jack_name, sizeof(jack_name), "DP%d Jack", dp_pcm_id);
 261        rval = snd_soc_card_jack_new(card, jack_name, SND_JACK_AVOUT, dp_jack);
 262        if (rval)
 263                return rval;
 264
 265        for_each_rtd_codec_dais(rtd, i, codec_dai) {
 266                rval = snd_soc_component_set_jack(codec_dai->component, dp_jack, NULL);
 267                if (rval != 0 && rval != -ENOTSUPP) {
 268                        dev_warn(card->dev, "Failed to set jack: %d\n", rval);
 269                        return rval;
 270                }
 271        }
 272
 273        return 0;
 274}
 275EXPORT_SYMBOL_GPL(qcom_snd_dp_jack_setup);
 276
 277MODULE_DESCRIPTION("ASoC Qualcomm helper functions");
 278MODULE_LICENSE("GPL");
 279