1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (C) STMicroelectronics SA 2015
4*4882a593Smuzhiyun * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
5*4882a593Smuzhiyun * for STMicroelectronics.
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include <linux/module.h>
9*4882a593Smuzhiyun #include <linux/pinctrl/consumer.h>
10*4882a593Smuzhiyun #include <linux/delay.h>
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include "uniperif.h"
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun /*
15*4882a593Smuzhiyun * User frame size shall be 2, 4, 6 or 8 32-bits words length
16*4882a593Smuzhiyun * (i.e. 8, 16, 24 or 32 bytes)
17*4882a593Smuzhiyun * This constraint comes from allowed values for
18*4882a593Smuzhiyun * UNIPERIF_I2S_FMT_NUM_CH register
19*4882a593Smuzhiyun */
20*4882a593Smuzhiyun #define UNIPERIF_MAX_FRAME_SZ 0x20
21*4882a593Smuzhiyun #define UNIPERIF_ALLOWED_FRAME_SZ (0x08 | 0x10 | 0x18 | UNIPERIF_MAX_FRAME_SZ)
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun struct sti_uniperiph_dev_data {
24*4882a593Smuzhiyun unsigned int id; /* Nb available player instances */
25*4882a593Smuzhiyun unsigned int version; /* player IP version */
26*4882a593Smuzhiyun unsigned int stream;
27*4882a593Smuzhiyun const char *dai_names;
28*4882a593Smuzhiyun enum uniperif_type type;
29*4882a593Smuzhiyun };
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun static const struct sti_uniperiph_dev_data sti_uniplayer_hdmi = {
32*4882a593Smuzhiyun .id = 0,
33*4882a593Smuzhiyun .version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0,
34*4882a593Smuzhiyun .stream = SNDRV_PCM_STREAM_PLAYBACK,
35*4882a593Smuzhiyun .dai_names = "Uni Player #0 (HDMI)",
36*4882a593Smuzhiyun .type = SND_ST_UNIPERIF_TYPE_HDMI
37*4882a593Smuzhiyun };
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun static const struct sti_uniperiph_dev_data sti_uniplayer_pcm_out = {
40*4882a593Smuzhiyun .id = 1,
41*4882a593Smuzhiyun .version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0,
42*4882a593Smuzhiyun .stream = SNDRV_PCM_STREAM_PLAYBACK,
43*4882a593Smuzhiyun .dai_names = "Uni Player #1 (PCM OUT)",
44*4882a593Smuzhiyun .type = SND_ST_UNIPERIF_TYPE_PCM | SND_ST_UNIPERIF_TYPE_TDM,
45*4882a593Smuzhiyun };
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun static const struct sti_uniperiph_dev_data sti_uniplayer_dac = {
48*4882a593Smuzhiyun .id = 2,
49*4882a593Smuzhiyun .version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0,
50*4882a593Smuzhiyun .stream = SNDRV_PCM_STREAM_PLAYBACK,
51*4882a593Smuzhiyun .dai_names = "Uni Player #2 (DAC)",
52*4882a593Smuzhiyun .type = SND_ST_UNIPERIF_TYPE_PCM,
53*4882a593Smuzhiyun };
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun static const struct sti_uniperiph_dev_data sti_uniplayer_spdif = {
56*4882a593Smuzhiyun .id = 3,
57*4882a593Smuzhiyun .version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0,
58*4882a593Smuzhiyun .stream = SNDRV_PCM_STREAM_PLAYBACK,
59*4882a593Smuzhiyun .dai_names = "Uni Player #3 (SPDIF)",
60*4882a593Smuzhiyun .type = SND_ST_UNIPERIF_TYPE_SPDIF
61*4882a593Smuzhiyun };
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun static const struct sti_uniperiph_dev_data sti_unireader_pcm_in = {
64*4882a593Smuzhiyun .id = 0,
65*4882a593Smuzhiyun .version = SND_ST_UNIPERIF_VERSION_UNI_RDR_1_0,
66*4882a593Smuzhiyun .stream = SNDRV_PCM_STREAM_CAPTURE,
67*4882a593Smuzhiyun .dai_names = "Uni Reader #0 (PCM IN)",
68*4882a593Smuzhiyun .type = SND_ST_UNIPERIF_TYPE_PCM | SND_ST_UNIPERIF_TYPE_TDM,
69*4882a593Smuzhiyun };
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun static const struct sti_uniperiph_dev_data sti_unireader_hdmi_in = {
72*4882a593Smuzhiyun .id = 1,
73*4882a593Smuzhiyun .version = SND_ST_UNIPERIF_VERSION_UNI_RDR_1_0,
74*4882a593Smuzhiyun .stream = SNDRV_PCM_STREAM_CAPTURE,
75*4882a593Smuzhiyun .dai_names = "Uni Reader #1 (HDMI IN)",
76*4882a593Smuzhiyun .type = SND_ST_UNIPERIF_TYPE_PCM,
77*4882a593Smuzhiyun };
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun static const struct of_device_id snd_soc_sti_match[] = {
80*4882a593Smuzhiyun { .compatible = "st,stih407-uni-player-hdmi",
81*4882a593Smuzhiyun .data = &sti_uniplayer_hdmi
82*4882a593Smuzhiyun },
83*4882a593Smuzhiyun { .compatible = "st,stih407-uni-player-pcm-out",
84*4882a593Smuzhiyun .data = &sti_uniplayer_pcm_out
85*4882a593Smuzhiyun },
86*4882a593Smuzhiyun { .compatible = "st,stih407-uni-player-dac",
87*4882a593Smuzhiyun .data = &sti_uniplayer_dac
88*4882a593Smuzhiyun },
89*4882a593Smuzhiyun { .compatible = "st,stih407-uni-player-spdif",
90*4882a593Smuzhiyun .data = &sti_uniplayer_spdif
91*4882a593Smuzhiyun },
92*4882a593Smuzhiyun { .compatible = "st,stih407-uni-reader-pcm_in",
93*4882a593Smuzhiyun .data = &sti_unireader_pcm_in
94*4882a593Smuzhiyun },
95*4882a593Smuzhiyun { .compatible = "st,stih407-uni-reader-hdmi",
96*4882a593Smuzhiyun .data = &sti_unireader_hdmi_in
97*4882a593Smuzhiyun },
98*4882a593Smuzhiyun {},
99*4882a593Smuzhiyun };
100*4882a593Smuzhiyun
sti_uniperiph_reset(struct uniperif * uni)101*4882a593Smuzhiyun int sti_uniperiph_reset(struct uniperif *uni)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun int count = 10;
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun /* Reset uniperipheral uni */
106*4882a593Smuzhiyun SET_UNIPERIF_SOFT_RST_SOFT_RST(uni);
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun if (uni->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) {
109*4882a593Smuzhiyun while (GET_UNIPERIF_SOFT_RST_SOFT_RST(uni) && count) {
110*4882a593Smuzhiyun udelay(5);
111*4882a593Smuzhiyun count--;
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun if (!count) {
116*4882a593Smuzhiyun dev_err(uni->dev, "Failed to reset uniperif\n");
117*4882a593Smuzhiyun return -EIO;
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun return 0;
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun
sti_uniperiph_set_tdm_slot(struct snd_soc_dai * dai,unsigned int tx_mask,unsigned int rx_mask,int slots,int slot_width)123*4882a593Smuzhiyun int sti_uniperiph_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
124*4882a593Smuzhiyun unsigned int rx_mask, int slots,
125*4882a593Smuzhiyun int slot_width)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
128*4882a593Smuzhiyun struct uniperif *uni = priv->dai_data.uni;
129*4882a593Smuzhiyun int i, frame_size, avail_slots;
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun if (!UNIPERIF_TYPE_IS_TDM(uni)) {
132*4882a593Smuzhiyun dev_err(uni->dev, "cpu dai not in tdm mode\n");
133*4882a593Smuzhiyun return -EINVAL;
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun /* store info in unip context */
137*4882a593Smuzhiyun uni->tdm_slot.slots = slots;
138*4882a593Smuzhiyun uni->tdm_slot.slot_width = slot_width;
139*4882a593Smuzhiyun /* unip is unidirectionnal */
140*4882a593Smuzhiyun uni->tdm_slot.mask = (tx_mask != 0) ? tx_mask : rx_mask;
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun /* number of available timeslots */
143*4882a593Smuzhiyun for (i = 0, avail_slots = 0; i < uni->tdm_slot.slots; i++) {
144*4882a593Smuzhiyun if ((uni->tdm_slot.mask >> i) & 0x01)
145*4882a593Smuzhiyun avail_slots++;
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun uni->tdm_slot.avail_slots = avail_slots;
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun /* frame size in bytes */
150*4882a593Smuzhiyun frame_size = uni->tdm_slot.avail_slots * uni->tdm_slot.slot_width / 8;
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun /* check frame size is allowed */
153*4882a593Smuzhiyun if ((frame_size > UNIPERIF_MAX_FRAME_SZ) ||
154*4882a593Smuzhiyun (frame_size & ~(int)UNIPERIF_ALLOWED_FRAME_SZ)) {
155*4882a593Smuzhiyun dev_err(uni->dev, "frame size not allowed: %d bytes\n",
156*4882a593Smuzhiyun frame_size);
157*4882a593Smuzhiyun return -EINVAL;
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun return 0;
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun
sti_uniperiph_fix_tdm_chan(struct snd_pcm_hw_params * params,struct snd_pcm_hw_rule * rule)163*4882a593Smuzhiyun int sti_uniperiph_fix_tdm_chan(struct snd_pcm_hw_params *params,
164*4882a593Smuzhiyun struct snd_pcm_hw_rule *rule)
165*4882a593Smuzhiyun {
166*4882a593Smuzhiyun struct uniperif *uni = rule->private;
167*4882a593Smuzhiyun struct snd_interval t;
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun t.min = uni->tdm_slot.avail_slots;
170*4882a593Smuzhiyun t.max = uni->tdm_slot.avail_slots;
171*4882a593Smuzhiyun t.openmin = 0;
172*4882a593Smuzhiyun t.openmax = 0;
173*4882a593Smuzhiyun t.integer = 0;
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun return snd_interval_refine(hw_param_interval(params, rule->var), &t);
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun
sti_uniperiph_fix_tdm_format(struct snd_pcm_hw_params * params,struct snd_pcm_hw_rule * rule)178*4882a593Smuzhiyun int sti_uniperiph_fix_tdm_format(struct snd_pcm_hw_params *params,
179*4882a593Smuzhiyun struct snd_pcm_hw_rule *rule)
180*4882a593Smuzhiyun {
181*4882a593Smuzhiyun struct uniperif *uni = rule->private;
182*4882a593Smuzhiyun struct snd_mask *maskp = hw_param_mask(params, rule->var);
183*4882a593Smuzhiyun u64 format;
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun switch (uni->tdm_slot.slot_width) {
186*4882a593Smuzhiyun case 16:
187*4882a593Smuzhiyun format = SNDRV_PCM_FMTBIT_S16_LE;
188*4882a593Smuzhiyun break;
189*4882a593Smuzhiyun case 32:
190*4882a593Smuzhiyun format = SNDRV_PCM_FMTBIT_S32_LE;
191*4882a593Smuzhiyun break;
192*4882a593Smuzhiyun default:
193*4882a593Smuzhiyun dev_err(uni->dev, "format not supported: %d bits\n",
194*4882a593Smuzhiyun uni->tdm_slot.slot_width);
195*4882a593Smuzhiyun return -EINVAL;
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun maskp->bits[0] &= (u_int32_t)format;
199*4882a593Smuzhiyun maskp->bits[1] &= (u_int32_t)(format >> 32);
200*4882a593Smuzhiyun /* clear remaining indexes */
201*4882a593Smuzhiyun memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX - 64) / 8);
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun if (!maskp->bits[0] && !maskp->bits[1])
204*4882a593Smuzhiyun return -EINVAL;
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun return 0;
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun
sti_uniperiph_get_tdm_word_pos(struct uniperif * uni,unsigned int * word_pos)209*4882a593Smuzhiyun int sti_uniperiph_get_tdm_word_pos(struct uniperif *uni,
210*4882a593Smuzhiyun unsigned int *word_pos)
211*4882a593Smuzhiyun {
212*4882a593Smuzhiyun int slot_width = uni->tdm_slot.slot_width / 8;
213*4882a593Smuzhiyun int slots_num = uni->tdm_slot.slots;
214*4882a593Smuzhiyun unsigned int slots_mask = uni->tdm_slot.mask;
215*4882a593Smuzhiyun int i, j, k;
216*4882a593Smuzhiyun unsigned int word16_pos[4];
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun /* word16_pos:
219*4882a593Smuzhiyun * word16_pos[0] = WORDX_LSB
220*4882a593Smuzhiyun * word16_pos[1] = WORDX_MSB,
221*4882a593Smuzhiyun * word16_pos[2] = WORDX+1_LSB
222*4882a593Smuzhiyun * word16_pos[3] = WORDX+1_MSB
223*4882a593Smuzhiyun */
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun /* set unip word position */
226*4882a593Smuzhiyun for (i = 0, j = 0, k = 0; (i < slots_num) && (k < WORD_MAX); i++) {
227*4882a593Smuzhiyun if ((slots_mask >> i) & 0x01) {
228*4882a593Smuzhiyun word16_pos[j] = i * slot_width;
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun if (slot_width == 4) {
231*4882a593Smuzhiyun word16_pos[j + 1] = word16_pos[j] + 2;
232*4882a593Smuzhiyun j++;
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun j++;
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun if (j > 3) {
237*4882a593Smuzhiyun word_pos[k] = word16_pos[1] |
238*4882a593Smuzhiyun (word16_pos[0] << 8) |
239*4882a593Smuzhiyun (word16_pos[3] << 16) |
240*4882a593Smuzhiyun (word16_pos[2] << 24);
241*4882a593Smuzhiyun j = 0;
242*4882a593Smuzhiyun k++;
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun }
245*4882a593Smuzhiyun }
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun return 0;
248*4882a593Smuzhiyun }
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun /*
251*4882a593Smuzhiyun * sti_uniperiph_dai_create_ctrl
252*4882a593Smuzhiyun * This function is used to create Ctrl associated to DAI but also pcm device.
253*4882a593Smuzhiyun * Request is done by front end to associate ctrl with pcm device id
254*4882a593Smuzhiyun */
sti_uniperiph_dai_create_ctrl(struct snd_soc_dai * dai)255*4882a593Smuzhiyun static int sti_uniperiph_dai_create_ctrl(struct snd_soc_dai *dai)
256*4882a593Smuzhiyun {
257*4882a593Smuzhiyun struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
258*4882a593Smuzhiyun struct uniperif *uni = priv->dai_data.uni;
259*4882a593Smuzhiyun struct snd_kcontrol_new *ctrl;
260*4882a593Smuzhiyun int i;
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun if (!uni->num_ctrls)
263*4882a593Smuzhiyun return 0;
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun for (i = 0; i < uni->num_ctrls; i++) {
266*4882a593Smuzhiyun /*
267*4882a593Smuzhiyun * Several Control can have same name. Controls are indexed on
268*4882a593Smuzhiyun * Uniperipheral instance ID
269*4882a593Smuzhiyun */
270*4882a593Smuzhiyun ctrl = &uni->snd_ctrls[i];
271*4882a593Smuzhiyun ctrl->index = uni->id;
272*4882a593Smuzhiyun ctrl->device = uni->id;
273*4882a593Smuzhiyun }
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun return snd_soc_add_dai_controls(dai, uni->snd_ctrls, uni->num_ctrls);
276*4882a593Smuzhiyun }
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun /*
279*4882a593Smuzhiyun * DAI
280*4882a593Smuzhiyun */
sti_uniperiph_dai_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)281*4882a593Smuzhiyun int sti_uniperiph_dai_hw_params(struct snd_pcm_substream *substream,
282*4882a593Smuzhiyun struct snd_pcm_hw_params *params,
283*4882a593Smuzhiyun struct snd_soc_dai *dai)
284*4882a593Smuzhiyun {
285*4882a593Smuzhiyun struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
286*4882a593Smuzhiyun struct uniperif *uni = priv->dai_data.uni;
287*4882a593Smuzhiyun struct snd_dmaengine_dai_dma_data *dma_data;
288*4882a593Smuzhiyun int transfer_size;
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun if (uni->type == SND_ST_UNIPERIF_TYPE_TDM)
291*4882a593Smuzhiyun /* transfer size = user frame size (in 32-bits FIFO cell) */
292*4882a593Smuzhiyun transfer_size = snd_soc_params_to_frame_size(params) / 32;
293*4882a593Smuzhiyun else
294*4882a593Smuzhiyun transfer_size = params_channels(params) * UNIPERIF_FIFO_FRAMES;
295*4882a593Smuzhiyun
296*4882a593Smuzhiyun dma_data = snd_soc_dai_get_dma_data(dai, substream);
297*4882a593Smuzhiyun dma_data->maxburst = transfer_size;
298*4882a593Smuzhiyun
299*4882a593Smuzhiyun return 0;
300*4882a593Smuzhiyun }
301*4882a593Smuzhiyun
sti_uniperiph_dai_set_fmt(struct snd_soc_dai * dai,unsigned int fmt)302*4882a593Smuzhiyun int sti_uniperiph_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
303*4882a593Smuzhiyun {
304*4882a593Smuzhiyun struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun priv->dai_data.uni->daifmt = fmt;
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun return 0;
309*4882a593Smuzhiyun }
310*4882a593Smuzhiyun
sti_uniperiph_suspend(struct snd_soc_component * component)311*4882a593Smuzhiyun static int sti_uniperiph_suspend(struct snd_soc_component *component)
312*4882a593Smuzhiyun {
313*4882a593Smuzhiyun struct sti_uniperiph_data *priv = snd_soc_component_get_drvdata(component);
314*4882a593Smuzhiyun struct uniperif *uni = priv->dai_data.uni;
315*4882a593Smuzhiyun int ret;
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun /* The uniperipheral should be in stopped state */
318*4882a593Smuzhiyun if (uni->state != UNIPERIF_STATE_STOPPED) {
319*4882a593Smuzhiyun dev_err(uni->dev, "%s: invalid uni state( %d)\n",
320*4882a593Smuzhiyun __func__, (int)uni->state);
321*4882a593Smuzhiyun return -EBUSY;
322*4882a593Smuzhiyun }
323*4882a593Smuzhiyun
324*4882a593Smuzhiyun /* Pinctrl: switch pinstate to sleep */
325*4882a593Smuzhiyun ret = pinctrl_pm_select_sleep_state(uni->dev);
326*4882a593Smuzhiyun if (ret)
327*4882a593Smuzhiyun dev_err(uni->dev, "%s: failed to select pinctrl state\n",
328*4882a593Smuzhiyun __func__);
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun return ret;
331*4882a593Smuzhiyun }
332*4882a593Smuzhiyun
sti_uniperiph_resume(struct snd_soc_component * component)333*4882a593Smuzhiyun static int sti_uniperiph_resume(struct snd_soc_component *component)
334*4882a593Smuzhiyun {
335*4882a593Smuzhiyun struct sti_uniperiph_data *priv = snd_soc_component_get_drvdata(component);
336*4882a593Smuzhiyun struct uniperif *uni = priv->dai_data.uni;
337*4882a593Smuzhiyun int ret;
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun if (priv->dai_data.stream == SNDRV_PCM_STREAM_PLAYBACK) {
340*4882a593Smuzhiyun ret = uni_player_resume(uni);
341*4882a593Smuzhiyun if (ret)
342*4882a593Smuzhiyun return ret;
343*4882a593Smuzhiyun }
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun /* pinctrl: switch pinstate to default */
346*4882a593Smuzhiyun ret = pinctrl_pm_select_default_state(uni->dev);
347*4882a593Smuzhiyun if (ret)
348*4882a593Smuzhiyun dev_err(uni->dev, "%s: failed to select pinctrl state\n",
349*4882a593Smuzhiyun __func__);
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun return ret;
352*4882a593Smuzhiyun }
353*4882a593Smuzhiyun
sti_uniperiph_dai_probe(struct snd_soc_dai * dai)354*4882a593Smuzhiyun static int sti_uniperiph_dai_probe(struct snd_soc_dai *dai)
355*4882a593Smuzhiyun {
356*4882a593Smuzhiyun struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
357*4882a593Smuzhiyun struct sti_uniperiph_dai *dai_data = &priv->dai_data;
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun /* DMA settings*/
360*4882a593Smuzhiyun if (priv->dai_data.stream == SNDRV_PCM_STREAM_PLAYBACK)
361*4882a593Smuzhiyun snd_soc_dai_init_dma_data(dai, &dai_data->dma_data, NULL);
362*4882a593Smuzhiyun else
363*4882a593Smuzhiyun snd_soc_dai_init_dma_data(dai, NULL, &dai_data->dma_data);
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun dai_data->dma_data.addr = dai_data->uni->fifo_phys_address;
366*4882a593Smuzhiyun dai_data->dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
367*4882a593Smuzhiyun
368*4882a593Smuzhiyun return sti_uniperiph_dai_create_ctrl(dai);
369*4882a593Smuzhiyun }
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun static const struct snd_soc_dai_driver sti_uniperiph_dai_template = {
372*4882a593Smuzhiyun .probe = sti_uniperiph_dai_probe,
373*4882a593Smuzhiyun };
374*4882a593Smuzhiyun
375*4882a593Smuzhiyun static const struct snd_soc_component_driver sti_uniperiph_dai_component = {
376*4882a593Smuzhiyun .name = "sti_cpu_dai",
377*4882a593Smuzhiyun .suspend = sti_uniperiph_suspend,
378*4882a593Smuzhiyun .resume = sti_uniperiph_resume
379*4882a593Smuzhiyun };
380*4882a593Smuzhiyun
sti_uniperiph_cpu_dai_of(struct device_node * node,struct sti_uniperiph_data * priv)381*4882a593Smuzhiyun static int sti_uniperiph_cpu_dai_of(struct device_node *node,
382*4882a593Smuzhiyun struct sti_uniperiph_data *priv)
383*4882a593Smuzhiyun {
384*4882a593Smuzhiyun struct device *dev = &priv->pdev->dev;
385*4882a593Smuzhiyun struct sti_uniperiph_dai *dai_data = &priv->dai_data;
386*4882a593Smuzhiyun struct snd_soc_dai_driver *dai = priv->dai;
387*4882a593Smuzhiyun struct snd_soc_pcm_stream *stream;
388*4882a593Smuzhiyun struct uniperif *uni;
389*4882a593Smuzhiyun const struct of_device_id *of_id;
390*4882a593Smuzhiyun const struct sti_uniperiph_dev_data *dev_data;
391*4882a593Smuzhiyun const char *mode;
392*4882a593Smuzhiyun int ret;
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun /* Populate data structure depending on compatibility */
395*4882a593Smuzhiyun of_id = of_match_node(snd_soc_sti_match, node);
396*4882a593Smuzhiyun if (!of_id->data) {
397*4882a593Smuzhiyun dev_err(dev, "data associated to device is missing\n");
398*4882a593Smuzhiyun return -EINVAL;
399*4882a593Smuzhiyun }
400*4882a593Smuzhiyun dev_data = (struct sti_uniperiph_dev_data *)of_id->data;
401*4882a593Smuzhiyun
402*4882a593Smuzhiyun uni = devm_kzalloc(dev, sizeof(*uni), GFP_KERNEL);
403*4882a593Smuzhiyun if (!uni)
404*4882a593Smuzhiyun return -ENOMEM;
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun uni->id = dev_data->id;
407*4882a593Smuzhiyun uni->ver = dev_data->version;
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun *dai = sti_uniperiph_dai_template;
410*4882a593Smuzhiyun dai->name = dev_data->dai_names;
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun /* Get resources */
413*4882a593Smuzhiyun uni->mem_region = platform_get_resource(priv->pdev, IORESOURCE_MEM, 0);
414*4882a593Smuzhiyun
415*4882a593Smuzhiyun if (!uni->mem_region) {
416*4882a593Smuzhiyun dev_err(dev, "Failed to get memory resource\n");
417*4882a593Smuzhiyun return -ENODEV;
418*4882a593Smuzhiyun }
419*4882a593Smuzhiyun
420*4882a593Smuzhiyun uni->base = devm_ioremap_resource(dev, uni->mem_region);
421*4882a593Smuzhiyun
422*4882a593Smuzhiyun if (IS_ERR(uni->base))
423*4882a593Smuzhiyun return PTR_ERR(uni->base);
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun uni->fifo_phys_address = uni->mem_region->start +
426*4882a593Smuzhiyun UNIPERIF_FIFO_DATA_OFFSET(uni);
427*4882a593Smuzhiyun
428*4882a593Smuzhiyun uni->irq = platform_get_irq(priv->pdev, 0);
429*4882a593Smuzhiyun if (uni->irq < 0)
430*4882a593Smuzhiyun return -ENXIO;
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun uni->type = dev_data->type;
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun /* check if player should be configured for tdm */
435*4882a593Smuzhiyun if (dev_data->type & SND_ST_UNIPERIF_TYPE_TDM) {
436*4882a593Smuzhiyun if (!of_property_read_string(node, "st,tdm-mode", &mode))
437*4882a593Smuzhiyun uni->type = SND_ST_UNIPERIF_TYPE_TDM;
438*4882a593Smuzhiyun else
439*4882a593Smuzhiyun uni->type = SND_ST_UNIPERIF_TYPE_PCM;
440*4882a593Smuzhiyun }
441*4882a593Smuzhiyun
442*4882a593Smuzhiyun dai_data->uni = uni;
443*4882a593Smuzhiyun dai_data->stream = dev_data->stream;
444*4882a593Smuzhiyun
445*4882a593Smuzhiyun if (priv->dai_data.stream == SNDRV_PCM_STREAM_PLAYBACK) {
446*4882a593Smuzhiyun ret = uni_player_init(priv->pdev, uni);
447*4882a593Smuzhiyun stream = &dai->playback;
448*4882a593Smuzhiyun } else {
449*4882a593Smuzhiyun ret = uni_reader_init(priv->pdev, uni);
450*4882a593Smuzhiyun stream = &dai->capture;
451*4882a593Smuzhiyun }
452*4882a593Smuzhiyun if (ret < 0)
453*4882a593Smuzhiyun return ret;
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun dai->ops = uni->dai_ops;
456*4882a593Smuzhiyun
457*4882a593Smuzhiyun stream->stream_name = dai->name;
458*4882a593Smuzhiyun stream->channels_min = uni->hw->channels_min;
459*4882a593Smuzhiyun stream->channels_max = uni->hw->channels_max;
460*4882a593Smuzhiyun stream->rates = uni->hw->rates;
461*4882a593Smuzhiyun stream->formats = uni->hw->formats;
462*4882a593Smuzhiyun
463*4882a593Smuzhiyun return 0;
464*4882a593Smuzhiyun }
465*4882a593Smuzhiyun
466*4882a593Smuzhiyun static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = {
467*4882a593Smuzhiyun .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
468*4882a593Smuzhiyun };
469*4882a593Smuzhiyun
sti_uniperiph_probe(struct platform_device * pdev)470*4882a593Smuzhiyun static int sti_uniperiph_probe(struct platform_device *pdev)
471*4882a593Smuzhiyun {
472*4882a593Smuzhiyun struct sti_uniperiph_data *priv;
473*4882a593Smuzhiyun struct device_node *node = pdev->dev.of_node;
474*4882a593Smuzhiyun int ret;
475*4882a593Smuzhiyun
476*4882a593Smuzhiyun /* Allocate the private data and the CPU_DAI array */
477*4882a593Smuzhiyun priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
478*4882a593Smuzhiyun if (!priv)
479*4882a593Smuzhiyun return -ENOMEM;
480*4882a593Smuzhiyun priv->dai = devm_kzalloc(&pdev->dev, sizeof(*priv->dai), GFP_KERNEL);
481*4882a593Smuzhiyun if (!priv->dai)
482*4882a593Smuzhiyun return -ENOMEM;
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun priv->pdev = pdev;
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun ret = sti_uniperiph_cpu_dai_of(node, priv);
487*4882a593Smuzhiyun
488*4882a593Smuzhiyun dev_set_drvdata(&pdev->dev, priv);
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun ret = devm_snd_soc_register_component(&pdev->dev,
491*4882a593Smuzhiyun &sti_uniperiph_dai_component,
492*4882a593Smuzhiyun priv->dai, 1);
493*4882a593Smuzhiyun if (ret < 0)
494*4882a593Smuzhiyun return ret;
495*4882a593Smuzhiyun
496*4882a593Smuzhiyun return devm_snd_dmaengine_pcm_register(&pdev->dev,
497*4882a593Smuzhiyun &dmaengine_pcm_config, 0);
498*4882a593Smuzhiyun }
499*4882a593Smuzhiyun
500*4882a593Smuzhiyun static struct platform_driver sti_uniperiph_driver = {
501*4882a593Smuzhiyun .driver = {
502*4882a593Smuzhiyun .name = "sti-uniperiph-dai",
503*4882a593Smuzhiyun .of_match_table = snd_soc_sti_match,
504*4882a593Smuzhiyun },
505*4882a593Smuzhiyun .probe = sti_uniperiph_probe,
506*4882a593Smuzhiyun };
507*4882a593Smuzhiyun module_platform_driver(sti_uniperiph_driver);
508*4882a593Smuzhiyun
509*4882a593Smuzhiyun MODULE_DESCRIPTION("uniperipheral DAI driver");
510*4882a593Smuzhiyun MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
511*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
512