ASoC: tlv320dac33: Add support for changing upper threshold

Upper threshold is used in mode7 of DAC33.
Instead of hard wired UTHR, add control to change the upper threshold
value.
Changing upper threshold is not allowed when the playback is already
running, since wrongly timed change in the UTHR can cause problems
with the codec.
With this control the length of the burst in mode7 can be changed.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index 65adc77..2fa946c 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -120,6 +120,8 @@
 					 * samples */
 	unsigned int mode7_us_to_lthr;	/* Time to reach lthr from uthr */
 
+	unsigned int uthr;
+
 	enum dac33_state state;
 };
 
@@ -442,6 +444,39 @@
 	return ret;
 }
 
+static int dac33_get_uthr(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = dac33->uthr;
+
+	return 0;
+}
+
+static int dac33_set_uthr(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	if (dac33->substream)
+		return -EBUSY;
+
+	if (dac33->uthr == ucontrol->value.integer.value[0])
+		return 0;
+
+	if (ucontrol->value.integer.value[0] < (MODE7_LTHR + 10) ||
+	    ucontrol->value.integer.value[0] > MODE7_UTHR)
+		ret = -EINVAL;
+	else
+		dac33->uthr = ucontrol->value.integer.value[0];
+
+	return ret;
+}
+
 static int dac33_get_fifo_mode(struct snd_kcontrol *kcontrol,
 			 struct snd_ctl_elem_value *ucontrol)
 {
@@ -506,6 +541,8 @@
 static const struct snd_kcontrol_new dac33_nsample_snd_controls[] = {
 	SOC_SINGLE_EXT("nSample", 0, 0, 5900, 0,
 		 dac33_get_nsample, dac33_set_nsample),
+	SOC_SINGLE_EXT("UTHR", 0, 0, MODE7_UTHR, 0,
+		 dac33_get_uthr, dac33_set_uthr),
 	SOC_ENUM_EXT("FIFO Mode", dac33_fifo_mode_enum,
 		 dac33_get_fifo_mode, dac33_set_fifo_mode),
 };
@@ -985,7 +1022,7 @@
 		 * Configure the threshold levels, and leave 10 sample space
 		 * at the bottom, and also at the top of the FIFO
 		 */
-		dac33_write16(codec, DAC33_UTHR_MSB, DAC33_THRREG(MODE7_UTHR));
+		dac33_write16(codec, DAC33_UTHR_MSB, DAC33_THRREG(dac33->uthr));
 		dac33_write16(codec, DAC33_LTHR_MSB, DAC33_THRREG(MODE7_LTHR));
 		break;
 	default:
@@ -1052,8 +1089,8 @@
 		break;
 	case DAC33_FIFO_MODE7:
 		dac33->mode7_us_to_lthr =
-					SAMPLES_TO_US(substream->runtime->rate,
-						MODE7_UTHR - MODE7_LTHR + 1);
+				SAMPLES_TO_US(substream->runtime->rate,
+					dac33->uthr - MODE7_LTHR + 1);
 		dac33->t_stamp1 = 0;
 		break;
 	default:
@@ -1104,7 +1141,7 @@
 	struct snd_soc_codec *codec = socdev->card->codec;
 	struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
 	unsigned long long t0, t1, t_now;
-	unsigned int time_delta;
+	unsigned int time_delta, uthr;
 	int samples_out, samples_in, samples;
 	snd_pcm_sframes_t delay = 0;
 
@@ -1182,6 +1219,7 @@
 	case DAC33_FIFO_MODE7:
 		spin_lock(&dac33->lock);
 		t0 = dac33->t_stamp1;
+		uthr = dac33->uthr;
 		spin_unlock(&dac33->lock);
 		t_now = ktime_to_us(ktime_get());
 
@@ -1194,7 +1232,7 @@
 			 * Either the timestamps are messed or equal. Report
 			 * maximum delay
 			 */
-			delay = MODE7_UTHR;
+			delay = uthr;
 			goto out;
 		}
 
@@ -1208,8 +1246,8 @@
 					substream->runtime->rate,
 					time_delta);
 
-			if (likely(MODE7_UTHR > samples_out))
-				delay = MODE7_UTHR - samples_out;
+			if (likely(uthr > samples_out))
+				delay = uthr - samples_out;
 			else
 				delay = 0;
 		} else {
@@ -1227,8 +1265,8 @@
 					time_delta);
 			delay = MODE7_LTHR + samples_in - samples_out;
 
-			if (unlikely(delay > MODE7_UTHR))
-				delay = MODE7_UTHR;
+			if (unlikely(delay > uthr))
+				delay = uthr;
 		}
 		break;
 	default:
@@ -1484,6 +1522,7 @@
 	dac33->irq = client->irq;
 	dac33->nsample = NSAMPLE_MAX;
 	dac33->nsample_max = NSAMPLE_MAX;
+	dac33->uthr = MODE7_UTHR;
 	/* Disable FIFO use by default */
 	dac33->fifo_mode = DAC33_FIFO_BYPASS;