]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - sound/pci/hda/hda_hwdep.c
ALSA: hda - Show missing GPIO unsol bits
[linux-2.6-omap-h63xx.git] / sound / pci / hda / hda_hwdep.c
index 2177d9af533496a4583d943c6f648d241f9ef078..653da1d3e4dfb6018f489d90ec1226e5988fecfb 100644 (file)
 #include <linux/pci.h>
 #include <linux/compat.h>
 #include <linux/mutex.h>
+#include <linux/ctype.h>
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
 #include <sound/hda_hwdep.h>
+#include <sound/minors.h>
 
 /*
  * write/read an out-of-bound verb
@@ -88,13 +90,32 @@ static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
 
 static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
 {
-#ifndef CONFIG_SND_DEBUG_DETECT
+#ifndef CONFIG_SND_DEBUG_VERBOSE
        if (!capable(CAP_SYS_RAWIO))
                return -EACCES;
 #endif
        return 0;
 }
 
+static void clear_hwdep_elements(struct hda_codec *codec)
+{
+       char **head;
+       int i;
+
+       /* clear init verbs */
+       snd_array_free(&codec->init_verbs);
+       /* clear hints */
+       head = codec->hints.list;
+       for (i = 0; i < codec->hints.used; i++, head++)
+               kfree(*head);
+       snd_array_free(&codec->hints);
+}
+
+static void hwdep_free(struct snd_hwdep *hwdep)
+{
+       clear_hwdep_elements(hwdep->private_data);
+}
+
 int __devinit snd_hda_create_hwdep(struct hda_codec *codec)
 {
        char hwname[16];
@@ -109,6 +130,7 @@ int __devinit snd_hda_create_hwdep(struct hda_codec *codec)
        sprintf(hwdep->name, "HDA Codec %d", codec->addr);
        hwdep->iface = SNDRV_HWDEP_IFACE_HDA;
        hwdep->private_data = codec;
+       hwdep->private_free = hwdep_free;
        hwdep->exclusive = 1;
 
        hwdep->ops.open = hda_hwdep_open;
@@ -117,5 +139,211 @@ int __devinit snd_hda_create_hwdep(struct hda_codec *codec)
        hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
 #endif
 
+       snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
+       snd_array_init(&codec->hints, sizeof(char *), 32);
+
+       return 0;
+}
+
+/*
+ * sysfs interface
+ */
+
+static int clear_codec(struct hda_codec *codec)
+{
+       snd_hda_codec_reset(codec);
+       clear_hwdep_elements(codec);
+       return 0;
+}
+
+static int reconfig_codec(struct hda_codec *codec)
+{
+       int err;
+
+       snd_printk(KERN_INFO "hda-codec: reconfiguring\n");
+       snd_hda_codec_reset(codec);
+       err = snd_hda_codec_configure(codec);
+       if (err < 0)
+               return err;
+       /* rebuild PCMs */
+       err = snd_hda_build_pcms(codec->bus);
+       if (err < 0)
+               return err;
+       /* rebuild mixers */
+       err = snd_hda_codec_build_controls(codec);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+/*
+ * allocate a string at most len chars, and remove the trailing EOL
+ */
+static char *kstrndup_noeol(const char *src, size_t len)
+{
+       char *s = kstrndup(src, len, GFP_KERNEL);
+       char *p;
+       if (!s)
+               return NULL;
+       p = strchr(s, '\n');
+       if (p)
+               *p = 0;
+       return s;
+}
+
+#define CODEC_INFO_SHOW(type)                                  \
+static ssize_t type##_show(struct device *dev,                 \
+                          struct device_attribute *attr,       \
+                          char *buf)                           \
+{                                                              \
+       struct snd_hwdep *hwdep = dev_get_drvdata(dev);         \
+       struct hda_codec *codec = hwdep->private_data;          \
+       return sprintf(buf, "0x%x\n", codec->type);             \
+}
+
+#define CODEC_INFO_STR_SHOW(type)                              \
+static ssize_t type##_show(struct device *dev,                 \
+                            struct device_attribute *attr,     \
+                                       char *buf)              \
+{                                                              \
+       struct snd_hwdep *hwdep = dev_get_drvdata(dev);         \
+       struct hda_codec *codec = hwdep->private_data;          \
+       return sprintf(buf, "%s\n",                             \
+                      codec->type ? codec->type : "");         \
+}
+
+CODEC_INFO_SHOW(vendor_id);
+CODEC_INFO_SHOW(subsystem_id);
+CODEC_INFO_SHOW(revision_id);
+CODEC_INFO_SHOW(afg);
+CODEC_INFO_SHOW(mfg);
+CODEC_INFO_STR_SHOW(name);
+CODEC_INFO_STR_SHOW(modelname);
+
+#define CODEC_INFO_STORE(type)                                 \
+static ssize_t type##_store(struct device *dev,                        \
+                           struct device_attribute *attr,      \
+                           const char *buf, size_t count)      \
+{                                                              \
+       struct snd_hwdep *hwdep = dev_get_drvdata(dev);         \
+       struct hda_codec *codec = hwdep->private_data;          \
+       char *after;                                            \
+       codec->type = simple_strtoul(buf, &after, 0);           \
+       return count;                                           \
+}
+
+#define CODEC_INFO_STR_STORE(type)                             \
+static ssize_t type##_store(struct device *dev,                        \
+                           struct device_attribute *attr,      \
+                           const char *buf, size_t count)      \
+{                                                              \
+       struct snd_hwdep *hwdep = dev_get_drvdata(dev);         \
+       struct hda_codec *codec = hwdep->private_data;          \
+       char *s = kstrndup_noeol(buf, 64);                      \
+       if (!s)                                                 \
+               return -ENOMEM;                                 \
+       kfree(codec->type);                                     \
+       codec->type = s;                                        \
+       return count;                                           \
+}
+
+CODEC_INFO_STORE(vendor_id);
+CODEC_INFO_STORE(subsystem_id);
+CODEC_INFO_STORE(revision_id);
+CODEC_INFO_STR_STORE(name);
+CODEC_INFO_STR_STORE(modelname);
+
+#define CODEC_ACTION_STORE(type)                               \
+static ssize_t type##_store(struct device *dev,                        \
+                           struct device_attribute *attr,      \
+                           const char *buf, size_t count)      \
+{                                                              \
+       struct snd_hwdep *hwdep = dev_get_drvdata(dev);         \
+       struct hda_codec *codec = hwdep->private_data;          \
+       int err = 0;                                            \
+       if (*buf)                                               \
+               err = type##_codec(codec);                      \
+       return err < 0 ? err : count;                           \
+}
+
+CODEC_ACTION_STORE(reconfig);
+CODEC_ACTION_STORE(clear);
+
+static ssize_t init_verbs_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+       struct hda_codec *codec = hwdep->private_data;
+       char *p;
+       struct hda_verb verb, *v;
+
+       verb.nid = simple_strtoul(buf, &p, 0);
+       verb.verb = simple_strtoul(p, &p, 0);
+       verb.param = simple_strtoul(p, &p, 0);
+       if (!verb.nid || !verb.verb || !verb.param)
+               return -EINVAL;
+       v = snd_array_new(&codec->init_verbs);
+       if (!v)
+               return -ENOMEM;
+       *v = verb;
+       return count;
+}
+
+static ssize_t hints_store(struct device *dev,
+                          struct device_attribute *attr,
+                          const char *buf, size_t count)
+{
+       struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+       struct hda_codec *codec = hwdep->private_data;
+       char *p;
+       char **hint;
+
+       if (!*buf || isspace(*buf) || *buf == '#' || *buf == '\n')
+               return count;
+       p = kstrndup_noeol(buf, 1024);
+       if (!p)
+               return -ENOMEM;
+       hint = snd_array_new(&codec->hints);
+       if (!hint) {
+               kfree(p);
+               return -ENOMEM;
+       }
+       *hint = p;
+       return count;
+}
+
+#define CODEC_ATTR_RW(type) \
+       __ATTR(type, 0644, type##_show, type##_store)
+#define CODEC_ATTR_RO(type) \
+       __ATTR_RO(type)
+#define CODEC_ATTR_WO(type) \
+       __ATTR(type, 0200, NULL, type##_store)
+
+static struct device_attribute codec_attrs[] = {
+       CODEC_ATTR_RW(vendor_id),
+       CODEC_ATTR_RW(subsystem_id),
+       CODEC_ATTR_RW(revision_id),
+       CODEC_ATTR_RO(afg),
+       CODEC_ATTR_RO(mfg),
+       CODEC_ATTR_RW(name),
+       CODEC_ATTR_RW(modelname),
+       CODEC_ATTR_WO(init_verbs),
+       CODEC_ATTR_WO(hints),
+       CODEC_ATTR_WO(reconfig),
+       CODEC_ATTR_WO(clear),
+};
+
+/*
+ * create sysfs files on hwdep directory
+ */
+int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
+{
+       struct snd_hwdep *hwdep = codec->hwdep;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(codec_attrs); i++)
+               snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card,
+                                         hwdep->device, &codec_attrs[i]);
        return 0;
 }