1f5e57a4bb7accdfdad3eefdd209b9b056b7351c
[linux-2.6.git] / sound / soc / codecs / ac97.c
1 /*
2  * ac97.c  --  ALSA Soc AC97 codec support
3  *
4  * Copyright 2005 Wolfson Microelectronics PLC.
5  * Author: Liam Girdwood <lrg@slimlogic.co.uk>
6  *
7  *  This program is free software; you can redistribute  it and/or modify it
8  *  under  the terms of  the GNU General  Public License as published by the
9  *  Free Software Foundation;  either version 2 of the  License, or (at your
10  *  option) any later version.
11  *
12  * Generic AC97 support.
13  */
14
15 #include <linux/init.h>
16 #include <linux/slab.h>
17 #include <linux/kernel.h>
18 #include <linux/device.h>
19 #include <sound/core.h>
20 #include <sound/pcm.h>
21 #include <sound/ac97_codec.h>
22 #include <sound/initval.h>
23 #include <sound/soc.h>
24 #include "ac97.h"
25
26 #define AC97_VERSION "0.6"
27
28 static int ac97_prepare(struct snd_pcm_substream *substream,
29                         struct snd_soc_dai *dai)
30 {
31         struct snd_pcm_runtime *runtime = substream->runtime;
32         struct snd_soc_pcm_runtime *rtd = substream->private_data;
33         struct snd_soc_device *socdev = rtd->socdev;
34         struct snd_soc_codec *codec = socdev->card->codec;
35
36         int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
37                   AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
38         return snd_ac97_set_rate(codec->ac97, reg, runtime->rate);
39 }
40
41 #define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
42                 SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
43                 SNDRV_PCM_RATE_48000)
44
45 static struct snd_soc_dai_ops ac97_dai_ops = {
46         .prepare        = ac97_prepare,
47 };
48
49 struct snd_soc_dai ac97_dai = {
50         .name = "AC97 HiFi",
51         .ac97_control = 1,
52         .playback = {
53                 .stream_name = "AC97 Playback",
54                 .channels_min = 1,
55                 .channels_max = 2,
56                 .rates = STD_AC97_RATES,
57                 .formats = SND_SOC_STD_AC97_FMTS,},
58         .capture = {
59                 .stream_name = "AC97 Capture",
60                 .channels_min = 1,
61                 .channels_max = 2,
62                 .rates = STD_AC97_RATES,
63                 .formats = SND_SOC_STD_AC97_FMTS,},
64         .ops = &ac97_dai_ops,
65 };
66 EXPORT_SYMBOL_GPL(ac97_dai);
67
68 static unsigned int ac97_read(struct snd_soc_codec *codec,
69         unsigned int reg)
70 {
71         return soc_ac97_ops.read(codec->ac97, reg);
72 }
73
74 static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
75         unsigned int val)
76 {
77         soc_ac97_ops.write(codec->ac97, reg, val);
78         return 0;
79 }
80
81 static int ac97_soc_probe(struct platform_device *pdev)
82 {
83         struct snd_soc_device *socdev = platform_get_drvdata(pdev);
84         struct snd_soc_card *card = socdev->card;
85         struct snd_soc_codec *codec;
86         struct snd_ac97_bus *ac97_bus;
87         struct snd_ac97_template ac97_template;
88         int i;
89         int ret = 0;
90
91         printk(KERN_INFO "AC97 SoC Audio Codec %s\n", AC97_VERSION);
92
93         socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
94         if (!socdev->card->codec)
95                 return -ENOMEM;
96         codec = socdev->card->codec;
97         mutex_init(&codec->mutex);
98
99         codec->name = "AC97";
100         codec->owner = THIS_MODULE;
101         codec->dai = &ac97_dai;
102         codec->num_dai = 1;
103         codec->write = ac97_write;
104         codec->read = ac97_read;
105         INIT_LIST_HEAD(&codec->dapm_widgets);
106         INIT_LIST_HEAD(&codec->dapm_paths);
107
108         /* register pcms */
109         ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
110         if (ret < 0)
111                 goto err;
112
113         /* add codec as bus device for standard ac97 */
114         ret = snd_ac97_bus(codec->card, 0, &soc_ac97_ops, NULL, &ac97_bus);
115         if (ret < 0)
116                 goto bus_err;
117
118         memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
119         ret = snd_ac97_mixer(ac97_bus, &ac97_template, &codec->ac97);
120         if (ret < 0)
121                 goto bus_err;
122
123         for (i = 0; i < card->num_links; i++) {
124                 if (card->dai_link[i].codec_dai->ac97_control) {
125                         snd_ac97_dev_add_pdata(codec->ac97,
126                                 card->dai_link[i].cpu_dai->ac97_pdata);
127                 }
128         }
129
130         return 0;
131
132 bus_err:
133         snd_soc_free_pcms(socdev);
134
135 err:
136         kfree(socdev->card->codec);
137         socdev->card->codec = NULL;
138         return ret;
139 }
140
141 static int ac97_soc_remove(struct platform_device *pdev)
142 {
143         struct snd_soc_device *socdev = platform_get_drvdata(pdev);
144         struct snd_soc_codec *codec = socdev->card->codec;
145
146         if (!codec)
147                 return 0;
148
149         snd_soc_free_pcms(socdev);
150         kfree(socdev->card->codec);
151
152         return 0;
153 }
154
155 #ifdef CONFIG_PM
156 static int ac97_soc_suspend(struct platform_device *pdev, pm_message_t msg)
157 {
158         struct snd_soc_device *socdev = platform_get_drvdata(pdev);
159
160         snd_ac97_suspend(socdev->card->codec->ac97);
161
162         return 0;
163 }
164
165 static int ac97_soc_resume(struct platform_device *pdev)
166 {
167         struct snd_soc_device *socdev = platform_get_drvdata(pdev);
168
169         snd_ac97_resume(socdev->card->codec->ac97);
170
171         return 0;
172 }
173 #else
174 #define ac97_soc_suspend NULL
175 #define ac97_soc_resume NULL
176 #endif
177
178 struct snd_soc_codec_device soc_codec_dev_ac97 = {
179         .probe =        ac97_soc_probe,
180         .remove =       ac97_soc_remove,
181         .suspend =      ac97_soc_suspend,
182         .resume =       ac97_soc_resume,
183 };
184 EXPORT_SYMBOL_GPL(soc_codec_dev_ac97);
185
186 MODULE_DESCRIPTION("Soc Generic AC97 driver");
187 MODULE_AUTHOR("Liam Girdwood");
188 MODULE_LICENSE("GPL");