1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * ALSA Soc PCM3008 codec support 4 * 5 * Author: Hugo Villeneuve 6 * Copyright (C) 2008 Lyrtech inc 7 * 8 * Based on AC97 Soc codec, original copyright follow: 9 * Copyright 2005 Wolfson Microelectronics PLC. 10 * 11 * Generic PCM3008 support. 12 */ 13 14 #include <linux/init.h> 15 #include <linux/kernel.h> 16 #include <linux/device.h> 17 #include <linux/gpio/consumer.h> 18 #include <linux/slab.h> 19 #include <linux/module.h> 20 #include <sound/core.h> 21 #include <sound/pcm.h> 22 #include <sound/initval.h> 23 #include <sound/soc.h> 24 25 struct pcm3008 { 26 struct gpio_desc *dem0_pin; 27 struct gpio_desc *dem1_pin; 28 struct gpio_desc *pdad_pin; 29 struct gpio_desc *pdda_pin; 30 }; 31 32 static int pcm3008_dac_ev(struct snd_soc_dapm_widget *w, 33 struct snd_kcontrol *kcontrol, 34 int event) 35 { 36 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 37 struct pcm3008 *pcm = component->dev->platform_data; 38 39 gpiod_set_value_cansleep(pcm->pdda_pin, 40 SND_SOC_DAPM_EVENT_ON(event)); 41 42 return 0; 43 } 44 45 static int pcm3008_adc_ev(struct snd_soc_dapm_widget *w, 46 struct snd_kcontrol *kcontrol, 47 int event) 48 { 49 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 50 struct pcm3008 *pcm = component->dev->platform_data; 51 52 gpiod_set_value_cansleep(pcm->pdad_pin, 53 SND_SOC_DAPM_EVENT_ON(event)); 54 55 return 0; 56 } 57 58 static const struct snd_soc_dapm_widget pcm3008_dapm_widgets[] = { 59 SND_SOC_DAPM_INPUT("VINL"), 60 SND_SOC_DAPM_INPUT("VINR"), 61 62 SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, pcm3008_dac_ev, 63 SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), 64 SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0, pcm3008_adc_ev, 65 SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), 66 67 SND_SOC_DAPM_OUTPUT("VOUTL"), 68 SND_SOC_DAPM_OUTPUT("VOUTR"), 69 }; 70 71 static const struct snd_soc_dapm_route pcm3008_dapm_routes[] = { 72 { "PCM3008 Capture", NULL, "ADC" }, 73 { "ADC", NULL, "VINL" }, 74 { "ADC", NULL, "VINR" }, 75 76 { "DAC", NULL, "PCM3008 Playback" }, 77 { "VOUTL", NULL, "DAC" }, 78 { "VOUTR", NULL, "DAC" }, 79 }; 80 81 #define PCM3008_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ 82 SNDRV_PCM_RATE_48000) 83 84 static struct snd_soc_dai_driver pcm3008_dai = { 85 .name = "pcm3008-hifi", 86 .playback = { 87 .stream_name = "PCM3008 Playback", 88 .channels_min = 1, 89 .channels_max = 2, 90 .rates = PCM3008_RATES, 91 .formats = SNDRV_PCM_FMTBIT_S16_LE, 92 }, 93 .capture = { 94 .stream_name = "PCM3008 Capture", 95 .channels_min = 1, 96 .channels_max = 2, 97 .rates = PCM3008_RATES, 98 .formats = SNDRV_PCM_FMTBIT_S16_LE, 99 }, 100 }; 101 102 static const struct snd_soc_component_driver soc_component_dev_pcm3008 = { 103 .dapm_widgets = pcm3008_dapm_widgets, 104 .num_dapm_widgets = ARRAY_SIZE(pcm3008_dapm_widgets), 105 .dapm_routes = pcm3008_dapm_routes, 106 .num_dapm_routes = ARRAY_SIZE(pcm3008_dapm_routes), 107 .idle_bias_on = 1, 108 .use_pmdown_time = 1, 109 .endianness = 1, 110 }; 111 112 static int pcm3008_codec_probe(struct platform_device *pdev) 113 { 114 struct device *dev = &pdev->dev; 115 struct pcm3008 *pcm; 116 117 pcm = devm_kzalloc(dev, sizeof(*pcm), GFP_KERNEL); 118 if (!pcm) 119 return -ENOMEM; 120 platform_set_drvdata(pdev, pcm); 121 122 /* DEM1 DEM0 DE-EMPHASIS_MODE 123 * Low Low De-emphasis 44.1 kHz ON 124 * Low High De-emphasis OFF 125 * High Low De-emphasis 48 kHz ON 126 * High High De-emphasis 32 kHz ON 127 */ 128 129 /* Configure DEM0 GPIO (turning OFF DAC De-emphasis). */ 130 pcm->dem0_pin = devm_gpiod_get(dev, "dem0", GPIOD_OUT_HIGH); 131 if (IS_ERR(pcm->dem0_pin)) 132 return PTR_ERR(pcm->dem0_pin); 133 134 /* Configure DEM1 GPIO (turning OFF DAC De-emphasis). */ 135 pcm->dem1_pin = devm_gpiod_get(dev, "dem1", GPIOD_OUT_LOW); 136 if (IS_ERR(pcm->dem1_pin)) 137 return PTR_ERR(pcm->dem1_pin); 138 139 /* Configure PDAD GPIO. */ 140 pcm->pdad_pin = devm_gpiod_get(dev, "pdad", GPIOD_OUT_LOW); 141 if (IS_ERR(pcm->pdad_pin)) 142 return PTR_ERR(pcm->pdad_pin); 143 144 /* Configure PDDA GPIO. */ 145 pcm->pdda_pin = devm_gpiod_get(dev, "pdda", GPIOD_OUT_LOW); 146 if (IS_ERR(pcm->pdda_pin)) 147 return PTR_ERR(pcm->pdda_pin); 148 149 return devm_snd_soc_register_component(dev, 150 &soc_component_dev_pcm3008, &pcm3008_dai, 1); 151 } 152 153 MODULE_ALIAS("platform:pcm3008-codec"); 154 155 static struct platform_driver pcm3008_codec_driver = { 156 .probe = pcm3008_codec_probe, 157 .driver = { 158 .name = "pcm3008-codec", 159 }, 160 }; 161 162 module_platform_driver(pcm3008_codec_driver); 163 164 MODULE_DESCRIPTION("Soc PCM3008 driver"); 165 MODULE_AUTHOR("Hugo Villeneuve"); 166 MODULE_LICENSE("GPL"); 167