summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/Kconfig2
-rw-r--r--sound/Makefile1
-rw-r--r--sound/ac97/Kconfig19
-rw-r--r--sound/ac97/Makefile8
-rw-r--r--sound/ac97/ac97_core.h16
-rw-r--r--sound/ac97/bus.c539
-rw-r--r--sound/ac97/codec.c15
-rw-r--r--sound/ac97/snd_ac97_compat.c108
-rw-r--r--sound/arm/pxa2xx-ac97-lib.c37
-rw-r--r--sound/arm/pxa2xx-ac97.c35
-rw-r--r--sound/soc/amd/Kconfig7
-rw-r--r--sound/soc/amd/Makefile6
-rw-r--r--sound/soc/amd/acp-pcm-dma.c356
-rw-r--r--sound/soc/amd/acp-rt5645.c199
-rw-r--r--sound/soc/amd/acp.h19
-rw-r--r--sound/soc/codecs/arizona.c166
-rw-r--r--sound/soc/codecs/arizona.h6
-rw-r--r--sound/soc/codecs/cs47l24.c14
-rw-r--r--sound/soc/codecs/wm5102.c14
-rw-r--r--sound/soc/codecs/wm5110.c14
-rw-r--r--sound/soc/codecs/wm8997.c15
-rw-r--r--sound/soc/codecs/wm8998.c11
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c32
23 files changed, 1491 insertions, 148 deletions
diff --git a/sound/Kconfig b/sound/Kconfig
index d7d2aac9542e..ed34218d38b8 100644
--- a/sound/Kconfig
+++ b/sound/Kconfig
@@ -80,6 +80,8 @@ source "sound/hda/Kconfig"
source "sound/ppc/Kconfig"
+source "sound/ac97/Kconfig"
+
source "sound/aoa/Kconfig"
source "sound/arm/Kconfig"
diff --git a/sound/Makefile b/sound/Makefile
index f2d1d093bcdc..f245c75a2add 100644
--- a/sound/Makefile
+++ b/sound/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_SND_AOA) += aoa/
# This one must be compilable even if sound is configured out
obj-$(CONFIG_AC97_BUS) += ac97_bus.o
+obj-$(CONFIG_AC97_BUS_NEW) += ac97/
ifeq ($(CONFIG_SND),y)
obj-y += last.o
diff --git a/sound/ac97/Kconfig b/sound/ac97/Kconfig
new file mode 100644
index 000000000000..f8a64e15e5bf
--- /dev/null
+++ b/sound/ac97/Kconfig
@@ -0,0 +1,19 @@
+#
+# AC97 configuration
+#
+
+
+config AC97_BUS_NEW
+ tristate
+ select AC97
+ help
+ This is the new AC97 bus type, successor of AC97_BUS. The ported
+ drivers which benefit from the AC97 automatic probing should "select"
+ this instead of the AC97_BUS.
+ Say Y here if you want to have AC97 devices, which are sound oriented
+ devices around an AC-Link.
+
+config AC97_BUS_COMPAT
+ bool
+ depends on AC97_BUS_NEW
+ depends on !AC97_BUS
diff --git a/sound/ac97/Makefile b/sound/ac97/Makefile
new file mode 100644
index 000000000000..f9c2640bfb59
--- /dev/null
+++ b/sound/ac97/Makefile
@@ -0,0 +1,8 @@
+#
+# make for AC97 bus drivers
+#
+
+obj-$(CONFIG_AC97_BUS_NEW) += ac97.o
+
+ac97-y += bus.o codec.o
+ac97-$(CONFIG_AC97_BUS_COMPAT) += snd_ac97_compat.o
diff --git a/sound/ac97/ac97_core.h b/sound/ac97/ac97_core.h
new file mode 100644
index 000000000000..08441a4fda7c
--- /dev/null
+++ b/sound/ac97/ac97_core.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+unsigned int snd_ac97_bus_scan_one(struct ac97_controller *ac97,
+ unsigned int codec_num);
+
+static inline bool ac97_ids_match(unsigned int id1, unsigned int id2,
+ unsigned int mask)
+{
+ return (id1 & mask) == (id2 & mask);
+}
diff --git a/sound/ac97/bus.c b/sound/ac97/bus.c
new file mode 100644
index 000000000000..31f858eceffc
--- /dev/null
+++ b/sound/ac97/bus.c
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/idr.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <sound/ac97/codec.h>
+#include <sound/ac97/controller.h>
+#include <sound/ac97/regs.h>
+
+#include "ac97_core.h"
+
+/*
+ * Protects ac97_controllers and each ac97_controller structure.
+ */
+static DEFINE_MUTEX(ac97_controllers_mutex);
+static DEFINE_IDR(ac97_adapter_idr);
+static LIST_HEAD(ac97_controllers);
+
+static struct bus_type ac97_bus_type;
+
+static inline struct ac97_controller*
+to_ac97_controller(struct device *ac97_adapter)
+{
+ return container_of(ac97_adapter, struct ac97_controller, adap);
+}
+
+static int ac97_unbound_ctrl_write(struct ac97_controller *adrv, int slot,
+ unsigned short reg, unsigned short val)
+{
+ return -ENODEV;
+}
+
+static int ac97_unbound_ctrl_read(struct ac97_controller *adrv, int slot,
+ unsigned short reg)
+{
+ return -ENODEV;
+}
+
+static const struct ac97_controller_ops ac97_unbound_ctrl_ops = {
+ .write = ac97_unbound_ctrl_write,
+ .read = ac97_unbound_ctrl_read,
+};
+
+static struct ac97_controller ac97_unbound_ctrl = {
+ .ops = &ac97_unbound_ctrl_ops,
+};
+
+static struct ac97_codec_device *
+ac97_codec_find(struct ac97_controller *ac97_ctrl, unsigned int codec_num)
+{
+ if (codec_num >= AC97_BUS_MAX_CODECS)
+ return ERR_PTR(-EINVAL);
+
+ return ac97_ctrl->codecs[codec_num];
+}
+
+static void ac97_codec_release(struct device *dev)
+{
+ struct ac97_codec_device *adev;
+ struct ac97_controller *ac97_ctrl;
+
+ adev = to_ac97_device(dev);
+ ac97_ctrl = adev->ac97_ctrl;
+ ac97_ctrl->codecs[adev->num] = NULL;
+ kfree(adev);
+}
+
+static int ac97_codec_add(struct ac97_controller *ac97_ctrl, int idx,
+ unsigned int vendor_id)
+{
+ struct ac97_codec_device *codec;
+ int ret;
+
+ codec = kzalloc(sizeof(*codec), GFP_KERNEL);
+ if (!codec)
+ return -ENOMEM;
+ ac97_ctrl->codecs[idx] = codec;
+ codec->vendor_id = vendor_id;
+ codec->dev.release = ac97_codec_release;
+ codec->dev.bus = &ac97_bus_type;
+ codec->dev.parent = &ac97_ctrl->adap;
+ codec->num = idx;
+ codec->ac97_ctrl = ac97_ctrl;
+
+ device_initialize(&codec->dev);
+ dev_set_name(&codec->dev, "%s:%u", dev_name(ac97_ctrl->parent), idx);
+
+ ret = device_add(&codec->dev);
+ if (ret)
+ goto err_free_codec;
+
+ return 0;
+err_free_codec:
+ put_device(&codec->dev);
+ kfree(codec);
+ ac97_ctrl->codecs[idx] = NULL;
+
+ return ret;
+}
+
+unsigned int snd_ac97_bus_scan_one(struct ac97_controller *adrv,
+ unsigned int codec_num)
+{
+ unsigned short vid1, vid2;
+ int ret;
+
+ ret = adrv->ops->read(adrv, codec_num, AC97_VENDOR_ID1);
+ vid1 = (ret & 0xffff);
+ if (ret < 0)
+ return 0;
+
+ ret = adrv->ops->read(adrv, codec_num, AC97_VENDOR_ID2);
+ vid2 = (ret & 0xffff);
+ if (ret < 0)
+ return 0;
+
+ dev_dbg(&adrv->adap, "%s(codec_num=%u): vendor_id=0x%08x\n",
+ __func__, codec_num, AC97_ID(vid1, vid2));
+ return AC97_ID(vid1, vid2);
+}
+
+static int ac97_bus_scan(struct ac97_controller *ac97_ctrl)
+{
+ int ret, i;
+ unsigned int vendor_id;
+
+ for (i = 0; i < AC97_BUS_MAX_CODECS; i++) {
+ if (ac97_codec_find(ac97_ctrl, i))
+ continue;
+ if (!(ac97_ctrl->slots_available & BIT(i)))
+ continue;
+ vendor_id = snd_ac97_bus_scan_one(ac97_ctrl, i);
+ if (!vendor_id)
+ continue;
+
+ ret = ac97_codec_add(ac97_ctrl, i, vendor_id);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int ac97_bus_reset(struct ac97_controller *ac97_ctrl)
+{
+ ac97_ctrl->ops->reset(ac97_ctrl);
+
+ return 0;
+}
+
+/**
+ * snd_ac97_codec_driver_register - register an AC97 codec driver
+ * @dev: AC97 driver codec to register
+ *
+ * Register an AC97 codec driver to the ac97 bus driver, aka. the AC97 digital
+ * controller.
+ *
+ * Returns 0 on success or error code
+ */
+int snd_ac97_codec_driver_register(struct ac97_codec_driver *drv)
+{
+ drv->driver.bus = &ac97_bus_type;
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(snd_ac97_codec_driver_register);
+
+/**
+ * snd_ac97_codec_driver_unregister - unregister an AC97 codec driver
+ * @dev: AC97 codec driver to unregister
+ *
+ * Unregister a previously registered ac97 codec driver.
+ */
+void snd_ac97_codec_driver_unregister(struct ac97_codec_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(snd_ac97_codec_driver_unregister);
+
+/**
+ * snd_ac97_codec_get_platdata - get platform_data
+ * @adev: the ac97 codec device
+ *
+ * For legacy platforms, in order to have platform_data in codec drivers
+ * available, while ac97 device are auto-created upon probe, this retrieves the
+ * platdata which was setup on ac97 controller registration.
+ *
+ * Returns the platform data pointer
+ */
+void *snd_ac97_codec_get_platdata(const struct ac97_codec_device *adev)
+{
+ struct ac97_controller *ac97_ctrl = adev->ac97_ctrl;
+
+ return ac97_ctrl->codecs_pdata[adev->num];
+}
+EXPORT_SYMBOL_GPL(snd_ac97_codec_get_platdata);
+
+static void ac97_ctrl_codecs_unregister(struct ac97_controller *ac97_ctrl)
+{
+ int i;
+
+ for (i = 0; i < AC97_BUS_MAX_CODECS; i++)
+ if (ac97_ctrl->codecs[i]) {
+ ac97_ctrl->codecs[i]->ac97_ctrl = &ac97_unbound_ctrl;
+ device_unregister(&ac97_ctrl->codecs[i]->dev);
+ }
+}
+
+static ssize_t cold_reset_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t len)
+{
+ struct ac97_controller *ac97_ctrl;
+
+ mutex_lock(&ac97_controllers_mutex);
+ ac97_ctrl = to_ac97_controller(dev);
+ ac97_ctrl->ops->reset(ac97_ctrl);
+ mutex_unlock(&ac97_controllers_mutex);
+ return len;
+}
+static DEVICE_ATTR_WO(cold_reset);
+
+static ssize_t warm_reset_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t len)
+{
+ struct ac97_controller *ac97_ctrl;
+
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&ac97_controllers_mutex);
+ ac97_ctrl = to_ac97_controller(dev);
+ ac97_ctrl->ops->warm_reset(ac97_ctrl);
+ mutex_unlock(&ac97_controllers_mutex);
+ return len;
+}
+static DEVICE_ATTR_WO(warm_reset);
+
+static struct attribute *ac97_controller_device_attrs[] = {
+ &dev_attr_cold_reset.attr,
+ &dev_attr_warm_reset.attr,
+ NULL
+};
+
+static struct attribute_group ac97_adapter_attr_group = {
+ .name = "ac97_operations",
+ .attrs = ac97_controller_device_attrs,
+};
+
+static const struct attribute_group *ac97_adapter_groups[] = {
+ &ac97_adapter_attr_group,
+ NULL,
+};
+
+static void ac97_del_adapter(struct ac97_controller *ac97_ctrl)
+{
+ mutex_lock(&ac97_controllers_mutex);
+ ac97_ctrl_codecs_unregister(ac97_ctrl);
+ list_del(&ac97_ctrl->controllers);
+ mutex_unlock(&ac97_controllers_mutex);
+
+ device_unregister(&ac97_ctrl->adap);
+}
+
+static void ac97_adapter_release(struct device *dev)
+{
+ struct ac97_controller *ac97_ctrl;
+
+ ac97_ctrl = to_ac97_controller(dev);
+ idr_remove(&ac97_adapter_idr, ac97_ctrl->nr);
+ dev_dbg(&ac97_ctrl->adap, "adapter unregistered by %s\n",
+ dev_name(ac97_ctrl->parent));
+}
+
+static const struct device_type ac97_adapter_type = {
+ .groups = ac97_adapter_groups,
+ .release = ac97_adapter_release,
+};
+
+static int ac97_add_adapter(struct ac97_controller *ac97_ctrl)
+{
+ int ret;
+
+ mutex_lock(&ac97_controllers_mutex);
+ ret = idr_alloc(&ac97_adapter_idr, ac97_ctrl, 0, 0, GFP_KERNEL);
+ ac97_ctrl->nr = ret;
+ if (ret >= 0) {
+ dev_set_name(&ac97_ctrl->adap, "ac97-%d", ret);
+ ac97_ctrl->adap.type = &ac97_adapter_type;
+ ac97_ctrl->adap.parent = ac97_ctrl->parent;
+ ret = device_register(&ac97_ctrl->adap);
+ if (ret)
+ put_device(&ac97_ctrl->adap);
+ }
+ if (!ret)
+ list_add(&ac97_ctrl->controllers, &ac97_controllers);
+ mutex_unlock(&ac97_controllers_mutex);
+
+ if (!ret)
+ dev_dbg(&ac97_ctrl->adap, "adapter registered by %s\n",
+ dev_name(ac97_ctrl->parent));
+ return ret;
+}
+
+/**
+ * snd_ac97_controller_register - register an ac97 controller
+ * @ops: the ac97 bus operations
+ * @dev: the device providing the ac97 DC function
+ * @slots_available: mask of the ac97 codecs that can be scanned and probed
+ * bit0 => codec 0, bit1 => codec 1 ... bit 3 => codec 3
+ *
+ * Register a digital controller which can control up to 4 ac97 codecs. This is
+ * the controller side of the AC97 AC-link, while the slave side are the codecs.
+ *
+ * Returns a valid controller upon success, negative pointer value upon error
+ */
+struct ac97_controller *snd_ac97_controller_register(
+ const struct ac97_controller_ops *ops, struct device *dev,
+ unsigned short slots_available, void **codecs_pdata)
+{
+ struct ac97_controller *ac97_ctrl;
+ int ret, i;
+
+ ac97_ctrl = kzalloc(sizeof(*ac97_ctrl), GFP_KERNEL);
+ if (!ac97_ctrl)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < AC97_BUS_MAX_CODECS && codecs_pdata; i++)
+ ac97_ctrl->codecs_pdata[i] = codecs_pdata[i];
+
+ ac97_ctrl->ops = ops;
+ ac97_ctrl->slots_available = slots_available;
+ ac97_ctrl->parent = dev;
+ ret = ac97_add_adapter(ac97_ctrl);
+
+ if (ret)
+ goto err;
+ ac97_bus_reset(ac97_ctrl);
+ ac97_bus_scan(ac97_ctrl);
+
+ return ac97_ctrl;
+err:
+ kfree(ac97_ctrl);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(snd_ac97_controller_register);
+
+/**
+ * snd_ac97_controller_unregister - unregister an ac97 controller
+ * @ac97_ctrl: the device previously provided to ac97_controller_register()
+ *
+ */
+void snd_ac97_controller_unregister(struct ac97_controller *ac97_ctrl)
+{
+ ac97_del_adapter(ac97_ctrl);
+}
+EXPORT_SYMBOL_GPL(snd_ac97_controller_unregister);
+
+#ifdef CONFIG_PM
+static int ac97_pm_runtime_suspend(struct device *dev)
+{
+ struct ac97_codec_device *codec = to_ac97_device(dev);
+ int ret = pm_generic_runtime_suspend(dev);
+
+ if (ret == 0 && dev->driver) {
+ if (pm_runtime_is_irq_safe(dev))
+ clk_disable(codec->clk);
+ else
+ clk_disable_unprepare(codec->clk);
+ }
+
+ return ret;
+}
+
+static int ac97_pm_runtime_resume(struct device *dev)
+{
+ struct ac97_codec_device *codec = to_ac97_device(dev);
+ int ret;
+
+ if (dev->driver) {
+ if (pm_runtime_is_irq_safe(dev))
+ ret = clk_enable(codec->clk);
+ else
+ ret = clk_prepare_enable(codec->clk);
+ if (ret)
+ return ret;
+ }
+
+ return pm_generic_runtime_resume(dev);
+}
+#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops ac97_pm = {
+ .suspend = pm_generic_suspend,
+ .resume = pm_generic_resume,
+ .freeze = pm_generic_freeze,
+ .thaw = pm_generic_thaw,
+ .poweroff = pm_generic_poweroff,
+ .restore = pm_generic_restore,
+ SET_RUNTIME_PM_OPS(
+ ac97_pm_runtime_suspend,
+ ac97_pm_runtime_resume,
+ NULL)
+};
+
+static int ac97_get_enable_clk(struct ac97_codec_device *adev)
+{
+ int ret;
+
+ adev->clk = clk_get(&adev->dev, "ac97_clk");
+ if (IS_ERR(adev->clk))
+ return PTR_ERR(adev->clk);
+
+ ret = clk_prepare_enable(adev->clk);
+ if (ret)
+ clk_put(adev->clk);
+
+ return ret;
+}
+
+static void ac97_put_disable_clk(struct ac97_codec_device *adev)
+{
+ clk_disable_unprepare(adev->clk);
+ clk_put(adev->clk);
+}
+
+static ssize_t vendor_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ac97_codec_device *codec = to_ac97_device(dev);
+
+ return sprintf(buf, "%08x", codec->vendor_id);
+}
+DEVICE_ATTR_RO(vendor_id);
+
+static struct attribute *ac97_dev_attrs[] = {
+ &dev_attr_vendor_id.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(ac97_dev);
+
+static int ac97_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct ac97_codec_device *adev = to_ac97_device(dev);
+ struct ac97_codec_driver *adrv = to_ac97_driver(drv);
+ const struct ac97_id *id = adrv->id_table;
+ int i = 0;
+
+ if (adev->vendor_id == 0x0 || adev->vendor_id == 0xffffffff)
+ return false;
+
+ do {
+ if (ac97_ids_match(id[i].id, adev->vendor_id, id[i].mask))
+ return true;
+ } while (id[i++].id);
+
+ return false;
+}
+
+static int ac97_bus_probe(struct device *dev)
+{
+ struct ac97_codec_device *adev = to_ac97_device(dev);
+ struct ac97_codec_driver *adrv = to_ac97_driver(dev->driver);
+ int ret;
+
+ ret = ac97_get_enable_clk(adev);
+ if (ret)
+ return ret;
+
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ ret = adrv->probe(adev);
+ if (ret == 0)
+ return 0;
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
+ ac97_put_disable_clk(adev);
+
+ return ret;
+}
+
+static int ac97_bus_remove(struct device *dev)
+{
+ struct ac97_codec_device *adev = to_ac97_device(dev);
+ struct ac97_codec_driver *adrv = to_ac97_driver(dev->driver);
+ int ret;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret)
+ return ret;
+
+ ret = adrv->remove(adev);
+ pm_runtime_put_noidle(dev);
+ if (ret == 0)
+ ac97_put_disable_clk(adev);
+
+ return ret;
+}
+
+static struct bus_type ac97_bus_type = {
+ .name = "ac97bus",
+ .dev_groups = ac97_dev_groups,
+ .match = ac97_bus_match,
+ .pm = &ac97_pm,
+ .probe = ac97_bus_probe,
+ .remove = ac97_bus_remove,
+};
+
+static int __init ac97_bus_init(void)
+{
+ return bus_register(&ac97_bus_type);
+}
+subsys_initcall(ac97_bus_init);
+
+static void __exit ac97_bus_exit(void)
+{
+ bus_unregister(&ac97_bus_type);
+}
+module_exit(ac97_bus_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>");
diff --git a/sound/ac97/codec.c b/sound/ac97/codec.c
new file mode 100644
index 000000000000..a835f03744bf
--- /dev/null
+++ b/sound/ac97/codec.c
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <sound/ac97_codec.h>
+#include <sound/ac97/codec.h>
+#include <sound/ac97/controller.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <sound/soc.h> /* For compat_ac97_* */
+
diff --git a/sound/ac97/snd_ac97_compat.c b/sound/ac97/snd_ac97_compat.c
new file mode 100644
index 000000000000..61544e0d8de4
--- /dev/null
+++ b/sound/ac97/snd_ac97_compat.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <sound/ac97/codec.h>
+#include <sound/ac97/compat.h>
+#include <sound/ac97/controller.h>
+#include <sound/soc.h>
+
+#include "ac97_core.h"
+
+static void compat_ac97_reset(struct snd_ac97 *ac97)
+{
+ struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
+ struct ac97_controller *actrl = adev->ac97_ctrl;
+
+ if (actrl->ops->reset)
+ actrl->ops->reset(actrl);
+}
+
+static void compat_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+ struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
+ struct ac97_controller *actrl = adev->ac97_ctrl;
+
+ if (actrl->ops->warm_reset)
+ actrl->ops->warm_reset(actrl);
+}
+
+static void compat_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+ unsigned short val)
+{
+ struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
+ struct ac97_controller *actrl = adev->ac97_ctrl;
+
+ actrl->ops->write(actrl, ac97->num, reg, val);
+}
+
+static unsigned short compat_ac97_read(struct snd_ac97 *ac97,
+ unsigned short reg)
+{
+ struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
+ struct ac97_controller *actrl = adev->ac97_ctrl;
+
+ return actrl->ops->read(actrl, ac97->num, reg);
+}
+
+static struct snd_ac97_bus_ops compat_snd_ac97_bus_ops = {
+ .reset = compat_ac97_reset,
+ .warm_reset = compat_ac97_warm_reset,
+ .write = compat_ac97_write,
+ .read = compat_ac97_read,
+};
+
+static struct snd_ac97_bus compat_soc_ac97_bus = {
+ .ops = &compat_snd_ac97_bus_ops,
+};
+
+struct snd_ac97 *snd_ac97_compat_alloc(struct ac97_codec_device *adev)
+{
+ struct snd_ac97 *ac97;
+
+ ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
+ if (ac97 == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ ac97->dev = adev->dev;
+ ac97->private_data = adev;
+ ac97->bus = &compat_soc_ac97_bus;
+ return ac97;
+}
+EXPORT_SYMBOL_GPL(snd_ac97_compat_alloc);
+
+void snd_ac97_compat_release(struct snd_ac97 *ac97)
+{
+ kfree(ac97);
+}
+EXPORT_SYMBOL_GPL(snd_ac97_compat_release);
+
+int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id,
+ unsigned int id_mask)
+{
+ struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
+ struct ac97_controller *actrl = adev->ac97_ctrl;
+ unsigned int scanned;
+
+ if (try_warm) {
+ compat_ac97_warm_reset(ac97);
+ scanned = snd_ac97_bus_scan_one(actrl, adev->num);
+ if (ac97_ids_match(scanned, adev->vendor_id, id_mask))
+ return 1;
+ }
+
+ compat_ac97_reset(ac97);
+ compat_ac97_warm_reset(ac97);
+ scanned = snd_ac97_bus_scan_one(actrl, adev->num);
+ if (ac97_ids_match(scanned, adev->vendor_id, id_mask))
+ return 0;
+
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(snd_ac97_reset);
diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c
index 39c3969ac1c7..5950a9e218d9 100644
--- a/sound/arm/pxa2xx-ac97-lib.c
+++ b/sound/arm/pxa2xx-ac97-lib.c
@@ -20,7 +20,6 @@
#include <linux/io.h>
#include <linux/gpio.h>
-#include <sound/ac97_codec.h>
#include <sound/pxa2xx-lib.h>
#include <mach/irqs.h>
@@ -46,38 +45,41 @@ extern void pxa27x_configure_ac97reset(int reset_gpio, bool to_gpio);
* 1 jiffy timeout if interrupt never comes).
*/
-unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
+int pxa2xx_ac97_read(int slot, unsigned short reg)
{
- unsigned short val = -1;
+ int val = -ENODEV;
volatile u32 *reg_addr;
+ if (slot > 0)
+ return -ENODEV;
+
mutex_lock(&car_mutex);
/* set up primary or secondary codec space */
if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS)
- reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE;
+ reg_addr = slot ? &SMC_REG_BASE : &PMC_REG_BASE;
else
- reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
+ reg_addr = slot ? &SAC_REG_BASE : &PAC_REG_BASE;
reg_addr += (reg >> 1);
/* start read access across the ac97 link */
GSR = GSR_CDONE | GSR_SDONE;
gsr_bits = 0;
- val = *reg_addr;
+ val = (*reg_addr & 0xffff);
if (reg == AC97_GPIO_STATUS)
goto out;
if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1) <= 0 &&
!((GSR | gsr_bits) & GSR_SDONE)) {
printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n",
__func__, reg, GSR | gsr_bits);
- val = -1;
+ val = -ETIMEDOUT;
goto out;
}
/* valid data now */
GSR = GSR_CDONE | GSR_SDONE;
gsr_bits = 0;
- val = *reg_addr;
+ val = (*reg_addr & 0xffff);
/* but we've just started another cycle... */
wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
@@ -86,29 +88,32 @@ out: mutex_unlock(&car_mutex);
}
EXPORT_SYMBOL_GPL(pxa2xx_ac97_read);
-void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
- unsigned short val)
+int pxa2xx_ac97_write(int slot, unsigned short reg, unsigned short val)
{
volatile u32 *reg_addr;
+ int ret = 0;
mutex_lock(&car_mutex);
/* set up primary or secondary codec space */
if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS)
- reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE;
+ reg_addr = slot ? &SMC_REG_BASE : &PMC_REG_BASE;
else
- reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
+ reg_addr = slot ? &SAC_REG_BASE : &PAC_REG_BASE;
reg_addr += (reg >> 1);
GSR = GSR_CDONE | GSR_SDONE;
gsr_bits = 0;
*reg_addr = val;
if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1) <= 0 &&
- !((GSR | gsr_bits) & GSR_CDONE))
+ !((GSR | gsr_bits) & GSR_CDONE)) {
printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n",
__func__, reg, GSR | gsr_bits);
+ ret = -EIO;
+ }
mutex_unlock(&car_mutex);
+ return ret;
}
EXPORT_SYMBOL_GPL(pxa2xx_ac97_write);
@@ -188,7 +193,7 @@ static inline void pxa_ac97_cold_pxa3xx(void)
}
#endif
-bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97)
+bool pxa2xx_ac97_try_warm_reset(void)
{
unsigned long gsr;
unsigned int timeout = 100;
@@ -225,7 +230,7 @@ bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97)
}
EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_warm_reset);
-bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97)
+bool pxa2xx_ac97_try_cold_reset(void)
{
unsigned long gsr;
unsigned int timeout = 1000;
@@ -263,7 +268,7 @@ bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97)
EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_cold_reset);
-void pxa2xx_ac97_finish_reset(struct snd_ac97 *ac97)
+void pxa2xx_ac97_finish_reset(void)
{
GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
index fbd5dad0c484..4bc244c40f80 100644
--- a/sound/arm/pxa2xx-ac97.c
+++ b/sound/arm/pxa2xx-ac97.c
@@ -29,19 +29,38 @@
#include "pxa2xx-pcm.h"
-static void pxa2xx_ac97_reset(struct snd_ac97 *ac97)
+static void pxa2xx_ac97_legacy_reset(struct snd_ac97 *ac97)
{
- if (!pxa2xx_ac97_try_cold_reset(ac97)) {
- pxa2xx_ac97_try_warm_reset(ac97);
- }
+ if (!pxa2xx_ac97_try_cold_reset())
+ pxa2xx_ac97_try_warm_reset();
+
+ pxa2xx_ac97_finish_reset();
+}
+
+static unsigned short pxa2xx_ac97_legacy_read(struct snd_ac97 *ac97,
+ unsigned short reg)
+{
+ int ret;
+
+ ret = pxa2xx_ac97_read(ac97->num, reg);
+ if (ret < 0)
+ return 0;
+ else
+ return (unsigned short)(ret & 0xffff);
+}
+
+static void pxa2xx_ac97_legacy_write(struct snd_ac97 *ac97,
+ unsigned short reg, unsigned short val)
+{
+ int __always_unused ret;
- pxa2xx_ac97_finish_reset(ac97);
+ ret = pxa2xx_ac97_write(ac97->num, reg, val);
}
static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
- .read = pxa2xx_ac97_read,
- .write = pxa2xx_ac97_write,
- .reset = pxa2xx_ac97_reset,
+ .read = pxa2xx_ac97_legacy_read,
+ .write = pxa2xx_ac97_legacy_write,
+ .reset = pxa2xx_ac97_legacy_reset,
};
static struct pxad_param pxa2xx_ac97_pcm_out_req = {
diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig
index 78187eb24f56..d5838402f667 100644
--- a/sound/soc/amd/Kconfig
+++ b/sound/soc/amd/Kconfig
@@ -2,3 +2,10 @@ config SND_SOC_AMD_ACP
tristate "AMD Audio Coprocessor support"
help
This option enables ACP DMA support on AMD platform.
+
+config SND_SOC_AMD_CZ_RT5645_MACH
+ tristate "AMD CZ support for RT5645"
+ select SND_SOC_RT5645
+ depends on SND_SOC_AMD_ACP && I2C
+ help
+ This option enables machine driver for rt5645.
diff --git a/sound/soc/amd/Makefile b/sound/soc/amd/Makefile
index 1a66ec0366b2..f07fd2e2870a 100644
--- a/sound/soc/amd/Makefile
+++ b/sound/soc/amd/Makefile
@@ -1,3 +1,5 @@
-snd-soc-acp-pcm-objs := acp-pcm-dma.o
+acp_audio_dma-objs := acp-pcm-dma.o
+snd-soc-acp-rt5645-mach-objs := acp-rt5645.o
-obj-$(CONFIG_SND_SOC_AMD_ACP) += snd-soc-acp-pcm.o
+obj-$(CONFIG_SND_SOC_AMD_ACP) += acp_audio_dma.o
+obj-$(CONFIG_SND_SOC_AMD_CZ_RT5645_MACH) += snd-soc-acp-rt5645-mach.o
diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c
index 08b1399d1da2..9f521a55d610 100644
--- a/sound/soc/amd/acp-pcm-dma.c
+++ b/sound/soc/amd/acp-pcm-dma.c
@@ -20,7 +20,7 @@
#include <linux/pm_runtime.h>
#include <sound/soc.h>
-
+#include <drm/amd_asic_type.h>
#include "acp.h"
#define PLAYBACK_MIN_NUM_PERIODS 2
@@ -35,6 +35,13 @@
#define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS)
#define MIN_BUFFER MAX_BUFFER
+#define ST_PLAYBACK_MAX_PERIOD_SIZE 8192
+#define ST_CAPTURE_MAX_PERIOD_SIZE ST_PLAYBACK_MAX_PERIOD_SIZE
+#define ST_MAX_BUFFER (ST_PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS)
+#define ST_MIN_BUFFER ST_MAX_BUFFER
+
+#define DRV_NAME "acp_audio_dma"
+
static const struct snd_pcm_hardware acp_pcm_hardware_playback = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP |
@@ -73,10 +80,42 @@ static const struct snd_pcm_hardware acp_pcm_hardware_capture = {
.periods_max = CAPTURE_MAX_NUM_PERIODS,
};
-struct audio_drv_data {
- struct snd_pcm_substream *play_stream;
- struct snd_pcm_substream *capture_stream;
- void __iomem *acp_mmio;
+static const struct snd_pcm_hardware acp_st_pcm_hardware_playback = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .buffer_bytes_max = ST_MAX_BUFFER,
+ .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
+ .period_bytes_max = ST_PLAYBACK_MAX_PERIOD_SIZE,
+ .periods_min = PLAYBACK_MIN_NUM_PERIODS,
+ .periods_max = PLAYBACK_MAX_NUM_PERIODS,
+};
+
+static const struct snd_pcm_hardware acp_st_pcm_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .buffer_bytes_max = ST_MAX_BUFFER,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = ST_CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
};
static u32 acp_reg_read(void __iomem *acp_mmio, u32 reg)
@@ -143,8 +182,8 @@ static void config_dma_descriptor_in_sram(void __iomem *acp_mmio,
* system memory <-> ACP SRAM
*/
static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio,
- u32 size, int direction,
- u32 pte_offset)
+ u32 size, int direction,
+ u32 pte_offset, u32 asic_type)
{
u16 i;
u16 dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH12;
@@ -154,24 +193,46 @@ static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio,
dmadscr[i].xfer_val = 0;
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH12 + i;
- dmadscr[i].dest = ACP_SHARED_RAM_BANK_1_ADDRESS +
- (size / 2) - (i * (size/2));
+ dmadscr[i].dest = ACP_SHARED_RAM_BANK_1_ADDRESS
+ + (i * (size/2));
dmadscr[i].src = ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS
+ (pte_offset * SZ_4K) + (i * (size/2));
- dmadscr[i].xfer_val |=
- (ACP_DMA_ATTRIBUTES_DAGB_ONION_TO_SHAREDMEM << 16) |
- (size / 2);
+ switch (asic_type) {
+ case CHIP_STONEY:
+ dmadscr[i].xfer_val |=
+ (ACP_DMA_ATTRIBUTES_DAGB_GARLIC_TO_SHAREDMEM << 16) |
+ (size / 2);
+ break;
+ default:
+ dmadscr[i].xfer_val |=
+ (ACP_DMA_ATTRIBUTES_DAGB_ONION_TO_SHAREDMEM << 16) |
+ (size / 2);
+ }
} else {
dma_dscr_idx = CAPTURE_START_DMA_DESCR_CH14 + i;
- dmadscr[i].src = ACP_SHARED_RAM_BANK_5_ADDRESS +
- (i * (size/2));
- dmadscr[i].dest = ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS
- + (pte_offset * SZ_4K) +
- (i * (size/2));
- dmadscr[i].xfer_val |=
- BIT(22) |
- (ACP_DMA_ATTRIBUTES_SHAREDMEM_TO_DAGB_ONION << 16) |
- (size / 2);
+ switch (asic_type) {
+ case CHIP_STONEY:
+ dmadscr[i].src = ACP_SHARED_RAM_BANK_3_ADDRESS +
+ (i * (size/2));
+ dmadscr[i].dest =
+ ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS +
+ (pte_offset * SZ_4K) + (i * (size/2));
+ dmadscr[i].xfer_val |=
+ BIT(22) |
+ (ACP_DMA_ATTRIBUTES_SHARED_MEM_TO_DAGB_GARLIC << 16) |
+ (size / 2);
+ break;
+ default:
+ dmadscr[i].src = ACP_SHARED_RAM_BANK_5_ADDRESS +
+ (i * (size/2));
+ dmadscr[i].dest =
+ ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS +
+ (pte_offset * SZ_4K) + (i * (size/2));
+ dmadscr[i].xfer_val |=
+ BIT(22) |
+ (ACP_DMA_ATTRIBUTES_SHAREDMEM_TO_DAGB_ONION << 16) |
+ (size / 2);
+ }
}
config_dma_descriptor_in_sram(acp_mmio, dma_dscr_idx,
&dmadscr[i]);
@@ -192,7 +253,8 @@ static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio,
* ACP SRAM <-> I2S
*/
static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio,
- u32 size, int direction)
+ u32 size, int direction,
+ u32 asic_type)
{
u16 i;
@@ -213,8 +275,17 @@ static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio,
dma_dscr_idx = CAPTURE_START_DMA_DESCR_CH15 + i;
/* dmadscr[i].src is unused by hardware. */
dmadscr[i].src = 0;
- dmadscr[i].dest = ACP_SHARED_RAM_BANK_5_ADDRESS +
+ switch (asic_type) {
+ case CHIP_STONEY:
+ dmadscr[i].dest =
+ ACP_SHARED_RAM_BANK_3_ADDRESS +
(i * (size / 2));
+ break;
+ default:
+ dmadscr[i].dest =
+ ACP_SHARED_RAM_BANK_5_ADDRESS +
+ (i * (size / 2));
+ }
dmadscr[i].xfer_val |= BIT(22) |
(FROM_ACP_I2S_1 << 16) | (size / 2);
}
@@ -270,7 +341,8 @@ static void acp_pte_config(void __iomem *acp_mmio, struct page *pg,
}
static void config_acp_dma(void __iomem *acp_mmio,
- struct audio_substream_data *audio_config)
+ struct audio_substream_data *audio_config,
+ u32 asic_type)
{
u32 pte_offset;
@@ -284,11 +356,11 @@ static void config_acp_dma(void __iomem *acp_mmio,
/* Configure System memory <-> ACP SRAM DMA descriptors */
set_acp_sysmem_dma_descriptors(acp_mmio, audio_config->size,
- audio_config->direction, pte_offset);
+ audio_config->direction, pte_offset, asic_type);
/* Configure ACP SRAM <-> I2S DMA descriptors */
set_acp_to_i2s_dma_descriptors(acp_mmio, audio_config->size,
- audio_config->direction);
+ audio_config->direction, asic_type);
}
/* Start a given DMA channel transfer */
@@ -425,7 +497,7 @@ static void acp_set_sram_bank_state(void __iomem *acp_mmio, u16 bank,
}
/* Initialize and bring ACP hardware to default state. */
-static int acp_init(void __iomem *acp_mmio)
+static int acp_init(void __iomem *acp_mmio, u32 asic_type)
{
u16 bank;
u32 val, count, sram_pte_offset;
@@ -499,10 +571,21 @@ static int acp_init(void __iomem *acp_mmio)
/* When ACP_TILE_P1 is turned on, all SRAM banks get turned on.
* Now, turn off all of them. This can't be done in 'poweron' of
* ACP pm domain, as this requires ACP to be initialized.
+ * For Stoney, Memory gating is disabled,i.e SRAM Banks
+ * won't be turned off. The default state for SRAM banks is ON.
+ * Setting SRAM bank state code skipped for STONEY platform.
*/
- for (bank = 1; bank < 48; bank++)
- acp_set_sram_bank_state(acp_mmio, bank, false);
+ if (asic_type != CHIP_STONEY) {
+ for (bank = 1; bank < 48; bank++)
+ acp_set_sram_bank_state(acp_mmio, bank, false);
+ }
+ /* Stoney supports 16bit resolution */
+ if (asic_type == CHIP_STONEY) {
+ val = acp_reg_read(acp_mmio, mmACP_I2S_16BIT_RESOLUTION_EN);
+ val |= 0x03;
+ acp_reg_write(val, acp_mmio, mmACP_I2S_16BIT_RESOLUTION_EN);
+ }
return 0;
}
@@ -572,9 +655,9 @@ static irqreturn_t dma_irq_handler(int irq, void *arg)
valid_irq = true;
if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_13) ==
PLAYBACK_START_DMA_DESCR_CH13)
- dscr_idx = PLAYBACK_START_DMA_DESCR_CH12;
- else
dscr_idx = PLAYBACK_END_DMA_DESCR_CH12;
+ else
+ dscr_idx = PLAYBACK_START_DMA_DESCR_CH12;
config_acp_dma_channel(acp_mmio, SYSRAM_TO_ACP_CH_NUM, dscr_idx,
1, 0);
acp_dma_start(acp_mmio, SYSRAM_TO_ACP_CH_NUM, false);
@@ -626,10 +709,23 @@ static int acp_dma_open(struct snd_pcm_substream *substream)
if (adata == NULL)
return -ENOMEM;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- runtime->hw = acp_pcm_hardware_playback;
- else
- runtime->hw = acp_pcm_hardware_capture;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (intr_data->asic_type) {
+ case CHIP_STONEY:
+ runtime->hw = acp_st_pcm_hardware_playback;
+ break;
+ default:
+ runtime->hw = acp_pcm_hardware_playback;
+ }
+ } else {
+ switch (intr_data->asic_type) {
+ case CHIP_STONEY:
+ runtime->hw = acp_st_pcm_hardware_capture;
+ break;
+ default:
+ runtime->hw = acp_pcm_hardware_capture;
+ }
+ }
ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
@@ -652,14 +748,22 @@ static int acp_dma_open(struct snd_pcm_substream *substream)
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
intr_data->play_stream = substream;
- for (bank = 1; bank <= 4; bank++)
- acp_set_sram_bank_state(intr_data->acp_mmio, bank,
- true);
+ /* For Stoney, Memory gating is disabled,i.e SRAM Banks
+ * won't be turned off. The default state for SRAM banks is ON.
+ * Setting SRAM bank state code skipped for STONEY platform.
+ */
+ if (intr_data->asic_type != CHIP_STONEY) {
+ for (bank = 1; bank <= 4; bank++)
+ acp_set_sram_bank_state(intr_data->acp_mmio,
+ bank, true);
+ }
} else {
intr_data->capture_stream = substream;
- for (bank = 5; bank <= 8; bank++)
- acp_set_sram_bank_state(intr_data->acp_mmio, bank,
- true);
+ if (intr_data->asic_type != CHIP_STONEY) {
+ for (bank = 5; bank <= 8; bank++)
+ acp_set_sram_bank_state(intr_data->acp_mmio,
+ bank, true);
+ }
}
return 0;
@@ -673,6 +777,8 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream,
struct page *pg;
struct snd_pcm_runtime *runtime;
struct audio_substream_data *rtd;
+ struct snd_soc_pcm_runtime *prtd = substream->private_data;
+ struct audio_drv_data *adata = dev_get_drvdata(prtd->platform->dev);
runtime = substream->runtime;
rtd = runtime->private_data;
@@ -700,7 +806,7 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream,
rtd->num_of_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
rtd->direction = substream->stream;
- config_acp_dma(rtd->acp_mmio, rtd);
+ config_acp_dma(rtd->acp_mmio, rtd, adata->asic_type);
status = 0;
} else {
status = -ENOMEM;
@@ -713,40 +819,48 @@ static int acp_dma_hw_free(struct snd_pcm_substream *substream)
return snd_pcm_lib_free_pages(substream);
}
+static u64 acp_get_byte_count(void __iomem *acp_mmio, int stream)
+{
+ union acp_dma_count playback_dma_count;
+ union acp_dma_count capture_dma_count;
+ u64 bytescount = 0;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ playback_dma_count.bcount.high = acp_reg_read(acp_mmio,
+ mmACP_I2S_TRANSMIT_BYTE_CNT_HIGH);
+ playback_dma_count.bcount.low = acp_reg_read(acp_mmio,
+ mmACP_I2S_TRANSMIT_BYTE_CNT_LOW);
+ bytescount = playback_dma_count.bytescount;
+ } else {
+ capture_dma_count.bcount.high = acp_reg_read(acp_mmio,
+ mmACP_I2S_RECEIVED_BYTE_CNT_HIGH);
+ capture_dma_count.bcount.low = acp_reg_read(acp_mmio,
+ mmACP_I2S_RECEIVED_BYTE_CNT_LOW);
+ bytescount = capture_dma_count.bytescount;
+ }
+ return bytescount;
+}
+
static snd_pcm_uframes_t acp_dma_pointer(struct snd_pcm_substream *substream)
{
- u16 dscr;
- u32 mul, dma_config, period_bytes;
+ u32 buffersize;
u32 pos = 0;
+ u64 bytescount = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
struct audio_substream_data *rtd = runtime->private_data;
- period_bytes = frames_to_bytes(runtime, runtime->period_size);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dscr = acp_reg_read(rtd->acp_mmio, mmACP_DMA_CUR_DSCR_13);
+ buffersize = frames_to_bytes(runtime, runtime->buffer_size);
+ bytescount = acp_get_byte_count(rtd->acp_mmio, substream->stream);
- if (dscr == PLAYBACK_START_DMA_DESCR_CH13)
- mul = 0;
- else
- mul = 1;
- pos = (mul * period_bytes);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (bytescount > rtd->renderbytescount)
+ bytescount = bytescount - rtd->renderbytescount;
} else {
- dma_config = acp_reg_read(rtd->acp_mmio, mmACP_DMA_CNTL_14);
- if (dma_config != 0) {
- dscr = acp_reg_read(rtd->acp_mmio,
- mmACP_DMA_CUR_DSCR_14);
- if (dscr == CAPTURE_START_DMA_DESCR_CH14)
- mul = 1;
- else
- mul = 2;
- pos = (mul * period_bytes);
- }
-
- if (pos >= (2 * period_bytes))
- pos = 0;
-
+ if (bytescount > rtd->capturebytescount)
+ bytescount = bytescount - rtd->capturebytescount;
}
+ pos = do_div(bytescount, buffersize);
return bytes_to_frames(runtime, pos);
}
@@ -768,23 +882,6 @@ static int acp_dma_prepare(struct snd_pcm_substream *substream)
config_acp_dma_channel(rtd->acp_mmio, ACP_TO_I2S_DMA_CH_NUM,
PLAYBACK_START_DMA_DESCR_CH13,
NUM_DSCRS_PER_CHANNEL, 0);
- /* Fill ACP SRAM (2 periods) with zeros from System RAM
- * which is zero-ed in hw_params
- */
- acp_dma_start(rtd->acp_mmio, SYSRAM_TO_ACP_CH_NUM, false);
-
- /* ACP SRAM (2 periods of buffer size) is intially filled with
- * zeros. Before rendering starts, 2nd half of SRAM will be
- * filled with valid audio data DMA'ed from first half of system
- * RAM and 1st half of SRAM will be filled with Zeros. This is
- * the initial scenario when redering starts from SRAM. Later
- * on, 2nd half of system memory will be DMA'ed to 1st half of
- * SRAM, 1st half of system memory will be DMA'ed to 2nd half of
- * SRAM in ping-pong way till rendering stops.
- */
- config_acp_dma_channel(rtd->acp_mmio, SYSRAM_TO_ACP_CH_NUM,
- PLAYBACK_START_DMA_DESCR_CH12,
- 1, 0);
} else {
config_acp_dma_channel(rtd->acp_mmio, ACP_TO_SYSRAM_CH_NUM,
CAPTURE_START_DMA_DESCR_CH14,
@@ -799,7 +896,8 @@ static int acp_dma_prepare(struct snd_pcm_substream *substream)
static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd)
{
int ret;
- u32 loops = 1000;
+ u32 loops = 4000;
+ u64 bytescount = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *prtd = substream->private_data;
@@ -811,7 +909,11 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
+ bytescount = acp_get_byte_count(rtd->acp_mmio,
+ substream->stream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (rtd->renderbytescount == 0)
+ rtd->renderbytescount = bytescount;
acp_dma_start(rtd->acp_mmio,
SYSRAM_TO_ACP_CH_NUM, false);
while (acp_reg_read(rtd->acp_mmio, mmACP_DMA_CH_STS) &
@@ -828,6 +930,8 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd)
ACP_TO_I2S_DMA_CH_NUM, true);
} else {
+ if (rtd->capturebytescount == 0)
+ rtd->capturebytescount = bytescount;
acp_dma_start(rtd->acp_mmio,
I2S_TO_ACP_DMA_CH_NUM, true);
}
@@ -841,12 +945,15 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd)
* channels will stopped automatically after its transfer
* completes : SYSRAM_TO_ACP_CH_NUM / ACP_TO_SYSRAM_CH_NUM
*/
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
ret = acp_dma_stop(rtd->acp_mmio,
ACP_TO_I2S_DMA_CH_NUM);
- else
+ rtd->renderbytescount = 0;
+ } else {
ret = acp_dma_stop(rtd->acp_mmio,
I2S_TO_ACP_DMA_CH_NUM);
+ rtd->capturebytescount = 0;
+ }
break;
default:
ret = -EINVAL;
@@ -857,10 +964,27 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd)
static int acp_dma_new(struct snd_soc_pcm_runtime *rtd)
{
- return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
+ int ret;
+ struct audio_drv_data *adata = dev_get_drvdata(rtd->platform->dev);
+
+ switch (adata->asic_type) {
+ case CHIP_STONEY:
+ ret = snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
+ SNDRV_DMA_TYPE_DEV,
+ NULL, ST_MIN_BUFFER,
+ ST_MAX_BUFFER);
+ break;
+ default:
+ ret = snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
SNDRV_DMA_TYPE_DEV,
NULL, MIN_BUFFER,
MAX_BUFFER);
+ break;
+ }
+ if (ret < 0)
+ dev_err(rtd->platform->dev,
+ "buffer preallocation failer error:%d\n", ret);
+ return ret;
}
static int acp_dma_close(struct snd_pcm_substream *substream)
@@ -875,14 +999,23 @@ static int acp_dma_close(struct snd_pcm_substream *substream)
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
adata->play_stream = NULL;
- for (bank = 1; bank <= 4; bank++)
- acp_set_sram_bank_state(adata->acp_mmio, bank,
- false);
- } else {
+ /* For Stoney, Memory gating is disabled,i.e SRAM Banks
+ * won't be turned off. The default state for SRAM banks is ON.
+ * Setting SRAM bank state code skipped for STONEY platform.
+ * added condition checks for Carrizo platform only
+ */
+ if (adata->asic_type != CHIP_STONEY) {
+ for (bank = 1; bank <= 4; bank++)
+ acp_set_sram_bank_state(adata->acp_mmio, bank,
+ false);
+ }
+ } else {
adata->capture_stream = NULL;
- for (bank = 5; bank <= 8; bank++)
- acp_set_sram_bank_state(adata->acp_mmio, bank,
- false);
+ if (adata->asic_type != CHIP_STONEY) {
+ for (bank = 5; bank <= 8; bank++)
+ acp_set_sram_bank_state(adata->acp_mmio, bank,
+ false);
+ }
}
/* Disable ACP irq, when the current stream is being closed and
@@ -916,6 +1049,7 @@ static int acp_audio_probe(struct platform_device *pdev)
int status;
struct audio_drv_data *audio_drv_data;
struct resource *res;
+ const u32 *pdata = pdev->dev.platform_data;
audio_drv_data = devm_kzalloc(&pdev->dev, sizeof(struct audio_drv_data),
GFP_KERNEL);
@@ -932,6 +1066,7 @@ static int acp_audio_probe(struct platform_device *pdev)
audio_drv_data->play_stream = NULL;
audio_drv_data->capture_stream = NULL;
+ audio_drv_data->asic_type = *pdata;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
@@ -949,7 +1084,7 @@ static int acp_audio_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, audio_drv_data);
/* Initialize the ACP */
- acp_init(audio_drv_data->acp_mmio);
+ acp_init(audio_drv_data->acp_mmio, audio_drv_data->asic_type);
status = snd_soc_register_platform(&pdev->dev, &acp_asoc_platform);
if (status != 0) {
@@ -980,21 +1115,31 @@ static int acp_pcm_resume(struct device *dev)
u16 bank;
struct audio_drv_data *adata = dev_get_drvdata(dev);
- acp_init(adata->acp_mmio);
+ acp_init(adata->acp_mmio, adata->asic_type);
if (adata->play_stream && adata->play_stream->runtime) {
- for (bank = 1; bank <= 4; bank++)
- acp_set_sram_bank_state(adata->acp_mmio, bank,
+ /* For Stoney, Memory gating is disabled,i.e SRAM Banks
+ * won't be turned off. The default state for SRAM banks is ON.
+ * Setting SRAM bank state code skipped for STONEY platform.
+ */
+ if (adata->asic_type != CHIP_STONEY) {
+ for (bank = 1; bank <= 4; bank++)
+ acp_set_sram_bank_state(adata->acp_mmio, bank,
true);
+ }
config_acp_dma(adata->acp_mmio,
- adata->play_stream->runtime->private_data);
+ adata->play_stream->runtime->private_data,
+ adata->asic_type);
}
if (adata->capture_stream && adata->capture_stream->runtime) {
- for (bank = 5; bank <= 8; bank++)
- acp_set_sram_bank_state(adata->acp_mmio, bank,
+ if (adata->asic_type != CHIP_STONEY) {
+ for (bank = 5; bank <= 8; bank++)
+ acp_set_sram_bank_state(adata->acp_mmio, bank,
true);
+ }
config_acp_dma(adata->acp_mmio,
- adata->capture_stream->runtime->private_data);
+ adata->capture_stream->runtime->private_data,
+ adata->asic_type);
}
acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB);
return 0;
@@ -1013,7 +1158,7 @@ static int acp_pcm_runtime_resume(struct device *dev)
{
struct audio_drv_data *adata = dev_get_drvdata(dev);
- acp_init(adata->acp_mmio);
+ acp_init(adata->acp_mmio, adata->asic_type);
acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB);
return 0;
}
@@ -1028,14 +1173,15 @@ static struct platform_driver acp_dma_driver = {
.probe = acp_audio_probe,
.remove = acp_audio_remove,
.driver = {
- .name = "acp_audio_dma",
+ .name = DRV_NAME,
.pm = &acp_pm_ops,
},
};
module_platform_driver(acp_dma_driver);
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com");
MODULE_DESCRIPTION("AMD ACP PCM Driver");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:acp-dma-audio");
+MODULE_ALIAS("platform:"DRV_NAME);
diff --git a/sound/soc/amd/acp-rt5645.c b/sound/soc/amd/acp-rt5645.c
new file mode 100644
index 000000000000..941aed6bb364
--- /dev/null
+++ b/sound/soc/amd/acp-rt5645.c
@@ -0,0 +1,199 @@
+/*
+ * Machine driver for AMD ACP Audio engine using Realtek RT5645 codec
+ *
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * This file is modified from rt288 machine driver
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ *
+ */
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+
+#include "../codecs/rt5645.h"
+
+#define CZ_PLAT_CLK 24000000
+
+static struct snd_soc_jack cz_jack;
+
+static int cz_aif1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT5645_PLL1_S_MCLK,
+ CZ_PLAT_CLK, params_rate(params) * 512);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_PLL1,
+ params_rate(params) * 512, SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int cz_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret;
+ struct snd_soc_card *card;
+ struct snd_soc_codec *codec;
+
+ codec = rtd->codec;
+ card = rtd->card;
+
+ ret = snd_soc_card_jack_new(card, "Headset Jack",
+ SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &cz_jack, NULL, 0);
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ rt5645_set_jack_detect(codec, &cz_jack, &cz_jack, &cz_jack);
+
+ return 0;
+}
+
+static struct snd_soc_ops cz_aif1_ops = {
+ .hw_params = cz_aif1_hw_params,
+};
+
+static struct snd_soc_dai_link cz_dai_rt5650[] = {
+ {
+ .name = "amd-rt5645-play",
+ .stream_name = "RT5645_AIF1",
+ .platform_name = "acp_audio_dma.0.auto",
+ .cpu_dai_name = "designware-i2s.1.auto",
+ .codec_dai_name = "rt5645-aif1",
+ .codec_name = "i2c-10EC5650:00",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM,
+ .init = cz_init,
+ .ops = &cz_aif1_ops,
+ },
+ {
+ .name = "amd-rt5645-cap",
+ .stream_name = "RT5645_AIF1",
+ .platform_name = "acp_audio_dma.0.auto",
+ .cpu_dai_name = "designware-i2s.2.auto",
+ .codec_dai_name = "rt5645-aif1",
+ .codec_name = "i2c-10EC5650:00",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM,
+ .ops = &cz_aif1_ops,
+ },
+};
+
+static const struct snd_soc_dapm_widget cz_widgets[] = {
+ SND_SOC_DAPM_HP("Headphones", NULL),
+ SND_SOC_DAPM_SPK("Speakers", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route cz_audio_route[] = {
+ {"Headphones", NULL, "HPOL"},
+ {"Headphones", NULL, "HPOR"},
+ {"RECMIXL", NULL, "Headset Mic"},
+ {"RECMIXR", NULL, "Headset Mic"},
+ {"Speakers", NULL, "SPOL"},
+ {"Speakers", NULL, "SPOR"},
+ {"DMIC L2", NULL, "Int Mic"},
+ {"DMIC R2", NULL, "Int Mic"},
+};
+
+static const struct snd_kcontrol_new cz_mc_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphones"),
+ SOC_DAPM_PIN_SWITCH("Speakers"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+};
+
+static struct snd_soc_card cz_card = {
+ .name = "acprt5650",
+ .owner = THIS_MODULE,
+ .dai_link = cz_dai_rt5650,
+ .num_links = ARRAY_SIZE(cz_dai_rt5650),
+ .dapm_widgets = cz_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cz_widgets),
+ .dapm_routes = cz_audio_route,
+ .num_dapm_routes = ARRAY_SIZE(cz_audio_route),
+ .controls = cz_mc_controls,
+ .num_controls = ARRAY_SIZE(cz_mc_controls),
+};
+
+static int cz_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct snd_soc_card *card;
+
+ card = &cz_card;
+ cz_card.dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ ret = devm_snd_soc_register_card(&pdev->dev, &cz_card);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "devm_snd_soc_register_card(%s) failed: %d\n",
+ cz_card.name, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static const struct acpi_device_id cz_audio_acpi_match[] = {
+ { "AMDI1002", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, cz_audio_acpi_match);
+
+static struct platform_driver cz_pcm_driver = {
+ .driver = {
+ .name = "cz-rt5645",
+ .acpi_match_table = ACPI_PTR(cz_audio_acpi_match),
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = cz_probe,
+};
+
+module_platform_driver(cz_pcm_driver);
+
+MODULE_AUTHOR("akshu.agrawal@amd.com");
+MODULE_DESCRIPTION("cz-rt5645 audio support");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp.h b/sound/soc/amd/acp.h
index 9d338216c5ae..ecb458935d1e 100644
--- a/sound/soc/amd/acp.h
+++ b/sound/soc/amd/acp.h
@@ -20,6 +20,7 @@
/* Capture SRAM address (as a source in dma descriptor) */
#define ACP_SHARED_RAM_BANK_5_ADDRESS 0x400A000
+#define ACP_SHARED_RAM_BANK_3_ADDRESS 0x4006000
#define ACP_DMA_RESET_TIME 10000
#define ACP_CLOCK_EN_TIME_OUT_VALUE 0x000000FF
@@ -68,6 +69,7 @@
#define CAPTURE_START_DMA_DESCR_CH15 6
#define CAPTURE_END_DMA_DESCR_CH15 7
+#define mmACP_I2S_16BIT_RESOLUTION_EN 0x5209
enum acp_dma_priority_level {
/* 0x0 Specifies the DMA channel is given normal priority */
ACP_DMA_PRIORITY_LEVEL_NORMAL = 0x0,
@@ -82,9 +84,26 @@ struct audio_substream_data {
u16 num_of_pages;
u16 direction;
uint64_t size;
+ u64 renderbytescount;
+ u64 capturebytescount;
void __iomem *acp_mmio;
};
+struct audio_drv_data {
+ struct snd_pcm_substream *play_stream;
+ struct snd_pcm_substream *capture_stream;
+ void __iomem *acp_mmio;
+ u32 asic_type;
+};
+
+union acp_dma_count {
+ struct {
+ u32 low;
+ u32 high;
+ } bcount;
+ u64 bytescount;
+};
+
enum {
ACP_TILE_P1 = 0,
ACP_TILE_P2,
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index a1149f6a8450..b3375e19598a 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -13,6 +13,7 @@
#include <linux/delay.h>
#include <linux/gcd.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -293,16 +294,99 @@ int arizona_init_gpio(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(arizona_init_gpio);
-int arizona_init_notifiers(struct snd_soc_codec *codec)
+int arizona_init_common(struct arizona *arizona)
{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct arizona *arizona = priv->arizona;
+ struct arizona_pdata *pdata = &arizona->pdata;
+ unsigned int val, mask;
+ int i;
BLOCKING_INIT_NOTIFIER_HEAD(&arizona->notifier);
+ for (i = 0; i < ARIZONA_MAX_OUTPUT; ++i) {
+ /* Default is 0 so noop with defaults */
+ if (pdata->out_mono[i])
+ val = ARIZONA_OUT1_MONO;
+ else
+ val = 0;
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8),
+ ARIZONA_OUT1_MONO, val);
+ }
+
+ for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) {
+ if (pdata->spk_mute[i])
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_PDM_SPK1_CTRL_1 + (i * 2),
+ ARIZONA_SPK1_MUTE_ENDIAN_MASK |
+ ARIZONA_SPK1_MUTE_SEQ1_MASK,
+ pdata->spk_mute[i]);
+
+ if (pdata->spk_fmt[i])
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_PDM_SPK1_CTRL_2 + (i * 2),
+ ARIZONA_SPK1_FMT_MASK,
+ pdata->spk_fmt[i]);
+ }
+
+ for (i = 0; i < ARIZONA_MAX_INPUT; i++) {
+ /* Default for both is 0 so noop with defaults */
+ val = pdata->dmic_ref[i] << ARIZONA_IN1_DMIC_SUP_SHIFT;
+ if (pdata->inmode[i] & ARIZONA_INMODE_DMIC)
+ val |= 1 << ARIZONA_IN1_MODE_SHIFT;
+
+ switch (arizona->type) {
+ case WM8998:
+ case WM1814:
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_ADC_DIGITAL_VOLUME_1L + (i * 8),
+ ARIZONA_IN1L_SRC_SE_MASK,
+ (pdata->inmode[i] & ARIZONA_INMODE_SE)
+ << ARIZONA_IN1L_SRC_SE_SHIFT);
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_ADC_DIGITAL_VOLUME_1R + (i * 8),
+ ARIZONA_IN1R_SRC_SE_MASK,
+ (pdata->inmode[i] & ARIZONA_INMODE_SE)
+ << ARIZONA_IN1R_SRC_SE_SHIFT);
+
+ mask = ARIZONA_IN1_DMIC_SUP_MASK |
+ ARIZONA_IN1_MODE_MASK;
+ break;
+ default:
+ if (pdata->inmode[i] & ARIZONA_INMODE_SE)
+ val |= 1 << ARIZONA_IN1_SINGLE_ENDED_SHIFT;
+
+ mask = ARIZONA_IN1_DMIC_SUP_MASK |
+ ARIZONA_IN1_MODE_MASK |
+ ARIZONA_IN1_SINGLE_ENDED_MASK;
+ break;
+ }
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_IN1L_CONTROL + (i * 8),
+ mask, val);
+ }
+
return 0;
}
-EXPORT_SYMBOL_GPL(arizona_init_notifiers);
+EXPORT_SYMBOL_GPL(arizona_init_common);
+
+int arizona_init_vol_limit(struct arizona *arizona)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(arizona->pdata.out_vol_limit); ++i) {
+ if (arizona->pdata.out_vol_limit[i])
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_DAC_VOLUME_LIMIT_1L + i * 4,
+ ARIZONA_OUT1L_VOL_LIM_MASK,
+ arizona->pdata.out_vol_limit[i]);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_vol_limit);
const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
"None",
@@ -2695,6 +2779,80 @@ int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_GPL(arizona_lhpf_coeff_put);
+int arizona_of_get_audio_pdata(struct arizona *arizona)
+{
+ struct arizona_pdata *pdata = &arizona->pdata;
+ struct device_node *np = arizona->dev->of_node;
+ struct property *prop;
+ const __be32 *cur;
+ u32 val;
+ u32 pdm_val[ARIZONA_MAX_PDM_SPK];
+ int ret;
+ int count = 0;
+
+ count = 0;
+ of_property_for_each_u32(np, "wlf,inmode", prop, cur, val) {
+ if (count == ARRAY_SIZE(pdata->inmode))
+ break;
+
+ pdata->inmode[count] = val;
+ count++;
+ }
+
+ count = 0;
+ of_property_for_each_u32(np, "wlf,dmic-ref", prop, cur, val) {
+ if (count == ARRAY_SIZE(pdata->dmic_ref))
+ break;
+
+ pdata->dmic_ref[count] = val;
+ count++;
+ }
+
+ count = 0;
+ of_property_for_each_u32(np, "wlf,out-mono", prop, cur, val) {
+ if (count == ARRAY_SIZE(pdata->out_mono))
+ break;
+
+ pdata->out_mono[count] = !!val;
+ count++;
+ }
+
+ count = 0;
+ of_property_for_each_u32(np, "wlf,max-channels-clocked", prop, cur, val) {
+ if (count == ARRAY_SIZE(pdata->max_channels_clocked))
+ break;
+
+ pdata->max_channels_clocked[count] = val;
+ count++;
+ }
+
+ count = 0;
+ of_property_for_each_u32(np, "wlf,out-volume-limit", prop, cur, val) {
+ if (count == ARRAY_SIZE(pdata->out_vol_limit))
+ break;
+
+ pdata->out_vol_limit[count] = val;
+ count++;
+ }
+
+ ret = of_property_read_u32_array(np, "wlf,spk-fmt",
+ pdm_val, ARRAY_SIZE(pdm_val));
+
+ if (ret >= 0)
+ for (count = 0; count < ARRAY_SIZE(pdata->spk_fmt); ++count)
+ pdata->spk_fmt[count] = pdm_val[count];
+
+ ret = of_property_read_u32_array(np, "wlf,spk-mute",
+ pdm_val, ARRAY_SIZE(pdm_val));
+
+ if (ret >= 0)
+ for (count = 0; count < ARRAY_SIZE(pdata->spk_mute); ++count)
+ pdata->spk_mute[count] = pdm_val[count];
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_of_get_audio_pdata);
+
MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index 1822e3b3de80..dfdf6d8c9687 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -313,7 +313,9 @@ int arizona_set_fll(struct arizona_fll *fll, int source,
int arizona_init_spk(struct snd_soc_codec *codec);
int arizona_init_gpio(struct snd_soc_codec *codec);
int arizona_init_mono(struct snd_soc_codec *codec);
-int arizona_init_notifiers(struct snd_soc_codec *codec);
+
+int arizona_init_common(struct arizona *arizona);
+int arizona_init_vol_limit(struct arizona *arizona);
int arizona_init_spk_irqs(struct arizona *arizona);
int arizona_free_spk_irqs(struct arizona *arizona);
@@ -350,4 +352,6 @@ static inline int arizona_unregister_notifier(struct snd_soc_codec *codec,
return blocking_notifier_chain_unregister(&arizona->notifier, nb);
}
+int arizona_of_get_audio_pdata(struct arizona *arizona);
+
#endif
diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c
index e09fc8f037f1..94c0209977d0 100644
--- a/sound/soc/codecs/cs47l24.c
+++ b/sound/soc/codecs/cs47l24.c
@@ -1130,7 +1130,6 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)
arizona_init_gpio(codec);
arizona_init_mono(codec);
- arizona_init_notifiers(codec);
ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec);
if (ret)
@@ -1230,6 +1229,14 @@ static int cs47l24_probe(struct platform_device *pdev)
if (!cs47l24)
return -ENOMEM;
+ if (IS_ENABLED(CONFIG_OF)) {
+ if (!dev_get_platdata(arizona->dev)) {
+ ret = arizona_of_get_audio_pdata(arizona);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
platform_set_drvdata(pdev, cs47l24);
cs47l24->core.arizona = arizona;
@@ -1288,6 +1295,11 @@ static int cs47l24_probe(struct platform_device *pdev)
return ret;
}
+ arizona_init_common(arizona);
+
+ ret = arizona_init_vol_limit(arizona);
+ if (ret < 0)
+ goto err_dsp_irq;
ret = arizona_init_spk_irqs(arizona);
if (ret < 0)
goto err_dsp_irq;
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 72486bf072f2..4f0481d3c7a7 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -1951,7 +1951,6 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec)
return ret;
arizona_init_gpio(codec);
- arizona_init_notifiers(codec);
snd_soc_component_disable_pin(component, "HAPTICS");
@@ -2043,6 +2042,14 @@ static int wm5102_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, wm5102);
+ if (IS_ENABLED(CONFIG_OF)) {
+ if (!dev_get_platdata(arizona->dev)) {
+ ret = arizona_of_get_audio_pdata(arizona);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
mutex_init(&arizona->dac_comp_lock);
wm5102->core.arizona = arizona;
@@ -2098,6 +2105,11 @@ static int wm5102_probe(struct platform_device *pdev)
return ret;
}
+ arizona_init_common(arizona);
+
+ ret = arizona_init_vol_limit(arizona);
+ if (ret < 0)
+ goto err_dsp_irq;
ret = arizona_init_spk_irqs(arizona);
if (ret < 0)
goto err_dsp_irq;
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index 858a24fc28e8..6ed1e1f9ce51 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -2290,7 +2290,6 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
arizona_init_gpio(codec);
arizona_init_mono(codec);
- arizona_init_notifiers(codec);
for (i = 0; i < WM5110_NUM_ADSP; ++i) {
ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec);
@@ -2398,6 +2397,14 @@ static int wm5110_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, wm5110);
+ if (IS_ENABLED(CONFIG_OF)) {
+ if (!dev_get_platdata(arizona->dev)) {
+ ret = arizona_of_get_audio_pdata(arizona);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
wm5110->core.arizona = arizona;
wm5110->core.num_inputs = 8;
@@ -2454,6 +2461,11 @@ static int wm5110_probe(struct platform_device *pdev)
return ret;
}
+ arizona_init_common(arizona);
+
+ ret = arizona_init_vol_limit(arizona);
+ if (ret < 0)
+ goto err_dsp_irq;
ret = arizona_init_spk_irqs(arizona);
if (ret < 0)
goto err_dsp_irq;
diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c
index 49401a8aae64..77f512767273 100644
--- a/sound/soc/codecs/wm8997.c
+++ b/sound/soc/codecs/wm8997.c
@@ -1068,8 +1068,6 @@ static int wm8997_codec_probe(struct snd_soc_codec *codec)
if (ret < 0)
return ret;
- arizona_init_notifiers(codec);
-
snd_soc_component_disable_pin(component, "HAPTICS");
priv->core.arizona->dapm = dapm;
@@ -1136,6 +1134,14 @@ static int wm8997_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, wm8997);
+ if (IS_ENABLED(CONFIG_OF)) {
+ if (!dev_get_platdata(arizona->dev)) {
+ ret = arizona_of_get_audio_pdata(arizona);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
wm8997->core.arizona = arizona;
wm8997->core.num_inputs = 4;
@@ -1168,6 +1174,11 @@ static int wm8997_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
+ arizona_init_common(arizona);
+
+ ret = arizona_init_vol_limit(arizona);
+ if (ret < 0)
+ return ret;
ret = arizona_init_spk_irqs(arizona);
if (ret < 0)
return ret;
diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c
index 4b64bb46eb21..2d211dbe7422 100644
--- a/sound/soc/codecs/wm8998.c
+++ b/sound/soc/codecs/wm8998.c
@@ -1284,7 +1284,6 @@ static int wm8998_codec_probe(struct snd_soc_codec *codec)
return ret;
arizona_init_gpio(codec);
- arizona_init_notifiers(codec);
snd_soc_component_disable_pin(component, "HAPTICS");
@@ -1353,6 +1352,14 @@ static int wm8998_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, wm8998);
+ if (IS_ENABLED(CONFIG_OF)) {
+ if (!dev_get_platdata(arizona->dev)) {
+ ret = arizona_of_get_audio_pdata(arizona);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
wm8998->core.arizona = arizona;
wm8998->core.num_inputs = 3; /* IN1L, IN1R, IN2 */
@@ -1377,6 +1384,8 @@ static int wm8998_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
+ arizona_init_common(arizona);
+
ret = arizona_init_spk_irqs(arizona);
if (ret < 0)
return ret;
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index f49bf02e5ec2..803818aabee9 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -29,21 +29,41 @@
static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97)
{
- pxa2xx_ac97_try_warm_reset(ac97);
+ pxa2xx_ac97_try_warm_reset();
- pxa2xx_ac97_finish_reset(ac97);
+ pxa2xx_ac97_finish_reset();
}
static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97)
{
- pxa2xx_ac97_try_cold_reset(ac97);
+ pxa2xx_ac97_try_cold_reset();
- pxa2xx_ac97_finish_reset(ac97);
+ pxa2xx_ac97_finish_reset();
+}
+
+static unsigned short pxa2xx_ac97_legacy_read(struct snd_ac97 *ac97,
+ unsigned short reg)
+{
+ int ret;
+
+ ret = pxa2xx_ac97_read(ac97->num, reg);
+ if (ret < 0)
+ return 0;
+ else
+ return (unsigned short)(ret & 0xffff);
+}
+
+static void pxa2xx_ac97_legacy_write(struct snd_ac97 *ac97,
+ unsigned short reg, unsigned short val)
+{
+ int ret;
+
+ ret = pxa2xx_ac97_write(ac97->num, reg, val);
}
static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
- .read = pxa2xx_ac97_read,
- .write = pxa2xx_ac97_write,
+ .read = pxa2xx_ac97_legacy_read,
+ .write = pxa2xx_ac97_legacy_write,
.warm_reset = pxa2xx_ac97_warm_reset,
.reset = pxa2xx_ac97_cold_reset,
};