Commit cdd23ae6 authored by Mark Brown's avatar Mark Brown

Merge series "Fix reset controls and RPM of NVIDIA Tegra ASoC drivers" from...

Merge series "Fix reset controls and RPM of NVIDIA Tegra ASoC drivers" from Dmitry Osipenko <digetx@gmail.com>:

Hi,

This series adds missing hardware reset controls to I2S and AC97 drivers,
corrects runtime PM usage and drivers probe/remove order. Currently drivers
happen to work properly because reset is implicitly deasserted by tegra-clk
driver, but clk driver shouldn't touch the resets and we need to fix it
because this breaks other Tegra drivers. Previously we fixed the resets of
the AHUB and HDMI codec drivers, but turned out that we missed the I2C and
AC97 drivers.

Thanks to Paul Fertser for testing the pending clk patches and finding
that audio got broken on Tegra20 AC100 netbook because of the missing I2S
reset.

Changelog:

v5: - After taking another look at the drivers I noticed couple more
      things that could be improved. These new patches correct runtime PM
      and probe/remove order of the drivers:

        ASoC: tegra20: spdif: Correct driver removal order
        ASoC: tegra20: spdif: Remove handing of disabled runtime PM
        ASoC: tegra20: i2s: Add system level suspend-resume callbacks
        ASoC: tegra20: i2s: Correct driver removal order
        ASoC: tegra20: i2s: Use devm_clk_get()
        ASoC: tegra20: i2s: Remove handing of disabled runtime PM
        ASoC: tegra30: i2s: Correct driver removal order
        ASoC: tegra30: i2s: Use devm_clk_get()
        ASoC: tegra30: i2s: Remove handing of disabled runtime PM
        ASoC: tegra30: ahub: Reset global variable
        ASoC: tegra30: ahub: Correct suspend-resume callbacks
        ASoC: tegra30: ahub: Remove handing of disabled runtime PM

v4: - Added missing prototype for reset_control_bulk_put().

v3: - Fixed reset stubs for !CONFIG_RESET_CONTROLLER.

v2: - After some more testing I found that I2S control logic doesn't require
      I2S clock to be enabled for resetting. Hence it's fine to have I2S to
      be reset by parent AHUB driver, so I dropped "tegra30: i2s: Add reset
      control" patch.

    - While I was double-checking resets on Tegra30, I found that that
      Tegra30 I2S driver has a broken runtime PM which doesn't restore
      hardware state on resume and it's lost after AHUB RPM-resume.
      Thus, added this new patch "tegra30: i2s: Restore hardware state
      on runtime PM resume".

    - Added new patches which switch AHUB driver to use reset-bulk API.
      I took the RFC patch from Philipp Zabel, fixed it and added
      devm_reset_control_bulk_optional_get_exclusive_released() that
      will be useful for further Tegra GPU patches. This is a minor
      improvement which makes code cleaner.

Dmitry Osipenko (16):
  ASoC: tegra20: ac97: Add reset control
  ASoC: tegra20: i2s: Add reset control
  ASoC: tegra30: i2s: Restore hardware state on runtime PM resume
  ASoC: tegra30: ahub: Switch to use reset-bulk API
  ASoC: tegra20: spdif: Correct driver removal order
  ASoC: tegra20: spdif: Remove handing of disabled runtime PM
  ASoC: tegra20: i2s: Add system level suspend-resume callbacks
  ASoC: tegra20: i2s: Correct driver removal order
  ASoC: tegra20: i2s: Use devm_clk_get()
  ASoC: tegra20: i2s: Remove handing of disabled runtime PM
  ASoC: tegra30: i2s: Correct driver removal order
  ASoC: tegra30: i2s: Use devm_clk_get()
  ASoC: tegra30: i2s: Remove handing of disabled runtime PM
  ASoC: tegra30: ahub: Reset global variable
  ASoC: tegra30: ahub: Correct suspend-resume callbacks
  ASoC: tegra30: ahub: Remove handing of disabled runtime PM

Philipp Zabel (1):
  reset: Add reset_control_bulk API

 drivers/reset/core.c            | 215 ++++++++++++++++++++++
 include/linux/reset.h           | 315 ++++++++++++++++++++++++++++++++
 sound/soc/tegra/tegra20_ac97.c  |  21 +++
 sound/soc/tegra/tegra20_ac97.h  |   1 +
 sound/soc/tegra/tegra20_i2s.c   |  60 +++---
 sound/soc/tegra/tegra20_i2s.h   |   1 +
 sound/soc/tegra/tegra20_spdif.c |  16 +-
 sound/soc/tegra/tegra30_ahub.c  | 168 ++++++-----------
 sound/soc/tegra/tegra30_ahub.h  |   5 +-
 sound/soc/tegra/tegra30_i2s.c   |  65 ++-----
 10 files changed, 667 insertions(+), 200 deletions(-)

