Add a common driver for the top block of the MediaTek audio subsystem.
This is a wrapper which manages resources for audio components.

Signed-off-by: Ryder Lee <[email protected]>
---
 drivers/mfd/Kconfig      |   9 ++++
 drivers/mfd/Makefile     |   2 +
 drivers/mfd/mtk-audsys.c | 138 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 149 insertions(+)
 create mode 100644 drivers/mfd/mtk-audsys.c

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 1d20a80..ea50b51 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -368,6 +368,15 @@ config MFD_MC13XXX_I2C
        help
          Select this if your MC13xxx is connected via an I2C bus.
 
+config MFD_MEDIATEK_AUDSYS
+       tristate "MediaTek audio subsystem interface"
+       select MDF_CORE
+       select REGMAP_MMIO
+       help
+         Select this if you have a audio subsystem in MediaTek SoC.
+         The audio subsystem has at least a clock driver part and some
+         audio components.
+
 config MFD_MXS_LRADC
        tristate "Freescale i.MX23/i.MX28 LRADC"
        depends on ARCH_MXS || COMPILE_TEST
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index d9474ad..3e20927 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -101,6 +101,8 @@ obj-$(CONFIG_MFD_MC13XXX)   += mc13xxx-core.o
 obj-$(CONFIG_MFD_MC13XXX_SPI)  += mc13xxx-spi.o
 obj-$(CONFIG_MFD_MC13XXX_I2C)  += mc13xxx-i2c.o
 
+obj-$(CONFIG_MFD_MEDIATEK_AUDSYS) += mtk-audsys.o
+
 obj-$(CONFIG_MFD_CORE)         += mfd-core.o
 
 obj-$(CONFIG_EZX_PCAP)         += ezx-pcap.o
diff --git a/drivers/mfd/mtk-audsys.c b/drivers/mfd/mtk-audsys.c
new file mode 100644
index 0000000..89399e1
--- /dev/null
+++ b/drivers/mfd/mtk-audsys.c
@@ -0,0 +1,138 @@
+/*
+ * Mediatek audio subsystem core driver
+ *
+ *  Copyright (c) 2017 MediaTek Inc.
+ *
+ * Author: Ryder Lee <[email protected]>
+ *
+ * For licencing details see kernel-base/COPYING
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define AUDSYS_MAX_CLK_NUM 3
+
+struct sys_dev {
+       struct device *dev;
+       struct regmap *regmap;
+       int clk_num;
+       struct clk *clks[];
+};
+
+static const struct regmap_config aud_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+       .max_register = 0x15e0,
+       .cache_type = REGCACHE_NONE,
+};
+
+static int mtk_subsys_enable(struct sys_dev *sys)
+{
+       struct device *dev = sys->dev;
+       struct clk *clk;
+       int i, ret;
+
+       for (i = 0; i < sys->clk_num; i++) {
+               clk = of_clk_get(dev->of_node, i);
+               if (IS_ERR(clk)) {
+                       if (PTR_ERR(clk) == -EPROBE_DEFER)
+                               return -EPROBE_DEFER;
+                       break;
+               }
+               sys->clks[i] = clk;
+       }
+
+       for (i = 0; i < sys->clk_num && sys->clks[i]; i++) {
+               ret = clk_prepare_enable(sys->clks[i]);
+               if (ret)
+                       goto err_enable_clks;
+       }
+
+       return 0;
+
+err_enable_clks:
+       while (--i >= 0)
+               clk_disable_unprepare(sys->clks[i]);
+
+       return ret;
+}
+
+static int mtk_subsys_probe(struct platform_device *pdev)
+{
+       struct sys_dev *sys;
+       struct resource *res;
+       void __iomem *mmio;
+       int num, ret;
+
+       num = (int)of_device_get_match_data(&pdev->dev);
+       if (!num)
+               return -EINVAL;
+
+       sys = devm_kzalloc(&pdev->dev, sizeof(*sys) +
+                          sizeof(struct clk *) * num, GFP_KERNEL);
+       if (!sys)
+               return -ENOMEM;
+
+       sys->clk_num = num;
+       sys->dev = &pdev->dev;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       mmio = devm_ioremap_resource(sys->dev, res);
+       if (IS_ERR(mmio))
+               return PTR_ERR(mmio);
+
+       sys->regmap = devm_regmap_init_mmio(sys->dev, mmio,
+                                           &aud_regmap_config);
+       if (IS_ERR(sys->regmap))
+               return PTR_ERR(sys->regmap);
+
+       platform_set_drvdata(pdev, sys);
+
+       /* Enable top level clocks */
+       ret = mtk_subsys_enable(sys);
+       if (ret)
+               return ret;
+
+       return devm_of_platform_populate(sys->dev);
+};
+
+static int mtk_subsys_remove(struct platform_device *pdev)
+{
+       struct sys_dev *sys = platform_get_drvdata(pdev);
+       int i;
+
+       for (i = sys->clk_num - 1; i >= 0; i--)
+               if (sys->clks[i])
+                       clk_disable_unprepare(sys->clks[i]);
+
+       return 0;
+}
+
+static const struct of_device_id of_match_audsys[] = {
+       {
+         .compatible = "mediatek,mt2701-audsys-core",
+         .data = (void *)AUDSYS_MAX_CLK_NUM,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(platform, of_match_audsys);
+
+static struct platform_driver audsys_drv = {
+       .probe = mtk_subsys_probe,
+       .remove = mtk_subsys_remove,
+       .driver = {
+               .name = "mediatek-audsys-core",
+               .of_match_table = of_match_ptr(of_match_audsys),
+       },
+};
+
+builtin_platform_driver(audsys_drv);
+
+MODULE_DESCRIPTION("Mediatek audio subsystem core driver");
+MODULE_LICENSE("GPL");
-- 
1.9.1

Reply via email to