xref: /openbmc/linux/sound/soc/sti/sti_uniperif.c (revision bf9185dd)
1f3bd847eSArnaud Pouliquen /*
2f3bd847eSArnaud Pouliquen  * Copyright (C) STMicroelectronics SA 2015
3f3bd847eSArnaud Pouliquen  * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
4f3bd847eSArnaud Pouliquen  *          for STMicroelectronics.
5f3bd847eSArnaud Pouliquen  * License terms:  GNU General Public License (GPL), version 2
6f3bd847eSArnaud Pouliquen  */
7f3bd847eSArnaud Pouliquen 
8f3bd847eSArnaud Pouliquen #include <linux/module.h>
9*bf9185ddSMark Brown #include <linux/pinctrl/consumer.h>
10f3bd847eSArnaud Pouliquen 
11f3bd847eSArnaud Pouliquen #include "uniperif.h"
12f3bd847eSArnaud Pouliquen 
13f3bd847eSArnaud Pouliquen /*
14f3bd847eSArnaud Pouliquen  * sti_uniperiph_dai_create_ctrl
15f3bd847eSArnaud Pouliquen  * This function is used to create Ctrl associated to DAI but also pcm device.
16f3bd847eSArnaud Pouliquen  * Request is done by front end to associate ctrl with pcm device id
17f3bd847eSArnaud Pouliquen  */
18f2da4542Skbuild test robot static int sti_uniperiph_dai_create_ctrl(struct snd_soc_dai *dai)
19f3bd847eSArnaud Pouliquen {
20f3bd847eSArnaud Pouliquen 	struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
21f3bd847eSArnaud Pouliquen 	struct uniperif *uni = priv->dai_data.uni;
22f3bd847eSArnaud Pouliquen 	struct snd_kcontrol_new *ctrl;
23f3bd847eSArnaud Pouliquen 	int i;
24f3bd847eSArnaud Pouliquen 
25f3bd847eSArnaud Pouliquen 	if (!uni->num_ctrls)
26f3bd847eSArnaud Pouliquen 		return 0;
27f3bd847eSArnaud Pouliquen 
28f3bd847eSArnaud Pouliquen 	for (i = 0; i < uni->num_ctrls; i++) {
29f3bd847eSArnaud Pouliquen 		/*
30f3bd847eSArnaud Pouliquen 		 * Several Control can have same name. Controls are indexed on
31f3bd847eSArnaud Pouliquen 		 * Uniperipheral instance ID
32f3bd847eSArnaud Pouliquen 		 */
33f3bd847eSArnaud Pouliquen 		ctrl = &uni->snd_ctrls[i];
34f3bd847eSArnaud Pouliquen 		ctrl->index = uni->info->id;
35f3bd847eSArnaud Pouliquen 		ctrl->device = uni->info->id;
36f3bd847eSArnaud Pouliquen 	}
37f3bd847eSArnaud Pouliquen 
38f3bd847eSArnaud Pouliquen 	return snd_soc_add_dai_controls(dai, uni->snd_ctrls, uni->num_ctrls);
39f3bd847eSArnaud Pouliquen }
40f3bd847eSArnaud Pouliquen 
41f3bd847eSArnaud Pouliquen /*
42f3bd847eSArnaud Pouliquen  * DAI
43f3bd847eSArnaud Pouliquen  */
44f3bd847eSArnaud Pouliquen int sti_uniperiph_dai_hw_params(struct snd_pcm_substream *substream,
45f3bd847eSArnaud Pouliquen 				struct snd_pcm_hw_params *params,
46f3bd847eSArnaud Pouliquen 				struct snd_soc_dai *dai)
47f3bd847eSArnaud Pouliquen {
48f3bd847eSArnaud Pouliquen 	struct snd_dmaengine_dai_dma_data *dma_data;
49f3bd847eSArnaud Pouliquen 	int transfer_size;
50f3bd847eSArnaud Pouliquen 
51f3bd847eSArnaud Pouliquen 	transfer_size = params_channels(params) * UNIPERIF_FIFO_FRAMES;
52f3bd847eSArnaud Pouliquen 
53f3bd847eSArnaud Pouliquen 	dma_data = snd_soc_dai_get_dma_data(dai, substream);
54f3bd847eSArnaud Pouliquen 	dma_data->maxburst = transfer_size;
55f3bd847eSArnaud Pouliquen 
56f3bd847eSArnaud Pouliquen 	return 0;
57f3bd847eSArnaud Pouliquen }
58f3bd847eSArnaud Pouliquen 
59f3bd847eSArnaud Pouliquen int sti_uniperiph_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
60f3bd847eSArnaud Pouliquen {
61f3bd847eSArnaud Pouliquen 	struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
62f3bd847eSArnaud Pouliquen 
63f3bd847eSArnaud Pouliquen 	priv->dai_data.uni->daifmt = fmt;
64f3bd847eSArnaud Pouliquen 
65f3bd847eSArnaud Pouliquen 	return 0;
66f3bd847eSArnaud Pouliquen }
67f3bd847eSArnaud Pouliquen 
68f3bd847eSArnaud Pouliquen static int sti_uniperiph_dai_suspend(struct snd_soc_dai *dai)
69f3bd847eSArnaud Pouliquen {
70f3bd847eSArnaud Pouliquen 	struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
71f3bd847eSArnaud Pouliquen 	struct uniperif *uni = priv->dai_data.uni;
72f3bd847eSArnaud Pouliquen 	int ret;
73f3bd847eSArnaud Pouliquen 
74f3bd847eSArnaud Pouliquen 	/* The uniperipheral should be in stopped state */
75f3bd847eSArnaud Pouliquen 	if (uni->state != UNIPERIF_STATE_STOPPED) {
76f3bd847eSArnaud Pouliquen 		dev_err(uni->dev, "%s: invalid uni state( %d)",
77f3bd847eSArnaud Pouliquen 			__func__, (int)uni->state);
78f3bd847eSArnaud Pouliquen 		return -EBUSY;
79f3bd847eSArnaud Pouliquen 	}
80f3bd847eSArnaud Pouliquen 
81f3bd847eSArnaud Pouliquen 	/* Pinctrl: switch pinstate to sleep */
82f3bd847eSArnaud Pouliquen 	ret = pinctrl_pm_select_sleep_state(uni->dev);
83f3bd847eSArnaud Pouliquen 	if (ret)
84f3bd847eSArnaud Pouliquen 		dev_err(uni->dev, "%s: failed to select pinctrl state",
85f3bd847eSArnaud Pouliquen 			__func__);
86f3bd847eSArnaud Pouliquen 
87f3bd847eSArnaud Pouliquen 	return ret;
88f3bd847eSArnaud Pouliquen }
89f3bd847eSArnaud Pouliquen 
90f3bd847eSArnaud Pouliquen static int sti_uniperiph_dai_resume(struct snd_soc_dai *dai)
91f3bd847eSArnaud Pouliquen {
92f3bd847eSArnaud Pouliquen 	struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
93f3bd847eSArnaud Pouliquen 	struct uniperif *uni = priv->dai_data.uni;
94f3bd847eSArnaud Pouliquen 	int ret;
95f3bd847eSArnaud Pouliquen 
96f3bd847eSArnaud Pouliquen 	if (of_device_is_compatible(dai->dev->of_node, "st,sti-uni-player")) {
97f3bd847eSArnaud Pouliquen 		ret = uni_player_resume(uni);
98f3bd847eSArnaud Pouliquen 		if (ret)
99f3bd847eSArnaud Pouliquen 			return ret;
100f3bd847eSArnaud Pouliquen 	}
101f3bd847eSArnaud Pouliquen 
102f3bd847eSArnaud Pouliquen 	/* pinctrl: switch pinstate to default */
103f3bd847eSArnaud Pouliquen 	ret = pinctrl_pm_select_default_state(uni->dev);
104f3bd847eSArnaud Pouliquen 	if (ret)
105f3bd847eSArnaud Pouliquen 		dev_err(uni->dev, "%s: failed to select pinctrl state",
106f3bd847eSArnaud Pouliquen 			__func__);
107f3bd847eSArnaud Pouliquen 
108f3bd847eSArnaud Pouliquen 	return ret;
109f3bd847eSArnaud Pouliquen }
110f3bd847eSArnaud Pouliquen 
111f3bd847eSArnaud Pouliquen static int sti_uniperiph_dai_probe(struct snd_soc_dai *dai)
112f3bd847eSArnaud Pouliquen {
113f3bd847eSArnaud Pouliquen 	struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
114f3bd847eSArnaud Pouliquen 	struct sti_uniperiph_dai *dai_data = &priv->dai_data;
115f3bd847eSArnaud Pouliquen 
116f3bd847eSArnaud Pouliquen 	/* DMA settings*/
117f3bd847eSArnaud Pouliquen 	if (of_device_is_compatible(dai->dev->of_node, "st,sti-uni-player"))
118f3bd847eSArnaud Pouliquen 		snd_soc_dai_init_dma_data(dai, &dai_data->dma_data, NULL);
119f3bd847eSArnaud Pouliquen 	else
120f3bd847eSArnaud Pouliquen 		snd_soc_dai_init_dma_data(dai, NULL, &dai_data->dma_data);
121f3bd847eSArnaud Pouliquen 
122f3bd847eSArnaud Pouliquen 	dai_data->dma_data.addr = dai_data->uni->fifo_phys_address;
123f3bd847eSArnaud Pouliquen 	dai_data->dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
124f3bd847eSArnaud Pouliquen 
125f3bd847eSArnaud Pouliquen 	return sti_uniperiph_dai_create_ctrl(dai);
126f3bd847eSArnaud Pouliquen }
127f3bd847eSArnaud Pouliquen 
128f3bd847eSArnaud Pouliquen static const struct snd_soc_dai_driver sti_uniperiph_dai_template = {
129f3bd847eSArnaud Pouliquen 	.probe = sti_uniperiph_dai_probe,
130f3bd847eSArnaud Pouliquen 	.suspend = sti_uniperiph_dai_suspend,
131f3bd847eSArnaud Pouliquen 	.resume = sti_uniperiph_dai_resume
132f3bd847eSArnaud Pouliquen };
133f3bd847eSArnaud Pouliquen 
134f3bd847eSArnaud Pouliquen static const struct snd_soc_component_driver sti_uniperiph_dai_component = {
135f3bd847eSArnaud Pouliquen 	.name = "sti_cpu_dai",
136f3bd847eSArnaud Pouliquen };
137f3bd847eSArnaud Pouliquen 
138f3bd847eSArnaud Pouliquen static int sti_uniperiph_cpu_dai_of(struct device_node *node,
139f3bd847eSArnaud Pouliquen 				    struct sti_uniperiph_data *priv)
140f3bd847eSArnaud Pouliquen {
141f3bd847eSArnaud Pouliquen 	const char *str;
142f3bd847eSArnaud Pouliquen 	int ret;
143f3bd847eSArnaud Pouliquen 	struct device *dev = &priv->pdev->dev;
144f3bd847eSArnaud Pouliquen 	struct sti_uniperiph_dai *dai_data = &priv->dai_data;
145f3bd847eSArnaud Pouliquen 	struct snd_soc_dai_driver *dai = priv->dai;
146f3bd847eSArnaud Pouliquen 	struct snd_soc_pcm_stream *stream;
147f3bd847eSArnaud Pouliquen 	struct uniperif *uni;
148f3bd847eSArnaud Pouliquen 
149f3bd847eSArnaud Pouliquen 	uni = devm_kzalloc(dev, sizeof(*uni), GFP_KERNEL);
150f3bd847eSArnaud Pouliquen 	if (!uni)
151f3bd847eSArnaud Pouliquen 		return -ENOMEM;
152f3bd847eSArnaud Pouliquen 
153f3bd847eSArnaud Pouliquen 	*dai = sti_uniperiph_dai_template;
154f3bd847eSArnaud Pouliquen 	ret = of_property_read_string(node, "dai-name", &str);
155f3bd847eSArnaud Pouliquen 	if (ret < 0) {
156f3bd847eSArnaud Pouliquen 		dev_err(dev, "%s: dai name missing.\n", __func__);
157f3bd847eSArnaud Pouliquen 		return -EINVAL;
158f3bd847eSArnaud Pouliquen 	}
159f3bd847eSArnaud Pouliquen 	dai->name = str;
160f3bd847eSArnaud Pouliquen 
161f3bd847eSArnaud Pouliquen 	/* Get resources */
162f3bd847eSArnaud Pouliquen 	uni->mem_region = platform_get_resource(priv->pdev, IORESOURCE_MEM, 0);
163f3bd847eSArnaud Pouliquen 
164f3bd847eSArnaud Pouliquen 	if (!uni->mem_region) {
165f3bd847eSArnaud Pouliquen 		dev_err(dev, "Failed to get memory resource");
166f3bd847eSArnaud Pouliquen 		return -ENODEV;
167f3bd847eSArnaud Pouliquen 	}
168f3bd847eSArnaud Pouliquen 
169f3bd847eSArnaud Pouliquen 	uni->base = devm_ioremap_resource(dev, uni->mem_region);
170f3bd847eSArnaud Pouliquen 
171f3bd847eSArnaud Pouliquen 	if (IS_ERR(uni->base))
172f3bd847eSArnaud Pouliquen 		return PTR_ERR(uni->base);
173f3bd847eSArnaud Pouliquen 
174f3bd847eSArnaud Pouliquen 	uni->fifo_phys_address = uni->mem_region->start +
175f3bd847eSArnaud Pouliquen 				     UNIPERIF_FIFO_DATA_OFFSET(uni);
176f3bd847eSArnaud Pouliquen 
177f3bd847eSArnaud Pouliquen 	uni->irq = platform_get_irq(priv->pdev, 0);
178f3bd847eSArnaud Pouliquen 	if (!uni->irq < 0) {
179f3bd847eSArnaud Pouliquen 		dev_err(dev, "Failed to get IRQ resource");
180f3bd847eSArnaud Pouliquen 		return -ENXIO;
181f3bd847eSArnaud Pouliquen 	}
182f3bd847eSArnaud Pouliquen 
183f3bd847eSArnaud Pouliquen 	dai_data->uni = uni;
184f3bd847eSArnaud Pouliquen 
185f3bd847eSArnaud Pouliquen 	if (of_device_is_compatible(node, "st,sti-uni-player")) {
186f3bd847eSArnaud Pouliquen 		uni_player_init(priv->pdev, uni);
187f3bd847eSArnaud Pouliquen 		stream = &dai->playback;
188f3bd847eSArnaud Pouliquen 	} else {
189f3bd847eSArnaud Pouliquen 		uni_reader_init(priv->pdev, uni);
190f3bd847eSArnaud Pouliquen 		stream = &dai->capture;
191f3bd847eSArnaud Pouliquen 	}
192f3bd847eSArnaud Pouliquen 	dai->ops = uni->dai_ops;
193f3bd847eSArnaud Pouliquen 
194f3bd847eSArnaud Pouliquen 	stream->stream_name = dai->name;
195f3bd847eSArnaud Pouliquen 	stream->channels_min = uni->hw->channels_min;
196f3bd847eSArnaud Pouliquen 	stream->channels_max = uni->hw->channels_max;
197f3bd847eSArnaud Pouliquen 	stream->rates = uni->hw->rates;
198f3bd847eSArnaud Pouliquen 	stream->formats = uni->hw->formats;
199f3bd847eSArnaud Pouliquen 
200f3bd847eSArnaud Pouliquen 	return 0;
201f3bd847eSArnaud Pouliquen }
202f3bd847eSArnaud Pouliquen 
203f3bd847eSArnaud Pouliquen static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = {
204f3bd847eSArnaud Pouliquen 	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
205f3bd847eSArnaud Pouliquen };
206f3bd847eSArnaud Pouliquen 
207f3bd847eSArnaud Pouliquen static int sti_uniperiph_probe(struct platform_device *pdev)
208f3bd847eSArnaud Pouliquen {
209f3bd847eSArnaud Pouliquen 	struct sti_uniperiph_data *priv;
210f3bd847eSArnaud Pouliquen 	struct device_node *node = pdev->dev.of_node;
211f3bd847eSArnaud Pouliquen 	int ret;
212f3bd847eSArnaud Pouliquen 
213f3bd847eSArnaud Pouliquen 	/* Allocate the private data and the CPU_DAI array */
214f3bd847eSArnaud Pouliquen 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
215f3bd847eSArnaud Pouliquen 	if (!priv)
216f3bd847eSArnaud Pouliquen 		return -ENOMEM;
217f3bd847eSArnaud Pouliquen 	priv->dai = devm_kzalloc(&pdev->dev, sizeof(*priv->dai), GFP_KERNEL);
218f3bd847eSArnaud Pouliquen 	if (!priv->dai)
219f3bd847eSArnaud Pouliquen 		return -ENOMEM;
220f3bd847eSArnaud Pouliquen 
221f3bd847eSArnaud Pouliquen 	priv->pdev = pdev;
222f3bd847eSArnaud Pouliquen 
223f3bd847eSArnaud Pouliquen 	ret = sti_uniperiph_cpu_dai_of(node, priv);
224f3bd847eSArnaud Pouliquen 
225f3bd847eSArnaud Pouliquen 	dev_set_drvdata(&pdev->dev, priv);
226f3bd847eSArnaud Pouliquen 
227f3bd847eSArnaud Pouliquen 	ret = snd_soc_register_component(&pdev->dev,
228f3bd847eSArnaud Pouliquen 					 &sti_uniperiph_dai_component,
229f3bd847eSArnaud Pouliquen 					 priv->dai, 1);
230f3bd847eSArnaud Pouliquen 	if (ret < 0)
231f3bd847eSArnaud Pouliquen 		return ret;
232f3bd847eSArnaud Pouliquen 
233f3bd847eSArnaud Pouliquen 	return devm_snd_dmaengine_pcm_register(&pdev->dev,
234f3bd847eSArnaud Pouliquen 					       &dmaengine_pcm_config, 0);
235f3bd847eSArnaud Pouliquen }
236f3bd847eSArnaud Pouliquen 
237f3bd847eSArnaud Pouliquen static const struct of_device_id snd_soc_sti_match[] = {
238f3bd847eSArnaud Pouliquen 	{ .compatible = "st,sti-uni-player", },
239f3bd847eSArnaud Pouliquen 	{ .compatible = "st,sti-uni-reader", },
240f3bd847eSArnaud Pouliquen 	{},
241f3bd847eSArnaud Pouliquen };
242f3bd847eSArnaud Pouliquen 
243f3bd847eSArnaud Pouliquen static struct platform_driver sti_uniperiph_driver = {
244f3bd847eSArnaud Pouliquen 	.driver = {
245f3bd847eSArnaud Pouliquen 		.name = "sti-uniperiph-dai",
246f3bd847eSArnaud Pouliquen 		.of_match_table = snd_soc_sti_match,
247f3bd847eSArnaud Pouliquen 	},
248f3bd847eSArnaud Pouliquen 	.probe = sti_uniperiph_probe,
249f3bd847eSArnaud Pouliquen };
250f3bd847eSArnaud Pouliquen module_platform_driver(sti_uniperiph_driver);
251f3bd847eSArnaud Pouliquen 
252f3bd847eSArnaud Pouliquen MODULE_DESCRIPTION("uniperipheral DAI driver");
253f3bd847eSArnaud Pouliquen MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
254f3bd847eSArnaud Pouliquen MODULE_LICENSE("GPL v2");
255