ALSA: hda - Use regmap for amp accesses
[deliverable/linux.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{
61 unsigned int verb = get_verb(reg);
62
63 switch (verb & 0xf00) {
64 case AC_VERB_GET_STREAM_FORMAT:
65 case AC_VERB_GET_AMP_GAIN_MUTE:
66 return true;
67 case 0xf00:
68 break;
69 default:
70 return false;
71 }
72
73 switch (verb) {
74 case AC_VERB_GET_CONNECT_SEL:
75 case AC_VERB_GET_SDI_SELECT:
76 case AC_VERB_GET_CONV:
77 case AC_VERB_GET_PIN_WIDGET_CONTROL:
78 case AC_VERB_GET_UNSOLICITED_RESPONSE: /* only as SET_UNSOLICITED_ENABLE */
79 case AC_VERB_GET_BEEP_CONTROL:
80 case AC_VERB_GET_EAPD_BTLENABLE:
81 case AC_VERB_GET_DIGI_CONVERT_1:
82 case AC_VERB_GET_DIGI_CONVERT_2: /* only for beep control */
83 case AC_VERB_GET_VOLUME_KNOB_CONTROL:
84 case AC_VERB_GET_CONFIG_DEFAULT:
85 case AC_VERB_GET_GPIO_MASK:
86 case AC_VERB_GET_GPIO_DIRECTION:
87 case AC_VERB_GET_GPIO_DATA: /* not for volatile read */
88 case AC_VERB_GET_GPIO_WAKE_MASK:
89 case AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK:
90 case AC_VERB_GET_GPIO_STICKY_MASK:
91 case AC_VERB_GET_CVT_CHAN_COUNT:
92 return true;
93 }
94
95 return false;
96}
97
98static bool hda_readable_reg(struct device *dev, unsigned int reg)
99{
100 unsigned int verb = get_verb(reg);
101
102 switch (verb) {
103 case AC_VERB_PARAMETERS:
104 case AC_VERB_GET_CONNECT_LIST:
105 case AC_VERB_GET_SUBSYSTEM_ID:
106 return true;
107 }
108
109 return hda_writeable_reg(dev, reg);
110}
111
112static int hda_reg_read(void *context, unsigned int reg, unsigned int *val)
113{
114 struct hdac_device *codec = context;
115
116 if (!codec_is_running(codec))
117 return -EAGAIN;
118 reg |= (codec->addr << 28);
119 return snd_hdac_exec_verb(codec, reg, 0, val);
120}
121
122static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
123{
124 struct hdac_device *codec = context;
125 unsigned int verb;
126 int i, bytes, err;
127
128 if (!codec_is_running(codec))
129 return codec->lazy_cache ? 0 : -EAGAIN;
130
131 reg &= ~0x00080000U; /* drop GET bit */
132 reg |= (codec->addr << 28);
133 verb = get_verb(reg);
134
135 switch (verb & 0xf00) {
136 case AC_VERB_SET_AMP_GAIN_MUTE:
137 verb = AC_VERB_SET_AMP_GAIN_MUTE;
138 if (reg & AC_AMP_GET_LEFT)
139 verb |= AC_AMP_SET_LEFT >> 8;
140 else
141 verb |= AC_AMP_SET_RIGHT >> 8;
142 if (reg & AC_AMP_GET_OUTPUT) {
143 verb |= AC_AMP_SET_OUTPUT >> 8;
144 } else {
145 verb |= AC_AMP_SET_INPUT >> 8;
146 verb |= reg & 0xf;
147 }
148 break;
149 }
150
151 switch (verb) {
152 case AC_VERB_SET_DIGI_CONVERT_1:
153 bytes = 2;
154 break;
155 case AC_VERB_SET_CONFIG_DEFAULT_BYTES_0:
156 bytes = 4;
157 break;
158 default:
159 bytes = 1;
160 break;
161 }
162
163 for (i = 0; i < bytes; i++) {
164 reg &= ~0xfffff;
165 reg |= (verb + i) << 8 | ((val >> (8 * i)) & 0xff);
166 err = snd_hdac_exec_verb(codec, reg, 0, NULL);
167 if (err < 0)
168 return err;
169 }
170
171 return 0;
172}
173
174static const struct regmap_config hda_regmap_cfg = {
175 .name = "hdaudio",
176 .reg_bits = 32,
177 .val_bits = 32,
178 .max_register = 0xfffffff,
179 .writeable_reg = hda_writeable_reg,
180 .readable_reg = hda_readable_reg,
181 .volatile_reg = hda_volatile_reg,
182 .cache_type = REGCACHE_RBTREE,
183 .reg_read = hda_reg_read,
184 .reg_write = hda_reg_write,
185};
186
187int snd_hdac_regmap_init(struct hdac_device *codec)
188{
189 struct regmap *regmap;
190
191 regmap = regmap_init(&codec->dev, NULL, codec, &hda_regmap_cfg);
192 if (IS_ERR(regmap))
193 return PTR_ERR(regmap);
194 codec->regmap = regmap;
195 return 0;
196}
197EXPORT_SYMBOL_GPL(snd_hdac_regmap_init);
198
199void snd_hdac_regmap_exit(struct hdac_device *codec)
200{
201 if (codec->regmap) {
202 regmap_exit(codec->regmap);
203 codec->regmap = NULL;
204 }
205}
206EXPORT_SYMBOL_GPL(snd_hdac_regmap_exit);
207
208/*
209 * helper functions
210 */
211
212/* write a pseudo-register value (w/o power sequence) */
213static int reg_raw_write(struct hdac_device *codec, unsigned int reg,
214 unsigned int val)
215{
216 if (!codec->regmap)
217 return hda_reg_write(codec, reg, val);
218 else
219 return regmap_write(codec->regmap, reg, val);
220}
221
222/**
223 * snd_hdac_regmap_write_raw - write a pseudo register with power mgmt
224 * @codec: the codec object
225 * @reg: pseudo register
226 * @val: value to write
227 *
228 * Returns zero if successful or a negative error code.
229 */
230int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg,
231 unsigned int val)
232{
233 int err;
234
235 err = reg_raw_write(codec, reg, val);
236 if (err == -EAGAIN) {
237 snd_hdac_power_up(codec);
238 err = reg_raw_write(codec, reg, val);
239 snd_hdac_power_down(codec);
240 }
241 return err;
242}
243EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw);
244
245static int reg_raw_read(struct hdac_device *codec, unsigned int reg,
246 unsigned int *val)
247{
248 if (!codec->regmap)
249 return hda_reg_read(codec, reg, val);
250 else
251 return regmap_read(codec->regmap, reg, val);
252}
253
254/**
255 * snd_hdac_regmap_read_raw - read a pseudo register with power mgmt
256 * @codec: the codec object
257 * @reg: pseudo register
258 * @val: pointer to store the read value
259 *
260 * Returns zero if successful or a negative error code.
261 */
262int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg,
263 unsigned int *val)
264{
265 int err;
266
267 err = reg_raw_read(codec, reg, val);
268 if (err == -EAGAIN) {
269 snd_hdac_power_up(codec);
270 err = reg_raw_read(codec, reg, val);
271 snd_hdac_power_down(codec);
272 }
273 return err;
274}
275EXPORT_SYMBOL_GPL(snd_hdac_regmap_read_raw);
276
277/**
278 * snd_hdac_regmap_update_raw - update a pseudo register with power mgmt
279 * @codec: the codec object
280 * @reg: pseudo register
281 * @mask: bit mask to udpate
282 * @val: value to update
283 *
284 * Returns zero if successful or a negative error code.
285 */
286int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg,
287 unsigned int mask, unsigned int val)
288{
289 unsigned int orig;
290 int err;
291
292 val &= mask;
293 err = snd_hdac_regmap_read_raw(codec, reg, &orig);
294 if (err < 0)
295 return err;
296 val |= orig & ~mask;
297 if (val == orig)
298 return 0;
299 err = snd_hdac_regmap_write_raw(codec, reg, val);
300 if (err < 0)
301 return err;
302 return 1;
303}
304EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw);
This page took 0.034268 seconds and 5 git commands to generate.