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.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 | #include "pcm3008.h" |
26 | |
27 | static int pcm3008_dac_ev(struct snd_soc_dapm_widget *w, |
28 | struct snd_kcontrol *kcontrol, |
29 | int event) |
30 | { |
31 | struct snd_soc_component *component = snd_soc_dapm_to_component(dapm: w->dapm); |
32 | struct pcm3008_setup_data *setup = component->dev->platform_data; |
33 | |
34 | gpio_set_value_cansleep(gpio: setup->pdda_pin, |
35 | SND_SOC_DAPM_EVENT_ON(event)); |
36 | |
37 | return 0; |
38 | } |
39 | |
40 | static int pcm3008_adc_ev(struct snd_soc_dapm_widget *w, |
41 | struct snd_kcontrol *kcontrol, |
42 | int event) |
43 | { |
44 | struct snd_soc_component *component = snd_soc_dapm_to_component(dapm: w->dapm); |
45 | struct pcm3008_setup_data *setup = component->dev->platform_data; |
46 | |
47 | gpio_set_value_cansleep(gpio: setup->pdad_pin, |
48 | SND_SOC_DAPM_EVENT_ON(event)); |
49 | |
50 | return 0; |
51 | } |
52 | |
53 | static const struct snd_soc_dapm_widget pcm3008_dapm_widgets[] = { |
54 | SND_SOC_DAPM_INPUT("VINL" ), |
55 | SND_SOC_DAPM_INPUT("VINR" ), |
56 | |
57 | SND_SOC_DAPM_DAC_E("DAC" , NULL, SND_SOC_NOPM, 0, 0, pcm3008_dac_ev, |
58 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), |
59 | SND_SOC_DAPM_ADC_E("ADC" , NULL, SND_SOC_NOPM, 0, 0, pcm3008_adc_ev, |
60 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), |
61 | |
62 | SND_SOC_DAPM_OUTPUT("VOUTL" ), |
63 | SND_SOC_DAPM_OUTPUT("VOUTR" ), |
64 | }; |
65 | |
66 | static const struct snd_soc_dapm_route pcm3008_dapm_routes[] = { |
67 | { "PCM3008 Capture" , NULL, "ADC" }, |
68 | { "ADC" , NULL, "VINL" }, |
69 | { "ADC" , NULL, "VINR" }, |
70 | |
71 | { "DAC" , NULL, "PCM3008 Playback" }, |
72 | { "VOUTL" , NULL, "DAC" }, |
73 | { "VOUTR" , NULL, "DAC" }, |
74 | }; |
75 | |
76 | #define PCM3008_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ |
77 | SNDRV_PCM_RATE_48000) |
78 | |
79 | static struct snd_soc_dai_driver pcm3008_dai = { |
80 | .name = "pcm3008-hifi" , |
81 | .playback = { |
82 | .stream_name = "PCM3008 Playback" , |
83 | .channels_min = 1, |
84 | .channels_max = 2, |
85 | .rates = PCM3008_RATES, |
86 | .formats = SNDRV_PCM_FMTBIT_S16_LE, |
87 | }, |
88 | .capture = { |
89 | .stream_name = "PCM3008 Capture" , |
90 | .channels_min = 1, |
91 | .channels_max = 2, |
92 | .rates = PCM3008_RATES, |
93 | .formats = SNDRV_PCM_FMTBIT_S16_LE, |
94 | }, |
95 | }; |
96 | |
97 | static const struct snd_soc_component_driver soc_component_dev_pcm3008 = { |
98 | .dapm_widgets = pcm3008_dapm_widgets, |
99 | .num_dapm_widgets = ARRAY_SIZE(pcm3008_dapm_widgets), |
100 | .dapm_routes = pcm3008_dapm_routes, |
101 | .num_dapm_routes = ARRAY_SIZE(pcm3008_dapm_routes), |
102 | .idle_bias_on = 1, |
103 | .use_pmdown_time = 1, |
104 | .endianness = 1, |
105 | }; |
106 | |
107 | static int pcm3008_codec_probe(struct platform_device *pdev) |
108 | { |
109 | struct pcm3008_setup_data *setup = pdev->dev.platform_data; |
110 | int ret; |
111 | |
112 | if (!setup) |
113 | return -EINVAL; |
114 | |
115 | /* DEM1 DEM0 DE-EMPHASIS_MODE |
116 | * Low Low De-emphasis 44.1 kHz ON |
117 | * Low High De-emphasis OFF |
118 | * High Low De-emphasis 48 kHz ON |
119 | * High High De-emphasis 32 kHz ON |
120 | */ |
121 | |
122 | /* Configure DEM0 GPIO (turning OFF DAC De-emphasis). */ |
123 | ret = devm_gpio_request_one(dev: &pdev->dev, gpio: setup->dem0_pin, |
124 | GPIOF_OUT_INIT_HIGH, label: "codec_dem0" ); |
125 | if (ret != 0) |
126 | return ret; |
127 | |
128 | /* Configure DEM1 GPIO (turning OFF DAC De-emphasis). */ |
129 | ret = devm_gpio_request_one(dev: &pdev->dev, gpio: setup->dem1_pin, |
130 | GPIOF_OUT_INIT_LOW, label: "codec_dem1" ); |
131 | if (ret != 0) |
132 | return ret; |
133 | |
134 | /* Configure PDAD GPIO. */ |
135 | ret = devm_gpio_request_one(dev: &pdev->dev, gpio: setup->pdad_pin, |
136 | GPIOF_OUT_INIT_LOW, label: "codec_pdad" ); |
137 | if (ret != 0) |
138 | return ret; |
139 | |
140 | /* Configure PDDA GPIO. */ |
141 | ret = devm_gpio_request_one(dev: &pdev->dev, gpio: setup->pdda_pin, |
142 | GPIOF_OUT_INIT_LOW, label: "codec_pdda" ); |
143 | if (ret != 0) |
144 | return ret; |
145 | |
146 | return devm_snd_soc_register_component(dev: &pdev->dev, |
147 | component_driver: &soc_component_dev_pcm3008, dai_drv: &pcm3008_dai, num_dai: 1); |
148 | } |
149 | |
150 | MODULE_ALIAS("platform:pcm3008-codec" ); |
151 | |
152 | static struct platform_driver pcm3008_codec_driver = { |
153 | .probe = pcm3008_codec_probe, |
154 | .driver = { |
155 | .name = "pcm3008-codec" , |
156 | }, |
157 | }; |
158 | |
159 | module_platform_driver(pcm3008_codec_driver); |
160 | |
161 | MODULE_DESCRIPTION("Soc PCM3008 driver" ); |
162 | MODULE_AUTHOR("Hugo Villeneuve" ); |
163 | MODULE_LICENSE("GPL" ); |
164 | |