xref: /linux/sound/soc/img/pistachio-internal-dac.c (revision ab44348955bfc3b9ca127dfb36d0c3231adacf07)
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 
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,
14139503622SDamien.Horsley };
14239503622SDamien.Horsley 
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 
218*ab443489SUwe 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
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 
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*ab443489SUwe Kleine-König 	.remove_new = 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