CHROMIUM: drm: bridge/dw_hdmi: improved the hdmi audio N/CTS cacluate math
The original math would bring some inaccurate to N/CTS that would caused those magic number won't fit the HDMI 1.4 Spec request: 128 * SampleRate = Tmds * N / CTS; So this time we try to improved to math of N that would find the minimal inaccurate with the HDMI 1.4 Spec. Change-Id: Ied3cde3c352d955ae6f15d5e7fb172e92316c2a5 Signed-off-by: Yakir Yang <ykk@rock-chips.com> Reviewed-on: https://chromium-review.googlesource.com/315424 Reviewed-by: Douglas Anderson <dianders@chromium.org>
This commit is contained in:
@@ -53,6 +53,62 @@ enum hdmi_datamap {
|
||||
YCbCr422_12B = 0x12,
|
||||
};
|
||||
|
||||
/*
|
||||
* Unless otherwise noted, entries in this table are 100% optimization.
|
||||
* Values can be obtained from hdmi_compute_n() but that function is
|
||||
* slow so we pre-compute values we expect to see.
|
||||
*
|
||||
* All 32k and 48k values are expected to be the same (due to the way
|
||||
* the math works) for any rate that's an exact kHz.
|
||||
*/
|
||||
static const struct dw_hdmi_audio_tmds_n common_tmds_n_table[] = {
|
||||
{ .tmds = 25175000, .n_32k = 4096, .n_44k1 = 12854, .n_48k = 6144, },
|
||||
{ .tmds = 25200000, .n_32k = 4096, .n_44k1 = 5656, .n_48k = 6144, },
|
||||
{ .tmds = 27000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
|
||||
{ .tmds = 28320000, .n_32k = 4096, .n_44k1 = 5586, .n_48k = 6144, },
|
||||
{ .tmds = 30240000, .n_32k = 4096, .n_44k1 = 5642, .n_48k = 6144, },
|
||||
{ .tmds = 31500000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, },
|
||||
{ .tmds = 32000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, },
|
||||
{ .tmds = 33750000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
|
||||
{ .tmds = 36000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
|
||||
{ .tmds = 40000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, },
|
||||
{ .tmds = 49500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
|
||||
{ .tmds = 50000000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, },
|
||||
{ .tmds = 54000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
|
||||
{ .tmds = 65000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
|
||||
{ .tmds = 68250000, .n_32k = 4096, .n_44k1 = 5376, .n_48k = 6144, },
|
||||
{ .tmds = 71000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
|
||||
{ .tmds = 72000000, .n_32k = 4096, .n_44k1 = 5635, .n_48k = 6144, },
|
||||
{ .tmds = 73250000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
|
||||
{ .tmds = 74250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
|
||||
{ .tmds = 75000000, .n_32k = 4096, .n_44k1 = 5880, .n_48k = 6144, },
|
||||
{ .tmds = 78750000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, },
|
||||
{ .tmds = 78800000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, },
|
||||
{ .tmds = 79500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, },
|
||||
{ .tmds = 83500000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
|
||||
{ .tmds = 85500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
|
||||
{ .tmds = 88750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
|
||||
{ .tmds = 97750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
|
||||
{ .tmds = 101000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
|
||||
{ .tmds = 106500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, },
|
||||
{ .tmds = 108000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
|
||||
{ .tmds = 115500000, .n_32k = 4096, .n_44k1 = 5712, .n_48k = 6144, },
|
||||
{ .tmds = 119000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, },
|
||||
{ .tmds = 135000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
|
||||
{ .tmds = 146250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
|
||||
{ .tmds = 148500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
|
||||
{ .tmds = 154000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, },
|
||||
{ .tmds = 162000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
|
||||
|
||||
/* For 297 MHz+ HDMI spec have some other rule for setting N */
|
||||
{ .tmds = 297000000, .n_32k = 3073, .n_44k1 = 4704, .n_48k = 5120, },
|
||||
{ .tmds = 594000000, .n_32k = 3073, .n_44k1 = 9408, .n_48k = 10240, },
|
||||
|
||||
/* End of table */
|
||||
{ .tmds = 0, .n_32k = 0, .n_44k1 = 0, .n_48k = 0, },
|
||||
};
|
||||
|
||||
|
||||
static const u16 csc_coeff_default[3][4] = {
|
||||
{ 0x2000, 0x0000, 0x0000, 0x0000 },
|
||||
{ 0x0000, 0x2000, 0x0000, 0x0000 },
|
||||
@@ -217,60 +273,117 @@ static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts,
|
||||
hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1);
|
||||
}
|
||||
|
||||
static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)
|
||||
static int hdmi_match_tmds_n_table(struct dw_hdmi *hdmi,
|
||||
unsigned long pixel_clk,
|
||||
unsigned long freq)
|
||||
{
|
||||
unsigned int n = (128 * freq) / 1000;
|
||||
unsigned int mult = 1;
|
||||
const struct dw_hdmi_plat_data *plat_data = hdmi->plat_data;
|
||||
const struct dw_hdmi_audio_tmds_n *tmds_n = NULL;
|
||||
int i;
|
||||
|
||||
while (freq > 48000) {
|
||||
mult *= 2;
|
||||
freq /= 2;
|
||||
if (plat_data->tmds_n_table) {
|
||||
for (i = 0; plat_data->tmds_n_table[i].tmds != 0; i++) {
|
||||
if (pixel_clk == plat_data->tmds_n_table[i].tmds) {
|
||||
tmds_n = &plat_data->tmds_n_table[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tmds_n == NULL) {
|
||||
for (i = 0; common_tmds_n_table[i].tmds != 0; i++) {
|
||||
if (pixel_clk == common_tmds_n_table[i].tmds) {
|
||||
tmds_n = &common_tmds_n_table[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tmds_n == NULL)
|
||||
return -ENOENT;
|
||||
|
||||
switch (freq) {
|
||||
case 32000:
|
||||
if (pixel_clk == 25175000)
|
||||
n = 4576;
|
||||
else if (pixel_clk == 27027000)
|
||||
n = 4096;
|
||||
else if (pixel_clk == 74176000 || pixel_clk == 148352000)
|
||||
n = 11648;
|
||||
else
|
||||
n = 4096;
|
||||
n *= mult;
|
||||
break;
|
||||
|
||||
return tmds_n->n_32k;
|
||||
case 44100:
|
||||
if (pixel_clk == 25175000)
|
||||
n = 7007;
|
||||
else if (pixel_clk == 74176000)
|
||||
n = 17836;
|
||||
else if (pixel_clk == 148352000)
|
||||
n = 8918;
|
||||
else
|
||||
n = 6272;
|
||||
n *= mult;
|
||||
break;
|
||||
|
||||
case 88200:
|
||||
case 176400:
|
||||
return (freq / 44100) * tmds_n->n_44k1;
|
||||
case 48000:
|
||||
if (pixel_clk == 25175000)
|
||||
n = 6864;
|
||||
else if (pixel_clk == 27027000)
|
||||
n = 6144;
|
||||
else if (pixel_clk == 74176000)
|
||||
n = 11648;
|
||||
else if (pixel_clk == 148352000)
|
||||
n = 5824;
|
||||
else
|
||||
n = 6144;
|
||||
n *= mult;
|
||||
break;
|
||||
|
||||
case 96000:
|
||||
case 192000:
|
||||
return (freq / 48000) * tmds_n->n_48k;
|
||||
default:
|
||||
break;
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
static u64 hdmi_audio_math_diff(unsigned int freq, unsigned int n,
|
||||
unsigned int pixel_clk)
|
||||
{
|
||||
u64 final, diff;
|
||||
u64 cts;
|
||||
|
||||
final = (u64)pixel_clk * n;
|
||||
|
||||
cts = final;
|
||||
do_div(cts, 128 * freq);
|
||||
|
||||
diff = final - (u64)cts * (128 * freq);
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
static unsigned int hdmi_compute_n(struct dw_hdmi *hdmi,
|
||||
unsigned long pixel_clk,
|
||||
unsigned long freq)
|
||||
{
|
||||
unsigned int min_n = DIV_ROUND_UP((128 * freq), 1500);
|
||||
unsigned int max_n = (128 * freq) / 300;
|
||||
unsigned int ideal_n = (128 * freq) / 1000;
|
||||
unsigned int best_n_distance = ideal_n;
|
||||
unsigned int best_n = 0;
|
||||
u64 best_diff = U64_MAX;
|
||||
int n;
|
||||
|
||||
/* If the ideal N could satisfy the audio math, then just take it */
|
||||
if (hdmi_audio_math_diff(freq, ideal_n, pixel_clk) == 0)
|
||||
return ideal_n;
|
||||
|
||||
for (n = min_n; n <= max_n; n++) {
|
||||
u64 diff = hdmi_audio_math_diff(freq, n, pixel_clk);
|
||||
|
||||
if (diff < best_diff || (diff == best_diff &&
|
||||
abs(n - ideal_n) < best_n_distance)) {
|
||||
best_n = n;
|
||||
best_diff = diff;
|
||||
best_n_distance = abs(best_n - ideal_n);
|
||||
}
|
||||
|
||||
/*
|
||||
* The best N already satisfy the audio math, and also be
|
||||
* the closest value to ideal N, so just cut the loop.
|
||||
*/
|
||||
if ((best_diff == 0) && (abs(n - ideal_n) > best_n_distance))
|
||||
break;
|
||||
}
|
||||
|
||||
return n;
|
||||
return best_n;
|
||||
}
|
||||
|
||||
static unsigned int hdmi_find_n(struct dw_hdmi *hdmi, unsigned long pixel_clk,
|
||||
unsigned long sample_rate)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = hdmi_match_tmds_n_table(hdmi, pixel_clk, sample_rate);
|
||||
if (n > 0)
|
||||
return n;
|
||||
|
||||
dev_warn(hdmi->dev, "Rate %lu missing; compute N dynamically\n",
|
||||
pixel_clk);
|
||||
|
||||
return hdmi_compute_n(hdmi, pixel_clk, sample_rate);
|
||||
}
|
||||
|
||||
static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
|
||||
@@ -280,7 +393,7 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
|
||||
unsigned int n, cts;
|
||||
u64 tmp;
|
||||
|
||||
n = hdmi_compute_n(sample_rate, pixel_clk);
|
||||
n = hdmi_find_n(hdmi, pixel_clk, sample_rate);
|
||||
|
||||
/*
|
||||
* Compute the CTS value from the N value. Note that CTS and N
|
||||
|
||||
@@ -28,6 +28,13 @@ enum dw_hdmi_devtype {
|
||||
RK3399_HDMI,
|
||||
};
|
||||
|
||||
struct dw_hdmi_audio_tmds_n {
|
||||
unsigned long tmds;
|
||||
unsigned int n_32k;
|
||||
unsigned int n_44k1;
|
||||
unsigned int n_48k;
|
||||
};
|
||||
|
||||
struct dw_hdmi_mpll_config {
|
||||
unsigned long mpixelclock;
|
||||
struct {
|
||||
@@ -50,6 +57,7 @@ struct dw_hdmi_phy_config {
|
||||
|
||||
struct dw_hdmi_plat_data {
|
||||
enum dw_hdmi_devtype dev_type;
|
||||
const struct dw_hdmi_audio_tmds_n *tmds_n_table;
|
||||
const struct dw_hdmi_mpll_config *mpll_cfg;
|
||||
const struct dw_hdmi_curr_ctrl *cur_ctr;
|
||||
const struct dw_hdmi_phy_config *phy_config;
|
||||
|
||||
Reference in New Issue
Block a user