aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnaud Pouliquen <arnaud.pouliquen@st.com>2015-07-16 05:36:06 -0400
committerMark Brown <broonie@kernel.org>2015-07-16 16:38:24 -0400
commitfa050796b35c80ac948c4d24c95262daa905e2ef (patch)
treea18d7a52332972940a5ef712e5ff57c084ea8fac
parent1e6babb417f76bd6d1a4d1d633e5c46435acbfe7 (diff)
ASoC: sti: Add clock adjustement control
Add capability to adjust player clock, for clocks drift management. Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--sound/soc/sti/uniperif.h2
-rw-r--r--sound/soc/sti/uniperif_player.c149
2 files changed, 150 insertions, 1 deletions
diff --git a/sound/soc/sti/uniperif.h b/sound/soc/sti/uniperif.h
index f1e583de3c6f..ee462f7daaff 100644
--- a/sound/soc/sti/uniperif.h
+++ b/sound/soc/sti/uniperif.h
@@ -1175,6 +1175,7 @@ struct uniperif {
1175 /* Clocks */ 1175 /* Clocks */
1176 struct clk *clk; 1176 struct clk *clk;
1177 int mclk; 1177 int mclk;
1178 int clk_adj;
1178 1179
1179 /* Runtime data */ 1180 /* Runtime data */
1180 enum uniperif_state state; 1181 enum uniperif_state state;
@@ -1183,6 +1184,7 @@ struct uniperif {
1183 1184
1184 /* Specific to IEC958 player */ 1185 /* Specific to IEC958 player */
1185 struct uniperif_iec958_settings stream_settings; 1186 struct uniperif_iec958_settings stream_settings;
1187 struct mutex ctrl_lock; /* For resource updated by stream and controls*/
1186 1188
1187 /*alsa ctrl*/ 1189 /*alsa ctrl*/
1188 struct snd_kcontrol_new *snd_ctrls; 1190 struct snd_kcontrol_new *snd_ctrls;
diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c
index d12d0502545e..d990d2c81a5d 100644
--- a/sound/soc/sti/uniperif_player.c
+++ b/sound/soc/sti/uniperif_player.c
@@ -36,6 +36,9 @@
36 (UNIPERIF_PLAYER_TYPE_IS_HDMI(p) || \ 36 (UNIPERIF_PLAYER_TYPE_IS_HDMI(p) || \
37 UNIPERIF_PLAYER_TYPE_IS_SPDIF(p)) 37 UNIPERIF_PLAYER_TYPE_IS_SPDIF(p))
38 38
39#define UNIPERIF_PLAYER_CLK_ADJ_MIN -999999
40#define UNIPERIF_PLAYER_CLK_ADJ_MAX 1000000
41
39/* 42/*
40 * Note: snd_pcm_hardware is linked to DMA controller but is declared here to 43 * Note: snd_pcm_hardware is linked to DMA controller but is declared here to
41 * integrate DAI_CPU capability in term of rate and supported channels 44 * integrate DAI_CPU capability in term of rate and supported channels
@@ -172,6 +175,70 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id)
172 return ret; 175 return ret;
173} 176}
174 177
178int uni_player_clk_set_rate(struct uniperif *player, unsigned long rate)
179{
180 int rate_adjusted, rate_achieved, delta, ret;
181 int adjustment = player->clk_adj;
182
183 /*
184 * a
185 * F = f + --------- * f = f + d
186 * 1000000
187 *
188 * a
189 * d = --------- * f
190 * 1000000
191 *
192 * where:
193 * f - nominal rate
194 * a - adjustment in ppm (parts per milion)
195 * F - rate to be set in synthesizer
196 * d - delta (difference) between f and F
197 */
198 if (adjustment < 0) {
199 /* div64_64 operates on unsigned values... */
200 delta = -1;
201 adjustment = -adjustment;
202 } else {
203 delta = 1;
204 }
205 /* 500000 ppm is 0.5, which is used to round up values */
206 delta *= (int)div64_u64((uint64_t)rate *
207 (uint64_t)adjustment + 500000, 1000000);
208 rate_adjusted = rate + delta;
209
210 /* Adjusted rate should never be == 0 */
211 if (!rate_adjusted)
212 return -EINVAL;
213
214 ret = clk_set_rate(player->clk, rate_adjusted);
215 if (ret < 0)
216 return ret;
217
218 rate_achieved = clk_get_rate(player->clk);
219 if (!rate_achieved)
220 /* If value is 0 means that clock or parent not valid */
221 return -EINVAL;
222
223 /*
224 * Using ALSA's adjustment control, we can modify the rate to be up
225 * to twice as much as requested, but no more
226 */
227 delta = rate_achieved - rate;
228 if (delta < 0) {
229 /* div64_64 operates on unsigned values... */
230 delta = -delta;
231 adjustment = -1;
232 } else {
233 adjustment = 1;
234 }
235 /* Frequency/2 is added to round up result */
236 adjustment *= (int)div64_u64((uint64_t)delta * 1000000 + rate / 2,
237 rate);
238 player->clk_adj = adjustment;
239 return 0;
240}
241
175static void uni_player_set_channel_status(struct uniperif *player, 242static void uni_player_set_channel_status(struct uniperif *player,
176 struct snd_pcm_runtime *runtime) 243 struct snd_pcm_runtime *runtime)
177{ 244{
@@ -470,6 +537,78 @@ static int uni_player_prepare_pcm(struct uniperif *player,
470 return 0; 537 return 0;
471} 538}
472 539
540/*
541 * uniperif rate adjustement control
542 */
543static int snd_sti_clk_adjustment_info(struct snd_kcontrol *kcontrol,
544 struct snd_ctl_elem_info *uinfo)
545{
546 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
547 uinfo->count = 1;
548 uinfo->value.integer.min = UNIPERIF_PLAYER_CLK_ADJ_MIN;
549 uinfo->value.integer.max = UNIPERIF_PLAYER_CLK_ADJ_MAX;
550 uinfo->value.integer.step = 1;
551
552 return 0;
553}
554
555static int snd_sti_clk_adjustment_get(struct snd_kcontrol *kcontrol,
556 struct snd_ctl_elem_value *ucontrol)
557{
558 struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
559 struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
560 struct uniperif *player = priv->dai_data.uni;
561
562 ucontrol->value.integer.value[0] = player->clk_adj;
563
564 return 0;
565}
566
567static int snd_sti_clk_adjustment_put(struct snd_kcontrol *kcontrol,
568 struct snd_ctl_elem_value *ucontrol)
569{
570 struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
571 struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
572 struct uniperif *player = priv->dai_data.uni;
573 int ret = 0;
574
575 if ((ucontrol->value.integer.value[0] < UNIPERIF_PLAYER_CLK_ADJ_MIN) ||
576 (ucontrol->value.integer.value[0] > UNIPERIF_PLAYER_CLK_ADJ_MAX))
577 return -EINVAL;
578
579 mutex_lock(&player->ctrl_lock);
580 player->clk_adj = ucontrol->value.integer.value[0];
581
582 if (player->mclk)
583 ret = uni_player_clk_set_rate(player, player->mclk);
584 mutex_unlock(&player->ctrl_lock);
585
586 return ret;
587}
588
589static struct snd_kcontrol_new uni_player_clk_adj_ctl = {
590 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
591 .name = "PCM Playback Oversampling Freq. Adjustment",
592 .info = snd_sti_clk_adjustment_info,
593 .get = snd_sti_clk_adjustment_get,
594 .put = snd_sti_clk_adjustment_put,
595};
596
597static struct snd_kcontrol_new *snd_sti_ctl[] = {
598 &uni_player_clk_adj_ctl,
599};
600
601static int uni_player_startup(struct snd_pcm_substream *substream,
602 struct snd_soc_dai *dai)
603{
604 struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
605 struct uniperif *player = priv->dai_data.uni;
606
607 player->clk_adj = 0;
608
609 return 0;
610}
611
473static int uni_player_set_sysclk(struct snd_soc_dai *dai, int clk_id, 612static int uni_player_set_sysclk(struct snd_soc_dai *dai, int clk_id,
474 unsigned int freq, int dir) 613 unsigned int freq, int dir)
475{ 614{
@@ -483,9 +622,11 @@ static int uni_player_set_sysclk(struct snd_soc_dai *dai, int clk_id,
483 if (clk_id != 0) 622 if (clk_id != 0)
484 return -EINVAL; 623 return -EINVAL;
485 624
486 ret = clk_set_rate(player->clk, freq); 625 mutex_lock(&player->ctrl_lock);
626 ret = uni_player_clk_set_rate(player, freq);
487 if (!ret) 627 if (!ret)
488 player->mclk = freq; 628 player->mclk = freq;
629 mutex_unlock(&player->ctrl_lock);
489 630
490 return ret; 631 return ret;
491} 632}
@@ -816,6 +957,7 @@ static int uni_player_parse_dt(struct platform_device *pdev,
816} 957}
817 958
818const struct snd_soc_dai_ops uni_player_dai_ops = { 959const struct snd_soc_dai_ops uni_player_dai_ops = {
960 .startup = uni_player_startup,
819 .shutdown = uni_player_shutdown, 961 .shutdown = uni_player_shutdown,
820 .prepare = uni_player_prepare, 962 .prepare = uni_player_prepare,
821 .trigger = uni_player_trigger, 963 .trigger = uni_player_trigger,
@@ -863,6 +1005,8 @@ int uni_player_init(struct platform_device *pdev,
863 if (ret < 0) 1005 if (ret < 0)
864 return ret; 1006 return ret;
865 1007
1008 mutex_init(&player->ctrl_lock);
1009
866 /* Ensure that disabled by default */ 1010 /* Ensure that disabled by default */
867 SET_UNIPERIF_CONFIG_BACK_STALL_REQ_DISABLE(player); 1011 SET_UNIPERIF_CONFIG_BACK_STALL_REQ_DISABLE(player);
868 SET_UNIPERIF_CTRL_ROUNDING_OFF(player); 1012 SET_UNIPERIF_CTRL_ROUNDING_OFF(player);
@@ -889,6 +1033,9 @@ int uni_player_init(struct platform_device *pdev,
889 IEC958_AES4_CON_WORDLEN_24_20; 1033 IEC958_AES4_CON_WORDLEN_24_20;
890 } 1034 }
891 1035
1036 player->num_ctrls = ARRAY_SIZE(snd_sti_ctl);
1037 player->snd_ctrls = snd_sti_ctl[0];
1038
892 return 0; 1039 return 0;
893} 1040}
894EXPORT_SYMBOL_GPL(uni_player_init); 1041EXPORT_SYMBOL_GPL(uni_player_init);