mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-07 03:46:41 +07:00
e6b5be2be4
Here's the set of driver core patches for 3.19-rc1. They are dominated by the removal of the .owner field in platform drivers. They touch a lot of files, but they are "simple" changes, just removing a line in a structure. Other than that, a few minor driver core and debugfs changes. There are some ath9k patches coming in through this tree that have been acked by the wireless maintainers as they relied on the debugfs changes. Everything has been in linux-next for a while. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlSOD20ACgkQMUfUDdst+ylLPACg2QrW1oHhdTMT9WI8jihlHVRM 53kAoLeteByQ3iVwWurwwseRPiWa8+MI =OVRS -----END PGP SIGNATURE----- Merge tag 'driver-core-3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core Pull driver core update from Greg KH: "Here's the set of driver core patches for 3.19-rc1. They are dominated by the removal of the .owner field in platform drivers. They touch a lot of files, but they are "simple" changes, just removing a line in a structure. Other than that, a few minor driver core and debugfs changes. There are some ath9k patches coming in through this tree that have been acked by the wireless maintainers as they relied on the debugfs changes. Everything has been in linux-next for a while" * tag 'driver-core-3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (324 commits) Revert "ath: ath9k: use debugfs_create_devm_seqfile() helper for seq_file entries" fs: debugfs: add forward declaration for struct device type firmware class: Deletion of an unnecessary check before the function call "vunmap" firmware loader: fix hung task warning dump devcoredump: provide a one-way disable function device: Add dev_<level>_once variants ath: ath9k: use debugfs_create_devm_seqfile() helper for seq_file entries ath: use seq_file api for ath9k debugfs files debugfs: add helper function to create device related seq_file drivers/base: cacheinfo: remove noisy error boot message Revert "core: platform: add warning if driver has no owner" drivers: base: support cpu cache information interface to userspace via sysfs drivers: base: add cpu_device_create to support per-cpu devices topology: replace custom attribute macros with standard DEVICE_ATTR* cpumask: factor out show_cpumap into separate helper function driver core: Fix unbalanced device reference in drivers_probe driver core: fix race with userland in device_add() sysfs/kernfs: make read requests on pre-alloc files use the buffer. sysfs/kernfs: allow attributes to request write buffer be pre-allocated. fs: sysfs: return EGBIG on write if offset is larger than file size ...
542 lines
12 KiB
C
542 lines
12 KiB
C
/* sound/soc/rockchip/rockchip_i2s.c
|
|
*
|
|
* ALSA SoC Audio Layer - Rockchip I2S Controller driver
|
|
*
|
|
* Copyright (c) 2014 Rockchip Electronics Co. Ltd.
|
|
* Author: Jianqun <jay.xu@rock-chips.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/regmap.h>
|
|
#include <sound/pcm_params.h>
|
|
#include <sound/dmaengine_pcm.h>
|
|
|
|
#include "rockchip_i2s.h"
|
|
|
|
#define DRV_NAME "rockchip-i2s"
|
|
|
|
struct rk_i2s_dev {
|
|
struct device *dev;
|
|
|
|
struct clk *hclk;
|
|
struct clk *mclk;
|
|
|
|
struct snd_dmaengine_dai_dma_data capture_dma_data;
|
|
struct snd_dmaengine_dai_dma_data playback_dma_data;
|
|
|
|
struct regmap *regmap;
|
|
|
|
/*
|
|
* Used to indicate the tx/rx status.
|
|
* I2S controller hopes to start the tx and rx together,
|
|
* also to stop them when they are both try to stop.
|
|
*/
|
|
bool tx_start;
|
|
bool rx_start;
|
|
};
|
|
|
|
static int i2s_runtime_suspend(struct device *dev)
|
|
{
|
|
struct rk_i2s_dev *i2s = dev_get_drvdata(dev);
|
|
|
|
clk_disable_unprepare(i2s->mclk);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int i2s_runtime_resume(struct device *dev)
|
|
{
|
|
struct rk_i2s_dev *i2s = dev_get_drvdata(dev);
|
|
int ret;
|
|
|
|
ret = clk_prepare_enable(i2s->mclk);
|
|
if (ret) {
|
|
dev_err(i2s->dev, "clock enable failed %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline struct rk_i2s_dev *to_info(struct snd_soc_dai *dai)
|
|
{
|
|
return snd_soc_dai_get_drvdata(dai);
|
|
}
|
|
|
|
static void rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on)
|
|
{
|
|
unsigned int val = 0;
|
|
int retry = 10;
|
|
|
|
if (on) {
|
|
regmap_update_bits(i2s->regmap, I2S_DMACR,
|
|
I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_ENABLE);
|
|
|
|
regmap_update_bits(i2s->regmap, I2S_XFER,
|
|
I2S_XFER_TXS_START | I2S_XFER_RXS_START,
|
|
I2S_XFER_TXS_START | I2S_XFER_RXS_START);
|
|
|
|
i2s->tx_start = true;
|
|
} else {
|
|
i2s->tx_start = false;
|
|
|
|
regmap_update_bits(i2s->regmap, I2S_DMACR,
|
|
I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_DISABLE);
|
|
|
|
if (!i2s->rx_start) {
|
|
regmap_update_bits(i2s->regmap, I2S_XFER,
|
|
I2S_XFER_TXS_START |
|
|
I2S_XFER_RXS_START,
|
|
I2S_XFER_TXS_STOP |
|
|
I2S_XFER_RXS_STOP);
|
|
|
|
regmap_update_bits(i2s->regmap, I2S_CLR,
|
|
I2S_CLR_TXC | I2S_CLR_RXC,
|
|
I2S_CLR_TXC | I2S_CLR_RXC);
|
|
|
|
regmap_read(i2s->regmap, I2S_CLR, &val);
|
|
|
|
/* Should wait for clear operation to finish */
|
|
while (val) {
|
|
regmap_read(i2s->regmap, I2S_CLR, &val);
|
|
retry--;
|
|
if (!retry) {
|
|
dev_warn(i2s->dev, "fail to clear\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on)
|
|
{
|
|
unsigned int val = 0;
|
|
int retry = 10;
|
|
|
|
if (on) {
|
|
regmap_update_bits(i2s->regmap, I2S_DMACR,
|
|
I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_ENABLE);
|
|
|
|
regmap_update_bits(i2s->regmap, I2S_XFER,
|
|
I2S_XFER_TXS_START | I2S_XFER_RXS_START,
|
|
I2S_XFER_TXS_START | I2S_XFER_RXS_START);
|
|
|
|
i2s->rx_start = true;
|
|
} else {
|
|
i2s->rx_start = false;
|
|
|
|
regmap_update_bits(i2s->regmap, I2S_DMACR,
|
|
I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_DISABLE);
|
|
|
|
if (!i2s->tx_start) {
|
|
regmap_update_bits(i2s->regmap, I2S_XFER,
|
|
I2S_XFER_TXS_START |
|
|
I2S_XFER_RXS_START,
|
|
I2S_XFER_TXS_STOP |
|
|
I2S_XFER_RXS_STOP);
|
|
|
|
regmap_update_bits(i2s->regmap, I2S_CLR,
|
|
I2S_CLR_TXC | I2S_CLR_RXC,
|
|
I2S_CLR_TXC | I2S_CLR_RXC);
|
|
|
|
regmap_read(i2s->regmap, I2S_CLR, &val);
|
|
|
|
/* Should wait for clear operation to finish */
|
|
while (val) {
|
|
regmap_read(i2s->regmap, I2S_CLR, &val);
|
|
retry--;
|
|
if (!retry) {
|
|
dev_warn(i2s->dev, "fail to clear\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
|
|
unsigned int fmt)
|
|
{
|
|
struct rk_i2s_dev *i2s = to_info(cpu_dai);
|
|
unsigned int mask = 0, val = 0;
|
|
|
|
mask = I2S_CKR_MSS_MASK;
|
|
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
|
case SND_SOC_DAIFMT_CBS_CFS:
|
|
/* Set source clock in Master mode */
|
|
val = I2S_CKR_MSS_MASTER;
|
|
break;
|
|
case SND_SOC_DAIFMT_CBM_CFM:
|
|
val = I2S_CKR_MSS_SLAVE;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
regmap_update_bits(i2s->regmap, I2S_CKR, mask, val);
|
|
|
|
mask = I2S_TXCR_IBM_MASK;
|
|
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
|
case SND_SOC_DAIFMT_RIGHT_J:
|
|
val = I2S_TXCR_IBM_RSJM;
|
|
break;
|
|
case SND_SOC_DAIFMT_LEFT_J:
|
|
val = I2S_TXCR_IBM_LSJM;
|
|
break;
|
|
case SND_SOC_DAIFMT_I2S:
|
|
val = I2S_TXCR_IBM_NORMAL;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
regmap_update_bits(i2s->regmap, I2S_TXCR, mask, val);
|
|
|
|
mask = I2S_RXCR_IBM_MASK;
|
|
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
|
case SND_SOC_DAIFMT_RIGHT_J:
|
|
val = I2S_RXCR_IBM_RSJM;
|
|
break;
|
|
case SND_SOC_DAIFMT_LEFT_J:
|
|
val = I2S_RXCR_IBM_LSJM;
|
|
break;
|
|
case SND_SOC_DAIFMT_I2S:
|
|
val = I2S_RXCR_IBM_NORMAL;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
regmap_update_bits(i2s->regmap, I2S_RXCR, mask, val);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
|
|
struct snd_pcm_hw_params *params,
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
struct rk_i2s_dev *i2s = to_info(dai);
|
|
unsigned int val = 0;
|
|
|
|
switch (params_format(params)) {
|
|
case SNDRV_PCM_FORMAT_S8:
|
|
val |= I2S_TXCR_VDW(8);
|
|
break;
|
|
case SNDRV_PCM_FORMAT_S16_LE:
|
|
val |= I2S_TXCR_VDW(16);
|
|
break;
|
|
case SNDRV_PCM_FORMAT_S20_3LE:
|
|
val |= I2S_TXCR_VDW(20);
|
|
break;
|
|
case SNDRV_PCM_FORMAT_S24_LE:
|
|
val |= I2S_TXCR_VDW(24);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
regmap_update_bits(i2s->regmap, I2S_TXCR, I2S_TXCR_VDW_MASK, val);
|
|
regmap_update_bits(i2s->regmap, I2S_RXCR, I2S_RXCR_VDW_MASK, val);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rockchip_i2s_trigger(struct snd_pcm_substream *substream,
|
|
int cmd, struct snd_soc_dai *dai)
|
|
{
|
|
struct rk_i2s_dev *i2s = to_info(dai);
|
|
int ret = 0;
|
|
|
|
switch (cmd) {
|
|
case SNDRV_PCM_TRIGGER_START:
|
|
case SNDRV_PCM_TRIGGER_RESUME:
|
|
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
|
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
|
rockchip_snd_rxctrl(i2s, 1);
|
|
else
|
|
rockchip_snd_txctrl(i2s, 1);
|
|
break;
|
|
case SNDRV_PCM_TRIGGER_SUSPEND:
|
|
case SNDRV_PCM_TRIGGER_STOP:
|
|
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
|
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
|
rockchip_snd_rxctrl(i2s, 0);
|
|
else
|
|
rockchip_snd_txctrl(i2s, 0);
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rockchip_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
|
|
unsigned int freq, int dir)
|
|
{
|
|
struct rk_i2s_dev *i2s = to_info(cpu_dai);
|
|
int ret;
|
|
|
|
ret = clk_set_rate(i2s->mclk, freq);
|
|
if (ret)
|
|
dev_err(i2s->dev, "Fail to set mclk %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rockchip_i2s_dai_probe(struct snd_soc_dai *dai)
|
|
{
|
|
struct rk_i2s_dev *i2s = snd_soc_dai_get_drvdata(dai);
|
|
|
|
dai->capture_dma_data = &i2s->capture_dma_data;
|
|
dai->playback_dma_data = &i2s->playback_dma_data;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct snd_soc_dai_ops rockchip_i2s_dai_ops = {
|
|
.hw_params = rockchip_i2s_hw_params,
|
|
.set_sysclk = rockchip_i2s_set_sysclk,
|
|
.set_fmt = rockchip_i2s_set_fmt,
|
|
.trigger = rockchip_i2s_trigger,
|
|
};
|
|
|
|
static struct snd_soc_dai_driver rockchip_i2s_dai = {
|
|
.probe = rockchip_i2s_dai_probe,
|
|
.playback = {
|
|
.stream_name = "Playback",
|
|
.channels_min = 2,
|
|
.channels_max = 8,
|
|
.rates = SNDRV_PCM_RATE_8000_192000,
|
|
.formats = (SNDRV_PCM_FMTBIT_S8 |
|
|
SNDRV_PCM_FMTBIT_S16_LE |
|
|
SNDRV_PCM_FMTBIT_S20_3LE |
|
|
SNDRV_PCM_FMTBIT_S24_LE),
|
|
},
|
|
.capture = {
|
|
.stream_name = "Capture",
|
|
.channels_min = 2,
|
|
.channels_max = 2,
|
|
.rates = SNDRV_PCM_RATE_8000_192000,
|
|
.formats = (SNDRV_PCM_FMTBIT_S8 |
|
|
SNDRV_PCM_FMTBIT_S16_LE |
|
|
SNDRV_PCM_FMTBIT_S20_3LE |
|
|
SNDRV_PCM_FMTBIT_S24_LE),
|
|
},
|
|
.ops = &rockchip_i2s_dai_ops,
|
|
};
|
|
|
|
static const struct snd_soc_component_driver rockchip_i2s_component = {
|
|
.name = DRV_NAME,
|
|
};
|
|
|
|
static bool rockchip_i2s_wr_reg(struct device *dev, unsigned int reg)
|
|
{
|
|
switch (reg) {
|
|
case I2S_TXCR:
|
|
case I2S_RXCR:
|
|
case I2S_CKR:
|
|
case I2S_DMACR:
|
|
case I2S_INTCR:
|
|
case I2S_XFER:
|
|
case I2S_CLR:
|
|
case I2S_TXDR:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool rockchip_i2s_rd_reg(struct device *dev, unsigned int reg)
|
|
{
|
|
switch (reg) {
|
|
case I2S_TXCR:
|
|
case I2S_RXCR:
|
|
case I2S_CKR:
|
|
case I2S_DMACR:
|
|
case I2S_INTCR:
|
|
case I2S_XFER:
|
|
case I2S_CLR:
|
|
case I2S_RXDR:
|
|
case I2S_FIFOLR:
|
|
case I2S_INTSR:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool rockchip_i2s_volatile_reg(struct device *dev, unsigned int reg)
|
|
{
|
|
switch (reg) {
|
|
case I2S_INTSR:
|
|
case I2S_CLR:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool rockchip_i2s_precious_reg(struct device *dev, unsigned int reg)
|
|
{
|
|
switch (reg) {
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static const struct regmap_config rockchip_i2s_regmap_config = {
|
|
.reg_bits = 32,
|
|
.reg_stride = 4,
|
|
.val_bits = 32,
|
|
.max_register = I2S_RXDR,
|
|
.writeable_reg = rockchip_i2s_wr_reg,
|
|
.readable_reg = rockchip_i2s_rd_reg,
|
|
.volatile_reg = rockchip_i2s_volatile_reg,
|
|
.precious_reg = rockchip_i2s_precious_reg,
|
|
.cache_type = REGCACHE_FLAT,
|
|
};
|
|
|
|
static int rockchip_i2s_probe(struct platform_device *pdev)
|
|
{
|
|
struct rk_i2s_dev *i2s;
|
|
struct resource *res;
|
|
void __iomem *regs;
|
|
int ret;
|
|
|
|
i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
|
|
if (!i2s) {
|
|
dev_err(&pdev->dev, "Can't allocate rk_i2s_dev\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* try to prepare related clocks */
|
|
i2s->hclk = devm_clk_get(&pdev->dev, "i2s_hclk");
|
|
if (IS_ERR(i2s->hclk)) {
|
|
dev_err(&pdev->dev, "Can't retrieve i2s bus clock\n");
|
|
return PTR_ERR(i2s->hclk);
|
|
}
|
|
ret = clk_prepare_enable(i2s->hclk);
|
|
if (ret) {
|
|
dev_err(i2s->dev, "hclock enable failed %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
i2s->mclk = devm_clk_get(&pdev->dev, "i2s_clk");
|
|
if (IS_ERR(i2s->mclk)) {
|
|
dev_err(&pdev->dev, "Can't retrieve i2s master clock\n");
|
|
return PTR_ERR(i2s->mclk);
|
|
}
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
regs = devm_ioremap_resource(&pdev->dev, res);
|
|
if (IS_ERR(regs))
|
|
return PTR_ERR(regs);
|
|
|
|
i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
|
|
&rockchip_i2s_regmap_config);
|
|
if (IS_ERR(i2s->regmap)) {
|
|
dev_err(&pdev->dev,
|
|
"Failed to initialise managed register map\n");
|
|
return PTR_ERR(i2s->regmap);
|
|
}
|
|
|
|
i2s->playback_dma_data.addr = res->start + I2S_TXDR;
|
|
i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
|
i2s->playback_dma_data.maxburst = 16;
|
|
|
|
i2s->capture_dma_data.addr = res->start + I2S_RXDR;
|
|
i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
|
i2s->capture_dma_data.maxburst = 16;
|
|
|
|
i2s->dev = &pdev->dev;
|
|
dev_set_drvdata(&pdev->dev, i2s);
|
|
|
|
pm_runtime_enable(&pdev->dev);
|
|
if (!pm_runtime_enabled(&pdev->dev)) {
|
|
ret = i2s_runtime_resume(&pdev->dev);
|
|
if (ret)
|
|
goto err_pm_disable;
|
|
}
|
|
|
|
ret = devm_snd_soc_register_component(&pdev->dev,
|
|
&rockchip_i2s_component,
|
|
&rockchip_i2s_dai, 1);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "Could not register DAI\n");
|
|
goto err_suspend;
|
|
}
|
|
|
|
ret = snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "Could not register PCM\n");
|
|
goto err_pcm_register;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_pcm_register:
|
|
snd_dmaengine_pcm_unregister(&pdev->dev);
|
|
err_suspend:
|
|
if (!pm_runtime_status_suspended(&pdev->dev))
|
|
i2s_runtime_suspend(&pdev->dev);
|
|
err_pm_disable:
|
|
pm_runtime_disable(&pdev->dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rockchip_i2s_remove(struct platform_device *pdev)
|
|
{
|
|
struct rk_i2s_dev *i2s = dev_get_drvdata(&pdev->dev);
|
|
|
|
pm_runtime_disable(&pdev->dev);
|
|
if (!pm_runtime_status_suspended(&pdev->dev))
|
|
i2s_runtime_suspend(&pdev->dev);
|
|
|
|
clk_disable_unprepare(i2s->mclk);
|
|
clk_disable_unprepare(i2s->hclk);
|
|
snd_dmaengine_pcm_unregister(&pdev->dev);
|
|
snd_soc_unregister_component(&pdev->dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id rockchip_i2s_match[] = {
|
|
{ .compatible = "rockchip,rk3066-i2s", },
|
|
{},
|
|
};
|
|
|
|
static const struct dev_pm_ops rockchip_i2s_pm_ops = {
|
|
SET_RUNTIME_PM_OPS(i2s_runtime_suspend, i2s_runtime_resume,
|
|
NULL)
|
|
};
|
|
|
|
static struct platform_driver rockchip_i2s_driver = {
|
|
.probe = rockchip_i2s_probe,
|
|
.remove = rockchip_i2s_remove,
|
|
.driver = {
|
|
.name = DRV_NAME,
|
|
.of_match_table = of_match_ptr(rockchip_i2s_match),
|
|
.pm = &rockchip_i2s_pm_ops,
|
|
},
|
|
};
|
|
module_platform_driver(rockchip_i2s_driver);
|
|
|
|
MODULE_DESCRIPTION("ROCKCHIP IIS ASoC Interface");
|
|
MODULE_AUTHOR("jianqun <jay.xu@rock-chips.com>");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_ALIAS("platform:" DRV_NAME);
|
|
MODULE_DEVICE_TABLE(of, rockchip_i2s_match);
|