linux/sound/soc/pxa/magician.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * SoC audio for HTC Magician
   4 *
   5 * Copyright (c) 2006 Philipp Zabel <philipp.zabel@gmail.com>
   6 *
   7 * based on spitz.c,
   8 * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
   9 *          Richard Purdie <richard@openedhand.com>
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/timer.h>
  14#include <linux/interrupt.h>
  15#include <linux/platform_device.h>
  16#include <linux/delay.h>
  17#include <linux/gpio.h>
  18#include <linux/i2c.h>
  19
  20#include <sound/core.h>
  21#include <sound/pcm.h>
  22#include <sound/pcm_params.h>
  23#include <sound/soc.h>
  24#include <sound/uda1380.h>
  25
  26#include <mach/magician.h>
  27#include <asm/mach-types.h>
  28#include "../codecs/uda1380.h"
  29#include "pxa2xx-i2s.h"
  30#include "pxa-ssp.h"
  31
  32#define MAGICIAN_MIC       0
  33#define MAGICIAN_MIC_EXT   1
  34
  35static int magician_hp_switch;
  36static int magician_spk_switch = 1;
  37static int magician_in_sel = MAGICIAN_MIC;
  38
  39static void magician_ext_control(struct snd_soc_dapm_context *dapm)
  40{
  41
  42        snd_soc_dapm_mutex_lock(dapm);
  43
  44        if (magician_spk_switch)
  45                snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker");
  46        else
  47                snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker");
  48        if (magician_hp_switch)
  49                snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
  50        else
  51                snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
  52
  53        switch (magician_in_sel) {
  54        case MAGICIAN_MIC:
  55                snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Mic");
  56                snd_soc_dapm_enable_pin_unlocked(dapm, "Call Mic");
  57                break;
  58        case MAGICIAN_MIC_EXT:
  59                snd_soc_dapm_disable_pin_unlocked(dapm, "Call Mic");
  60                snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Mic");
  61                break;
  62        }
  63
  64        snd_soc_dapm_sync_unlocked(dapm);
  65
  66        snd_soc_dapm_mutex_unlock(dapm);
  67}
  68
  69static int magician_startup(struct snd_pcm_substream *substream)
  70{
  71        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  72
  73        /* check the jack status at stream startup */
  74        magician_ext_control(&rtd->card->dapm);
  75
  76        return 0;
  77}
  78
  79/*
  80 * Magician uses SSP port for playback.
  81 */
  82static int magician_playback_hw_params(struct snd_pcm_substream *substream,
  83                                       struct snd_pcm_hw_params *params)
  84{
  85        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  86        struct snd_soc_dai *codec_dai = rtd->codec_dai;
  87        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  88        unsigned int width;
  89        int ret = 0;
  90
  91        /* set codec DAI configuration */
  92        ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_MSB |
  93                        SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
  94        if (ret < 0)
  95                return ret;
  96
  97        /* set cpu DAI configuration */
  98        ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
  99                        SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_CBS_CFS);
 100        if (ret < 0)
 101                return ret;
 102
 103        width = snd_pcm_format_physical_width(params_format(params));
 104        ret = snd_soc_dai_set_tdm_slot(cpu_dai, 1, 0, 1, width);
 105        if (ret < 0)
 106                return ret;
 107
 108        /* set audio clock as clock source */
 109        ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0,
 110                        SND_SOC_CLOCK_OUT);
 111        if (ret < 0)
 112                return ret;
 113
 114        return 0;
 115}
 116
 117/*
 118 * Magician uses I2S for capture.
 119 */
 120static int magician_capture_hw_params(struct snd_pcm_substream *substream,
 121                                      struct snd_pcm_hw_params *params)
 122{
 123        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 124        struct snd_soc_dai *codec_dai = rtd->codec_dai;
 125        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 126        int ret = 0;
 127
 128        /* set codec DAI configuration */
 129        ret = snd_soc_dai_set_fmt(codec_dai,
 130                        SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
 131                        SND_SOC_DAIFMT_CBS_CFS);
 132        if (ret < 0)
 133                return ret;
 134
 135        /* set cpu DAI configuration */
 136        ret = snd_soc_dai_set_fmt(cpu_dai,
 137                        SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
 138                        SND_SOC_DAIFMT_CBS_CFS);
 139        if (ret < 0)
 140                return ret;
 141
 142        /* set the I2S system clock as output */
 143        ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
 144                        SND_SOC_CLOCK_OUT);
 145        if (ret < 0)
 146                return ret;
 147
 148        return 0;
 149}
 150
 151static const struct snd_soc_ops magician_capture_ops = {
 152        .startup = magician_startup,
 153        .hw_params = magician_capture_hw_params,
 154};
 155
 156static const struct snd_soc_ops magician_playback_ops = {
 157        .startup = magician_startup,
 158        .hw_params = magician_playback_hw_params,
 159};
 160
 161static int magician_get_hp(struct snd_kcontrol *kcontrol,
 162                             struct snd_ctl_elem_value *ucontrol)
 163{
 164        ucontrol->value.integer.value[0] = magician_hp_switch;
 165        return 0;
 166}
 167
 168static int magician_set_hp(struct snd_kcontrol *kcontrol,
 169                             struct snd_ctl_elem_value *ucontrol)
 170{
 171        struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 172
 173        if (magician_hp_switch == ucontrol->value.integer.value[0])
 174                return 0;
 175
 176        magician_hp_switch = ucontrol->value.integer.value[0];
 177        magician_ext_control(&card->dapm);
 178        return 1;
 179}
 180
 181static int magician_get_spk(struct snd_kcontrol *kcontrol,
 182                            struct snd_ctl_elem_value *ucontrol)
 183{
 184        ucontrol->value.integer.value[0] = magician_spk_switch;
 185        return 0;
 186}
 187
 188static int magician_set_spk(struct snd_kcontrol *kcontrol,
 189                            struct snd_ctl_elem_value *ucontrol)
 190{
 191        struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 192
 193        if (magician_spk_switch == ucontrol->value.integer.value[0])
 194                return 0;
 195
 196        magician_spk_switch = ucontrol->value.integer.value[0];
 197        magician_ext_control(&card->dapm);
 198        return 1;
 199}
 200
 201static int magician_get_input(struct snd_kcontrol *kcontrol,
 202                              struct snd_ctl_elem_value *ucontrol)
 203{
 204        ucontrol->value.enumerated.item[0] = magician_in_sel;
 205        return 0;
 206}
 207
 208static int magician_set_input(struct snd_kcontrol *kcontrol,
 209                              struct snd_ctl_elem_value *ucontrol)
 210{
 211        if (magician_in_sel == ucontrol->value.enumerated.item[0])
 212                return 0;
 213
 214        magician_in_sel = ucontrol->value.enumerated.item[0];
 215
 216        switch (magician_in_sel) {
 217        case MAGICIAN_MIC:
 218                gpio_set_value(EGPIO_MAGICIAN_IN_SEL1, 1);
 219                break;
 220        case MAGICIAN_MIC_EXT:
 221                gpio_set_value(EGPIO_MAGICIAN_IN_SEL1, 0);
 222        }
 223
 224        return 1;
 225}
 226
 227static int magician_spk_power(struct snd_soc_dapm_widget *w,
 228                                struct snd_kcontrol *k, int event)
 229{
 230        gpio_set_value(EGPIO_MAGICIAN_SPK_POWER, SND_SOC_DAPM_EVENT_ON(event));
 231        return 0;
 232}
 233
 234static int magician_hp_power(struct snd_soc_dapm_widget *w,
 235                                struct snd_kcontrol *k, int event)
 236{
 237        gpio_set_value(EGPIO_MAGICIAN_EP_POWER, SND_SOC_DAPM_EVENT_ON(event));
 238        return 0;
 239}
 240
 241static int magician_mic_bias(struct snd_soc_dapm_widget *w,
 242                                struct snd_kcontrol *k, int event)
 243{
 244        gpio_set_value(EGPIO_MAGICIAN_MIC_POWER, SND_SOC_DAPM_EVENT_ON(event));
 245        return 0;
 246}
 247
 248/* magician machine dapm widgets */
 249static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
 250        SND_SOC_DAPM_HP("Headphone Jack", magician_hp_power),
 251        SND_SOC_DAPM_SPK("Speaker", magician_spk_power),
 252        SND_SOC_DAPM_MIC("Call Mic", magician_mic_bias),
 253        SND_SOC_DAPM_MIC("Headset Mic", magician_mic_bias),
 254};
 255
 256/* magician machine audio_map */
 257static const struct snd_soc_dapm_route audio_map[] = {
 258
 259        /* Headphone connected to VOUTL, VOUTR */
 260        {"Headphone Jack", NULL, "VOUTL"},
 261        {"Headphone Jack", NULL, "VOUTR"},
 262
 263        /* Speaker connected to VOUTL, VOUTR */
 264        {"Speaker", NULL, "VOUTL"},
 265        {"Speaker", NULL, "VOUTR"},
 266
 267        /* Mics are connected to VINM */
 268        {"VINM", NULL, "Headset Mic"},
 269        {"VINM", NULL, "Call Mic"},
 270};
 271
 272static const char * const input_select[] = {"Call Mic", "Headset Mic"};
 273static const struct soc_enum magician_in_sel_enum =
 274        SOC_ENUM_SINGLE_EXT(2, input_select);
 275
 276static const struct snd_kcontrol_new uda1380_magician_controls[] = {
 277        SOC_SINGLE_BOOL_EXT("Headphone Switch",
 278                        (unsigned long)&magician_hp_switch,
 279                        magician_get_hp, magician_set_hp),
 280        SOC_SINGLE_BOOL_EXT("Speaker Switch",
 281                        (unsigned long)&magician_spk_switch,
 282                        magician_get_spk, magician_set_spk),
 283        SOC_ENUM_EXT("Input Select", magician_in_sel_enum,
 284                        magician_get_input, magician_set_input),
 285};
 286
 287/* magician digital audio interface glue - connects codec <--> CPU */
 288SND_SOC_DAILINK_DEFS(playback,
 289        DAILINK_COMP_ARRAY(COMP_CPU("pxa-ssp-dai.0")),
 290        DAILINK_COMP_ARRAY(COMP_CODEC("uda1380-codec.0-0018",
 291                                      "uda1380-hifi-playback")),
 292        DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
 293
 294SND_SOC_DAILINK_DEFS(capture,
 295        DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
 296        DAILINK_COMP_ARRAY(COMP_CODEC("uda1380-codec.0-0018",
 297                                      "uda1380-hifi-capture")),
 298        DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
 299
 300static struct snd_soc_dai_link magician_dai[] = {
 301{
 302        .name = "uda1380",
 303        .stream_name = "UDA1380 Playback",
 304        .ops = &magician_playback_ops,
 305        SND_SOC_DAILINK_REG(playback),
 306},
 307{
 308        .name = "uda1380",
 309        .stream_name = "UDA1380 Capture",
 310        .ops = &magician_capture_ops,
 311        SND_SOC_DAILINK_REG(capture),
 312}
 313};
 314
 315/* magician audio machine driver */
 316static struct snd_soc_card snd_soc_card_magician = {
 317        .name = "Magician",
 318        .owner = THIS_MODULE,
 319        .dai_link = magician_dai,
 320        .num_links = ARRAY_SIZE(magician_dai),
 321
 322        .controls = uda1380_magician_controls,
 323        .num_controls = ARRAY_SIZE(uda1380_magician_controls),
 324        .dapm_widgets = uda1380_dapm_widgets,
 325        .num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets),
 326        .dapm_routes = audio_map,
 327        .num_dapm_routes = ARRAY_SIZE(audio_map),
 328        .fully_routed = true,
 329};
 330
 331static struct platform_device *magician_snd_device;
 332
 333/*
 334 * FIXME: move into magician board file once merged into the pxa tree
 335 */
 336static struct uda1380_platform_data uda1380_info = {
 337        .gpio_power = EGPIO_MAGICIAN_CODEC_POWER,
 338        .gpio_reset = EGPIO_MAGICIAN_CODEC_RESET,
 339        .dac_clk    = UDA1380_DAC_CLK_WSPLL,
 340};
 341
 342static struct i2c_board_info i2c_board_info[] = {
 343        {
 344                I2C_BOARD_INFO("uda1380", 0x18),
 345                .platform_data = &uda1380_info,
 346        },
 347};
 348
 349static int __init magician_init(void)
 350{
 351        int ret;
 352        struct i2c_adapter *adapter;
 353        struct i2c_client *client;
 354
 355        if (!machine_is_magician())
 356                return -ENODEV;
 357
 358        adapter = i2c_get_adapter(0);
 359        if (!adapter)
 360                return -ENODEV;
 361        client = i2c_new_device(adapter, i2c_board_info);
 362        i2c_put_adapter(adapter);
 363        if (!client)
 364                return -ENODEV;
 365
 366        ret = gpio_request(EGPIO_MAGICIAN_SPK_POWER, "SPK_POWER");
 367        if (ret)
 368                goto err_request_spk;
 369        ret = gpio_request(EGPIO_MAGICIAN_EP_POWER, "EP_POWER");
 370        if (ret)
 371                goto err_request_ep;
 372        ret = gpio_request(EGPIO_MAGICIAN_MIC_POWER, "MIC_POWER");
 373        if (ret)
 374                goto err_request_mic;
 375        ret = gpio_request(EGPIO_MAGICIAN_IN_SEL0, "IN_SEL0");
 376        if (ret)
 377                goto err_request_in_sel0;
 378        ret = gpio_request(EGPIO_MAGICIAN_IN_SEL1, "IN_SEL1");
 379        if (ret)
 380                goto err_request_in_sel1;
 381
 382        gpio_set_value(EGPIO_MAGICIAN_IN_SEL0, 0);
 383
 384        magician_snd_device = platform_device_alloc("soc-audio", -1);
 385        if (!magician_snd_device) {
 386                ret = -ENOMEM;
 387                goto err_pdev;
 388        }
 389
 390        platform_set_drvdata(magician_snd_device, &snd_soc_card_magician);
 391        ret = platform_device_add(magician_snd_device);
 392        if (ret) {
 393                platform_device_put(magician_snd_device);
 394                goto err_pdev;
 395        }
 396
 397        return 0;
 398
 399err_pdev:
 400        gpio_free(EGPIO_MAGICIAN_IN_SEL1);
 401err_request_in_sel1:
 402        gpio_free(EGPIO_MAGICIAN_IN_SEL0);
 403err_request_in_sel0:
 404        gpio_free(EGPIO_MAGICIAN_MIC_POWER);
 405err_request_mic:
 406        gpio_free(EGPIO_MAGICIAN_EP_POWER);
 407err_request_ep:
 408        gpio_free(EGPIO_MAGICIAN_SPK_POWER);
 409err_request_spk:
 410        return ret;
 411}
 412
 413static void __exit magician_exit(void)
 414{
 415        platform_device_unregister(magician_snd_device);
 416
 417        gpio_set_value(EGPIO_MAGICIAN_SPK_POWER, 0);
 418        gpio_set_value(EGPIO_MAGICIAN_EP_POWER, 0);
 419        gpio_set_value(EGPIO_MAGICIAN_MIC_POWER, 0);
 420
 421        gpio_free(EGPIO_MAGICIAN_IN_SEL1);
 422        gpio_free(EGPIO_MAGICIAN_IN_SEL0);
 423        gpio_free(EGPIO_MAGICIAN_MIC_POWER);
 424        gpio_free(EGPIO_MAGICIAN_EP_POWER);
 425        gpio_free(EGPIO_MAGICIAN_SPK_POWER);
 426}
 427
 428module_init(magician_init);
 429module_exit(magician_exit);
 430
 431MODULE_AUTHOR("Philipp Zabel");
 432MODULE_DESCRIPTION("ALSA SoC Magician");
 433MODULE_LICENSE("GPL");
 434