713 lines
17 KiB
C
713 lines
17 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* ALSA SoC Audio Layer - Rockchip Multi-DAIS driver
|
|
*
|
|
* Copyright (c) 2018 Rockchip Electronics Co., Ltd.
|
|
* Author: Sugar Zhang <sugar.zhang@rock-chips.com>
|
|
*
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/mfd/syscon.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <sound/pcm_params.h>
|
|
#include <sound/soc.h>
|
|
|
|
#include "rockchip_multi_dais.h"
|
|
|
|
#define BITCLOCK_INV_STR "bitclock-inversion"
|
|
#define FRAME_INV_STR "frame-inversion"
|
|
#define BITCLOCK_MASTER_STR "bitclock-master"
|
|
#define FRAME_MASTER_STR "frame-master"
|
|
#define DAIS_DRV_NAME "rockchip-mdais"
|
|
#define RK3308_GRF_SOC_CON2 0x308
|
|
|
|
#define SOUND_NAME_PREFIX "sound-name-prefix"
|
|
|
|
#define I2S_CKR 0x8
|
|
#define IS_I2S_TRCM(v) ((v) & GENMASK(29, 28))
|
|
|
|
static inline struct rk_mdais_dev *to_info(struct snd_soc_dai *dai)
|
|
{
|
|
return snd_soc_dai_get_drvdata(dai);
|
|
}
|
|
|
|
static inline unsigned int *mdais_channel_maps(struct rk_mdais_dev *mdais,
|
|
struct snd_pcm_substream *substream)
|
|
{
|
|
return substream->stream ? mdais->capture_channel_maps :
|
|
mdais->playback_channel_maps;
|
|
}
|
|
|
|
static void hw_refine_channels(struct snd_pcm_hw_params *params,
|
|
unsigned int channel)
|
|
{
|
|
struct snd_interval *c =
|
|
hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
|
|
|
|
c->min = channel;
|
|
c->max = channel;
|
|
}
|
|
|
|
static int rockchip_mdais_hw_params(struct snd_pcm_substream *substream,
|
|
struct snd_pcm_hw_params *params,
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
struct rk_mdais_dev *mdais = to_info(dai);
|
|
struct snd_pcm_hw_params *cparams;
|
|
struct snd_soc_dai *child;
|
|
unsigned int *channel_maps;
|
|
unsigned int freq;
|
|
int ret = 0, i = 0;
|
|
|
|
cparams = kmemdup(params, sizeof(*params), GFP_KERNEL);
|
|
if (IS_ERR(cparams))
|
|
return PTR_ERR(cparams);
|
|
|
|
channel_maps = mdais_channel_maps(mdais, substream);
|
|
|
|
for (i = 0; i < mdais->num_dais; i++) {
|
|
child = mdais->dais[i].dai;
|
|
if (!channel_maps[i])
|
|
continue;
|
|
|
|
if (mdais->mclk_fs_maps[i] > 0) {
|
|
freq = params_rate(params) * mdais->mclk_fs_maps[i];
|
|
ret = snd_soc_dai_set_sysclk(child, substream->stream, freq,
|
|
SND_SOC_CLOCK_OUT);
|
|
if (ret && ret != -ENOTSUPP) {
|
|
dev_err(dai->dev, "Set sysclk(%uHZ) failed: %d\n", freq, ret);
|
|
break;
|
|
}
|
|
}
|
|
|
|
hw_refine_channels(cparams, channel_maps[i]);
|
|
if (child->driver->ops && child->driver->ops->hw_params) {
|
|
ret = child->driver->ops->hw_params(substream, cparams, child);
|
|
if (ret < 0) {
|
|
dev_err(dai->dev, "Failed to set %s hw params: %d\n",
|
|
dai->name, ret);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
kfree(cparams);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rockchip_mdais_hw_free(struct snd_pcm_substream *substream,
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
struct rk_mdais_dev *mdais = to_info(dai);
|
|
struct snd_soc_dai *child;
|
|
unsigned int *channel_maps;
|
|
int ret = 0, i = 0;
|
|
|
|
channel_maps = mdais_channel_maps(mdais, substream);
|
|
|
|
for (i = 0; i < mdais->num_dais; i++) {
|
|
child = mdais->dais[i].dai;
|
|
if (!channel_maps[i])
|
|
continue;
|
|
|
|
if (child->driver->ops && child->driver->ops->hw_free) {
|
|
ret = child->driver->ops->hw_free(substream, child);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rockchip_mdais_trigger(struct snd_pcm_substream *substream,
|
|
int cmd, struct snd_soc_dai *dai)
|
|
{
|
|
struct rk_mdais_dev *mdais = to_info(dai);
|
|
struct snd_soc_dai *child;
|
|
unsigned int *channel_maps;
|
|
int ret = 0, i = 0;
|
|
|
|
channel_maps = mdais_channel_maps(mdais, substream);
|
|
|
|
for (i = 0; i < mdais->num_dais; i++) {
|
|
/* skip DAIs which have no channel mapping */
|
|
if (!channel_maps[i])
|
|
continue;
|
|
|
|
child = mdais->dais[i].dai;
|
|
if (child->driver->ops && child->driver->ops->trigger) {
|
|
ret = child->driver->ops->trigger(substream,
|
|
cmd, child);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rockchip_mdais_startup(struct snd_pcm_substream *substream,
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
struct rk_mdais_dev *mdais = to_info(dai);
|
|
struct snd_soc_dai *child;
|
|
unsigned int *channel_maps;
|
|
int ret = 0, i = 0;
|
|
|
|
channel_maps = mdais_channel_maps(mdais, substream);
|
|
|
|
for (i = 0; i < mdais->num_dais; i++) {
|
|
if (!channel_maps[i])
|
|
continue;
|
|
|
|
child = mdais->dais[i].dai;
|
|
if (child->driver->ops && child->driver->ops->startup) {
|
|
ret = child->driver->ops->startup(substream, child);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rockchip_mdais_shutdown(struct snd_pcm_substream *substream,
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
struct rk_mdais_dev *mdais = to_info(dai);
|
|
struct snd_soc_dai *child;
|
|
unsigned int *channel_maps;
|
|
int i = 0;
|
|
|
|
channel_maps = mdais_channel_maps(mdais, substream);
|
|
|
|
for (i = 0; i < mdais->num_dais; i++) {
|
|
if (!channel_maps[i])
|
|
continue;
|
|
|
|
child = mdais->dais[i].dai;
|
|
if (child->driver->ops && child->driver->ops->shutdown) {
|
|
child->driver->ops->shutdown(substream, child);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int rockchip_mdais_prepare(struct snd_pcm_substream *substream,
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
struct rk_mdais_dev *mdais = to_info(dai);
|
|
struct snd_soc_dai *child;
|
|
unsigned int *channel_maps;
|
|
int ret = 0, i = 0;
|
|
|
|
channel_maps = mdais_channel_maps(mdais, substream);
|
|
|
|
for (i = 0; i < mdais->num_dais; i++) {
|
|
if (!channel_maps[i])
|
|
continue;
|
|
|
|
child = mdais->dais[i].dai;
|
|
if (child->driver->ops && child->driver->ops->prepare) {
|
|
ret = child->driver->ops->prepare(substream, child);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rockchip_mdais_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
|
|
unsigned int freq, int dir)
|
|
{
|
|
struct rk_mdais_dev *mdais = to_info(cpu_dai);
|
|
struct snd_soc_dai *child;
|
|
int ret, i = 0;
|
|
|
|
for (i = 0; i < mdais->num_dais; i++) {
|
|
child = mdais->dais[i].dai;
|
|
if (mdais->mclk_fs_maps[i] > 0)
|
|
continue;
|
|
ret = snd_soc_dai_set_sysclk(child, clk_id, freq, dir);
|
|
if (ret && ret != -ENOTSUPP) {
|
|
dev_err(cpu_dai->dev, "Set soc_dai sysclk(%uHZ) failed: %d\n", freq, ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rockchip_mdais_set_fmt(struct snd_soc_dai *cpu_dai,
|
|
unsigned int fmt)
|
|
{
|
|
struct rk_mdais_dev *mdais = to_info(cpu_dai);
|
|
struct snd_soc_dai *child;
|
|
unsigned int dai_fmt;
|
|
int ret, i = 0;
|
|
|
|
for (i = 0; i < mdais->num_dais; i++) {
|
|
child = mdais->dais[i].dai;
|
|
dai_fmt = fmt;
|
|
if (mdais->dais[i].fmt_msk) {
|
|
dai_fmt &= ~(mdais->dais[i].fmt_msk);
|
|
dai_fmt |= mdais->dais[i].fmt;
|
|
}
|
|
ret = snd_soc_dai_set_fmt(child, dai_fmt);
|
|
if (ret && ret != -ENOTSUPP)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rockchip_mdais_tdm_slot(struct snd_soc_dai *dai,
|
|
unsigned int tx_mask, unsigned int rx_mask,
|
|
int slots, int slot_width)
|
|
{
|
|
struct rk_mdais_dev *mdais = to_info(dai);
|
|
struct snd_soc_dai *child;
|
|
int ret, i = 0;
|
|
|
|
for (i = 0; i < mdais->num_dais; i++) {
|
|
child = mdais->dais[i].dai;
|
|
ret = snd_soc_dai_set_tdm_slot(child, tx_mask, rx_mask,
|
|
slots, slot_width);
|
|
if (ret && ret != -ENOTSUPP)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rockchip_mdais_dai_probe(struct snd_soc_dai *dai)
|
|
{
|
|
struct rk_mdais_dev *mdais = to_info(dai);
|
|
struct snd_soc_component *comp;
|
|
struct snd_soc_dai *child;
|
|
const char *str;
|
|
int ret, i = 0;
|
|
|
|
if (dai->probed)
|
|
return 0;
|
|
|
|
for (i = 0; i < mdais->num_dais; i++) {
|
|
child = mdais->dais[i].dai;
|
|
comp = child->component;
|
|
if (!child->probed && child->driver->probe) {
|
|
if (!comp->name_prefix) {
|
|
ret = device_property_read_string(child->dev,
|
|
SOUND_NAME_PREFIX, &str);
|
|
if (!ret)
|
|
comp->name_prefix = str;
|
|
}
|
|
|
|
comp->card = dai->component->card;
|
|
ret = child->driver->probe(child);
|
|
if (ret < 0) {
|
|
dev_err(child->dev,
|
|
"Failed to probe DAI %s: %d\n",
|
|
child->name, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = snd_soc_add_component_controls(comp,
|
|
comp->driver->controls,
|
|
comp->driver->num_controls);
|
|
if (ret)
|
|
dev_err(dai->dev, "%s: Failed to add controls, should add '%s' in DT\n",
|
|
dev_name(child->dev), SOUND_NAME_PREFIX);
|
|
|
|
dai->probed = 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct snd_soc_dai_ops rockchip_mdais_dai_ops = {
|
|
.hw_params = rockchip_mdais_hw_params,
|
|
.hw_free = rockchip_mdais_hw_free,
|
|
.set_sysclk = rockchip_mdais_set_sysclk,
|
|
.set_fmt = rockchip_mdais_set_fmt,
|
|
.set_tdm_slot = rockchip_mdais_tdm_slot,
|
|
.trigger = rockchip_mdais_trigger,
|
|
.startup = rockchip_mdais_startup,
|
|
.shutdown = rockchip_mdais_shutdown,
|
|
.prepare = rockchip_mdais_prepare,
|
|
};
|
|
|
|
static const struct snd_soc_component_driver rockchip_mdais_component = {
|
|
.name = DAIS_DRV_NAME,
|
|
.legacy_dai_naming = 1,
|
|
};
|
|
|
|
static const struct of_device_id rockchip_mdais_match[] = {
|
|
{ .compatible = "rockchip,multi-dais", },
|
|
{ .compatible = "rockchip,rk3308-multi-dais", },
|
|
{},
|
|
};
|
|
|
|
static struct snd_soc_dai *rockchip_mdais_find_dai(struct device_node *np)
|
|
{
|
|
struct snd_soc_dai_link_component dai_component = { 0 };
|
|
|
|
dai_component.of_node = np;
|
|
|
|
return snd_soc_find_dai_with_mutex(&dai_component);
|
|
}
|
|
|
|
static int mdais_runtime_suspend(struct device *dev)
|
|
{
|
|
struct rk_mdais_dev *mdais = dev_get_drvdata(dev);
|
|
struct snd_soc_dai *child;
|
|
int i = 0;
|
|
|
|
for (i = 0; i < mdais->num_dais; i++) {
|
|
child = mdais->dais[i].dai;
|
|
pm_runtime_put(child->dev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mdais_runtime_resume(struct device *dev)
|
|
{
|
|
struct rk_mdais_dev *mdais = dev_get_drvdata(dev);
|
|
struct snd_soc_dai *child;
|
|
int i = 0;
|
|
|
|
for (i = 0; i < mdais->num_dais; i++) {
|
|
child = mdais->dais[i].dai;
|
|
pm_runtime_get_sync(child->dev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mdais_read_prop_array(struct device_node *node,
|
|
const char *propname,
|
|
unsigned int *array, int num)
|
|
{
|
|
int ret = 0;
|
|
|
|
memset(array, 0, sizeof(*array) * num);
|
|
if (of_property_read_bool(node, propname)) {
|
|
ret = of_property_read_u32_array(node, propname, array, num);
|
|
if (ret)
|
|
ret = -EINVAL;
|
|
} else {
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void mdais_parse_daifmt(struct device_node *node, struct rk_dai *dais,
|
|
int num_dai)
|
|
{
|
|
unsigned int cinv[MAX_DAIS], finv[MAX_DAIS];
|
|
unsigned int cmst[MAX_DAIS], fmst[MAX_DAIS];
|
|
unsigned int format = 0, format_mask = 0;
|
|
int i = 0, ret = 0;
|
|
|
|
ret = mdais_read_prop_array(node, BITCLOCK_INV_STR, cinv, num_dai);
|
|
if (!ret)
|
|
format_mask |= SND_SOC_DAIFMT_INV_MASK;
|
|
ret = mdais_read_prop_array(node, FRAME_INV_STR, finv, num_dai);
|
|
if (!ret)
|
|
format_mask |= SND_SOC_DAIFMT_INV_MASK;
|
|
ret = mdais_read_prop_array(node, BITCLOCK_MASTER_STR, cmst, num_dai);
|
|
if (!ret)
|
|
format_mask |= SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
|
|
ret = mdais_read_prop_array(node, FRAME_MASTER_STR, fmst, num_dai);
|
|
if (!ret)
|
|
format_mask |= SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
|
|
|
|
for (i = 0; i < num_dai; i++) {
|
|
format = 0;
|
|
|
|
switch ((cinv[i] << 4) + finv[i]) {
|
|
case 0x11:
|
|
format |= SND_SOC_DAIFMT_IB_IF;
|
|
break;
|
|
case 0x10:
|
|
format |= SND_SOC_DAIFMT_IB_NF;
|
|
break;
|
|
case 0x01:
|
|
format |= SND_SOC_DAIFMT_NB_IF;
|
|
break;
|
|
default:
|
|
/* SND_SOC_DAIFMT_NB_NF is default */
|
|
break;
|
|
}
|
|
|
|
switch ((cmst[i] << 4) + fmst[i]) {
|
|
case 0x11:
|
|
format |= SND_SOC_DAIFMT_BP_FP;
|
|
break;
|
|
case 0x10:
|
|
format |= SND_SOC_DAIFMT_BP_FC;
|
|
break;
|
|
case 0x01:
|
|
format |= SND_SOC_DAIFMT_BC_FP;
|
|
break;
|
|
default:
|
|
format |= SND_SOC_DAIFMT_BC_FC;
|
|
break;
|
|
}
|
|
|
|
dais[i].fmt = format & format_mask;
|
|
dais[i].fmt_msk = format_mask;
|
|
}
|
|
}
|
|
|
|
static int rockchip_mdais_dai_prepare(struct platform_device *pdev,
|
|
struct snd_soc_dai_driver **soc_dai)
|
|
{
|
|
struct snd_soc_dai_driver rockchip_mdais_dai = {
|
|
.probe = rockchip_mdais_dai_probe,
|
|
.playback = {
|
|
.stream_name = "Playback",
|
|
.channels_min = 1,
|
|
.channels_max = 512,
|
|
.rates = SNDRV_PCM_RATE_8000_384000,
|
|
.formats = (SNDRV_PCM_FMTBIT_S8 |
|
|
SNDRV_PCM_FMTBIT_S16_LE |
|
|
SNDRV_PCM_FMTBIT_S20_3LE |
|
|
SNDRV_PCM_FMTBIT_S24_LE |
|
|
SNDRV_PCM_FMTBIT_S32_LE),
|
|
},
|
|
.capture = {
|
|
.stream_name = "Capture",
|
|
.channels_min = 1,
|
|
.channels_max = 512,
|
|
.rates = SNDRV_PCM_RATE_8000_384000,
|
|
.formats = (SNDRV_PCM_FMTBIT_S8 |
|
|
SNDRV_PCM_FMTBIT_S16_LE |
|
|
SNDRV_PCM_FMTBIT_S20_3LE |
|
|
SNDRV_PCM_FMTBIT_S24_LE |
|
|
SNDRV_PCM_FMTBIT_S32_LE),
|
|
},
|
|
.ops = &rockchip_mdais_dai_ops,
|
|
};
|
|
|
|
*soc_dai = devm_kmemdup(&pdev->dev, &rockchip_mdais_dai,
|
|
sizeof(rockchip_mdais_dai), GFP_KERNEL);
|
|
if (!(*soc_dai))
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mdais_fixup_dai(struct snd_soc_dai_driver *soc_dai,
|
|
struct rk_mdais_dev *mdais)
|
|
{
|
|
int i, tch, rch;
|
|
unsigned int *tx_maps, *rx_maps;
|
|
|
|
tch = 0;
|
|
rch = 0;
|
|
tx_maps = mdais->playback_channel_maps;
|
|
rx_maps = mdais->capture_channel_maps;
|
|
for (i = 0; i < mdais->num_dais; i++) {
|
|
tch += tx_maps[i];
|
|
rch += rx_maps[i];
|
|
}
|
|
|
|
soc_dai->playback.channels_min = tch;
|
|
soc_dai->playback.channels_max = tch;
|
|
soc_dai->capture.channels_min = rch;
|
|
soc_dai->capture.channels_max = rch;
|
|
}
|
|
|
|
static int rockchip_mdais_probe(struct platform_device *pdev)
|
|
{
|
|
struct device_node *np = pdev->dev.of_node;
|
|
struct platform_device *sub_pdev;
|
|
struct rk_mdais_dev *mdais;
|
|
struct device_node *node;
|
|
struct snd_soc_dai_driver *soc_dai;
|
|
struct rk_dai *dais;
|
|
unsigned int *map, val;
|
|
int count, mp_count;
|
|
int ret = 0, i = 0;
|
|
|
|
ret = rockchip_mdais_dai_prepare(pdev, &soc_dai);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
mdais = devm_kzalloc(&pdev->dev, sizeof(*mdais), GFP_KERNEL);
|
|
if (!mdais)
|
|
return -ENOMEM;
|
|
|
|
count = of_count_phandle_with_args(np, "dais", NULL);
|
|
if (count < 0 || count > MAX_DAIS)
|
|
return -EINVAL;
|
|
|
|
mp_count = of_property_count_u32_elems(np, "capture,channel-mapping");
|
|
if (mp_count != count)
|
|
return -EINVAL;
|
|
mp_count = of_property_count_u32_elems(np, "playback,channel-mapping");
|
|
if (mp_count != count)
|
|
return -EINVAL;
|
|
|
|
mdais->num_dais = count;
|
|
dais = devm_kcalloc(&pdev->dev, count,
|
|
sizeof(*dais), GFP_KERNEL);
|
|
if (!dais)
|
|
return -ENOMEM;
|
|
|
|
map = devm_kcalloc(&pdev->dev, count,
|
|
sizeof(*map), GFP_KERNEL);
|
|
if (!map)
|
|
return -ENOMEM;
|
|
ret = of_property_read_u32_array(np, "capture,channel-mapping",
|
|
map, count);
|
|
if (ret)
|
|
return -EINVAL;
|
|
mdais->capture_channel_maps = map;
|
|
map = devm_kcalloc(&pdev->dev, count,
|
|
sizeof(*map), GFP_KERNEL);
|
|
if (!map)
|
|
return -ENOMEM;
|
|
ret = of_property_read_u32_array(np, "playback,channel-mapping",
|
|
map, count);
|
|
if (ret)
|
|
return -EINVAL;
|
|
mdais->playback_channel_maps = map;
|
|
map = devm_kcalloc(&pdev->dev, count,
|
|
sizeof(*map), GFP_KERNEL);
|
|
if (!map)
|
|
return -ENOMEM;
|
|
ret = of_property_read_u32_array(np, "mclk-fs-mapping",
|
|
map, count);
|
|
if (ret)
|
|
memset(map, 0x0, sizeof(*map) * count);
|
|
mdais->mclk_fs_maps = map;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
node = of_parse_phandle(np, "dais", i);
|
|
sub_pdev = of_find_device_by_node(node);
|
|
if (!sub_pdev) {
|
|
dev_err(&pdev->dev, "fail to find subnode dev\n");
|
|
return -ENODEV;
|
|
}
|
|
dais[i].of_node = node;
|
|
dais[i].dev = &sub_pdev->dev;
|
|
dais[i].dai = rockchip_mdais_find_dai(node);
|
|
if (!dais[i].dai)
|
|
return -EPROBE_DEFER;
|
|
|
|
if (strstr(dev_driver_string(dais[i].dai->dev), "i2s")) {
|
|
val = snd_soc_component_read(dais[i].dai->component, I2S_CKR);
|
|
dais[i].trcm = IS_I2S_TRCM(val);
|
|
}
|
|
}
|
|
|
|
mdais_parse_daifmt(np, dais, count);
|
|
mdais_fixup_dai(soc_dai, mdais);
|
|
|
|
if (of_device_is_compatible(np, "rockchip,rk3308-multi-dais")) {
|
|
struct regmap *grf;
|
|
const char *name;
|
|
unsigned int i2s0_fmt = 0, i2s1_fmt = 0;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
name = dev_name(dais[i].dev);
|
|
if (strstr(name, "ff300000"))
|
|
i2s0_fmt = dais[i].fmt;
|
|
else if (strstr(name, "ff310000"))
|
|
i2s1_fmt = dais[i].fmt;
|
|
}
|
|
i2s0_fmt &= SND_SOC_DAIFMT_MASTER_MASK;
|
|
i2s1_fmt &= SND_SOC_DAIFMT_MASTER_MASK;
|
|
|
|
if ((i2s0_fmt == SND_SOC_DAIFMT_CBS_CFS &&
|
|
i2s1_fmt == SND_SOC_DAIFMT_CBM_CFM) ||
|
|
(i2s0_fmt == SND_SOC_DAIFMT_CBM_CFM &&
|
|
i2s1_fmt == SND_SOC_DAIFMT_CBS_CFS)) {
|
|
grf = syscon_regmap_lookup_by_phandle(np,
|
|
"rockchip,grf");
|
|
if (IS_ERR(grf))
|
|
return PTR_ERR(grf);
|
|
|
|
dev_info(&pdev->dev, "enable i2s 16ch ctrl en\n");
|
|
regmap_write(grf, RK3308_GRF_SOC_CON2,
|
|
BIT(14) << 16 | BIT(14));
|
|
}
|
|
}
|
|
|
|
mdais->dais = dais;
|
|
mdais->dev = &pdev->dev;
|
|
dev_set_drvdata(&pdev->dev, mdais);
|
|
|
|
pm_runtime_enable(&pdev->dev);
|
|
if (!pm_runtime_enabled(&pdev->dev)) {
|
|
ret = mdais_runtime_resume(&pdev->dev);
|
|
if (ret)
|
|
goto err_pm_disable;
|
|
}
|
|
|
|
ret = snd_dmaengine_mpcm_register(mdais);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "Could not register PCM\n");
|
|
goto err_suspend;
|
|
}
|
|
|
|
ret = devm_snd_soc_register_component(&pdev->dev,
|
|
&rockchip_mdais_component,
|
|
soc_dai, 1);
|
|
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "could not register dai: %d\n", ret);
|
|
goto err_suspend;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_suspend:
|
|
if (!pm_runtime_status_suspended(&pdev->dev))
|
|
mdais_runtime_resume(&pdev->dev);
|
|
err_pm_disable:
|
|
pm_runtime_disable(&pdev->dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rockchip_mdais_remove(struct platform_device *pdev)
|
|
{
|
|
snd_dmaengine_mpcm_unregister(&pdev->dev);
|
|
pm_runtime_disable(&pdev->dev);
|
|
if (!pm_runtime_status_suspended(&pdev->dev))
|
|
mdais_runtime_suspend(&pdev->dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dev_pm_ops rockchip_mdais_pm_ops = {
|
|
SET_RUNTIME_PM_OPS(mdais_runtime_suspend, mdais_runtime_resume,
|
|
NULL)
|
|
};
|
|
|
|
static struct platform_driver rockchip_mdais_driver = {
|
|
.probe = rockchip_mdais_probe,
|
|
.remove = rockchip_mdais_remove,
|
|
.driver = {
|
|
.name = DAIS_DRV_NAME,
|
|
.of_match_table = of_match_ptr(rockchip_mdais_match),
|
|
.pm = &rockchip_mdais_pm_ops,
|
|
},
|
|
};
|
|
module_platform_driver(rockchip_mdais_driver);
|
|
|
|
MODULE_DESCRIPTION("ROCKCHIP MULTI-DAIS ASoC Interface");
|
|
MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_ALIAS("platform:" DAIS_DRV_NAME);
|
|
MODULE_DEVICE_TABLE(of, rockchip_mdais_match);
|