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