h1940_uda1380.c (5437B)
1// SPDX-License-Identifier: GPL-2.0+ 2// 3// h1940_uda1380.c - ALSA SoC Audio Layer 4// 5// Copyright (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org> 6// Copyright (c) 2010 Vasily Khoruzhick <anarsoul@gmail.com> 7// 8// Based on version from Arnaud Patard <arnaud.patard@rtp-net.org> 9 10#include <linux/types.h> 11#include <linux/gpio.h> 12#include <linux/module.h> 13 14#include <sound/soc.h> 15#include <sound/jack.h> 16 17#include "regs-iis.h" 18#include "s3c24xx-i2s.h" 19 20static const unsigned int rates[] = { 21 11025, 22 22050, 23 44100, 24}; 25 26static const struct snd_pcm_hw_constraint_list hw_rates = { 27 .count = ARRAY_SIZE(rates), 28 .list = rates, 29}; 30 31static struct gpio_desc *gpiod_speaker_power; 32 33static struct snd_soc_jack hp_jack; 34 35static struct snd_soc_jack_pin hp_jack_pins[] = { 36 { 37 .pin = "Headphone Jack", 38 .mask = SND_JACK_HEADPHONE, 39 }, 40 { 41 .pin = "Speaker", 42 .mask = SND_JACK_HEADPHONE, 43 .invert = 1, 44 }, 45}; 46 47static struct snd_soc_jack_gpio hp_jack_gpios[] = { 48 { 49 .name = "hp-gpio", 50 .report = SND_JACK_HEADPHONE, 51 .invert = 1, 52 .debounce_time = 200, 53 }, 54}; 55 56static int h1940_startup(struct snd_pcm_substream *substream) 57{ 58 struct snd_pcm_runtime *runtime = substream->runtime; 59 60 return snd_pcm_hw_constraint_list(runtime, 0, 61 SNDRV_PCM_HW_PARAM_RATE, 62 &hw_rates); 63} 64 65static int h1940_hw_params(struct snd_pcm_substream *substream, 66 struct snd_pcm_hw_params *params) 67{ 68 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 69 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 70 int div; 71 int ret; 72 unsigned int rate = params_rate(params); 73 74 switch (rate) { 75 case 11025: 76 case 22050: 77 case 44100: 78 div = s3c24xx_i2s_get_clockrate() / (384 * rate); 79 if (s3c24xx_i2s_get_clockrate() % (384 * rate) > (192 * rate)) 80 div++; 81 break; 82 default: 83 dev_err(rtd->dev, "%s: rate %d is not supported\n", 84 __func__, rate); 85 return -EINVAL; 86 } 87 88 /* select clock source */ 89 ret = snd_soc_dai_set_sysclk(cpu_dai, S3C24XX_CLKSRC_PCLK, rate, 90 SND_SOC_CLOCK_OUT); 91 if (ret < 0) 92 return ret; 93 94 /* set MCLK division for sample rate */ 95 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, 96 S3C2410_IISMOD_384FS); 97 if (ret < 0) 98 return ret; 99 100 /* set BCLK division for sample rate */ 101 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK, 102 S3C2410_IISMOD_32FS); 103 if (ret < 0) 104 return ret; 105 106 /* set prescaler division for sample rate */ 107 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER, 108 S3C24XX_PRESCALE(div, div)); 109 if (ret < 0) 110 return ret; 111 112 return 0; 113} 114 115static const struct snd_soc_ops h1940_ops = { 116 .startup = h1940_startup, 117 .hw_params = h1940_hw_params, 118}; 119 120static int h1940_spk_power(struct snd_soc_dapm_widget *w, 121 struct snd_kcontrol *kcontrol, int event) 122{ 123 if (SND_SOC_DAPM_EVENT_ON(event)) 124 gpiod_set_value(gpiod_speaker_power, 1); 125 else 126 gpiod_set_value(gpiod_speaker_power, 0); 127 128 return 0; 129} 130 131/* h1940 machine dapm widgets */ 132static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = { 133 SND_SOC_DAPM_HP("Headphone Jack", NULL), 134 SND_SOC_DAPM_MIC("Mic Jack", NULL), 135 SND_SOC_DAPM_SPK("Speaker", h1940_spk_power), 136}; 137 138/* h1940 machine audio_map */ 139static const struct snd_soc_dapm_route audio_map[] = { 140 /* headphone connected to VOUTLHP, VOUTRHP */ 141 {"Headphone Jack", NULL, "VOUTLHP"}, 142 {"Headphone Jack", NULL, "VOUTRHP"}, 143 144 /* ext speaker connected to VOUTL, VOUTR */ 145 {"Speaker", NULL, "VOUTL"}, 146 {"Speaker", NULL, "VOUTR"}, 147 148 /* mic is connected to VINM */ 149 {"VINM", NULL, "Mic Jack"}, 150}; 151 152static int h1940_uda1380_init(struct snd_soc_pcm_runtime *rtd) 153{ 154 snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack", 155 SND_JACK_HEADPHONE, 156 &hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins)); 157 158 snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios), 159 hp_jack_gpios); 160 161 return 0; 162} 163 164/* s3c24xx digital audio interface glue - connects codec <--> CPU */ 165SND_SOC_DAILINK_DEFS(uda1380, 166 DAILINK_COMP_ARRAY(COMP_CPU("s3c24xx-iis")), 167 DAILINK_COMP_ARRAY(COMP_CODEC("uda1380-codec.0-001a", "uda1380-hifi")), 168 DAILINK_COMP_ARRAY(COMP_PLATFORM("s3c24xx-iis"))); 169 170static struct snd_soc_dai_link h1940_uda1380_dai[] = { 171 { 172 .name = "uda1380", 173 .stream_name = "UDA1380 Duplex", 174 .init = h1940_uda1380_init, 175 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 176 SND_SOC_DAIFMT_CBS_CFS, 177 .ops = &h1940_ops, 178 SND_SOC_DAILINK_REG(uda1380), 179 }, 180}; 181 182static struct snd_soc_card h1940_asoc = { 183 .name = "h1940", 184 .owner = THIS_MODULE, 185 .dai_link = h1940_uda1380_dai, 186 .num_links = ARRAY_SIZE(h1940_uda1380_dai), 187 188 .dapm_widgets = uda1380_dapm_widgets, 189 .num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets), 190 .dapm_routes = audio_map, 191 .num_dapm_routes = ARRAY_SIZE(audio_map), 192}; 193 194static int h1940_probe(struct platform_device *pdev) 195{ 196 struct device *dev = &pdev->dev; 197 198 h1940_asoc.dev = dev; 199 hp_jack_gpios[0].gpiod_dev = dev; 200 gpiod_speaker_power = devm_gpiod_get(&pdev->dev, "speaker-power", 201 GPIOD_OUT_LOW); 202 203 if (IS_ERR(gpiod_speaker_power)) { 204 dev_err(dev, "Could not get gpio\n"); 205 return PTR_ERR(gpiod_speaker_power); 206 } 207 208 return devm_snd_soc_register_card(dev, &h1940_asoc); 209} 210 211static struct platform_driver h1940_audio_driver = { 212 .driver = { 213 .name = "h1940-audio", 214 .pm = &snd_soc_pm_ops, 215 }, 216 .probe = h1940_probe, 217}; 218module_platform_driver(h1940_audio_driver); 219 220/* Module information */ 221MODULE_AUTHOR("Arnaud Patard, Vasily Khoruzhick"); 222MODULE_DESCRIPTION("ALSA SoC H1940"); 223MODULE_LICENSE("GPL"); 224MODULE_ALIAS("platform:h1940-audio");