18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// Copyright 2012 Freescale Semiconductor, Inc.
48c2ecf20Sopenharmony_ci// Copyright 2012 Linaro Ltd.
58c2ecf20Sopenharmony_ci// Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
68c2ecf20Sopenharmony_ci//
78c2ecf20Sopenharmony_ci// Initial development of this code was funded by
88c2ecf20Sopenharmony_ci// Phytec Messtechnik GmbH, https://www.phytec.de
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/clk.h>
118c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
128c2ecf20Sopenharmony_ci#include <linux/err.h>
138c2ecf20Sopenharmony_ci#include <linux/io.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/of.h>
168c2ecf20Sopenharmony_ci#include <linux/of_device.h>
178c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "imx-audmux.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define DRIVER_NAME "imx-audmux"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic struct clk *audmux_clk;
258c2ecf20Sopenharmony_cistatic void __iomem *audmux_base;
268c2ecf20Sopenharmony_cistatic u32 *regcache;
278c2ecf20Sopenharmony_cistatic u32 reg_max;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define IMX_AUDMUX_V2_PTCR(x)		((x) * 8)
308c2ecf20Sopenharmony_ci#define IMX_AUDMUX_V2_PDCR(x)		((x) * 8 + 4)
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
338c2ecf20Sopenharmony_cistatic struct dentry *audmux_debugfs_root;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* There is an annoying discontinuity in the SSI numbering with regard
368c2ecf20Sopenharmony_ci * to the Linux number of the devices */
378c2ecf20Sopenharmony_cistatic const char *audmux_port_string(int port)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	switch (port) {
408c2ecf20Sopenharmony_ci	case MX31_AUDMUX_PORT1_SSI0:
418c2ecf20Sopenharmony_ci		return "imx-ssi.0";
428c2ecf20Sopenharmony_ci	case MX31_AUDMUX_PORT2_SSI1:
438c2ecf20Sopenharmony_ci		return "imx-ssi.1";
448c2ecf20Sopenharmony_ci	case MX31_AUDMUX_PORT3_SSI_PINS_3:
458c2ecf20Sopenharmony_ci		return "SSI3";
468c2ecf20Sopenharmony_ci	case MX31_AUDMUX_PORT4_SSI_PINS_4:
478c2ecf20Sopenharmony_ci		return "SSI4";
488c2ecf20Sopenharmony_ci	case MX31_AUDMUX_PORT5_SSI_PINS_5:
498c2ecf20Sopenharmony_ci		return "SSI5";
508c2ecf20Sopenharmony_ci	case MX31_AUDMUX_PORT6_SSI_PINS_6:
518c2ecf20Sopenharmony_ci		return "SSI6";
528c2ecf20Sopenharmony_ci	default:
538c2ecf20Sopenharmony_ci		return "UNKNOWN";
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic ssize_t audmux_read_file(struct file *file, char __user *user_buf,
588c2ecf20Sopenharmony_ci				size_t count, loff_t *ppos)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	ssize_t ret;
618c2ecf20Sopenharmony_ci	char *buf;
628c2ecf20Sopenharmony_ci	uintptr_t port = (uintptr_t)file->private_data;
638c2ecf20Sopenharmony_ci	u32 pdcr, ptcr;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	if (audmux_clk) {
668c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(audmux_clk);
678c2ecf20Sopenharmony_ci		if (ret)
688c2ecf20Sopenharmony_ci			return ret;
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	ptcr = readl(audmux_base + IMX_AUDMUX_V2_PTCR(port));
728c2ecf20Sopenharmony_ci	pdcr = readl(audmux_base + IMX_AUDMUX_V2_PDCR(port));
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	if (audmux_clk)
758c2ecf20Sopenharmony_ci		clk_disable_unprepare(audmux_clk);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
788c2ecf20Sopenharmony_ci	if (!buf)
798c2ecf20Sopenharmony_ci		return -ENOMEM;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	ret = scnprintf(buf, PAGE_SIZE, "PDCR: %08x\nPTCR: %08x\n",
828c2ecf20Sopenharmony_ci		       pdcr, ptcr);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	if (ptcr & IMX_AUDMUX_V2_PTCR_TFSDIR)
858c2ecf20Sopenharmony_ci		ret += scnprintf(buf + ret, PAGE_SIZE - ret,
868c2ecf20Sopenharmony_ci				"TxFS output from %s, ",
878c2ecf20Sopenharmony_ci				audmux_port_string((ptcr >> 27) & 0x7));
888c2ecf20Sopenharmony_ci	else
898c2ecf20Sopenharmony_ci		ret += scnprintf(buf + ret, PAGE_SIZE - ret,
908c2ecf20Sopenharmony_ci				"TxFS input, ");
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (ptcr & IMX_AUDMUX_V2_PTCR_TCLKDIR)
938c2ecf20Sopenharmony_ci		ret += scnprintf(buf + ret, PAGE_SIZE - ret,
948c2ecf20Sopenharmony_ci				"TxClk output from %s",
958c2ecf20Sopenharmony_ci				audmux_port_string((ptcr >> 22) & 0x7));
968c2ecf20Sopenharmony_ci	else
978c2ecf20Sopenharmony_ci		ret += scnprintf(buf + ret, PAGE_SIZE - ret,
988c2ecf20Sopenharmony_ci				"TxClk input");
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	if (ptcr & IMX_AUDMUX_V2_PTCR_SYN) {
1038c2ecf20Sopenharmony_ci		ret += scnprintf(buf + ret, PAGE_SIZE - ret,
1048c2ecf20Sopenharmony_ci				"Port is symmetric");
1058c2ecf20Sopenharmony_ci	} else {
1068c2ecf20Sopenharmony_ci		if (ptcr & IMX_AUDMUX_V2_PTCR_RFSDIR)
1078c2ecf20Sopenharmony_ci			ret += scnprintf(buf + ret, PAGE_SIZE - ret,
1088c2ecf20Sopenharmony_ci					"RxFS output from %s, ",
1098c2ecf20Sopenharmony_ci					audmux_port_string((ptcr >> 17) & 0x7));
1108c2ecf20Sopenharmony_ci		else
1118c2ecf20Sopenharmony_ci			ret += scnprintf(buf + ret, PAGE_SIZE - ret,
1128c2ecf20Sopenharmony_ci					"RxFS input, ");
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci		if (ptcr & IMX_AUDMUX_V2_PTCR_RCLKDIR)
1158c2ecf20Sopenharmony_ci			ret += scnprintf(buf + ret, PAGE_SIZE - ret,
1168c2ecf20Sopenharmony_ci					"RxClk output from %s",
1178c2ecf20Sopenharmony_ci					audmux_port_string((ptcr >> 12) & 0x7));
1188c2ecf20Sopenharmony_ci		else
1198c2ecf20Sopenharmony_ci			ret += scnprintf(buf + ret, PAGE_SIZE - ret,
1208c2ecf20Sopenharmony_ci					"RxClk input");
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	ret += scnprintf(buf + ret, PAGE_SIZE - ret,
1248c2ecf20Sopenharmony_ci			"\nData received from %s\n",
1258c2ecf20Sopenharmony_ci			audmux_port_string((pdcr >> 13) & 0x7));
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	kfree(buf);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	return ret;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic const struct file_operations audmux_debugfs_fops = {
1358c2ecf20Sopenharmony_ci	.open = simple_open,
1368c2ecf20Sopenharmony_ci	.read = audmux_read_file,
1378c2ecf20Sopenharmony_ci	.llseek = default_llseek,
1388c2ecf20Sopenharmony_ci};
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic void audmux_debugfs_init(void)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	uintptr_t i;
1438c2ecf20Sopenharmony_ci	char buf[20];
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	audmux_debugfs_root = debugfs_create_dir("audmux", NULL);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	for (i = 0; i < MX31_AUDMUX_PORT7_SSI_PINS_7 + 1; i++) {
1488c2ecf20Sopenharmony_ci		snprintf(buf, sizeof(buf), "ssi%lu", i);
1498c2ecf20Sopenharmony_ci		debugfs_create_file(buf, 0444, audmux_debugfs_root,
1508c2ecf20Sopenharmony_ci				    (void *)i, &audmux_debugfs_fops);
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic void audmux_debugfs_remove(void)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	debugfs_remove_recursive(audmux_debugfs_root);
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci#else
1598c2ecf20Sopenharmony_cistatic inline void audmux_debugfs_init(void)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic inline void audmux_debugfs_remove(void)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci#endif
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic enum imx_audmux_type {
1698c2ecf20Sopenharmony_ci	IMX21_AUDMUX,
1708c2ecf20Sopenharmony_ci	IMX31_AUDMUX,
1718c2ecf20Sopenharmony_ci} audmux_type;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic const struct platform_device_id imx_audmux_ids[] = {
1748c2ecf20Sopenharmony_ci	{
1758c2ecf20Sopenharmony_ci		.name = "imx21-audmux",
1768c2ecf20Sopenharmony_ci		.driver_data = IMX21_AUDMUX,
1778c2ecf20Sopenharmony_ci	}, {
1788c2ecf20Sopenharmony_ci		.name = "imx31-audmux",
1798c2ecf20Sopenharmony_ci		.driver_data = IMX31_AUDMUX,
1808c2ecf20Sopenharmony_ci	}, {
1818c2ecf20Sopenharmony_ci		/* sentinel */
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci};
1848c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, imx_audmux_ids);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic const struct of_device_id imx_audmux_dt_ids[] = {
1878c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx21-audmux", .data = &imx_audmux_ids[0], },
1888c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx31-audmux", .data = &imx_audmux_ids[1], },
1898c2ecf20Sopenharmony_ci	{ /* sentinel */ }
1908c2ecf20Sopenharmony_ci};
1918c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, imx_audmux_dt_ids);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic const uint8_t port_mapping[] = {
1948c2ecf20Sopenharmony_ci	0x0, 0x4, 0x8, 0x10, 0x14, 0x1c,
1958c2ecf20Sopenharmony_ci};
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ciint imx_audmux_v1_configure_port(unsigned int port, unsigned int pcr)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	if (audmux_type != IMX21_AUDMUX)
2008c2ecf20Sopenharmony_ci		return -EINVAL;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (!audmux_base)
2038c2ecf20Sopenharmony_ci		return -ENOSYS;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (port >= ARRAY_SIZE(port_mapping))
2068c2ecf20Sopenharmony_ci		return -EINVAL;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	writel(pcr, audmux_base + port_mapping[port]);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	return 0;
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(imx_audmux_v1_configure_port);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ciint imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr,
2158c2ecf20Sopenharmony_ci		unsigned int pdcr)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	int ret;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (audmux_type != IMX31_AUDMUX)
2208c2ecf20Sopenharmony_ci		return -EINVAL;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	if (!audmux_base)
2238c2ecf20Sopenharmony_ci		return -ENOSYS;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	if (audmux_clk) {
2268c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(audmux_clk);
2278c2ecf20Sopenharmony_ci		if (ret)
2288c2ecf20Sopenharmony_ci			return ret;
2298c2ecf20Sopenharmony_ci	}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	writel(ptcr, audmux_base + IMX_AUDMUX_V2_PTCR(port));
2328c2ecf20Sopenharmony_ci	writel(pdcr, audmux_base + IMX_AUDMUX_V2_PDCR(port));
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (audmux_clk)
2358c2ecf20Sopenharmony_ci		clk_disable_unprepare(audmux_clk);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	return 0;
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(imx_audmux_v2_configure_port);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic int imx_audmux_parse_dt_defaults(struct platform_device *pdev,
2428c2ecf20Sopenharmony_ci		struct device_node *of_node)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct device_node *child;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	for_each_available_child_of_node(of_node, child) {
2478c2ecf20Sopenharmony_ci		unsigned int port;
2488c2ecf20Sopenharmony_ci		unsigned int ptcr = 0;
2498c2ecf20Sopenharmony_ci		unsigned int pdcr = 0;
2508c2ecf20Sopenharmony_ci		unsigned int pcr = 0;
2518c2ecf20Sopenharmony_ci		unsigned int val;
2528c2ecf20Sopenharmony_ci		int ret;
2538c2ecf20Sopenharmony_ci		int i = 0;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci		ret = of_property_read_u32(child, "fsl,audmux-port", &port);
2568c2ecf20Sopenharmony_ci		if (ret) {
2578c2ecf20Sopenharmony_ci			dev_warn(&pdev->dev, "Failed to get fsl,audmux-port of child node \"%pOF\"\n",
2588c2ecf20Sopenharmony_ci					child);
2598c2ecf20Sopenharmony_ci			continue;
2608c2ecf20Sopenharmony_ci		}
2618c2ecf20Sopenharmony_ci		if (!of_property_read_bool(child, "fsl,port-config")) {
2628c2ecf20Sopenharmony_ci			dev_warn(&pdev->dev, "child node \"%pOF\" does not have property fsl,port-config\n",
2638c2ecf20Sopenharmony_ci					child);
2648c2ecf20Sopenharmony_ci			continue;
2658c2ecf20Sopenharmony_ci		}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci		for (i = 0; (ret = of_property_read_u32_index(child,
2688c2ecf20Sopenharmony_ci					"fsl,port-config", i, &val)) == 0;
2698c2ecf20Sopenharmony_ci				++i) {
2708c2ecf20Sopenharmony_ci			if (audmux_type == IMX31_AUDMUX) {
2718c2ecf20Sopenharmony_ci				if (i % 2)
2728c2ecf20Sopenharmony_ci					pdcr |= val;
2738c2ecf20Sopenharmony_ci				else
2748c2ecf20Sopenharmony_ci					ptcr |= val;
2758c2ecf20Sopenharmony_ci			} else {
2768c2ecf20Sopenharmony_ci				pcr |= val;
2778c2ecf20Sopenharmony_ci			}
2788c2ecf20Sopenharmony_ci		}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		if (ret != -EOVERFLOW) {
2818c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "Failed to read u32 at index %d of child %pOF\n",
2828c2ecf20Sopenharmony_ci					i, child);
2838c2ecf20Sopenharmony_ci			continue;
2848c2ecf20Sopenharmony_ci		}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci		if (audmux_type == IMX31_AUDMUX) {
2878c2ecf20Sopenharmony_ci			if (i % 2) {
2888c2ecf20Sopenharmony_ci				dev_err(&pdev->dev, "One pdcr value is missing in child node %pOF\n",
2898c2ecf20Sopenharmony_ci						child);
2908c2ecf20Sopenharmony_ci				continue;
2918c2ecf20Sopenharmony_ci			}
2928c2ecf20Sopenharmony_ci			imx_audmux_v2_configure_port(port, ptcr, pdcr);
2938c2ecf20Sopenharmony_ci		} else {
2948c2ecf20Sopenharmony_ci			imx_audmux_v1_configure_port(port, pcr);
2958c2ecf20Sopenharmony_ci		}
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	return 0;
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic int imx_audmux_probe(struct platform_device *pdev)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	const struct of_device_id *of_id =
3048c2ecf20Sopenharmony_ci			of_match_device(imx_audmux_dt_ids, &pdev->dev);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	audmux_base = devm_platform_ioremap_resource(pdev, 0);
3078c2ecf20Sopenharmony_ci	if (IS_ERR(audmux_base))
3088c2ecf20Sopenharmony_ci		return PTR_ERR(audmux_base);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	audmux_clk = devm_clk_get(&pdev->dev, "audmux");
3118c2ecf20Sopenharmony_ci	if (IS_ERR(audmux_clk)) {
3128c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "cannot get clock: %ld\n",
3138c2ecf20Sopenharmony_ci				PTR_ERR(audmux_clk));
3148c2ecf20Sopenharmony_ci		audmux_clk = NULL;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	if (of_id)
3188c2ecf20Sopenharmony_ci		pdev->id_entry = of_id->data;
3198c2ecf20Sopenharmony_ci	audmux_type = pdev->id_entry->driver_data;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	switch (audmux_type) {
3228c2ecf20Sopenharmony_ci	case IMX31_AUDMUX:
3238c2ecf20Sopenharmony_ci		audmux_debugfs_init();
3248c2ecf20Sopenharmony_ci		reg_max = 14;
3258c2ecf20Sopenharmony_ci		break;
3268c2ecf20Sopenharmony_ci	case IMX21_AUDMUX:
3278c2ecf20Sopenharmony_ci		reg_max = 6;
3288c2ecf20Sopenharmony_ci		break;
3298c2ecf20Sopenharmony_ci	default:
3308c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "unsupported version!\n");
3318c2ecf20Sopenharmony_ci		return -EINVAL;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	regcache = devm_kzalloc(&pdev->dev, sizeof(u32) * reg_max, GFP_KERNEL);
3358c2ecf20Sopenharmony_ci	if (!regcache)
3368c2ecf20Sopenharmony_ci		return -ENOMEM;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	if (of_id)
3398c2ecf20Sopenharmony_ci		imx_audmux_parse_dt_defaults(pdev, pdev->dev.of_node);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	return 0;
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic int imx_audmux_remove(struct platform_device *pdev)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	if (audmux_type == IMX31_AUDMUX)
3478c2ecf20Sopenharmony_ci		audmux_debugfs_remove();
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	return 0;
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
3538c2ecf20Sopenharmony_cistatic int imx_audmux_suspend(struct device *dev)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	int i;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	clk_prepare_enable(audmux_clk);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	for (i = 0; i < reg_max; i++)
3608c2ecf20Sopenharmony_ci		regcache[i] = readl(audmux_base + i * 4);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	clk_disable_unprepare(audmux_clk);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	return 0;
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic int imx_audmux_resume(struct device *dev)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	int i;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	clk_prepare_enable(audmux_clk);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	for (i = 0; i < reg_max; i++)
3748c2ecf20Sopenharmony_ci		writel(regcache[i], audmux_base + i * 4);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	clk_disable_unprepare(audmux_clk);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	return 0;
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_cistatic const struct dev_pm_ops imx_audmux_pm = {
3838c2ecf20Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(imx_audmux_suspend, imx_audmux_resume)
3848c2ecf20Sopenharmony_ci};
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic struct platform_driver imx_audmux_driver = {
3878c2ecf20Sopenharmony_ci	.probe		= imx_audmux_probe,
3888c2ecf20Sopenharmony_ci	.remove		= imx_audmux_remove,
3898c2ecf20Sopenharmony_ci	.id_table	= imx_audmux_ids,
3908c2ecf20Sopenharmony_ci	.driver	= {
3918c2ecf20Sopenharmony_ci		.name	= DRIVER_NAME,
3928c2ecf20Sopenharmony_ci		.pm = &imx_audmux_pm,
3938c2ecf20Sopenharmony_ci		.of_match_table = imx_audmux_dt_ids,
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci};
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistatic int __init imx_audmux_init(void)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	return platform_driver_register(&imx_audmux_driver);
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_cisubsys_initcall(imx_audmux_init);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic void __exit imx_audmux_exit(void)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	platform_driver_unregister(&imx_audmux_driver);
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_cimodule_exit(imx_audmux_exit);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Freescale i.MX AUDMUX driver");
4108c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
4118c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
4128c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" DRIVER_NAME);
413