blob: 71039a6dec28ffd9b20e0a08c5d2704bec77e397 [file] [log] [blame]
Takashi Iwai28073142007-07-27 18:58:06 +02001/*
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
Takashi Iwai28073142007-07-27 18:58:06 +020021#include <linux/init.h>
22#include <linux/slab.h>
23#include <linux/pci.h>
24#include <linux/compat.h>
25#include <linux/mutex.h>
Takashi Iwai1e1be432008-07-30 15:01:46 +020026#include <linux/ctype.h>
Takashi Iwai28073142007-07-27 18:58:06 +020027#include <sound/core.h>
28#include "hda_codec.h"
29#include "hda_local.h"
30#include <sound/hda_hwdep.h>
Takashi Iwaid7ffba12008-07-30 15:01:46 +020031#include <sound/minors.h>
Takashi Iwai28073142007-07-27 18:58:06 +020032
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{
Takashi Iwai312d0452007-07-31 11:08:10 +020087 return hda_hwdep_ioctl(hw, file, cmd, (unsigned long)compat_ptr(arg));
Takashi Iwai28073142007-07-27 18:58:06 +020088}
89#endif
90
91static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
92{
Takashi Iwai62cf8722008-05-20 12:15:15 +020093#ifndef CONFIG_SND_DEBUG_VERBOSE
Takashi Iwai28073142007-07-27 18:58:06 +020094 if (!capable(CAP_SYS_RAWIO))
95 return -EACCES;
96#endif
97 return 0;
98}
99
Takashi Iwai11aeff02008-07-30 15:01:46 +0200100static void clear_hwdep_elements(struct hda_codec *codec)
101{
Takashi Iwai1e1be432008-07-30 15:01:46 +0200102 char **head;
103 int i;
104
Takashi Iwai11aeff02008-07-30 15:01:46 +0200105 /* clear init verbs */
106 snd_array_free(&codec->init_verbs);
Takashi Iwai1e1be432008-07-30 15:01:46 +0200107 /* 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);
Takashi Iwai3be14142009-02-20 14:11:16 +0100112 snd_array_free(&codec->override_pins);
Takashi Iwai11aeff02008-07-30 15:01:46 +0200113}
114
115static void hwdep_free(struct snd_hwdep *hwdep)
116{
117 clear_hwdep_elements(hwdep->private_data);
118}
119
Takashi Iwai1289e9e2008-11-27 15:47:11 +0100120int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
Takashi Iwai28073142007-07-27 18:58:06 +0200121{
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;
Takashi Iwai11aeff02008-07-30 15:01:46 +0200134 hwdep->private_free = hwdep_free;
Takashi Iwai28073142007-07-27 18:58:06 +0200135 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
Takashi Iwai11aeff02008-07-30 15:01:46 +0200143 snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
Takashi Iwai1e1be432008-07-30 15:01:46 +0200144 snd_array_init(&codec->hints, sizeof(char *), 32);
Takashi Iwai3be14142009-02-20 14:11:16 +0100145 snd_array_init(&codec->override_pins, sizeof(struct hda_pincfg), 16);
Takashi Iwai11aeff02008-07-30 15:01:46 +0200146
Takashi Iwai28073142007-07-27 18:58:06 +0200147 return 0;
148}
Takashi Iwaid7ffba12008-07-30 15:01:46 +0200149
Takashi Iwaie7ee0582008-11-21 09:26:20 +0100150#ifdef CONFIG_SND_HDA_RECONFIG
151
Takashi Iwaid7ffba12008-07-30 15:01:46 +0200152/*
153 * sysfs interface
154 */
155
156static int clear_codec(struct hda_codec *codec)
157{
158 snd_hda_codec_reset(codec);
Takashi Iwai11aeff02008-07-30 15:01:46 +0200159 clear_hwdep_elements(codec);
Takashi Iwaid7ffba12008-07-30 15:01:46 +0200160 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 */
Takashi Iwai529bd6c2008-11-27 14:17:01 +0100173 err = snd_hda_codec_build_pcms(codec);
Takashi Iwaid7ffba12008-07-30 15:01:46 +0200174 if (err < 0)
175 return err;
176 /* rebuild mixers */
177 err = snd_hda_codec_build_controls(codec);
178 if (err < 0)
179 return err;
Takashi Iwai26a74f12009-02-12 00:13:19 +0100180 return snd_card_register(codec->bus->card);
Takashi Iwaid7ffba12008-07-30 15:01:46 +0200181}
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
Takashi Iwai11aeff02008-07-30 15:01:46 +0200276static 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;
Takashi Iwai55290e12009-02-20 15:59:01 +0100282 struct hda_verb *v;
283 int nid, verb, param;
Takashi Iwai11aeff02008-07-30 15:01:46 +0200284
Takashi Iwai55290e12009-02-20 15:59:01 +0100285 if (sscanf(buf, "%i %i %i", &nid, &verb, &param) != 3)
286 return -EINVAL;
287 if (!nid || !verb)
Takashi Iwai11aeff02008-07-30 15:01:46 +0200288 return -EINVAL;
289 v = snd_array_new(&codec->init_verbs);
290 if (!v)
291 return -ENOMEM;
Takashi Iwai55290e12009-02-20 15:59:01 +0100292 v->nid = nid;
293 v->verb = verb;
294 v->param = param;
Takashi Iwai11aeff02008-07-30 15:01:46 +0200295 return count;
296}
297
Takashi Iwai1e1be432008-07-30 15:01:46 +0200298static 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
Takashi Iwai3be14142009-02-20 14:11:16 +0100321static 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
343static ssize_t override_pin_configs_show(struct device *dev,
344 struct device_attribute *attr,
345 char *buf)
346{
347 struct snd_hwdep *hwdep = dev_get_drvdata(dev);
348 struct hda_codec *codec = hwdep->private_data;
349 return pin_configs_show(codec, &codec->override_pins, buf);
350}
351
352static ssize_t cur_pin_configs_show(struct device *dev,
353 struct device_attribute *attr,
354 char *buf)
355{
356 struct snd_hwdep *hwdep = dev_get_drvdata(dev);
357 struct hda_codec *codec = hwdep->private_data;
358 return pin_configs_show(codec, &codec->cur_pins, buf);
359}
360
361#define MAX_PIN_CONFIGS 32
362
363static ssize_t override_pin_configs_store(struct device *dev,
364 struct device_attribute *attr,
365 const char *buf, size_t count)
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;
376 err = snd_hda_add_pincfg(codec, &codec->override_pins, nid, cfg);
377 if (err < 0)
378 return err;
379 return count;
380}
381
Takashi Iwaid7ffba12008-07-30 15:01:46 +0200382#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),
Takashi Iwai11aeff02008-07-30 15:01:46 +0200397 CODEC_ATTR_WO(init_verbs),
Takashi Iwai1e1be432008-07-30 15:01:46 +0200398 CODEC_ATTR_WO(hints),
Takashi Iwai3be14142009-02-20 14:11:16 +0100399 CODEC_ATTR_RO(init_pin_configs),
400 CODEC_ATTR_RW(override_pin_configs),
401 CODEC_ATTR_RO(cur_pin_configs),
Takashi Iwaid7ffba12008-07-30 15:01:46 +0200402 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}
Takashi Iwaie7ee0582008-11-21 09:26:20 +0100419
420#endif /* CONFIG_SND_HDA_RECONFIG */