1 /*
2  * mcpdm.c  --	McPDM interface driver
3  *
4  * Author: Jorge Eduardo Candelaria <x0107209@ti.com>
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 
omap_mcpdm_write(u16 reg,u32 val)40 static inline void omap_mcpdm_write(u16 reg, u32 val)
41 {
42 	__raw_writel(val, mcpdm->io_base + reg);
43 }
44 
omap_mcpdm_read(u16 reg)45 static inline int omap_mcpdm_read(u16 reg)
46 {
47 	return __raw_readl(mcpdm->io_base + reg);
48 }
49 
omap_mcpdm_reg_dump(void)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  */
omap_mcpdm_reset_capture(int reset)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 
omap_mcpdm_reset_playback(int reset)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  */
omap_mcpdm_start(int stream)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  */
omap_mcpdm_stop(int stream)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  */
omap_mcpdm_capture_open(struct omap_mcpdm_link * uplink)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  */
omap_mcpdm_playback_open(struct omap_mcpdm_link * downlink)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  */
omap_mcpdm_capture_close(struct omap_mcpdm_link * uplink)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  */
omap_mcpdm_playback_close(struct omap_mcpdm_link * downlink)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 
omap_mcpdm_irq_handler(int irq,void * dev_id)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 
omap_mcpdm_request(void)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 
omap_mcpdm_free(void)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  */
omap_mcpdm_set_offset(int offset1,int offset2)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 
omap_mcpdm_probe(struct platform_device * pdev)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 
omap_mcpdm_remove(struct platform_device * pdev)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