ALSA: hda - Use regmap for parameter caches, too
[linux-2.6-block.git] / sound / hda / hdac_regmap.c
CommitLineData
4d75faa0
TI
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
34static 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
59static bool hda_writeable_reg(struct device *dev, unsigned int reg)
60{
faa75f8a 61 struct hdac_device *codec = dev_to_hdac_dev(dev);
4d75faa0
TI
62 unsigned int verb = get_verb(reg);
63
faa75f8a
TI
64 if (codec->caps_overwriting)
65 return true;
66
4d75faa0
TI
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
102static bool hda_readable_reg(struct device *dev, unsigned int reg)
103{
faa75f8a 104 struct hdac_device *codec = dev_to_hdac_dev(dev);
4d75faa0
TI
105 unsigned int verb = get_verb(reg);
106
faa75f8a
TI
107 if (codec->caps_overwriting)
108 return true;
109
4d75faa0
TI
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
120static 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
130static 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
182static 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
195int 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}
205EXPORT_SYMBOL_GPL(snd_hdac_regmap_init);
206
207void snd_hdac_regmap_exit(struct hdac_device *codec)
208{
209 if (codec->regmap) {
210 regmap_exit(codec->regmap);
211 codec->regmap = NULL;
212 }
213}
214EXPORT_SYMBOL_GPL(snd_hdac_regmap_exit);
215
216/*
217 * helper functions
218 */
219
220/* write a pseudo-register value (w/o power sequence) */
221static 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 */
238int 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}
251EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw);
252
253static 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 */
270int 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}
283EXPORT_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 */
294int 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}
312EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw);