1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * ac97.c -- ALSA Soc AC97 codec support |
4 | * |
5 | * Copyright 2005 Wolfson Microelectronics PLC. |
6 | * Author: Liam Girdwood <lrg@slimlogic.co.uk> |
7 | * |
8 | * Generic AC97 support. |
9 | */ |
10 | |
11 | #include <linux/init.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/device.h> |
15 | #include <linux/module.h> |
16 | #include <linux/of.h> |
17 | #include <sound/core.h> |
18 | #include <sound/pcm.h> |
19 | #include <sound/ac97_codec.h> |
20 | #include <sound/initval.h> |
21 | #include <sound/soc.h> |
22 | |
23 | static const struct snd_soc_dapm_widget ac97_widgets[] = { |
24 | SND_SOC_DAPM_INPUT("RX" ), |
25 | SND_SOC_DAPM_OUTPUT("TX" ), |
26 | }; |
27 | |
28 | static const struct snd_soc_dapm_route ac97_routes[] = { |
29 | { "AC97 Capture" , NULL, "RX" }, |
30 | { "TX" , NULL, "AC97 Playback" }, |
31 | }; |
32 | |
33 | static int ac97_prepare(struct snd_pcm_substream *substream, |
34 | struct snd_soc_dai *dai) |
35 | { |
36 | struct snd_soc_component *component = dai->component; |
37 | struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(c: component); |
38 | |
39 | int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? |
40 | AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE; |
41 | return snd_ac97_set_rate(ac97, reg, rate: substream->runtime->rate); |
42 | } |
43 | |
44 | static const struct snd_soc_dai_ops ac97_dai_ops = { |
45 | .prepare = ac97_prepare, |
46 | }; |
47 | |
48 | static struct snd_soc_dai_driver ac97_dai = { |
49 | .name = "ac97-hifi" , |
50 | .playback = { |
51 | .stream_name = "AC97 Playback" , |
52 | .channels_min = 1, |
53 | .channels_max = 2, |
54 | .rates = SNDRV_PCM_RATE_KNOT, |
55 | .formats = SND_SOC_STD_AC97_FMTS,}, |
56 | .capture = { |
57 | .stream_name = "AC97 Capture" , |
58 | .channels_min = 1, |
59 | .channels_max = 2, |
60 | .rates = SNDRV_PCM_RATE_KNOT, |
61 | .formats = SND_SOC_STD_AC97_FMTS,}, |
62 | .ops = &ac97_dai_ops, |
63 | }; |
64 | |
65 | static int ac97_soc_probe(struct snd_soc_component *component) |
66 | { |
67 | struct snd_ac97 *ac97; |
68 | struct snd_ac97_bus *ac97_bus; |
69 | struct snd_ac97_template ac97_template; |
70 | int ret; |
71 | |
72 | /* add codec as bus device for standard ac97 */ |
73 | ret = snd_ac97_bus(card: component->card->snd_card, num: 0, ops: soc_ac97_ops, |
74 | NULL, rbus: &ac97_bus); |
75 | if (ret < 0) |
76 | return ret; |
77 | |
78 | memset(&ac97_template, 0, sizeof(struct snd_ac97_template)); |
79 | ret = snd_ac97_mixer(bus: ac97_bus, template: &ac97_template, rac97: &ac97); |
80 | if (ret < 0) |
81 | return ret; |
82 | |
83 | snd_soc_component_set_drvdata(c: component, data: ac97); |
84 | |
85 | return 0; |
86 | } |
87 | |
88 | #ifdef CONFIG_PM |
89 | static int ac97_soc_suspend(struct snd_soc_component *component) |
90 | { |
91 | struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(c: component); |
92 | |
93 | snd_ac97_suspend(ac97); |
94 | |
95 | return 0; |
96 | } |
97 | |
98 | static int ac97_soc_resume(struct snd_soc_component *component) |
99 | { |
100 | |
101 | struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(c: component); |
102 | |
103 | snd_ac97_resume(ac97); |
104 | |
105 | return 0; |
106 | } |
107 | #else |
108 | #define ac97_soc_suspend NULL |
109 | #define ac97_soc_resume NULL |
110 | #endif |
111 | |
112 | static const struct snd_soc_component_driver soc_component_dev_ac97 = { |
113 | .probe = ac97_soc_probe, |
114 | .suspend = ac97_soc_suspend, |
115 | .resume = ac97_soc_resume, |
116 | .dapm_widgets = ac97_widgets, |
117 | .num_dapm_widgets = ARRAY_SIZE(ac97_widgets), |
118 | .dapm_routes = ac97_routes, |
119 | .num_dapm_routes = ARRAY_SIZE(ac97_routes), |
120 | .idle_bias_on = 1, |
121 | .use_pmdown_time = 1, |
122 | .endianness = 1, |
123 | }; |
124 | |
125 | static int ac97_probe(struct platform_device *pdev) |
126 | { |
127 | return devm_snd_soc_register_component(dev: &pdev->dev, |
128 | component_driver: &soc_component_dev_ac97, dai_drv: &ac97_dai, num_dai: 1); |
129 | } |
130 | |
131 | #ifdef CONFIG_OF |
132 | static const struct of_device_id ac97_codec_of_match[] = { |
133 | { .compatible = "realtek,alc203" , }, |
134 | { } |
135 | }; |
136 | MODULE_DEVICE_TABLE(of, ac97_codec_of_match); |
137 | #endif |
138 | |
139 | static struct platform_driver ac97_codec_driver = { |
140 | .driver = { |
141 | .name = "ac97-codec" , |
142 | .of_match_table = of_match_ptr(ac97_codec_of_match), |
143 | }, |
144 | |
145 | .probe = ac97_probe, |
146 | }; |
147 | |
148 | module_platform_driver(ac97_codec_driver); |
149 | |
150 | MODULE_DESCRIPTION("Soc Generic AC97 driver" ); |
151 | MODULE_AUTHOR("Liam Girdwood" ); |
152 | MODULE_LICENSE("GPL" ); |
153 | MODULE_ALIAS("platform:ac97-codec" ); |
154 | |