162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// tegra210_ope.c - Tegra210 OPE driver 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/clk.h> 862306a36Sopenharmony_ci#include <linux/device.h> 962306a36Sopenharmony_ci#include <linux/io.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/of.h> 1262306a36Sopenharmony_ci#include <linux/of_device.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1562306a36Sopenharmony_ci#include <linux/regmap.h> 1662306a36Sopenharmony_ci#include <sound/core.h> 1762306a36Sopenharmony_ci#include <sound/pcm.h> 1862306a36Sopenharmony_ci#include <sound/pcm_params.h> 1962306a36Sopenharmony_ci#include <sound/soc.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "tegra210_mbdrc.h" 2262306a36Sopenharmony_ci#include "tegra210_ope.h" 2362306a36Sopenharmony_ci#include "tegra210_peq.h" 2462306a36Sopenharmony_ci#include "tegra_cif.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic const struct reg_default tegra210_ope_reg_defaults[] = { 2762306a36Sopenharmony_ci { TEGRA210_OPE_RX_INT_MASK, 0x00000001}, 2862306a36Sopenharmony_ci { TEGRA210_OPE_RX_CIF_CTRL, 0x00007700}, 2962306a36Sopenharmony_ci { TEGRA210_OPE_TX_INT_MASK, 0x00000001}, 3062306a36Sopenharmony_ci { TEGRA210_OPE_TX_CIF_CTRL, 0x00007700}, 3162306a36Sopenharmony_ci { TEGRA210_OPE_CG, 0x1}, 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic int tegra210_ope_set_audio_cif(struct tegra210_ope *ope, 3562306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 3662306a36Sopenharmony_ci unsigned int reg) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci int channels, audio_bits; 3962306a36Sopenharmony_ci struct tegra_cif_conf cif_conf; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci channels = params_channels(params); 4462306a36Sopenharmony_ci if (channels < 2) 4562306a36Sopenharmony_ci return -EINVAL; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci switch (params_format(params)) { 4862306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 4962306a36Sopenharmony_ci audio_bits = TEGRA_ACIF_BITS_16; 5062306a36Sopenharmony_ci break; 5162306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 5262306a36Sopenharmony_ci audio_bits = TEGRA_ACIF_BITS_32; 5362306a36Sopenharmony_ci break; 5462306a36Sopenharmony_ci default: 5562306a36Sopenharmony_ci return -EINVAL; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci cif_conf.audio_ch = channels; 5962306a36Sopenharmony_ci cif_conf.client_ch = channels; 6062306a36Sopenharmony_ci cif_conf.audio_bits = audio_bits; 6162306a36Sopenharmony_ci cif_conf.client_bits = audio_bits; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci tegra_set_cif(ope->regmap, reg, &cif_conf); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return 0; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int tegra210_ope_hw_params(struct snd_pcm_substream *substream, 6962306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 7062306a36Sopenharmony_ci struct snd_soc_dai *dai) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct device *dev = dai->dev; 7362306a36Sopenharmony_ci struct tegra210_ope *ope = snd_soc_dai_get_drvdata(dai); 7462306a36Sopenharmony_ci int err; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* Set RX and TX CIF */ 7762306a36Sopenharmony_ci err = tegra210_ope_set_audio_cif(ope, params, 7862306a36Sopenharmony_ci TEGRA210_OPE_RX_CIF_CTRL); 7962306a36Sopenharmony_ci if (err) { 8062306a36Sopenharmony_ci dev_err(dev, "Can't set OPE RX CIF: %d\n", err); 8162306a36Sopenharmony_ci return err; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci err = tegra210_ope_set_audio_cif(ope, params, 8562306a36Sopenharmony_ci TEGRA210_OPE_TX_CIF_CTRL); 8662306a36Sopenharmony_ci if (err) { 8762306a36Sopenharmony_ci dev_err(dev, "Can't set OPE TX CIF: %d\n", err); 8862306a36Sopenharmony_ci return err; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci tegra210_mbdrc_hw_params(dai->component); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci return err; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic int tegra210_ope_component_probe(struct snd_soc_component *cmpnt) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct tegra210_ope *ope = dev_get_drvdata(cmpnt->dev); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci tegra210_peq_component_init(cmpnt); 10162306a36Sopenharmony_ci tegra210_mbdrc_component_init(cmpnt); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* 10462306a36Sopenharmony_ci * The OPE, PEQ and MBDRC functionalities are combined under one 10562306a36Sopenharmony_ci * device registered by OPE driver. In fact OPE HW block includes 10662306a36Sopenharmony_ci * sub blocks PEQ and MBDRC. However driver registers separate 10762306a36Sopenharmony_ci * regmap interfaces for each of these. ASoC core depends on 10862306a36Sopenharmony_ci * dev_get_regmap() to populate the regmap field for a given ASoC 10962306a36Sopenharmony_ci * component. A component can have one regmap reference and since 11062306a36Sopenharmony_ci * the DAPM routes depend on OPE regmap only, below explicit 11162306a36Sopenharmony_ci * assignment is done to highlight this. This is needed for ASoC 11262306a36Sopenharmony_ci * core to access correct regmap during DAPM path setup. 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci snd_soc_component_init_regmap(cmpnt, ope->regmap); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic const struct snd_soc_dai_ops tegra210_ope_dai_ops = { 12062306a36Sopenharmony_ci .hw_params = tegra210_ope_hw_params, 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic struct snd_soc_dai_driver tegra210_ope_dais[] = { 12462306a36Sopenharmony_ci { 12562306a36Sopenharmony_ci .name = "OPE-RX-CIF", 12662306a36Sopenharmony_ci .playback = { 12762306a36Sopenharmony_ci .stream_name = "RX-CIF-Playback", 12862306a36Sopenharmony_ci .channels_min = 1, 12962306a36Sopenharmony_ci .channels_max = 8, 13062306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000, 13162306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S8 | 13262306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | 13362306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 13462306a36Sopenharmony_ci }, 13562306a36Sopenharmony_ci .capture = { 13662306a36Sopenharmony_ci .stream_name = "RX-CIF-Capture", 13762306a36Sopenharmony_ci .channels_min = 1, 13862306a36Sopenharmony_ci .channels_max = 8, 13962306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000, 14062306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S8 | 14162306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | 14262306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 14362306a36Sopenharmony_ci }, 14462306a36Sopenharmony_ci }, 14562306a36Sopenharmony_ci { 14662306a36Sopenharmony_ci .name = "OPE-TX-CIF", 14762306a36Sopenharmony_ci .playback = { 14862306a36Sopenharmony_ci .stream_name = "TX-CIF-Playback", 14962306a36Sopenharmony_ci .channels_min = 1, 15062306a36Sopenharmony_ci .channels_max = 8, 15162306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000, 15262306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S8 | 15362306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | 15462306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 15562306a36Sopenharmony_ci }, 15662306a36Sopenharmony_ci .capture = { 15762306a36Sopenharmony_ci .stream_name = "TX-CIF-Capture", 15862306a36Sopenharmony_ci .channels_min = 1, 15962306a36Sopenharmony_ci .channels_max = 8, 16062306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000, 16162306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S8 | 16262306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | 16362306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 16462306a36Sopenharmony_ci }, 16562306a36Sopenharmony_ci .ops = &tegra210_ope_dai_ops, 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget tegra210_ope_widgets[] = { 17062306a36Sopenharmony_ci SND_SOC_DAPM_AIF_IN("RX", NULL, 0, SND_SOC_NOPM, 0, 0), 17162306a36Sopenharmony_ci SND_SOC_DAPM_AIF_OUT("TX", NULL, 0, TEGRA210_OPE_ENABLE, 17262306a36Sopenharmony_ci TEGRA210_OPE_EN_SHIFT, 0), 17362306a36Sopenharmony_ci}; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci#define OPE_ROUTES(sname) \ 17662306a36Sopenharmony_ci { "RX XBAR-" sname, NULL, "XBAR-TX" }, \ 17762306a36Sopenharmony_ci { "RX-CIF-" sname, NULL, "RX XBAR-" sname }, \ 17862306a36Sopenharmony_ci { "RX", NULL, "RX-CIF-" sname }, \ 17962306a36Sopenharmony_ci { "TX-CIF-" sname, NULL, "TX" }, \ 18062306a36Sopenharmony_ci { "TX XBAR-" sname, NULL, "TX-CIF-" sname }, \ 18162306a36Sopenharmony_ci { "XBAR-RX", NULL, "TX XBAR-" sname } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic const struct snd_soc_dapm_route tegra210_ope_routes[] = { 18462306a36Sopenharmony_ci { "TX", NULL, "RX" }, 18562306a36Sopenharmony_ci OPE_ROUTES("Playback"), 18662306a36Sopenharmony_ci OPE_ROUTES("Capture"), 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic const char * const tegra210_ope_data_dir_text[] = { 19062306a36Sopenharmony_ci "MBDRC to PEQ", 19162306a36Sopenharmony_ci "PEQ to MBDRC" 19262306a36Sopenharmony_ci}; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic const struct soc_enum tegra210_ope_data_dir_enum = 19562306a36Sopenharmony_ci SOC_ENUM_SINGLE(TEGRA210_OPE_DIR, TEGRA210_OPE_DIR_SHIFT, 19662306a36Sopenharmony_ci 2, tegra210_ope_data_dir_text); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int tegra210_ope_get_data_dir(struct snd_kcontrol *kcontrol, 19962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 20262306a36Sopenharmony_ci struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = ope->data_dir; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic int tegra210_ope_put_data_dir(struct snd_kcontrol *kcontrol, 21062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 21362306a36Sopenharmony_ci struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt); 21462306a36Sopenharmony_ci unsigned int value = ucontrol->value.enumerated.item[0]; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (value == ope->data_dir) 21762306a36Sopenharmony_ci return 0; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci ope->data_dir = value; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return 1; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic const struct snd_kcontrol_new tegra210_ope_controls[] = { 22562306a36Sopenharmony_ci SOC_ENUM_EXT("Data Flow Direction", tegra210_ope_data_dir_enum, 22662306a36Sopenharmony_ci tegra210_ope_get_data_dir, tegra210_ope_put_data_dir), 22762306a36Sopenharmony_ci}; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic const struct snd_soc_component_driver tegra210_ope_cmpnt = { 23062306a36Sopenharmony_ci .probe = tegra210_ope_component_probe, 23162306a36Sopenharmony_ci .dapm_widgets = tegra210_ope_widgets, 23262306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(tegra210_ope_widgets), 23362306a36Sopenharmony_ci .dapm_routes = tegra210_ope_routes, 23462306a36Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(tegra210_ope_routes), 23562306a36Sopenharmony_ci .controls = tegra210_ope_controls, 23662306a36Sopenharmony_ci .num_controls = ARRAY_SIZE(tegra210_ope_controls), 23762306a36Sopenharmony_ci}; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic bool tegra210_ope_wr_reg(struct device *dev, unsigned int reg) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci switch (reg) { 24262306a36Sopenharmony_ci case TEGRA210_OPE_RX_INT_MASK ... TEGRA210_OPE_RX_CIF_CTRL: 24362306a36Sopenharmony_ci case TEGRA210_OPE_TX_INT_MASK ... TEGRA210_OPE_TX_CIF_CTRL: 24462306a36Sopenharmony_ci case TEGRA210_OPE_ENABLE ... TEGRA210_OPE_CG: 24562306a36Sopenharmony_ci case TEGRA210_OPE_DIR: 24662306a36Sopenharmony_ci return true; 24762306a36Sopenharmony_ci default: 24862306a36Sopenharmony_ci return false; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic bool tegra210_ope_rd_reg(struct device *dev, unsigned int reg) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci if (tegra210_ope_wr_reg(dev, reg)) 25562306a36Sopenharmony_ci return true; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci switch (reg) { 25862306a36Sopenharmony_ci case TEGRA210_OPE_RX_STATUS: 25962306a36Sopenharmony_ci case TEGRA210_OPE_RX_INT_STATUS: 26062306a36Sopenharmony_ci case TEGRA210_OPE_TX_STATUS: 26162306a36Sopenharmony_ci case TEGRA210_OPE_TX_INT_STATUS: 26262306a36Sopenharmony_ci case TEGRA210_OPE_STATUS: 26362306a36Sopenharmony_ci case TEGRA210_OPE_INT_STATUS: 26462306a36Sopenharmony_ci return true; 26562306a36Sopenharmony_ci default: 26662306a36Sopenharmony_ci return false; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic bool tegra210_ope_volatile_reg(struct device *dev, unsigned int reg) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci switch (reg) { 27362306a36Sopenharmony_ci case TEGRA210_OPE_RX_STATUS: 27462306a36Sopenharmony_ci case TEGRA210_OPE_RX_INT_STATUS: 27562306a36Sopenharmony_ci case TEGRA210_OPE_TX_STATUS: 27662306a36Sopenharmony_ci case TEGRA210_OPE_TX_INT_STATUS: 27762306a36Sopenharmony_ci case TEGRA210_OPE_SOFT_RESET: 27862306a36Sopenharmony_ci case TEGRA210_OPE_STATUS: 27962306a36Sopenharmony_ci case TEGRA210_OPE_INT_STATUS: 28062306a36Sopenharmony_ci return true; 28162306a36Sopenharmony_ci default: 28262306a36Sopenharmony_ci return false; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic const struct regmap_config tegra210_ope_regmap_config = { 28762306a36Sopenharmony_ci .reg_bits = 32, 28862306a36Sopenharmony_ci .reg_stride = 4, 28962306a36Sopenharmony_ci .val_bits = 32, 29062306a36Sopenharmony_ci .max_register = TEGRA210_OPE_DIR, 29162306a36Sopenharmony_ci .writeable_reg = tegra210_ope_wr_reg, 29262306a36Sopenharmony_ci .readable_reg = tegra210_ope_rd_reg, 29362306a36Sopenharmony_ci .volatile_reg = tegra210_ope_volatile_reg, 29462306a36Sopenharmony_ci .reg_defaults = tegra210_ope_reg_defaults, 29562306a36Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(tegra210_ope_reg_defaults), 29662306a36Sopenharmony_ci .cache_type = REGCACHE_FLAT, 29762306a36Sopenharmony_ci}; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int tegra210_ope_probe(struct platform_device *pdev) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 30262306a36Sopenharmony_ci struct tegra210_ope *ope; 30362306a36Sopenharmony_ci void __iomem *regs; 30462306a36Sopenharmony_ci int err; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci ope = devm_kzalloc(dev, sizeof(*ope), GFP_KERNEL); 30762306a36Sopenharmony_ci if (!ope) 30862306a36Sopenharmony_ci return -ENOMEM; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci regs = devm_platform_ioremap_resource(pdev, 0); 31162306a36Sopenharmony_ci if (IS_ERR(regs)) 31262306a36Sopenharmony_ci return PTR_ERR(regs); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci ope->regmap = devm_regmap_init_mmio(dev, regs, 31562306a36Sopenharmony_ci &tegra210_ope_regmap_config); 31662306a36Sopenharmony_ci if (IS_ERR(ope->regmap)) { 31762306a36Sopenharmony_ci dev_err(dev, "regmap init failed\n"); 31862306a36Sopenharmony_ci return PTR_ERR(ope->regmap); 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci regcache_cache_only(ope->regmap, true); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci dev_set_drvdata(dev, ope); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci err = tegra210_peq_regmap_init(pdev); 32662306a36Sopenharmony_ci if (err < 0) { 32762306a36Sopenharmony_ci dev_err(dev, "PEQ init failed\n"); 32862306a36Sopenharmony_ci return err; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci err = tegra210_mbdrc_regmap_init(pdev); 33262306a36Sopenharmony_ci if (err < 0) { 33362306a36Sopenharmony_ci dev_err(dev, "MBDRC init failed\n"); 33462306a36Sopenharmony_ci return err; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci err = devm_snd_soc_register_component(dev, &tegra210_ope_cmpnt, 33862306a36Sopenharmony_ci tegra210_ope_dais, 33962306a36Sopenharmony_ci ARRAY_SIZE(tegra210_ope_dais)); 34062306a36Sopenharmony_ci if (err) { 34162306a36Sopenharmony_ci dev_err(dev, "can't register OPE component, err: %d\n", err); 34262306a36Sopenharmony_ci return err; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci pm_runtime_enable(dev); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return 0; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic void tegra210_ope_remove(struct platform_device *pdev) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic int __maybe_unused tegra210_ope_runtime_suspend(struct device *dev) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct tegra210_ope *ope = dev_get_drvdata(dev); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci tegra210_peq_save(ope->peq_regmap, ope->peq_biquad_gains, 36062306a36Sopenharmony_ci ope->peq_biquad_shifts); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci regcache_cache_only(ope->mbdrc_regmap, true); 36362306a36Sopenharmony_ci regcache_cache_only(ope->peq_regmap, true); 36462306a36Sopenharmony_ci regcache_cache_only(ope->regmap, true); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci regcache_mark_dirty(ope->regmap); 36762306a36Sopenharmony_ci regcache_mark_dirty(ope->peq_regmap); 36862306a36Sopenharmony_ci regcache_mark_dirty(ope->mbdrc_regmap); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci return 0; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic int __maybe_unused tegra210_ope_runtime_resume(struct device *dev) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct tegra210_ope *ope = dev_get_drvdata(dev); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci regcache_cache_only(ope->regmap, false); 37862306a36Sopenharmony_ci regcache_cache_only(ope->peq_regmap, false); 37962306a36Sopenharmony_ci regcache_cache_only(ope->mbdrc_regmap, false); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci regcache_sync(ope->regmap); 38262306a36Sopenharmony_ci regcache_sync(ope->peq_regmap); 38362306a36Sopenharmony_ci regcache_sync(ope->mbdrc_regmap); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci tegra210_peq_restore(ope->peq_regmap, ope->peq_biquad_gains, 38662306a36Sopenharmony_ci ope->peq_biquad_shifts); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return 0; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic const struct dev_pm_ops tegra210_ope_pm_ops = { 39262306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(tegra210_ope_runtime_suspend, 39362306a36Sopenharmony_ci tegra210_ope_runtime_resume, NULL) 39462306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 39562306a36Sopenharmony_ci pm_runtime_force_resume) 39662306a36Sopenharmony_ci}; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic const struct of_device_id tegra210_ope_of_match[] = { 39962306a36Sopenharmony_ci { .compatible = "nvidia,tegra210-ope" }, 40062306a36Sopenharmony_ci {}, 40162306a36Sopenharmony_ci}; 40262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra210_ope_of_match); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic struct platform_driver tegra210_ope_driver = { 40562306a36Sopenharmony_ci .driver = { 40662306a36Sopenharmony_ci .name = "tegra210-ope", 40762306a36Sopenharmony_ci .of_match_table = tegra210_ope_of_match, 40862306a36Sopenharmony_ci .pm = &tegra210_ope_pm_ops, 40962306a36Sopenharmony_ci }, 41062306a36Sopenharmony_ci .probe = tegra210_ope_probe, 41162306a36Sopenharmony_ci .remove_new = tegra210_ope_remove, 41262306a36Sopenharmony_ci}; 41362306a36Sopenharmony_cimodule_platform_driver(tegra210_ope_driver) 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ciMODULE_AUTHOR("Sumit Bhattacharya <sumitb@nvidia.com>"); 41662306a36Sopenharmony_ciMODULE_DESCRIPTION("Tegra210 OPE ASoC driver"); 41762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 418