| 
									
										
										
										
											2010-11-22 15:37:25 +09:00
										 |  |  | /* sound/soc/samsung/s3c-i2c-v2.c
 | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  |  * | 
					
						
							|  |  |  |  * ALSA Soc Audio Layer - I2S core for newer Samsung SoCs. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2006 Wolfson Microelectronics PLC. | 
					
						
							|  |  |  |  *	Graeme Gregory graeme.gregory@wolfsonmicro.com | 
					
						
							|  |  |  |  *	linux@wolfsonmicro.com | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2008, 2007, 2004-2005 Simtec Electronics | 
					
						
							|  |  |  |  *	http://armlinux.simtec.co.uk/
 | 
					
						
							|  |  |  |  *	Ben Dooks <ben@simtec.co.uk> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute  it and/or modify it | 
					
						
							|  |  |  |  * under  the terms of  the GNU General  Public License as published by the | 
					
						
							|  |  |  |  * Free Software Foundation;  either version 2 of the  License, or (at your | 
					
						
							|  |  |  |  * option) any later version. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-02 21:34:26 +08:00
										 |  |  | #include <linux/module.h>
 | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | #include <linux/delay.h>
 | 
					
						
							|  |  |  | #include <linux/clk.h>
 | 
					
						
							|  |  |  | #include <linux/io.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <sound/soc.h>
 | 
					
						
							| 
									
										
										
										
											2011-01-11 07:26:06 +09:00
										 |  |  | #include <sound/pcm_params.h>
 | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <mach/dma.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-27 15:55:21 +09:00
										 |  |  | #include "regs-i2s-v2.h"
 | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | #include "s3c-i2s-v2.h"
 | 
					
						
							| 
									
										
										
										
											2010-11-22 15:35:57 +09:00
										 |  |  | #include "dma.h"
 | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-29 20:28:47 +01:00
										 |  |  | #undef S3C_IIS_V2_SUPPORTED
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-20 14:28:30 +09:00
										 |  |  | #if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413) \
 | 
					
						
							|  |  |  | 	|| defined(CONFIG_CPU_S5PV210) | 
					
						
							| 
									
										
										
										
											2009-04-29 20:28:47 +01:00
										 |  |  | #define S3C_IIS_V2_SUPPORTED
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_PLAT_S3C64XX
 | 
					
						
							|  |  |  | #define S3C_IIS_V2_SUPPORTED
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifndef S3C_IIS_V2_SUPPORTED
 | 
					
						
							|  |  |  | #error Unsupported CPU model
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | #define S3C2412_I2S_DEBUG_CON 0
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2010-03-17 20:15:21 +00:00
										 |  |  | 	return snd_soc_dai_get_drvdata(cpu_dai); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define bit_set(v, b) (((v) & (b)) ? 1 : 0)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if S3C2412_I2S_DEBUG_CON
 | 
					
						
							|  |  |  | static void dbg_showcon(const char *fn, u32 con) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn, | 
					
						
							|  |  |  | 	       bit_set(con, S3C2412_IISCON_LRINDEX), | 
					
						
							|  |  |  | 	       bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY), | 
					
						
							|  |  |  | 	       bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY), | 
					
						
							|  |  |  | 	       bit_set(con, S3C2412_IISCON_TXFIFO_FULL), | 
					
						
							|  |  |  | 	       bit_set(con, S3C2412_IISCON_RXFIFO_FULL)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n", | 
					
						
							|  |  |  | 	       fn, | 
					
						
							|  |  |  | 	       bit_set(con, S3C2412_IISCON_TXDMA_PAUSE), | 
					
						
							|  |  |  | 	       bit_set(con, S3C2412_IISCON_RXDMA_PAUSE), | 
					
						
							|  |  |  | 	       bit_set(con, S3C2412_IISCON_TXCH_PAUSE), | 
					
						
							|  |  |  | 	       bit_set(con, S3C2412_IISCON_RXCH_PAUSE)); | 
					
						
							|  |  |  | 	printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn, | 
					
						
							|  |  |  | 	       bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE), | 
					
						
							|  |  |  | 	       bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE), | 
					
						
							|  |  |  | 	       bit_set(con, S3C2412_IISCON_IIS_ACTIVE)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | static inline void dbg_showcon(const char *fn, u32 con) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Turn on or off the transmission path. */ | 
					
						
							| 
									
										
										
										
											2009-04-30 13:21:52 +01:00
										 |  |  | static void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on) | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	void __iomem *regs = i2s->regs; | 
					
						
							|  |  |  | 	u32 fic, con, mod; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-06 18:04:34 +00:00
										 |  |  | 	pr_debug("%s(%d)\n", __func__, on); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	fic = readl(regs + S3C2412_IISFIC); | 
					
						
							|  |  |  | 	con = readl(regs + S3C2412_IISCON); | 
					
						
							|  |  |  | 	mod = readl(regs + S3C2412_IISMOD); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-06 18:04:34 +00:00
										 |  |  | 	pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (on) { | 
					
						
							|  |  |  | 		con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE; | 
					
						
							|  |  |  | 		con &= ~S3C2412_IISCON_TXDMA_PAUSE; | 
					
						
							|  |  |  | 		con &= ~S3C2412_IISCON_TXCH_PAUSE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch (mod & S3C2412_IISMOD_MODE_MASK) { | 
					
						
							|  |  |  | 		case S3C2412_IISMOD_MODE_TXONLY: | 
					
						
							|  |  |  | 		case S3C2412_IISMOD_MODE_TXRX: | 
					
						
							|  |  |  | 			/* do nothing, we are in the right mode */ | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case S3C2412_IISMOD_MODE_RXONLY: | 
					
						
							|  |  |  | 			mod &= ~S3C2412_IISMOD_MODE_MASK; | 
					
						
							|  |  |  | 			mod |= S3C2412_IISMOD_MODE_TXRX; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		default: | 
					
						
							| 
									
										
										
										
											2009-04-30 13:21:52 +01:00
										 |  |  | 			dev_err(i2s->dev, "TXEN: Invalid MODE %x in IISMOD\n", | 
					
						
							|  |  |  | 				mod & S3C2412_IISMOD_MODE_MASK); | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		writel(con, regs + S3C2412_IISCON); | 
					
						
							|  |  |  | 		writel(mod, regs + S3C2412_IISMOD); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		/* Note, we do not have any indication that the FIFO problems
 | 
					
						
							|  |  |  | 		 * tha the S3C2410/2440 had apply here, so we should be able | 
					
						
							|  |  |  | 		 * to disable the DMA and TX without resetting the FIFOS. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		con |=  S3C2412_IISCON_TXDMA_PAUSE; | 
					
						
							|  |  |  | 		con |=  S3C2412_IISCON_TXCH_PAUSE; | 
					
						
							|  |  |  | 		con &= ~S3C2412_IISCON_TXDMA_ACTIVE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch (mod & S3C2412_IISMOD_MODE_MASK) { | 
					
						
							|  |  |  | 		case S3C2412_IISMOD_MODE_TXRX: | 
					
						
							|  |  |  | 			mod &= ~S3C2412_IISMOD_MODE_MASK; | 
					
						
							|  |  |  | 			mod |= S3C2412_IISMOD_MODE_RXONLY; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case S3C2412_IISMOD_MODE_TXONLY: | 
					
						
							|  |  |  | 			mod &= ~S3C2412_IISMOD_MODE_MASK; | 
					
						
							|  |  |  | 			con &= ~S3C2412_IISCON_IIS_ACTIVE; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		default: | 
					
						
							| 
									
										
										
										
											2009-04-30 13:21:52 +01:00
										 |  |  | 			dev_err(i2s->dev, "TXDIS: Invalid MODE %x in IISMOD\n", | 
					
						
							|  |  |  | 				mod & S3C2412_IISMOD_MODE_MASK); | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		writel(mod, regs + S3C2412_IISMOD); | 
					
						
							|  |  |  | 		writel(con, regs + S3C2412_IISCON); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fic = readl(regs + S3C2412_IISFIC); | 
					
						
							|  |  |  | 	dbg_showcon(__func__, con); | 
					
						
							| 
									
										
										
										
											2009-03-06 18:04:34 +00:00
										 |  |  | 	pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-30 13:21:52 +01:00
										 |  |  | static void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on) | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	void __iomem *regs = i2s->regs; | 
					
						
							|  |  |  | 	u32 fic, con, mod; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-06 18:04:34 +00:00
										 |  |  | 	pr_debug("%s(%d)\n", __func__, on); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	fic = readl(regs + S3C2412_IISFIC); | 
					
						
							|  |  |  | 	con = readl(regs + S3C2412_IISCON); | 
					
						
							|  |  |  | 	mod = readl(regs + S3C2412_IISMOD); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-06 18:04:34 +00:00
										 |  |  | 	pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (on) { | 
					
						
							|  |  |  | 		con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE; | 
					
						
							|  |  |  | 		con &= ~S3C2412_IISCON_RXDMA_PAUSE; | 
					
						
							|  |  |  | 		con &= ~S3C2412_IISCON_RXCH_PAUSE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch (mod & S3C2412_IISMOD_MODE_MASK) { | 
					
						
							|  |  |  | 		case S3C2412_IISMOD_MODE_TXRX: | 
					
						
							|  |  |  | 		case S3C2412_IISMOD_MODE_RXONLY: | 
					
						
							|  |  |  | 			/* do nothing, we are in the right mode */ | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case S3C2412_IISMOD_MODE_TXONLY: | 
					
						
							|  |  |  | 			mod &= ~S3C2412_IISMOD_MODE_MASK; | 
					
						
							|  |  |  | 			mod |= S3C2412_IISMOD_MODE_TXRX; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		default: | 
					
						
							| 
									
										
										
										
											2009-04-30 13:21:52 +01:00
										 |  |  | 			dev_err(i2s->dev, "RXEN: Invalid MODE %x in IISMOD\n", | 
					
						
							|  |  |  | 				mod & S3C2412_IISMOD_MODE_MASK); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		writel(mod, regs + S3C2412_IISMOD); | 
					
						
							|  |  |  | 		writel(con, regs + S3C2412_IISCON); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		/* See txctrl notes on FIFOs. */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		con &= ~S3C2412_IISCON_RXDMA_ACTIVE; | 
					
						
							|  |  |  | 		con |=  S3C2412_IISCON_RXDMA_PAUSE; | 
					
						
							|  |  |  | 		con |=  S3C2412_IISCON_RXCH_PAUSE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch (mod & S3C2412_IISMOD_MODE_MASK) { | 
					
						
							|  |  |  | 		case S3C2412_IISMOD_MODE_RXONLY: | 
					
						
							|  |  |  | 			con &= ~S3C2412_IISCON_IIS_ACTIVE; | 
					
						
							|  |  |  | 			mod &= ~S3C2412_IISMOD_MODE_MASK; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case S3C2412_IISMOD_MODE_TXRX: | 
					
						
							|  |  |  | 			mod &= ~S3C2412_IISMOD_MODE_MASK; | 
					
						
							|  |  |  | 			mod |= S3C2412_IISMOD_MODE_TXONLY; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		default: | 
					
						
							| 
									
										
										
										
											2009-04-30 13:21:52 +01:00
										 |  |  | 			dev_err(i2s->dev, "RXDIS: Invalid MODE %x in IISMOD\n", | 
					
						
							|  |  |  | 				mod & S3C2412_IISMOD_MODE_MASK); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		writel(con, regs + S3C2412_IISCON); | 
					
						
							|  |  |  | 		writel(mod, regs + S3C2412_IISMOD); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fic = readl(regs + S3C2412_IISFIC); | 
					
						
							| 
									
										
										
										
											2009-03-06 18:04:34 +00:00
										 |  |  | 	pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-15 19:02:37 +09:00
										 |  |  | #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Wait for the LR signal to allow synchronisation to the L/R clock | 
					
						
							|  |  |  |  * from the codec. May only be needed for slave mode. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int s3c2412_snd_lrsync(struct s3c_i2sv2_info *i2s) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 iiscon; | 
					
						
							| 
									
										
										
										
											2009-09-15 19:02:37 +09:00
										 |  |  | 	unsigned long loops = msecs_to_loops(5); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-06 18:04:34 +00:00
										 |  |  | 	pr_debug("Entered %s\n", __func__); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-15 19:02:37 +09:00
										 |  |  | 	while (--loops) { | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 		iiscon = readl(i2s->regs + S3C2412_IISCON); | 
					
						
							|  |  |  | 		if (iiscon & S3C2412_IISCON_LRINDEX) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-15 19:02:37 +09:00
										 |  |  | 		cpu_relax(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!loops) { | 
					
						
							|  |  |  | 		printk(KERN_ERR "%s: timeout\n", __func__); | 
					
						
							|  |  |  | 		return -ETIMEDOUT; | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Set S3C2412 I2S DAI format | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai, | 
					
						
							|  |  |  | 			       unsigned int fmt) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct s3c_i2sv2_info *i2s = to_info(cpu_dai); | 
					
						
							|  |  |  | 	u32 iismod; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-06 18:04:34 +00:00
										 |  |  | 	pr_debug("Entered %s\n", __func__); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	iismod = readl(i2s->regs + S3C2412_IISMOD); | 
					
						
							| 
									
										
										
										
											2009-03-06 18:04:34 +00:00
										 |  |  | 	pr_debug("hw_params r: IISMOD: %x \n", iismod); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | 
					
						
							|  |  |  | 	case SND_SOC_DAIFMT_CBM_CFM: | 
					
						
							|  |  |  | 		i2s->master = 0; | 
					
						
							| 
									
										
										
										
											2010-04-27 15:56:27 +09:00
										 |  |  | 		iismod |= S3C2412_IISMOD_SLAVE; | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case SND_SOC_DAIFMT_CBS_CFS: | 
					
						
							|  |  |  | 		i2s->master = 1; | 
					
						
							| 
									
										
										
										
											2010-04-27 15:56:27 +09:00
										 |  |  | 		iismod &= ~S3C2412_IISMOD_SLAVE; | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2009-04-30 13:14:38 +01:00
										 |  |  | 		pr_err("unknwon master/slave format\n"); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	iismod &= ~S3C2412_IISMOD_SDF_MASK; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | 
					
						
							|  |  |  | 	case SND_SOC_DAIFMT_RIGHT_J: | 
					
						
							| 
									
										
										
										
											2009-09-15 19:02:38 +09:00
										 |  |  | 		iismod |= S3C2412_IISMOD_LR_RLOW; | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 		iismod |= S3C2412_IISMOD_SDF_MSB; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case SND_SOC_DAIFMT_LEFT_J: | 
					
						
							| 
									
										
										
										
											2009-09-15 19:02:38 +09:00
										 |  |  | 		iismod |= S3C2412_IISMOD_LR_RLOW; | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 		iismod |= S3C2412_IISMOD_SDF_LSB; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case SND_SOC_DAIFMT_I2S: | 
					
						
							| 
									
										
										
										
											2009-09-15 19:02:38 +09:00
										 |  |  | 		iismod &= ~S3C2412_IISMOD_LR_RLOW; | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 		iismod |= S3C2412_IISMOD_SDF_IIS; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2009-04-30 13:14:38 +01:00
										 |  |  | 		pr_err("Unknown data format\n"); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	writel(iismod, i2s->regs + S3C2412_IISMOD); | 
					
						
							| 
									
										
										
										
											2009-03-06 18:04:34 +00:00
										 |  |  | 	pr_debug("hw_params w: IISMOD: %x \n", iismod); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-10 16:48:58 +09:00
										 |  |  | static int s3c_i2sv2_hw_params(struct snd_pcm_substream *substream, | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 				 struct snd_pcm_hw_params *params, | 
					
						
							| 
									
										
										
										
											2010-03-17 20:15:21 +00:00
										 |  |  | 				 struct snd_soc_dai *dai) | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2010-03-17 20:15:21 +00:00
										 |  |  | 	struct s3c_i2sv2_info *i2s = to_info(dai); | 
					
						
							| 
									
										
										
										
											2010-03-19 14:52:55 +00:00
										 |  |  | 	struct s3c_dma_params *dma_data; | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 	u32 iismod; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-06 18:04:34 +00:00
										 |  |  | 	pr_debug("Entered %s\n", __func__); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 
					
						
							| 
									
										
										
										
											2010-03-19 14:52:55 +00:00
										 |  |  | 		dma_data = i2s->dma_playback; | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 	else | 
					
						
							| 
									
										
										
										
											2010-03-19 14:52:55 +00:00
										 |  |  | 		dma_data = i2s->dma_capture; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-17 20:15:21 +00:00
										 |  |  | 	snd_soc_dai_set_dma_data(dai, substream, dma_data); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Working copies of register */ | 
					
						
							|  |  |  | 	iismod = readl(i2s->regs + S3C2412_IISMOD); | 
					
						
							| 
									
										
										
										
											2009-03-06 18:04:34 +00:00
										 |  |  | 	pr_debug("%s: r: IISMOD: %x\n", __func__, iismod); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-10 16:48:52 +09:00
										 |  |  | 	iismod &= ~S3C64XX_IISMOD_BLC_MASK; | 
					
						
							| 
									
										
										
										
											2009-04-29 20:29:25 +01:00
										 |  |  | 	/* Sample size */ | 
					
						
							|  |  |  | 	switch (params_format(params)) { | 
					
						
							|  |  |  | 	case SNDRV_PCM_FORMAT_S8: | 
					
						
							| 
									
										
										
										
											2010-03-10 16:48:52 +09:00
										 |  |  | 		iismod |= S3C64XX_IISMOD_BLC_8BIT; | 
					
						
							| 
									
										
										
										
											2009-04-29 20:29:25 +01:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case SNDRV_PCM_FORMAT_S16_LE: | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case SNDRV_PCM_FORMAT_S24_LE: | 
					
						
							| 
									
										
										
										
											2010-03-10 16:48:52 +09:00
										 |  |  | 		iismod |= S3C64XX_IISMOD_BLC_24BIT; | 
					
						
							| 
									
										
										
										
											2009-04-29 20:29:25 +01:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	writel(iismod, i2s->regs + S3C2412_IISMOD); | 
					
						
							| 
									
										
										
										
											2009-03-06 18:04:34 +00:00
										 |  |  | 	pr_debug("%s: w: IISMOD: %x\n", __func__, iismod); | 
					
						
							| 
									
										
										
										
											2010-04-27 15:57:05 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int s3c_i2sv2_set_sysclk(struct snd_soc_dai *cpu_dai, | 
					
						
							|  |  |  | 				  int clk_id, unsigned int freq, int dir) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct s3c_i2sv2_info *i2s = to_info(cpu_dai); | 
					
						
							|  |  |  | 	u32 iismod = readl(i2s->regs + S3C2412_IISMOD); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pr_debug("Entered %s\n", __func__); | 
					
						
							|  |  |  | 	pr_debug("%s r: IISMOD: %x\n", __func__, iismod); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (clk_id) { | 
					
						
							|  |  |  | 	case S3C_I2SV2_CLKSRC_PCLK: | 
					
						
							|  |  |  | 		iismod &= ~S3C2412_IISMOD_IMS_SYSMUX; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case S3C_I2SV2_CLKSRC_AUDIOBUS: | 
					
						
							|  |  |  | 		iismod |= S3C2412_IISMOD_IMS_SYSMUX; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case S3C_I2SV2_CLKSRC_CDCLK: | 
					
						
							|  |  |  | 		/* Error if controller doesn't have the CDCLKCON bit */ | 
					
						
							|  |  |  | 		if (!(i2s->feature & S3C_FEATURE_CDCLKCON)) | 
					
						
							|  |  |  | 			return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch (dir) { | 
					
						
							|  |  |  | 		case SND_SOC_CLOCK_IN: | 
					
						
							|  |  |  | 			iismod |= S3C64XX_IISMOD_CDCLKCON; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case SND_SOC_CLOCK_OUT: | 
					
						
							|  |  |  | 			iismod &= ~S3C64XX_IISMOD_CDCLKCON; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			return -EINVAL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	writel(iismod, i2s->regs + S3C2412_IISMOD); | 
					
						
							|  |  |  | 	pr_debug("%s w: IISMOD: %x\n", __func__, iismod); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd, | 
					
						
							|  |  |  | 			       struct snd_soc_dai *dai) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct snd_soc_pcm_runtime *rtd = substream->private_data; | 
					
						
							| 
									
										
										
										
											2010-03-17 20:15:21 +00:00
										 |  |  | 	struct s3c_i2sv2_info *i2s = to_info(rtd->cpu_dai); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 	int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); | 
					
						
							|  |  |  | 	unsigned long irqs; | 
					
						
							|  |  |  | 	int ret = 0; | 
					
						
							| 
									
										
										
										
											2010-03-19 14:52:55 +00:00
										 |  |  | 	struct s3c_dma_params *dma_data = | 
					
						
							| 
									
										
										
										
											2010-03-17 20:15:21 +00:00
										 |  |  | 		snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-06 18:04:34 +00:00
										 |  |  | 	pr_debug("Entered %s\n", __func__); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	switch (cmd) { | 
					
						
							|  |  |  | 	case SNDRV_PCM_TRIGGER_START: | 
					
						
							|  |  |  | 		/* On start, ensure that the FIFOs are cleared and reset. */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH, | 
					
						
							|  |  |  | 		       i2s->regs + S3C2412_IISFIC); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* clear again, just in case */ | 
					
						
							|  |  |  | 		writel(0x0, i2s->regs + S3C2412_IISFIC); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case SNDRV_PCM_TRIGGER_RESUME: | 
					
						
							|  |  |  | 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 
					
						
							|  |  |  | 		if (!i2s->master) { | 
					
						
							|  |  |  | 			ret = s3c2412_snd_lrsync(i2s); | 
					
						
							|  |  |  | 			if (ret) | 
					
						
							|  |  |  | 				goto exit_err; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		local_irq_save(irqs); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (capture) | 
					
						
							|  |  |  | 			s3c2412_snd_rxctrl(i2s, 1); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			s3c2412_snd_txctrl(i2s, 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		local_irq_restore(irqs); | 
					
						
							| 
									
										
											  
											
												ASoC: S3C platform: Fix s3c2410_dma_started() called at improper time
s3c24xx dma has the auto reload feature, when the the trnasfer is done,
CURR_TC(DSTAT[19:0], current value of transfer count) reaches 0, and DMA
ACK becomes 1, and then, TC(DCON[19:0]) will be loaded into CURR_TC. So
the transmission is repeated.
IRQ is issued while auto reload occurs. We change the DISRC and
DCON[19:0] in the ISR, but at this time, the auto reload has been
performed already. The first block is being re-transmitted by the DMA.
So we need rewrite the DISRC and DCON[19:0] for the next block
immediatly after the this block has been started to be transported.
The function s3c2410_dma_started() is for this perpose, which is called
in the form of "s3c2410_dma_ctrl(prtd->params->channel,
S3C2410_DMAOP_STARTED);" in s3c24xx_pcm_trigger().
But it is not correct. DMA transmission won't start until DMA REQ signal
arrived, it is the time s3c24xx_snd_txctrl(1) or s3c24xx_snd_rxctrl(1)
is called in s3c24xx_i2s_trigger().
In the current framework, s3c24xx_pcm_trigger() is always called before
s3c24xx_pcm_trigger(). So the s3c2410_dma_started() should be called in
s3c24xx_pcm_trigger() after s3c24xx_snd_txctrl(1) or
s3c24xx_snd_rxctrl(1) is called in this function.
However, s3c2410_dma_started() is dma related, to call this function we
should provide the channel number, which is given by
substream->runtime->private_data->params->channel. The private_data
points to a struct s3c24xx_runtime_data object, which is define in
s3c24xx_pcm.c, so s3c2410_dma_started() can't be called in s3c24xx_i2s.c
Fix this by moving the call to signal the DMA started to the DAI
drivers.
Signed-off-by: Shine Liu <liuxian@redflag-linux.com>
Signed-off-by: Shine Liu <shinel@foxmail.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
											
										 
											2009-08-25 20:05:50 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Load the next buffer to DMA to meet the reqirement | 
					
						
							|  |  |  | 		 * of the auto reload mechanism of S3C24XX. | 
					
						
							|  |  |  | 		 * This call won't bother S3C64XX. | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2010-03-19 14:52:55 +00:00
										 |  |  | 		s3c2410_dma_ctrl(dma_data->channel, S3C2410_DMAOP_STARTED); | 
					
						
							| 
									
										
											  
											
												ASoC: S3C platform: Fix s3c2410_dma_started() called at improper time
s3c24xx dma has the auto reload feature, when the the trnasfer is done,
CURR_TC(DSTAT[19:0], current value of transfer count) reaches 0, and DMA
ACK becomes 1, and then, TC(DCON[19:0]) will be loaded into CURR_TC. So
the transmission is repeated.
IRQ is issued while auto reload occurs. We change the DISRC and
DCON[19:0] in the ISR, but at this time, the auto reload has been
performed already. The first block is being re-transmitted by the DMA.
So we need rewrite the DISRC and DCON[19:0] for the next block
immediatly after the this block has been started to be transported.
The function s3c2410_dma_started() is for this perpose, which is called
in the form of "s3c2410_dma_ctrl(prtd->params->channel,
S3C2410_DMAOP_STARTED);" in s3c24xx_pcm_trigger().
But it is not correct. DMA transmission won't start until DMA REQ signal
arrived, it is the time s3c24xx_snd_txctrl(1) or s3c24xx_snd_rxctrl(1)
is called in s3c24xx_i2s_trigger().
In the current framework, s3c24xx_pcm_trigger() is always called before
s3c24xx_pcm_trigger(). So the s3c2410_dma_started() should be called in
s3c24xx_pcm_trigger() after s3c24xx_snd_txctrl(1) or
s3c24xx_snd_rxctrl(1) is called in this function.
However, s3c2410_dma_started() is dma related, to call this function we
should provide the channel number, which is given by
substream->runtime->private_data->params->channel. The private_data
points to a struct s3c24xx_runtime_data object, which is define in
s3c24xx_pcm.c, so s3c2410_dma_started() can't be called in s3c24xx_i2s.c
Fix this by moving the call to signal the DMA started to the DAI
drivers.
Signed-off-by: Shine Liu <liuxian@redflag-linux.com>
Signed-off-by: Shine Liu <shinel@foxmail.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
											
										 
											2009-08-25 20:05:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case SNDRV_PCM_TRIGGER_STOP: | 
					
						
							|  |  |  | 	case SNDRV_PCM_TRIGGER_SUSPEND: | 
					
						
							|  |  |  | 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 
					
						
							|  |  |  | 		local_irq_save(irqs); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (capture) | 
					
						
							|  |  |  | 			s3c2412_snd_rxctrl(i2s, 0); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			s3c2412_snd_txctrl(i2s, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		local_irq_restore(irqs); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		ret = -EINVAL; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | exit_err: | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Set S3C2412 Clock dividers | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai, | 
					
						
							|  |  |  | 				  int div_id, int div) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct s3c_i2sv2_info *i2s = to_info(cpu_dai); | 
					
						
							|  |  |  | 	u32 reg; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-06 18:04:34 +00:00
										 |  |  | 	pr_debug("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	switch (div_id) { | 
					
						
							|  |  |  | 	case S3C_I2SV2_DIV_BCLK: | 
					
						
							| 
									
										
										
										
											2010-03-10 16:48:51 +09:00
										 |  |  | 		switch (div) { | 
					
						
							|  |  |  | 		case 16: | 
					
						
							|  |  |  | 			div = S3C2412_IISMOD_BCLK_16FS; | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2009-09-15 19:02:38 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-10 16:48:51 +09:00
										 |  |  | 		case 32: | 
					
						
							|  |  |  | 			div = S3C2412_IISMOD_BCLK_32FS; | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2009-09-15 19:02:38 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-10 16:48:51 +09:00
										 |  |  | 		case 24: | 
					
						
							|  |  |  | 			div = S3C2412_IISMOD_BCLK_24FS; | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2009-09-15 19:02:38 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-10 16:48:51 +09:00
										 |  |  | 		case 48: | 
					
						
							|  |  |  | 			div = S3C2412_IISMOD_BCLK_48FS; | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2009-09-15 19:02:38 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-10 16:48:51 +09:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 			return -EINVAL; | 
					
						
							| 
									
										
										
										
											2009-09-15 19:02:38 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 		reg = readl(i2s->regs + S3C2412_IISMOD); | 
					
						
							|  |  |  | 		reg &= ~S3C2412_IISMOD_BCLK_MASK; | 
					
						
							|  |  |  | 		writel(reg | div, i2s->regs + S3C2412_IISMOD); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-06 18:04:34 +00:00
										 |  |  | 		pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD)); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case S3C_I2SV2_DIV_RCLK: | 
					
						
							| 
									
										
										
										
											2010-03-10 16:48:51 +09:00
										 |  |  | 		switch (div) { | 
					
						
							|  |  |  | 		case 256: | 
					
						
							|  |  |  | 			div = S3C2412_IISMOD_RCLK_256FS; | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-10 16:48:51 +09:00
										 |  |  | 		case 384: | 
					
						
							|  |  |  | 			div = S3C2412_IISMOD_RCLK_384FS; | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-10 16:48:51 +09:00
										 |  |  | 		case 512: | 
					
						
							|  |  |  | 			div = S3C2412_IISMOD_RCLK_512FS; | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-10 16:48:51 +09:00
										 |  |  | 		case 768: | 
					
						
							|  |  |  | 			div = S3C2412_IISMOD_RCLK_768FS; | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-10 16:48:51 +09:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 			return -EINVAL; | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		reg = readl(i2s->regs + S3C2412_IISMOD); | 
					
						
							|  |  |  | 		reg &= ~S3C2412_IISMOD_RCLK_MASK; | 
					
						
							|  |  |  | 		writel(reg | div, i2s->regs + S3C2412_IISMOD); | 
					
						
							| 
									
										
										
										
											2009-03-06 18:04:34 +00:00
										 |  |  | 		pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD)); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case S3C_I2SV2_DIV_PRESCALER: | 
					
						
							|  |  |  | 		if (div >= 0) { | 
					
						
							|  |  |  | 			writel((div << 8) | S3C2412_IISPSR_PSREN, | 
					
						
							|  |  |  | 			       i2s->regs + S3C2412_IISPSR); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			writel(0x0, i2s->regs + S3C2412_IISPSR); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-03-06 18:04:34 +00:00
										 |  |  | 		pr_debug("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR)); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-04 14:54:40 +00:00
										 |  |  | static snd_pcm_sframes_t s3c2412_i2s_delay(struct snd_pcm_substream *substream, | 
					
						
							|  |  |  | 					   struct snd_soc_dai *dai) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct s3c_i2sv2_info *i2s = to_info(dai); | 
					
						
							|  |  |  | 	u32 reg = readl(i2s->regs + S3C2412_IISFIC); | 
					
						
							|  |  |  | 	snd_pcm_sframes_t delay; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 
					
						
							|  |  |  | 		delay = S3C2412_IISFIC_TXCOUNT(reg); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		delay = S3C2412_IISFIC_RXCOUNT(reg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return delay; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-27 15:56:45 +09:00
										 |  |  | struct clk *s3c_i2sv2_get_clock(struct snd_soc_dai *cpu_dai) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct s3c_i2sv2_info *i2s = to_info(cpu_dai); | 
					
						
							|  |  |  | 	u32 iismod = readl(i2s->regs + S3C2412_IISMOD); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (iismod & S3C2412_IISMOD_IMS_SYSMUX) | 
					
						
							|  |  |  | 		return i2s->iis_cclk; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		return i2s->iis_pclk; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(s3c_i2sv2_get_clock); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | /* default table of all avaialable root fs divisors */ | 
					
						
							|  |  |  | static unsigned int iis_fs_tab[] = { 256, 512, 384, 768 }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-16 10:32:23 +01:00
										 |  |  | int s3c_i2sv2_iis_calc_rate(struct s3c_i2sv2_rate_calc *info, | 
					
						
							|  |  |  | 			    unsigned int *fstab, | 
					
						
							|  |  |  | 			    unsigned int rate, struct clk *clk) | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	unsigned long clkrate = clk_get_rate(clk); | 
					
						
							|  |  |  | 	unsigned int div; | 
					
						
							|  |  |  | 	unsigned int fsclk; | 
					
						
							|  |  |  | 	unsigned int actual; | 
					
						
							|  |  |  | 	unsigned int fs; | 
					
						
							|  |  |  | 	unsigned int fsdiv; | 
					
						
							|  |  |  | 	signed int deviation = 0; | 
					
						
							|  |  |  | 	unsigned int best_fs = 0; | 
					
						
							|  |  |  | 	unsigned int best_div = 0; | 
					
						
							|  |  |  | 	unsigned int best_rate = 0; | 
					
						
							|  |  |  | 	unsigned int best_deviation = INT_MAX; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-30 13:13:55 +01:00
										 |  |  | 	pr_debug("Input clock rate %ldHz\n", clkrate); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 	if (fstab == NULL) | 
					
						
							|  |  |  | 		fstab = iis_fs_tab; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (fs = 0; fs < ARRAY_SIZE(iis_fs_tab); fs++) { | 
					
						
							|  |  |  | 		fsdiv = iis_fs_tab[fs]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		fsclk = clkrate / fsdiv; | 
					
						
							|  |  |  | 		div = fsclk / rate; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if ((fsclk % rate) > (rate / 2)) | 
					
						
							|  |  |  | 			div++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (div <= 1) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		actual = clkrate / (fsdiv * div); | 
					
						
							|  |  |  | 		deviation = actual - rate; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-27 17:08:39 -07:00
										 |  |  | 		printk(KERN_DEBUG "%ufs: div %u => result %u, deviation %d\n", | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 		       fsdiv, div, actual, deviation); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		deviation = abs(deviation); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (deviation < best_deviation) { | 
					
						
							|  |  |  | 			best_fs = fsdiv; | 
					
						
							|  |  |  | 			best_div = div; | 
					
						
							|  |  |  | 			best_rate = actual; | 
					
						
							|  |  |  | 			best_deviation = deviation; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (deviation == 0) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-27 17:08:39 -07:00
										 |  |  | 	printk(KERN_DEBUG "best: fs=%u, div=%u, rate=%u\n", | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 	       best_fs, best_div, best_rate); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	info->fs_div = best_fs; | 
					
						
							|  |  |  | 	info->clk_div = best_div; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2009-04-16 10:32:23 +01:00
										 |  |  | EXPORT_SYMBOL_GPL(s3c_i2sv2_iis_calc_rate); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-17 20:15:21 +00:00
										 |  |  | int s3c_i2sv2_probe(struct snd_soc_dai *dai, | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 		    struct s3c_i2sv2_info *i2s, | 
					
						
							|  |  |  | 		    unsigned long base) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2010-03-17 20:15:21 +00:00
										 |  |  | 	struct device *dev = dai->dev; | 
					
						
							| 
									
										
										
										
											2009-04-30 13:13:14 +01:00
										 |  |  | 	unsigned int iismod; | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	i2s->dev = dev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* record our i2s structure for later use in the callbacks */ | 
					
						
							| 
									
										
										
										
											2010-03-17 20:15:21 +00:00
										 |  |  | 	snd_soc_dai_set_drvdata(dai, i2s); | 
					
						
							| 
									
										
										
										
											2009-04-30 13:09:33 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 	i2s->regs = ioremap(base, 0x100); | 
					
						
							|  |  |  | 	if (i2s->regs == NULL) { | 
					
						
							|  |  |  | 		dev_err(dev, "cannot ioremap registers\n"); | 
					
						
							|  |  |  | 		return -ENXIO; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	i2s->iis_pclk = clk_get(dev, "iis"); | 
					
						
							| 
									
										
										
										
											2009-09-15 19:02:38 +09:00
										 |  |  | 	if (IS_ERR(i2s->iis_pclk)) { | 
					
						
							| 
									
										
										
										
											2009-03-06 18:13:43 +00:00
										 |  |  | 		dev_err(dev, "failed to get iis_clock\n"); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 		iounmap(i2s->regs); | 
					
						
							|  |  |  | 		return -ENOENT; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	clk_enable(i2s->iis_pclk); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-30 13:13:14 +01:00
										 |  |  | 	/* Mark ourselves as in TXRX mode so we can run through our cleanup
 | 
					
						
							|  |  |  | 	 * process without warnings. */ | 
					
						
							|  |  |  | 	iismod = readl(i2s->regs + S3C2412_IISMOD); | 
					
						
							|  |  |  | 	iismod |= S3C2412_IISMOD_MODE_TXRX; | 
					
						
							|  |  |  | 	writel(iismod, i2s->regs + S3C2412_IISMOD); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 	s3c2412_snd_txctrl(i2s, 0); | 
					
						
							|  |  |  | 	s3c2412_snd_rxctrl(i2s, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(s3c_i2sv2_probe); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_PM
 | 
					
						
							|  |  |  | static int s3c2412_i2s_suspend(struct snd_soc_dai *dai) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct s3c_i2sv2_info *i2s = to_info(dai); | 
					
						
							|  |  |  | 	u32 iismod; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (dai->active) { | 
					
						
							|  |  |  | 		i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD); | 
					
						
							|  |  |  | 		i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON); | 
					
						
							|  |  |  | 		i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* some basic suspend checks */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		iismod = readl(i2s->regs + S3C2412_IISMOD); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (iismod & S3C2412_IISCON_RXDMA_ACTIVE) | 
					
						
							|  |  |  | 			pr_warning("%s: RXDMA active?\n", __func__); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (iismod & S3C2412_IISCON_TXDMA_ACTIVE) | 
					
						
							|  |  |  | 			pr_warning("%s: TXDMA active?\n", __func__); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (iismod & S3C2412_IISCON_IIS_ACTIVE) | 
					
						
							|  |  |  | 			pr_warning("%s: IIS active\n", __func__); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int s3c2412_i2s_resume(struct snd_soc_dai *dai) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct s3c_i2sv2_info *i2s = to_info(dai); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pr_info("dai_active %d, IISMOD %08x, IISCON %08x\n", | 
					
						
							|  |  |  | 		dai->active, i2s->suspend_iismod, i2s->suspend_iiscon); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (dai->active) { | 
					
						
							|  |  |  | 		writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON); | 
					
						
							|  |  |  | 		writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD); | 
					
						
							|  |  |  | 		writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH, | 
					
						
							|  |  |  | 		       i2s->regs + S3C2412_IISFIC); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ndelay(250); | 
					
						
							|  |  |  | 		writel(0x0, i2s->regs + S3C2412_IISFIC); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | #define s3c2412_i2s_suspend NULL
 | 
					
						
							|  |  |  | #define s3c2412_i2s_resume  NULL
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-17 20:15:21 +00:00
										 |  |  | int s3c_i2sv2_register_dai(struct device *dev, int id, | 
					
						
							|  |  |  | 		struct snd_soc_dai_driver *drv) | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2010-03-17 20:15:21 +00:00
										 |  |  | 	struct snd_soc_dai_ops *ops = drv->ops; | 
					
						
							| 
									
										
										
										
											2009-04-16 10:32:22 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ops->trigger = s3c2412_i2s_trigger; | 
					
						
							| 
									
										
										
										
											2010-03-10 16:48:58 +09:00
										 |  |  | 	if (!ops->hw_params) | 
					
						
							|  |  |  | 		ops->hw_params = s3c_i2sv2_hw_params; | 
					
						
							| 
									
										
										
										
											2009-04-16 10:32:22 +01:00
										 |  |  | 	ops->set_fmt = s3c2412_i2s_set_fmt; | 
					
						
							|  |  |  | 	ops->set_clkdiv = s3c2412_i2s_set_clkdiv; | 
					
						
							| 
									
										
										
										
											2010-04-27 15:57:05 +09:00
										 |  |  | 	ops->set_sysclk = s3c_i2sv2_set_sysclk; | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-04 14:54:40 +00:00
										 |  |  | 	/* Allow overriding by (for example) IISv4 */ | 
					
						
							|  |  |  | 	if (!ops->delay) | 
					
						
							| 
									
										
										
										
											2010-03-10 16:48:50 +09:00
										 |  |  | 		ops->delay = s3c2412_i2s_delay; | 
					
						
							| 
									
										
										
										
											2010-03-04 14:54:40 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-17 20:15:21 +00:00
										 |  |  | 	drv->suspend = s3c2412_i2s_suspend; | 
					
						
							|  |  |  | 	drv->resume = s3c2412_i2s_resume; | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-21 08:06:56 +09:00
										 |  |  | 	return snd_soc_register_dai(dev, drv); | 
					
						
							| 
									
										
										
										
											2009-03-04 00:49:30 +00:00
										 |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(s3c_i2sv2_register_dai); | 
					
						
							| 
									
										
										
										
											2009-04-23 15:43:45 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); |