aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2015-04-06 18:38:49 +0200
committerTakashi Iwai <tiwai@suse.de>2015-04-06 18:38:49 +0200
commit8239a38f5039a28c41065012efa40c8f8d5fa556 (patch)
tree9c2b60844beabfad9f33610f54a3edf1edcf517e
parent2fef2d590a80efa3b685ca3e506a582e0d9e478e (diff)
downloadhda-emu-8239a38f5039a28c41065012efa40c8f8d5fa556.tar.gz
Add regmap support code
Now we support regmap in the upstream, so follows the hda-emu, too. The regmap support codes are added without checks in configure. If you need to build the pre 4.1-rc1 version, use the previous git checkout. Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--include/linux/regmap.h56
-rw-r--r--include/sound/hda_regmap.h3
-rw-r--r--lib/Makefile.am3
-rw-r--r--lib/array.c1
-rw-r--r--lib/hdac_regmap.c183
5 files changed, 245 insertions, 1 deletions
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
new file mode 100644
index 0000000..67551c7
--- /dev/null
+++ b/include/linux/regmap.h
@@ -0,0 +1,56 @@
+/* stripped version of regmap implementation for hda-emu */
+
+#ifndef __LINUX_REGMAP_H
+#define __LINUX_REGMAP_H
+
+#include <linux/err.h>
+
+enum regcache_type {
+ REGCACHE_NONE,
+ REGCACHE_RBTREE,
+ REGCACHE_COMPRESSED,
+ REGCACHE_FLAT,
+};
+
+enum regmap_endian {
+ /* Unspecified -> 0 -> Backwards compatible default */
+ REGMAP_ENDIAN_DEFAULT = 0,
+ REGMAP_ENDIAN_BIG,
+ REGMAP_ENDIAN_LITTLE,
+ REGMAP_ENDIAN_NATIVE,
+};
+
+struct remgap;
+struct regmap_bus;
+
+struct regmap_config {
+ const char *name;
+ int reg_bits;
+ int val_bits;
+ bool (*writeable_reg)(struct device *dev, unsigned int reg);
+ bool (*readable_reg)(struct device *dev, unsigned int reg);
+ bool (*volatile_reg)(struct device *dev, unsigned int reg);
+ int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
+ int (*reg_write)(void *context, unsigned int reg, unsigned int val);
+ unsigned int max_register;
+ enum regcache_type cache_type;
+ bool use_single_rw;
+};
+
+struct regmap *regmap_init(struct device *dev,
+ const struct regmap_bus *bus,
+ void *bus_context,
+ const struct regmap_config *config);
+
+void regmap_exit(struct regmap *map);
+int regmap_write(struct regmap *map, unsigned int reg, unsigned int val);
+int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val);
+
+int regcache_sync(struct regmap *map);
+int regcache_sync_region(struct regmap *map, unsigned int min,
+ unsigned int max);
+void regcache_cache_only(struct regmap *map, bool enable);
+void regcache_cache_bypass(struct regmap *map, bool enable);
+void regcache_mark_dirty(struct regmap *map);
+
+#endif /* __LINUX_REGMAP_H */
diff --git a/include/sound/hda_regmap.h b/include/sound/hda_regmap.h
new file mode 100644
index 0000000..62668c3
--- /dev/null
+++ b/include/sound/hda_regmap.h
@@ -0,0 +1,3 @@
+#include <wrapper.h>
+#include <sound/core.h>
+#include "../../dist/include/sound/hda_regmap.h"
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 09ebe85..738af69 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,3 +1,4 @@
noinst_LIBRARIES = libhdacore.a
-libhdacore_a_SOURCES = hda_bus_type.c hdac_bus.c hdac_device.c hdac_sysfs.c
+libhdacore_a_SOURCES = hda_bus_type.c hdac_bus.c hdac_device.c hdac_sysfs.c \
+ hdac_regmap.c array.c
INCLUDES = -I$(top_srcdir)/include
diff --git a/lib/array.c b/lib/array.c
new file mode 100644
index 0000000..3c7ca6a
--- /dev/null
+++ b/lib/array.c
@@ -0,0 +1 @@
+#include "../dist/sound/hda/array.c"
diff --git a/lib/hdac_regmap.c b/lib/hdac_regmap.c
new file mode 100644
index 0000000..b3d71b6
--- /dev/null
+++ b/lib/hdac_regmap.c
@@ -0,0 +1,183 @@
+#include "../dist/sound/hda/hdac_regmap.c"
+
+/*
+ * regmap wrapper implementations
+ */
+
+/* dumb value management by linked list */
+struct regmap_val;
+
+struct regmap_val {
+ int reg;
+ int val;
+ struct regmap_val *next;
+};
+
+struct regmap {
+ struct device *dev;
+ const struct regmap_bus *bus;
+ void *bus_context;
+ const struct regmap_config *config;
+ bool cache_dirty;
+ bool cache_only;
+ bool cache_bypass;
+ struct regmap_val *val;
+ struct regmap_val *val_last;
+};
+
+struct regmap *regmap_init(struct device *dev,
+ const struct regmap_bus *bus,
+ void *bus_context,
+ const struct regmap_config *config)
+{
+ struct regmap *map;
+
+ map = calloc(1, sizeof(*map));
+ if (!map)
+ exit(1);
+ map->dev = dev;
+ map->bus = bus;
+ map->bus_context = bus_context;
+ map->config = config;
+ return map;
+}
+
+void regmap_exit(struct regmap *map)
+{
+ struct regmap_val *vp, *next;
+
+ for (vp = map->val; vp; vp = next) {
+ next = vp->next;
+ free(vp);
+ }
+
+ free(map);
+}
+
+static struct regmap_val *cache_find_reg(struct regmap *map, unsigned int reg)
+{
+ struct regmap_val *vp;
+
+ for (vp = map->val; vp; vp = vp->next) {
+ if (vp->reg == reg)
+ return vp;
+ }
+ return NULL;
+}
+
+static bool cache_read_reg(struct regmap *map, unsigned int reg, unsigned int *val)
+{
+ struct regmap_val *vp = cache_find_reg(map, reg);
+
+ if (!vp)
+ return false;
+ *val = vp->val;
+ return true;
+}
+
+static void cache_write_reg(struct regmap *map, unsigned int reg, unsigned int val)
+{
+ struct regmap_val *vp = cache_find_reg(map, reg);
+
+ if (vp) {
+ vp->val = val;
+ return;
+ }
+
+ vp = malloc(sizeof(*vp));
+ if (!vp)
+ exit(1);
+ vp->reg = reg;
+ vp->val = val;
+ vp->next = NULL;
+ if (!map->val)
+ map->val = vp;
+ else
+ map->val_last->next = vp;
+ map->val_last = vp;
+}
+
+int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
+{
+ int err;
+
+ if (!map->config->writeable_reg(map->dev, reg))
+ return -EINVAL;
+ if (map->cache_only)
+ err = 0;
+ else
+ err = map->config->reg_write(map->bus_context, reg, val);
+ if (!err && !map->cache_bypass &&
+ !map->config->volatile_reg(map->dev, reg))
+ cache_write_reg(map, reg, val);
+ return err;
+}
+
+int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
+{
+ int err;
+
+ if (!map->config->readable_reg(map->dev, reg))
+ return -EINVAL;
+ if (!map->cache_bypass && cache_read_reg(map, reg, val))
+ return 0;
+ err = map->config->reg_read(map->bus_context, reg, val);
+ if (!err && !map->cache_bypass &&
+ !map->config->volatile_reg(map->dev, reg))
+ cache_write_reg(map, reg, *val);
+ return err;
+}
+
+int regcache_sync(struct regmap *map)
+{
+ struct regmap_val *vp;
+ int err;
+
+ if (!map->cache_dirty)
+ return 0;
+ for (vp = map->val; vp; vp = vp->next) {
+ if (!map->config->writeable_reg(map->dev, vp->reg))
+ continue;
+ err = map->config->reg_write(map->bus_context, vp->reg, vp->val);
+ if (err < 0)
+ return err;
+ }
+ map->cache_dirty = false;
+ return 0;
+}
+
+int regcache_sync_region(struct regmap *map, unsigned int min,
+ unsigned int max)
+{
+ struct regmap_val *vp;
+ int err;
+
+ if (!map->cache_dirty)
+ return 0;
+ for (vp = map->val; vp; vp = vp->next) {
+ if (vp->reg < min || vp->reg > max)
+ continue;
+ if (!map->config->writeable_reg(map->dev, vp->reg))
+ continue;
+ err = map->config->reg_write(map->bus_context, vp->reg, vp->val);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+void regcache_cache_only(struct regmap *map, bool enable)
+{
+ map->cache_only = enable;
+}
+
+void regcache_cache_bypass(struct regmap *map, bool enable)
+{
+ map->cache_bypass = enable;
+}
+
+void regcache_mark_dirty(struct regmap *map)
+{
+ map->cache_dirty = true;
+}
+