Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 76067540 authored by Dong Aisheng's avatar Dong Aisheng Committed by Mark Brown
Browse files

ASoC: mxs-saif: add record function



1. add different clkmux mode handling
SAIF can use two instances to implement full duplex (playback &
recording) and record saif may work on EXTMASTER mode which is
using other saif's BITCLK&LRCLK.

The clkmux mode could be set in pdata->init() in mach-specific code.
For generic saif driver, it only needs to know who is his master
and the master id is also provided in mach-specific code.

2. support playback and capture simutaneously however the sample
rates can not be different due to hw limitation.

Signed-off-by: default avatarDong Aisheng <b29396@freescale.com>
Acked-by: default avatarWolfram Sang <w.sang@pengutronix.de>
Acked-by: default avatarLiam Girdwood <lrg@ti.com>
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
parent 5d42940c
Loading
Loading
Loading
Loading

include/sound/saif.h

0 → 100644
+16 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#ifndef __SOUND_SAIF_H__
#define __SOUND_SAIF_H__

struct mxs_saif_platform_data {
	int (*init) (void);
	int (*get_master_id) (unsigned int saif_id);
};
#endif
+131 −14
Original line number Original line Diff line number Diff line
@@ -23,10 +23,12 @@
#include <linux/dma-mapping.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/delay.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc.h>
#include <sound/saif.h>
#include <mach/dma.h>
#include <mach/dma.h>
#include <asm/mach-types.h>
#include <asm/mach-types.h>
#include <mach/hardware.h>
#include <mach/hardware.h>
@@ -36,6 +38,24 @@


static struct mxs_saif *mxs_saif[2];
static struct mxs_saif *mxs_saif[2];


/*
 * SAIF is a little different with other normal SOC DAIs on clock using.
 *
 * For MXS, two SAIF modules are instantiated on-chip.
 * Each SAIF has a set of clock pins and can be operating in master
 * mode simultaneously if they are connected to different off-chip codecs.
 * Also, one of the two SAIFs can master or drive the clock pins while the
 * other SAIF, in slave mode, receives clocking from the master SAIF.
 * This also means that both SAIFs must operate at the same sample rate.
 *
 * We abstract this as each saif has a master, the master could be
 * himself or other saifs. In the generic saif driver, saif does not need
 * to know the different clkmux. Saif only needs to know who is his master
 * and operating his master to generate the proper clock rate for him.
 * The master id is provided in mach-specific layer according to different
 * clkmux setting.
 */

static int mxs_saif_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
static int mxs_saif_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
			int clk_id, unsigned int freq, int dir)
			int clk_id, unsigned int freq, int dir)
{
{
@@ -51,6 +71,17 @@ static int mxs_saif_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
	return 0;
	return 0;
}
}


/*
 * Since SAIF may work on EXTMASTER mode, IOW, it's working BITCLK&LRCLK
 * is provided by other SAIF, we provide a interface here to get its master
 * from its master_id.
 * Note that the master could be himself.
 */
static inline struct mxs_saif *mxs_saif_get_master(struct mxs_saif * saif)
{
	return mxs_saif[saif->master_id];
}

/*
/*
 * Set SAIF clock and MCLK
 * Set SAIF clock and MCLK
 */
 */
