brintos

brintos / linux-shallow public Read only

0
0
Text · 13.5 KiB · 73f36c9 Raw
586 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * linux/sound/soc/pxa/mmp-sspa.c4 * Base on pxa2xx-ssp.c5 *6 * Copyright (C) 2011 Marvell International Ltd.7 */8#include <linux/init.h>9#include <linux/module.h>10#include <linux/platform_device.h>11#include <linux/delay.h>12#include <linux/clk.h>13#include <linux/slab.h>14#include <linux/io.h>15#include <linux/dmaengine.h>16#include <linux/pm_runtime.h>17 18#include <sound/core.h>19#include <sound/pcm.h>20#include <sound/initval.h>21#include <sound/pcm_params.h>22#include <sound/soc.h>23#include <sound/pxa2xx-lib.h>24#include <sound/dmaengine_pcm.h>25#include "mmp-sspa.h"26 27/*28 * SSPA audio private data29 */30struct sspa_priv {31	void __iomem *tx_base;32	void __iomem *rx_base;33 34	struct snd_dmaengine_dai_dma_data playback_dma_data;35	struct snd_dmaengine_dai_dma_data capture_dma_data;36	struct clk *clk;37	struct clk *audio_clk;38	struct clk *sysclk;39 40	int running_cnt;41	u32 sp;42	u32 ctrl;43};44 45static void mmp_sspa_tx_enable(struct sspa_priv *sspa)46{47	unsigned int sspa_sp = sspa->sp;48 49	sspa_sp &= ~SSPA_SP_MSL;50	sspa_sp |= SSPA_SP_S_EN;51	sspa_sp |= SSPA_SP_WEN;52	__raw_writel(sspa_sp, sspa->tx_base + SSPA_SP);53}54 55static void mmp_sspa_tx_disable(struct sspa_priv *sspa)56{57	unsigned int sspa_sp = sspa->sp;58 59	sspa_sp &= ~SSPA_SP_MSL;60	sspa_sp &= ~SSPA_SP_S_EN;61	sspa_sp |= SSPA_SP_WEN;62	__raw_writel(sspa_sp, sspa->tx_base + SSPA_SP);63}64 65static void mmp_sspa_rx_enable(struct sspa_priv *sspa)66{67	unsigned int sspa_sp = sspa->sp;68 69	sspa_sp |= SSPA_SP_S_EN;70	sspa_sp |= SSPA_SP_WEN;71	__raw_writel(sspa_sp, sspa->rx_base + SSPA_SP);72}73 74static void mmp_sspa_rx_disable(struct sspa_priv *sspa)75{76	unsigned int sspa_sp = sspa->sp;77 78	sspa_sp &= ~SSPA_SP_S_EN;79	sspa_sp |= SSPA_SP_WEN;80	__raw_writel(sspa_sp, sspa->rx_base + SSPA_SP);81}82 83static int mmp_sspa_startup(struct snd_pcm_substream *substream,84	struct snd_soc_dai *dai)85{86	struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai);87 88	clk_prepare_enable(sspa->sysclk);89	clk_prepare_enable(sspa->clk);90 91	return 0;92}93 94static void mmp_sspa_shutdown(struct snd_pcm_substream *substream,95	struct snd_soc_dai *dai)96{97	struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai);98 99	clk_disable_unprepare(sspa->clk);100	clk_disable_unprepare(sspa->sysclk);101}102 103/*104 * Set the SSP ports SYSCLK.105 */106static int mmp_sspa_set_dai_sysclk(struct snd_soc_dai *cpu_dai,107				    int clk_id, unsigned int freq, int dir)108{109	struct sspa_priv *sspa = snd_soc_dai_get_drvdata(cpu_dai);110	struct device *dev = cpu_dai->component->dev;111	int ret = 0;112 113	if (dev->of_node)114		return -ENOTSUPP;115 116	switch (clk_id) {117	case MMP_SSPA_CLK_AUDIO:118		ret = clk_set_rate(sspa->audio_clk, freq);119		if (ret)120			return ret;121		break;122	case MMP_SSPA_CLK_PLL:123	case MMP_SSPA_CLK_VCXO:124		/* not support yet */125		return -EINVAL;126	default:127		return -EINVAL;128	}129 130	return 0;131}132 133static int mmp_sspa_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id,134				 int source, unsigned int freq_in,135				 unsigned int freq_out)136{137	struct sspa_priv *sspa = snd_soc_dai_get_drvdata(cpu_dai);138	struct device *dev = cpu_dai->component->dev;139	int ret = 0;140 141	if (dev->of_node)142		return -ENOTSUPP;143 144	switch (pll_id) {145	case MMP_SYSCLK:146		ret = clk_set_rate(sspa->sysclk, freq_out);147		if (ret)148			return ret;149		break;150	case MMP_SSPA_CLK:151		ret = clk_set_rate(sspa->clk, freq_out);152		if (ret)153			return ret;154		break;155	default:156		return -ENODEV;157	}158 159	return 0;160}161 162/*163 * Set up the sspa dai format.164 */165static int mmp_sspa_set_dai_fmt(struct snd_soc_dai *cpu_dai,166				 unsigned int fmt)167{168	struct sspa_priv *sspa = snd_soc_dai_get_drvdata(cpu_dai);169 170	/* reset port settings */171	sspa->sp   = SSPA_SP_WEN | SSPA_SP_S_RST | SSPA_SP_FFLUSH;172	sspa->ctrl = 0;173 174	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {175	case SND_SOC_DAIFMT_BP_FP:176		sspa->sp |= SSPA_SP_MSL;177		break;178	case SND_SOC_DAIFMT_BC_FC:179		break;180	default:181		return -EINVAL;182	}183 184	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {185	case SND_SOC_DAIFMT_NB_NF:186		sspa->sp |= SSPA_SP_FSP;187		break;188	default:189		return -EINVAL;190	}191 192	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {193	case SND_SOC_DAIFMT_I2S:194		sspa->ctrl |= SSPA_CTL_XDATDLY(1);195		break;196	default:197		return -EINVAL;198	}199 200	/* Since we are configuring the timings for the format by hand201	 * we have to defer some things until hw_params() where we202	 * know parameters like the sample size.203	 */204	return 0;205}206 207/*208 * Set the SSPA audio DMA parameters and sample size.209 * Can be called multiple times by oss emulation.210 */211static int mmp_sspa_hw_params(struct snd_pcm_substream *substream,212			       struct snd_pcm_hw_params *params,213			       struct snd_soc_dai *dai)214{215	struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai);216	struct device *dev = dai->component->dev;217	u32 sspa_ctrl = sspa->ctrl;218	int bits;219	int bitval;220 221	switch (params_format(params)) {222	case SNDRV_PCM_FORMAT_S8:223		bits = 8;224		bitval = SSPA_CTL_8_BITS;225		break;226	case SNDRV_PCM_FORMAT_S16_LE:227		bits = 16;228		bitval = SSPA_CTL_16_BITS;229		break;230	case SNDRV_PCM_FORMAT_S24_3LE:231		bits = 24;232		bitval = SSPA_CTL_24_BITS;233		break;234	case SNDRV_PCM_FORMAT_S32_LE:235		bits = 32;236		bitval = SSPA_CTL_32_BITS;237		break;238	default:239		return -EINVAL;240	}241 242	sspa_ctrl &= ~SSPA_CTL_XPH;243	if (dev->of_node || params_channels(params) == 2)244		sspa_ctrl |= SSPA_CTL_XPH;245 246	sspa_ctrl &= ~SSPA_CTL_XWDLEN1_MASK;247	sspa_ctrl |= SSPA_CTL_XWDLEN1(bitval);248 249	sspa_ctrl &= ~SSPA_CTL_XWDLEN2_MASK;250	sspa_ctrl |= SSPA_CTL_XWDLEN2(bitval);251 252	sspa_ctrl &= ~SSPA_CTL_XSSZ1_MASK;253	sspa_ctrl |= SSPA_CTL_XSSZ1(bitval);254 255	sspa_ctrl &= ~SSPA_CTL_XSSZ2_MASK;256	sspa_ctrl |= SSPA_CTL_XSSZ2(bitval);257 258	sspa->sp &= ~SSPA_SP_FWID_MASK;259	sspa->sp |= SSPA_SP_FWID(bits - 1);260 261	sspa->sp &= ~SSPA_TXSP_FPER_MASK;262	sspa->sp |= SSPA_TXSP_FPER(bits * 2 - 1);263 264	if (dev->of_node) {265		clk_set_rate(sspa->clk, params_rate(params) *266					params_channels(params) * bits);267	}268 269	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {270		__raw_writel(sspa_ctrl, sspa->tx_base + SSPA_CTL);271		__raw_writel(0x1, sspa->tx_base + SSPA_FIFO_UL);272	} else {273		__raw_writel(sspa_ctrl, sspa->rx_base + SSPA_CTL);274		__raw_writel(0x0, sspa->rx_base + SSPA_FIFO_UL);275	}276 277	return 0;278}279 280static int mmp_sspa_trigger(struct snd_pcm_substream *substream, int cmd,281			     struct snd_soc_dai *dai)282{283	struct sspa_priv *sspa = snd_soc_dai_get_drvdata(dai);284	int ret = 0;285 286	switch (cmd) {287	case SNDRV_PCM_TRIGGER_START:288	case SNDRV_PCM_TRIGGER_RESUME:289	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:290		/*291		 * whatever playback or capture, must enable rx.292		 * this is a hw issue, so need check if rx has been293		 * enabled or not; if has been enabled by another294		 * stream, do not enable again.295		 */296		if (!sspa->running_cnt)297			mmp_sspa_rx_enable(sspa);298 299		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)300			mmp_sspa_tx_enable(sspa);301 302		sspa->running_cnt++;303		break;304 305	case SNDRV_PCM_TRIGGER_STOP:306	case SNDRV_PCM_TRIGGER_SUSPEND:307	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:308		sspa->running_cnt--;309 310		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)311			mmp_sspa_tx_disable(sspa);312 313		/* have no capture stream, disable rx port */314		if (!sspa->running_cnt)315			mmp_sspa_rx_disable(sspa);316		break;317 318	default:319		ret = -EINVAL;320	}321 322	return ret;323}324 325static int mmp_sspa_probe(struct snd_soc_dai *dai)326{327	struct sspa_priv *sspa = dev_get_drvdata(dai->dev);328 329	snd_soc_dai_init_dma_data(dai,330				&sspa->playback_dma_data,331				&sspa->capture_dma_data);332 333	return 0;334}335 336#define MMP_SSPA_RATES SNDRV_PCM_RATE_8000_192000337#define MMP_SSPA_FORMATS (SNDRV_PCM_FMTBIT_S8 | \338		SNDRV_PCM_FMTBIT_S16_LE | \339		SNDRV_PCM_FMTBIT_S24_3LE | \340		SNDRV_PCM_FMTBIT_S32_LE)341 342static const struct snd_soc_dai_ops mmp_sspa_dai_ops = {343	.probe		= mmp_sspa_probe,344	.startup	= mmp_sspa_startup,345	.shutdown	= mmp_sspa_shutdown,346	.trigger	= mmp_sspa_trigger,347	.hw_params	= mmp_sspa_hw_params,348	.set_sysclk	= mmp_sspa_set_dai_sysclk,349	.set_pll	= mmp_sspa_set_dai_pll,350	.set_fmt	= mmp_sspa_set_dai_fmt,351};352 353static struct snd_soc_dai_driver mmp_sspa_dai = {354	.playback = {355		.channels_min = 1,356		.channels_max = 128,357		.rates = MMP_SSPA_RATES,358		.formats = MMP_SSPA_FORMATS,359	},360	.capture = {361		.channels_min = 1,362		.channels_max = 2,363		.rates = MMP_SSPA_RATES,364		.formats = MMP_SSPA_FORMATS,365	},366	.ops = &mmp_sspa_dai_ops,367};368 369#define MMP_PCM_INFO (SNDRV_PCM_INFO_MMAP |	\370		SNDRV_PCM_INFO_MMAP_VALID |	\371		SNDRV_PCM_INFO_INTERLEAVED |	\372		SNDRV_PCM_INFO_PAUSE |		\373		SNDRV_PCM_INFO_RESUME |		\374		SNDRV_PCM_INFO_NO_PERIOD_WAKEUP)375 376static const struct snd_pcm_hardware mmp_pcm_hardware[] = {377	{378		.info			= MMP_PCM_INFO,379		.period_bytes_min	= 1024,380		.period_bytes_max	= 2048,381		.periods_min		= 2,382		.periods_max		= 32,383		.buffer_bytes_max	= 4096,384		.fifo_size		= 32,385	},386	{387		.info			= MMP_PCM_INFO,388		.period_bytes_min	= 1024,389		.period_bytes_max	= 2048,390		.periods_min		= 2,391		.periods_max		= 32,392		.buffer_bytes_max	= 4096,393		.fifo_size		= 32,394	},395};396 397static const struct snd_dmaengine_pcm_config mmp_pcm_config = {398	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,399	.pcm_hardware = mmp_pcm_hardware,400	.prealloc_buffer_size = 4096,401};402 403static int mmp_pcm_mmap(struct snd_soc_component *component,404			struct snd_pcm_substream *substream,405			struct vm_area_struct *vma)406{407	vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);408	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);409	return remap_pfn_range(vma, vma->vm_start,410		substream->dma_buffer.addr >> PAGE_SHIFT,411		vma->vm_end - vma->vm_start, vma->vm_page_prot);412}413 414static int mmp_sspa_open(struct snd_soc_component *component,415			 struct snd_pcm_substream *substream)416{417	struct sspa_priv *sspa = snd_soc_component_get_drvdata(component);418 419	pm_runtime_get_sync(component->dev);420 421	/* we can only change the settings if the port is not in use */422	if ((__raw_readl(sspa->tx_base + SSPA_SP) & SSPA_SP_S_EN) ||423	    (__raw_readl(sspa->rx_base + SSPA_SP) & SSPA_SP_S_EN)) {424		dev_err(component->dev,425			"can't change hardware dai format: stream is in use\n");426		return -EBUSY;427	}428 429	__raw_writel(sspa->sp, sspa->tx_base + SSPA_SP);430	__raw_writel(sspa->sp, sspa->rx_base + SSPA_SP);431 432	sspa->sp &= ~(SSPA_SP_S_RST | SSPA_SP_FFLUSH);433	__raw_writel(sspa->sp, sspa->tx_base + SSPA_SP);434	__raw_writel(sspa->sp, sspa->rx_base + SSPA_SP);435 436	/*437	 * FIXME: hw issue, for the tx serial port,438	 * can not config the master/slave mode;439	 * so must clean this bit.440	 * The master/slave mode has been set in the441	 * rx port.442	 */443	__raw_writel(sspa->sp & ~SSPA_SP_MSL, sspa->tx_base + SSPA_SP);444 445	__raw_writel(sspa->ctrl, sspa->tx_base + SSPA_CTL);446	__raw_writel(sspa->ctrl, sspa->rx_base + SSPA_CTL);447 448	return 0;449}450 451static int mmp_sspa_close(struct snd_soc_component *component,452			  struct snd_pcm_substream *substream)453{454	pm_runtime_put_sync(component->dev);455	return 0;456}457 458static const struct snd_soc_component_driver mmp_sspa_component = {459	.name			= "mmp-sspa",460	.mmap			= mmp_pcm_mmap,461	.open			= mmp_sspa_open,462	.close			= mmp_sspa_close,463	.legacy_dai_naming	= 1,464};465 466static int asoc_mmp_sspa_probe(struct platform_device *pdev)467{468	struct sspa_priv *sspa;469	int ret;470 471	sspa = devm_kzalloc(&pdev->dev,472				sizeof(struct sspa_priv), GFP_KERNEL);473	if (!sspa)474		return -ENOMEM;475 476	if (pdev->dev.of_node) {477		sspa->rx_base = devm_platform_ioremap_resource(pdev, 0);478		if (IS_ERR(sspa->rx_base))479			return PTR_ERR(sspa->rx_base);480 481		sspa->tx_base = devm_platform_ioremap_resource(pdev, 1);482		if (IS_ERR(sspa->tx_base))483			return PTR_ERR(sspa->tx_base);484 485		sspa->clk = devm_clk_get(&pdev->dev, "bitclk");486		if (IS_ERR(sspa->clk))487			return PTR_ERR(sspa->clk);488 489		sspa->audio_clk = devm_clk_get(&pdev->dev, "audio");490		if (IS_ERR(sspa->audio_clk))491			return PTR_ERR(sspa->audio_clk);492	} else {493		struct resource *res;494 495		res = platform_get_resource(pdev, IORESOURCE_IO, 0);496		if (res == NULL)497			return -ENODEV;498 499		sspa->rx_base = devm_ioremap(&pdev->dev, res->start, 0x30);500		if (!sspa->rx_base)501			return -ENOMEM;502 503		sspa->tx_base = devm_ioremap(&pdev->dev,504					     res->start + 0x80, 0x30);505		if (!sspa->tx_base)506			return -ENOMEM;507 508		sspa->clk = devm_clk_get(&pdev->dev, NULL);509		if (IS_ERR(sspa->clk))510			return PTR_ERR(sspa->clk);511 512		sspa->audio_clk = clk_get(NULL, "mmp-audio");513		if (IS_ERR(sspa->audio_clk))514			return PTR_ERR(sspa->audio_clk);515 516		sspa->sysclk = clk_get(NULL, "mmp-sysclk");517		if (IS_ERR(sspa->sysclk)) {518			clk_put(sspa->audio_clk);519			return PTR_ERR(sspa->sysclk);520		}521	}522	platform_set_drvdata(pdev, sspa);523 524	sspa->playback_dma_data.maxburst = 4;525	sspa->capture_dma_data.maxburst = 4;526	/* You know, these addresses are actually ignored. */527	sspa->capture_dma_data.addr = SSPA_D;528	sspa->playback_dma_data.addr = 0x80 + SSPA_D;529 530	if (pdev->dev.of_node) {531		ret = devm_snd_dmaengine_pcm_register(&pdev->dev,532						      &mmp_pcm_config, 0);533		if (ret)534			return ret;535	}536 537	ret = devm_snd_soc_register_component(&pdev->dev, &mmp_sspa_component,538					      &mmp_sspa_dai, 1);539	if (ret)540		return ret;541 542	pm_runtime_enable(&pdev->dev);543	clk_prepare_enable(sspa->audio_clk);544 545	return 0;546}547 548static void asoc_mmp_sspa_remove(struct platform_device *pdev)549{550	struct sspa_priv *sspa = platform_get_drvdata(pdev);551 552	clk_disable_unprepare(sspa->audio_clk);553	pm_runtime_disable(&pdev->dev);554 555	if (pdev->dev.of_node)556		return;557 558	clk_put(sspa->audio_clk);559	clk_put(sspa->sysclk);560}561 562#ifdef CONFIG_OF563static const struct of_device_id mmp_sspa_of_match[] = {564	{ .compatible = "marvell,mmp-sspa" },565	{},566};567 568MODULE_DEVICE_TABLE(of, mmp_sspa_of_match);569#endif570 571static struct platform_driver asoc_mmp_sspa_driver = {572	.driver = {573		.name = "mmp-sspa-dai",574		.of_match_table = of_match_ptr(mmp_sspa_of_match),575	},576	.probe = asoc_mmp_sspa_probe,577	.remove = asoc_mmp_sspa_remove,578};579 580module_platform_driver(asoc_mmp_sspa_driver);581 582MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");583MODULE_DESCRIPTION("MMP SSPA SoC Interface");584MODULE_LICENSE("GPL");585MODULE_ALIAS("platform:mmp-sspa-dai");586