mirror of
https://github.com/torvalds/linux.git
synced 2024-11-15 08:31:55 +00:00
340d79a14d
The DT of_device.h and of_platform.h date back to the separate of_platform_bus_type before it was merged into the regular platform bus. As part of that merge prepping Arm DT support 13 years ago, they "temporarily" include each other. They also include platform_device.h and of.h. As a result, there's a pretty much random mix of those include files used throughout the tree. In order to detangle these headers and replace the implicit includes with struct declarations, users need to explicitly include the correct includes. Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> Acked-by: Charles Keepax <ckeepax@opensource.cirrus.com> Reviewed-by: Claudiu Beznea <claudiu.beznea@tuxon.dev> # for at91 Signed-off-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20231006-dt-asoc-header-cleanups-v3-1-13a4f0f7fee6@kernel.org Signed-off-by: Mark Brown <broonie@kernel.org>
312 lines
7.7 KiB
C
312 lines
7.7 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
// Copyright 2020 NXP
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/clk-provider.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/dmaengine.h>
|
|
#include <linux/mod_devicetable.h>
|
|
#include <linux/module.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/time.h>
|
|
#include <linux/pm_qos.h>
|
|
#include <sound/core.h>
|
|
#include <sound/dmaengine_pcm.h>
|
|
#include <sound/pcm_params.h>
|
|
#include <linux/dma-mapping.h>
|
|
|
|
#include "fsl_aud2htx.h"
|
|
#include "imx-pcm.h"
|
|
|
|
static int fsl_aud2htx_trigger(struct snd_pcm_substream *substream, int cmd,
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
struct fsl_aud2htx *aud2htx = snd_soc_dai_get_drvdata(dai);
|
|
|
|
switch (cmd) {
|
|
case SNDRV_PCM_TRIGGER_START:
|
|
case SNDRV_PCM_TRIGGER_RESUME:
|
|
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
|
regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL,
|
|
AUD2HTX_CTRL_EN, AUD2HTX_CTRL_EN);
|
|
regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
|
|
AUD2HTX_CTRE_DE, AUD2HTX_CTRE_DE);
|
|
break;
|
|
case SNDRV_PCM_TRIGGER_SUSPEND:
|
|
case SNDRV_PCM_TRIGGER_STOP:
|
|
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
|
regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
|
|
AUD2HTX_CTRE_DE, 0);
|
|
regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL,
|
|
AUD2HTX_CTRL_EN, 0);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int fsl_aud2htx_dai_probe(struct snd_soc_dai *cpu_dai)
|
|
{
|
|
struct fsl_aud2htx *aud2htx = dev_get_drvdata(cpu_dai->dev);
|
|
|
|
/* DMA request when number of entries < WTMK_LOW */
|
|
regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
|
|
AUD2HTX_CTRE_DT_MASK, 0);
|
|
|
|
/* Disable interrupts*/
|
|
regmap_update_bits(aud2htx->regmap, AUD2HTX_IRQ_MASK,
|
|
AUD2HTX_WM_HIGH_IRQ_MASK |
|
|
AUD2HTX_WM_LOW_IRQ_MASK |
|
|
AUD2HTX_OVF_MASK,
|
|
AUD2HTX_WM_HIGH_IRQ_MASK |
|
|
AUD2HTX_WM_LOW_IRQ_MASK |
|
|
AUD2HTX_OVF_MASK);
|
|
|
|
/* Configure watermark */
|
|
regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
|
|
AUD2HTX_CTRE_WL_MASK,
|
|
AUD2HTX_WTMK_LOW << AUD2HTX_CTRE_WL_SHIFT);
|
|
regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
|
|
AUD2HTX_CTRE_WH_MASK,
|
|
AUD2HTX_WTMK_HIGH << AUD2HTX_CTRE_WH_SHIFT);
|
|
|
|
snd_soc_dai_init_dma_data(cpu_dai, &aud2htx->dma_params_tx,
|
|
&aud2htx->dma_params_rx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct snd_soc_dai_ops fsl_aud2htx_dai_ops = {
|
|
.probe = fsl_aud2htx_dai_probe,
|
|
.trigger = fsl_aud2htx_trigger,
|
|
};
|
|
|
|
static struct snd_soc_dai_driver fsl_aud2htx_dai = {
|
|
.playback = {
|
|
.stream_name = "CPU-Playback",
|
|
.channels_min = 1,
|
|
.channels_max = 8,
|
|
.rates = SNDRV_PCM_RATE_32000 |
|
|
SNDRV_PCM_RATE_44100 |
|
|
SNDRV_PCM_RATE_48000 |
|
|
SNDRV_PCM_RATE_88200 |
|
|
SNDRV_PCM_RATE_96000 |
|
|
SNDRV_PCM_RATE_176400 |
|
|
SNDRV_PCM_RATE_192000,
|
|
.formats = FSL_AUD2HTX_FORMATS,
|
|
},
|
|
.ops = &fsl_aud2htx_dai_ops,
|
|
};
|
|
|
|
static const struct snd_soc_component_driver fsl_aud2htx_component = {
|
|
.name = "fsl-aud2htx",
|
|
.legacy_dai_naming = 1,
|
|
};
|
|
|
|
static const struct reg_default fsl_aud2htx_reg_defaults[] = {
|
|
{AUD2HTX_CTRL, 0x00000000},
|
|
{AUD2HTX_CTRL_EXT, 0x00000000},
|
|
{AUD2HTX_WR, 0x00000000},
|
|
{AUD2HTX_STATUS, 0x00000000},
|
|
{AUD2HTX_IRQ_NOMASK, 0x00000000},
|
|
{AUD2HTX_IRQ_MASKED, 0x00000000},
|
|
{AUD2HTX_IRQ_MASK, 0x00000000},
|
|
};
|
|
|
|
static bool fsl_aud2htx_readable_reg(struct device *dev, unsigned int reg)
|
|
{
|
|
switch (reg) {
|
|
case AUD2HTX_CTRL:
|
|
case AUD2HTX_CTRL_EXT:
|
|
case AUD2HTX_STATUS:
|
|
case AUD2HTX_IRQ_NOMASK:
|
|
case AUD2HTX_IRQ_MASKED:
|
|
case AUD2HTX_IRQ_MASK:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool fsl_aud2htx_writeable_reg(struct device *dev, unsigned int reg)
|
|
{
|
|
switch (reg) {
|
|
case AUD2HTX_CTRL:
|
|
case AUD2HTX_CTRL_EXT:
|
|
case AUD2HTX_WR:
|
|
case AUD2HTX_IRQ_NOMASK:
|
|
case AUD2HTX_IRQ_MASKED:
|
|
case AUD2HTX_IRQ_MASK:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool fsl_aud2htx_volatile_reg(struct device *dev, unsigned int reg)
|
|
{
|
|
switch (reg) {
|
|
case AUD2HTX_STATUS:
|
|
case AUD2HTX_IRQ_NOMASK:
|
|
case AUD2HTX_IRQ_MASKED:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static const struct regmap_config fsl_aud2htx_regmap_config = {
|
|
.reg_bits = 32,
|
|
.reg_stride = 4,
|
|
.val_bits = 32,
|
|
|
|
.max_register = AUD2HTX_IRQ_MASK,
|
|
.reg_defaults = fsl_aud2htx_reg_defaults,
|
|
.num_reg_defaults = ARRAY_SIZE(fsl_aud2htx_reg_defaults),
|
|
.readable_reg = fsl_aud2htx_readable_reg,
|
|
.volatile_reg = fsl_aud2htx_volatile_reg,
|
|
.writeable_reg = fsl_aud2htx_writeable_reg,
|
|
.cache_type = REGCACHE_RBTREE,
|
|
};
|
|
|
|
static const struct of_device_id fsl_aud2htx_dt_ids[] = {
|
|
{ .compatible = "fsl,imx8mp-aud2htx",},
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(of, fsl_aud2htx_dt_ids);
|
|
|
|
static irqreturn_t fsl_aud2htx_isr(int irq, void *dev_id)
|
|
{
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int fsl_aud2htx_probe(struct platform_device *pdev)
|
|
{
|
|
struct fsl_aud2htx *aud2htx;
|
|
struct resource *res;
|
|
void __iomem *regs;
|
|
int ret, irq;
|
|
|
|
aud2htx = devm_kzalloc(&pdev->dev, sizeof(*aud2htx), GFP_KERNEL);
|
|
if (!aud2htx)
|
|
return -ENOMEM;
|
|
|
|
aud2htx->pdev = pdev;
|
|
|
|
regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
|
if (IS_ERR(regs))
|
|
return PTR_ERR(regs);
|
|
|
|
aud2htx->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
|
|
&fsl_aud2htx_regmap_config);
|
|
if (IS_ERR(aud2htx->regmap)) {
|
|
dev_err(&pdev->dev, "failed to init regmap");
|
|
return PTR_ERR(aud2htx->regmap);
|
|
}
|
|
|
|
irq = platform_get_irq(pdev, 0);
|
|
if (irq < 0)
|
|
return irq;
|
|
|
|
ret = devm_request_irq(&pdev->dev, irq, fsl_aud2htx_isr, 0,
|
|
dev_name(&pdev->dev), aud2htx);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "failed to claim irq %u: %d\n", irq, ret);
|
|
return ret;
|
|
}
|
|
|
|
aud2htx->bus_clk = devm_clk_get(&pdev->dev, "bus");
|
|
if (IS_ERR(aud2htx->bus_clk)) {
|
|
dev_err(&pdev->dev, "failed to get mem clock\n");
|
|
return PTR_ERR(aud2htx->bus_clk);
|
|
}
|
|
|
|
aud2htx->dma_params_tx.chan_name = "tx";
|
|
aud2htx->dma_params_tx.maxburst = AUD2HTX_MAXBURST;
|
|
aud2htx->dma_params_tx.addr = res->start + AUD2HTX_WR;
|
|
|
|
platform_set_drvdata(pdev, aud2htx);
|
|
pm_runtime_enable(&pdev->dev);
|
|
|
|
regcache_cache_only(aud2htx->regmap, true);
|
|
|
|
/*
|
|
* Register platform component before registering cpu dai for there
|
|
* is not defer probe for platform component in snd_soc_add_pcm_runtime().
|
|
*/
|
|
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "failed to pcm register\n");
|
|
pm_runtime_disable(&pdev->dev);
|
|
return ret;
|
|
}
|
|
|
|
ret = devm_snd_soc_register_component(&pdev->dev,
|
|
&fsl_aud2htx_component,
|
|
&fsl_aud2htx_dai, 1);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "failed to register ASoC DAI\n");
|
|
pm_runtime_disable(&pdev->dev);
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void fsl_aud2htx_remove(struct platform_device *pdev)
|
|
{
|
|
pm_runtime_disable(&pdev->dev);
|
|
}
|
|
|
|
static int __maybe_unused fsl_aud2htx_runtime_suspend(struct device *dev)
|
|
{
|
|
struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev);
|
|
|
|
regcache_cache_only(aud2htx->regmap, true);
|
|
clk_disable_unprepare(aud2htx->bus_clk);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __maybe_unused fsl_aud2htx_runtime_resume(struct device *dev)
|
|
{
|
|
struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev);
|
|
int ret;
|
|
|
|
ret = clk_prepare_enable(aud2htx->bus_clk);
|
|
if (ret)
|
|
return ret;
|
|
|
|
regcache_cache_only(aud2htx->regmap, false);
|
|
regcache_mark_dirty(aud2htx->regmap);
|
|
regcache_sync(aud2htx->regmap);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dev_pm_ops fsl_aud2htx_pm_ops = {
|
|
SET_RUNTIME_PM_OPS(fsl_aud2htx_runtime_suspend,
|
|
fsl_aud2htx_runtime_resume,
|
|
NULL)
|
|
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
|
pm_runtime_force_resume)
|
|
};
|
|
|
|
static struct platform_driver fsl_aud2htx_driver = {
|
|
.probe = fsl_aud2htx_probe,
|
|
.remove_new = fsl_aud2htx_remove,
|
|
.driver = {
|
|
.name = "fsl-aud2htx",
|
|
.pm = &fsl_aud2htx_pm_ops,
|
|
.of_match_table = fsl_aud2htx_dt_ids,
|
|
},
|
|
};
|
|
module_platform_driver(fsl_aud2htx_driver);
|
|
|
|
MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>");
|
|
MODULE_DESCRIPTION("NXP AUD2HTX driver");
|
|
MODULE_LICENSE("GPL v2");
|