xref: /linux/sound/soc/img/pistachio-internal-dac.c (revision 75a6faf617d107bdbc74d36ccf89f2280b96ac26)
1*75a6faf6SThomas 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 
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 
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 
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 
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,
1418dc906d3SKuninori Morimoto 	.non_legacy_dai_naming	= 1,
14239503622SDamien.Horsley };
14339503622SDamien.Horsley 
14439503622SDamien.Horsley static int pistachio_internal_dac_probe(struct platform_device *pdev)
14539503622SDamien.Horsley {
14639503622SDamien.Horsley 	struct pistachio_internal_dac *dac;
14739503622SDamien.Horsley 	int ret, voltage;
14839503622SDamien.Horsley 	struct device *dev = &pdev->dev;
14939503622SDamien.Horsley 	u32 reg;
15039503622SDamien.Horsley 
15139503622SDamien.Horsley 	dac = devm_kzalloc(dev, sizeof(*dac), GFP_KERNEL);
15239503622SDamien.Horsley 
15339503622SDamien.Horsley 	if (!dac)
15439503622SDamien.Horsley 		return -ENOMEM;
15539503622SDamien.Horsley 
15639503622SDamien.Horsley 	platform_set_drvdata(pdev, dac);
15739503622SDamien.Horsley 
15839503622SDamien.Horsley 	dac->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
15939503622SDamien.Horsley 							    "img,cr-top");
16039503622SDamien.Horsley 	if (IS_ERR(dac->regmap))
16139503622SDamien.Horsley 		return PTR_ERR(dac->regmap);
16239503622SDamien.Horsley 
16339503622SDamien.Horsley 	dac->supply = devm_regulator_get(dev, "VDD");
16439503622SDamien.Horsley 	if (IS_ERR(dac->supply)) {
16539503622SDamien.Horsley 		ret = PTR_ERR(dac->supply);
16639503622SDamien.Horsley 		if (ret != -EPROBE_DEFER)
16739503622SDamien.Horsley 			dev_err(dev, "failed to acquire supply 'VDD-supply': %d\n", ret);
16839503622SDamien.Horsley 		return ret;
16939503622SDamien.Horsley 	}
17039503622SDamien.Horsley 
17139503622SDamien.Horsley 	ret = regulator_enable(dac->supply);
17239503622SDamien.Horsley 	if (ret) {
17339503622SDamien.Horsley 		dev_err(dev, "failed to enable supply: %d\n", ret);
17439503622SDamien.Horsley 		return ret;
17539503622SDamien.Horsley 	}
17639503622SDamien.Horsley 
17739503622SDamien.Horsley 	voltage = regulator_get_voltage(dac->supply);
17839503622SDamien.Horsley 
17939503622SDamien.Horsley 	switch (voltage) {
18039503622SDamien.Horsley 	case 1800000:
18139503622SDamien.Horsley 		reg = 0;
18239503622SDamien.Horsley 		break;
18339503622SDamien.Horsley 	case 3300000:
18439503622SDamien.Horsley 		reg = PISTACHIO_INTERNAL_DAC_CTRL_PWR_SEL_MASK;
18539503622SDamien.Horsley 		break;
18639503622SDamien.Horsley 	default:
18739503622SDamien.Horsley 		dev_err(dev, "invalid voltage: %d\n", voltage);
18839503622SDamien.Horsley 		ret = -EINVAL;
18939503622SDamien.Horsley 		goto err_regulator;
19039503622SDamien.Horsley 	}
19139503622SDamien.Horsley 
19239503622SDamien.Horsley 	regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_CTRL,
19339503622SDamien.Horsley 			PISTACHIO_INTERNAL_DAC_CTRL_PWR_SEL_MASK, reg);
19439503622SDamien.Horsley 
19539503622SDamien.Horsley 	pistachio_internal_dac_pwr_off(dac);
19639503622SDamien.Horsley 	pistachio_internal_dac_pwr_on(dac);
19739503622SDamien.Horsley 
19839503622SDamien.Horsley 	pm_runtime_set_active(dev);
19939503622SDamien.Horsley 	pm_runtime_enable(dev);
20039503622SDamien.Horsley 	pm_runtime_idle(dev);
20139503622SDamien.Horsley 
2028dc906d3SKuninori Morimoto 	ret = devm_snd_soc_register_component(dev,
2038dc906d3SKuninori Morimoto 			&pistachio_internal_dac_driver,
20439503622SDamien.Horsley 			pistachio_internal_dac_dais,
20539503622SDamien.Horsley 			ARRAY_SIZE(pistachio_internal_dac_dais));
20639503622SDamien.Horsley 	if (ret) {
2078dc906d3SKuninori Morimoto 		dev_err(dev, "failed to register component: %d\n", ret);
20839503622SDamien.Horsley 		goto err_pwr;
20939503622SDamien.Horsley 	}
21039503622SDamien.Horsley 
21139503622SDamien.Horsley 	return 0;
21239503622SDamien.Horsley 
21339503622SDamien.Horsley err_pwr:
21439503622SDamien.Horsley 	pm_runtime_disable(&pdev->dev);
21539503622SDamien.Horsley 	pistachio_internal_dac_pwr_off(dac);
21639503622SDamien.Horsley err_regulator:
21739503622SDamien.Horsley 	regulator_disable(dac->supply);
21839503622SDamien.Horsley 
21939503622SDamien.Horsley 	return ret;
22039503622SDamien.Horsley }
22139503622SDamien.Horsley 
22239503622SDamien.Horsley static int pistachio_internal_dac_remove(struct platform_device *pdev)
22339503622SDamien.Horsley {
22439503622SDamien.Horsley 	struct pistachio_internal_dac *dac = dev_get_drvdata(&pdev->dev);
22539503622SDamien.Horsley 
22639503622SDamien.Horsley 	pm_runtime_disable(&pdev->dev);
22739503622SDamien.Horsley 	pistachio_internal_dac_pwr_off(dac);
22839503622SDamien.Horsley 	regulator_disable(dac->supply);
22939503622SDamien.Horsley 
23039503622SDamien.Horsley 	return 0;
23139503622SDamien.Horsley }
23239503622SDamien.Horsley 
23339503622SDamien.Horsley #ifdef CONFIG_PM
23439503622SDamien.Horsley static int pistachio_internal_dac_rt_resume(struct device *dev)
23539503622SDamien.Horsley {
23639503622SDamien.Horsley 	struct pistachio_internal_dac *dac = dev_get_drvdata(dev);
23739503622SDamien.Horsley 	int ret;
23839503622SDamien.Horsley 
23939503622SDamien.Horsley 	ret = regulator_enable(dac->supply);
24039503622SDamien.Horsley 	if (ret) {
24139503622SDamien.Horsley 		dev_err(dev, "failed to enable supply: %d\n", ret);
24239503622SDamien.Horsley 		return ret;
24339503622SDamien.Horsley 	}
24439503622SDamien.Horsley 
24539503622SDamien.Horsley 	pistachio_internal_dac_pwr_on(dac);
24639503622SDamien.Horsley 
24739503622SDamien.Horsley 	return 0;
24839503622SDamien.Horsley }
24939503622SDamien.Horsley 
25039503622SDamien.Horsley static int pistachio_internal_dac_rt_suspend(struct device *dev)
25139503622SDamien.Horsley {
25239503622SDamien.Horsley 	struct pistachio_internal_dac *dac = dev_get_drvdata(dev);
25339503622SDamien.Horsley 
25439503622SDamien.Horsley 	pistachio_internal_dac_pwr_off(dac);
25539503622SDamien.Horsley 
25639503622SDamien.Horsley 	regulator_disable(dac->supply);
25739503622SDamien.Horsley 
25839503622SDamien.Horsley 	return 0;
25939503622SDamien.Horsley }
26039503622SDamien.Horsley #endif
26139503622SDamien.Horsley 
26239503622SDamien.Horsley static const struct dev_pm_ops pistachio_internal_dac_pm_ops = {
26339503622SDamien.Horsley 	SET_RUNTIME_PM_OPS(pistachio_internal_dac_rt_suspend,
26439503622SDamien.Horsley 			pistachio_internal_dac_rt_resume, NULL)
26539503622SDamien.Horsley };
26639503622SDamien.Horsley 
26739503622SDamien.Horsley static const struct of_device_id pistachio_internal_dac_of_match[] = {
26839503622SDamien.Horsley 	{ .compatible = "img,pistachio-internal-dac" },
26939503622SDamien.Horsley 	{}
27039503622SDamien.Horsley };
27139503622SDamien.Horsley MODULE_DEVICE_TABLE(of, pistachio_internal_dac_of_match);
27239503622SDamien.Horsley 
27339503622SDamien.Horsley static struct platform_driver pistachio_internal_dac_plat_driver = {
27439503622SDamien.Horsley 	.driver = {
27539503622SDamien.Horsley 		.name = "img-pistachio-internal-dac",
27639503622SDamien.Horsley 		.of_match_table = pistachio_internal_dac_of_match,
27739503622SDamien.Horsley 		.pm = &pistachio_internal_dac_pm_ops
27839503622SDamien.Horsley 	},
27939503622SDamien.Horsley 	.probe = pistachio_internal_dac_probe,
28039503622SDamien.Horsley 	.remove = pistachio_internal_dac_remove
28139503622SDamien.Horsley };
28239503622SDamien.Horsley module_platform_driver(pistachio_internal_dac_plat_driver);
28339503622SDamien.Horsley 
28439503622SDamien.Horsley MODULE_DESCRIPTION("Pistachio Internal DAC driver");
28539503622SDamien.Horsley MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
28639503622SDamien.Horsley MODULE_LICENSE("GPL v2");
287