brintos

brintos / linux-shallow public Read only

0
0
Text · 8.1 KiB · e7f2a05 Raw
331 lines · c
1// SPDX-License-Identifier: GPL-2.0+2//3// AMD ALSA SoC PCM Driver4//5//Copyright 2016 Advanced Micro Devices, Inc.6 7#include <linux/platform_device.h>8#include <linux/module.h>9#include <linux/err.h>10#include <linux/io.h>11#include <sound/pcm_params.h>12#include <sound/soc.h>13#include <sound/soc-dai.h>14#include <linux/dma-mapping.h>15 16#include "acp3x.h"17 18#define DRV_NAME "acp3x_i2s_playcap"19 20static int acp3x_i2s_set_fmt(struct snd_soc_dai *cpu_dai,21					unsigned int fmt)22{23	struct i2s_dev_data *adata;24	int mode;25 26	adata = snd_soc_dai_get_drvdata(cpu_dai);27	mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;28	switch (mode) {29	case SND_SOC_DAIFMT_I2S:30		adata->tdm_mode = TDM_DISABLE;31		break;32	case SND_SOC_DAIFMT_DSP_A:33		adata->tdm_mode = TDM_ENABLE;34		break;35	default:36		return -EINVAL;37	}38	return 0;39}40 41static int acp3x_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai,42		u32 tx_mask, u32 rx_mask, int slots, int slot_width)43{44	struct i2s_dev_data *adata;45	u32 frm_len;46	u16 slot_len;47 48	adata = snd_soc_dai_get_drvdata(cpu_dai);49 50	/* These values are as per Hardware Spec */51	switch (slot_width) {52	case SLOT_WIDTH_8:53		slot_len = 8;54		break;55	case SLOT_WIDTH_16:56		slot_len = 16;57		break;58	case SLOT_WIDTH_24:59		slot_len = 24;60		break;61	case SLOT_WIDTH_32:62		slot_len = 0;63		break;64	default:65		return -EINVAL;66	}67	frm_len = FRM_LEN | (slots << 15) | (slot_len << 18);68	adata->tdm_fmt = frm_len;69	return 0;70}71 72static int acp3x_i2s_hwparams(struct snd_pcm_substream *substream,73	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)74{75	struct i2s_stream_instance *rtd;76	struct snd_soc_pcm_runtime *prtd;77	struct snd_soc_card *card;78	struct acp3x_platform_info *pinfo;79	struct i2s_dev_data *adata;80	u32 val;81	u32 reg_val, frmt_reg;82 83	prtd = snd_soc_substream_to_rtd(substream);84	rtd = substream->runtime->private_data;85	card = prtd->card;86	adata = snd_soc_dai_get_drvdata(dai);87	pinfo = snd_soc_card_get_drvdata(card);88	if (pinfo) {89		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)90			rtd->i2s_instance = pinfo->play_i2s_instance;91		else92			rtd->i2s_instance = pinfo->cap_i2s_instance;93	}94 95	/* These values are as per Hardware Spec */96	switch (params_format(params)) {97	case SNDRV_PCM_FORMAT_U8:98	case SNDRV_PCM_FORMAT_S8:99		rtd->xfer_resolution = 0x0;100		break;101	case SNDRV_PCM_FORMAT_S16_LE:102		rtd->xfer_resolution = 0x02;103		break;104	case SNDRV_PCM_FORMAT_S24_LE:105		rtd->xfer_resolution = 0x04;106		break;107	case SNDRV_PCM_FORMAT_S32_LE:108		rtd->xfer_resolution = 0x05;109		break;110	default:111		return -EINVAL;112	}113	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {114		switch (rtd->i2s_instance) {115		case I2S_BT_INSTANCE:116			reg_val = mmACP_BTTDM_ITER;117			frmt_reg = mmACP_BTTDM_TXFRMT;118			break;119		case I2S_SP_INSTANCE:120		default:121			reg_val = mmACP_I2STDM_ITER;122			frmt_reg = mmACP_I2STDM_TXFRMT;123		}124	} else {125		switch (rtd->i2s_instance) {126		case I2S_BT_INSTANCE:127			reg_val = mmACP_BTTDM_IRER;128			frmt_reg = mmACP_BTTDM_RXFRMT;129			break;130		case I2S_SP_INSTANCE:131		default:132			reg_val = mmACP_I2STDM_IRER;133			frmt_reg = mmACP_I2STDM_RXFRMT;134		}135	}136	if (adata->tdm_mode) {137		val = rv_readl(rtd->acp3x_base + reg_val);138		rv_writel(val | 0x2, rtd->acp3x_base + reg_val);139		rv_writel(adata->tdm_fmt, rtd->acp3x_base + frmt_reg);140	}141	val = rv_readl(rtd->acp3x_base + reg_val);142	val &= ~ACP3x_ITER_IRER_SAMP_LEN_MASK;143	val = val | (rtd->xfer_resolution  << 3);144	rv_writel(val, rtd->acp3x_base + reg_val);145	return 0;146}147 148static int acp3x_i2s_trigger(struct snd_pcm_substream *substream,149				int cmd, struct snd_soc_dai *dai)150{151	struct i2s_stream_instance *rtd;152	u32 ret, val, period_bytes, reg_val, ier_val, water_val;153	u32 buf_size, buf_reg;154 155	rtd = substream->runtime->private_data;156	period_bytes = frames_to_bytes(substream->runtime,157			substream->runtime->period_size);158	buf_size = frames_to_bytes(substream->runtime,159			substream->runtime->buffer_size);160	switch (cmd) {161	case SNDRV_PCM_TRIGGER_START:162	case SNDRV_PCM_TRIGGER_RESUME:163	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:164		rtd->bytescount = acp_get_byte_count(rtd,165						substream->stream);166		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {167			switch (rtd->i2s_instance) {168			case I2S_BT_INSTANCE:169				water_val =170					mmACP_BT_TX_INTR_WATERMARK_SIZE;171				reg_val = mmACP_BTTDM_ITER;172				ier_val = mmACP_BTTDM_IER;173				buf_reg = mmACP_BT_TX_RINGBUFSIZE;174				break;175			case I2S_SP_INSTANCE:176			default:177				water_val =178					mmACP_I2S_TX_INTR_WATERMARK_SIZE;179				reg_val = mmACP_I2STDM_ITER;180				ier_val = mmACP_I2STDM_IER;181				buf_reg = mmACP_I2S_TX_RINGBUFSIZE;182			}183		} else {184			switch (rtd->i2s_instance) {185			case I2S_BT_INSTANCE:186				water_val =187					mmACP_BT_RX_INTR_WATERMARK_SIZE;188				reg_val = mmACP_BTTDM_IRER;189				ier_val = mmACP_BTTDM_IER;190				buf_reg = mmACP_BT_RX_RINGBUFSIZE;191				break;192			case I2S_SP_INSTANCE:193			default:194				water_val =195					mmACP_I2S_RX_INTR_WATERMARK_SIZE;196				reg_val = mmACP_I2STDM_IRER;197				ier_val = mmACP_I2STDM_IER;198				buf_reg = mmACP_I2S_RX_RINGBUFSIZE;199			}200		}201		rv_writel(period_bytes, rtd->acp3x_base + water_val);202		rv_writel(buf_size, rtd->acp3x_base + buf_reg);203		val = rv_readl(rtd->acp3x_base + reg_val);204		val = val | BIT(0);205		rv_writel(val, rtd->acp3x_base + reg_val);206		rv_writel(1, rtd->acp3x_base + ier_val);207		ret = 0;208		break;209	case SNDRV_PCM_TRIGGER_STOP:210	case SNDRV_PCM_TRIGGER_SUSPEND:211	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:212		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {213			switch (rtd->i2s_instance) {214			case I2S_BT_INSTANCE:215				reg_val = mmACP_BTTDM_ITER;216				break;217			case I2S_SP_INSTANCE:218			default:219				reg_val = mmACP_I2STDM_ITER;220			}221 222		} else {223			switch (rtd->i2s_instance) {224			case I2S_BT_INSTANCE:225				reg_val = mmACP_BTTDM_IRER;226				break;227			case I2S_SP_INSTANCE:228			default:229				reg_val = mmACP_I2STDM_IRER;230			}231		}232		val = rv_readl(rtd->acp3x_base + reg_val);233		val = val & ~BIT(0);234		rv_writel(val, rtd->acp3x_base + reg_val);235 236		if (!(rv_readl(rtd->acp3x_base + mmACP_BTTDM_ITER) & BIT(0)) &&237		     !(rv_readl(rtd->acp3x_base + mmACP_BTTDM_IRER) & BIT(0)))238			rv_writel(0, rtd->acp3x_base + mmACP_BTTDM_IER);239		if (!(rv_readl(rtd->acp3x_base + mmACP_I2STDM_ITER) & BIT(0)) &&240		     !(rv_readl(rtd->acp3x_base + mmACP_I2STDM_IRER) & BIT(0)))241			rv_writel(0, rtd->acp3x_base + mmACP_I2STDM_IER);242		ret = 0;243		break;244	default:245		ret = -EINVAL;246		break;247	}248 249	return ret;250}251 252static const struct snd_soc_dai_ops acp3x_i2s_dai_ops = {253	.hw_params = acp3x_i2s_hwparams,254	.trigger = acp3x_i2s_trigger,255	.set_fmt = acp3x_i2s_set_fmt,256	.set_tdm_slot = acp3x_i2s_set_tdm_slot,257};258 259static const struct snd_soc_component_driver acp3x_dai_component = {260	.name			= DRV_NAME,261	.legacy_dai_naming	= 1,262};263 264static struct snd_soc_dai_driver acp3x_i2s_dai = {265	.playback = {266		.rates = SNDRV_PCM_RATE_8000_96000,267		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |268			SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,269		.channels_min = 2,270		.channels_max = 8,271		.rate_min = 8000,272		.rate_max = 96000,273	},274	.capture = {275		.rates = SNDRV_PCM_RATE_8000_48000,276		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |277			SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,278		.channels_min = 2,279		.channels_max = 2,280		.rate_min = 8000,281		.rate_max = 48000,282	},283	.ops = &acp3x_i2s_dai_ops,284};285 286static int acp3x_dai_probe(struct platform_device *pdev)287{288	struct resource *res;289	struct i2s_dev_data *adata;290	int ret;291 292	adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data),293			GFP_KERNEL);294	if (!adata)295		return -ENOMEM;296 297	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);298	if (!res) {299		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");300		return -ENOMEM;301	}302	adata->acp3x_base = devm_ioremap(&pdev->dev, res->start,303						resource_size(res));304	if (!adata->acp3x_base)305		return -ENOMEM;306 307	adata->i2s_irq = res->start;308	dev_set_drvdata(&pdev->dev, adata);309	ret = devm_snd_soc_register_component(&pdev->dev,310			&acp3x_dai_component, &acp3x_i2s_dai, 1);311	if (ret) {312		dev_err(&pdev->dev, "Fail to register acp i2s dai\n");313		return -ENODEV;314	}315	return 0;316}317 318static struct platform_driver acp3x_dai_driver = {319	.probe = acp3x_dai_probe,320	.driver = {321		.name = "acp3x_i2s_playcap",322	},323};324 325module_platform_driver(acp3x_dai_driver);326 327MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");328MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver");329MODULE_LICENSE("GPL v2");330MODULE_ALIAS("platform:"DRV_NAME);331