diff options
Diffstat (limited to 'kernel/sound/soc/qcom/lpass-cpu.c')
-rw-r--r-- | kernel/sound/soc/qcom/lpass-cpu.c | 241 |
1 files changed, 128 insertions, 113 deletions
diff --git a/kernel/sound/soc/qcom/lpass-cpu.c b/kernel/sound/soc/qcom/lpass-cpu.c index dc790abaa..e5101e0d2 100644 --- a/kernel/sound/soc/qcom/lpass-cpu.c +++ b/kernel/sound/soc/qcom/lpass-cpu.c @@ -14,21 +14,17 @@ */ #include <linux/clk.h> -#include <linux/compiler.h> -#include <linux/device.h> -#include <linux/err.h> -#include <linux/ioport.h> #include <linux/kernel.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <linux/regmap.h> #include <sound/soc.h> #include <sound/soc-dai.h> -#include "lpass-lpaif-ipq806x.h" +#include "lpass-lpaif-reg.h" #include "lpass.h" static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id, @@ -37,7 +33,10 @@ static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id, struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); int ret; - ret = clk_set_rate(drvdata->mi2s_osr_clk, freq); + if (IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) + return 0; + + ret = clk_set_rate(drvdata->mi2s_osr_clk[dai->driver->id], freq); if (ret) dev_err(dai->dev, "%s() error setting mi2s osrclk to %u: %d\n", __func__, freq, ret); @@ -51,18 +50,23 @@ static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream, struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); int ret; - ret = clk_prepare_enable(drvdata->mi2s_osr_clk); - if (ret) { - dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n", - __func__, ret); - return ret; + if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) { + ret = clk_prepare_enable( + drvdata->mi2s_osr_clk[dai->driver->id]); + if (ret) { + dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n", + __func__, ret); + return ret; + } } - ret = clk_prepare_enable(drvdata->mi2s_bit_clk); + ret = clk_prepare_enable(drvdata->mi2s_bit_clk[dai->driver->id]); if (ret) { dev_err(dai->dev, "%s() error in enabling mi2s bit clk: %d\n", __func__, ret); - clk_disable_unprepare(drvdata->mi2s_osr_clk); + if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) + clk_disable_unprepare( + drvdata->mi2s_osr_clk[dai->driver->id]); return ret; } @@ -74,8 +78,10 @@ static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream, { struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); - clk_disable_unprepare(drvdata->mi2s_bit_clk); - clk_disable_unprepare(drvdata->mi2s_osr_clk); + clk_disable_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]); + + if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) + clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]); } static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, @@ -142,14 +148,16 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, } ret = regmap_write(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), regval); + LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), + regval); if (ret) { dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", __func__, ret); return ret; } - ret = clk_set_rate(drvdata->mi2s_bit_clk, rate * bitwidth * 2); + ret = clk_set_rate(drvdata->mi2s_bit_clk[dai->driver->id], + rate * bitwidth * 2); if (ret) { dev_err(dai->dev, "%s() error setting mi2s bitclk to %u: %d\n", __func__, rate * bitwidth * 2, ret); @@ -166,7 +174,8 @@ static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream, int ret; ret = regmap_write(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0); + LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), + 0); if (ret) dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", __func__, ret); @@ -181,7 +190,7 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream, int ret; ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), + LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE); if (ret) dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", @@ -201,7 +210,8 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), + LPAIF_I2SCTL_REG(drvdata->variant, + dai->driver->id), LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE); if (ret) @@ -212,7 +222,8 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), + LPAIF_I2SCTL_REG(drvdata->variant, + dai->driver->id), LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_DISABLE); if (ret) @@ -224,7 +235,7 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_dai_ops lpass_cpu_dai_ops = { +const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = { .set_sysclk = lpass_cpu_daiops_set_sysclk, .startup = lpass_cpu_daiops_startup, .shutdown = lpass_cpu_daiops_shutdown, @@ -233,41 +244,23 @@ static struct snd_soc_dai_ops lpass_cpu_dai_ops = { .prepare = lpass_cpu_daiops_prepare, .trigger = lpass_cpu_daiops_trigger, }; +EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops); -static int lpass_cpu_dai_probe(struct snd_soc_dai *dai) +int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai) { struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); int ret; /* ensure audio hardware is disabled */ ret = regmap_write(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0); + LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), 0); if (ret) dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", __func__, ret); return ret; } - -static struct snd_soc_dai_driver lpass_cpu_dai_driver = { - .playback = { - .stream_name = "lpass-cpu-playback", - .formats = SNDRV_PCM_FMTBIT_S16 | - SNDRV_PCM_FMTBIT_S24 | - SNDRV_PCM_FMTBIT_S32, - .rates = SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000 | - SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_96000, - .rate_min = 8000, - .rate_max = 96000, - .channels_min = 1, - .channels_max = 8, - }, - .probe = &lpass_cpu_dai_probe, - .ops = &lpass_cpu_dai_ops, -}; +EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_probe); static const struct snd_soc_component_driver lpass_cpu_comp_driver = { .name = "lpass-cpu", @@ -275,27 +268,29 @@ static const struct snd_soc_component_driver lpass_cpu_comp_driver = { static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg) { + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; int i; - for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i) - if (reg == LPAIF_I2SCTL_REG(i)) + for (i = 0; i < v->i2s_ports; ++i) + if (reg == LPAIF_I2SCTL_REG(v, i)) return true; - for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) { - if (reg == LPAIF_IRQEN_REG(i)) + for (i = 0; i < v->irq_ports; ++i) { + if (reg == LPAIF_IRQEN_REG(v, i)) return true; - if (reg == LPAIF_IRQCLEAR_REG(i)) + if (reg == LPAIF_IRQCLEAR_REG(v, i)) return true; } - for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) { - if (reg == LPAIF_RDMACTL_REG(i)) + for (i = 0; i < v->rdma_channels; ++i) { + if (reg == LPAIF_RDMACTL_REG(v, i)) return true; - if (reg == LPAIF_RDMABASE_REG(i)) + if (reg == LPAIF_RDMABASE_REG(v, i)) return true; - if (reg == LPAIF_RDMABUFF_REG(i)) + if (reg == LPAIF_RDMABUFF_REG(v, i)) return true; - if (reg == LPAIF_RDMAPER_REG(i)) + if (reg == LPAIF_RDMAPER_REG(v, i)) return true; } @@ -304,29 +299,31 @@ static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg) static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg) { + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; int i; - for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i) - if (reg == LPAIF_I2SCTL_REG(i)) + for (i = 0; i < v->i2s_ports; ++i) + if (reg == LPAIF_I2SCTL_REG(v, i)) return true; - for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) { - if (reg == LPAIF_IRQEN_REG(i)) + for (i = 0; i < v->irq_ports; ++i) { + if (reg == LPAIF_IRQEN_REG(v, i)) return true; - if (reg == LPAIF_IRQSTAT_REG(i)) + if (reg == LPAIF_IRQSTAT_REG(v, i)) return true; } - for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) { - if (reg == LPAIF_RDMACTL_REG(i)) + for (i = 0; i < v->rdma_channels; ++i) { + if (reg == LPAIF_RDMACTL_REG(v, i)) return true; - if (reg == LPAIF_RDMABASE_REG(i)) + if (reg == LPAIF_RDMABASE_REG(v, i)) return true; - if (reg == LPAIF_RDMABUFF_REG(i)) + if (reg == LPAIF_RDMABUFF_REG(v, i)) return true; - if (reg == LPAIF_RDMACURR_REG(i)) + if (reg == LPAIF_RDMACURR_REG(v, i)) return true; - if (reg == LPAIF_RDMAPER_REG(i)) + if (reg == LPAIF_RDMAPER_REG(v, i)) return true; } @@ -335,36 +332,41 @@ static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg) static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg) { + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; int i; - for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) - if (reg == LPAIF_IRQSTAT_REG(i)) + for (i = 0; i < v->irq_ports; ++i) + if (reg == LPAIF_IRQSTAT_REG(v, i)) return true; - for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) - if (reg == LPAIF_RDMACURR_REG(i)) + for (i = 0; i < v->rdma_channels; ++i) + if (reg == LPAIF_RDMACURR_REG(v, i)) return true; return false; } -static const struct regmap_config lpass_cpu_regmap_config = { +static struct regmap_config lpass_cpu_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .max_register = LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MAX), .writeable_reg = lpass_cpu_regmap_writeable, .readable_reg = lpass_cpu_regmap_readable, .volatile_reg = lpass_cpu_regmap_volatile, .cache_type = REGCACHE_FLAT, }; -static int lpass_cpu_platform_probe(struct platform_device *pdev) +int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) { struct lpass_data *drvdata; struct device_node *dsp_of_node; struct resource *res; - int ret; + struct lpass_variant *variant; + struct device *dev = &pdev->dev; + const struct of_device_id *match; + char clk_name[16]; + int ret, i, dai_id; dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0); if (dsp_of_node) { @@ -379,11 +381,14 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, drvdata); + match = of_match_device(dev->driver->of_match_table, dev); + if (!match || !match->data) + return -EINVAL; + + drvdata->variant = (struct lpass_variant *)match->data; + variant = drvdata->variant; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif"); - if (!res) { - dev_err(&pdev->dev, "%s() error getting resource\n", __func__); - return -ENODEV; - } drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR((void const __force *)drvdata->lpaif)) { @@ -393,6 +398,9 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev) return PTR_ERR((void const __force *)drvdata->lpaif); } + lpass_cpu_regmap_config.max_register = LPAIF_RDMAPER_REG(variant, + variant->rdma_channels); + drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif, &lpass_cpu_regmap_config); if (IS_ERR(drvdata->lpaif_map)) { @@ -401,18 +409,39 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev) return PTR_ERR(drvdata->lpaif_map); } - drvdata->mi2s_osr_clk = devm_clk_get(&pdev->dev, "mi2s-osr-clk"); - if (IS_ERR(drvdata->mi2s_osr_clk)) { - dev_err(&pdev->dev, "%s() error getting mi2s-osr-clk: %ld\n", - __func__, PTR_ERR(drvdata->mi2s_osr_clk)); - return PTR_ERR(drvdata->mi2s_osr_clk); - } - - drvdata->mi2s_bit_clk = devm_clk_get(&pdev->dev, "mi2s-bit-clk"); - if (IS_ERR(drvdata->mi2s_bit_clk)) { - dev_err(&pdev->dev, "%s() error getting mi2s-bit-clk: %ld\n", - __func__, PTR_ERR(drvdata->mi2s_bit_clk)); - return PTR_ERR(drvdata->mi2s_bit_clk); + if (variant->init) + variant->init(pdev); + + for (i = 0; i < variant->num_dai; i++) { + dai_id = variant->dai_driver[i].id; + if (variant->num_dai > 1) + sprintf(clk_name, "mi2s-osr-clk%d", i); + else + sprintf(clk_name, "mi2s-osr-clk"); + + drvdata->mi2s_osr_clk[dai_id] = devm_clk_get(&pdev->dev, + clk_name); + if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) { + dev_warn(&pdev->dev, + "%s() error getting mi2s-osr-clk: %ld\n", + __func__, + PTR_ERR(drvdata->mi2s_osr_clk[dai_id])); + } + + if (variant->num_dai > 1) + sprintf(clk_name, "mi2s-bit-clk%d", i); + else + sprintf(clk_name, "mi2s-bit-clk"); + + drvdata->mi2s_bit_clk[dai_id] = devm_clk_get(&pdev->dev, + clk_name); + if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) { + dev_err(&pdev->dev, + "%s() error getting mi2s-bit-clk: %ld\n", + __func__, + PTR_ERR(drvdata->mi2s_bit_clk[dai_id])); + return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]); + } } drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk"); @@ -439,7 +468,9 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev) } ret = devm_snd_soc_register_component(&pdev->dev, - &lpass_cpu_comp_driver, &lpass_cpu_dai_driver, 1); + &lpass_cpu_comp_driver, + variant->dai_driver, + variant->num_dai); if (ret) { dev_err(&pdev->dev, "%s() error registering cpu driver: %d\n", __func__, ret); @@ -459,33 +490,17 @@ err_clk: clk_disable_unprepare(drvdata->ahbix_clk); return ret; } +EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_probe); -static int lpass_cpu_platform_remove(struct platform_device *pdev) +int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev) { struct lpass_data *drvdata = platform_get_drvdata(pdev); + if (drvdata->variant->exit) + drvdata->variant->exit(pdev); + clk_disable_unprepare(drvdata->ahbix_clk); return 0; } - -#ifdef CONFIG_OF -static const struct of_device_id lpass_cpu_device_id[] = { - { .compatible = "qcom,lpass-cpu" }, - {} -}; -MODULE_DEVICE_TABLE(of, lpass_cpu_device_id); -#endif - -static struct platform_driver lpass_cpu_platform_driver = { - .driver = { - .name = "lpass-cpu", - .of_match_table = of_match_ptr(lpass_cpu_device_id), - }, - .probe = lpass_cpu_platform_probe, - .remove = lpass_cpu_platform_remove, -}; -module_platform_driver(lpass_cpu_platform_driver); - -MODULE_DESCRIPTION("QTi LPASS CPU Driver"); -MODULE_LICENSE("GPL v2"); +EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_remove); |