Commit | Line | Data |
---|---|---|
97fb5e8d | 1 | // SPDX-License-Identifier: GPL-2.0-only |
79119c79 KW |
2 | /* |
3 | * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. | |
4 | * | |
79119c79 KW |
5 | * storm.c -- ALSA SoC machine driver for QTi ipq806x-based Storm board |
6 | */ | |
7 | ||
8 | #include <linux/device.h> | |
9 | #include <linux/module.h> | |
10 | #include <linux/of.h> | |
11 | #include <linux/mod_devicetable.h> | |
12 | #include <linux/platform_device.h> | |
13 | #include <sound/pcm.h> | |
14 | #include <sound/pcm_params.h> | |
15 | #include <sound/soc.h> | |
16 | ||
17 | #define STORM_SYSCLK_MULT 4 | |
18 | ||
19 | static int storm_ops_hw_params(struct snd_pcm_substream *substream, | |
20 | struct snd_pcm_hw_params *params) | |
21 | { | |
22 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | |
23 | struct snd_soc_card *card = soc_runtime->card; | |
24 | snd_pcm_format_t format = params_format(params); | |
25 | unsigned int rate = params_rate(params); | |
26 | unsigned int sysclk_freq; | |
27 | int bitwidth, ret; | |
28 | ||
29 | bitwidth = snd_pcm_format_width(format); | |
30 | if (bitwidth < 0) { | |
b6e643ad | 31 | dev_err(card->dev, "invalid bit width given: %d\n", bitwidth); |
79119c79 KW |
32 | return bitwidth; |
33 | } | |
34 | ||
35 | /* | |
36 | * as the CPU DAI is the I2S bus master and no system clock is needed by | |
37 | * the MAX98357a DAC, simply set the system clock to be a constant | |
38 | * multiple of the bit clock for the clock divider | |
39 | */ | |
40 | sysclk_freq = rate * bitwidth * 2 * STORM_SYSCLK_MULT; | |
41 | ||
42 | ret = snd_soc_dai_set_sysclk(soc_runtime->cpu_dai, 0, sysclk_freq, 0); | |
43 | if (ret) { | |
b6e643ad BA |
44 | dev_err(card->dev, "error setting sysclk to %u: %d\n", |
45 | sysclk_freq, ret); | |
79119c79 KW |
46 | return ret; |
47 | } | |
48 | ||
49 | return 0; | |
50 | } | |
51 | ||
251fc7a5 | 52 | static const struct snd_soc_ops storm_soc_ops = { |
79119c79 KW |
53 | .hw_params = storm_ops_hw_params, |
54 | }; | |
55 | ||
fa350b77 KM |
56 | SND_SOC_DAILINK_DEFS(hifi, |
57 | DAILINK_COMP_ARRAY(COMP_EMPTY()), | |
9522e947 KM |
58 | DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "HiFi")), |
59 | DAILINK_COMP_ARRAY(COMP_EMPTY())); | |
fa350b77 | 60 | |
79119c79 KW |
61 | static struct snd_soc_dai_link storm_dai_link = { |
62 | .name = "Primary", | |
63 | .stream_name = "Primary", | |
79119c79 | 64 | .ops = &storm_soc_ops, |
fa350b77 | 65 | SND_SOC_DAILINK_REG(hifi), |
79119c79 KW |
66 | }; |
67 | ||
79119c79 KW |
68 | static int storm_parse_of(struct snd_soc_card *card) |
69 | { | |
70 | struct snd_soc_dai_link *dai_link = card->dai_link; | |
71 | struct device_node *np = card->dev->of_node; | |
72 | ||
fa350b77 KM |
73 | dai_link->cpus->of_node = of_parse_phandle(np, "cpu", 0); |
74 | if (!dai_link->cpus->of_node) { | |
b6e643ad | 75 | dev_err(card->dev, "error getting cpu phandle\n"); |
79119c79 KW |
76 | return -EINVAL; |
77 | } | |
9522e947 | 78 | dai_link->platforms->of_node = dai_link->cpus->of_node; |
79119c79 | 79 | |
fa350b77 KM |
80 | dai_link->codecs->of_node = of_parse_phandle(np, "codec", 0); |
81 | if (!dai_link->codecs->of_node) { | |
b6e643ad | 82 | dev_err(card->dev, "error getting codec phandle\n"); |
79119c79 KW |
83 | return -EINVAL; |
84 | } | |
85 | ||
86 | return 0; | |
87 | } | |
88 | ||
89 | static int storm_platform_probe(struct platform_device *pdev) | |
90 | { | |
21e904bc | 91 | struct snd_soc_card *card; |
79119c79 KW |
92 | int ret; |
93 | ||
21e904bc SK |
94 | card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL); |
95 | if (!card) | |
96 | return -ENOMEM; | |
97 | ||
79119c79 | 98 | card->dev = &pdev->dev; |
79119c79 KW |
99 | |
100 | ret = snd_soc_of_parse_card_name(card, "qcom,model"); | |
101 | if (ret) { | |
b6e643ad | 102 | dev_err(&pdev->dev, "error parsing card name: %d\n", ret); |
79119c79 KW |
103 | return ret; |
104 | } | |
105 | ||
106 | card->dai_link = &storm_dai_link; | |
107 | card->num_links = 1; | |
108 | ||
109 | ret = storm_parse_of(card); | |
110 | if (ret) { | |
b6e643ad | 111 | dev_err(&pdev->dev, "error resolving dai links: %d\n", ret); |
79119c79 KW |
112 | return ret; |
113 | } | |
114 | ||
115 | ret = devm_snd_soc_register_card(&pdev->dev, card); | |
2682c7c7 | 116 | if (ret) |
b6e643ad | 117 | dev_err(&pdev->dev, "error registering soundcard: %d\n", ret); |
79119c79 | 118 | |
2682c7c7 SK |
119 | return ret; |
120 | ||
79119c79 KW |
121 | } |
122 | ||
123 | #ifdef CONFIG_OF | |
124 | static const struct of_device_id storm_device_id[] = { | |
125 | { .compatible = "google,storm-audio" }, | |
126 | {}, | |
127 | }; | |
128 | MODULE_DEVICE_TABLE(of, storm_device_id); | |
129 | #endif | |
130 | ||
131 | static struct platform_driver storm_platform_driver = { | |
132 | .driver = { | |
133 | .name = "storm-audio", | |
134 | .of_match_table = | |
135 | of_match_ptr(storm_device_id), | |
136 | }, | |
137 | .probe = storm_platform_probe, | |
138 | }; | |
139 | module_platform_driver(storm_platform_driver); | |
140 | ||
141 | MODULE_DESCRIPTION("QTi IPQ806x-based Storm Machine Driver"); | |
142 | MODULE_LICENSE("GPL v2"); |