]> rtime.felk.cvut.cz Git - hercules2020/nv-tegra/linux-4.4.git/blob - sound/soc/tegra-alt/tegra210_ope_alt.c
ASoC: tegra-alt: Fix system suspend handling
[hercules2020/nv-tegra/linux-4.4.git] / sound / soc / tegra-alt / tegra210_ope_alt.c
1 /*
2  * tegra210_ope_alt.c - Tegra210 OPE driver
3  *
4  * Copyright (c) 2014-2017, NVIDIA CORPORATION.  All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms and conditions of the GNU General Public License,
8  * version 2, as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <linux/clk.h>
20 #include <linux/device.h>
21 #include <linux/io.h>
22 #include <linux/module.h>
23 #include <linux/of.h>
24 #include <linux/platform_device.h>
25 #include <linux/pm_runtime.h>
26 #include <linux/regmap.h>
27 #include <linux/slab.h>
28 #include <sound/core.h>
29 #include <sound/pcm.h>
30 #include <sound/pcm_params.h>
31 #include <sound/soc.h>
32 #include <linux/pinctrl/consumer.h>
33 #include <linux/of_device.h>
34
35 #include "tegra210_xbar_alt.h"
36 #include "tegra210_ope_alt.h"
37
38 #define DRV_NAME "tegra210-ope"
39
40 static const struct reg_default tegra210_ope_reg_defaults[] = {
41         { TEGRA210_OPE_AXBAR_RX_INT_MASK, 0x00000001},
42         { TEGRA210_OPE_AXBAR_RX_CIF_CTRL, 0x00007700},
43         { TEGRA210_OPE_AXBAR_TX_INT_MASK, 0x00000001},
44         { TEGRA210_OPE_AXBAR_TX_CIF_CTRL, 0x00007700},
45         { TEGRA210_OPE_CG, 0x1},
46 };
47
48 static int tegra210_ope_runtime_suspend(struct device *dev)
49 {
50         struct tegra210_ope *ope = dev_get_drvdata(dev);
51
52         regcache_cache_only(ope->mbdrc_regmap, true);
53         regcache_cache_only(ope->peq_regmap, true);
54         regcache_cache_only(ope->regmap, true);
55         regcache_mark_dirty(ope->regmap);
56         regcache_mark_dirty(ope->peq_regmap);
57         regcache_mark_dirty(ope->mbdrc_regmap);
58
59         return 0;
60 }
61
62 static int tegra210_ope_runtime_resume(struct device *dev)
63 {
64         struct tegra210_ope *ope = dev_get_drvdata(dev);
65
66         regcache_cache_only(ope->regmap, false);
67         regcache_cache_only(ope->peq_regmap, false);
68         regcache_cache_only(ope->mbdrc_regmap, false);
69
70         if (!ope->is_shutdown) {
71                 regcache_sync(ope->regmap);
72                 regcache_sync(ope->peq_regmap);
73                 regcache_sync(ope->mbdrc_regmap);
74         }
75
76         return 0;
77 }
78
79 #ifdef CONFIG_PM_SLEEP
80 static int tegra210_ope_suspend(struct device *dev)
81 {
82         if (pm_runtime_status_suspended(dev))
83                 return 0;
84
85         return tegra210_ope_runtime_suspend(dev);
86 }
87
88 static int tegra210_ope_resume(struct device *dev)
89 {
90         if (pm_runtime_status_suspended(dev))
91                 return 0;
92
93         return tegra210_ope_runtime_resume(dev);
94 }
95 #endif
96
97 static int tegra210_ope_set_audio_cif(struct tegra210_ope *ope,
98                                 struct snd_pcm_hw_params *params,
99                                 unsigned int reg)
100 {
101         int channels, audio_bits;
102         struct tegra210_xbar_cif_conf cif_conf;
103
104         memset(&cif_conf, 0, sizeof(struct tegra210_xbar_cif_conf));
105
106         channels = params_channels(params);
107         if (channels < 2)
108                 return -EINVAL;
109
110         switch (params_format(params)) {
111         case SNDRV_PCM_FORMAT_S16_LE:
112                 audio_bits = TEGRA210_AUDIOCIF_BITS_16;
113                 break;
114         case SNDRV_PCM_FORMAT_S32_LE:
115                 audio_bits = TEGRA210_AUDIOCIF_BITS_32;
116                 break;
117         default:
118                 return -EINVAL;
119         }
120
121         cif_conf.audio_channels = channels;
122         cif_conf.client_channels = channels;
123         cif_conf.audio_bits = audio_bits;
124         cif_conf.client_bits = audio_bits;
125
126         ope->soc_data->set_audio_cif(ope->regmap, reg, &cif_conf);
127
128         return 0;
129 }
130
131 static int tegra210_ope_hw_params(struct snd_pcm_substream *substream,
132                                  struct snd_pcm_hw_params *params,
133                                  struct snd_soc_dai *dai)
134 {
135         struct device *dev = dai->dev;
136         struct tegra210_ope *ope = snd_soc_dai_get_drvdata(dai);
137         int ret;
138
139         /* set RX cif and TX cif */
140         ret = tegra210_ope_set_audio_cif(ope, params,
141                                 TEGRA210_OPE_AXBAR_RX_CIF_CTRL);
142         if (ret) {
143                 dev_err(dev, "Can't set OPE RX CIF: %d\n", ret);
144                 return ret;
145         }
146
147         ret = tegra210_ope_set_audio_cif(ope, params,
148                                 TEGRA210_OPE_AXBAR_TX_CIF_CTRL);
149         if (ret) {
150                 dev_err(dev, "Can't set OPE TX CIF: %d\n", ret);
151                 return ret;
152         }
153
154         return ret;
155 }
156
157 static int tegra210_ope_codec_probe(struct snd_soc_codec *codec)
158 {
159         struct tegra210_ope *ope = snd_soc_codec_get_drvdata(codec);
160
161         codec->control_data = ope->regmap;
162
163         ope->soc_data->peq_soc_data.codec_init(codec);
164         ope->soc_data->mbdrc_soc_data.codec_init(codec);
165
166         return 0;
167 }
168
169 static struct regmap *tegra210_ope_init_regmap(struct device *dev)
170 {
171         struct tegra210_ope *ope = dev_get_drvdata(dev);
172
173         return ope->regmap;
174 }
175
176 static struct snd_soc_dai_ops tegra210_ope_dai_ops = {
177         .hw_params      = tegra210_ope_hw_params,
178 };
179
180 static struct snd_soc_dai_driver tegra210_ope_dais[] = {
181         {
182                 .name = "OPE IN",
183                 .playback = {
184                         .stream_name = "OPE Receive",
185                         .channels_min = 1,
186                         .channels_max = 8,
187                         .rates = SNDRV_PCM_RATE_8000_192000,
188                         .formats = SNDRV_PCM_FMTBIT_S8 |
189                                 SNDRV_PCM_FMTBIT_S16_LE |
190                                 SNDRV_PCM_FMTBIT_S24_LE |
191                                 SNDRV_PCM_FMTBIT_S32_LE,
192                 },
193         },
194         {
195                 .name = "OPE OUT",
196                 .capture = {
197                         .stream_name = "OPE Transmit",
198                         .channels_min = 1,
199                         .channels_max = 8,
200                         .rates = SNDRV_PCM_RATE_8000_192000,
201                         .formats = SNDRV_PCM_FMTBIT_S8 |
202                                 SNDRV_PCM_FMTBIT_S16_LE |
203                                 SNDRV_PCM_FMTBIT_S24_LE |
204                                 SNDRV_PCM_FMTBIT_S32_LE,
205                 },
206                 .ops = &tegra210_ope_dai_ops,
207         }
208 };
209
210 static const struct snd_soc_dapm_widget tegra210_ope_widgets[] = {
211         SND_SOC_DAPM_AIF_IN("OPE RX", NULL, 0, SND_SOC_NOPM,
212                                 0, 0),
213         SND_SOC_DAPM_AIF_OUT("OPE TX", NULL, 0, TEGRA210_OPE_ENABLE,
214                                 TEGRA210_OPE_EN_SHIFT, 0),
215 };
216
217 static const struct snd_soc_dapm_route tegra210_ope_routes[] = {
218         { "OPE RX",       NULL, "OPE Receive" },
219         { "OPE TX",       NULL, "OPE RX" },
220         { "OPE Transmit", NULL, "OPE TX" },
221 };
222
223 static const struct snd_kcontrol_new tegra210_ope_controls[] = {
224         SOC_SINGLE("direction peq to mbdrc", TEGRA210_OPE_DIRECTION,
225                                 TEGRA210_OPE_DIRECTION_SHIFT, 1, 0),
226 };
227
228 static struct snd_soc_codec_driver tegra210_ope_codec = {
229         .probe = tegra210_ope_codec_probe,
230         .dapm_widgets = tegra210_ope_widgets,
231         .num_dapm_widgets = ARRAY_SIZE(tegra210_ope_widgets),
232         .dapm_routes = tegra210_ope_routes,
233         .num_dapm_routes = ARRAY_SIZE(tegra210_ope_routes),
234         .controls = tegra210_ope_controls,
235         .num_controls = ARRAY_SIZE(tegra210_ope_controls),
236         .idle_bias_off = 1,
237         .get_regmap = tegra210_ope_init_regmap,
238 };
239
240 static bool tegra210_ope_wr_reg(struct device *dev, unsigned int reg)
241 {
242         switch (reg) {
243         case TEGRA210_OPE_AXBAR_RX_INT_MASK:
244         case TEGRA210_OPE_AXBAR_RX_INT_SET:
245         case TEGRA210_OPE_AXBAR_RX_INT_CLEAR:
246         case TEGRA210_OPE_AXBAR_RX_CIF_CTRL:
247
248         case TEGRA210_OPE_AXBAR_TX_INT_MASK:
249         case TEGRA210_OPE_AXBAR_TX_INT_SET:
250         case TEGRA210_OPE_AXBAR_TX_INT_CLEAR:
251         case TEGRA210_OPE_AXBAR_TX_CIF_CTRL:
252
253         case TEGRA210_OPE_ENABLE:
254         case TEGRA210_OPE_SOFT_RESET:
255         case TEGRA210_OPE_CG:
256         case TEGRA210_OPE_DIRECTION:
257                 return true;
258         default:
259                 return false;
260         };
261 }
262
263 static bool tegra210_ope_rd_reg(struct device *dev, unsigned int reg)
264 {
265         switch (reg) {
266         case TEGRA210_OPE_AXBAR_RX_STATUS:
267         case TEGRA210_OPE_AXBAR_RX_INT_STATUS:
268         case TEGRA210_OPE_AXBAR_RX_INT_MASK:
269         case TEGRA210_OPE_AXBAR_RX_INT_SET:
270         case TEGRA210_OPE_AXBAR_RX_INT_CLEAR:
271         case TEGRA210_OPE_AXBAR_RX_CIF_CTRL:
272
273         case TEGRA210_OPE_AXBAR_TX_STATUS:
274         case TEGRA210_OPE_AXBAR_TX_INT_STATUS:
275         case TEGRA210_OPE_AXBAR_TX_INT_MASK:
276         case TEGRA210_OPE_AXBAR_TX_INT_SET:
277         case TEGRA210_OPE_AXBAR_TX_INT_CLEAR:
278         case TEGRA210_OPE_AXBAR_TX_CIF_CTRL:
279
280         case TEGRA210_OPE_ENABLE:
281         case TEGRA210_OPE_SOFT_RESET:
282         case TEGRA210_OPE_CG:
283         case TEGRA210_OPE_STATUS:
284         case TEGRA210_OPE_INT_STATUS:
285         case TEGRA210_OPE_DIRECTION:
286                 return true;
287         default:
288                 return false;
289         };
290 }
291
292 static bool tegra210_ope_volatile_reg(struct device *dev, unsigned int reg)
293 {
294         switch (reg) {
295         case TEGRA210_OPE_AXBAR_RX_STATUS:
296         case TEGRA210_OPE_AXBAR_RX_INT_SET:
297         case TEGRA210_OPE_AXBAR_RX_INT_STATUS:
298
299         case TEGRA210_OPE_AXBAR_TX_STATUS:
300         case TEGRA210_OPE_AXBAR_TX_INT_SET:
301         case TEGRA210_OPE_AXBAR_TX_INT_STATUS:
302
303         case TEGRA210_OPE_SOFT_RESET:
304         case TEGRA210_OPE_STATUS:
305         case TEGRA210_OPE_INT_STATUS:
306                 return true;
307         default:
308                 return false;
309         };
310 }
311
312 static const struct regmap_config tegra210_ope_regmap_config = {
313         .reg_bits = 32,
314         .reg_stride = 4,
315         .val_bits = 32,
316         .max_register = TEGRA210_OPE_DIRECTION,
317         .writeable_reg = tegra210_ope_wr_reg,
318         .readable_reg = tegra210_ope_rd_reg,
319         .volatile_reg = tegra210_ope_volatile_reg,
320         .reg_defaults = tegra210_ope_reg_defaults,
321         .num_reg_defaults = ARRAY_SIZE(tegra210_ope_reg_defaults),
322         .cache_type = REGCACHE_FLAT,
323 };
324
325 static const struct tegra210_ope_soc_data soc_data_tegra210 = {
326         .set_audio_cif = tegra210_xbar_set_cif,
327         .peq_soc_data = {
328                 .init = tegra210_peq_init,
329                 .codec_init = tegra210_peq_codec_init,
330         },
331         .mbdrc_soc_data = {
332                 .init = tegra210_mbdrc_init,
333                 .codec_init = tegra210_mbdrc_codec_init,
334         },
335 };
336
337 static const struct of_device_id tegra210_ope_of_match[] = {
338         { .compatible = "nvidia,tegra210-ope", .data = &soc_data_tegra210 },
339         {},
340 };
341
342 static int tegra210_ope_platform_probe(struct platform_device *pdev)
343 {
344         struct tegra210_ope *ope;
345         struct resource *mem, *memregion;
346         void __iomem *regs;
347         int ret = 0;
348         const struct of_device_id *match;
349         struct tegra210_ope_soc_data *soc_data;
350
351         pr_info("OPE platform probe\n");
352
353         match = of_match_device(tegra210_ope_of_match, &pdev->dev);
354         if (!match) {
355                 dev_err(&pdev->dev, "Error: No device match found\n");
356                 ret = -ENODEV;
357                 goto err;
358         }
359         soc_data = (struct tegra210_ope_soc_data *)match->data;
360
361         ope = devm_kzalloc(&pdev->dev, sizeof(struct tegra210_ope), GFP_KERNEL);
362         if (!ope) {
363                 dev_err(&pdev->dev, "Can't allocate ope\n");
364                 ret = -ENOMEM;
365                 goto err;
366         }
367
368         ope->soc_data = soc_data;
369         ope->is_shutdown = false;
370
371         mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
372         if (!mem) {
373                 dev_err(&pdev->dev, "No memory resource\n");
374                 ret = -ENODEV;
375                 goto err;
376         }
377
378         memregion = devm_request_mem_region(&pdev->dev, mem->start,
379                                             resource_size(mem), pdev->name);
380         if (!memregion) {
381                 dev_err(&pdev->dev, "Memory region already claimed\n");
382                 ret = -EBUSY;
383                 goto err;
384         }
385
386         regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
387         if (!regs) {
388                 dev_err(&pdev->dev, "ioremap failed\n");
389                 ret = -ENOMEM;
390                 goto err;
391         }
392
393         ope->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
394                                             &tegra210_ope_regmap_config);
395         if (IS_ERR(ope->regmap)) {
396                 dev_err(&pdev->dev, "regmap init failed\n");
397                 ret = PTR_ERR(ope->regmap);
398                 goto err;
399         }
400         regcache_cache_only(ope->regmap, true);
401
402         dev_set_drvdata(&pdev->dev, ope);
403
404         ret = ope->soc_data->peq_soc_data.init(pdev,
405                                 TEGRA210_PEQ_IORESOURCE_MEM);
406         if (ret < 0) {
407                 dev_err(&pdev->dev, "peq init failed\n");
408                 goto err;
409         }
410         regcache_cache_only(ope->peq_regmap, true);
411
412         ret = ope->soc_data->mbdrc_soc_data.init(pdev,
413                                 TEGRA210_MBDRC_IORESOURCE_MEM);
414         if (ret < 0) {
415                 dev_err(&pdev->dev, "mbdrc init failed\n");
416                 goto err;
417         }
418         regcache_cache_only(ope->mbdrc_regmap, true);
419
420         if (of_property_read_u32(pdev->dev.of_node,
421                                 "nvidia,ahub-ope-id",
422                                 &pdev->dev.id) < 0) {
423                 dev_err(&pdev->dev,
424                         "Missing property nvidia,ahub-ope-id\n");
425                 ret = -ENODEV;
426                 goto err;
427         }
428
429         pm_runtime_enable(&pdev->dev);
430         if (!pm_runtime_enabled(&pdev->dev)) {
431                 ret = tegra210_ope_runtime_resume(&pdev->dev);
432                 if (ret)
433                         goto err_pm_disable;
434         }
435
436         ret = snd_soc_register_codec(&pdev->dev, &tegra210_ope_codec,
437                                      tegra210_ope_dais,
438                                      ARRAY_SIZE(tegra210_ope_dais));
439         if (ret != 0) {
440                 dev_err(&pdev->dev, "Could not register CODEC: %d\n", ret);
441                 goto err_suspend;
442         }
443
444         pr_info("OPE platform probe successful\n");
445
446         return 0;
447
448 err_suspend:
449         if (!pm_runtime_status_suspended(&pdev->dev))
450                 tegra210_ope_runtime_suspend(&pdev->dev);
451 err_pm_disable:
452         pm_runtime_disable(&pdev->dev);
453 err:
454         return ret;
455 }
456
457 static void tegra210_ope_platform_shutdown(struct platform_device *pdev)
458 {
459         struct tegra210_ope *ope = dev_get_drvdata(&pdev->dev);
460
461         ope->is_shutdown = true;
462 }
463
464 static int tegra210_ope_platform_remove(struct platform_device *pdev)
465 {
466         snd_soc_unregister_codec(&pdev->dev);
467
468         pm_runtime_disable(&pdev->dev);
469         if (!pm_runtime_status_suspended(&pdev->dev))
470                 tegra210_ope_runtime_suspend(&pdev->dev);
471
472         return 0;
473 }
474
475 static const struct dev_pm_ops tegra210_ope_pm_ops = {
476         SET_RUNTIME_PM_OPS(tegra210_ope_runtime_suspend,
477                            tegra210_ope_runtime_resume, NULL)
478         SET_LATE_SYSTEM_SLEEP_PM_OPS(tegra210_ope_suspend, tegra210_ope_resume)
479 };
480
481 static struct platform_driver tegra210_ope_driver = {
482         .driver = {
483                 .name = DRV_NAME,
484                 .owner = THIS_MODULE,
485                 .of_match_table = tegra210_ope_of_match,
486                 .pm = &tegra210_ope_pm_ops,
487         },
488         .probe = tegra210_ope_platform_probe,
489         .remove = tegra210_ope_platform_remove,
490         .shutdown = tegra210_ope_platform_shutdown,
491 };
492 module_platform_driver(tegra210_ope_driver)
493
494 MODULE_AUTHOR("Sumit Bhattacharya <sumitb@nvidia.com>");
495 MODULE_DESCRIPTION("Tegra210 OPE ASoC driver");
496 MODULE_LICENSE("GPL");
497 MODULE_ALIAS("platform:" DRV_NAME);
498 MODULE_DEVICE_TABLE(of, tegra210_ope_of_match);