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 25 static struct snd_soc_jack hs_jack; 26 27 /* Headphones jack detection DAPM pin */ 28 static 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 */ 42 static 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 */ 53 static int hx4700_hw_params(struct snd_pcm_substream *substream, 54 struct snd_pcm_hw_params *params) 55 { 56 struct snd_soc_pcm_runtime *rtd = substream->private_data; 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 77 static const struct snd_soc_ops hx4700_ops = { 78 .hw_params = hx4700_hw_params, 79 }; 80 81 static 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 88 static 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 */ 96 static 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 */ 103 static 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 */ 120 static 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 */ 137 SND_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 142 static 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 */ 153 static 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 165 static 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 170 static 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 191 static 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 200 static 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 209 module_platform_driver(hx4700_audio_driver); 210 211 MODULE_AUTHOR("Philipp Zabel"); 212 MODULE_DESCRIPTION("ALSA SoC iPAQ hx4700"); 213 MODULE_LICENSE("GPL"); 214 MODULE_ALIAS("platform:hx4700-audio"); 215
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.