summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/i2c/tda998x_drv.c94
1 files changed, 74 insertions, 20 deletions
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index f23aee65846c..9b3e47f408ca 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -43,6 +43,7 @@ struct tda998x_audio_port {
struct tda998x_audio_settings {
struct tda998x_audio_params params;
u8 i2s_format;
+ u8 cts_n;
};
struct tda998x_priv {
@@ -891,6 +892,58 @@ static u8 tda998x_get_adiv(struct tda998x_priv *priv, unsigned int fs)
return adiv;
}
+/*
+ * In auto-CTS mode, the TDA998x uses a "measured time stamp" counter to
+ * generate the CTS value. It appears that the "measured time stamp" is
+ * the number of TDMS clock cycles within a number of audio input clock
+ * cycles defined by the k and N parameters defined below, in a similar
+ * way to that which is set out in the CTS generation in the HDMI spec.
+ *
+ * tmdsclk ----> mts -> /m ---> CTS
+ * ^
+ * sclk -> /k -> /N
+ *
+ * CTS = mts / m, where m is 2^M.
+ * /k is a divider based on the K value below, K+1 for K < 4, or 8 for K >= 4
+ * /N is a divider based on the HDMI specified N value.
+ *
+ * This produces the following equation:
+ * CTS = tmds_clock * k * N / (sclk * m)
+ *
+ * When combined with the sink-side equation, and realising that sclk is
+ * bclk_ratio * fs, we end up with:
+ * k = m * bclk_ratio / 128.
+ *
+ * Note: S/PDIF always uses a bclk_ratio of 64.
+ */
+static int tda998x_derive_cts_n(struct tda998x_priv *priv,
+ struct tda998x_audio_settings *settings,
+ unsigned int ratio)
+{
+ switch (ratio) {
+ case 16:
+ settings->cts_n = CTS_N_M(3) | CTS_N_K(0);
+ break;
+ case 32:
+ settings->cts_n = CTS_N_M(3) | CTS_N_K(1);
+ break;
+ case 48:
+ settings->cts_n = CTS_N_M(3) | CTS_N_K(2);
+ break;
+ case 64:
+ settings->cts_n = CTS_N_M(3) | CTS_N_K(3);
+ break;
+ case 128:
+ settings->cts_n = CTS_N_M(0) | CTS_N_K(0);
+ break;
+ default:
+ dev_err(&priv->hdmi->dev, "unsupported bclk ratio %ufs\n",
+ ratio);
+ return -EINVAL;
+ }
+ return 0;
+}
+
static void tda998x_audio_mute(struct tda998x_priv *priv, bool on)
{
if (on) {
@@ -905,7 +958,7 @@ static void tda998x_audio_mute(struct tda998x_priv *priv, bool on)
static int tda998x_configure_audio(struct tda998x_priv *priv,
const struct tda998x_audio_settings *settings)
{
- u8 buf[6], clksel_aip, clksel_fs, cts_n, adiv;
+ u8 buf[6], clksel_aip, clksel_fs, adiv;
u32 n;
adiv = tda998x_get_adiv(priv, settings->params.sample_rate);
@@ -920,7 +973,6 @@ static int tda998x_configure_audio(struct tda998x_priv *priv,
reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_SPDIF);
clksel_aip = AIP_CLKSEL_AIP_SPDIF;
clksel_fs = AIP_CLKSEL_FS_FS64SPDIF;
- cts_n = CTS_N_M(3) | CTS_N_K(3);
break;
case AFMT_I2S:
@@ -928,20 +980,6 @@ static int tda998x_configure_audio(struct tda998x_priv *priv,
reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_I2S);
clksel_aip = AIP_CLKSEL_AIP_I2S;
clksel_fs = AIP_CLKSEL_FS_ACLK;
- switch (settings->params.sample_width) {
- case 16:
- cts_n = CTS_N_M(3) | CTS_N_K(1);
- break;
- case 18:
- case 20:
- case 24:
- cts_n = CTS_N_M(3) | CTS_N_K(2);
- break;
- default:
- case 32:
- cts_n = CTS_N_M(3) | CTS_N_K(3);
- break;
- }
break;
default:
@@ -953,7 +991,7 @@ static int tda998x_configure_audio(struct tda998x_priv *priv,
reg_write(priv, REG_AIP_CLKSEL, clksel_aip);
reg_clear(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_LAYOUT |
AIP_CNTRL_0_ACR_MAN); /* auto CTS */
- reg_write(priv, REG_CTS_N, cts_n);
+ reg_write(priv, REG_CTS_N, settings->cts_n);
reg_write(priv, REG_AUDIO_DIV, adiv);
/*
@@ -1000,6 +1038,7 @@ static int tda998x_audio_hw_params(struct device *dev, void *data,
struct hdmi_codec_params *params)
{
struct tda998x_priv *priv = dev_get_drvdata(dev);
+ unsigned int bclk_ratio;
bool spdif = daifmt->fmt == HDMI_SPDIF;
int i, ret;
struct tda998x_audio_settings audio = {
@@ -1052,6 +1091,11 @@ static int tda998x_audio_hw_params(struct device *dev, void *data,
return -EINVAL;
}
+ bclk_ratio = spdif ? 64 : params->sample_width * 2;
+ ret = tda998x_derive_cts_n(priv, &audio, bclk_ratio);
+ if (ret < 0)
+ return ret;
+
mutex_lock(&priv->audio_mutex);
if (priv->supports_infoframes && priv->sink_has_audio)
ret = tda998x_configure_audio(priv, &audio);
@@ -1640,8 +1684,8 @@ static int tda998x_get_audio_ports(struct tda998x_priv *priv,
return 0;
}
-static void tda998x_set_config(struct tda998x_priv *priv,
- const struct tda998x_encoder_params *p)
+static int tda998x_set_config(struct tda998x_priv *priv,
+ const struct tda998x_encoder_params *p)
{
priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(p->swap_a) |
(p->mirr_a ? VIP_CNTRL_0_MIRR_A : 0) |
@@ -1657,9 +1701,17 @@ static void tda998x_set_config(struct tda998x_priv *priv,
(p->mirr_f ? VIP_CNTRL_2_MIRR_F : 0);
if (p->audio_params.format != AFMT_UNUSED) {
+ unsigned int ratio;
+ bool spdif = p->audio_params.format == AFMT_SPDIF;
+
priv->audio.params = p->audio_params;
priv->audio.i2s_format = I2S_FORMAT_PHILIPS;
+
+ ratio = spdif ? 64 : p->audio_params.sample_width * 2;
+ return tda998x_derive_cts_n(priv, &priv->audio, ratio);
}
+
+ return 0;
}
static void tda998x_destroy(struct device *dev)
@@ -1861,7 +1913,9 @@ static int tda998x_create(struct device *dev)
if (priv->audio_port[0].format != AFMT_UNUSED)
tda998x_audio_codec_init(priv, &client->dev);
} else if (dev->platform_data) {
- tda998x_set_config(priv, dev->platform_data);
+ ret = tda998x_set_config(priv, dev->platform_data);
+ if (ret)
+ goto fail;
}
priv->bridge.funcs = &tda998x_bridge_funcs;