Merge branch 'test/hda-pincfg' into topic/hda
[linux-2.6-block.git] / sound / pci / hda / hda_hwdep.c
CommitLineData
2807314d
TI
1/*
2 * HWDEP Interface for HD-audio codec
3 *
4 * Copyright (c) 2007 Takashi Iwai <tiwai@suse.de>
5 *
6 * This driver is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This driver is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
2807314d
TI
21#include <linux/init.h>
22#include <linux/slab.h>
23#include <linux/pci.h>
24#include <linux/compat.h>
25#include <linux/mutex.h>
1e1be432 26#include <linux/ctype.h>
2807314d
TI
27#include <sound/core.h>
28#include "hda_codec.h"
29#include "hda_local.h"
30#include <sound/hda_hwdep.h>
d7ffba19 31#include <sound/minors.h>
2807314d
TI
32
33/*
34 * write/read an out-of-bound verb
35 */
36static int verb_write_ioctl(struct hda_codec *codec,
37 struct hda_verb_ioctl __user *arg)
38{
39 u32 verb, res;
40
41 if (get_user(verb, &arg->verb))
42 return -EFAULT;
43 res = snd_hda_codec_read(codec, verb >> 24, 0,
44 (verb >> 8) & 0xffff, verb & 0xff);
45 if (put_user(res, &arg->res))
46 return -EFAULT;
47 return 0;
48}
49
50static int get_wcap_ioctl(struct hda_codec *codec,
51 struct hda_verb_ioctl __user *arg)
52{
53 u32 verb, res;
54
55 if (get_user(verb, &arg->verb))
56 return -EFAULT;
57 res = get_wcaps(codec, verb >> 24);
58 if (put_user(res, &arg->res))
59 return -EFAULT;
60 return 0;
61}
62
63
64/*
65 */
66static int hda_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
67 unsigned int cmd, unsigned long arg)
68{
69 struct hda_codec *codec = hw->private_data;
70 void __user *argp = (void __user *)arg;
71
72 switch (cmd) {
73 case HDA_IOCTL_PVERSION:
74 return put_user(HDA_HWDEP_VERSION, (int __user *)argp);
75 case HDA_IOCTL_VERB_WRITE:
76 return verb_write_ioctl(codec, argp);
77 case HDA_IOCTL_GET_WCAP:
78 return get_wcap_ioctl(codec, argp);
79 }
80 return -ENOIOCTLCMD;
81}
82
83#ifdef CONFIG_COMPAT
84static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
85 unsigned int cmd, unsigned long arg)
86{
312d045c 87 return hda_hwdep_ioctl(hw, file, cmd, (unsigned long)compat_ptr(arg));
2807314d
TI
88}
89#endif
90
91static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
92{
62cf872a 93#ifndef CONFIG_SND_DEBUG_VERBOSE
2807314d
TI
94 if (!capable(CAP_SYS_RAWIO))
95 return -EACCES;
96#endif
97 return 0;
98}
99
11aeff08
TI
100static void clear_hwdep_elements(struct hda_codec *codec)
101{
1e1be432
TI
102 char **head;
103 int i;
104
11aeff08
TI
105 /* clear init verbs */
106 snd_array_free(&codec->init_verbs);
1e1be432
TI
107 /* clear hints */
108 head = codec->hints.list;
109 for (i = 0; i < codec->hints.used; i++, head++)
110 kfree(*head);
111 snd_array_free(&codec->hints);
346ff70f 112 snd_array_free(&codec->user_pins);
11aeff08
TI
113}
114
115static void hwdep_free(struct snd_hwdep *hwdep)
116{
117 clear_hwdep_elements(hwdep->private_data);
118}
119
1289e9e8 120int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
2807314d
TI
121{
122 char hwname[16];
123 struct snd_hwdep *hwdep;
124 int err;
125
126 sprintf(hwname, "HDA Codec %d", codec->addr);
127 err = snd_hwdep_new(codec->bus->card, hwname, codec->addr, &hwdep);
128 if (err < 0)
129 return err;
130 codec->hwdep = hwdep;
131 sprintf(hwdep->name, "HDA Codec %d", codec->addr);
132 hwdep->iface = SNDRV_HWDEP_IFACE_HDA;
133 hwdep->private_data = codec;
11aeff08 134 hwdep->private_free = hwdep_free;
2807314d
TI
135 hwdep->exclusive = 1;
136
137 hwdep->ops.open = hda_hwdep_open;
138 hwdep->ops.ioctl = hda_hwdep_ioctl;
139#ifdef CONFIG_COMPAT
140 hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
141#endif
142
11aeff08 143 snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
1e1be432 144 snd_array_init(&codec->hints, sizeof(char *), 32);
346ff70f 145 snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16);
11aeff08 146
2807314d
TI
147 return 0;
148}
d7ffba19 149
e7ee058c
TI
150#ifdef CONFIG_SND_HDA_RECONFIG
151
d7ffba19
TI
152/*
153 * sysfs interface
154 */
155
156static int clear_codec(struct hda_codec *codec)
157{
158 snd_hda_codec_reset(codec);
11aeff08 159 clear_hwdep_elements(codec);
d7ffba19
TI
160 return 0;
161}
162
163static int reconfig_codec(struct hda_codec *codec)
164{
165 int err;
166
167 snd_printk(KERN_INFO "hda-codec: reconfiguring\n");
168 snd_hda_codec_reset(codec);
169 err = snd_hda_codec_configure(codec);
170 if (err < 0)
171 return err;
172 /* rebuild PCMs */
529bd6c4 173 err = snd_hda_codec_build_pcms(codec);
d7ffba19
TI
174 if (err < 0)
175 return err;
176 /* rebuild mixers */
177 err = snd_hda_codec_build_controls(codec);
178 if (err < 0)
179 return err;
26a74f1f 180 return snd_card_register(codec->bus->card);
d7ffba19
TI
181}
182
183/*
184 * allocate a string at most len chars, and remove the trailing EOL
185 */
186static char *kstrndup_noeol(const char *src, size_t len)
187{
188 char *s = kstrndup(src, len, GFP_KERNEL);
189 char *p;
190 if (!s)
191 return NULL;
192 p = strchr(s, '\n');
193 if (p)
194 *p = 0;
195 return s;
196}
197
198#define CODEC_INFO_SHOW(type) \
199static ssize_t type##_show(struct device *dev, \
200 struct device_attribute *attr, \
201 char *buf) \
202{ \
203 struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
204 struct hda_codec *codec = hwdep->private_data; \
205 return sprintf(buf, "0x%x\n", codec->type); \
206}
207
208#define CODEC_INFO_STR_SHOW(type) \
209static ssize_t type##_show(struct device *dev, \
210 struct device_attribute *attr, \
211 char *buf) \
212{ \
213 struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
214 struct hda_codec *codec = hwdep->private_data; \
215 return sprintf(buf, "%s\n", \
216 codec->type ? codec->type : ""); \
217}
218
219CODEC_INFO_SHOW(vendor_id);
220CODEC_INFO_SHOW(subsystem_id);
221CODEC_INFO_SHOW(revision_id);
222CODEC_INFO_SHOW(afg);
223CODEC_INFO_SHOW(mfg);
224CODEC_INFO_STR_SHOW(name);
225CODEC_INFO_STR_SHOW(modelname);
226
227#define CODEC_INFO_STORE(type) \
228static ssize_t type##_store(struct device *dev, \
229 struct device_attribute *attr, \
230 const char *buf, size_t count) \
231{ \
232 struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
233 struct hda_codec *codec = hwdep->private_data; \
234 char *after; \
235 codec->type = simple_strtoul(buf, &after, 0); \
236 return count; \
237}
238
239#define CODEC_INFO_STR_STORE(type) \
240static ssize_t type##_store(struct device *dev, \
241 struct device_attribute *attr, \
242 const char *buf, size_t count) \
243{ \
244 struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
245 struct hda_codec *codec = hwdep->private_data; \
246 char *s = kstrndup_noeol(buf, 64); \
247 if (!s) \
248 return -ENOMEM; \
249 kfree(codec->type); \
250 codec->type = s; \
251 return count; \
252}
253
254CODEC_INFO_STORE(vendor_id);
255CODEC_INFO_STORE(subsystem_id);
256CODEC_INFO_STORE(revision_id);
257CODEC_INFO_STR_STORE(name);
258CODEC_INFO_STR_STORE(modelname);
259
260#define CODEC_ACTION_STORE(type) \
261static ssize_t type##_store(struct device *dev, \
262 struct device_attribute *attr, \
263 const char *buf, size_t count) \
264{ \
265 struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
266 struct hda_codec *codec = hwdep->private_data; \
267 int err = 0; \
268 if (*buf) \
269 err = type##_codec(codec); \
270 return err < 0 ? err : count; \
271}
272
273CODEC_ACTION_STORE(reconfig);
274CODEC_ACTION_STORE(clear);
275
11aeff08
TI
276static ssize_t init_verbs_store(struct device *dev,
277 struct device_attribute *attr,
278 const char *buf, size_t count)
279{
280 struct snd_hwdep *hwdep = dev_get_drvdata(dev);
281 struct hda_codec *codec = hwdep->private_data;
55290e19
TI
282 struct hda_verb *v;
283 int nid, verb, param;
11aeff08 284
55290e19
TI
285 if (sscanf(buf, "%i %i %i", &nid, &verb, &param) != 3)
286 return -EINVAL;
287 if (!nid || !verb)
11aeff08
TI
288 return -EINVAL;
289 v = snd_array_new(&codec->init_verbs);
290 if (!v)
291 return -ENOMEM;
55290e19
TI
292 v->nid = nid;
293 v->verb = verb;
294 v->param = param;
11aeff08
TI
295 return count;
296}
297
1e1be432
TI
298static ssize_t hints_store(struct device *dev,
299 struct device_attribute *attr,
300 const char *buf, size_t count)
301{
302 struct snd_hwdep *hwdep = dev_get_drvdata(dev);
303 struct hda_codec *codec = hwdep->private_data;
304 char *p;
305 char **hint;
306
307 if (!*buf || isspace(*buf) || *buf == '#' || *buf == '\n')
308 return count;
309 p = kstrndup_noeol(buf, 1024);
310 if (!p)
311 return -ENOMEM;
312 hint = snd_array_new(&codec->hints);
313 if (!hint) {
314 kfree(p);
315 return -ENOMEM;
316 }
317 *hint = p;
318 return count;
319}
320
3be14149
TI
321static ssize_t pin_configs_show(struct hda_codec *codec,
322 struct snd_array *list,
323 char *buf)
324{
325 int i, len = 0;
326 for (i = 0; i < list->used; i++) {
327 struct hda_pincfg *pin = snd_array_elem(list, i);
328 len += sprintf(buf + len, "0x%02x 0x%08x\n",
329 pin->nid, pin->cfg);
330 }
331 return len;
332}
333
334static ssize_t init_pin_configs_show(struct device *dev,
335 struct device_attribute *attr,
336 char *buf)
337{
338 struct snd_hwdep *hwdep = dev_get_drvdata(dev);
339 struct hda_codec *codec = hwdep->private_data;
340 return pin_configs_show(codec, &codec->init_pins, buf);
341}
342
346ff70f
TI
343static ssize_t user_pin_configs_show(struct device *dev,
344 struct device_attribute *attr,
345 char *buf)
3be14149
TI
346{
347 struct snd_hwdep *hwdep = dev_get_drvdata(dev);
348 struct hda_codec *codec = hwdep->private_data;
346ff70f 349 return pin_configs_show(codec, &codec->user_pins, buf);
3be14149
TI
350}
351
346ff70f
TI
352static ssize_t driver_pin_configs_show(struct device *dev,
353 struct device_attribute *attr,
354 char *buf)
3be14149
TI
355{
356 struct snd_hwdep *hwdep = dev_get_drvdata(dev);
357 struct hda_codec *codec = hwdep->private_data;
346ff70f 358 return pin_configs_show(codec, &codec->driver_pins, buf);
3be14149
TI
359}
360
361#define MAX_PIN_CONFIGS 32
362
346ff70f
TI
363static ssize_t user_pin_configs_store(struct device *dev,
364 struct device_attribute *attr,
365 const char *buf, size_t count)
3be14149
TI
366{
367 struct snd_hwdep *hwdep = dev_get_drvdata(dev);
368 struct hda_codec *codec = hwdep->private_data;
369 int nid, cfg;
370 int err;
371
372 if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
373 return -EINVAL;
374 if (!nid)
375 return -EINVAL;
346ff70f 376 err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
3be14149
TI
377 if (err < 0)
378 return err;
379 return count;
380}
381
d7ffba19
TI
382#define CODEC_ATTR_RW(type) \
383 __ATTR(type, 0644, type##_show, type##_store)
384#define CODEC_ATTR_RO(type) \
385 __ATTR_RO(type)
386#define CODEC_ATTR_WO(type) \
387 __ATTR(type, 0200, NULL, type##_store)
388
389static struct device_attribute codec_attrs[] = {
390 CODEC_ATTR_RW(vendor_id),
391 CODEC_ATTR_RW(subsystem_id),
392 CODEC_ATTR_RW(revision_id),
393 CODEC_ATTR_RO(afg),
394 CODEC_ATTR_RO(mfg),
395 CODEC_ATTR_RW(name),
396 CODEC_ATTR_RW(modelname),
11aeff08 397 CODEC_ATTR_WO(init_verbs),
1e1be432 398 CODEC_ATTR_WO(hints),
3be14149 399 CODEC_ATTR_RO(init_pin_configs),
346ff70f
TI
400 CODEC_ATTR_RW(user_pin_configs),
401 CODEC_ATTR_RO(driver_pin_configs),
d7ffba19
TI
402 CODEC_ATTR_WO(reconfig),
403 CODEC_ATTR_WO(clear),
404};
405
406/*
407 * create sysfs files on hwdep directory
408 */
409int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
410{
411 struct snd_hwdep *hwdep = codec->hwdep;
412 int i;
413
414 for (i = 0; i < ARRAY_SIZE(codec_attrs); i++)
415 snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card,
416 hwdep->device, &codec_attrs[i]);
417 return 0;
418}
e7ee058c
TI
419
420#endif /* CONFIG_SND_HDA_RECONFIG */