#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
--- --enum samsung_dai_type {
--- -- TYPE_PRI,
--- -- TYPE_SEC,
--- --};
--- --
struct samsung_i2s_variant_regs {
unsigned int bfs_off;
unsigned int rfs_off;
};
struct samsung_i2s_dai_data {
--- -- int dai_type;
u32 quirks;
const struct samsung_i2s_variant_regs *i2s_variant_regs;
};
unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off;
u32 mod, mask, val = 0;
unsigned long flags;
+++ ++ int ret = 0;
+++ ++
+++ ++ pm_runtime_get_sync(dai->dev);
spin_lock_irqsave(i2s->lock, flags);
mod = readl(i2s->addr + I2SMOD);
&& (mod & cdcon_mask))))) {
dev_err(&i2s->pdev->dev,
"%s:%d Other DAI busy\n", __func__, __LINE__);
--- -- return -EAGAIN;
+++ ++ ret = -EAGAIN;
+++ ++ goto err;
}
if (dir == SND_SOC_CLOCK_IN)
} else {
i2s->rclk_srcrate =
clk_get_rate(i2s->op_clk);
--- -- return 0;
+++ ++ goto done;
}
}
i2s->op_clk = clk_get(&i2s->pdev->dev,
"i2s_opclk0");
--- -- if (WARN_ON(IS_ERR(i2s->op_clk)))
--- -- return PTR_ERR(i2s->op_clk);
+++ ++ if (WARN_ON(IS_ERR(i2s->op_clk))) {
+++ ++ ret = PTR_ERR(i2s->op_clk);
+++ ++ i2s->op_clk = NULL;
+++ ++ goto err;
+++ ++ }
clk_prepare_enable(i2s->op_clk);
i2s->rclk_srcrate = clk_get_rate(i2s->op_clk);
|| (clk_id && !(mod & rsrc_mask))) {
dev_err(&i2s->pdev->dev,
"%s:%d Other DAI busy\n", __func__, __LINE__);
--- -- return -EAGAIN;
+++ ++ ret = -EAGAIN;
+++ ++ goto err;
} else {
/* Call can't be on the active DAI */
i2s->op_clk = other->op_clk;
i2s->rclk_srcrate = other->rclk_srcrate;
--- -- return 0;
+++ ++ goto done;
}
if (clk_id == 1)
break;
default:
dev_err(&i2s->pdev->dev, "We don't serve that!\n");
--- -- return -EINVAL;
+++ ++ ret = -EINVAL;
+++ ++ goto err;
}
spin_lock_irqsave(i2s->lock, flags);
mod = (mod & ~mask) | val;
writel(mod, i2s->addr + I2SMOD);
spin_unlock_irqrestore(i2s->lock, flags);
+++ ++done:
+++ ++ pm_runtime_put(dai->dev);
return 0;
+++ ++err:
+++ ++ pm_runtime_put(dai->dev);
+++ ++ return ret;
}
static int i2s_set_fmt(struct snd_soc_dai *dai,
return -EINVAL;
}
+++ ++ pm_runtime_get_sync(dai->dev);
spin_lock_irqsave(i2s->lock, flags);
mod = readl(i2s->addr + I2SMOD);
/*
if (any_active(i2s) &&
((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) {
spin_unlock_irqrestore(i2s->lock, flags);
+++ ++ pm_runtime_put(dai->dev);
dev_err(&i2s->pdev->dev,
"%s:%d Other DAI busy\n", __func__, __LINE__);
return -EAGAIN;
mod |= tmp;
writel(mod, i2s->addr + I2SMOD);
spin_unlock_irqrestore(i2s->lock, flags);
+++ ++ pm_runtime_put(dai->dev);
return 0;
}
u32 mod, mask = 0, val = 0;
unsigned long flags;
+++ ++ WARN_ON(!pm_runtime_active(dai->dev));
+++ ++
if (!is_secondary(i2s))
mask |= (MOD_DC2_EN | MOD_DC1_EN);
struct i2s_dai *other = get_other_dai(i2s);
unsigned long flags;
+++ ++ pm_runtime_get_sync(dai->dev);
+++ ++
spin_lock_irqsave(&lock, flags);
i2s->mode |= DAI_OPENED;
i2s->bfs = 0;
spin_unlock_irqrestore(&lock, flags);
+++ ++
+++ ++ pm_runtime_put(dai->dev);
}
static int config_setup(struct i2s_dai *i2s)
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+++ ++ pm_runtime_get_sync(dai->dev);
spin_lock_irqsave(i2s->lock, flags);
if (config_setup(i2s)) {
}
spin_unlock_irqrestore(i2s->lock, flags);
+++ ++ pm_runtime_put(dai->dev);
break;
}
switch (div_id) {
case SAMSUNG_I2S_DIV_BCLK:
+++ ++ pm_runtime_get_sync(dai->dev);
if ((any_active(i2s) && div && (get_bfs(i2s) != div))
|| (other && other->bfs && (other->bfs != div))) {
+++ ++ pm_runtime_put(dai->dev);
dev_err(&i2s->pdev->dev,
"%s:%d Other DAI busy\n", __func__, __LINE__);
return -EAGAIN;
}
i2s->bfs = div;
+++ ++ pm_runtime_put(dai->dev);
break;
default:
dev_err(&i2s->pdev->dev,
snd_pcm_sframes_t delay;
const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
+++ ++ WARN_ON(!pm_runtime_active(dai->dev));
+++ ++
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
delay = FIC_RXCOUNT(reg);
else if (is_secondary(i2s))
#ifdef CONFIG_PM
static int i2s_suspend(struct snd_soc_dai *dai)
{
--- -- struct i2s_dai *i2s = to_info(dai);
--- --
--- -- i2s->suspend_i2smod = readl(i2s->addr + I2SMOD);
--- -- i2s->suspend_i2scon = readl(i2s->addr + I2SCON);
--- -- i2s->suspend_i2spsr = readl(i2s->addr + I2SPSR);
--- --
--- -- return 0;
+++ ++ return pm_runtime_force_suspend(dai->dev);
}
static int i2s_resume(struct snd_soc_dai *dai)
{
--- -- struct i2s_dai *i2s = to_info(dai);
--- --
--- -- writel(i2s->suspend_i2scon, i2s->addr + I2SCON);
--- -- writel(i2s->suspend_i2smod, i2s->addr + I2SMOD);
--- -- writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR);
--- --
--- -- return 0;
+++ ++ return pm_runtime_force_resume(dai->dev);
}
#else
#define i2s_suspend NULL
struct i2s_dai *other = get_other_dai(i2s);
unsigned long flags;
+++ ++ pm_runtime_get_sync(dai->dev);
+++ ++
if (is_secondary(i2s)) { /* If this is probe on the secondary DAI */
snd_soc_dai_init_dma_data(dai, &other->sec_dai->dma_playback,
NULL);
if (!is_opened(other))
i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK,
0, SND_SOC_CLOCK_IN);
+++ ++ pm_runtime_put(dai->dev);
return 0;
}
struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai);
unsigned long flags;
+++ ++ pm_runtime_get_sync(dai->dev);
+++ ++
if (!is_secondary(i2s)) {
if (i2s->quirks & QUIRK_NEED_RSTCLR) {
spin_lock_irqsave(i2s->lock, flags);
}
}
+++ ++ pm_runtime_put(dai->dev);
+++ ++
return 0;
}
static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
{
struct i2s_dai *i2s;
--- -- int ret;
i2s = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dai), GFP_KERNEL);
if (i2s == NULL)
i2s->i2s_dai_drv.capture.channels_max = 2;
i2s->i2s_dai_drv.capture.rates = SAMSUNG_I2S_RATES;
i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS;
--- -- dev_set_drvdata(&i2s->pdev->dev, i2s);
--- -- } else { /* Create a new platform_device for Secondary */
--- -- i2s->pdev = platform_device_alloc("samsung-i2s-sec", -1);
--- -- if (!i2s->pdev)
--- -- return NULL;
--- --
--- -- i2s->pdev->dev.parent = &pdev->dev;
--- --
--- -- platform_set_drvdata(i2s->pdev, i2s);
--- -- ret = platform_device_add(i2s->pdev);
--- -- if (ret < 0)
--- -- return NULL;
}
--- --
return i2s;
}
--- --static void i2s_free_sec_dai(struct i2s_dai *i2s)
--- --{
--- -- platform_device_del(i2s->pdev);
--- --}
--- --
#ifdef CONFIG_PM
static int i2s_runtime_suspend(struct device *dev)
{
struct i2s_dai *i2s = dev_get_drvdata(dev);
+++ ++ i2s->suspend_i2smod = readl(i2s->addr + I2SMOD);
+++ ++ i2s->suspend_i2scon = readl(i2s->addr + I2SCON);
+++ ++ i2s->suspend_i2spsr = readl(i2s->addr + I2SPSR);
+++ ++
+++ ++ if (i2s->op_clk)
+++ ++ clk_disable_unprepare(i2s->op_clk);
clk_disable_unprepare(i2s->clk);
return 0;
struct i2s_dai *i2s = dev_get_drvdata(dev);
clk_prepare_enable(i2s->clk);
+++ ++ if (i2s->op_clk)
+++ ++ clk_prepare_enable(i2s->op_clk);
+++ ++
+++ ++ writel(i2s->suspend_i2scon, i2s->addr + I2SCON);
+++ ++ writel(i2s->suspend_i2smod, i2s->addr + I2SMOD);
+++ ++ writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR);
return 0;
}
u32 val = readl(i2s->addr + I2SPSR);
writel(val | PSR_PSREN, i2s->addr + I2SPSR);
--- -- i2s->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(NULL,
+++ ++ i2s->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(dev,
"i2s_rclksrc", p_names, ARRAY_SIZE(p_names),
CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
i2s->addr + I2SMOD, reg_info->rclksrc_off,
1, 0, i2s->lock);
--- -- i2s->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(NULL,
+++ ++ i2s->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(dev,
"i2s_presc", "i2s_rclksrc",
CLK_SET_RATE_PARENT,
i2s->addr + I2SPSR, 8, 6, 0, i2s->lock);
of_property_read_string_index(dev->of_node,
"clock-output-names", 0, &clk_name[0]);
--- -- i2s->clk_table[CLK_I2S_CDCLK] = clk_register_gate(NULL, clk_name[0],
+++ ++ i2s->clk_table[CLK_I2S_CDCLK] = clk_register_gate(dev, clk_name[0],
p_names[0], CLK_SET_RATE_PARENT,
i2s->addr + I2SMOD, reg_info->cdclkcon_off,
CLK_GATE_SET_TO_DISABLE, i2s->lock);
{
struct i2s_dai *pri_dai, *sec_dai = NULL;
struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data;
--- -- struct samsung_i2s *i2s_cfg = NULL;
struct resource *res;
u32 regs_base, quirks = 0, idma_addr = 0;
struct device_node *np = pdev->dev.of_node;
i2s_dai_data = (struct samsung_i2s_dai_data *)
platform_get_device_id(pdev)->driver_data;
--- -- /* Call during the secondary interface registration */
--- -- if (i2s_dai_data->dai_type == TYPE_SEC) {
--- -- sec_dai = dev_get_drvdata(&pdev->dev);
--- -- if (!sec_dai) {
--- -- dev_err(&pdev->dev, "Unable to get drvdata\n");
--- -- return -EFAULT;
--- -- }
--- -- ret = samsung_asoc_dma_platform_register(&pdev->dev,
--- -- sec_dai->filter, "tx-sec", NULL);
--- -- if (ret != 0)
--- -- return ret;
--- --
--- -- return devm_snd_soc_register_component(&sec_dai->pdev->dev,
--- -- &samsung_i2s_component,
--- -- &sec_dai->i2s_dai_drv, 1);
--- -- }
pri_dai = i2s_alloc_dai(pdev, false);
if (!pri_dai) {
pri_dai->dma_capture.filter_data = i2s_pdata->dma_capture;
pri_dai->filter = i2s_pdata->dma_filter;
--- -- if (&i2s_pdata->type)
--- -- i2s_cfg = &i2s_pdata->type.i2s;
--- --
--- -- if (i2s_cfg) {
--- -- quirks = i2s_cfg->quirks;
--- -- idma_addr = i2s_cfg->idma_addr;
--- -- }
+++ ++ quirks = i2s_pdata->type.quirks;
+++ ++ idma_addr = i2s_pdata->type.idma_addr;
} else {
quirks = i2s_dai_data->quirks;
if (of_property_read_u32(np, "samsung,idma-addr",
}
pri_dai->dma_playback.addr = regs_base + I2STXD;
pri_dai->dma_capture.addr = regs_base + I2SRXD;
+++++ pri_dai->dma_playback.chan_name = "tx";
+++++ pri_dai->dma_capture.chan_name = "rx";
pri_dai->dma_playback.addr_width = 4;
pri_dai->dma_capture.addr_width = 4;
pri_dai->quirks = quirks;
if (ret < 0)
goto err_disable_clk;
+++ ++ ret = devm_snd_soc_register_component(&pdev->dev,
+++ ++ &samsung_i2s_component,
+++ ++ &pri_dai->i2s_dai_drv, 1);
+++ ++ if (ret < 0)
+++ ++ goto err_disable_clk;
+++ ++
if (quirks & QUIRK_SEC_DAI) {
sec_dai = i2s_alloc_dai(pdev, true);
if (!sec_dai) {
sec_dai->lock = &pri_dai->spinlock;
sec_dai->variant_regs = pri_dai->variant_regs;
sec_dai->dma_playback.addr = regs_base + I2STXDS;
+++++ sec_dai->dma_playback.chan_name = "tx-sec";
if (!np) {
sec_dai->dma_playback.filter_data = i2s_pdata->dma_play_sec;
sec_dai->idma_playback.addr = idma_addr;
sec_dai->pri_dai = pri_dai;
pri_dai->sec_dai = sec_dai;
+++ ++
+++ ++ ret = samsung_asoc_dma_platform_register(&pdev->dev,
+++ ++ sec_dai->filter, "tx-sec", NULL);
+++ ++ if (ret < 0)
+++ ++ goto err_disable_clk;
+++ ++
+++ ++ ret = devm_snd_soc_register_component(&pdev->dev,
+++ ++ &samsung_i2s_component,
+++ ++ &sec_dai->i2s_dai_drv, 1);
+++ ++ if (ret < 0)
+++ ++ goto err_disable_clk;
}
if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) {
goto err_disable_clk;
}
--- -- ret = devm_snd_soc_register_component(&pri_dai->pdev->dev,
--- -- &samsung_i2s_component,
--- -- &pri_dai->i2s_dai_drv, 1);
--- -- if (ret < 0)
--- -- goto err_free_dai;
--- --
+++ ++ dev_set_drvdata(&pdev->dev, pri_dai);
+++ ++ pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
ret = i2s_register_clock_provider(pdev);
return 0;
pm_runtime_disable(&pdev->dev);
--- --err_free_dai:
--- -- if (sec_dai)
--- -- i2s_free_sec_dai(sec_dai);
err_disable_clk:
clk_disable_unprepare(pri_dai->clk);
return ret;
static int samsung_i2s_remove(struct platform_device *pdev)
{
--- -- struct i2s_dai *i2s, *other;
+++ ++ struct i2s_dai *pri_dai, *sec_dai;
--- -- i2s = dev_get_drvdata(&pdev->dev);
--- -- other = get_other_dai(i2s);
+++ ++ pri_dai = dev_get_drvdata(&pdev->dev);
+++ ++ sec_dai = pri_dai->sec_dai;
--- -- if (other) {
--- -- other->pri_dai = NULL;
--- -- other->sec_dai = NULL;
--- -- } else {
--- -- pm_runtime_disable(&pdev->dev);
--- -- }
+++ ++ pri_dai->sec_dai = NULL;
+++ ++ sec_dai->pri_dai = NULL;
--- -- if (!is_secondary(i2s)) {
--- -- i2s_unregister_clock_provider(pdev);
--- -- clk_disable_unprepare(i2s->clk);
--- -- }
+++ ++ pm_runtime_get_sync(&pdev->dev);
+++ ++ pm_runtime_disable(&pdev->dev);
--- -- i2s->pri_dai = NULL;
--- -- i2s->sec_dai = NULL;
+++ ++ i2s_unregister_clock_provider(pdev);
+++ ++ clk_disable_unprepare(pri_dai->clk);
+++ ++ pm_runtime_put_noidle(&pdev->dev);
return 0;
}
};
static const struct samsung_i2s_dai_data i2sv3_dai_type = {
--- -- .dai_type = TYPE_PRI,
.quirks = QUIRK_NO_MUXPSR,
.i2s_variant_regs = &i2sv3_regs,
};
static const struct samsung_i2s_dai_data i2sv5_dai_type = {
--- -- .dai_type = TYPE_PRI,
.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
QUIRK_SUPPORTS_IDMA,
.i2s_variant_regs = &i2sv3_regs,
};
static const struct samsung_i2s_dai_data i2sv6_dai_type = {
--- -- .dai_type = TYPE_PRI,
.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA,
.i2s_variant_regs = &i2sv6_regs,
};
static const struct samsung_i2s_dai_data i2sv7_dai_type = {
--- -- .dai_type = TYPE_PRI,
.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
QUIRK_SUPPORTS_TDM,
.i2s_variant_regs = &i2sv7_regs,
};
static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 = {
--- -- .dai_type = TYPE_PRI,
.quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR,
.i2s_variant_regs = &i2sv5_i2s1_regs,
};
--- --static const struct samsung_i2s_dai_data samsung_dai_type_sec = {
--- -- .dai_type = TYPE_SEC,
--- --};
--- --
static const struct platform_device_id samsung_i2s_driver_ids[] = {
{
.name = "samsung-i2s",
.driver_data = (kernel_ulong_t)&i2sv3_dai_type,
--- -- }, {
--- -- .name = "samsung-i2s-sec",
--- -- .driver_data = (kernel_ulong_t)&samsung_dai_type_sec,
},
{},
};
static const struct dev_pm_ops samsung_i2s_pm = {
SET_RUNTIME_PM_OPS(i2s_runtime_suspend,
i2s_runtime_resume, NULL)
+++ ++ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+++ ++ pm_runtime_force_resume)
};
static struct platform_driver samsung_i2s_driver = {
#include <linux/clk.h>
#include <linux/dmaengine.h>
#include <linux/module.h>
+++++ #include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
+++++ #include <linux/reset.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
struct clk *bus_clk;
struct clk *mod_clk;
struct regmap *regmap;
+++++ struct reset_control *rst;
unsigned int mclk_freq;
return 0;
}
+++++ struct sun4i_i2s_quirks {
+++++ bool has_reset;
+++++ };
+++++
+++++ static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = {
+++++ .has_reset = false,
+++++ };
+++++
+++++ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
+++++ .has_reset = true,
+++++ };
+++++
static int sun4i_i2s_probe(struct platform_device *pdev)
{
struct sun4i_i2s *i2s;
+++++ const struct sun4i_i2s_quirks *quirks;
struct resource *res;
void __iomem *regs;
int irq, ret;
return irq;
}
+++++ quirks = of_device_get_match_data(&pdev->dev);
+++++ if (!quirks) {
+++++ dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
+++++ return -ENODEV;
+++++ }
+++++
i2s->bus_clk = devm_clk_get(&pdev->dev, "apb");
if (IS_ERR(i2s->bus_clk)) {
dev_err(&pdev->dev, "Can't get our bus clock\n");
dev_err(&pdev->dev, "Can't get our mod clock\n");
return PTR_ERR(i2s->mod_clk);
}
-----
+++++
+++++ if (quirks->has_reset) {
+++++ i2s->rst = devm_reset_control_get(&pdev->dev, NULL);
+++++ if (IS_ERR(i2s->rst)) {
+++++ dev_err(&pdev->dev, "Failed to get reset control\n");
+++++ return PTR_ERR(i2s->rst);
+++++ }
+++++ }
+++++
+++++ if (!IS_ERR(i2s->rst)) {
+++++ ret = reset_control_deassert(i2s->rst);
+++++ if (ret) {
+++++ dev_err(&pdev->dev,
+++++ "Failed to deassert the reset control\n");
+++++ return -EINVAL;
+++++ }
+++++ }
+++++
i2s->playback_dma_data.addr = res->start + SUN4I_I2S_FIFO_TX_REG;
----- i2s->playback_dma_data.maxburst = 4;
+++++ i2s->playback_dma_data.maxburst = 8;
i2s->capture_dma_data.addr = res->start + SUN4I_I2S_FIFO_RX_REG;
----- i2s->capture_dma_data.maxburst = 4;
+++++ i2s->capture_dma_data.maxburst = 8;
pm_runtime_enable(&pdev->dev);
if (!pm_runtime_enabled(&pdev->dev)) {
sun4i_i2s_runtime_suspend(&pdev->dev);
err_pm_disable:
pm_runtime_disable(&pdev->dev);
+++++ if (!IS_ERR(i2s->rst))
+++++ reset_control_assert(i2s->rst);
return ret;
}
static int sun4i_i2s_remove(struct platform_device *pdev)
{
+++++ struct sun4i_i2s *i2s = dev_get_drvdata(&pdev->dev);
+++++
snd_dmaengine_pcm_unregister(&pdev->dev);
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
sun4i_i2s_runtime_suspend(&pdev->dev);
+++++ if (!IS_ERR(i2s->rst))
+++++ reset_control_assert(i2s->rst);
+++++
return 0;
}
static const struct of_device_id sun4i_i2s_match[] = {
----- { .compatible = "allwinner,sun4i-a10-i2s", },
+++++ {
+++++ .compatible = "allwinner,sun4i-a10-i2s",
+++++ .data = &sun4i_a10_i2s_quirks,
+++++ },
+++++ {
+++++ .compatible = "allwinner,sun6i-a31-i2s",
+++++ .data = &sun6i_a31_i2s_quirks,
+++++ },
{}
};
MODULE_DEVICE_TABLE(of, sun4i_i2s_match);