Path: blob/master/sound/soc/loongson/loongson_i2s_plat.c
26437 views
// SPDX-License-Identifier: GPL-2.01//2// Loongson I2S controller master mode dirver(platform device)3//4// Copyright (C) 2023-2024 Loongson Technology Corporation Limited5//6// Author: Yingkun Meng <[email protected]>7// Binbin Zhou <[email protected]>89#include <linux/clk.h>10#include <linux/dma-mapping.h>11#include <linux/module.h>12#include <linux/of_dma.h>13#include <linux/platform_device.h>14#include <linux/pm_runtime.h>15#include <sound/dmaengine_pcm.h>16#include <sound/pcm.h>17#include <sound/pcm_params.h>18#include <sound/soc.h>1920#include "loongson_i2s.h"2122#define LOONGSON_I2S_RX_DMA_OFFSET 2123#define LOONGSON_I2S_TX_DMA_OFFSET 182425#define LOONGSON_DMA0_CONF 0x026#define LOONGSON_DMA1_CONF 0x127#define LOONGSON_DMA2_CONF 0x228#define LOONGSON_DMA3_CONF 0x329#define LOONGSON_DMA4_CONF 0x43031/* periods_max = PAGE_SIZE / sizeof(struct ls_dma_chan_reg) */32static const struct snd_pcm_hardware loongson_pcm_hardware = {33.info = SNDRV_PCM_INFO_MMAP |34SNDRV_PCM_INFO_INTERLEAVED |35SNDRV_PCM_INFO_MMAP_VALID |36SNDRV_PCM_INFO_RESUME |37SNDRV_PCM_INFO_PAUSE,38.formats = SNDRV_PCM_FMTBIT_S16_LE |39SNDRV_PCM_FMTBIT_S20_3LE |40SNDRV_PCM_FMTBIT_S24_LE,41.period_bytes_min = 128,42.period_bytes_max = 128 * 1024,43.periods_min = 1,44.periods_max = 64,45.buffer_bytes_max = 1024 * 1024,46};4748static const struct snd_dmaengine_pcm_config loongson_dmaengine_pcm_config = {49.pcm_hardware = &loongson_pcm_hardware,50.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,51.prealloc_buffer_size = 128 * 1024,52};5354static int loongson_pcm_open(struct snd_soc_component *component,55struct snd_pcm_substream *substream)56{57struct snd_pcm_runtime *runtime = substream->runtime;5859if (substream->pcm->device & 1) {60runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;61runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;62}6364if (substream->pcm->device & 2)65runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP |66SNDRV_PCM_INFO_MMAP_VALID);67/*68* For mysterious reasons (and despite what the manual says)69* playback samples are lost if the DMA count is not a multiple70* of the DMA burst size. Let's add a rule to enforce that.71*/72snd_pcm_hw_constraint_step(runtime, 0,73SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128);74snd_pcm_hw_constraint_step(runtime, 0,75SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 128);76snd_pcm_hw_constraint_integer(substream->runtime,77SNDRV_PCM_HW_PARAM_PERIODS);7879return 0;80}8182static const struct snd_soc_component_driver loongson_i2s_component_driver = {83.name = LS_I2S_DRVNAME,84.open = loongson_pcm_open,85};8687static const struct regmap_config loongson_i2s_regmap_config = {88.reg_bits = 32,89.reg_stride = 4,90.val_bits = 32,91.max_register = 0x14,92.cache_type = REGCACHE_FLAT,93};9495static int loongson_i2s_apbdma_config(struct platform_device *pdev)96{97int val;98void __iomem *regs;99100regs = devm_platform_ioremap_resource(pdev, 1);101if (IS_ERR(regs))102return PTR_ERR(regs);103104val = readl(regs);105val |= LOONGSON_DMA2_CONF << LOONGSON_I2S_TX_DMA_OFFSET;106val |= LOONGSON_DMA3_CONF << LOONGSON_I2S_RX_DMA_OFFSET;107writel(val, regs);108109return 0;110}111112static int loongson_i2s_plat_probe(struct platform_device *pdev)113{114struct device *dev = &pdev->dev;115struct loongson_i2s *i2s;116struct resource *res;117struct clk *i2s_clk;118int ret;119120i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL);121if (!i2s)122return -ENOMEM;123124ret = loongson_i2s_apbdma_config(pdev);125if (ret)126return ret;127128res = platform_get_resource(pdev, IORESOURCE_MEM, 0);129i2s->reg_base = devm_ioremap_resource(&pdev->dev, res);130if (IS_ERR(i2s->reg_base))131return dev_err_probe(dev, PTR_ERR(i2s->reg_base),132"devm_ioremap_resource failed\n");133134i2s->regmap = devm_regmap_init_mmio(dev, i2s->reg_base,135&loongson_i2s_regmap_config);136if (IS_ERR(i2s->regmap))137return dev_err_probe(dev, PTR_ERR(i2s->regmap),138"devm_regmap_init_mmio failed\n");139140i2s->playback_dma_data.addr = res->start + LS_I2S_TX_DATA;141i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;142i2s->playback_dma_data.maxburst = 4;143144i2s->capture_dma_data.addr = res->start + LS_I2S_RX_DATA;145i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;146i2s->capture_dma_data.maxburst = 4;147148i2s_clk = devm_clk_get_enabled(dev, NULL);149if (IS_ERR(i2s_clk))150return dev_err_probe(dev, PTR_ERR(i2s_clk), "clock property invalid\n");151i2s->clk_rate = clk_get_rate(i2s_clk);152153dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));154dev_set_name(dev, LS_I2S_DRVNAME);155dev_set_drvdata(dev, i2s);156157ret = devm_snd_soc_register_component(dev, &loongson_i2s_component_driver,158&loongson_i2s_dai, 1);159if (ret)160return dev_err_probe(dev, ret, "failed to register DAI\n");161162return devm_snd_dmaengine_pcm_register(dev, &loongson_dmaengine_pcm_config,163SND_DMAENGINE_PCM_FLAG_COMPAT);164}165166static const struct of_device_id loongson_i2s_ids[] = {167{ .compatible = "loongson,ls2k1000-i2s" },168{ /* sentinel */ },169};170MODULE_DEVICE_TABLE(of, loongson_i2s_ids);171172static struct platform_driver loongson_i2s_driver = {173.probe = loongson_i2s_plat_probe,174.driver = {175.name = "loongson-i2s-plat",176.pm = pm_sleep_ptr(&loongson_i2s_pm),177.of_match_table = loongson_i2s_ids,178},179};180module_platform_driver(loongson_i2s_driver);181182MODULE_DESCRIPTION("Loongson I2S Master Mode ASoC Driver");183MODULE_AUTHOR("Loongson Technology Corporation Limited");184MODULE_LICENSE("GPL");185186187