ALSA: hda - Use regmap for parameter caches, too
[pandora-kernel.git] / sound / hda / hdac_regmap.c
1 /*
2  * Regmap support for HD-audio verbs
3  *
4  * A virtual register is translated to one or more hda verbs for write,
5  * vice versa for read.
6  *
7  * A few limitations:
8  * - Provided for not all verbs but only subset standard non-volatile verbs.
9  * - For reading, only AC_VERB_GET_* variants can be used.
10  * - For writing, mapped to the *corresponding* AC_VERB_SET_* variants,
11  *   so can't handle asymmetric verbs for read and write
12  */
13
14 #include <linux/slab.h>
15 #include <linux/device.h>
16 #include <linux/regmap.h>
17 #include <linux/export.h>
18 #include <linux/pm.h>
19 #include <linux/pm_runtime.h>
20 #include <sound/core.h>
21 #include <sound/hdaudio.h>
22 #include <sound/hda_regmap.h>
23
24 #ifdef CONFIG_PM
25 #define codec_is_running(codec)                         \
26         (atomic_read(&(codec)->in_pm) ||                \
27          !pm_runtime_suspended(&(codec)->dev))
28 #else
29 #define codec_is_running(codec)         true
30 #endif
31
32 #define get_verb(reg)   (((reg) >> 8) & 0xfff)
33
34 static bool hda_volatile_reg(struct device *dev, unsigned int reg)
35 {
36         unsigned int verb = get_verb(reg);
37
38         switch (verb) {
39         case AC_VERB_GET_PROC_COEF:
40         case AC_VERB_GET_COEF_INDEX:
41         case AC_VERB_GET_PROC_STATE:
42         case AC_VERB_GET_POWER_STATE:
43         case AC_VERB_GET_PIN_SENSE:
44         case AC_VERB_GET_HDMI_DIP_SIZE:
45         case AC_VERB_GET_HDMI_ELDD:
46         case AC_VERB_GET_HDMI_DIP_INDEX:
47         case AC_VERB_GET_HDMI_DIP_DATA:
48         case AC_VERB_GET_HDMI_DIP_XMIT:
49         case AC_VERB_GET_HDMI_CP_CTRL:
50         case AC_VERB_GET_HDMI_CHAN_SLOT:
51         case AC_VERB_GET_DEVICE_SEL:
52         case AC_VERB_GET_DEVICE_LIST:   /* read-only volatile */
53                 return true;
54         }
55
56         return false;
57 }
58
59 static bool hda_writeable_reg(struct device *dev, unsigned int reg)
60 {
61         struct hdac_device *codec = dev_to_hdac_dev(dev);
62         unsigned int verb = get_verb(reg);
63
64         if (codec->caps_overwriting)
65                 return true;
66
67         switch (verb & 0xf00) {
68         case AC_VERB_GET_STREAM_FORMAT:
69         case AC_VERB_GET_AMP_GAIN_MUTE:
70                 return true;
71         case 0xf00:
72                 break;
73         default:
74                 return false;
75         }
76
77         switch (verb) {
78         case AC_VERB_GET_CONNECT_SEL:
79         case AC_VERB_GET_SDI_SELECT:
80         case AC_VERB_GET_CONV:
81         case AC_VERB_GET_PIN_WIDGET_CONTROL:
82         case AC_VERB_GET_UNSOLICITED_RESPONSE: /* only as SET_UNSOLICITED_ENABLE */
83         case AC_VERB_GET_BEEP_CONTROL:
84         case AC_VERB_GET_EAPD_BTLENABLE:
85         case AC_VERB_GET_DIGI_CONVERT_1:
86         case AC_VERB_GET_DIGI_CONVERT_2: /* only for beep control */
87         case AC_VERB_GET_VOLUME_KNOB_CONTROL:
88         case AC_VERB_GET_CONFIG_DEFAULT:
89         case AC_VERB_GET_GPIO_MASK:
90         case AC_VERB_GET_GPIO_DIRECTION:
91         case AC_VERB_GET_GPIO_DATA: /* not for volatile read */
92         case AC_VERB_GET_GPIO_WAKE_MASK:
93         case AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK:
94         case AC_VERB_GET_GPIO_STICKY_MASK:
95         case AC_VERB_GET_CVT_CHAN_COUNT:
96                 return true;
97         }
98
99         return false;
100 }
101
102 static bool hda_readable_reg(struct device *dev, unsigned int reg)
103 {
104         struct hdac_device *codec = dev_to_hdac_dev(dev);
105         unsigned int verb = get_verb(reg);
106
107         if (codec->caps_overwriting)
108                 return true;
109
110         switch (verb) {
111         case AC_VERB_PARAMETERS:
112         case AC_VERB_GET_CONNECT_LIST:
113         case AC_VERB_GET_SUBSYSTEM_ID:
114                 return true;
115         }
116
117         return hda_writeable_reg(dev, reg);
118 }
119
120 static int hda_reg_read(void *context, unsigned int reg, unsigned int *val)
121 {
122         struct hdac_device *codec = context;
123
124         if (!codec_is_running(codec))
125                 return -EAGAIN;
126         reg |= (codec->addr << 28);
127         return snd_hdac_exec_verb(codec, reg, 0, val);
128 }
129
130 static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
131 {
132         struct hdac_device *codec = context;
133         unsigned int verb;
134         int i, bytes, err;
135
136         if (!codec_is_running(codec))
137                 return codec->lazy_cache ? 0 : -EAGAIN;
138
139         reg &= ~0x00080000U; /* drop GET bit */
140         reg |= (codec->addr << 28);
141         verb = get_verb(reg);
142
143         switch (verb & 0xf00) {
144         case AC_VERB_SET_AMP_GAIN_MUTE:
145                 verb = AC_VERB_SET_AMP_GAIN_MUTE;
146                 if (reg & AC_AMP_GET_LEFT)
147                         verb |= AC_AMP_SET_LEFT >> 8;
148                 else
149                         verb |= AC_AMP_SET_RIGHT >> 8;
150                 if (reg & AC_AMP_GET_OUTPUT) {
151                         verb |= AC_AMP_SET_OUTPUT >> 8;
152                 } else {
153                         verb |= AC_AMP_SET_INPUT >> 8;
154                         verb |= reg & 0xf;
155                 }
156                 break;
157         }
158
159         switch (verb) {
160         case AC_VERB_SET_DIGI_CONVERT_1:
161                 bytes = 2;
162                 break;
163         case AC_VERB_SET_CONFIG_DEFAULT_BYTES_0:
164                 bytes = 4;
165                 break;
166         default:
167                 bytes = 1;
168                 break;
169         }
170
171         for (i = 0; i < bytes; i++) {
172                 reg &= ~0xfffff;
173                 reg |= (verb + i) << 8 | ((val >> (8 * i)) & 0xff);
174                 err = snd_hdac_exec_verb(codec, reg, 0, NULL);
175                 if (err < 0)
176                         return err;
177         }
178
179         return 0;
180 }
181
182 static const struct regmap_config hda_regmap_cfg = {
183         .name = "hdaudio",
184         .reg_bits = 32,
185         .val_bits = 32,
186         .max_register = 0xfffffff,
187         .writeable_reg = hda_writeable_reg,
188         .readable_reg = hda_readable_reg,
189         .volatile_reg = hda_volatile_reg,
190         .cache_type = REGCACHE_RBTREE,
191         .reg_read = hda_reg_read,
192         .reg_write = hda_reg_write,
193 };
194
195 int snd_hdac_regmap_init(struct hdac_device *codec)
196 {
197         struct regmap *regmap;
198
199         regmap = regmap_init(&codec->dev, NULL, codec, &hda_regmap_cfg);
200         if (IS_ERR(regmap))
201                 return PTR_ERR(regmap);
202         codec->regmap = regmap;
203         return 0;
204 }
205 EXPORT_SYMBOL_GPL(snd_hdac_regmap_init);
206
207 void snd_hdac_regmap_exit(struct hdac_device *codec)
208 {
209         if (codec->regmap) {
210                 regmap_exit(codec->regmap);
211                 codec->regmap = NULL;
212         }
213 }
214 EXPORT_SYMBOL_GPL(snd_hdac_regmap_exit);
215
216 /*
217  * helper functions
218  */
219
220 /* write a pseudo-register value (w/o power sequence) */
221 static int reg_raw_write(struct hdac_device *codec, unsigned int reg,
222                          unsigned int val)
223 {
224         if (!codec->regmap)
225                 return hda_reg_write(codec, reg, val);
226         else
227                 return regmap_write(codec->regmap, reg, val);
228 }
229
230 /**
231  * snd_hdac_regmap_write_raw - write a pseudo register with power mgmt
232  * @codec: the codec object
233  * @reg: pseudo register
234  * @val: value to write
235  *
236  * Returns zero if successful or a negative error code.
237  */
238 int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg,
239                               unsigned int val)
240 {
241         int err;
242
243         err = reg_raw_write(codec, reg, val);
244         if (err == -EAGAIN) {
245                 snd_hdac_power_up(codec);
246                 err = reg_raw_write(codec, reg, val);
247                 snd_hdac_power_down(codec);
248         }
249         return err;
250 }
251 EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw);
252
253 static int reg_raw_read(struct hdac_device *codec, unsigned int reg,
254                         unsigned int *val)
255 {
256         if (!codec->regmap)
257                 return hda_reg_read(codec, reg, val);
258         else
259                 return regmap_read(codec->regmap, reg, val);
260 }
261
262 /**
263  * snd_hdac_regmap_read_raw - read a pseudo register with power mgmt
264  * @codec: the codec object
265  * @reg: pseudo register
266  * @val: pointer to store the read value
267  *
268  * Returns zero if successful or a negative error code.
269  */
270 int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg,
271                              unsigned int *val)
272 {
273         int err;
274
275         err = reg_raw_read(codec, reg, val);
276         if (err == -EAGAIN) {
277                 snd_hdac_power_up(codec);
278                 err = reg_raw_read(codec, reg, val);
279                 snd_hdac_power_down(codec);
280         }
281         return err;
282 }
283 EXPORT_SYMBOL_GPL(snd_hdac_regmap_read_raw);
284
285 /**
286  * snd_hdac_regmap_update_raw - update a pseudo register with power mgmt
287  * @codec: the codec object
288  * @reg: pseudo register
289  * @mask: bit mask to udpate
290  * @val: value to update
291  *
292  * Returns zero if successful or a negative error code.
293  */
294 int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg,
295                                unsigned int mask, unsigned int val)
296 {
297         unsigned int orig;
298         int err;
299
300         val &= mask;
301         err = snd_hdac_regmap_read_raw(codec, reg, &orig);
302         if (err < 0)
303                 return err;
304         val |= orig & ~mask;
305         if (val == orig)
306                 return 0;
307         err = snd_hdac_regmap_write_raw(codec, reg, val);
308         if (err < 0)
309                 return err;
310         return 1;
311 }
312 EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw);