blob: 4af484b8240cde92a39a3010cbe69f39bec4abf0 [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 Iwai346ff702009-02-23 09:42:57 +0100112 snd_array_free(&codec->user_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 Iwai346ff702009-02-23 09:42:57 +0100145 snd_array_init(&codec->user_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{
Takashi Iwaia65d6292009-02-23 16:57:04 +0100158 int err;
159
160 err = snd_hda_codec_reset(codec);
161 if (err < 0) {
162 snd_printk(KERN_ERR "The codec is being used, can't free.\n");
163 return err;
164 }
Takashi Iwai11aeff02008-07-30 15:01:46 +0200165 clear_hwdep_elements(codec);
Takashi Iwaid7ffba12008-07-30 15:01:46 +0200166 return 0;
167}
168
169static int reconfig_codec(struct hda_codec *codec)
170{
171 int err;
172
173 snd_printk(KERN_INFO "hda-codec: reconfiguring\n");
Takashi Iwaia65d6292009-02-23 16:57:04 +0100174 err = snd_hda_codec_reset(codec);
175 if (err < 0) {
176 snd_printk(KERN_ERR
177 "The codec is being used, can't reconfigure.\n");
178 return err;
179 }
Takashi Iwaid7ffba12008-07-30 15:01:46 +0200180 err = snd_hda_codec_configure(codec);
181 if (err < 0)
182 return err;
183 /* rebuild PCMs */
Takashi Iwai529bd6c2008-11-27 14:17:01 +0100184 err = snd_hda_codec_build_pcms(codec);
Takashi Iwaid7ffba12008-07-30 15:01:46 +0200185 if (err < 0)
186 return err;
187 /* rebuild mixers */
188 err = snd_hda_codec_build_controls(codec);
189 if (err < 0)
190 return err;
Takashi Iwai26a74f12009-02-12 00:13:19 +0100191 return snd_card_register(codec->bus->card);
Takashi Iwaid7ffba12008-07-30 15:01:46 +0200192}
193
194/*
195 * allocate a string at most len chars, and remove the trailing EOL
196 */
197static char *kstrndup_noeol(const char *src, size_t len)
198{
199 char *s = kstrndup(src, len, GFP_KERNEL);
200 char *p;
201 if (!s)
202 return NULL;
203 p = strchr(s, '\n');
204 if (p)
205 *p = 0;
206 return s;
207}
208
209#define CODEC_INFO_SHOW(type) \
210static ssize_t type##_show(struct device *dev, \
211 struct device_attribute *attr, \
212 char *buf) \
213{ \
214 struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
215 struct hda_codec *codec = hwdep->private_data; \
216 return sprintf(buf, "0x%x\n", codec->type); \
217}
218
219#define CODEC_INFO_STR_SHOW(type) \
220static ssize_t type##_show(struct device *dev, \
221 struct device_attribute *attr, \
222 char *buf) \
223{ \
224 struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
225 struct hda_codec *codec = hwdep->private_data; \
226 return sprintf(buf, "%s\n", \
227 codec->type ? codec->type : ""); \
228}
229
230CODEC_INFO_SHOW(vendor_id);
231CODEC_INFO_SHOW(subsystem_id);
232CODEC_INFO_SHOW(revision_id);
233CODEC_INFO_SHOW(afg);
234CODEC_INFO_SHOW(mfg);
235CODEC_INFO_STR_SHOW(name);
236CODEC_INFO_STR_SHOW(modelname);
237
238#define CODEC_INFO_STORE(type) \
239static ssize_t type##_store(struct device *dev, \
240 struct device_attribute *attr, \
241 const char *buf, size_t count) \
242{ \
243 struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
244 struct hda_codec *codec = hwdep->private_data; \
245 char *after; \
246 codec->type = simple_strtoul(buf, &after, 0); \
247 return count; \
248}
249
250#define CODEC_INFO_STR_STORE(type) \
251static ssize_t type##_store(struct device *dev, \
252 struct device_attribute *attr, \
253 const char *buf, size_t count) \
254{ \
255 struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
256 struct hda_codec *codec = hwdep->private_data; \
257 char *s = kstrndup_noeol(buf, 64); \
258 if (!s) \
259 return -ENOMEM; \
260 kfree(codec->type); \
261 codec->type = s; \
262 return count; \
263}
264
265CODEC_INFO_STORE(vendor_id);
266CODEC_INFO_STORE(subsystem_id);
267CODEC_INFO_STORE(revision_id);
268CODEC_INFO_STR_STORE(name);
269CODEC_INFO_STR_STORE(modelname);
270
271#define CODEC_ACTION_STORE(type) \
272static ssize_t type##_store(struct device *dev, \
273 struct device_attribute *attr, \
274 const char *buf, size_t count) \
275{ \
276 struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
277 struct hda_codec *codec = hwdep->private_data; \
278 int err = 0; \
279 if (*buf) \
280 err = type##_codec(codec); \
281 return err < 0 ? err : count; \
282}
283
284CODEC_ACTION_STORE(reconfig);
285CODEC_ACTION_STORE(clear);
286
Takashi Iwai11aeff02008-07-30 15:01:46 +0200287static ssize_t init_verbs_store(struct device *dev,
288 struct device_attribute *attr,
289 const char *buf, size_t count)
290{
291 struct snd_hwdep *hwdep = dev_get_drvdata(dev);
292 struct hda_codec *codec = hwdep->private_data;
Takashi Iwai55290e12009-02-20 15:59:01 +0100293 struct hda_verb *v;
294 int nid, verb, param;
Takashi Iwai11aeff02008-07-30 15:01:46 +0200295
Takashi Iwai55290e12009-02-20 15:59:01 +0100296 if (sscanf(buf, "%i %i %i", &nid, &verb, &param) != 3)
297 return -EINVAL;
298 if (!nid || !verb)
Takashi Iwai11aeff02008-07-30 15:01:46 +0200299 return -EINVAL;
300 v = snd_array_new(&codec->init_verbs);
301 if (!v)
302 return -ENOMEM;
Takashi Iwai55290e12009-02-20 15:59:01 +0100303 v->nid = nid;
304 v->verb = verb;
305 v->param = param;
Takashi Iwai11aeff02008-07-30 15:01:46 +0200306 return count;
307}
308
Takashi Iwai1e1be432008-07-30 15:01:46 +0200309static ssize_t hints_store(struct device *dev,
310 struct device_attribute *attr,
311 const char *buf, size_t count)
312{
313 struct snd_hwdep *hwdep = dev_get_drvdata(dev);
314 struct hda_codec *codec = hwdep->private_data;
315 char *p;
316 char **hint;
317
318 if (!*buf || isspace(*buf) || *buf == '#' || *buf == '\n')
319 return count;
320 p = kstrndup_noeol(buf, 1024);
321 if (!p)
322 return -ENOMEM;
323 hint = snd_array_new(&codec->hints);
324 if (!hint) {
325 kfree(p);
326 return -ENOMEM;
327 }
328 *hint = p;
329 return count;
330}
331
Takashi Iwai3be14142009-02-20 14:11:16 +0100332static ssize_t pin_configs_show(struct hda_codec *codec,
333 struct snd_array *list,
334 char *buf)
335{
336 int i, len = 0;
337 for (i = 0; i < list->used; i++) {
338 struct hda_pincfg *pin = snd_array_elem(list, i);
339 len += sprintf(buf + len, "0x%02x 0x%08x\n",
340 pin->nid, pin->cfg);
341 }
342 return len;
343}
344
345static ssize_t init_pin_configs_show(struct device *dev,
346 struct device_attribute *attr,
347 char *buf)
348{
349 struct snd_hwdep *hwdep = dev_get_drvdata(dev);
350 struct hda_codec *codec = hwdep->private_data;
351 return pin_configs_show(codec, &codec->init_pins, buf);
352}
353
Takashi Iwai346ff702009-02-23 09:42:57 +0100354static ssize_t user_pin_configs_show(struct device *dev,
355 struct device_attribute *attr,
356 char *buf)
Takashi Iwai3be14142009-02-20 14:11:16 +0100357{
358 struct snd_hwdep *hwdep = dev_get_drvdata(dev);
359 struct hda_codec *codec = hwdep->private_data;
Takashi Iwai346ff702009-02-23 09:42:57 +0100360 return pin_configs_show(codec, &codec->user_pins, buf);
Takashi Iwai3be14142009-02-20 14:11:16 +0100361}
362
Takashi Iwai346ff702009-02-23 09:42:57 +0100363static ssize_t driver_pin_configs_show(struct device *dev,
364 struct device_attribute *attr,
365 char *buf)
Takashi Iwai3be14142009-02-20 14:11:16 +0100366{
367 struct snd_hwdep *hwdep = dev_get_drvdata(dev);
368 struct hda_codec *codec = hwdep->private_data;
Takashi Iwai346ff702009-02-23 09:42:57 +0100369 return pin_configs_show(codec, &codec->driver_pins, buf);
Takashi Iwai3be14142009-02-20 14:11:16 +0100370}
371
372#define MAX_PIN_CONFIGS 32
373
Takashi Iwai346ff702009-02-23 09:42:57 +0100374static ssize_t user_pin_configs_store(struct device *dev,
375 struct device_attribute *attr,
376 const char *buf, size_t count)
Takashi Iwai3be14142009-02-20 14:11:16 +0100377{
378 struct snd_hwdep *hwdep = dev_get_drvdata(dev);
379 struct hda_codec *codec = hwdep->private_data;
380 int nid, cfg;
381 int err;
382
383 if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
384 return -EINVAL;
385 if (!nid)
386 return -EINVAL;
Takashi Iwai346ff702009-02-23 09:42:57 +0100387 err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
Takashi Iwai3be14142009-02-20 14:11:16 +0100388 if (err < 0)
389 return err;
390 return count;
391}
392
Takashi Iwaid7ffba12008-07-30 15:01:46 +0200393#define CODEC_ATTR_RW(type) \
394 __ATTR(type, 0644, type##_show, type##_store)
395#define CODEC_ATTR_RO(type) \
396 __ATTR_RO(type)
397#define CODEC_ATTR_WO(type) \
398 __ATTR(type, 0200, NULL, type##_store)
399
400static struct device_attribute codec_attrs[] = {
401 CODEC_ATTR_RW(vendor_id),
402 CODEC_ATTR_RW(subsystem_id),
403 CODEC_ATTR_RW(revision_id),
404 CODEC_ATTR_RO(afg),
405 CODEC_ATTR_RO(mfg),
406 CODEC_ATTR_RW(name),
407 CODEC_ATTR_RW(modelname),
Takashi Iwai11aeff02008-07-30 15:01:46 +0200408 CODEC_ATTR_WO(init_verbs),
Takashi Iwai1e1be432008-07-30 15:01:46 +0200409 CODEC_ATTR_WO(hints),
Takashi Iwai3be14142009-02-20 14:11:16 +0100410 CODEC_ATTR_RO(init_pin_configs),
Takashi Iwai346ff702009-02-23 09:42:57 +0100411 CODEC_ATTR_RW(user_pin_configs),
412 CODEC_ATTR_RO(driver_pin_configs),
Takashi Iwaid7ffba12008-07-30 15:01:46 +0200413 CODEC_ATTR_WO(reconfig),
414 CODEC_ATTR_WO(clear),
415};
416
417/*
418 * create sysfs files on hwdep directory
419 */
420int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
421{
422 struct snd_hwdep *hwdep = codec->hwdep;
423 int i;
424
425 for (i = 0; i < ARRAY_SIZE(codec_attrs); i++)
426 snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card,
427 hwdep->device, &codec_attrs[i]);
428 return 0;
429}
Takashi Iwaie7ee0582008-11-21 09:26:20 +0100430
431#endif /* CONFIG_SND_HDA_RECONFIG */