2 * Copyright (C) STMicroelectronics SA 2015
3 * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
4 * for STMicroelectronics.
5 * License terms: GNU General Public License (GPL), version 2
8 #include <linux/module.h>
9 #include <linux/pinctrl/consumer.h>
14 * sti_uniperiph_dai_create_ctrl
15 * This function is used to create Ctrl associated to DAI but also pcm device.
16 * Request is done by front end to associate ctrl with pcm device id
18 static int sti_uniperiph_dai_create_ctrl(struct snd_soc_dai
*dai
)
20 struct sti_uniperiph_data
*priv
= snd_soc_dai_get_drvdata(dai
);
21 struct uniperif
*uni
= priv
->dai_data
.uni
;
22 struct snd_kcontrol_new
*ctrl
;
28 for (i
= 0; i
< uni
->num_ctrls
; i
++) {
30 * Several Control can have same name. Controls are indexed on
31 * Uniperipheral instance ID
33 ctrl
= &uni
->snd_ctrls
[i
];
34 ctrl
->index
= uni
->info
->id
;
35 ctrl
->device
= uni
->info
->id
;
38 return snd_soc_add_dai_controls(dai
, uni
->snd_ctrls
, uni
->num_ctrls
);
44 int sti_uniperiph_dai_hw_params(struct snd_pcm_substream
*substream
,
45 struct snd_pcm_hw_params
*params
,
46 struct snd_soc_dai
*dai
)
48 struct snd_dmaengine_dai_dma_data
*dma_data
;
51 transfer_size
= params_channels(params
) * UNIPERIF_FIFO_FRAMES
;
53 dma_data
= snd_soc_dai_get_dma_data(dai
, substream
);
54 dma_data
->maxburst
= transfer_size
;
59 int sti_uniperiph_dai_set_fmt(struct snd_soc_dai
*dai
, unsigned int fmt
)
61 struct sti_uniperiph_data
*priv
= snd_soc_dai_get_drvdata(dai
);
63 priv
->dai_data
.uni
->daifmt
= fmt
;
68 static int sti_uniperiph_dai_suspend(struct snd_soc_dai
*dai
)
70 struct sti_uniperiph_data
*priv
= snd_soc_dai_get_drvdata(dai
);
71 struct uniperif
*uni
= priv
->dai_data
.uni
;
74 /* The uniperipheral should be in stopped state */
75 if (uni
->state
!= UNIPERIF_STATE_STOPPED
) {
76 dev_err(uni
->dev
, "%s: invalid uni state( %d)",
77 __func__
, (int)uni
->state
);
81 /* Pinctrl: switch pinstate to sleep */
82 ret
= pinctrl_pm_select_sleep_state(uni
->dev
);
84 dev_err(uni
->dev
, "%s: failed to select pinctrl state",
90 static int sti_uniperiph_dai_resume(struct snd_soc_dai
*dai
)
92 struct sti_uniperiph_data
*priv
= snd_soc_dai_get_drvdata(dai
);
93 struct uniperif
*uni
= priv
->dai_data
.uni
;
96 if (of_device_is_compatible(dai
->dev
->of_node
, "st,sti-uni-player")) {
97 ret
= uni_player_resume(uni
);
102 /* pinctrl: switch pinstate to default */
103 ret
= pinctrl_pm_select_default_state(uni
->dev
);
105 dev_err(uni
->dev
, "%s: failed to select pinctrl state",
111 static int sti_uniperiph_dai_probe(struct snd_soc_dai
*dai
)
113 struct sti_uniperiph_data
*priv
= snd_soc_dai_get_drvdata(dai
);
114 struct sti_uniperiph_dai
*dai_data
= &priv
->dai_data
;
117 if (of_device_is_compatible(dai
->dev
->of_node
, "st,sti-uni-player"))
118 snd_soc_dai_init_dma_data(dai
, &dai_data
->dma_data
, NULL
);
120 snd_soc_dai_init_dma_data(dai
, NULL
, &dai_data
->dma_data
);
122 dai_data
->dma_data
.addr
= dai_data
->uni
->fifo_phys_address
;
123 dai_data
->dma_data
.addr_width
= DMA_SLAVE_BUSWIDTH_4_BYTES
;
125 return sti_uniperiph_dai_create_ctrl(dai
);
128 static const struct snd_soc_dai_driver sti_uniperiph_dai_template
= {
129 .probe
= sti_uniperiph_dai_probe
,
130 .suspend
= sti_uniperiph_dai_suspend
,
131 .resume
= sti_uniperiph_dai_resume
134 static const struct snd_soc_component_driver sti_uniperiph_dai_component
= {
135 .name
= "sti_cpu_dai",
138 static int sti_uniperiph_cpu_dai_of(struct device_node
*node
,
139 struct sti_uniperiph_data
*priv
)
143 struct device
*dev
= &priv
->pdev
->dev
;
144 struct sti_uniperiph_dai
*dai_data
= &priv
->dai_data
;
145 struct snd_soc_dai_driver
*dai
= priv
->dai
;
146 struct snd_soc_pcm_stream
*stream
;
147 struct uniperif
*uni
;
149 uni
= devm_kzalloc(dev
, sizeof(*uni
), GFP_KERNEL
);
153 *dai
= sti_uniperiph_dai_template
;
154 ret
= of_property_read_string(node
, "dai-name", &str
);
156 dev_err(dev
, "%s: dai name missing.\n", __func__
);
162 uni
->mem_region
= platform_get_resource(priv
->pdev
, IORESOURCE_MEM
, 0);
164 if (!uni
->mem_region
) {
165 dev_err(dev
, "Failed to get memory resource");
169 uni
->base
= devm_ioremap_resource(dev
, uni
->mem_region
);
171 if (IS_ERR(uni
->base
))
172 return PTR_ERR(uni
->base
);
174 uni
->fifo_phys_address
= uni
->mem_region
->start
+
175 UNIPERIF_FIFO_DATA_OFFSET(uni
);
177 uni
->irq
= platform_get_irq(priv
->pdev
, 0);
179 dev_err(dev
, "Failed to get IRQ resource");
185 if (of_device_is_compatible(node
, "st,sti-uni-player")) {
186 uni_player_init(priv
->pdev
, uni
);
187 stream
= &dai
->playback
;
189 uni_reader_init(priv
->pdev
, uni
);
190 stream
= &dai
->capture
;
192 dai
->ops
= uni
->dai_ops
;
194 stream
->stream_name
= dai
->name
;
195 stream
->channels_min
= uni
->hw
->channels_min
;
196 stream
->channels_max
= uni
->hw
->channels_max
;
197 stream
->rates
= uni
->hw
->rates
;
198 stream
->formats
= uni
->hw
->formats
;
203 static const struct snd_dmaengine_pcm_config dmaengine_pcm_config
= {
204 .prepare_slave_config
= snd_dmaengine_pcm_prepare_slave_config
,
207 static int sti_uniperiph_probe(struct platform_device
*pdev
)
209 struct sti_uniperiph_data
*priv
;
210 struct device_node
*node
= pdev
->dev
.of_node
;
213 /* Allocate the private data and the CPU_DAI array */
214 priv
= devm_kzalloc(&pdev
->dev
, sizeof(*priv
), GFP_KERNEL
);
217 priv
->dai
= devm_kzalloc(&pdev
->dev
, sizeof(*priv
->dai
), GFP_KERNEL
);
223 ret
= sti_uniperiph_cpu_dai_of(node
, priv
);
225 dev_set_drvdata(&pdev
->dev
, priv
);
227 ret
= devm_snd_soc_register_component(&pdev
->dev
,
228 &sti_uniperiph_dai_component
,
233 return devm_snd_dmaengine_pcm_register(&pdev
->dev
,
234 &dmaengine_pcm_config
, 0);
237 static const struct of_device_id snd_soc_sti_match
[] = {
238 { .compatible
= "st,sti-uni-player", },
239 { .compatible
= "st,sti-uni-reader", },
243 static struct platform_driver sti_uniperiph_driver
= {
245 .name
= "sti-uniperiph-dai",
246 .of_match_table
= snd_soc_sti_match
,
248 .probe
= sti_uniperiph_probe
,
250 module_platform_driver(sti_uniperiph_driver
);
252 MODULE_DESCRIPTION("uniperipheral DAI driver");
253 MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
254 MODULE_LICENSE("GPL v2");