--
2.30.2

base-commit: a38fd874
parents ad83b1ad b5571449
......@@ -358,6 +358,30 @@ int reset_control_reset(struct reset_control *rstc)
}
EXPORT_SYMBOL_GPL(reset_control_reset);
/**
* reset_control_bulk_reset - reset the controlled devices in order
* @num_rstcs: number of entries in rstcs array
* @rstcs: array of struct reset_control_bulk_data with reset controls set
*
* Issue a reset on all provided reset controls, in order.
*
* See also: reset_control_reset()
*/
int reset_control_bulk_reset(int num_rstcs,
struct reset_control_bulk_data *rstcs)
{
int ret, i;
for (i = 0; i < num_rstcs; i++) {
ret = reset_control_reset(rstcs[i].rstc);
if (ret)
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(reset_control_bulk_reset);
/**
* reset_control_rearm - allow shared reset line to be re-triggered"
* @rstc: reset controller
......@@ -461,6 +485,36 @@ int reset_control_assert(struct reset_control *rstc)
}
EXPORT_SYMBOL_GPL(reset_control_assert);
/**
* reset_control_bulk_assert - asserts the reset lines in order
* @num_rstcs: number of entries in rstcs array
* @rstcs: array of struct reset_control_bulk_data with reset controls set
*
* Assert the reset lines for all provided reset controls, in order.
* If an assertion fails, already asserted resets are deasserted again.
*
* See also: reset_control_assert()
*/
int reset_control_bulk_assert(int num_rstcs,
struct reset_control_bulk_data *rstcs)
{
int ret, i;
for (i = 0; i < num_rstcs; i++) {
ret = reset_control_assert(rstcs[i].rstc);
if (ret)
goto err;
}
return 0;
err:
while (i--)
reset_control_deassert(rstcs[i].rstc);
return ret;
}
EXPORT_SYMBOL_GPL(reset_control_bulk_assert);
/**
* reset_control_deassert - deasserts the reset line
* @rstc: reset controller
......@@ -511,6 +565,36 @@ int reset_control_deassert(struct reset_control *rstc)
}
EXPORT_SYMBOL_GPL(reset_control_deassert);
/**
* reset_control_bulk_deassert - deasserts the reset lines in reverse order
* @num_rstcs: number of entries in rstcs array
* @rstcs: array of struct reset_control_bulk_data with reset controls set
*
* Deassert the reset lines for all provided reset controls, in reverse order.
* If a deassertion fails, already deasserted resets are asserted again.
*
* See also: reset_control_deassert()
*/
int reset_control_bulk_deassert(int num_rstcs,
struct reset_control_bulk_data *rstcs)
{
int ret, i;
for (i = num_rstcs - 1; i >= 0; i--) {
ret = reset_control_deassert(rstcs[i].rstc);
if (ret)
goto err;
}
return 0;
err:
while (i < num_rstcs)
reset_control_assert(rstcs[i++].rstc);
return ret;
}
EXPORT_SYMBOL_GPL(reset_control_bulk_deassert);
/**
* reset_control_status - returns a negative errno if not supported, a
* positive value if the reset line is asserted, or zero if the reset
......@@ -588,6 +672,36 @@ int reset_control_acquire(struct reset_control *rstc)
}
EXPORT_SYMBOL_GPL(reset_control_acquire);
/**
* reset_control_bulk_acquire - acquires reset controls for exclusive use
* @num_rstcs: number of entries in rstcs array
* @rstcs: array of struct reset_control_bulk_data with reset controls set
*
* This is used to explicitly acquire reset controls requested with
* reset_control_bulk_get_exclusive_release() for temporary exclusive use.
*
* See also: reset_control_acquire(), reset_control_bulk_release()
*/
int reset_control_bulk_acquire(int num_rstcs,
struct reset_control_bulk_data *rstcs)
{
int ret, i;
for (i = 0; i < num_rstcs; i++) {
ret = reset_control_acquire(rstcs[i].rstc);
if (ret)
goto err;
}
return 0;
err:
while (i--)
reset_control_release(rstcs[i].rstc);
return ret;
}
EXPORT_SYMBOL_GPL(reset_control_bulk_acquire);
/**
* reset_control_release() - releases exclusive access to a reset control
* @rstc: reset control
......@@ -610,6 +724,26 @@ void reset_control_release(struct reset_control *rstc)
}
EXPORT_SYMBOL_GPL(reset_control_release);
/**
* reset_control_bulk_release() - releases exclusive access to reset controls
* @num_rstcs: number of entries in rstcs array
* @rstcs: array of struct reset_control_bulk_data with reset controls set
*
* Releases exclusive access right to reset controls previously obtained by a
* call to reset_control_bulk_acquire().
*
* See also: reset_control_release(), reset_control_bulk_acquire()
*/
void reset_control_bulk_release(int num_rstcs,
struct reset_control_bulk_data *rstcs)
{
int i;
for (i = 0; i < num_rstcs; i++)
reset_control_release(rstcs[i].rstc);
}
EXPORT_SYMBOL_GPL(reset_control_bulk_release);
static struct reset_control *__reset_control_get_internal(
struct reset_controller_dev *rcdev,
unsigned int index, bool shared, bool acquired)
......@@ -814,6 +948,32 @@ struct reset_control *__reset_control_get(struct device *dev, const char *id,
}
EXPORT_SYMBOL_GPL(__reset_control_get);
int __reset_control_bulk_get(struct device *dev, int num_rstcs,
struct reset_control_bulk_data *rstcs,
bool shared, bool optional, bool acquired)
{
int ret, i;
for (i = 0; i < num_rstcs; i++) {
rstcs[i].rstc = __reset_control_get(dev, rstcs[i].id, 0,
shared, optional, acquired);
if (IS_ERR(rstcs[i].rstc)) {
ret = PTR_ERR(rstcs[i].rstc);
goto err;
}
}
return 0;
err:
mutex_lock(&reset_list_mutex);
while (i--)
__reset_control_put_internal(rstcs[i].rstc);
mutex_unlock(&reset_list_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(__reset_control_bulk_get);
static void reset_control_array_put(struct reset_control_array *resets)
{
int i;
......@@ -845,6 +1005,23 @@ void reset_control_put(struct reset_control *rstc)
}
EXPORT_SYMBOL_GPL(reset_control_put);
/**
* reset_control_bulk_put - free the reset controllers
* @num_rstcs: number of entries in rstcs array
* @rstcs: array of struct reset_control_bulk_data with reset controls set
*/
void reset_control_bulk_put(int num_rstcs, struct reset_control_bulk_data *rstcs)
{
mutex_lock(&reset_list_mutex);
while (num_rstcs--) {
if (IS_ERR_OR_NULL(rstcs[num_rstcs].rstc))
continue;
__reset_control_put_internal(rstcs[num_rstcs].rstc);
}
mutex_unlock(&reset_list_mutex);
}
EXPORT_SYMBOL_GPL(reset_control_bulk_put);
static void devm_reset_control_release(struct device *dev, void *res)
{
reset_control_put(*(struct reset_control **)res);
......@@ -874,6 +1051,44 @@ struct reset_control *__devm_reset_control_get(struct device *dev,
}
EXPORT_SYMBOL_GPL(__devm_reset_control_get);
struct reset_control_bulk_devres {
int num_rstcs;
struct reset_control_bulk_data *rstcs;
};
static void devm_reset_control_bulk_release(struct device *dev, void *res)
{
struct reset_control_bulk_devres *devres = res;
reset_control_bulk_put(devres->num_rstcs, devres->rstcs);
}
int __devm_reset_control_bulk_get(struct device *dev, int num_rstcs,
struct reset_control_bulk_data *rstcs,
bool shared, bool optional, bool acquired)
{
struct reset_control_bulk_devres *ptr;
int ret;
ptr = devres_alloc(devm_reset_control_bulk_release, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
return -ENOMEM;
ret = __reset_control_bulk_get(dev, num_rstcs, rstcs, shared, optional, acquired);
if (ret < 0) {
devres_free(ptr);
return ret;
}
ptr->num_rstcs = num_rstcs;
ptr->rstcs = rstcs;
devres_add(dev, ptr);
return 0;
}
EXPORT_SYMBOL_GPL(__devm_reset_control_bulk_get);
/**
* __device_reset - find reset controller associated with the device
* and perform reset
......
This diff is collapsed.
......@@ -21,6 +21,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
......@@ -313,6 +314,12 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
}
dev_set_drvdata(&pdev->dev, ac97);
ac97->reset = devm_reset_control_get_exclusive(&pdev->dev, "ac97");
if (IS_ERR(ac97->reset)) {
dev_err(&pdev->dev, "Can't retrieve ac97 reset\n");
return PTR_ERR(ac97->reset);
}
ac97->clk_ac97 = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(ac97->clk_ac97)) {
dev_err(&pdev->dev, "Can't retrieve ac97 clock\n");
......@@ -364,12 +371,26 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
ac97->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
ac97->playback_dma_data.maxburst = 4;
ret = reset_control_assert(ac97->reset);
if (ret) {
dev_err(&pdev->dev, "Failed to assert AC'97 reset: %d\n", ret);
goto err_clk_put;
}
ret = clk_prepare_enable(ac97->clk_ac97);
if (ret) {
dev_err(&pdev->dev, "clk_enable failed: %d\n", ret);
goto err_clk_put;
}
usleep_range(10, 100);
ret = reset_control_deassert(ac97->reset);
if (ret) {
dev_err(&pdev->dev, "Failed to deassert AC'97 reset: %d\n", ret);
goto err_clk_disable_unprepare;
}
ret = snd_soc_set_ac97_ops(&tegra20_ac97_ops);
if (ret) {
dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret);
......
......@@ -78,6 +78,7 @@ struct tegra20_ac97 {
struct clk *clk_ac97;
struct snd_dmaengine_dai_dma_data capture_dma_data;
struct snd_dmaengine_dai_dma_data playback_dma_data;
struct reset_control *reset;
struct regmap *regmap;
int reset_gpio;
int sync_gpio;
......
......@@ -22,6 +22,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
......@@ -37,6 +38,8 @@ static int tegra20_i2s_runtime_suspend(struct device *dev)
{
struct tegra20_i2s *i2s = dev_get_drvdata(dev);
regcache_cache_only(i2s->regmap, true);
clk_disable_unprepare(i2s->clk_i2s);
return 0;
......@@ -47,13 +50,35 @@ static int tegra20_i2s_runtime_resume(struct device *dev)
struct tegra20_i2s *i2s = dev_get_drvdata(dev);
int ret;
ret = reset_control_assert(i2s->reset);
if (ret)
return ret;
ret = clk_prepare_enable(i2s->clk_i2s);
if (ret) {
dev_err(dev, "clk_enable failed: %d\n", ret);
return ret;
}
usleep_range(10, 100);
ret = reset_control_deassert(i2s->reset);
if (ret)
goto disable_clocks;
regcache_cache_only(i2s->regmap, false);
regcache_mark_dirty(i2s->regmap);
ret = regcache_sync(i2s->regmap);
if (ret)
goto disable_clocks;
return 0;
disable_clocks:
clk_disable_unprepare(i2s->clk_i2s);
return ret;
}
static int tegra20_i2s_set_fmt(struct snd_soc_dai *dai,
......@@ -339,7 +364,13 @@ static int tegra20_i2s_platform_probe(struct platform_device *pdev)
i2s->dai = tegra20_i2s_dai_template;
i2s->dai.name = dev_name(&pdev->dev);
i2s->clk_i2s = clk_get(&pdev->dev, NULL);
i2s->reset = devm_reset_control_get_exclusive(&pdev->dev, "i2s");
if (IS_ERR(i2s->reset)) {
dev_err(&pdev->dev, "Can't retrieve i2s reset\n");
return PTR_ERR(i2s->reset);
}
i2s->clk_i2s = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(i2s->clk_i2s)) {
dev_err(&pdev->dev, "Can't retrieve i2s clock\n");
ret = PTR_ERR(i2s->clk_i2s);
......@@ -350,7 +381,7 @@ static int tegra20_i2s_platform_probe(struct platform_device *pdev)
regs = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(regs)) {
ret = PTR_ERR(regs);
goto err_clk_put;
goto err;
}
i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
......@@ -358,7 +389,7 @@ static int tegra20_i2s_platform_probe(struct platform_device *pdev)
if (IS_ERR(i2s->regmap)) {
dev_err(&pdev->dev, "regmap init failed\n");
ret = PTR_ERR(i2s->regmap);
goto err_clk_put;
goto err;
}
i2s->capture_dma_data.addr = mem->start + TEGRA20_I2S_FIFO2;
......@@ -370,18 +401,13 @@ static int tegra20_i2s_platform_probe(struct platform_device *pdev)
i2s->playback_dma_data.maxburst = 4;
pm_runtime_enable(&pdev->dev);
if (!pm_runtime_enabled(&pdev->dev)) {
ret = tegra20_i2s_runtime_resume(&pdev->dev);
if (ret)
goto err_pm_disable;
}
ret = snd_soc_register_component(&pdev->dev, &tegra20_i2s_component,
&i2s->dai, 1);
if (ret) {
dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
ret = -ENOMEM;
goto err_suspend;
goto err_pm_disable;
}
ret = tegra_pcm_platform_register(&pdev->dev);
......@@ -394,29 +420,17 @@ static int tegra20_i2s_platform_probe(struct platform_device *pdev)
err_unregister_component:
snd_soc_unregister_component(&pdev->dev);
err_suspend:
if (!pm_runtime_status_suspended(&pdev->dev))
tegra20_i2s_runtime_suspend(&pdev->dev);
err_pm_disable:
pm_runtime_disable(&pdev->dev);
err_clk_put:
clk_put(i2s->clk_i2s);
err:
return ret;
}
static int tegra20_i2s_platform_remove(struct platform_device *pdev)
{
struct tegra20_i2s *i2s = dev_get_drvdata(&pdev->dev);
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
tegra20_i2s_runtime_suspend(&pdev->dev);
tegra_pcm_platform_unregister(&pdev->dev);
snd_soc_unregister_component(&pdev->dev);
clk_put(i2s->clk_i2s);
pm_runtime_disable(&pdev->dev);
return 0;
}
......@@ -429,6 +443,8 @@ static const struct of_device_id tegra20_i2s_of_match[] = {
static const struct dev_pm_ops tegra20_i2s_pm_ops = {
SET_RUNTIME_PM_OPS(tegra20_i2s_runtime_suspend,
tegra20_i2s_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static struct platform_driver tegra20_i2s_driver = {
......
......@@ -144,6 +144,7 @@ struct tegra20_i2s {
struct snd_dmaengine_dai_dma_data capture_dma_data;
struct snd_dmaengine_dai_dma_data playback_dma_data;
struct regmap *regmap;
struct reset_control *reset;
};
#endif
......@@ -294,18 +294,13 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev)
spdif->playback_dma_data.slave_id = dmareq->start;
pm_runtime_enable(&pdev->dev);
if (!pm_runtime_enabled(&pdev->dev)) {
ret = tegra20_spdif_runtime_resume(&pdev->dev);
if (ret)
goto err_pm_disable;
}
ret = snd_soc_register_component(&pdev->dev, &tegra20_spdif_component,
&tegra20_spdif_dai, 1);
if (ret) {
dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
ret = -ENOMEM;
goto err_suspend;
goto err_pm_disable;
}
ret = tegra_pcm_platform_register(&pdev->dev);
......@@ -318,9 +313,6 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev)
err_unregister_component:
snd_soc_unregister_component(&pdev->dev);
err_suspend:
if (!pm_runtime_status_suspended(&pdev->dev))
tegra20_spdif_runtime_suspend(&pdev->dev);
err_pm_disable:
pm_runtime_disable(&pdev->dev);
......@@ -329,13 +321,11 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev)
static int tegra20_spdif_platform_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
tegra20_spdif_runtime_suspend(&pdev->dev);
tegra_pcm_platform_unregister(&pdev->dev);
snd_soc_unregister_component(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return 0;
}
......
......@@ -65,7 +65,7 @@ static int tegra30_ahub_runtime_resume(struct device *dev)
{
int ret;
ret = reset_control_assert(ahub->reset);
ret = reset_control_bulk_assert(ahub->nresets, ahub->resets);
if (ret)
return ret;
......@@ -75,7 +75,7 @@ static int tegra30_ahub_runtime_resume(struct device *dev)
usleep_range(10, 100);
ret = reset_control_deassert(ahub->reset);
ret = reset_control_bulk_deassert(ahub->nresets, ahub->resets);
if (ret)
goto disable_clocks;
......@@ -339,41 +339,28 @@ int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif)
}
EXPORT_SYMBOL_GPL(tegra30_ahub_unset_rx_cif_source);
#define MOD_LIST_MASK_TEGRA30 BIT(0)
#define MOD_LIST_MASK_TEGRA114 BIT(1)
#define MOD_LIST_MASK_TEGRA124 BIT(2)
#define MOD_LIST_MASK_TEGRA30_OR_LATER \
(MOD_LIST_MASK_TEGRA30 | MOD_LIST_MASK_TEGRA114 | \
MOD_LIST_MASK_TEGRA124)
#define MOD_LIST_MASK_TEGRA114_OR_LATER \
(MOD_LIST_MASK_TEGRA114 | MOD_LIST_MASK_TEGRA124)
static const struct {
const char *rst_name;
u32 mod_list_mask;
} configlink_mods[] = {
{ "d_audio", MOD_LIST_MASK_TEGRA30_OR_LATER },
{ "apbif", MOD_LIST_MASK_TEGRA30_OR_LATER },
{ "i2s0", MOD_LIST_MASK_TEGRA30_OR_LATER },
{ "i2s1", MOD_LIST_MASK_TEGRA30_OR_LATER },
{ "i2s2", MOD_LIST_MASK_TEGRA30_OR_LATER },
{ "i2s3", MOD_LIST_MASK_TEGRA30_OR_LATER },
{ "i2s4", MOD_LIST_MASK_TEGRA30_OR_LATER },
{ "dam0", MOD_LIST_MASK_TEGRA30_OR_LATER },
{ "dam1", MOD_LIST_MASK_TEGRA30_OR_LATER },
{ "dam2", MOD_LIST_MASK_TEGRA30_OR_LATER },
{ "spdif", MOD_LIST_MASK_TEGRA30_OR_LATER },
{ "amx", MOD_LIST_MASK_TEGRA114_OR_LATER },
{ "adx", MOD_LIST_MASK_TEGRA114_OR_LATER },
{ "amx1", MOD_LIST_MASK_TEGRA124 },
{ "adx1", MOD_LIST_MASK_TEGRA124 },
{ "afc0", MOD_LIST_MASK_TEGRA124 },
{ "afc1", MOD_LIST_MASK_TEGRA124 },
{ "afc2", MOD_LIST_MASK_TEGRA124 },
{ "afc3", MOD_LIST_MASK_TEGRA124 },
{ "afc4", MOD_LIST_MASK_TEGRA124 },
{ "afc5", MOD_LIST_MASK_TEGRA124 },
static const struct reset_control_bulk_data tegra30_ahub_resets_data[] = {
{ "d_audio" },
{ "apbif" },
{ "i2s0" },
{ "i2s1" },
{ "i2s2" },
{ "i2s3" },
{ "i2s4" },
{ "dam0" },
{ "dam1" },
{ "dam2" },
{ "spdif" },
{ "amx" }, /* Tegra114+ */
{ "adx" }, /* Tegra114+ */
{ "amx1" }, /* Tegra124 */
{ "adx1" }, /* Tegra124 */
{ "afc0" }, /* Tegra124 */
{ "afc1" }, /* Tegra124 */
{ "afc2" }, /* Tegra124 */
{ "afc3" }, /* Tegra124 */
{ "afc4" }, /* Tegra124 */
{ "afc5" }, /* Tegra124 */
};
#define LAST_REG(name) \
......@@ -502,17 +489,17 @@ static const struct regmap_config tegra30_ahub_ahub_regmap_config = {
};
static struct tegra30_ahub_soc_data soc_data_tegra30 = {
.mod_list_mask = MOD_LIST_MASK_TEGRA30,
.num_resets = 11,
.set_audio_cif = tegra30_ahub_set_cif,
};
static struct tegra30_ahub_soc_data soc_data_tegra114 = {
.mod_list_mask = MOD_LIST_MASK_TEGRA114,
.num_resets = 13,
.set_audio_cif = tegra30_ahub_set_cif,
};
static struct tegra30_ahub_soc_data soc_data_tegra124 = {
.mod_list_mask = MOD_LIST_MASK_TEGRA124,
.num_resets = 21,
.set_audio_cif = tegra124_ahub_set_cif,
};
......@@ -527,48 +514,25 @@ static int tegra30_ahub_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
const struct tegra30_ahub_soc_data *soc_data;
struct reset_control *rst;
int i;
struct resource *res0;
void __iomem *regs_apbif, *regs_ahub;
int ret = 0;
if (ahub)
return -ENODEV;
match = of_match_device(tegra30_ahub_of_match, &pdev->dev);
if (!match)
return -EINVAL;
soc_data = match->data;
/*
* The AHUB hosts a register bus: the "configlink". For this to
* operate correctly, all devices on this bus must be out of reset.
*/
for (i = 0; i < ARRAY_SIZE(configlink_mods); i++) {
if (!(configlink_mods[i].mod_list_mask &
soc_data->mod_list_mask))
continue;
rst = reset_control_get_exclusive(&pdev->dev,
configlink_mods[i].rst_name);
if (IS_ERR(rst)) {
dev_err(&pdev->dev, "Can't get reset %s\n",
configlink_mods[i].rst_name);
ret = PTR_ERR(rst);
return ret;
}
/* just check presence of the reset control in DT */
reset_control_put(rst);
}
ahub = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_ahub),
GFP_KERNEL);
if (!ahub)
return -ENOMEM;
dev_set_drvdata(&pdev->dev, ahub);
BUILD_BUG_ON(sizeof(ahub->resets) != sizeof(tegra30_ahub_resets_data));
memcpy(ahub->resets, tegra30_ahub_resets_data, sizeof(ahub->resets));
ahub->nresets = soc_data->num_resets;
ahub->soc_data = soc_data;
ahub->dev = &pdev->dev;
......@@ -577,18 +541,21 @@ static int tegra30_ahub_probe(struct platform_device *pdev)
ret = devm_clk_bulk_get(&pdev->dev, ahub->nclocks, ahub->clocks);
if (ret)
return ret;
goto err_unset_ahub;
ahub->reset = devm_reset_control_array_get_exclusive(&pdev->dev);
if (IS_ERR(ahub->reset)) {
dev_err(&pdev->dev, "Can't get resets: %pe\n", ahub->reset);
return PTR_ERR(ahub->reset);
ret = devm_reset_control_bulk_get_exclusive(&pdev->dev, ahub->nresets,
ahub->resets);
if (ret) {
dev_err(&pdev->dev, "Can't get resets: %d\n", ret);
goto err_unset_ahub;
}
res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs_apbif = devm_ioremap_resource(&pdev->dev, res0);
if (IS_ERR(regs_apbif))
return PTR_ERR(regs_apbif);
if (IS_ERR(regs_apbif)) {
ret = PTR_ERR(regs_apbif);
goto err_unset_ahub;
}
ahub->apbif_addr = res0->start;
......@@ -597,82 +564,51 @@ static int tegra30_ahub_probe(struct platform_device *pdev)
if (IS_ERR(ahub->regmap_apbif)) {
dev_err(&pdev->dev, "apbif regmap init failed\n");
ret = PTR_ERR(ahub->regmap_apbif);
return ret;
goto err_unset_ahub;
}
regcache_cache_only(ahub->regmap_apbif, true);
regs_ahub = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(regs_ahub))
return PTR_ERR(regs_ahub);
if (IS_ERR(regs_ahub)) {
ret = PTR_ERR(regs_ahub);
goto err_unset_ahub;
}
ahub->regmap_ahub = devm_regmap_init_mmio(&pdev->dev, regs_ahub,
&tegra30_ahub_ahub_regmap_config);
if (IS_ERR(ahub->regmap_ahub)) {
dev_err(&pdev->dev, "ahub regmap init failed\n");
ret = PTR_ERR(ahub->regmap_ahub);
return ret;
goto err_unset_ahub;
}
regcache_cache_only(ahub->regmap_ahub, true);
pm_runtime_enable(&pdev->dev);
if (!pm_runtime_enabled(&pdev->dev)) {
ret = tegra30_ahub_runtime_resume(&pdev->dev);
if (ret)
goto err_pm_disable;
}
of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
return 0;
err_pm_disable:
pm_runtime_disable(&pdev->dev);
err_unset_ahub:
ahub = NULL;
return ret;
}
static int tegra30_ahub_remove(struct platform_device *pdev)
{
if (!ahub)
return -ENODEV;
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
tegra30_ahub_runtime_suspend(&pdev->dev);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int tegra30_ahub_suspend(struct device *dev)
{
regcache_mark_dirty(ahub->regmap_ahub);
regcache_mark_dirty(ahub->regmap_apbif);
ahub = NULL;
return 0;
}
static int tegra30_ahub_resume(struct device *dev)
{
int ret;
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
pm_runtime_put(dev);
return ret;
}
ret = regcache_sync(ahub->regmap_ahub);
ret |= regcache_sync(ahub->regmap_apbif);
pm_runtime_put(dev);
return ret;
}
#endif
static const struct dev_pm_ops tegra30_ahub_pm_ops = {
SET_RUNTIME_PM_OPS(tegra30_ahub_runtime_suspend,
tegra30_ahub_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(tegra30_ahub_suspend, tegra30_ahub_resume)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static struct platform_driver tegra30_ahub_driver = {
......
......@@ -491,7 +491,7 @@ void tegra124_ahub_set_cif(struct regmap *regmap, unsigned int reg,
struct tegra30_ahub_cif_conf *conf);
struct tegra30_ahub_soc_data {
u32 mod_list_mask;
unsigned int num_resets;
void (*set_audio_cif)(struct regmap *regmap,
unsigned int reg,
struct tegra30_ahub_cif_conf *conf);
......@@ -511,7 +511,8 @@ struct tegra30_ahub_soc_data {
struct tegra30_ahub {
const struct tegra30_ahub_soc_data *soc_data;
struct device *dev;
struct reset_control *reset;
struct reset_control_bulk_data resets[21];
unsigned int nresets;
struct clk_bulk_data clocks[2];
unsigned int nclocks;
resource_size_t apbif_addr;
......
......@@ -23,6 +23,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
......@@ -58,8 +59,18 @@ static int tegra30_i2s_runtime_resume(struct device *dev)
}
regcache_cache_only(i2s->regmap, false);
regcache_mark_dirty(i2s->regmap);
ret = regcache_sync(i2s->regmap);
if (ret)
goto disable_clocks;
return 0;
disable_clocks:
clk_disable_unprepare(i2s->clk_i2s);
return ret;
}
static int tegra30_i2s_set_fmt(struct snd_soc_dai *dai,
......@@ -427,7 +438,7 @@ static int tegra30_i2s_platform_probe(struct platform_device *pdev)
i2s->playback_i2s_cif = cif_ids[0];
i2s->capture_i2s_cif = cif_ids[1];
i2s->clk_i2s = clk_get(&pdev->dev, NULL);
i2s->clk_i2s = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(i2s->clk_i2s)) {
dev_err(&pdev->dev, "Can't retrieve i2s clock\n");
ret = PTR_ERR(i2s->clk_i2s);
......@@ -437,7 +448,7 @@ static int tegra30_i2s_platform_probe(struct platform_device *pdev)
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs)) {
ret = PTR_ERR(regs);
goto err_clk_put;
goto err;
}
i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
......@@ -445,16 +456,11 @@ static int tegra30_i2s_platform_probe(struct platform_device *pdev)
if (IS_ERR(i2s->regmap)) {
dev_err(&pdev->dev, "regmap init failed\n");
ret = PTR_ERR(i2s->regmap);
goto err_clk_put;
goto err;
}
regcache_cache_only(i2s->regmap, true);
pm_runtime_enable(&pdev->dev);
if (!pm_runtime_enabled(&pdev->dev)) {
ret = tegra30_i2s_runtime_resume(&pdev->dev);
if (ret)
goto err_pm_disable;
}
i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
i2s->playback_dma_data.maxburst = 4;
......@@ -464,7 +470,7 @@ static int tegra30_i2s_platform_probe(struct platform_device *pdev)
&i2s->playback_dma_data.addr);
if (ret) {
dev_err(&pdev->dev, "Could not alloc TX FIFO: %d\n", ret);
goto err_suspend;
goto err_pm_disable;
}
ret = tegra30_ahub_set_rx_cif_source(i2s->playback_i2s_cif,
i2s->playback_fifo_cif);
......@@ -518,13 +524,8 @@ static int tegra30_i2s_platform_probe(struct platform_device *pdev)
tegra30_ahub_unset_rx_cif_source(i2s->playback_i2s_cif);
err_free_tx_fifo:
tegra30_ahub_free_tx_fifo(i2s->playback_fifo_cif);
err_suspend:
if (!pm_runtime_status_suspended(&pdev->dev))
tegra30_i2s_runtime_suspend(&pdev->dev);
err_pm_disable:
pm_runtime_disable(&pdev->dev);
err_clk_put:
clk_put(i2s->clk_i2s);
err:
return ret;
}
......@@ -533,10 +534,6 @@ static int tegra30_i2s_platform_remove(struct platform_device *pdev)
{
struct tegra30_i2s *i2s = dev_get_drvdata(&pdev->dev);
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
tegra30_i2s_runtime_suspend(&pdev->dev);
tegra_pcm_platform_unregister(&pdev->dev);
snd_soc_unregister_component(&pdev->dev);
......@@ -546,42 +543,16 @@ static int tegra30_i2s_platform_remove(struct platform_device *pdev)
tegra30_ahub_unset_rx_cif_source(i2s->playback_i2s_cif);
tegra30_ahub_free_tx_fifo(i2s->playback_fifo_cif);
clk_put(i2s->clk_i2s);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int tegra30_i2s_suspend(struct device *dev)
{
struct tegra30_i2s *i2s = dev_get_drvdata(dev);
regcache_mark_dirty(i2s->regmap);
pm_runtime_disable(&pdev->dev);
return 0;
}
static int tegra30_i2s_resume(struct device *dev)
{
struct tegra30_i2s *i2s = dev_get_drvdata(dev);
int ret;
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
pm_runtime_put(dev);
return ret;
}
ret = regcache_sync(i2s->regmap);
pm_runtime_put(dev);
return ret;
}
#endif
static const struct dev_pm_ops tegra30_i2s_pm_ops = {
SET_RUNTIME_PM_OPS(tegra30_i2s_runtime_suspend,
tegra30_i2s_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(tegra30_i2s_suspend, tegra30_i2s_resume)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static struct platform_driver tegra30_i2s_driver = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment