linux/sound/soc/pxa/hx4700.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * SoC audio for HP iPAQ hx4700
   4 *
   5 * Copyright (c) 2009 Philipp Zabel
   6 */
   7
   8#include <linux/module.h>
   9#include <linux/timer.h>
  10#include <linux/interrupt.h>
  11#include <linux/platform_device.h>
  12#include <linux/delay.h>
  13#include <linux/gpio.h>
  14
  15#include <sound/core.h>
  16#include <sound/jack.h>
  17#include <sound/pcm.h>
  18#include <sound/pcm_params.h>
  19#include <sound/soc.h>
  20
  21#include <mach/hx4700.h>
  22#include <asm/mach-types.h>
  23#include "pxa2xx-i2s.h"
  24
  25static struct snd_soc_jack hs_jack;
  26
  27/* Headphones jack detection DAPM pin */
  28static struct snd_soc_jack_pin hs_jack_pin[] = {
  29        {
  30                .pin    = "Headphone Jack",
  31                .mask   = SND_JACK_HEADPHONE,
  32        },
  33        {
  34                .pin    = "Speaker",
  35                /* disable speaker when hp jack is inserted */
  36                .mask   = SND_JACK_HEADPHONE,
  37                .invert = 1,
  38        },
  39};
  40
  41/* Headphones jack detection GPIO */
  42static struct snd_soc_jack_gpio hs_jack_gpio = {
  43        .gpio           = GPIO75_HX4700_EARPHONE_nDET,
  44        .invert         = true,
  45        .name           = "hp-gpio",
  46        .report         = SND_JACK_HEADPHONE,
  47        .debounce_time  = 200,
  48};
  49
  50/*
  51 * iPAQ hx4700 uses I2S for capture and playback.
  52 */
  53static int hx4700_hw_params(struct snd_pcm_substream *substream,
  54                            struct snd_pcm_hw_params *params)
  55{
  56        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
  57        struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
  58        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
  59        int ret = 0;
  60
  61        /* set the I2S system clock as output */
  62        ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
  63                        SND_SOC_CLOCK_OUT);
  64        if (ret < 0)
  65                return ret;
  66
  67        /* inform codec driver about clock freq *
  68         * (PXA I2S always uses divider 256)    */
  69        ret = snd_soc_dai_set_sysclk(codec_dai, 0, 256 * params_rate(params),
  70                        SND_SOC_CLOCK_IN);
  71        if (ret < 0)
  72                return ret;
  73
  74        return 0;
  75}
  76
  77static const struct snd_soc_ops hx4700_ops = {
  78        .hw_params = hx4700_hw_params,
  79};
  80
  81static int hx4700_spk_power(struct snd_soc_dapm_widget *w,
  82                            struct snd_kcontrol *k, int event)
  83{
  84        gpio_set_value(GPIO107_HX4700_SPK_nSD, !!SND_SOC_DAPM_EVENT_ON(event));
  85        return 0;
  86}
  87
  88static int hx4700_hp_power(struct snd_soc_dapm_widget *w,
  89                           struct snd_kcontrol *k, int event)
  90{
  91        gpio_set_value(GPIO92_HX4700_HP_DRIVER, !!SND_SOC_DAPM_EVENT_ON(event));
  92        return 0;
  93}
  94
  95/* hx4700 machine dapm widgets */
  96static const struct snd_soc_dapm_widget hx4700_dapm_widgets[] = {
  97        SND_SOC_DAPM_HP("Headphone Jack", hx4700_hp_power),
  98        SND_SOC_DAPM_SPK("Speaker", hx4700_spk_power),
  99        SND_SOC_DAPM_MIC("Built-in Microphone", NULL),
 100};
 101
 102/* hx4700 machine audio_map */
 103static const struct snd_soc_dapm_route hx4700_audio_map[] = {
 104
 105        /* Headphone connected to LOUT, ROUT */
 106        {"Headphone Jack", NULL, "LOUT"},
 107        {"Headphone Jack", NULL, "ROUT"},
 108
 109        /* Speaker connected to MOUT2 */
 110        {"Speaker", NULL, "MOUT2"},
 111
 112        /* Microphone connected to MICIN */
 113        {"MICIN", NULL, "Built-in Microphone"},
 114        {"AIN", NULL, "MICOUT"},
 115};
 116
 117/*
 118 * Logic for a ak4641 as connected on a HP iPAQ hx4700
 119 */
 120static int hx4700_ak4641_init(struct snd_soc_pcm_runtime *rtd)
 121{
 122        int err;
 123
 124        /* Jack detection API stuff */
 125        err = snd_soc_card_jack_new(rtd->card, "Headphone Jack",
 126                                    SND_JACK_HEADPHONE, &hs_jack, hs_jack_pin,
 127                                    ARRAY_SIZE(hs_jack_pin));
 128        if (err)
 129                return err;
 130
 131        err = snd_soc_jack_add_gpios(&hs_jack, 1, &hs_jack_gpio);
 132
 133        return err;
 134}
 135
 136/* hx4700 digital audio interface glue - connects codec <--> CPU */
 137SND_SOC_DAILINK_DEFS(ak4641,
 138        DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
 139        DAILINK_COMP_ARRAY(COMP_CODEC("ak4641.0-0012", "ak4641-hifi")),
 140        DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
 141
 142static struct snd_soc_dai_link hx4700_dai = {
 143        .name = "ak4641",
 144        .stream_name = "AK4641",
 145        .init = hx4700_ak4641_init,
 146        .dai_fmt = SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
 147                   SND_SOC_DAIFMT_CBS_CFS,
 148        .ops = &hx4700_ops,
 149        SND_SOC_DAILINK_REG(ak4641),
 150};
 151
 152/* hx4700 audio machine driver */
 153static struct snd_soc_card snd_soc_card_hx4700 = {
 154        .name                   = "iPAQ hx4700",
 155        .owner                  = THIS_MODULE,
 156        .dai_link               = &hx4700_dai,
 157        .num_links              = 1,
 158        .dapm_widgets           = hx4700_dapm_widgets,
 159        .num_dapm_widgets       = ARRAY_SIZE(hx4700_dapm_widgets),
 160        .dapm_routes            = hx4700_audio_map,
 161        .num_dapm_routes        = ARRAY_SIZE(hx4700_audio_map),
 162        .fully_routed           = true,
 163};
 164
 165static struct gpio hx4700_audio_gpios[] = {
 166        { GPIO107_HX4700_SPK_nSD, GPIOF_OUT_INIT_HIGH, "SPK_POWER" },
 167        { GPIO92_HX4700_HP_DRIVER, GPIOF_OUT_INIT_LOW, "EP_POWER" },
 168};
 169
 170static int hx4700_audio_probe(struct platform_device *pdev)
 171{
 172        int ret;
 173
 174        if (!machine_is_h4700())
 175                return -ENODEV;
 176
 177        ret = gpio_request_array(hx4700_audio_gpios,
 178                                ARRAY_SIZE(hx4700_audio_gpios));
 179        if (ret)
 180                return ret;
 181
 182        snd_soc_card_hx4700.dev = &pdev->dev;
 183        ret = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_hx4700);
 184        if (ret)
 185                gpio_free_array(hx4700_audio_gpios,
 186                                ARRAY_SIZE(hx4700_audio_gpios));
 187
 188        return ret;
 189}
 190
 191static int hx4700_audio_remove(struct platform_device *pdev)
 192{
 193        gpio_set_value(GPIO92_HX4700_HP_DRIVER, 0);
 194        gpio_set_value(GPIO107_HX4700_SPK_nSD, 0);
 195
 196        gpio_free_array(hx4700_audio_gpios, ARRAY_SIZE(hx4700_audio_gpios));
 197        return 0;
 198}
 199
 200static struct platform_driver hx4700_audio_driver = {
 201        .driver = {
 202                .name = "hx4700-audio",
 203                .pm = &snd_soc_pm_ops,
 204        },
 205        .probe  = hx4700_audio_probe,
 206        .remove = hx4700_audio_remove,
 207};
 208
 209module_platform_driver(hx4700_audio_driver);
 210
 211MODULE_AUTHOR("Philipp Zabel");
 212MODULE_DESCRIPTION("ALSA SoC iPAQ hx4700");
 213MODULE_LICENSE("GPL");
 214MODULE_ALIAS("platform:hx4700-audio");
 215