xref: /OK3568_Linux_fs/kernel/sound/soc/fsl/imx-audmux.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun //
3*4882a593Smuzhiyun // Copyright 2012 Freescale Semiconductor, Inc.
4*4882a593Smuzhiyun // Copyright 2012 Linaro Ltd.
5*4882a593Smuzhiyun // Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
6*4882a593Smuzhiyun //
7*4882a593Smuzhiyun // Initial development of this code was funded by
8*4882a593Smuzhiyun // Phytec Messtechnik GmbH, https://www.phytec.de
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include <linux/clk.h>
11*4882a593Smuzhiyun #include <linux/debugfs.h>
12*4882a593Smuzhiyun #include <linux/err.h>
13*4882a593Smuzhiyun #include <linux/io.h>
14*4882a593Smuzhiyun #include <linux/module.h>
15*4882a593Smuzhiyun #include <linux/of.h>
16*4882a593Smuzhiyun #include <linux/of_device.h>
17*4882a593Smuzhiyun #include <linux/platform_device.h>
18*4882a593Smuzhiyun #include <linux/slab.h>
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #include "imx-audmux.h"
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun #define DRIVER_NAME "imx-audmux"
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun static struct clk *audmux_clk;
25*4882a593Smuzhiyun static void __iomem *audmux_base;
26*4882a593Smuzhiyun static u32 *regcache;
27*4882a593Smuzhiyun static u32 reg_max;
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun #define IMX_AUDMUX_V2_PTCR(x)		((x) * 8)
30*4882a593Smuzhiyun #define IMX_AUDMUX_V2_PDCR(x)		((x) * 8 + 4)
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun #ifdef CONFIG_DEBUG_FS
33*4882a593Smuzhiyun static struct dentry *audmux_debugfs_root;
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun /* There is an annoying discontinuity in the SSI numbering with regard
36*4882a593Smuzhiyun  * to the Linux number of the devices */
audmux_port_string(int port)37*4882a593Smuzhiyun static const char *audmux_port_string(int port)
38*4882a593Smuzhiyun {
39*4882a593Smuzhiyun 	switch (port) {
40*4882a593Smuzhiyun 	case MX31_AUDMUX_PORT1_SSI0:
41*4882a593Smuzhiyun 		return "imx-ssi.0";
42*4882a593Smuzhiyun 	case MX31_AUDMUX_PORT2_SSI1:
43*4882a593Smuzhiyun 		return "imx-ssi.1";
44*4882a593Smuzhiyun 	case MX31_AUDMUX_PORT3_SSI_PINS_3:
45*4882a593Smuzhiyun 		return "SSI3";
46*4882a593Smuzhiyun 	case MX31_AUDMUX_PORT4_SSI_PINS_4:
47*4882a593Smuzhiyun 		return "SSI4";
48*4882a593Smuzhiyun 	case MX31_AUDMUX_PORT5_SSI_PINS_5:
49*4882a593Smuzhiyun 		return "SSI5";
50*4882a593Smuzhiyun 	case MX31_AUDMUX_PORT6_SSI_PINS_6:
51*4882a593Smuzhiyun 		return "SSI6";
52*4882a593Smuzhiyun 	default:
53*4882a593Smuzhiyun 		return "UNKNOWN";
54*4882a593Smuzhiyun 	}
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun 
audmux_read_file(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)57*4882a593Smuzhiyun static ssize_t audmux_read_file(struct file *file, char __user *user_buf,
58*4882a593Smuzhiyun 				size_t count, loff_t *ppos)
59*4882a593Smuzhiyun {
60*4882a593Smuzhiyun 	ssize_t ret;
61*4882a593Smuzhiyun 	char *buf;
62*4882a593Smuzhiyun 	uintptr_t port = (uintptr_t)file->private_data;
63*4882a593Smuzhiyun 	u32 pdcr, ptcr;
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 	if (audmux_clk) {
66*4882a593Smuzhiyun 		ret = clk_prepare_enable(audmux_clk);
67*4882a593Smuzhiyun 		if (ret)
68*4882a593Smuzhiyun 			return ret;
69*4882a593Smuzhiyun 	}
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 	ptcr = readl(audmux_base + IMX_AUDMUX_V2_PTCR(port));
72*4882a593Smuzhiyun 	pdcr = readl(audmux_base + IMX_AUDMUX_V2_PDCR(port));
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	if (audmux_clk)
75*4882a593Smuzhiyun 		clk_disable_unprepare(audmux_clk);
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
78*4882a593Smuzhiyun 	if (!buf)
79*4882a593Smuzhiyun 		return -ENOMEM;
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 	ret = scnprintf(buf, PAGE_SIZE, "PDCR: %08x\nPTCR: %08x\n",
82*4882a593Smuzhiyun 		       pdcr, ptcr);
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	if (ptcr & IMX_AUDMUX_V2_PTCR_TFSDIR)
85*4882a593Smuzhiyun 		ret += scnprintf(buf + ret, PAGE_SIZE - ret,
86*4882a593Smuzhiyun 				"TxFS output from %s, ",
87*4882a593Smuzhiyun 				audmux_port_string((ptcr >> 27) & 0x7));
88*4882a593Smuzhiyun 	else
89*4882a593Smuzhiyun 		ret += scnprintf(buf + ret, PAGE_SIZE - ret,
90*4882a593Smuzhiyun 				"TxFS input, ");
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	if (ptcr & IMX_AUDMUX_V2_PTCR_TCLKDIR)
93*4882a593Smuzhiyun 		ret += scnprintf(buf + ret, PAGE_SIZE - ret,
94*4882a593Smuzhiyun 				"TxClk output from %s",
95*4882a593Smuzhiyun 				audmux_port_string((ptcr >> 22) & 0x7));
96*4882a593Smuzhiyun 	else
97*4882a593Smuzhiyun 		ret += scnprintf(buf + ret, PAGE_SIZE - ret,
98*4882a593Smuzhiyun 				"TxClk input");
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 	ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 	if (ptcr & IMX_AUDMUX_V2_PTCR_SYN) {
103*4882a593Smuzhiyun 		ret += scnprintf(buf + ret, PAGE_SIZE - ret,
104*4882a593Smuzhiyun 				"Port is symmetric");
105*4882a593Smuzhiyun 	} else {
106*4882a593Smuzhiyun 		if (ptcr & IMX_AUDMUX_V2_PTCR_RFSDIR)
107*4882a593Smuzhiyun 			ret += scnprintf(buf + ret, PAGE_SIZE - ret,
108*4882a593Smuzhiyun 					"RxFS output from %s, ",
109*4882a593Smuzhiyun 					audmux_port_string((ptcr >> 17) & 0x7));
110*4882a593Smuzhiyun 		else
111*4882a593Smuzhiyun 			ret += scnprintf(buf + ret, PAGE_SIZE - ret,
112*4882a593Smuzhiyun 					"RxFS input, ");
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 		if (ptcr & IMX_AUDMUX_V2_PTCR_RCLKDIR)
115*4882a593Smuzhiyun 			ret += scnprintf(buf + ret, PAGE_SIZE - ret,
116*4882a593Smuzhiyun 					"RxClk output from %s",
117*4882a593Smuzhiyun 					audmux_port_string((ptcr >> 12) & 0x7));
118*4882a593Smuzhiyun 		else
119*4882a593Smuzhiyun 			ret += scnprintf(buf + ret, PAGE_SIZE - ret,
120*4882a593Smuzhiyun 					"RxClk input");
121*4882a593Smuzhiyun 	}
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	ret += scnprintf(buf + ret, PAGE_SIZE - ret,
124*4882a593Smuzhiyun 			"\nData received from %s\n",
125*4882a593Smuzhiyun 			audmux_port_string((pdcr >> 13) & 0x7));
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 	kfree(buf);
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 	return ret;
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun static const struct file_operations audmux_debugfs_fops = {
135*4882a593Smuzhiyun 	.open = simple_open,
136*4882a593Smuzhiyun 	.read = audmux_read_file,
137*4882a593Smuzhiyun 	.llseek = default_llseek,
138*4882a593Smuzhiyun };
139*4882a593Smuzhiyun 
audmux_debugfs_init(void)140*4882a593Smuzhiyun static void audmux_debugfs_init(void)
141*4882a593Smuzhiyun {
142*4882a593Smuzhiyun 	uintptr_t i;
143*4882a593Smuzhiyun 	char buf[20];
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	audmux_debugfs_root = debugfs_create_dir("audmux", NULL);
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 	for (i = 0; i < MX31_AUDMUX_PORT7_SSI_PINS_7 + 1; i++) {
148*4882a593Smuzhiyun 		snprintf(buf, sizeof(buf), "ssi%lu", i);
149*4882a593Smuzhiyun 		debugfs_create_file(buf, 0444, audmux_debugfs_root,
150*4882a593Smuzhiyun 				    (void *)i, &audmux_debugfs_fops);
151*4882a593Smuzhiyun 	}
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun 
audmux_debugfs_remove(void)154*4882a593Smuzhiyun static void audmux_debugfs_remove(void)
155*4882a593Smuzhiyun {
156*4882a593Smuzhiyun 	debugfs_remove_recursive(audmux_debugfs_root);
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun #else
audmux_debugfs_init(void)159*4882a593Smuzhiyun static inline void audmux_debugfs_init(void)
160*4882a593Smuzhiyun {
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun 
audmux_debugfs_remove(void)163*4882a593Smuzhiyun static inline void audmux_debugfs_remove(void)
164*4882a593Smuzhiyun {
165*4882a593Smuzhiyun }
166*4882a593Smuzhiyun #endif
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun static enum imx_audmux_type {
169*4882a593Smuzhiyun 	IMX21_AUDMUX,
170*4882a593Smuzhiyun 	IMX31_AUDMUX,
171*4882a593Smuzhiyun } audmux_type;
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun static const struct platform_device_id imx_audmux_ids[] = {
174*4882a593Smuzhiyun 	{
175*4882a593Smuzhiyun 		.name = "imx21-audmux",
176*4882a593Smuzhiyun 		.driver_data = IMX21_AUDMUX,
177*4882a593Smuzhiyun 	}, {
178*4882a593Smuzhiyun 		.name = "imx31-audmux",
179*4882a593Smuzhiyun 		.driver_data = IMX31_AUDMUX,
180*4882a593Smuzhiyun 	}, {
181*4882a593Smuzhiyun 		/* sentinel */
182*4882a593Smuzhiyun 	}
183*4882a593Smuzhiyun };
184*4882a593Smuzhiyun MODULE_DEVICE_TABLE(platform, imx_audmux_ids);
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun static const struct of_device_id imx_audmux_dt_ids[] = {
187*4882a593Smuzhiyun 	{ .compatible = "fsl,imx21-audmux", .data = &imx_audmux_ids[0], },
188*4882a593Smuzhiyun 	{ .compatible = "fsl,imx31-audmux", .data = &imx_audmux_ids[1], },
189*4882a593Smuzhiyun 	{ /* sentinel */ }
190*4882a593Smuzhiyun };
191*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, imx_audmux_dt_ids);
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun static const uint8_t port_mapping[] = {
194*4882a593Smuzhiyun 	0x0, 0x4, 0x8, 0x10, 0x14, 0x1c,
195*4882a593Smuzhiyun };
196*4882a593Smuzhiyun 
imx_audmux_v1_configure_port(unsigned int port,unsigned int pcr)197*4882a593Smuzhiyun int imx_audmux_v1_configure_port(unsigned int port, unsigned int pcr)
198*4882a593Smuzhiyun {
199*4882a593Smuzhiyun 	if (audmux_type != IMX21_AUDMUX)
200*4882a593Smuzhiyun 		return -EINVAL;
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	if (!audmux_base)
203*4882a593Smuzhiyun 		return -ENOSYS;
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	if (port >= ARRAY_SIZE(port_mapping))
206*4882a593Smuzhiyun 		return -EINVAL;
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun 	writel(pcr, audmux_base + port_mapping[port]);
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	return 0;
211*4882a593Smuzhiyun }
212*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(imx_audmux_v1_configure_port);
213*4882a593Smuzhiyun 
imx_audmux_v2_configure_port(unsigned int port,unsigned int ptcr,unsigned int pdcr)214*4882a593Smuzhiyun int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr,
215*4882a593Smuzhiyun 		unsigned int pdcr)
216*4882a593Smuzhiyun {
217*4882a593Smuzhiyun 	int ret;
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	if (audmux_type != IMX31_AUDMUX)
220*4882a593Smuzhiyun 		return -EINVAL;
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 	if (!audmux_base)
223*4882a593Smuzhiyun 		return -ENOSYS;
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	if (audmux_clk) {
226*4882a593Smuzhiyun 		ret = clk_prepare_enable(audmux_clk);
227*4882a593Smuzhiyun 		if (ret)
228*4882a593Smuzhiyun 			return ret;
229*4882a593Smuzhiyun 	}
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 	writel(ptcr, audmux_base + IMX_AUDMUX_V2_PTCR(port));
232*4882a593Smuzhiyun 	writel(pdcr, audmux_base + IMX_AUDMUX_V2_PDCR(port));
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun 	if (audmux_clk)
235*4882a593Smuzhiyun 		clk_disable_unprepare(audmux_clk);
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	return 0;
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(imx_audmux_v2_configure_port);
240*4882a593Smuzhiyun 
imx_audmux_parse_dt_defaults(struct platform_device * pdev,struct device_node * of_node)241*4882a593Smuzhiyun static int imx_audmux_parse_dt_defaults(struct platform_device *pdev,
242*4882a593Smuzhiyun 		struct device_node *of_node)
243*4882a593Smuzhiyun {
244*4882a593Smuzhiyun 	struct device_node *child;
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	for_each_available_child_of_node(of_node, child) {
247*4882a593Smuzhiyun 		unsigned int port;
248*4882a593Smuzhiyun 		unsigned int ptcr = 0;
249*4882a593Smuzhiyun 		unsigned int pdcr = 0;
250*4882a593Smuzhiyun 		unsigned int pcr = 0;
251*4882a593Smuzhiyun 		unsigned int val;
252*4882a593Smuzhiyun 		int ret;
253*4882a593Smuzhiyun 		int i = 0;
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 		ret = of_property_read_u32(child, "fsl,audmux-port", &port);
256*4882a593Smuzhiyun 		if (ret) {
257*4882a593Smuzhiyun 			dev_warn(&pdev->dev, "Failed to get fsl,audmux-port of child node \"%pOF\"\n",
258*4882a593Smuzhiyun 					child);
259*4882a593Smuzhiyun 			continue;
260*4882a593Smuzhiyun 		}
261*4882a593Smuzhiyun 		if (!of_property_read_bool(child, "fsl,port-config")) {
262*4882a593Smuzhiyun 			dev_warn(&pdev->dev, "child node \"%pOF\" does not have property fsl,port-config\n",
263*4882a593Smuzhiyun 					child);
264*4882a593Smuzhiyun 			continue;
265*4882a593Smuzhiyun 		}
266*4882a593Smuzhiyun 
267*4882a593Smuzhiyun 		for (i = 0; (ret = of_property_read_u32_index(child,
268*4882a593Smuzhiyun 					"fsl,port-config", i, &val)) == 0;
269*4882a593Smuzhiyun 				++i) {
270*4882a593Smuzhiyun 			if (audmux_type == IMX31_AUDMUX) {
271*4882a593Smuzhiyun 				if (i % 2)
272*4882a593Smuzhiyun 					pdcr |= val;
273*4882a593Smuzhiyun 				else
274*4882a593Smuzhiyun 					ptcr |= val;
275*4882a593Smuzhiyun 			} else {
276*4882a593Smuzhiyun 				pcr |= val;
277*4882a593Smuzhiyun 			}
278*4882a593Smuzhiyun 		}
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 		if (ret != -EOVERFLOW) {
281*4882a593Smuzhiyun 			dev_err(&pdev->dev, "Failed to read u32 at index %d of child %pOF\n",
282*4882a593Smuzhiyun 					i, child);
283*4882a593Smuzhiyun 			continue;
284*4882a593Smuzhiyun 		}
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 		if (audmux_type == IMX31_AUDMUX) {
287*4882a593Smuzhiyun 			if (i % 2) {
288*4882a593Smuzhiyun 				dev_err(&pdev->dev, "One pdcr value is missing in child node %pOF\n",
289*4882a593Smuzhiyun 						child);
290*4882a593Smuzhiyun 				continue;
291*4882a593Smuzhiyun 			}
292*4882a593Smuzhiyun 			imx_audmux_v2_configure_port(port, ptcr, pdcr);
293*4882a593Smuzhiyun 		} else {
294*4882a593Smuzhiyun 			imx_audmux_v1_configure_port(port, pcr);
295*4882a593Smuzhiyun 		}
296*4882a593Smuzhiyun 	}
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 	return 0;
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun 
imx_audmux_probe(struct platform_device * pdev)301*4882a593Smuzhiyun static int imx_audmux_probe(struct platform_device *pdev)
302*4882a593Smuzhiyun {
303*4882a593Smuzhiyun 	const struct of_device_id *of_id =
304*4882a593Smuzhiyun 			of_match_device(imx_audmux_dt_ids, &pdev->dev);
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 	audmux_base = devm_platform_ioremap_resource(pdev, 0);
307*4882a593Smuzhiyun 	if (IS_ERR(audmux_base))
308*4882a593Smuzhiyun 		return PTR_ERR(audmux_base);
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	audmux_clk = devm_clk_get(&pdev->dev, "audmux");
311*4882a593Smuzhiyun 	if (IS_ERR(audmux_clk)) {
312*4882a593Smuzhiyun 		dev_dbg(&pdev->dev, "cannot get clock: %ld\n",
313*4882a593Smuzhiyun 				PTR_ERR(audmux_clk));
314*4882a593Smuzhiyun 		audmux_clk = NULL;
315*4882a593Smuzhiyun 	}
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun 	if (of_id)
318*4882a593Smuzhiyun 		pdev->id_entry = of_id->data;
319*4882a593Smuzhiyun 	audmux_type = pdev->id_entry->driver_data;
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun 	switch (audmux_type) {
322*4882a593Smuzhiyun 	case IMX31_AUDMUX:
323*4882a593Smuzhiyun 		audmux_debugfs_init();
324*4882a593Smuzhiyun 		reg_max = 14;
325*4882a593Smuzhiyun 		break;
326*4882a593Smuzhiyun 	case IMX21_AUDMUX:
327*4882a593Smuzhiyun 		reg_max = 6;
328*4882a593Smuzhiyun 		break;
329*4882a593Smuzhiyun 	default:
330*4882a593Smuzhiyun 		dev_err(&pdev->dev, "unsupported version!\n");
331*4882a593Smuzhiyun 		return -EINVAL;
332*4882a593Smuzhiyun 	}
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun 	regcache = devm_kzalloc(&pdev->dev, sizeof(u32) * reg_max, GFP_KERNEL);
335*4882a593Smuzhiyun 	if (!regcache)
336*4882a593Smuzhiyun 		return -ENOMEM;
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 	if (of_id)
339*4882a593Smuzhiyun 		imx_audmux_parse_dt_defaults(pdev, pdev->dev.of_node);
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun 	return 0;
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun 
imx_audmux_remove(struct platform_device * pdev)344*4882a593Smuzhiyun static int imx_audmux_remove(struct platform_device *pdev)
345*4882a593Smuzhiyun {
346*4882a593Smuzhiyun 	if (audmux_type == IMX31_AUDMUX)
347*4882a593Smuzhiyun 		audmux_debugfs_remove();
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	return 0;
350*4882a593Smuzhiyun }
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
imx_audmux_suspend(struct device * dev)353*4882a593Smuzhiyun static int imx_audmux_suspend(struct device *dev)
354*4882a593Smuzhiyun {
355*4882a593Smuzhiyun 	int i;
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun 	clk_prepare_enable(audmux_clk);
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	for (i = 0; i < reg_max; i++)
360*4882a593Smuzhiyun 		regcache[i] = readl(audmux_base + i * 4);
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 	clk_disable_unprepare(audmux_clk);
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun 	return 0;
365*4882a593Smuzhiyun }
366*4882a593Smuzhiyun 
imx_audmux_resume(struct device * dev)367*4882a593Smuzhiyun static int imx_audmux_resume(struct device *dev)
368*4882a593Smuzhiyun {
369*4882a593Smuzhiyun 	int i;
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	clk_prepare_enable(audmux_clk);
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun 	for (i = 0; i < reg_max; i++)
374*4882a593Smuzhiyun 		writel(regcache[i], audmux_base + i * 4);
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	clk_disable_unprepare(audmux_clk);
377*4882a593Smuzhiyun 
378*4882a593Smuzhiyun 	return 0;
379*4882a593Smuzhiyun }
380*4882a593Smuzhiyun #endif /* CONFIG_PM_SLEEP */
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun static const struct dev_pm_ops imx_audmux_pm = {
383*4882a593Smuzhiyun 	SET_SYSTEM_SLEEP_PM_OPS(imx_audmux_suspend, imx_audmux_resume)
384*4882a593Smuzhiyun };
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun static struct platform_driver imx_audmux_driver = {
387*4882a593Smuzhiyun 	.probe		= imx_audmux_probe,
388*4882a593Smuzhiyun 	.remove		= imx_audmux_remove,
389*4882a593Smuzhiyun 	.id_table	= imx_audmux_ids,
390*4882a593Smuzhiyun 	.driver	= {
391*4882a593Smuzhiyun 		.name	= DRIVER_NAME,
392*4882a593Smuzhiyun 		.pm = &imx_audmux_pm,
393*4882a593Smuzhiyun 		.of_match_table = imx_audmux_dt_ids,
394*4882a593Smuzhiyun 	}
395*4882a593Smuzhiyun };
396*4882a593Smuzhiyun 
imx_audmux_init(void)397*4882a593Smuzhiyun static int __init imx_audmux_init(void)
398*4882a593Smuzhiyun {
399*4882a593Smuzhiyun 	return platform_driver_register(&imx_audmux_driver);
400*4882a593Smuzhiyun }
401*4882a593Smuzhiyun subsys_initcall(imx_audmux_init);
402*4882a593Smuzhiyun 
imx_audmux_exit(void)403*4882a593Smuzhiyun static void __exit imx_audmux_exit(void)
404*4882a593Smuzhiyun {
405*4882a593Smuzhiyun 	platform_driver_unregister(&imx_audmux_driver);
406*4882a593Smuzhiyun }
407*4882a593Smuzhiyun module_exit(imx_audmux_exit);
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun MODULE_DESCRIPTION("Freescale i.MX AUDMUX driver");
410*4882a593Smuzhiyun MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
411*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
412*4882a593Smuzhiyun MODULE_ALIAS("platform:" DRIVER_NAME);
413