xref: /linux/sound/soc/img/pistachio-internal-dac.c (revision 395036225390a940cba7cec5c2306a6999d13d94)
1*39503622SDamien.Horsley /*
2*39503622SDamien.Horsley  * Pistachio internal dac driver
3*39503622SDamien.Horsley  *
4*39503622SDamien.Horsley  * Copyright (C) 2015 Imagination Technologies Ltd.
5*39503622SDamien.Horsley  *
6*39503622SDamien.Horsley  * Author: Damien Horsley <Damien.Horsley@imgtec.com>
7*39503622SDamien.Horsley  *
8*39503622SDamien.Horsley  * This program is free software; you can redistribute it and/or modify it
9*39503622SDamien.Horsley  * under the terms and conditions of the GNU General Public License,
10*39503622SDamien.Horsley  * version 2, as published by the Free Software Foundation.
11*39503622SDamien.Horsley  */
12*39503622SDamien.Horsley 
13*39503622SDamien.Horsley #include <linux/clk.h>
14*39503622SDamien.Horsley #include <linux/delay.h>
15*39503622SDamien.Horsley #include <linux/mfd/syscon.h>
16*39503622SDamien.Horsley #include <linux/module.h>
17*39503622SDamien.Horsley #include <linux/pm_runtime.h>
18*39503622SDamien.Horsley #include <linux/regmap.h>
19*39503622SDamien.Horsley #include <linux/regulator/consumer.h>
20*39503622SDamien.Horsley 
21*39503622SDamien.Horsley #include <sound/pcm_params.h>
22*39503622SDamien.Horsley #include <sound/soc.h>
23*39503622SDamien.Horsley 
24*39503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_CTRL			0x40
25*39503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_CTRL_PWR_SEL_MASK	0x2
26*39503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK		0x1
27*39503622SDamien.Horsley 
28*39503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_SRST			0x44
29*39503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_SRST_MASK		0x1
30*39503622SDamien.Horsley 
31*39503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_GTI_CTRL			0x48
32*39503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_SHIFT	0
33*39503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_MASK	0xFFF
34*39503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK		0x1000
35*39503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_SHIFT	13
36*39503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_MASK	0x1FE000
37*39503622SDamien.Horsley 
38*39503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_PWR			0x1
39*39503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_PWR_MASK			0x1
40*39503622SDamien.Horsley 
41*39503622SDamien.Horsley #define PISTACHIO_INTERNAL_DAC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE |  \
42*39503622SDamien.Horsley 					SNDRV_PCM_FMTBIT_S32_LE)
43*39503622SDamien.Horsley 
44*39503622SDamien.Horsley /* codec private data */
45*39503622SDamien.Horsley struct pistachio_internal_dac {
46*39503622SDamien.Horsley 	struct regmap *regmap;
47*39503622SDamien.Horsley 	struct regulator *supply;
48*39503622SDamien.Horsley 	bool mute;
49*39503622SDamien.Horsley };
50*39503622SDamien.Horsley 
51*39503622SDamien.Horsley static const struct snd_kcontrol_new pistachio_internal_dac_snd_controls[] = {
52*39503622SDamien.Horsley 	SOC_SINGLE("Playback Switch", PISTACHIO_INTERNAL_DAC_CTRL, 2, 1, 1)
53*39503622SDamien.Horsley };
54*39503622SDamien.Horsley 
55*39503622SDamien.Horsley static const struct snd_soc_dapm_widget pistachio_internal_dac_widgets[] = {
56*39503622SDamien.Horsley 	SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
57*39503622SDamien.Horsley 	SND_SOC_DAPM_OUTPUT("AOUTL"),
58*39503622SDamien.Horsley 	SND_SOC_DAPM_OUTPUT("AOUTR"),
59*39503622SDamien.Horsley };
60*39503622SDamien.Horsley 
61*39503622SDamien.Horsley static const struct snd_soc_dapm_route pistachio_internal_dac_routes[] = {
62*39503622SDamien.Horsley 	{ "AOUTL", NULL, "DAC" },
63*39503622SDamien.Horsley 	{ "AOUTR", NULL, "DAC" },
64*39503622SDamien.Horsley };
65*39503622SDamien.Horsley 
66*39503622SDamien.Horsley static void pistachio_internal_dac_reg_writel(struct regmap *top_regs,
67*39503622SDamien.Horsley 						u32 val, u32 reg)
68*39503622SDamien.Horsley {
69*39503622SDamien.Horsley 	regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL,
70*39503622SDamien.Horsley 			PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_MASK,
71*39503622SDamien.Horsley 			reg << PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_SHIFT);
72*39503622SDamien.Horsley 
73*39503622SDamien.Horsley 	regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL,
74*39503622SDamien.Horsley 			PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_MASK,
75*39503622SDamien.Horsley 			val << PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_SHIFT);
76*39503622SDamien.Horsley 
77*39503622SDamien.Horsley 	regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL,
78*39503622SDamien.Horsley 			PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK,
79*39503622SDamien.Horsley 			PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK);
80*39503622SDamien.Horsley 
81*39503622SDamien.Horsley 	regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL,
82*39503622SDamien.Horsley 			PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK, 0);
83*39503622SDamien.Horsley }
84*39503622SDamien.Horsley 
85*39503622SDamien.Horsley static void pistachio_internal_dac_pwr_off(struct pistachio_internal_dac *dac)
86*39503622SDamien.Horsley {
87*39503622SDamien.Horsley 	regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_CTRL,
88*39503622SDamien.Horsley 		PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK,
89*39503622SDamien.Horsley 		PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK);
90*39503622SDamien.Horsley 
91*39503622SDamien.Horsley 	pistachio_internal_dac_reg_writel(dac->regmap, 0,
92*39503622SDamien.Horsley 					PISTACHIO_INTERNAL_DAC_PWR);
93*39503622SDamien.Horsley }
94*39503622SDamien.Horsley 
95*39503622SDamien.Horsley static void pistachio_internal_dac_pwr_on(struct pistachio_internal_dac *dac)
96*39503622SDamien.Horsley {
97*39503622SDamien.Horsley 	regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_SRST,
98*39503622SDamien.Horsley 			PISTACHIO_INTERNAL_DAC_SRST_MASK,
99*39503622SDamien.Horsley 			PISTACHIO_INTERNAL_DAC_SRST_MASK);
100*39503622SDamien.Horsley 
101*39503622SDamien.Horsley 	regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_SRST,
102*39503622SDamien.Horsley 			PISTACHIO_INTERNAL_DAC_SRST_MASK, 0);
103*39503622SDamien.Horsley 
104*39503622SDamien.Horsley 	pistachio_internal_dac_reg_writel(dac->regmap,
105*39503622SDamien.Horsley 					PISTACHIO_INTERNAL_DAC_PWR_MASK,
106*39503622SDamien.Horsley 					PISTACHIO_INTERNAL_DAC_PWR);
107*39503622SDamien.Horsley 
108*39503622SDamien.Horsley 	regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_CTRL,
109*39503622SDamien.Horsley 			PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK, 0);
110*39503622SDamien.Horsley }
111*39503622SDamien.Horsley 
112*39503622SDamien.Horsley static struct snd_soc_dai_driver pistachio_internal_dac_dais[] = {
113*39503622SDamien.Horsley 	{
114*39503622SDamien.Horsley 		.name = "pistachio_internal_dac",
115*39503622SDamien.Horsley 		.playback = {
116*39503622SDamien.Horsley 			.stream_name = "Playback",
117*39503622SDamien.Horsley 			.channels_min = 2,
118*39503622SDamien.Horsley 			.channels_max = 2,
119*39503622SDamien.Horsley 			.rates = SNDRV_PCM_RATE_8000_48000,
120*39503622SDamien.Horsley 			.formats = PISTACHIO_INTERNAL_DAC_FORMATS,
121*39503622SDamien.Horsley 		}
122*39503622SDamien.Horsley 	},
123*39503622SDamien.Horsley };
124*39503622SDamien.Horsley 
125*39503622SDamien.Horsley static int pistachio_internal_dac_codec_probe(struct snd_soc_codec *codec)
126*39503622SDamien.Horsley {
127*39503622SDamien.Horsley 	struct pistachio_internal_dac *dac = snd_soc_codec_get_drvdata(codec);
128*39503622SDamien.Horsley 
129*39503622SDamien.Horsley 	snd_soc_codec_init_regmap(codec, dac->regmap);
130*39503622SDamien.Horsley 
131*39503622SDamien.Horsley 	return 0;
132*39503622SDamien.Horsley }
133*39503622SDamien.Horsley 
134*39503622SDamien.Horsley static const struct snd_soc_codec_driver pistachio_internal_dac_driver = {
135*39503622SDamien.Horsley 	.probe = pistachio_internal_dac_codec_probe,
136*39503622SDamien.Horsley 	.idle_bias_off = true,
137*39503622SDamien.Horsley 	.controls = pistachio_internal_dac_snd_controls,
138*39503622SDamien.Horsley 	.num_controls = ARRAY_SIZE(pistachio_internal_dac_snd_controls),
139*39503622SDamien.Horsley 	.dapm_widgets = pistachio_internal_dac_widgets,
140*39503622SDamien.Horsley 	.num_dapm_widgets = ARRAY_SIZE(pistachio_internal_dac_widgets),
141*39503622SDamien.Horsley 	.dapm_routes = pistachio_internal_dac_routes,
142*39503622SDamien.Horsley 	.num_dapm_routes = ARRAY_SIZE(pistachio_internal_dac_routes),
143*39503622SDamien.Horsley };
144*39503622SDamien.Horsley 
145*39503622SDamien.Horsley static int pistachio_internal_dac_probe(struct platform_device *pdev)
146*39503622SDamien.Horsley {
147*39503622SDamien.Horsley 	struct pistachio_internal_dac *dac;
148*39503622SDamien.Horsley 	int ret, voltage;
149*39503622SDamien.Horsley 	struct device *dev = &pdev->dev;
150*39503622SDamien.Horsley 	u32 reg;
151*39503622SDamien.Horsley 
152*39503622SDamien.Horsley 	dac = devm_kzalloc(dev, sizeof(*dac), GFP_KERNEL);
153*39503622SDamien.Horsley 
154*39503622SDamien.Horsley 	if (!dac)
155*39503622SDamien.Horsley 		return -ENOMEM;
156*39503622SDamien.Horsley 
157*39503622SDamien.Horsley 	platform_set_drvdata(pdev, dac);
158*39503622SDamien.Horsley 
159*39503622SDamien.Horsley 	dac->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
160*39503622SDamien.Horsley 							    "img,cr-top");
161*39503622SDamien.Horsley 	if (IS_ERR(dac->regmap))
162*39503622SDamien.Horsley 		return PTR_ERR(dac->regmap);
163*39503622SDamien.Horsley 
164*39503622SDamien.Horsley 	dac->supply = devm_regulator_get(dev, "VDD");
165*39503622SDamien.Horsley 	if (IS_ERR(dac->supply)) {
166*39503622SDamien.Horsley 		ret = PTR_ERR(dac->supply);
167*39503622SDamien.Horsley 		if (ret != -EPROBE_DEFER)
168*39503622SDamien.Horsley 			dev_err(dev, "failed to acquire supply 'VDD-supply': %d\n", ret);
169*39503622SDamien.Horsley 		return ret;
170*39503622SDamien.Horsley 	}
171*39503622SDamien.Horsley 
172*39503622SDamien.Horsley 	ret = regulator_enable(dac->supply);
173*39503622SDamien.Horsley 	if (ret) {
174*39503622SDamien.Horsley 		dev_err(dev, "failed to enable supply: %d\n", ret);
175*39503622SDamien.Horsley 		return ret;
176*39503622SDamien.Horsley 	}
177*39503622SDamien.Horsley 
178*39503622SDamien.Horsley 	voltage = regulator_get_voltage(dac->supply);
179*39503622SDamien.Horsley 
180*39503622SDamien.Horsley 	switch (voltage) {
181*39503622SDamien.Horsley 	case 1800000:
182*39503622SDamien.Horsley 		reg = 0;
183*39503622SDamien.Horsley 		break;
184*39503622SDamien.Horsley 	case 3300000:
185*39503622SDamien.Horsley 		reg = PISTACHIO_INTERNAL_DAC_CTRL_PWR_SEL_MASK;
186*39503622SDamien.Horsley 		break;
187*39503622SDamien.Horsley 	default:
188*39503622SDamien.Horsley 		dev_err(dev, "invalid voltage: %d\n", voltage);
189*39503622SDamien.Horsley 		ret = -EINVAL;
190*39503622SDamien.Horsley 		goto err_regulator;
191*39503622SDamien.Horsley 	}
192*39503622SDamien.Horsley 
193*39503622SDamien.Horsley 	regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_CTRL,
194*39503622SDamien.Horsley 			PISTACHIO_INTERNAL_DAC_CTRL_PWR_SEL_MASK, reg);
195*39503622SDamien.Horsley 
196*39503622SDamien.Horsley 	pistachio_internal_dac_pwr_off(dac);
197*39503622SDamien.Horsley 	pistachio_internal_dac_pwr_on(dac);
198*39503622SDamien.Horsley 
199*39503622SDamien.Horsley 	pm_runtime_set_active(dev);
200*39503622SDamien.Horsley 	pm_runtime_enable(dev);
201*39503622SDamien.Horsley 	pm_runtime_idle(dev);
202*39503622SDamien.Horsley 
203*39503622SDamien.Horsley 	ret = snd_soc_register_codec(dev, &pistachio_internal_dac_driver,
204*39503622SDamien.Horsley 			pistachio_internal_dac_dais,
205*39503622SDamien.Horsley 			ARRAY_SIZE(pistachio_internal_dac_dais));
206*39503622SDamien.Horsley 	if (ret) {
207*39503622SDamien.Horsley 		dev_err(dev, "failed to register codec: %d\n", ret);
208*39503622SDamien.Horsley 		goto err_pwr;
209*39503622SDamien.Horsley 	}
210*39503622SDamien.Horsley 
211*39503622SDamien.Horsley 	return 0;
212*39503622SDamien.Horsley 
213*39503622SDamien.Horsley err_pwr:
214*39503622SDamien.Horsley 	pm_runtime_disable(&pdev->dev);
215*39503622SDamien.Horsley 	pistachio_internal_dac_pwr_off(dac);
216*39503622SDamien.Horsley err_regulator:
217*39503622SDamien.Horsley 	regulator_disable(dac->supply);
218*39503622SDamien.Horsley 
219*39503622SDamien.Horsley 	return ret;
220*39503622SDamien.Horsley }
221*39503622SDamien.Horsley 
222*39503622SDamien.Horsley static int pistachio_internal_dac_remove(struct platform_device *pdev)
223*39503622SDamien.Horsley {
224*39503622SDamien.Horsley 	struct pistachio_internal_dac *dac = dev_get_drvdata(&pdev->dev);
225*39503622SDamien.Horsley 
226*39503622SDamien.Horsley 	snd_soc_unregister_codec(&pdev->dev);
227*39503622SDamien.Horsley 	pm_runtime_disable(&pdev->dev);
228*39503622SDamien.Horsley 	pistachio_internal_dac_pwr_off(dac);
229*39503622SDamien.Horsley 	regulator_disable(dac->supply);
230*39503622SDamien.Horsley 
231*39503622SDamien.Horsley 	return 0;
232*39503622SDamien.Horsley }
233*39503622SDamien.Horsley 
234*39503622SDamien.Horsley #ifdef CONFIG_PM
235*39503622SDamien.Horsley static int pistachio_internal_dac_rt_resume(struct device *dev)
236*39503622SDamien.Horsley {
237*39503622SDamien.Horsley 	struct pistachio_internal_dac *dac = dev_get_drvdata(dev);
238*39503622SDamien.Horsley 	int ret;
239*39503622SDamien.Horsley 
240*39503622SDamien.Horsley 	ret = regulator_enable(dac->supply);
241*39503622SDamien.Horsley 	if (ret) {
242*39503622SDamien.Horsley 		dev_err(dev, "failed to enable supply: %d\n", ret);
243*39503622SDamien.Horsley 		return ret;
244*39503622SDamien.Horsley 	}
245*39503622SDamien.Horsley 
246*39503622SDamien.Horsley 	pistachio_internal_dac_pwr_on(dac);
247*39503622SDamien.Horsley 
248*39503622SDamien.Horsley 	return 0;
249*39503622SDamien.Horsley }
250*39503622SDamien.Horsley 
251*39503622SDamien.Horsley static int pistachio_internal_dac_rt_suspend(struct device *dev)
252*39503622SDamien.Horsley {
253*39503622SDamien.Horsley 	struct pistachio_internal_dac *dac = dev_get_drvdata(dev);
254*39503622SDamien.Horsley 
255*39503622SDamien.Horsley 	pistachio_internal_dac_pwr_off(dac);
256*39503622SDamien.Horsley 
257*39503622SDamien.Horsley 	regulator_disable(dac->supply);
258*39503622SDamien.Horsley 
259*39503622SDamien.Horsley 	return 0;
260*39503622SDamien.Horsley }
261*39503622SDamien.Horsley #endif
262*39503622SDamien.Horsley 
263*39503622SDamien.Horsley static const struct dev_pm_ops pistachio_internal_dac_pm_ops = {
264*39503622SDamien.Horsley 	SET_RUNTIME_PM_OPS(pistachio_internal_dac_rt_suspend,
265*39503622SDamien.Horsley 			pistachio_internal_dac_rt_resume, NULL)
266*39503622SDamien.Horsley };
267*39503622SDamien.Horsley 
268*39503622SDamien.Horsley static const struct of_device_id pistachio_internal_dac_of_match[] = {
269*39503622SDamien.Horsley 	{ .compatible = "img,pistachio-internal-dac" },
270*39503622SDamien.Horsley 	{}
271*39503622SDamien.Horsley };
272*39503622SDamien.Horsley MODULE_DEVICE_TABLE(of, pistachio_internal_dac_of_match);
273*39503622SDamien.Horsley 
274*39503622SDamien.Horsley static struct platform_driver pistachio_internal_dac_plat_driver = {
275*39503622SDamien.Horsley 	.driver = {
276*39503622SDamien.Horsley 		.name = "img-pistachio-internal-dac",
277*39503622SDamien.Horsley 		.of_match_table = pistachio_internal_dac_of_match,
278*39503622SDamien.Horsley 		.pm = &pistachio_internal_dac_pm_ops
279*39503622SDamien.Horsley 	},
280*39503622SDamien.Horsley 	.probe = pistachio_internal_dac_probe,
281*39503622SDamien.Horsley 	.remove = pistachio_internal_dac_remove
282*39503622SDamien.Horsley };
283*39503622SDamien.Horsley module_platform_driver(pistachio_internal_dac_plat_driver);
284*39503622SDamien.Horsley 
285*39503622SDamien.Horsley MODULE_DESCRIPTION("Pistachio Internal DAC driver");
286*39503622SDamien.Horsley MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
287*39503622SDamien.Horsley MODULE_LICENSE("GPL v2");
288