175a6faf6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
239503622SDamien.Horsley /*
339503622SDamien.Horsley * Pistachio internal dac driver
439503622SDamien.Horsley *
539503622SDamien.Horsley * Copyright (C) 2015 Imagination Technologies Ltd.
639503622SDamien.Horsley *
739503622SDamien.Horsley * Author: Damien Horsley <Damien.Horsley@imgtec.com>
839503622SDamien.Horsley */
939503622SDamien.Horsley
1039503622SDamien.Horsley #include <linux/clk.h>
1139503622SDamien.Horsley #include <linux/delay.h>
1239503622SDamien.Horsley #include <linux/mfd/syscon.h>
1339503622SDamien.Horsley #include <linux/module.h>
1439503622SDamien.Horsley #include <linux/pm_runtime.h>
1539503622SDamien.Horsley #include <linux/regmap.h>
1639503622SDamien.Horsley #include <linux/regulator/consumer.h>
1739503622SDamien.Horsley
1839503622SDamien.Horsley #include <sound/pcm_params.h>
1939503622SDamien.Horsley #include <sound/soc.h>
2039503622SDamien.Horsley
2139503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_CTRL 0x40
2239503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_CTRL_PWR_SEL_MASK 0x2
2339503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK 0x1
2439503622SDamien.Horsley
2539503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_SRST 0x44
2639503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_SRST_MASK 0x1
2739503622SDamien.Horsley
2839503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_GTI_CTRL 0x48
2939503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_SHIFT 0
3039503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_MASK 0xFFF
3139503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK 0x1000
3239503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_SHIFT 13
3339503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_MASK 0x1FE000
3439503622SDamien.Horsley
3539503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_PWR 0x1
3639503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_PWR_MASK 0x1
3739503622SDamien.Horsley
3839503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \
3939503622SDamien.Horsley SNDRV_PCM_FMTBIT_S32_LE)
4039503622SDamien.Horsley
4139503622SDamien.Horsley /* codec private data */
4239503622SDamien.Horsley struct pistachio_internal_dac {
4339503622SDamien.Horsley struct regmap *regmap;
4439503622SDamien.Horsley struct regulator *supply;
4539503622SDamien.Horsley bool mute;
4639503622SDamien.Horsley };
4739503622SDamien.Horsley
4839503622SDamien.Horsley static const struct snd_kcontrol_new pistachio_internal_dac_snd_controls[] = {
4939503622SDamien.Horsley SOC_SINGLE("Playback Switch", PISTACHIO_INTERNAL_DAC_CTRL, 2, 1, 1)
5039503622SDamien.Horsley };
5139503622SDamien.Horsley
5239503622SDamien.Horsley static const struct snd_soc_dapm_widget pistachio_internal_dac_widgets[] = {
5339503622SDamien.Horsley SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
5439503622SDamien.Horsley SND_SOC_DAPM_OUTPUT("AOUTL"),
5539503622SDamien.Horsley SND_SOC_DAPM_OUTPUT("AOUTR"),
5639503622SDamien.Horsley };
5739503622SDamien.Horsley
5839503622SDamien.Horsley static const struct snd_soc_dapm_route pistachio_internal_dac_routes[] = {
5939503622SDamien.Horsley { "AOUTL", NULL, "DAC" },
6039503622SDamien.Horsley { "AOUTR", NULL, "DAC" },
6139503622SDamien.Horsley };
6239503622SDamien.Horsley
pistachio_internal_dac_reg_writel(struct regmap * top_regs,u32 val,u32 reg)6339503622SDamien.Horsley static void pistachio_internal_dac_reg_writel(struct regmap *top_regs,
6439503622SDamien.Horsley u32 val, u32 reg)
6539503622SDamien.Horsley {
6639503622SDamien.Horsley regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL,
6739503622SDamien.Horsley PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_MASK,
6839503622SDamien.Horsley reg << PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_SHIFT);
6939503622SDamien.Horsley
7039503622SDamien.Horsley regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL,
7139503622SDamien.Horsley PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_MASK,
7239503622SDamien.Horsley val << PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_SHIFT);
7339503622SDamien.Horsley
7439503622SDamien.Horsley regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL,
7539503622SDamien.Horsley PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK,
7639503622SDamien.Horsley PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK);
7739503622SDamien.Horsley
7839503622SDamien.Horsley regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL,
7939503622SDamien.Horsley PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK, 0);
8039503622SDamien.Horsley }
8139503622SDamien.Horsley
pistachio_internal_dac_pwr_off(struct pistachio_internal_dac * dac)8239503622SDamien.Horsley static void pistachio_internal_dac_pwr_off(struct pistachio_internal_dac *dac)
8339503622SDamien.Horsley {
8439503622SDamien.Horsley regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_CTRL,
8539503622SDamien.Horsley PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK,
8639503622SDamien.Horsley PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK);
8739503622SDamien.Horsley
8839503622SDamien.Horsley pistachio_internal_dac_reg_writel(dac->regmap, 0,
8939503622SDamien.Horsley PISTACHIO_INTERNAL_DAC_PWR);
9039503622SDamien.Horsley }
9139503622SDamien.Horsley
pistachio_internal_dac_pwr_on(struct pistachio_internal_dac * dac)9239503622SDamien.Horsley static void pistachio_internal_dac_pwr_on(struct pistachio_internal_dac *dac)
9339503622SDamien.Horsley {
9439503622SDamien.Horsley regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_SRST,
9539503622SDamien.Horsley PISTACHIO_INTERNAL_DAC_SRST_MASK,
9639503622SDamien.Horsley PISTACHIO_INTERNAL_DAC_SRST_MASK);
9739503622SDamien.Horsley
9839503622SDamien.Horsley regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_SRST,
9939503622SDamien.Horsley PISTACHIO_INTERNAL_DAC_SRST_MASK, 0);
10039503622SDamien.Horsley
10139503622SDamien.Horsley pistachio_internal_dac_reg_writel(dac->regmap,
10239503622SDamien.Horsley PISTACHIO_INTERNAL_DAC_PWR_MASK,
10339503622SDamien.Horsley PISTACHIO_INTERNAL_DAC_PWR);
10439503622SDamien.Horsley
10539503622SDamien.Horsley regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_CTRL,
10639503622SDamien.Horsley PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK, 0);
10739503622SDamien.Horsley }
10839503622SDamien.Horsley
10939503622SDamien.Horsley static struct snd_soc_dai_driver pistachio_internal_dac_dais[] = {
11039503622SDamien.Horsley {
11139503622SDamien.Horsley .name = "pistachio_internal_dac",
11239503622SDamien.Horsley .playback = {
11339503622SDamien.Horsley .stream_name = "Playback",
11439503622SDamien.Horsley .channels_min = 2,
11539503622SDamien.Horsley .channels_max = 2,
11639503622SDamien.Horsley .rates = SNDRV_PCM_RATE_8000_48000,
11739503622SDamien.Horsley .formats = PISTACHIO_INTERNAL_DAC_FORMATS,
11839503622SDamien.Horsley }
11939503622SDamien.Horsley },
12039503622SDamien.Horsley };
12139503622SDamien.Horsley
pistachio_internal_dac_codec_probe(struct snd_soc_component * component)1228dc906d3SKuninori Morimoto static int pistachio_internal_dac_codec_probe(struct snd_soc_component *component)
12339503622SDamien.Horsley {
1248dc906d3SKuninori Morimoto struct pistachio_internal_dac *dac = snd_soc_component_get_drvdata(component);
12539503622SDamien.Horsley
1268dc906d3SKuninori Morimoto snd_soc_component_init_regmap(component, dac->regmap);
12739503622SDamien.Horsley
12839503622SDamien.Horsley return 0;
12939503622SDamien.Horsley }
13039503622SDamien.Horsley
1318dc906d3SKuninori Morimoto static const struct snd_soc_component_driver pistachio_internal_dac_driver = {
13239503622SDamien.Horsley .probe = pistachio_internal_dac_codec_probe,
13339503622SDamien.Horsley .controls = pistachio_internal_dac_snd_controls,
13439503622SDamien.Horsley .num_controls = ARRAY_SIZE(pistachio_internal_dac_snd_controls),
13539503622SDamien.Horsley .dapm_widgets = pistachio_internal_dac_widgets,
13639503622SDamien.Horsley .num_dapm_widgets = ARRAY_SIZE(pistachio_internal_dac_widgets),
13739503622SDamien.Horsley .dapm_routes = pistachio_internal_dac_routes,
13839503622SDamien.Horsley .num_dapm_routes = ARRAY_SIZE(pistachio_internal_dac_routes),
1398dc906d3SKuninori Morimoto .use_pmdown_time = 1,
1408dc906d3SKuninori Morimoto .endianness = 1,
14139503622SDamien.Horsley };
14239503622SDamien.Horsley
pistachio_internal_dac_probe(struct platform_device * pdev)14339503622SDamien.Horsley static int pistachio_internal_dac_probe(struct platform_device *pdev)
14439503622SDamien.Horsley {
14539503622SDamien.Horsley struct pistachio_internal_dac *dac;
14639503622SDamien.Horsley int ret, voltage;
14739503622SDamien.Horsley struct device *dev = &pdev->dev;
14839503622SDamien.Horsley u32 reg;
14939503622SDamien.Horsley
15039503622SDamien.Horsley dac = devm_kzalloc(dev, sizeof(*dac), GFP_KERNEL);
15139503622SDamien.Horsley
15239503622SDamien.Horsley if (!dac)
15339503622SDamien.Horsley return -ENOMEM;
15439503622SDamien.Horsley
15539503622SDamien.Horsley platform_set_drvdata(pdev, dac);
15639503622SDamien.Horsley
15739503622SDamien.Horsley dac->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
15839503622SDamien.Horsley "img,cr-top");
15939503622SDamien.Horsley if (IS_ERR(dac->regmap))
16039503622SDamien.Horsley return PTR_ERR(dac->regmap);
16139503622SDamien.Horsley
16239503622SDamien.Horsley dac->supply = devm_regulator_get(dev, "VDD");
163ef12f373SKuninori Morimoto if (IS_ERR(dac->supply))
164ef12f373SKuninori Morimoto return dev_err_probe(dev, PTR_ERR(dac->supply),
165ef12f373SKuninori Morimoto "failed to acquire supply 'VDD-supply'\n");
16639503622SDamien.Horsley
16739503622SDamien.Horsley ret = regulator_enable(dac->supply);
16839503622SDamien.Horsley if (ret) {
16939503622SDamien.Horsley dev_err(dev, "failed to enable supply: %d\n", ret);
17039503622SDamien.Horsley return ret;
17139503622SDamien.Horsley }
17239503622SDamien.Horsley
17339503622SDamien.Horsley voltage = regulator_get_voltage(dac->supply);
17439503622SDamien.Horsley
17539503622SDamien.Horsley switch (voltage) {
17639503622SDamien.Horsley case 1800000:
17739503622SDamien.Horsley reg = 0;
17839503622SDamien.Horsley break;
17939503622SDamien.Horsley case 3300000:
18039503622SDamien.Horsley reg = PISTACHIO_INTERNAL_DAC_CTRL_PWR_SEL_MASK;
18139503622SDamien.Horsley break;
18239503622SDamien.Horsley default:
18339503622SDamien.Horsley dev_err(dev, "invalid voltage: %d\n", voltage);
18439503622SDamien.Horsley ret = -EINVAL;
18539503622SDamien.Horsley goto err_regulator;
18639503622SDamien.Horsley }
18739503622SDamien.Horsley
18839503622SDamien.Horsley regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_CTRL,
18939503622SDamien.Horsley PISTACHIO_INTERNAL_DAC_CTRL_PWR_SEL_MASK, reg);
19039503622SDamien.Horsley
19139503622SDamien.Horsley pistachio_internal_dac_pwr_off(dac);
19239503622SDamien.Horsley pistachio_internal_dac_pwr_on(dac);
19339503622SDamien.Horsley
19439503622SDamien.Horsley pm_runtime_set_active(dev);
19539503622SDamien.Horsley pm_runtime_enable(dev);
19639503622SDamien.Horsley pm_runtime_idle(dev);
19739503622SDamien.Horsley
1988dc906d3SKuninori Morimoto ret = devm_snd_soc_register_component(dev,
1998dc906d3SKuninori Morimoto &pistachio_internal_dac_driver,
20039503622SDamien.Horsley pistachio_internal_dac_dais,
20139503622SDamien.Horsley ARRAY_SIZE(pistachio_internal_dac_dais));
20239503622SDamien.Horsley if (ret) {
2038dc906d3SKuninori Morimoto dev_err(dev, "failed to register component: %d\n", ret);
20439503622SDamien.Horsley goto err_pwr;
20539503622SDamien.Horsley }
20639503622SDamien.Horsley
20739503622SDamien.Horsley return 0;
20839503622SDamien.Horsley
20939503622SDamien.Horsley err_pwr:
21039503622SDamien.Horsley pm_runtime_disable(&pdev->dev);
21139503622SDamien.Horsley pistachio_internal_dac_pwr_off(dac);
21239503622SDamien.Horsley err_regulator:
21339503622SDamien.Horsley regulator_disable(dac->supply);
21439503622SDamien.Horsley
21539503622SDamien.Horsley return ret;
21639503622SDamien.Horsley }
21739503622SDamien.Horsley
pistachio_internal_dac_remove(struct platform_device * pdev)218ab443489SUwe Kleine-König static void pistachio_internal_dac_remove(struct platform_device *pdev)
21939503622SDamien.Horsley {
22039503622SDamien.Horsley struct pistachio_internal_dac *dac = dev_get_drvdata(&pdev->dev);
22139503622SDamien.Horsley
22239503622SDamien.Horsley pm_runtime_disable(&pdev->dev);
22339503622SDamien.Horsley pistachio_internal_dac_pwr_off(dac);
22439503622SDamien.Horsley regulator_disable(dac->supply);
22539503622SDamien.Horsley }
22639503622SDamien.Horsley
22739503622SDamien.Horsley #ifdef CONFIG_PM
pistachio_internal_dac_rt_resume(struct device * dev)22839503622SDamien.Horsley static int pistachio_internal_dac_rt_resume(struct device *dev)
22939503622SDamien.Horsley {
23039503622SDamien.Horsley struct pistachio_internal_dac *dac = dev_get_drvdata(dev);
23139503622SDamien.Horsley int ret;
23239503622SDamien.Horsley
23339503622SDamien.Horsley ret = regulator_enable(dac->supply);
23439503622SDamien.Horsley if (ret) {
23539503622SDamien.Horsley dev_err(dev, "failed to enable supply: %d\n", ret);
23639503622SDamien.Horsley return ret;
23739503622SDamien.Horsley }
23839503622SDamien.Horsley
23939503622SDamien.Horsley pistachio_internal_dac_pwr_on(dac);
24039503622SDamien.Horsley
24139503622SDamien.Horsley return 0;
24239503622SDamien.Horsley }
24339503622SDamien.Horsley
pistachio_internal_dac_rt_suspend(struct device * dev)24439503622SDamien.Horsley static int pistachio_internal_dac_rt_suspend(struct device *dev)
24539503622SDamien.Horsley {
24639503622SDamien.Horsley struct pistachio_internal_dac *dac = dev_get_drvdata(dev);
24739503622SDamien.Horsley
24839503622SDamien.Horsley pistachio_internal_dac_pwr_off(dac);
24939503622SDamien.Horsley
25039503622SDamien.Horsley regulator_disable(dac->supply);
25139503622SDamien.Horsley
25239503622SDamien.Horsley return 0;
25339503622SDamien.Horsley }
25439503622SDamien.Horsley #endif
25539503622SDamien.Horsley
25639503622SDamien.Horsley static const struct dev_pm_ops pistachio_internal_dac_pm_ops = {
25739503622SDamien.Horsley SET_RUNTIME_PM_OPS(pistachio_internal_dac_rt_suspend,
25839503622SDamien.Horsley pistachio_internal_dac_rt_resume, NULL)
25939503622SDamien.Horsley };
26039503622SDamien.Horsley
26139503622SDamien.Horsley static const struct of_device_id pistachio_internal_dac_of_match[] = {
26239503622SDamien.Horsley { .compatible = "img,pistachio-internal-dac" },
26339503622SDamien.Horsley {}
26439503622SDamien.Horsley };
26539503622SDamien.Horsley MODULE_DEVICE_TABLE(of, pistachio_internal_dac_of_match);
26639503622SDamien.Horsley
26739503622SDamien.Horsley static struct platform_driver pistachio_internal_dac_plat_driver = {
26839503622SDamien.Horsley .driver = {
26939503622SDamien.Horsley .name = "img-pistachio-internal-dac",
27039503622SDamien.Horsley .of_match_table = pistachio_internal_dac_of_match,
27139503622SDamien.Horsley .pm = &pistachio_internal_dac_pm_ops
27239503622SDamien.Horsley },
27339503622SDamien.Horsley .probe = pistachio_internal_dac_probe,
274*130af75bSUwe Kleine-König .remove = pistachio_internal_dac_remove
27539503622SDamien.Horsley };
27639503622SDamien.Horsley module_platform_driver(pistachio_internal_dac_plat_driver);
27739503622SDamien.Horsley
27839503622SDamien.Horsley MODULE_DESCRIPTION("Pistachio Internal DAC driver");
27939503622SDamien.Horsley MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
28039503622SDamien.Horsley MODULE_LICENSE("GPL v2");
281