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