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