diff options
Diffstat (limited to 'sound/soc/sirf')
-rw-r--r-- | sound/soc/sirf/Kconfig | 21 | ||||
-rw-r--r-- | sound/soc/sirf/Makefile | 8 | ||||
-rw-r--r-- | sound/soc/sirf/sirf-audio-port.c | 86 | ||||
-rw-r--r-- | sound/soc/sirf/sirf-audio.c | 160 | ||||
-rw-r--r-- | sound/soc/sirf/sirf-usp.c | 435 | ||||
-rw-r--r-- | sound/soc/sirf/sirf-usp.h | 292 |
6 files changed, 1002 insertions, 0 deletions
diff --git a/sound/soc/sirf/Kconfig b/sound/soc/sirf/Kconfig new file mode 100644 index 000000000..094a1c89c --- /dev/null +++ b/sound/soc/sirf/Kconfig @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0-only +config SND_SOC_SIRF + tristate "SoC Audio for the SiRF SoC chips" + depends on ARCH_SIRF || COMPILE_TEST + select SND_SOC_GENERIC_DMAENGINE_PCM + +config SND_SOC_SIRF_AUDIO + tristate "SoC Audio support for SiRF internal audio codec" + depends on SND_SOC_SIRF + select SND_SOC_SIRF_AUDIO_CODEC + select SND_SOC_SIRF_AUDIO_PORT + +config SND_SOC_SIRF_AUDIO_PORT + select REGMAP_MMIO + tristate + +config SND_SOC_SIRF_USP + tristate "SoC Audio (I2S protocol) for SiRF SoC USP interface" + depends on SND_SOC_SIRF + select REGMAP_MMIO + tristate diff --git a/sound/soc/sirf/Makefile b/sound/soc/sirf/Makefile new file mode 100644 index 000000000..16ed11965 --- /dev/null +++ b/sound/soc/sirf/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +snd-soc-sirf-audio-objs := sirf-audio.o +snd-soc-sirf-audio-port-objs := sirf-audio-port.o +snd-soc-sirf-usp-objs := sirf-usp.o + +obj-$(CONFIG_SND_SOC_SIRF_AUDIO) += snd-soc-sirf-audio.o +obj-$(CONFIG_SND_SOC_SIRF_AUDIO_PORT) += snd-soc-sirf-audio-port.o +obj-$(CONFIG_SND_SOC_SIRF_USP) += snd-soc-sirf-usp.o diff --git a/sound/soc/sirf/sirf-audio-port.c b/sound/soc/sirf/sirf-audio-port.c new file mode 100644 index 000000000..8be2f0bc4 --- /dev/null +++ b/sound/soc/sirf/sirf-audio-port.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * SiRF Audio port driver + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + */ +#include <linux/module.h> +#include <sound/soc.h> +#include <sound/dmaengine_pcm.h> + +struct sirf_audio_port { + struct regmap *regmap; + struct snd_dmaengine_dai_dma_data playback_dma_data; + struct snd_dmaengine_dai_dma_data capture_dma_data; +}; + + +static int sirf_audio_port_dai_probe(struct snd_soc_dai *dai) +{ + struct sirf_audio_port *port = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &port->playback_dma_data, + &port->capture_dma_data); + return 0; +} + +static struct snd_soc_dai_driver sirf_audio_port_dai = { + .probe = sirf_audio_port_dai_probe, + .name = "sirf-audio-port", + .id = 0, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}; + +static const struct snd_soc_component_driver sirf_audio_port_component = { + .name = "sirf-audio-port", +}; + +static int sirf_audio_port_probe(struct platform_device *pdev) +{ + int ret; + struct sirf_audio_port *port; + + port = devm_kzalloc(&pdev->dev, + sizeof(struct sirf_audio_port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + ret = devm_snd_soc_register_component(&pdev->dev, + &sirf_audio_port_component, &sirf_audio_port_dai, 1); + if (ret) + return ret; + + platform_set_drvdata(pdev, port); + return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); +} + +static const struct of_device_id sirf_audio_port_of_match[] = { + { .compatible = "sirf,audio-port", }, + {} +}; +MODULE_DEVICE_TABLE(of, sirf_audio_port_of_match); + +static struct platform_driver sirf_audio_port_driver = { + .driver = { + .name = "sirf-audio-port", + .of_match_table = sirf_audio_port_of_match, + }, + .probe = sirf_audio_port_probe, +}; + +module_platform_driver(sirf_audio_port_driver); + +MODULE_DESCRIPTION("SiRF Audio Port driver"); +MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/sirf/sirf-audio.c b/sound/soc/sirf/sirf-audio.c new file mode 100644 index 000000000..c923b6772 --- /dev/null +++ b/sound/soc/sirf/sirf-audio.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * SiRF audio card driver + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + */ + +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> + +struct sirf_audio_card { + unsigned int gpio_hp_pa; + unsigned int gpio_spk_pa; +}; + +static int sirf_audio_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *ctrl, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct sirf_audio_card *sirf_audio_card = snd_soc_card_get_drvdata(card); + int on = !SND_SOC_DAPM_EVENT_OFF(event); + + if (gpio_is_valid(sirf_audio_card->gpio_hp_pa)) + gpio_set_value(sirf_audio_card->gpio_hp_pa, on); + return 0; +} + +static int sirf_audio_spk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *ctrl, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct sirf_audio_card *sirf_audio_card = snd_soc_card_get_drvdata(card); + int on = !SND_SOC_DAPM_EVENT_OFF(event); + + if (gpio_is_valid(sirf_audio_card->gpio_spk_pa)) + gpio_set_value(sirf_audio_card->gpio_spk_pa, on); + + return 0; +} +static const struct snd_soc_dapm_widget sirf_audio_dapm_widgets[] = { + SND_SOC_DAPM_HP("Hp", sirf_audio_hp_event), + SND_SOC_DAPM_SPK("Ext Spk", sirf_audio_spk_event), + SND_SOC_DAPM_MIC("Ext Mic", NULL), +}; + +static const struct snd_soc_dapm_route intercon[] = { + {"Hp", NULL, "HPOUTL"}, + {"Hp", NULL, "HPOUTR"}, + {"Ext Spk", NULL, "SPKOUT"}, + {"MICIN1", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Ext Mic"}, +}; + +/* Digital audio interface glue - connects codec <--> CPU */ +SND_SOC_DAILINK_DEFS(sirf, + DAILINK_COMP_ARRAY(COMP_EMPTY()), + DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "sirf-audio-codec")), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +static struct snd_soc_dai_link sirf_audio_dai_link[] = { + { + .name = "SiRF audio card", + .stream_name = "SiRF audio HiFi", + SND_SOC_DAILINK_REG(sirf), + }, +}; + +/* Audio machine driver */ +static struct snd_soc_card snd_soc_sirf_audio_card = { + .name = "SiRF audio card", + .owner = THIS_MODULE, + .dai_link = sirf_audio_dai_link, + .num_links = ARRAY_SIZE(sirf_audio_dai_link), + .dapm_widgets = sirf_audio_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sirf_audio_dapm_widgets), + .dapm_routes = intercon, + .num_dapm_routes = ARRAY_SIZE(intercon), +}; + +static int sirf_audio_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &snd_soc_sirf_audio_card; + struct sirf_audio_card *sirf_audio_card; + int ret; + + sirf_audio_card = devm_kzalloc(&pdev->dev, sizeof(struct sirf_audio_card), + GFP_KERNEL); + if (sirf_audio_card == NULL) + return -ENOMEM; + + sirf_audio_dai_link[0].cpus->of_node = + of_parse_phandle(pdev->dev.of_node, "sirf,audio-platform", 0); + sirf_audio_dai_link[0].platforms->of_node = + of_parse_phandle(pdev->dev.of_node, "sirf,audio-platform", 0); + sirf_audio_dai_link[0].codecs->of_node = + of_parse_phandle(pdev->dev.of_node, "sirf,audio-codec", 0); + sirf_audio_card->gpio_spk_pa = of_get_named_gpio(pdev->dev.of_node, + "spk-pa-gpios", 0); + sirf_audio_card->gpio_hp_pa = of_get_named_gpio(pdev->dev.of_node, + "hp-pa-gpios", 0); + if (gpio_is_valid(sirf_audio_card->gpio_spk_pa)) { + ret = devm_gpio_request_one(&pdev->dev, + sirf_audio_card->gpio_spk_pa, + GPIOF_OUT_INIT_LOW, "SPA_PA_SD"); + if (ret) { + dev_err(&pdev->dev, + "Failed to request GPIO_%d for reset: %d\n", + sirf_audio_card->gpio_spk_pa, ret); + return ret; + } + } + if (gpio_is_valid(sirf_audio_card->gpio_hp_pa)) { + ret = devm_gpio_request_one(&pdev->dev, + sirf_audio_card->gpio_hp_pa, + GPIOF_OUT_INIT_LOW, "HP_PA_SD"); + if (ret) { + dev_err(&pdev->dev, + "Failed to request GPIO_%d for reset: %d\n", + sirf_audio_card->gpio_hp_pa, ret); + return ret; + } + } + + card->dev = &pdev->dev; + snd_soc_card_set_drvdata(card, sirf_audio_card); + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) + dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret); + + return ret; +} + +static const struct of_device_id sirf_audio_of_match[] = { + {.compatible = "sirf,sirf-audio-card", }, + { }, +}; +MODULE_DEVICE_TABLE(of, sirf_audio_of_match); + +static struct platform_driver sirf_audio_driver = { + .driver = { + .name = "sirf-audio-card", + .pm = &snd_soc_pm_ops, + .of_match_table = sirf_audio_of_match, + }, + .probe = sirf_audio_probe, +}; +module_platform_driver(sirf_audio_driver); + +MODULE_AUTHOR("RongJun Ying <RongJun.Ying@csr.com>"); +MODULE_DESCRIPTION("ALSA SoC SIRF audio card driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/sirf/sirf-usp.c b/sound/soc/sirf/sirf-usp.c new file mode 100644 index 000000000..2af0c6f14 --- /dev/null +++ b/sound/soc/sirf/sirf-usp.c @@ -0,0 +1,435 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * SiRF USP in I2S/DSP mode + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + */ +#include <linux/module.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/clk.h> +#include <linux/pm_runtime.h> +#include <sound/soc.h> +#include <sound/pcm_params.h> +#include <sound/dmaengine_pcm.h> + +#include "sirf-usp.h" + +struct sirf_usp { + struct regmap *regmap; + struct clk *clk; + u32 mode1_reg; + u32 mode2_reg; + int daifmt_format; + struct snd_dmaengine_dai_dma_data playback_dma_data; + struct snd_dmaengine_dai_dma_data capture_dma_data; +}; + +static void sirf_usp_tx_enable(struct sirf_usp *usp) +{ + regmap_update_bits(usp->regmap, USP_TX_FIFO_OP, + USP_TX_FIFO_RESET, USP_TX_FIFO_RESET); + regmap_write(usp->regmap, USP_TX_FIFO_OP, 0); + + regmap_update_bits(usp->regmap, USP_TX_FIFO_OP, + USP_TX_FIFO_START, USP_TX_FIFO_START); + + regmap_update_bits(usp->regmap, USP_TX_RX_ENABLE, + USP_TX_ENA, USP_TX_ENA); +} + +static void sirf_usp_tx_disable(struct sirf_usp *usp) +{ + regmap_update_bits(usp->regmap, USP_TX_RX_ENABLE, + USP_TX_ENA, ~USP_TX_ENA); + /* FIFO stop */ + regmap_write(usp->regmap, USP_TX_FIFO_OP, 0); +} + +static void sirf_usp_rx_enable(struct sirf_usp *usp) +{ + regmap_update_bits(usp->regmap, USP_RX_FIFO_OP, + USP_RX_FIFO_RESET, USP_RX_FIFO_RESET); + regmap_write(usp->regmap, USP_RX_FIFO_OP, 0); + + regmap_update_bits(usp->regmap, USP_RX_FIFO_OP, + USP_RX_FIFO_START, USP_RX_FIFO_START); + + regmap_update_bits(usp->regmap, USP_TX_RX_ENABLE, + USP_RX_ENA, USP_RX_ENA); +} + +static void sirf_usp_rx_disable(struct sirf_usp *usp) +{ + regmap_update_bits(usp->regmap, USP_TX_RX_ENABLE, + USP_RX_ENA, ~USP_RX_ENA); + /* FIFO stop */ + regmap_write(usp->regmap, USP_RX_FIFO_OP, 0); +} + +static int sirf_usp_pcm_dai_probe(struct snd_soc_dai *dai) +{ + struct sirf_usp *usp = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &usp->playback_dma_data, + &usp->capture_dma_data); + return 0; +} + +static int sirf_usp_pcm_set_dai_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct sirf_usp *usp = snd_soc_dai_get_drvdata(dai); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + break; + default: + dev_err(dai->dev, "Only CBM and CFM supported\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_DSP_A: + usp->daifmt_format = (fmt & SND_SOC_DAIFMT_FORMAT_MASK); + break; + default: + dev_err(dai->dev, "Only I2S and DSP_A format supported\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + usp->daifmt_format |= (fmt & SND_SOC_DAIFMT_INV_MASK); + break; + default: + return -EINVAL; + } + + return 0; +} + +static void sirf_usp_i2s_init(struct sirf_usp *usp) +{ + /* Configure RISC mode */ + regmap_update_bits(usp->regmap, USP_RISC_DSP_MODE, + USP_RISC_DSP_SEL, ~USP_RISC_DSP_SEL); + + /* + * Configure DMA IO Length register + * Set no limit, USP can receive data continuously until it is diabled + */ + regmap_write(usp->regmap, USP_TX_DMA_IO_LEN, 0); + regmap_write(usp->regmap, USP_RX_DMA_IO_LEN, 0); + + /* Configure Mode2 register */ + regmap_write(usp->regmap, USP_MODE2, (1 << USP_RXD_DELAY_LEN_OFFSET) | + (0 << USP_TXD_DELAY_LEN_OFFSET) | + USP_TFS_CLK_SLAVE_MODE | USP_RFS_CLK_SLAVE_MODE); + + /* Configure Mode1 register */ + regmap_write(usp->regmap, USP_MODE1, + USP_SYNC_MODE | USP_EN | USP_TXD_ACT_EDGE_FALLING | + USP_RFS_ACT_LEVEL_LOGIC1 | USP_TFS_ACT_LEVEL_LOGIC1 | + USP_TX_UFLOW_REPEAT_ZERO | USP_CLOCK_MODE_SLAVE); + + /* Configure RX DMA IO Control register */ + regmap_write(usp->regmap, USP_RX_DMA_IO_CTRL, 0); + + /* Congiure RX FIFO Control register */ + regmap_write(usp->regmap, USP_RX_FIFO_CTRL, + (USP_RX_FIFO_THRESHOLD << USP_RX_FIFO_THD_OFFSET) | + (USP_TX_RX_FIFO_WIDTH_DWORD << USP_RX_FIFO_WIDTH_OFFSET)); + + /* Congiure RX FIFO Level Check register */ + regmap_write(usp->regmap, USP_RX_FIFO_LEVEL_CHK, + RX_FIFO_SC(0x04) | RX_FIFO_LC(0x0E) | RX_FIFO_HC(0x1B)); + + /* Configure TX DMA IO Control register*/ + regmap_write(usp->regmap, USP_TX_DMA_IO_CTRL, 0); + + /* Configure TX FIFO Control register */ + regmap_write(usp->regmap, USP_TX_FIFO_CTRL, + (USP_TX_FIFO_THRESHOLD << USP_TX_FIFO_THD_OFFSET) | + (USP_TX_RX_FIFO_WIDTH_DWORD << USP_TX_FIFO_WIDTH_OFFSET)); + /* Congiure TX FIFO Level Check register */ + regmap_write(usp->regmap, USP_TX_FIFO_LEVEL_CHK, + TX_FIFO_SC(0x1B) | TX_FIFO_LC(0x0E) | TX_FIFO_HC(0x04)); +} + +static int sirf_usp_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct sirf_usp *usp = snd_soc_dai_get_drvdata(dai); + u32 data_len, frame_len, shifter_len; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + data_len = 16; + frame_len = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + data_len = 24; + frame_len = 32; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + data_len = 24; + frame_len = 24; + break; + default: + dev_err(dai->dev, "Format unsupported\n"); + return -EINVAL; + } + + shifter_len = data_len; + + switch (usp->daifmt_format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + regmap_update_bits(usp->regmap, USP_RX_FRAME_CTRL, + USP_I2S_SYNC_CHG, USP_I2S_SYNC_CHG); + break; + case SND_SOC_DAIFMT_DSP_A: + regmap_update_bits(usp->regmap, USP_RX_FRAME_CTRL, + USP_I2S_SYNC_CHG, 0); + frame_len = data_len * params_channels(params); + data_len = frame_len; + break; + default: + dev_err(dai->dev, "Only support I2S and DSP_A mode\n"); + return -EINVAL; + } + + switch (usp->daifmt_format & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + regmap_update_bits(usp->regmap, USP_MODE1, + USP_RXD_ACT_EDGE_FALLING | USP_TXD_ACT_EDGE_FALLING, + USP_RXD_ACT_EDGE_FALLING); + break; + default: + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_update_bits(usp->regmap, USP_TX_FRAME_CTRL, + USP_TXC_DATA_LEN_MASK | USP_TXC_FRAME_LEN_MASK + | USP_TXC_SHIFTER_LEN_MASK | USP_TXC_SLAVE_CLK_SAMPLE, + ((data_len - 1) << USP_TXC_DATA_LEN_OFFSET) + | ((frame_len - 1) << USP_TXC_FRAME_LEN_OFFSET) + | ((shifter_len - 1) << USP_TXC_SHIFTER_LEN_OFFSET) + | USP_TXC_SLAVE_CLK_SAMPLE); + else + regmap_update_bits(usp->regmap, USP_RX_FRAME_CTRL, + USP_RXC_DATA_LEN_MASK | USP_RXC_FRAME_LEN_MASK + | USP_RXC_SHIFTER_LEN_MASK | USP_SINGLE_SYNC_MODE, + ((data_len - 1) << USP_RXC_DATA_LEN_OFFSET) + | ((frame_len - 1) << USP_RXC_FRAME_LEN_OFFSET) + | ((shifter_len - 1) << USP_RXC_SHIFTER_LEN_OFFSET) + | USP_SINGLE_SYNC_MODE); + + return 0; +} + +static int sirf_usp_pcm_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct sirf_usp *usp = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + sirf_usp_tx_enable(usp); + else + sirf_usp_rx_enable(usp); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + sirf_usp_tx_disable(usp); + else + sirf_usp_rx_disable(usp); + break; + } + + return 0; +} + +static const struct snd_soc_dai_ops sirf_usp_pcm_dai_ops = { + .trigger = sirf_usp_pcm_trigger, + .set_fmt = sirf_usp_pcm_set_dai_fmt, + .hw_params = sirf_usp_pcm_hw_params, +}; + +static struct snd_soc_dai_driver sirf_usp_pcm_dai = { + .probe = sirf_usp_pcm_dai_probe, + .name = "sirf-usp-pcm", + .id = 0, + .playback = { + .stream_name = "SiRF USP PCM Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + }, + .capture = { + .stream_name = "SiRF USP PCM Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + }, + .ops = &sirf_usp_pcm_dai_ops, +}; + +static int sirf_usp_pcm_runtime_suspend(struct device *dev) +{ + struct sirf_usp *usp = dev_get_drvdata(dev); + + clk_disable_unprepare(usp->clk); + return 0; +} + +static int sirf_usp_pcm_runtime_resume(struct device *dev) +{ + struct sirf_usp *usp = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(usp->clk); + if (ret) { + dev_err(dev, "clk_enable failed: %d\n", ret); + return ret; + } + sirf_usp_i2s_init(usp); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int sirf_usp_pcm_suspend(struct device *dev) +{ + struct sirf_usp *usp = dev_get_drvdata(dev); + + if (!pm_runtime_status_suspended(dev)) { + regmap_read(usp->regmap, USP_MODE1, &usp->mode1_reg); + regmap_read(usp->regmap, USP_MODE2, &usp->mode2_reg); + sirf_usp_pcm_runtime_suspend(dev); + } + return 0; +} + +static int sirf_usp_pcm_resume(struct device *dev) +{ + struct sirf_usp *usp = dev_get_drvdata(dev); + int ret; + + if (!pm_runtime_status_suspended(dev)) { + ret = sirf_usp_pcm_runtime_resume(dev); + if (ret) + return ret; + regmap_write(usp->regmap, USP_MODE1, usp->mode1_reg); + regmap_write(usp->regmap, USP_MODE2, usp->mode2_reg); + } + return 0; +} +#endif + +static const struct snd_soc_component_driver sirf_usp_component = { + .name = "sirf-usp", +}; + +static const struct regmap_config sirf_usp_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = USP_RX_FIFO_DATA, + .cache_type = REGCACHE_NONE, +}; + +static int sirf_usp_pcm_probe(struct platform_device *pdev) +{ + int ret; + struct sirf_usp *usp; + void __iomem *base; + + usp = devm_kzalloc(&pdev->dev, sizeof(struct sirf_usp), + GFP_KERNEL); + if (!usp) + return -ENOMEM; + + platform_set_drvdata(pdev, usp); + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + usp->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &sirf_usp_regmap_config); + if (IS_ERR(usp->regmap)) + return PTR_ERR(usp->regmap); + + usp->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(usp->clk)) { + dev_err(&pdev->dev, "Get clock failed.\n"); + return PTR_ERR(usp->clk); + } + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = sirf_usp_pcm_runtime_resume(&pdev->dev); + if (ret) + return ret; + } + + ret = devm_snd_soc_register_component(&pdev->dev, &sirf_usp_component, + &sirf_usp_pcm_dai, 1); + if (ret) { + dev_err(&pdev->dev, "Register Audio SoC dai failed.\n"); + return ret; + } + return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); +} + +static int sirf_usp_pcm_remove(struct platform_device *pdev) +{ + if (!pm_runtime_enabled(&pdev->dev)) + sirf_usp_pcm_runtime_suspend(&pdev->dev); + else + pm_runtime_disable(&pdev->dev); + return 0; +} + +static const struct of_device_id sirf_usp_pcm_of_match[] = { + { .compatible = "sirf,prima2-usp-pcm", }, + {} +}; +MODULE_DEVICE_TABLE(of, sirf_usp_pcm_of_match); + +static const struct dev_pm_ops sirf_usp_pcm_pm_ops = { + SET_RUNTIME_PM_OPS(sirf_usp_pcm_runtime_suspend, + sirf_usp_pcm_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(sirf_usp_pcm_suspend, sirf_usp_pcm_resume) +}; + +static struct platform_driver sirf_usp_pcm_driver = { + .driver = { + .name = "sirf-usp-pcm", + .of_match_table = sirf_usp_pcm_of_match, + .pm = &sirf_usp_pcm_pm_ops, + }, + .probe = sirf_usp_pcm_probe, + .remove = sirf_usp_pcm_remove, +}; + +module_platform_driver(sirf_usp_pcm_driver); + +MODULE_DESCRIPTION("SiRF SoC USP PCM bus driver"); +MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/sirf/sirf-usp.h b/sound/soc/sirf/sirf-usp.h new file mode 100644 index 000000000..08993b599 --- /dev/null +++ b/sound/soc/sirf/sirf-usp.h @@ -0,0 +1,292 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * arch/arm/mach-prima2/include/mach/sirfsoc_usp.h + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + */ + +#ifndef _SIRF_USP_H +#define _SIRF_USP_H + +/* USP Registers */ +#define USP_MODE1 0x00 +#define USP_MODE2 0x04 +#define USP_TX_FRAME_CTRL 0x08 +#define USP_RX_FRAME_CTRL 0x0C +#define USP_TX_RX_ENABLE 0x10 +#define USP_INT_ENABLE 0x14 +#define USP_INT_STATUS 0x18 +#define USP_PIN_IO_DATA 0x1C +#define USP_RISC_DSP_MODE 0x20 +#define USP_AYSNC_PARAM_REG 0x24 +#define USP_IRDA_X_MODE_DIV 0x28 +#define USP_SM_CFG 0x2C +#define USP_TX_DMA_IO_CTRL 0x100 +#define USP_TX_DMA_IO_LEN 0x104 +#define USP_TX_FIFO_CTRL 0x108 +#define USP_TX_FIFO_LEVEL_CHK 0x10C +#define USP_TX_FIFO_OP 0x110 +#define USP_TX_FIFO_STATUS 0x114 +#define USP_TX_FIFO_DATA 0x118 +#define USP_RX_DMA_IO_CTRL 0x120 +#define USP_RX_DMA_IO_LEN 0x124 +#define USP_RX_FIFO_CTRL 0x128 +#define USP_RX_FIFO_LEVEL_CHK 0x12C +#define USP_RX_FIFO_OP 0x130 +#define USP_RX_FIFO_STATUS 0x134 +#define USP_RX_FIFO_DATA 0x138 + +/* USP MODE register-1 */ +#define USP_SYNC_MODE 0x00000001 +#define USP_CLOCK_MODE_SLAVE 0x00000002 +#define USP_LOOP_BACK_EN 0x00000004 +#define USP_HPSIR_EN 0x00000008 +#define USP_ENDIAN_CTRL_LSBF 0x00000010 +#define USP_EN 0x00000020 +#define USP_RXD_ACT_EDGE_FALLING 0x00000040 +#define USP_TXD_ACT_EDGE_FALLING 0x00000080 +#define USP_RFS_ACT_LEVEL_LOGIC1 0x00000100 +#define USP_TFS_ACT_LEVEL_LOGIC1 0x00000200 +#define USP_SCLK_IDLE_MODE_TOGGLE 0x00000400 +#define USP_SCLK_IDLE_LEVEL_LOGIC1 0x00000800 +#define USP_SCLK_PIN_MODE_IO 0x00001000 +#define USP_RFS_PIN_MODE_IO 0x00002000 +#define USP_TFS_PIN_MODE_IO 0x00004000 +#define USP_RXD_PIN_MODE_IO 0x00008000 +#define USP_TXD_PIN_MODE_IO 0x00010000 +#define USP_SCLK_IO_MODE_INPUT 0x00020000 +#define USP_RFS_IO_MODE_INPUT 0x00040000 +#define USP_TFS_IO_MODE_INPUT 0x00080000 +#define USP_RXD_IO_MODE_INPUT 0x00100000 +#define USP_TXD_IO_MODE_INPUT 0x00200000 +#define USP_IRDA_WIDTH_DIV_MASK 0x3FC00000 +#define USP_IRDA_WIDTH_DIV_OFFSET 0 +#define USP_IRDA_IDLE_LEVEL_HIGH 0x40000000 +#define USP_TX_UFLOW_REPEAT_ZERO 0x80000000 +#define USP_TX_ENDIAN_MODE 0x00000020 +#define USP_RX_ENDIAN_MODE 0x00000020 + +/* USP Mode Register-2 */ +#define USP_RXD_DELAY_LEN_MASK 0x000000FF +#define USP_RXD_DELAY_LEN_OFFSET 0 + +#define USP_TXD_DELAY_LEN_MASK 0x0000FF00 +#define USP_TXD_DELAY_LEN_OFFSET 8 + +#define USP_ENA_CTRL_MODE 0x00010000 +#define USP_FRAME_CTRL_MODE 0x00020000 +#define USP_TFS_SOURCE_MODE 0x00040000 +#define USP_TFS_MS_MODE 0x00080000 +#define USP_CLK_DIVISOR_MASK 0x7FE00000 +#define USP_CLK_DIVISOR_OFFSET 21 + +#define USP_TFS_CLK_SLAVE_MODE (1<<20) +#define USP_RFS_CLK_SLAVE_MODE (1<<19) + +#define USP_IRDA_DATA_WIDTH 0x80000000 + +/* USP Transmit Frame Control Register */ + +#define USP_TXC_DATA_LEN_MASK 0x000000FF +#define USP_TXC_DATA_LEN_OFFSET 0 + +#define USP_TXC_SYNC_LEN_MASK 0x0000FF00 +#define USP_TXC_SYNC_LEN_OFFSET 8 + +#define USP_TXC_FRAME_LEN_MASK 0x00FF0000 +#define USP_TXC_FRAME_LEN_OFFSET 16 + +#define USP_TXC_SHIFTER_LEN_MASK 0x1F000000 +#define USP_TXC_SHIFTER_LEN_OFFSET 24 + +#define USP_TXC_SLAVE_CLK_SAMPLE 0x20000000 + +#define USP_TXC_CLK_DIVISOR_MASK 0xC0000000 +#define USP_TXC_CLK_DIVISOR_OFFSET 30 + +/* USP Receive Frame Control Register */ + +#define USP_RXC_DATA_LEN_MASK 0x000000FF +#define USP_RXC_DATA_LEN_OFFSET 0 + +#define USP_RXC_FRAME_LEN_MASK 0x0000FF00 +#define USP_RXC_FRAME_LEN_OFFSET 8 + +#define USP_RXC_SHIFTER_LEN_MASK 0x001F0000 +#define USP_RXC_SHIFTER_LEN_OFFSET 16 + +#define USP_START_EDGE_MODE 0x00800000 +#define USP_I2S_SYNC_CHG 0x00200000 + +#define USP_RXC_CLK_DIVISOR_MASK 0x0F000000 +#define USP_RXC_CLK_DIVISOR_OFFSET 24 +#define USP_SINGLE_SYNC_MODE 0x00400000 + +/* Tx - RX Enable Register */ + +#define USP_RX_ENA 0x00000001 +#define USP_TX_ENA 0x00000002 + +/* USP Interrupt Enable and status Register */ +#define USP_RX_DONE_INT 0x00000001 +#define USP_TX_DONE_INT 0x00000002 +#define USP_RX_OFLOW_INT 0x00000004 +#define USP_TX_UFLOW_INT 0x00000008 +#define USP_RX_IO_DMA_INT 0x00000010 +#define USP_TX_IO_DMA_INT 0x00000020 +#define USP_RXFIFO_FULL_INT 0x00000040 +#define USP_TXFIFO_EMPTY_INT 0x00000080 +#define USP_RXFIFO_THD_INT 0x00000100 +#define USP_TXFIFO_THD_INT 0x00000200 +#define USP_UART_FRM_ERR_INT 0x00000400 +#define USP_RX_TIMEOUT_INT 0x00000800 +#define USP_TX_ALLOUT_INT 0x00001000 +#define USP_RXD_BREAK_INT 0x00008000 + +/* All possible TX interruots */ +#define USP_TX_INTERRUPT (USP_TX_DONE_INT|USP_TX_UFLOW_INT|\ + USP_TX_IO_DMA_INT|\ + USP_TXFIFO_EMPTY_INT|\ + USP_TXFIFO_THD_INT) +/* All possible RX interruots */ +#define USP_RX_INTERRUPT (USP_RX_DONE_INT|USP_RX_OFLOW_INT|\ + USP_RX_IO_DMA_INT|\ + USP_RXFIFO_FULL_INT|\ + USP_RXFIFO_THD_INT|\ + USP_RX_TIMEOUT_INT) + +#define USP_INT_ALL 0x1FFF + +/* USP Pin I/O Data Register */ + +#define USP_RFS_PIN_VALUE_MASK 0x00000001 +#define USP_TFS_PIN_VALUE_MASK 0x00000002 +#define USP_RXD_PIN_VALUE_MASK 0x00000004 +#define USP_TXD_PIN_VALUE_MASK 0x00000008 +#define USP_SCLK_PIN_VALUE_MASK 0x00000010 + +/* USP RISC/DSP Mode Register */ +#define USP_RISC_DSP_SEL 0x00000001 + +/* USP ASYNC PARAMETER Register*/ + +#define USP_ASYNC_TIMEOUT_MASK 0x0000FFFF +#define USP_ASYNC_TIMEOUT_OFFSET 0 +#define USP_ASYNC_TIMEOUT(x) (((x)&USP_ASYNC_TIMEOUT_MASK) \ + <<USP_ASYNC_TIMEOUT_OFFSET) + +#define USP_ASYNC_DIV2_MASK 0x003F0000 +#define USP_ASYNC_DIV2_OFFSET 16 + +/* USP TX DMA I/O MODE Register */ +#define USP_TX_MODE_IO 0x00000001 + +/* USP TX DMA I/O Length Register */ +#define USP_TX_DATA_LEN_MASK 0xFFFFFFFF +#define USP_TX_DATA_LEN_OFFSET 0 + +/* USP TX FIFO Control Register */ +#define USP_TX_FIFO_WIDTH_MASK 0x00000003 +#define USP_TX_FIFO_WIDTH_OFFSET 0 + +#define USP_TX_FIFO_THD_MASK 0x000001FC +#define USP_TX_FIFO_THD_OFFSET 2 + +/* USP TX FIFO Level Check Register */ +#define USP_TX_FIFO_LEVEL_CHECK_MASK 0x1F +#define USP_TX_FIFO_SC_OFFSET 0 +#define USP_TX_FIFO_LC_OFFSET 10 +#define USP_TX_FIFO_HC_OFFSET 20 + +#define TX_FIFO_SC(x) (((x) & USP_TX_FIFO_LEVEL_CHECK_MASK) \ + << USP_TX_FIFO_SC_OFFSET) +#define TX_FIFO_LC(x) (((x) & USP_TX_FIFO_LEVEL_CHECK_MASK) \ + << USP_TX_FIFO_LC_OFFSET) +#define TX_FIFO_HC(x) (((x) & USP_TX_FIFO_LEVEL_CHECK_MASK) \ + << USP_TX_FIFO_HC_OFFSET) + +/* USP TX FIFO Operation Register */ +#define USP_TX_FIFO_RESET 0x00000001 +#define USP_TX_FIFO_START 0x00000002 + +/* USP TX FIFO Status Register */ +#define USP_TX_FIFO_LEVEL_MASK 0x0000007F +#define USP_TX_FIFO_LEVEL_OFFSET 0 + +#define USP_TX_FIFO_FULL 0x00000080 +#define USP_TX_FIFO_EMPTY 0x00000100 + +/* USP TX FIFO Data Register */ +#define USP_TX_FIFO_DATA_MASK 0xFFFFFFFF +#define USP_TX_FIFO_DATA_OFFSET 0 + +/* USP RX DMA I/O MODE Register */ +#define USP_RX_MODE_IO 0x00000001 +#define USP_RX_DMA_FLUSH 0x00000004 + +/* USP RX DMA I/O Length Register */ +#define USP_RX_DATA_LEN_MASK 0xFFFFFFFF +#define USP_RX_DATA_LEN_OFFSET 0 + +/* USP RX FIFO Control Register */ +#define USP_RX_FIFO_WIDTH_MASK 0x00000003 +#define USP_RX_FIFO_WIDTH_OFFSET 0 + +#define USP_RX_FIFO_THD_MASK 0x000001FC +#define USP_RX_FIFO_THD_OFFSET 2 + +/* USP RX FIFO Level Check Register */ + +#define USP_RX_FIFO_LEVEL_CHECK_MASK 0x1F +#define USP_RX_FIFO_SC_OFFSET 0 +#define USP_RX_FIFO_LC_OFFSET 10 +#define USP_RX_FIFO_HC_OFFSET 20 + +#define RX_FIFO_SC(x) (((x) & USP_RX_FIFO_LEVEL_CHECK_MASK) \ + << USP_RX_FIFO_SC_OFFSET) +#define RX_FIFO_LC(x) (((x) & USP_RX_FIFO_LEVEL_CHECK_MASK) \ + << USP_RX_FIFO_LC_OFFSET) +#define RX_FIFO_HC(x) (((x) & USP_RX_FIFO_LEVEL_CHECK_MASK) \ + << USP_RX_FIFO_HC_OFFSET) + +/* USP RX FIFO Operation Register */ +#define USP_RX_FIFO_RESET 0x00000001 +#define USP_RX_FIFO_START 0x00000002 + +/* USP RX FIFO Status Register */ + +#define USP_RX_FIFO_LEVEL_MASK 0x0000007F +#define USP_RX_FIFO_LEVEL_OFFSET 0 + +#define USP_RX_FIFO_FULL 0x00000080 +#define USP_RX_FIFO_EMPTY 0x00000100 + +/* USP RX FIFO Data Register */ + +#define USP_RX_FIFO_DATA_MASK 0xFFFFFFFF +#define USP_RX_FIFO_DATA_OFFSET 0 + +/* + * When rx thd irq occur, sender just disable tx empty irq, + * Remaining data in tx fifo wil also be sent out. + */ +#define USP_FIFO_SIZE 128 +#define USP_TX_FIFO_THRESHOLD (USP_FIFO_SIZE/2) +#define USP_RX_FIFO_THRESHOLD (USP_FIFO_SIZE/2) + +/* FIFO_WIDTH for the USP_TX_FIFO_CTRL and USP_RX_FIFO_CTRL registers */ +#define USP_FIFO_WIDTH_BYTE 0x00 +#define USP_FIFO_WIDTH_WORD 0x01 +#define USP_FIFO_WIDTH_DWORD 0x02 + +#define USP_ASYNC_DIV2 16 + +#define USP_PLUGOUT_RETRY_CNT 2 + +#define USP_TX_RX_FIFO_WIDTH_DWORD 2 + +#define SIRF_USP_DIV_MCLK 0 + +#define SIRF_USP_I2S_TFS_SYNC 0 +#define SIRF_USP_I2S_RFS_SYNC 1 +#endif |