@@ -60,8 +91,26 @@ static int mxs_saif_set_clk(struct mxs_saif *saif,
{
{
	u32 scr;
	u32 scr;
	int ret;
	int ret;
	struct mxs_saif *master_saif;


	scr = __raw_readl(saif->base + SAIF_CTRL);
	dev_dbg(saif->dev, "mclk %d rate %d\n", mclk, rate);

	/* Set master saif to generate proper clock */
	master_saif = mxs_saif_get_master(saif);
	if (!master_saif)
		return -EINVAL;

	dev_dbg(saif->dev, "master saif%d\n", master_saif->id);

	/* Checking if can playback and capture simutaneously */
	if (master_saif->ongoing && rate != master_saif->cur_rate) {
		dev_err(saif->dev,
			"can not change clock, master saif%d(rate %d) is ongoing\n",
			master_saif->id, master_saif->cur_rate);
		return -EINVAL;
	}

	scr = __raw_readl(master_saif->base + SAIF_CTRL);
	scr &= ~BM_SAIF_CTRL_BITCLK_MULT_RATE;
	scr &= ~BM_SAIF_CTRL_BITCLK_MULT_RATE;
	scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
	scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;


@@ -75,27 +124,29 @@ static int mxs_saif_set_clk(struct mxs_saif *saif,
	 *
	 *
	 * If MCLK is not used, we just set saif clk to 512*fs.
	 * If MCLK is not used, we just set saif clk to 512*fs.
	 */
	 */
	if (saif->mclk_in_use) {
	if (master_saif->mclk_in_use) {
		if (mclk % 32 == 0) {
		if (mclk % 32 == 0) {
			scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
			scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
			ret = clk_set_rate(saif->clk, 512 * rate);
			ret = clk_set_rate(master_saif->clk, 512 * rate);
		} else if (mclk % 48 == 0) {
		} else if (mclk % 48 == 0) {
			scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE;
			scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE;
			ret = clk_set_rate(saif->clk, 384 * rate);
			ret = clk_set_rate(master_saif->clk, 384 * rate);
		} else {
		} else {
			/* SAIF MCLK should be either 32x or 48x */
			/* SAIF MCLK should be either 32x or 48x */
			return -EINVAL;
			return -EINVAL;
		}
		}
	} else {
	} else {
		ret = clk_set_rate(saif->clk, 512 * rate);
		ret = clk_set_rate(master_saif->clk, 512 * rate);
		scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
		scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
	}
	}


	if (ret)
	if (ret)
		return ret;
		return ret;


	if (!saif->mclk_in_use) {
	master_saif->cur_rate = rate;
		__raw_writel(scr, saif->base + SAIF_CTRL);

	if (!master_saif->mclk_in_use) {
		__raw_writel(scr, master_saif->base + SAIF_CTRL);
		return 0;
		return 0;
	}
	}


@@ -137,7 +188,7 @@ static int mxs_saif_set_clk(struct mxs_saif *saif,
		return -EINVAL;
		return -EINVAL;
	}
	}


	__raw_writel(scr, saif->base + SAIF_CTRL);
	__raw_writel(scr, master_saif->base + SAIF_CTRL);


	return 0;
	return 0;
}
}
@@ -183,6 +234,7 @@ int mxs_saif_get_mclk(unsigned int saif_id, unsigned int mclk,
	struct mxs_saif *saif = mxs_saif[saif_id];
	struct mxs_saif *saif = mxs_saif[saif_id];
	u32 stat;
	u32 stat;
	int ret;
	int ret;
	struct mxs_saif *master_saif;


	if (!saif)
	if (!saif)
		return -EINVAL;
		return -EINVAL;
@@ -195,6 +247,12 @@ int mxs_saif_get_mclk(unsigned int saif_id, unsigned int mclk,
	__raw_writel(BM_SAIF_CTRL_CLKGATE,
	__raw_writel(BM_SAIF_CTRL_CLKGATE,
		saif->base + SAIF_CTRL + MXS_CLR_ADDR);
		saif->base + SAIF_CTRL + MXS_CLR_ADDR);


	master_saif = mxs_saif_get_master(saif);
	if (saif != master_saif) {
		dev_err(saif->dev, "can not get mclk from a non-master saif\n");
		return -EINVAL;
	}

	stat = __raw_readl(saif->base + SAIF_STAT);
	stat = __raw_readl(saif->base + SAIF_STAT);
	if (stat & BM_SAIF_STAT_BUSY) {
	if (stat & BM_SAIF_STAT_BUSY) {
		dev_err(saif->dev, "error: busy\n");
		dev_err(saif->dev, "error: busy\n");
@@ -278,10 +336,17 @@ static int mxs_saif_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
	/*
	/*
	 * Note: We simply just support master mode since SAIF TX can only
	 * Note: We simply just support master mode since SAIF TX can only
	 * work as master.
	 * work as master.
	 * Here the master is relative to codec side.
	 * Saif internally could be slave when working on EXTMASTER mode.
	 * We just hide this to machine driver.
	 */
	 */
	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
	case SND_SOC_DAIFMT_CBS_CFS:
	case SND_SOC_DAIFMT_CBS_CFS:
		if (saif->id == saif->master_id)
			scr &= ~BM_SAIF_CTRL_SLAVE_MODE;
			scr &= ~BM_SAIF_CTRL_SLAVE_MODE;
		else
			scr |= BM_SAIF_CTRL_SLAVE_MODE;

		__raw_writel(scr | scr0, saif->base + SAIF_CTRL);
		__raw_writel(scr | scr0, saif->base + SAIF_CTRL);
		break;
		break;
	default:
	default:
@@ -396,6 +461,12 @@ static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd,
				struct snd_soc_dai *cpu_dai)
				struct snd_soc_dai *cpu_dai)
{
{
	struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
	struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
	struct mxs_saif *master_saif;
	u32 delay;

	master_saif = mxs_saif_get_master(saif);
	if (!master_saif)
		return -EINVAL;


	switch (cmd) {
	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
	case SNDRV_PCM_TRIGGER_START:
@@ -403,10 +474,20 @@ static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd,
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
		dev_dbg(cpu_dai->dev, "start\n");
		dev_dbg(cpu_dai->dev, "start\n");


		clk_enable(master_saif->clk);
		if (!master_saif->mclk_in_use)
			__raw_writel(BM_SAIF_CTRL_RUN,
				master_saif->base + SAIF_CTRL + MXS_SET_ADDR);

		/*
		 * If the saif's master is not himself, we also need to enable
		 * itself clk for its internal basic logic to work.
		 */
		if (saif != master_saif) {
			clk_enable(saif->clk);
			clk_enable(saif->clk);
		if (!saif->mclk_in_use)
			__raw_writel(BM_SAIF_CTRL_RUN,
			__raw_writel(BM_SAIF_CTRL_RUN,
				saif->base + SAIF_CTRL + MXS_SET_ADDR);
				saif->base + SAIF_CTRL + MXS_SET_ADDR);
		}


		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
			/*
			/*
@@ -422,20 +503,39 @@ static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd,
			__raw_readl(saif->base + SAIF_DATA);
			__raw_readl(saif->base + SAIF_DATA);
		}
		}


		dev_dbg(cpu_dai->dev, "CTRL 0x%x STAT 0x%x\n",
		master_saif->ongoing = 1;

		dev_dbg(saif->dev, "CTRL 0x%x STAT 0x%x\n",
			__raw_readl(saif->base + SAIF_CTRL),
			__raw_readl(saif->base + SAIF_CTRL),
			__raw_readl(saif->base + SAIF_STAT));
			__raw_readl(saif->base + SAIF_STAT));


		dev_dbg(master_saif->dev, "CTRL 0x%x STAT 0x%x\n",
			__raw_readl(master_saif->base + SAIF_CTRL),
			__raw_readl(master_saif->base + SAIF_STAT));
		break;
		break;
	case SNDRV_PCM_TRIGGER_SUSPEND:
	case SNDRV_PCM_TRIGGER_SUSPEND:
	case SNDRV_PCM_TRIGGER_STOP:
	case SNDRV_PCM_TRIGGER_STOP:
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
		dev_dbg(cpu_dai->dev, "stop\n");
		dev_dbg(cpu_dai->dev, "stop\n");


		clk_disable(saif->clk);
		/* wait a while for the current sample to complete */
		if (!saif->mclk_in_use)
		delay = USEC_PER_SEC / master_saif->cur_rate;

		if (!master_saif->mclk_in_use) {
			__raw_writel(BM_SAIF_CTRL_RUN,
				master_saif->base + SAIF_CTRL + MXS_CLR_ADDR);
			udelay(delay);
		}
		clk_disable(master_saif->clk);

		if (saif != master_saif) {
			__raw_writel(BM_SAIF_CTRL_RUN,
			__raw_writel(BM_SAIF_CTRL_RUN,
				saif->base + SAIF_CTRL + MXS_CLR_ADDR);
				saif->base + SAIF_CTRL + MXS_CLR_ADDR);
			udelay(delay);
			clk_disable(saif->clk);
		}

		master_saif->ongoing = 0;


		break;
		break;
	default:
	default:
@@ -519,16 +619,33 @@ static int mxs_saif_probe(struct platform_device *pdev)
{
{
	struct resource *res;
	struct resource *res;
	struct mxs_saif *saif;
	struct mxs_saif *saif;
	struct mxs_saif_platform_data *pdata;
	int ret = 0;
	int ret = 0;


	if (pdev->id >= ARRAY_SIZE(mxs_saif))
	if (pdev->id >= ARRAY_SIZE(mxs_saif))
		return -EINVAL;
		return -EINVAL;


	pdata = pdev->dev.platform_data;
	if (pdata && pdata->init) {
		ret = pdata->init();
		if (ret)
			return ret;
	}

	saif = kzalloc(sizeof(*saif), GFP_KERNEL);
	saif = kzalloc(sizeof(*saif), GFP_KERNEL);
	if (!saif)
	if (!saif)
		return -ENOMEM;
		return -ENOMEM;


	mxs_saif[pdev->id] = saif;
	mxs_saif[pdev->id] = saif;
	saif->id = pdev->id;

	saif->master_id = saif->id;
	if (pdata && pdata->get_master_id) {
		saif->master_id = pdata->get_master_id(saif->id);
		if (saif->master_id < 0 ||
			saif->master_id >= ARRAY_SIZE(mxs_saif))
			return -EINVAL;
	}


	saif->clk = clk_get(&pdev->dev, NULL);
	saif->clk = clk_get(&pdev->dev, NULL);
	if (IS_ERR(saif->clk)) {
	if (IS_ERR(saif->clk)) {
+4 −0
Original line number Original line Diff line number Diff line
@@ -118,6 +118,10 @@ struct mxs_saif {
	void __iomem *base;
	void __iomem *base;
	int irq;
	int irq;
	struct mxs_pcm_dma_params dma_param;
	struct mxs_pcm_dma_params dma_param;
	unsigned int id;
	unsigned int master_id;
	unsigned int cur_rate;
	unsigned int ongoing;


	struct platform_device *soc_platform_pdev;
	struct platform_device *soc_platform_pdev;
	u32 fifo_underrun;
	u32 fifo_underrun;