Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/soc/omap/mcpdm.c
10817 views
1
/*
2
* mcpdm.c -- McPDM interface driver
3
*
4
* Author: Jorge Eduardo Candelaria <[email protected]>
5
* Copyright (C) 2009 - Texas Instruments, Inc.
6
*
7
* This program is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU General Public License
9
* version 2 as published by the Free Software Foundation.
10
*
11
* This program is distributed in the hope that it will be useful, but
12
* WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
* General Public License for more details.
15
*
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19
* 02110-1301 USA
20
*
21
*/
22
23
#include <linux/module.h>
24
#include <linux/init.h>
25
#include <linux/device.h>
26
#include <linux/platform_device.h>
27
#include <linux/wait.h>
28
#include <linux/slab.h>
29
#include <linux/interrupt.h>
30
#include <linux/err.h>
31
#include <linux/clk.h>
32
#include <linux/delay.h>
33
#include <linux/io.h>
34
#include <linux/irq.h>
35
36
#include "mcpdm.h"
37
38
static struct omap_mcpdm *mcpdm;
39
40
static inline void omap_mcpdm_write(u16 reg, u32 val)
41
{
42
__raw_writel(val, mcpdm->io_base + reg);
43
}
44
45
static inline int omap_mcpdm_read(u16 reg)
46
{
47
return __raw_readl(mcpdm->io_base + reg);
48
}
49
50
static void omap_mcpdm_reg_dump(void)
51
{
52
dev_dbg(mcpdm->dev, "***********************\n");
53
dev_dbg(mcpdm->dev, "IRQSTATUS_RAW: 0x%04x\n",
54
omap_mcpdm_read(MCPDM_IRQSTATUS_RAW));
55
dev_dbg(mcpdm->dev, "IRQSTATUS: 0x%04x\n",
56
omap_mcpdm_read(MCPDM_IRQSTATUS));
57
dev_dbg(mcpdm->dev, "IRQENABLE_SET: 0x%04x\n",
58
omap_mcpdm_read(MCPDM_IRQENABLE_SET));
59
dev_dbg(mcpdm->dev, "IRQENABLE_CLR: 0x%04x\n",
60
omap_mcpdm_read(MCPDM_IRQENABLE_CLR));
61
dev_dbg(mcpdm->dev, "IRQWAKE_EN: 0x%04x\n",
62
omap_mcpdm_read(MCPDM_IRQWAKE_EN));
63
dev_dbg(mcpdm->dev, "DMAENABLE_SET: 0x%04x\n",
64
omap_mcpdm_read(MCPDM_DMAENABLE_SET));
65
dev_dbg(mcpdm->dev, "DMAENABLE_CLR: 0x%04x\n",
66
omap_mcpdm_read(MCPDM_DMAENABLE_CLR));
67
dev_dbg(mcpdm->dev, "DMAWAKEEN: 0x%04x\n",
68
omap_mcpdm_read(MCPDM_DMAWAKEEN));
69
dev_dbg(mcpdm->dev, "CTRL: 0x%04x\n",
70
omap_mcpdm_read(MCPDM_CTRL));
71
dev_dbg(mcpdm->dev, "DN_DATA: 0x%04x\n",
72
omap_mcpdm_read(MCPDM_DN_DATA));
73
dev_dbg(mcpdm->dev, "UP_DATA: 0x%04x\n",
74
omap_mcpdm_read(MCPDM_UP_DATA));
75
dev_dbg(mcpdm->dev, "FIFO_CTRL_DN: 0x%04x\n",
76
omap_mcpdm_read(MCPDM_FIFO_CTRL_DN));
77
dev_dbg(mcpdm->dev, "FIFO_CTRL_UP: 0x%04x\n",
78
omap_mcpdm_read(MCPDM_FIFO_CTRL_UP));
79
dev_dbg(mcpdm->dev, "DN_OFFSET: 0x%04x\n",
80
omap_mcpdm_read(MCPDM_DN_OFFSET));
81
dev_dbg(mcpdm->dev, "***********************\n");
82
}
83
84
/*
85
* Takes the McPDM module in and out of reset state.
86
* Uplink and downlink can be reset individually.
87
*/
88
static void omap_mcpdm_reset_capture(int reset)
89
{
90
int ctrl = omap_mcpdm_read(MCPDM_CTRL);
91
92
if (reset)
93
ctrl |= SW_UP_RST;
94
else
95
ctrl &= ~SW_UP_RST;
96
97
omap_mcpdm_write(MCPDM_CTRL, ctrl);
98
}
99
100
static void omap_mcpdm_reset_playback(int reset)
101
{
102
int ctrl = omap_mcpdm_read(MCPDM_CTRL);
103
104
if (reset)
105
ctrl |= SW_DN_RST;
106
else
107
ctrl &= ~SW_DN_RST;
108
109
omap_mcpdm_write(MCPDM_CTRL, ctrl);
110
}
111
112
/*
113
* Enables the transfer through the PDM interface to/from the Phoenix
114
* codec by enabling the corresponding UP or DN channels.
115
*/
116
void omap_mcpdm_start(int stream)
117
{
118
int ctrl = omap_mcpdm_read(MCPDM_CTRL);
119
120
if (stream)
121
ctrl |= mcpdm->up_channels;
122
else
123
ctrl |= mcpdm->dn_channels;
124
125
omap_mcpdm_write(MCPDM_CTRL, ctrl);
126
}
127
128
/*
129
* Disables the transfer through the PDM interface to/from the Phoenix
130
* codec by disabling the corresponding UP or DN channels.
131
*/
132
void omap_mcpdm_stop(int stream)
133
{
134
int ctrl = omap_mcpdm_read(MCPDM_CTRL);
135
136
if (stream)
137
ctrl &= ~mcpdm->up_channels;
138
else
139
ctrl &= ~mcpdm->dn_channels;
140
141
omap_mcpdm_write(MCPDM_CTRL, ctrl);
142
}
143
144
/*
145
* Configures McPDM uplink for audio recording.
146
* This function should be called before omap_mcpdm_start.
147
*/
148
int omap_mcpdm_capture_open(struct omap_mcpdm_link *uplink)
149
{
150
int irq_mask = 0;
151
int ctrl;
152
153
if (!uplink)
154
return -EINVAL;
155
156
mcpdm->uplink = uplink;
157
158
/* Enable irq request generation */
159
irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK;
160
omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask);
161
162
/* Configure uplink threshold */
163
if (uplink->threshold > UP_THRES_MAX)
164
uplink->threshold = UP_THRES_MAX;
165
166
omap_mcpdm_write(MCPDM_FIFO_CTRL_UP, uplink->threshold);
167
168
/* Configure DMA controller */
169
omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_UP_ENABLE);
170
171
/* Set pdm out format */
172
ctrl = omap_mcpdm_read(MCPDM_CTRL);
173
ctrl &= ~PDMOUTFORMAT;
174
ctrl |= uplink->format & PDMOUTFORMAT;
175
176
/* Uplink channels */
177
mcpdm->up_channels = uplink->channels & (PDM_UP_MASK | PDM_STATUS_MASK);
178
179
omap_mcpdm_write(MCPDM_CTRL, ctrl);
180
181
return 0;
182
}
183
184
/*
185
* Configures McPDM downlink for audio playback.
186
* This function should be called before omap_mcpdm_start.
187
*/
188
int omap_mcpdm_playback_open(struct omap_mcpdm_link *downlink)
189
{
190
int irq_mask = 0;
191
int ctrl;
192
193
if (!downlink)
194
return -EINVAL;
195
196
mcpdm->downlink = downlink;
197
198
/* Enable irq request generation */
199
irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK;
200
omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask);
201
202
/* Configure uplink threshold */
203
if (downlink->threshold > DN_THRES_MAX)
204
downlink->threshold = DN_THRES_MAX;
205
206
omap_mcpdm_write(MCPDM_FIFO_CTRL_DN, downlink->threshold);
207
208
/* Enable DMA request generation */
209
omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_DN_ENABLE);
210
211
/* Set pdm out format */
212
ctrl = omap_mcpdm_read(MCPDM_CTRL);
213
ctrl &= ~PDMOUTFORMAT;
214
ctrl |= downlink->format & PDMOUTFORMAT;
215
216
/* Downlink channels */
217
mcpdm->dn_channels = downlink->channels & (PDM_DN_MASK | PDM_CMD_MASK);
218
219
omap_mcpdm_write(MCPDM_CTRL, ctrl);
220
221
return 0;
222
}
223
224
/*
225
* Cleans McPDM uplink configuration.
226
* This function should be called when the stream is closed.
227
*/
228
int omap_mcpdm_capture_close(struct omap_mcpdm_link *uplink)
229
{
230
int irq_mask = 0;
231
232
if (!uplink)
233
return -EINVAL;
234
235
/* Disable irq request generation */
236
irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK;
237
omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask);
238
239
/* Disable DMA request generation */
240
omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_UP_ENABLE);
241
242
/* Clear Downlink channels */
243
mcpdm->up_channels = 0;
244
245
mcpdm->uplink = NULL;
246
247
return 0;
248
}
249
250
/*
251
* Cleans McPDM downlink configuration.
252
* This function should be called when the stream is closed.
253
*/
254
int omap_mcpdm_playback_close(struct omap_mcpdm_link *downlink)
255
{
256
int irq_mask = 0;
257
258
if (!downlink)
259
return -EINVAL;
260
261
/* Disable irq request generation */
262
irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK;
263
omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask);
264
265
/* Disable DMA request generation */
266
omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_DN_ENABLE);
267
268
/* clear Downlink channels */
269
mcpdm->dn_channels = 0;
270
271
mcpdm->downlink = NULL;
272
273
return 0;
274
}
275
276
static irqreturn_t omap_mcpdm_irq_handler(int irq, void *dev_id)
277
{
278
struct omap_mcpdm *mcpdm_irq = dev_id;
279
int irq_status;
280
281
irq_status = omap_mcpdm_read(MCPDM_IRQSTATUS);
282
283
/* Acknowledge irq event */
284
omap_mcpdm_write(MCPDM_IRQSTATUS, irq_status);
285
286
if (irq & MCPDM_DN_IRQ_FULL) {
287
dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status);
288
omap_mcpdm_reset_playback(1);
289
omap_mcpdm_playback_open(mcpdm_irq->downlink);
290
omap_mcpdm_reset_playback(0);
291
}
292
293
if (irq & MCPDM_DN_IRQ_EMPTY) {
294
dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status);
295
omap_mcpdm_reset_playback(1);
296
omap_mcpdm_playback_open(mcpdm_irq->downlink);
297
omap_mcpdm_reset_playback(0);
298
}
299
300
if (irq & MCPDM_DN_IRQ) {
301
dev_dbg(mcpdm_irq->dev, "DN write request\n");
302
}
303
304
if (irq & MCPDM_UP_IRQ_FULL) {
305
dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status);
306
omap_mcpdm_reset_capture(1);
307
omap_mcpdm_capture_open(mcpdm_irq->uplink);
308
omap_mcpdm_reset_capture(0);
309
}
310
311
if (irq & MCPDM_UP_IRQ_EMPTY) {
312
dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status);
313
omap_mcpdm_reset_capture(1);
314
omap_mcpdm_capture_open(mcpdm_irq->uplink);
315
omap_mcpdm_reset_capture(0);
316
}
317
318
if (irq & MCPDM_UP_IRQ) {
319
dev_dbg(mcpdm_irq->dev, "UP write request\n");
320
}
321
322
return IRQ_HANDLED;
323
}
324
325
int omap_mcpdm_request(void)
326
{
327
int ret;
328
329
clk_enable(mcpdm->clk);
330
331
spin_lock(&mcpdm->lock);
332
333
if (!mcpdm->free) {
334
dev_err(mcpdm->dev, "McPDM interface is in use\n");
335
spin_unlock(&mcpdm->lock);
336
ret = -EBUSY;
337
goto err;
338
}
339
mcpdm->free = 0;
340
341
spin_unlock(&mcpdm->lock);
342
343
/* Disable lines while request is ongoing */
344
omap_mcpdm_write(MCPDM_CTRL, 0x00);
345
346
ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler,
347
0, "McPDM", (void *)mcpdm);
348
if (ret) {
349
dev_err(mcpdm->dev, "Request for McPDM IRQ failed\n");
350
goto err;
351
}
352
353
return 0;
354
355
err:
356
clk_disable(mcpdm->clk);
357
return ret;
358
}
359
360
void omap_mcpdm_free(void)
361
{
362
spin_lock(&mcpdm->lock);
363
if (mcpdm->free) {
364
dev_err(mcpdm->dev, "McPDM interface is already free\n");
365
spin_unlock(&mcpdm->lock);
366
return;
367
}
368
mcpdm->free = 1;
369
spin_unlock(&mcpdm->lock);
370
371
clk_disable(mcpdm->clk);
372
373
free_irq(mcpdm->irq, (void *)mcpdm);
374
}
375
376
/* Enable/disable DC offset cancelation for the analog
377
* headset path (PDM channels 1 and 2).
378
*/
379
int omap_mcpdm_set_offset(int offset1, int offset2)
380
{
381
int offset;
382
383
if ((offset1 > DN_OFST_MAX) || (offset2 > DN_OFST_MAX))
384
return -EINVAL;
385
386
offset = (offset1 << DN_OFST_RX1) | (offset2 << DN_OFST_RX2);
387
388
/* offset cancellation for channel 1 */
389
if (offset1)
390
offset |= DN_OFST_RX1_EN;
391
else
392
offset &= ~DN_OFST_RX1_EN;
393
394
/* offset cancellation for channel 2 */
395
if (offset2)
396
offset |= DN_OFST_RX2_EN;
397
else
398
offset &= ~DN_OFST_RX2_EN;
399
400
omap_mcpdm_write(MCPDM_DN_OFFSET, offset);
401
402
return 0;
403
}
404
405
int __devinit omap_mcpdm_probe(struct platform_device *pdev)
406
{
407
struct resource *res;
408
int ret = 0;
409
410
mcpdm = kzalloc(sizeof(struct omap_mcpdm), GFP_KERNEL);
411
if (!mcpdm) {
412
ret = -ENOMEM;
413
goto exit;
414
}
415
416
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
417
if (res == NULL) {
418
dev_err(&pdev->dev, "no resource\n");
419
goto err_resource;
420
}
421
422
spin_lock_init(&mcpdm->lock);
423
mcpdm->free = 1;
424
mcpdm->io_base = ioremap(res->start, resource_size(res));
425
if (!mcpdm->io_base) {
426
ret = -ENOMEM;
427
goto err_resource;
428
}
429
430
mcpdm->irq = platform_get_irq(pdev, 0);
431
432
mcpdm->clk = clk_get(&pdev->dev, "pdm_ck");
433
if (IS_ERR(mcpdm->clk)) {
434
ret = PTR_ERR(mcpdm->clk);
435
dev_err(&pdev->dev, "unable to get pdm_ck: %d\n", ret);
436
goto err_clk;
437
}
438
439
mcpdm->dev = &pdev->dev;
440
platform_set_drvdata(pdev, mcpdm);
441
442
return 0;
443
444
err_clk:
445
iounmap(mcpdm->io_base);
446
err_resource:
447
kfree(mcpdm);
448
exit:
449
return ret;
450
}
451
452
int __devexit omap_mcpdm_remove(struct platform_device *pdev)
453
{
454
struct omap_mcpdm *mcpdm_ptr = platform_get_drvdata(pdev);
455
456
platform_set_drvdata(pdev, NULL);
457
458
clk_put(mcpdm_ptr->clk);
459
460
iounmap(mcpdm_ptr->io_base);
461
462
mcpdm_ptr->clk = NULL;
463
mcpdm_ptr->free = 0;
464
mcpdm_ptr->dev = NULL;
465
466
kfree(mcpdm_ptr);
467
468
return 0;
469
}
470
471
472