diff options
Diffstat (limited to 'sound/soc/sh/fsi.c')
| -rw-r--r-- | sound/soc/sh/fsi.c | 227 | 
1 files changed, 144 insertions, 83 deletions
| diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 42813b808389..993abb730dfa 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -67,6 +67,7 @@  /* DOFF_ST */  #define ERR_OVER	0x00000010  #define ERR_UNDER	0x00000001 +#define ST_ERR		(ERR_OVER | ERR_UNDER)  /* CLK_RST */  #define B_CLK		0x00000010 @@ -92,6 +93,7 @@  struct fsi_priv {  	void __iomem *base;  	struct snd_pcm_substream *substream; +	struct fsi_master *master;  	int fifo_max;  	int chan; @@ -108,10 +110,9 @@ struct fsi_master {  	struct fsi_priv fsia;  	struct fsi_priv fsib;  	struct sh_fsi_platform_info *info; +	spinlock_t lock;  }; -static struct fsi_master *master; -  /************************************************************************ @@ -119,35 +120,35 @@ static struct fsi_master *master;  ************************************************************************/ -static int __fsi_reg_write(u32 reg, u32 data) +static void __fsi_reg_write(u32 reg, u32 data)  {  	/* valid data area is 24bit */  	data &= 0x00ffffff; -	return ctrl_outl(data, reg); +	__raw_writel(data, reg);  }  static u32 __fsi_reg_read(u32 reg)  { -	return ctrl_inl(reg); +	return __raw_readl(reg);  } -static int __fsi_reg_mask_set(u32 reg, u32 mask, u32 data) +static void __fsi_reg_mask_set(u32 reg, u32 mask, u32 data)  {  	u32 val = __fsi_reg_read(reg);  	val &= ~mask;  	val |= data & mask; -	return __fsi_reg_write(reg, val); +	__fsi_reg_write(reg, val);  } -static int fsi_reg_write(struct fsi_priv *fsi, u32 reg, u32 data) +static void fsi_reg_write(struct fsi_priv *fsi, u32 reg, u32 data)  {  	if (reg > REG_END) -		return -1; +		return; -	return __fsi_reg_write((u32)(fsi->base + reg), data); +	__fsi_reg_write((u32)(fsi->base + reg), data);  }  static u32 fsi_reg_read(struct fsi_priv *fsi, u32 reg) @@ -158,39 +159,55 @@ static u32 fsi_reg_read(struct fsi_priv *fsi, u32 reg)  	return __fsi_reg_read((u32)(fsi->base + reg));  } -static int fsi_reg_mask_set(struct fsi_priv *fsi, u32 reg, u32 mask, u32 data) +static void fsi_reg_mask_set(struct fsi_priv *fsi, u32 reg, u32 mask, u32 data)  {  	if (reg > REG_END) -		return -1; +		return; -	return __fsi_reg_mask_set((u32)(fsi->base + reg), mask, data); +	__fsi_reg_mask_set((u32)(fsi->base + reg), mask, data);  } -static int fsi_master_write(u32 reg, u32 data) +static void fsi_master_write(struct fsi_master *master, u32 reg, u32 data)  { +	unsigned long flags; +  	if ((reg < MREG_START) ||  	    (reg > MREG_END)) -		return -1; +		return; -	return __fsi_reg_write((u32)(master->base + reg), data); +	spin_lock_irqsave(&master->lock, flags); +	__fsi_reg_write((u32)(master->base + reg), data); +	spin_unlock_irqrestore(&master->lock, flags);  } -static u32 fsi_master_read(u32 reg) +static u32 fsi_master_read(struct fsi_master *master, u32 reg)  { +	u32 ret; +	unsigned long flags; +  	if ((reg < MREG_START) ||  	    (reg > MREG_END))  		return 0; -	return __fsi_reg_read((u32)(master->base + reg)); +	spin_lock_irqsave(&master->lock, flags); +	ret = __fsi_reg_read((u32)(master->base + reg)); +	spin_unlock_irqrestore(&master->lock, flags); + +	return ret;  } -static int fsi_master_mask_set(u32 reg, u32 mask, u32 data) +static void fsi_master_mask_set(struct fsi_master *master, +			       u32 reg, u32 mask, u32 data)  { +	unsigned long flags; +  	if ((reg < MREG_START) ||  	    (reg > MREG_END)) -		return -1; +		return; -	return __fsi_reg_mask_set((u32)(master->base + reg), mask, data); +	spin_lock_irqsave(&master->lock, flags); +	__fsi_reg_mask_set((u32)(master->base + reg), mask, data); +	spin_unlock_irqrestore(&master->lock, flags);  }  /************************************************************************ @@ -200,43 +217,35 @@ static int fsi_master_mask_set(u32 reg, u32 mask, u32 data)  ************************************************************************/ -static struct fsi_priv *fsi_get(struct snd_pcm_substream *substream) +static struct fsi_master *fsi_get_master(struct fsi_priv *fsi)  { -	struct snd_soc_pcm_runtime *rtd; -	struct fsi_priv *fsi = NULL; +	return fsi->master; +} -	if (!substream || !master) -		return NULL; +static int fsi_is_port_a(struct fsi_priv *fsi) +{ +	return fsi->master->base == fsi->base; +} -	rtd = substream->private_data; -	switch (rtd->dai->cpu_dai->id) { -	case 0: -		fsi = &master->fsia; -		break; -	case 1: -		fsi = &master->fsib; -		break; -	} +static struct snd_soc_dai *fsi_get_dai(struct snd_pcm_substream *substream) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_dai_link *machine = rtd->dai; -	return fsi; +	return  machine->cpu_dai;  } -static int fsi_is_port_a(struct fsi_priv *fsi) +static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream)  { -	/* return -	 * 1 : port a -	 * 0 : port b -	 */ +	struct snd_soc_dai *dai = fsi_get_dai(substream); -	if (fsi == &master->fsia) -		return 1; - -	return 0; +	return dai->private_data;  }  static u32 fsi_get_info_flags(struct fsi_priv *fsi)  {  	int is_porta = fsi_is_port_a(fsi); +	struct fsi_master *master = fsi_get_master(fsi);  	return is_porta ? master->info->porta_flags :  		master->info->portb_flags; @@ -314,27 +323,30 @@ static int fsi_get_fifo_residue(struct fsi_priv *fsi, int is_play)  static void fsi_irq_enable(struct fsi_priv *fsi, int is_play)  {  	u32 data = fsi_port_ab_io_bit(fsi, is_play); +	struct fsi_master *master = fsi_get_master(fsi); -	fsi_master_mask_set(IMSK,  data, data); -	fsi_master_mask_set(IEMSK, data, data); +	fsi_master_mask_set(master, IMSK,  data, data); +	fsi_master_mask_set(master, IEMSK, data, data);  }  static void fsi_irq_disable(struct fsi_priv *fsi, int is_play)  {  	u32 data = fsi_port_ab_io_bit(fsi, is_play); +	struct fsi_master *master = fsi_get_master(fsi); -	fsi_master_mask_set(IMSK,  data, 0); -	fsi_master_mask_set(IEMSK, data, 0); +	fsi_master_mask_set(master, IMSK,  data, 0); +	fsi_master_mask_set(master, IEMSK, data, 0);  }  static void fsi_clk_ctrl(struct fsi_priv *fsi, int enable)  {  	u32 val = fsi_is_port_a(fsi) ? (1 << 0) : (1 << 4); +	struct fsi_master *master = fsi_get_master(fsi);  	if (enable) -		fsi_master_mask_set(CLK_RST, val, val); +		fsi_master_mask_set(master, CLK_RST, val, val);  	else -		fsi_master_mask_set(CLK_RST, val, 0); +		fsi_master_mask_set(master, CLK_RST, val, 0);  }  static void fsi_irq_init(struct fsi_priv *fsi, int is_play) @@ -355,43 +367,46 @@ static void fsi_irq_init(struct fsi_priv *fsi, int is_play)  	fsi_reg_mask_set(fsi, ctrl, FIFO_CLR, FIFO_CLR);  	/* clear interrupt factor */ -	fsi_master_mask_set(INT_ST, data, 0); +	fsi_master_mask_set(fsi_get_master(fsi), INT_ST, data, 0);  } -static void fsi_soft_all_reset(void) +static void fsi_soft_all_reset(struct fsi_master *master)  { -	u32 status = fsi_master_read(SOFT_RST); +	u32 status = fsi_master_read(master, SOFT_RST);  	/* port AB reset */  	status &= 0x000000ff; -	fsi_master_write(SOFT_RST, status); +	fsi_master_write(master, SOFT_RST, status);  	mdelay(10);  	/* soft reset */  	status &= 0x000000f0; -	fsi_master_write(SOFT_RST, status); +	fsi_master_write(master, SOFT_RST, status);  	status |= 0x00000001; -	fsi_master_write(SOFT_RST, status); +	fsi_master_write(master, SOFT_RST, status);  	mdelay(10);  }  /* playback interrupt */ -static int fsi_data_push(struct fsi_priv *fsi) +static int fsi_data_push(struct fsi_priv *fsi, int startup)  {  	struct snd_pcm_runtime *runtime;  	struct snd_pcm_substream *substream = NULL; +	u32 status;  	int send;  	int fifo_free;  	int width;  	u8 *start; -	int i; +	int i, over_period;  	if (!fsi			||  	    !fsi->substream		||  	    !fsi->substream->runtime)  		return -EINVAL; -	runtime = fsi->substream->runtime; +	over_period	= 0; +	substream	= fsi->substream; +	runtime		= substream->runtime;  	/* FSI FIFO has limit.  	 * So, this driver can not send periods data at a time @@ -399,7 +414,7 @@ static int fsi_data_push(struct fsi_priv *fsi)  	if (fsi->byte_offset >=  	    fsi->period_len * (fsi->periods + 1)) { -		substream = fsi->substream; +		over_period = 1;  		fsi->periods = (fsi->periods + 1) % runtime->periods;  		if (0 == fsi->periods) @@ -438,30 +453,44 @@ static int fsi_data_push(struct fsi_priv *fsi)  	fsi->byte_offset += send * width; +	status = fsi_reg_read(fsi, DOFF_ST); +	if (!startup) { +		struct snd_soc_dai *dai = fsi_get_dai(substream); + +		if (status & ERR_OVER) +			dev_err(dai->dev, "over run\n"); +		if (status & ERR_UNDER) +			dev_err(dai->dev, "under run\n"); +	} +	fsi_reg_write(fsi, DOFF_ST, 0); +  	fsi_irq_enable(fsi, 1); -	if (substream) +	if (over_period)  		snd_pcm_period_elapsed(substream);  	return 0;  } -static int fsi_data_pop(struct fsi_priv *fsi) +static int fsi_data_pop(struct fsi_priv *fsi, int startup)  {  	struct snd_pcm_runtime *runtime;  	struct snd_pcm_substream *substream = NULL; +	u32 status;  	int free;  	int fifo_fill;  	int width;  	u8 *start; -	int i; +	int i, over_period;  	if (!fsi			||  	    !fsi->substream		||  	    !fsi->substream->runtime)  		return -EINVAL; -	runtime = fsi->substream->runtime; +	over_period	= 0; +	substream	= fsi->substream; +	runtime		= substream->runtime;  	/* FSI FIFO has limit.  	 * So, this driver can not send periods data at a time @@ -469,7 +498,7 @@ static int fsi_data_pop(struct fsi_priv *fsi)  	if (fsi->byte_offset >=  	    fsi->period_len * (fsi->periods + 1)) { -		substream = fsi->substream; +		over_period = 1;  		fsi->periods = (fsi->periods + 1) % runtime->periods;  		if (0 == fsi->periods) @@ -507,9 +536,20 @@ static int fsi_data_pop(struct fsi_priv *fsi)  	fsi->byte_offset += fifo_fill * width; +	status = fsi_reg_read(fsi, DIFF_ST); +	if (!startup) { +		struct snd_soc_dai *dai = fsi_get_dai(substream); + +		if (status & ERR_OVER) +			dev_err(dai->dev, "over run\n"); +		if (status & ERR_UNDER) +			dev_err(dai->dev, "under run\n"); +	} +	fsi_reg_write(fsi, DIFF_ST, 0); +  	fsi_irq_enable(fsi, 0); -	if (substream) +	if (over_period)  		snd_pcm_period_elapsed(substream);  	return 0; @@ -517,23 +557,24 @@ static int fsi_data_pop(struct fsi_priv *fsi)  static irqreturn_t fsi_interrupt(int irq, void *data)  { -	u32 status = fsi_master_read(SOFT_RST) & ~0x00000010; -	u32 int_st = fsi_master_read(INT_ST); +	struct fsi_master *master = data; +	u32 status = fsi_master_read(master, SOFT_RST) & ~0x00000010; +	u32 int_st = fsi_master_read(master, INT_ST);  	/* clear irq status */ -	fsi_master_write(SOFT_RST, status); -	fsi_master_write(SOFT_RST, status | 0x00000010); +	fsi_master_write(master, SOFT_RST, status); +	fsi_master_write(master, SOFT_RST, status | 0x00000010);  	if (int_st & INT_A_OUT) -		fsi_data_push(&master->fsia); +		fsi_data_push(&master->fsia, 0);  	if (int_st & INT_B_OUT) -		fsi_data_push(&master->fsib); +		fsi_data_push(&master->fsib, 0);  	if (int_st & INT_A_IN) -		fsi_data_pop(&master->fsia); +		fsi_data_pop(&master->fsia, 0);  	if (int_st & INT_B_IN) -		fsi_data_pop(&master->fsib); +		fsi_data_pop(&master->fsib, 0); -	fsi_master_write(INT_ST, 0x0000000); +	fsi_master_write(master, INT_ST, 0x0000000);  	return IRQ_HANDLED;  } @@ -548,7 +589,7 @@ static irqreturn_t fsi_interrupt(int irq, void *data)  static int fsi_dai_startup(struct snd_pcm_substream *substream,  			   struct snd_soc_dai *dai)  { -	struct fsi_priv *fsi = fsi_get(substream); +	struct fsi_priv *fsi = fsi_get_priv(substream);  	const char *msg;  	u32 flags = fsi_get_info_flags(fsi);  	u32 fmt; @@ -667,7 +708,7 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream,  static void fsi_dai_shutdown(struct snd_pcm_substream *substream,  			     struct snd_soc_dai *dai)  { -	struct fsi_priv *fsi = fsi_get(substream); +	struct fsi_priv *fsi = fsi_get_priv(substream);  	int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;  	fsi_irq_disable(fsi, is_play); @@ -679,7 +720,7 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream,  static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,  			   struct snd_soc_dai *dai)  { -	struct fsi_priv *fsi = fsi_get(substream); +	struct fsi_priv *fsi = fsi_get_priv(substream);  	struct snd_pcm_runtime *runtime = substream->runtime;  	int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;  	int ret = 0; @@ -689,7 +730,7 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,  		fsi_stream_push(fsi, substream,  				frames_to_bytes(runtime, runtime->buffer_size),  				frames_to_bytes(runtime, runtime->period_size)); -		ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi); +		ret = is_play ? fsi_data_push(fsi, 1) : fsi_data_pop(fsi, 1);  		break;  	case SNDRV_PCM_TRIGGER_STOP:  		fsi_irq_disable(fsi, is_play); @@ -760,7 +801,7 @@ static int fsi_hw_free(struct snd_pcm_substream *substream)  static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream)  {  	struct snd_pcm_runtime *runtime = substream->runtime; -	struct fsi_priv *fsi = fsi_get(substream); +	struct fsi_priv *fsi = fsi_get_priv(substream);  	long location;  	location = (fsi->byte_offset - 1); @@ -870,10 +911,16 @@ EXPORT_SYMBOL_GPL(fsi_soc_platform);  ************************************************************************/  static int fsi_probe(struct platform_device *pdev)  { +	struct fsi_master *master;  	struct resource *res;  	unsigned int irq;  	int ret; +	if (0 != pdev->id) { +		dev_err(&pdev->dev, "current fsi support id 0 only now\n"); +		return -ENODEV; +	} +  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	irq = platform_get_irq(pdev, 0);  	if (!res || (int)irq <= 0) { @@ -899,15 +946,20 @@ static int fsi_probe(struct platform_device *pdev)  	master->irq		= irq;  	master->info		= pdev->dev.platform_data;  	master->fsia.base	= master->base; +	master->fsia.master	= master;  	master->fsib.base	= master->base + 0x40; +	master->fsib.master	= master; +	spin_lock_init(&master->lock);  	pm_runtime_enable(&pdev->dev);  	pm_runtime_resume(&pdev->dev);  	fsi_soc_dai[0].dev		= &pdev->dev; +	fsi_soc_dai[0].private_data	= &master->fsia;  	fsi_soc_dai[1].dev		= &pdev->dev; +	fsi_soc_dai[1].private_data	= &master->fsib; -	fsi_soft_all_reset(); +	fsi_soft_all_reset(master);  	ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED, "fsi", master);  	if (ret) { @@ -937,6 +989,10 @@ exit:  static int fsi_remove(struct platform_device *pdev)  { +	struct fsi_master *master; + +	master = fsi_get_master(fsi_soc_dai[0].private_data); +  	snd_soc_unregister_dais(fsi_soc_dai, ARRAY_SIZE(fsi_soc_dai));  	snd_soc_unregister_platform(&fsi_soc_platform); @@ -946,7 +1002,12 @@ static int fsi_remove(struct platform_device *pdev)  	iounmap(master->base);  	kfree(master); -	master = NULL; + +	fsi_soc_dai[0].dev		= NULL; +	fsi_soc_dai[0].private_data	= NULL; +	fsi_soc_dai[1].dev		= NULL; +	fsi_soc_dai[1].private_data	= NULL; +  	return 0;  } | 
