[PATCH] drm/bridge:anx7625: Enable DSC feature

Xin Ji posted 1 patch 10 months, 1 week ago
There is a newer version of this series
drivers/gpu/drm/bridge/analogix/anx7625.c | 300 ++++++++++++++++++----
drivers/gpu/drm/bridge/analogix/anx7625.h |  32 +++
2 files changed, 284 insertions(+), 48 deletions(-)
[PATCH] drm/bridge:anx7625: Enable DSC feature
Posted by Xin Ji 10 months, 1 week ago
As anx7625 MIPI RX bandwidth(maximum 1.5Gbps per lane) and internal
pixel clock(maximum 300M) limitation. Anx7625 must enable DSC feature
while MIPI source want to output 4K30 resolution.

Signed-off-by: Xin Ji <xji@analogixsemi.com>
---
 drivers/gpu/drm/bridge/analogix/anx7625.c | 300 ++++++++++++++++++----
 drivers/gpu/drm/bridge/analogix/anx7625.h |  32 +++
 2 files changed, 284 insertions(+), 48 deletions(-)

diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c
index 4be34d5c7a3b..7d86ab02f71c 100644
--- a/drivers/gpu/drm/bridge/analogix/anx7625.c
+++ b/drivers/gpu/drm/bridge/analogix/anx7625.c
@@ -22,6 +22,7 @@
 
 #include <drm/display/drm_dp_aux_bus.h>
 #include <drm/display/drm_dp_helper.h>
+#include <drm/display/drm_dsc_helper.h>
 #include <drm/display/drm_hdcp_helper.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
@@ -476,6 +477,138 @@ static int anx7625_set_k_value(struct anx7625_data *ctx)
 				 MIPI_DIGITAL_ADJ_1, 0x3D);
 }
 
+static int anx7625_set_dsc_params(struct anx7625_data *ctx)
+{
+	int ret, i;
+	u16 htotal, vtotal;
+
+	if (!ctx->dsc_en)
+		return 0;
+
+	/* Htotal */
+	htotal = ctx->dt.hactive.min + ctx->dt.hfront_porch.min +
+		ctx->dt.hback_porch.min + ctx->dt.hsync_len.min;
+	ret = anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
+				HORIZONTAL_TOTAL_PIXELS_L, htotal);
+	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
+				 HORIZONTAL_TOTAL_PIXELS_H, htotal >> 8);
+	/* Hactive */
+	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
+				 HORIZONTAL_ACTIVE_PIXELS_L,
+				 ctx->dt.hactive.min);
+	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
+				 HORIZONTAL_ACTIVE_PIXELS_H,
+				 ctx->dt.hactive.min >> 8);
+	/* HFP */
+	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
+				 HORIZONTAL_FRONT_PORCH_L,
+				 ctx->dt.hfront_porch.min);
+	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
+				 HORIZONTAL_FRONT_PORCH_H,
+				 ctx->dt.hfront_porch.min >> 8);
+	/* HWS */
+	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
+				 HORIZONTAL_SYNC_WIDTH_L,
+				 ctx->dt.hsync_len.min);
+	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
+				 HORIZONTAL_SYNC_WIDTH_H,
+				 ctx->dt.hsync_len.min >> 8);
+	/* HBP */
+	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
+				 HORIZONTAL_BACK_PORCH_L,
+				 ctx->dt.hback_porch.min);
+	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
+				 HORIZONTAL_BACK_PORCH_H,
+				 ctx->dt.hback_porch.min >> 8);
+	/* Vtotal */
+	vtotal = ctx->dt.vactive.min + ctx->dt.vfront_porch.min +
+		 ctx->dt.vback_porch.min + ctx->dt.vsync_len.min;
+	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client, TOTAL_LINES_L,
+				 vtotal);
+	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client, TOTAL_LINES_H,
+				 vtotal >> 8);
+	/* Vactive */
+	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client, ACTIVE_LINES_L,
+				 ctx->dt.vactive.min);
+	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client, ACTIVE_LINES_H,
+				 ctx->dt.vactive.min >> 8);
+	/* VFP */
+	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
+				 VERTICAL_FRONT_PORCH, ctx->dt.vfront_porch.min);
+	/* VWS */
+	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
+				 VERTICAL_SYNC_WIDTH, ctx->dt.vsync_len.min);
+	/* VBP */
+	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
+				 VERTICAL_BACK_PORCH, ctx->dt.vback_porch.min);
+
+	/* Htotal */
+	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, TOTAL_PIXEL_L_7E,
+				 htotal);
+	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, TOTAL_PIXEL_H_7E,
+				 htotal >> 8);
+	/* Hactive */
+	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+				 ACTIVE_PIXEL_L_7E, ctx->dt.hactive.min);
+	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+				 ACTIVE_PIXEL_H_7E, ctx->dt.hactive.min >> 8);
+	/* HFP */
+	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+				 HORIZON_FRONT_PORCH_L_7E,
+				 ctx->dt.hfront_porch.min);
+	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+				 HORIZON_FRONT_PORCH_H_7E,
+				 ctx->dt.hfront_porch.min >> 8);
+	/* HWS */
+	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+				 HORIZON_SYNC_WIDTH_L_7E,
+				 ctx->dt.hsync_len.min);
+	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+				 HORIZON_SYNC_WIDTH_H_7E,
+				 ctx->dt.hsync_len.min >> 8);
+	/* HBP */
+	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+				 HORIZON_BACK_PORCH_L_7E,
+				 ctx->dt.hback_porch.min);
+	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+				 HORIZON_BACK_PORCH_H_7E,
+				 ctx->dt.hback_porch.min >> 8);
+
+	/* Config DSC decoder internal blank timing for decoder to start */
+	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p1_client,
+				 H_BLANK_L,
+				 dsc_div(htotal - ctx->dt.hactive.min));
+	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p1_client,
+				 H_BLANK_H,
+				 dsc_div(htotal - ctx->dt.hactive.min) >> 8);
+
+	/* Compress ratio  RATIO bit[7:6] */
+	ret |= anx7625_write_and(ctx, ctx->i2c.rx_p0_client, R_I2C_1, 0x3F);
+	ret |= anx7625_write_or(ctx, ctx->i2c.rx_p0_client, R_I2C_1,
+				(5 - DSC_COMPRESS_RATIO) << 6);
+	/*PPS table*/
+	for (i = 0; i < PPS_SIZE; i += PPS_BLOCK_SIZE)
+		ret |= anx7625_reg_block_write(ctx, ctx->i2c.rx_p2_client,
+					       R_PPS_REG_0 + i, PPS_BLOCK_SIZE,
+					       &ctx->pps_table[i]);
+
+	return ret;
+}
+
+static int anx7625_timing_write(struct anx7625_data *ctx,
+				struct i2c_client *client,
+				u8 reg_addr, u16 reg_val, bool high_byte)
+{
+	u8 reg_data;
+
+	if (ctx->dsc_en)
+		reg_val = dsc_div(reg_val);
+
+	reg_data = (high_byte ? (reg_val >> 8) : reg_val) & 0xFF;
+
+	return anx7625_reg_write(ctx, client, reg_addr, reg_data);
+}
+
 static int anx7625_dsi_video_timing_config(struct anx7625_data *ctx)
 {
 	struct device *dev = ctx->dev;
@@ -506,34 +639,33 @@ static int anx7625_dsi_video_timing_config(struct anx7625_data *ctx)
 	ret |= anx7625_write_or(ctx, ctx->i2c.rx_p1_client,
 				MIPI_LANE_CTRL_0, ctx->pdata.mipi_lanes - 1);
 
-	/* Htotal */
 	htotal = ctx->dt.hactive.min + ctx->dt.hfront_porch.min +
-		ctx->dt.hback_porch.min + ctx->dt.hsync_len.min;
-	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client,
-			HORIZONTAL_TOTAL_PIXELS_L, htotal & 0xFF);
-	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client,
-			HORIZONTAL_TOTAL_PIXELS_H, htotal >> 8);
+			 ctx->dt.hback_porch.min + ctx->dt.hsync_len.min;
+	/* Htotal */
+	ret |= anx7625_timing_write(ctx, ctx->i2c.rx_p2_client,
+			HORIZONTAL_TOTAL_PIXELS_L, htotal, false);
+	ret |= anx7625_timing_write(ctx, ctx->i2c.rx_p2_client,
+			HORIZONTAL_TOTAL_PIXELS_H, htotal, true);
 	/* Hactive */
-	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client,
-			HORIZONTAL_ACTIVE_PIXELS_L, ctx->dt.hactive.min & 0xFF);
-	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client,
-			HORIZONTAL_ACTIVE_PIXELS_H, ctx->dt.hactive.min >> 8);
+	ret |= anx7625_timing_write(ctx, ctx->i2c.rx_p2_client,
+			HORIZONTAL_ACTIVE_PIXELS_L, ctx->dt.hactive.min, false);
+	ret |= anx7625_timing_write(ctx, ctx->i2c.rx_p2_client,
+			HORIZONTAL_ACTIVE_PIXELS_H, ctx->dt.hactive.min, true);
 	/* HFP */
-	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client,
-			HORIZONTAL_FRONT_PORCH_L, ctx->dt.hfront_porch.min);
-	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client,
-			HORIZONTAL_FRONT_PORCH_H,
-			ctx->dt.hfront_porch.min >> 8);
+	ret |= anx7625_timing_write(ctx, ctx->i2c.rx_p2_client,
+			HORIZONTAL_FRONT_PORCH_L, ctx->dt.hfront_porch.min, false);
+	ret |= anx7625_timing_write(ctx, ctx->i2c.rx_p2_client,
+			HORIZONTAL_FRONT_PORCH_H, ctx->dt.hfront_porch.min, true);
 	/* HWS */
-	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client,
-			HORIZONTAL_SYNC_WIDTH_L, ctx->dt.hsync_len.min);
-	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client,
-			HORIZONTAL_SYNC_WIDTH_H, ctx->dt.hsync_len.min >> 8);
+	ret |= anx7625_timing_write(ctx, ctx->i2c.rx_p2_client,
+			HORIZONTAL_SYNC_WIDTH_L, ctx->dt.hsync_len.min, false);
+	ret |= anx7625_timing_write(ctx, ctx->i2c.rx_p2_client,
+			HORIZONTAL_SYNC_WIDTH_H, ctx->dt.hsync_len.min, true);
 	/* HBP */
-	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client,
-			HORIZONTAL_BACK_PORCH_L, ctx->dt.hback_porch.min);
-	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client,
-			HORIZONTAL_BACK_PORCH_H, ctx->dt.hback_porch.min >> 8);
+	ret |= anx7625_timing_write(ctx, ctx->i2c.rx_p2_client,
+			HORIZONTAL_BACK_PORCH_L, ctx->dt.hback_porch.min, false);
+	ret |= anx7625_timing_write(ctx, ctx->i2c.rx_p2_client,
+			HORIZONTAL_BACK_PORCH_H, ctx->dt.hback_porch.min, true);
 	/* Vactive */
 	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client, ACTIVE_LINES_L,
 			ctx->dt.vactive.min);
@@ -663,9 +795,15 @@ static int anx7625_dsi_config(struct anx7625_data *ctx)
 
 	DRM_DEV_DEBUG_DRIVER(dev, "config dsi.\n");
 
-	/* DSC disable */
-	ret = anx7625_write_and(ctx, ctx->i2c.rx_p0_client,
-				R_DSC_CTRL_0, ~DSC_EN);
+	ret = anx7625_set_dsc_params(ctx);
+	if (ctx->dsc_en)
+		/* DSC enable */
+		ret |= anx7625_write_or(ctx, ctx->i2c.rx_p0_client,
+					R_DSC_CTRL_0, DSC_EN);
+	else
+		/* DSC disable */
+		ret |= anx7625_write_and(ctx, ctx->i2c.rx_p0_client,
+					 R_DSC_CTRL_0, ~DSC_EN);
 
 	ret |= anx7625_api_dsi_config(ctx);
 
@@ -2083,6 +2221,7 @@ static int anx7625_setup_dsi_device(struct anx7625_data *ctx)
 		MIPI_DSI_MODE_VIDEO_HSE	|
 		MIPI_DSI_HS_PKT_END_ALIGNED;
 
+	dsi->dsc = &ctx->dsc;
 	ctx->dsi = dsi;
 
 	return 0;
@@ -2186,20 +2325,68 @@ anx7625_bridge_mode_valid(struct drm_bridge *bridge,
 	struct anx7625_data *ctx = bridge_to_anx7625(bridge);
 	struct device *dev = ctx->dev;
 
-	DRM_DEV_DEBUG_DRIVER(dev, "drm mode checking\n");
+	dev_dbg(dev, "drm mode checking\n");
+	if (mode->clock > SUPPORT_PIXEL_CLOCK)
+		return MODE_CLOCK_HIGH;
+
+	if (mode->clock < SUPPORT_MIN_PIXEL_CLOCK)
+		return MODE_CLOCK_LOW;
 
-	/* Max 1200p at 5.4 Ghz, one lane, pixel clock 300M */
-	if (mode->clock > SUPPORT_PIXEL_CLOCK) {
-		DRM_DEV_DEBUG_DRIVER(dev,
-				     "drm mode invalid, pixelclock too high.\n");
+	if (mode->clock > DSC_PIXEL_CLOCK && (mode->hdisplay % 3 != 0))
 		return MODE_CLOCK_HIGH;
-	}
 
-	DRM_DEV_DEBUG_DRIVER(dev, "drm mode valid.\n");
+	dev_dbg(dev, "drm mode valid.\n");
 
 	return MODE_OK;
 }
 
+static void anx7625_dsc_enable(struct anx7625_data *ctx, bool en)
+{
+	int ret;
+	struct device *dev = ctx->dev;
+
+	ctx->dsc_en = en;
+
+	if (en) {
+		ctx->dsc.dsc_version_major = 1;
+		ctx->dsc.dsc_version_minor = 1;
+		ctx->dsc.slice_height = 8;
+		ctx->dsc.slice_width = ctx->dt.hactive.min / DSC_SLICE_NUM;
+		ctx->dsc.slice_count = DSC_SLICE_NUM;
+		ctx->dsc.bits_per_component = 8;
+		ctx->dsc.bits_per_pixel = 8 << 4; /* 4 fractional bits */
+		ctx->dsc.block_pred_enable = true;
+		ctx->dsc.native_420 = false;
+		ctx->dsc.native_422 = false;
+		ctx->dsc.simple_422 = false;
+		ctx->dsc.vbr_enable = false;
+		ctx->dsc.rc_model_size = DSC_RC_MODEL_SIZE_CONST;
+		ctx->dsc.pic_width = ctx->dt.hactive.min;
+		ctx->dsc.pic_height = ctx->dt.vactive.min;
+		ctx->dsc.convert_rgb = 1;
+		ctx->dsc.vbr_enable = 0;
+
+		drm_dsc_set_rc_buf_thresh(&ctx->dsc);
+		drm_dsc_set_const_params(&ctx->dsc);
+
+		ctx->dsc.initial_scale_value = drm_dsc_initial_scale_value(&ctx->dsc);
+		ctx->dsc.line_buf_depth = ctx->dsc.bits_per_component + 1;
+		ret = drm_dsc_setup_rc_params(&ctx->dsc, DRM_DSC_1_2_444);
+		if (ret < 0)
+			dev_warn(dev, "drm_dsc_setup_rc_params ret %d\n", ret);
+
+		drm_dsc_compute_rc_parameters(&ctx->dsc);
+
+		drm_dsc_pps_payload_pack((struct drm_dsc_picture_parameter_set *)&ctx->pps_table,
+					 &ctx->dsc);
+		dev_dbg(dev, "anx7625 enable dsc\n");
+	} else {
+		ctx->dsc.dsc_version_major = 0;
+		ctx->dsc.dsc_version_minor = 0;
+		dev_dbg(dev, "anx7625 disable dsc\n");
+	}
+}
+
 static void anx7625_bridge_mode_set(struct drm_bridge *bridge,
 				    const struct drm_display_mode *old_mode,
 				    const struct drm_display_mode *mode)
@@ -2244,6 +2431,11 @@ static void anx7625_bridge_mode_set(struct drm_bridge *bridge,
 	DRM_DEV_DEBUG_DRIVER(dev, "vsync_end(%d),vtotal(%d).\n",
 			     mode->vsync_end,
 			     mode->vtotal);
+
+	if (mode->clock > DSC_PIXEL_CLOCK)
+		anx7625_dsc_enable(ctx, true);
+	else
+		anx7625_dsc_enable(ctx, false);
 }
 
 static bool anx7625_bridge_mode_fixup(struct drm_bridge *bridge,
@@ -2258,26 +2450,33 @@ static bool anx7625_bridge_mode_fixup(struct drm_bridge *bridge,
 
 	DRM_DEV_DEBUG_DRIVER(dev, "drm mode fixup set\n");
 
-	/* No need fixup for external monitor */
-	if (!ctx->pdata.panel_bridge)
-		return true;
-
 	hsync = mode->hsync_end - mode->hsync_start;
 	hfp = mode->hsync_start - mode->hdisplay;
 	hbp = mode->htotal - mode->hsync_end;
 	hblanking = mode->htotal - mode->hdisplay;
 
-	DRM_DEV_DEBUG_DRIVER(dev, "before mode fixup\n");
-	DRM_DEV_DEBUG_DRIVER(dev, "hsync(%d), hfp(%d), hbp(%d), clock(%d)\n",
-			     hsync, hfp, hbp, adj->clock);
-	DRM_DEV_DEBUG_DRIVER(dev, "hsync_start(%d), hsync_end(%d), htot(%d)\n",
-			     adj->hsync_start, adj->hsync_end, adj->htotal);
-
+	dev_dbg(dev, "before mode fixup\n");
+	dev_dbg(dev, "hsync(%d), hfp(%d), hbp(%d), clock(%d)\n",
+		hsync, hfp, hbp, adj->clock);
+	dev_dbg(dev, "hsync_start(%d), hsync_end(%d), htot(%d)\n",
+		adj->hsync_start, adj->hsync_end, adj->htotal);
 	adj_hfp = hfp;
 	adj_hsync = hsync;
 	adj_hbp = hbp;
 	adj_hblanking = hblanking;
 
+	if (mode->clock > DSC_PIXEL_CLOCK) {
+		adj_hsync = DSC_HSYNC_LEN;
+		adj_hfp = DSC_HFP_LEN;
+		adj_hbp = DSC_HBP_LEN;
+		vref = (u64)adj->clock * 1000 * 1000 / (adj->htotal * adj->vtotal);
+		goto calculate_timing;
+	}
+
+	/* No need fixup for external monitor */
+	if (!ctx->pdata.panel_bridge)
+		return true;
+
 	/* HFP needs to be even */
 	if (hfp & 0x1) {
 		adj_hfp += 1;
@@ -2349,16 +2548,21 @@ static bool anx7625_bridge_mode_fixup(struct drm_bridge *bridge,
 			adj_hfp -= delta_adj;
 	}
 
-	DRM_DEV_DEBUG_DRIVER(dev, "after mode fixup\n");
-	DRM_DEV_DEBUG_DRIVER(dev, "hsync(%d), hfp(%d), hbp(%d), clock(%d)\n",
-			     adj_hsync, adj_hfp, adj_hbp, adj->clock);
+calculate_timing:
+
+	dev_dbg(dev, "after mode fixup\n");
+	dev_dbg(dev, "hsync(%d), hfp(%d), hbp(%d), clock(%d)\n",
+		adj_hsync, adj_hfp, adj_hbp, adj->clock);
 
 	/* Reconstruct timing */
 	adj->hsync_start = adj->hdisplay + adj_hfp;
 	adj->hsync_end = adj->hsync_start + adj_hsync;
 	adj->htotal = adj->hsync_end + adj_hbp;
-	DRM_DEV_DEBUG_DRIVER(dev, "hsync_start(%d), hsync_end(%d), htot(%d)\n",
-			     adj->hsync_start, adj->hsync_end, adj->htotal);
+	if (mode->clock > DSC_PIXEL_CLOCK)
+		adj->clock = (u64)vref * adj->htotal * adj->vtotal / 1000 / 1000;
+
+	dev_dbg(dev, "hsync_start(%d), hsync_end(%d), htot(%d), clock(%d)\n",
+		adj->hsync_start, adj->hsync_end, adj->htotal, adj->clock);
 
 	return true;
 }
diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.h b/drivers/gpu/drm/bridge/analogix/anx7625.h
index eb5580f1ab2f..db931f5800b1 100644
--- a/drivers/gpu/drm/bridge/analogix/anx7625.h
+++ b/drivers/gpu/drm/bridge/analogix/anx7625.h
@@ -149,6 +149,8 @@
 #define HFP_HBP_DEF		((HBLANKING_MIN - SYNC_LEN_DEF) / 2)
 #define VIDEO_CONTROL_0	0x08
 
+#define  TOTAL_LINES_L          0x12
+#define  TOTAL_LINES_H          0x13
 #define  ACTIVE_LINES_L         0x14
 #define  ACTIVE_LINES_H         0x15  /* Bit[7:6] are reserved */
 #define  VERTICAL_FRONT_PORCH   0x16
@@ -166,6 +168,32 @@
 #define  HORIZONTAL_BACK_PORCH_L      0x21
 #define  HORIZONTAL_BACK_PORCH_H      0x22  /* Bit[7:4] are reserved */
 
+#define  H_BLANK_L                    0x3E
+#define  H_BLANK_H                    0x3F
+#define  DSC_COMPRESS_RATIO           3
+#define  dsc_div(X)                   ((X) / DSC_COMPRESS_RATIO)
+#define  DSC_SLICE_NUM                2
+#define  DSC_PIXEL_CLOCK              250000
+#define  DSC_HSYNC_LEN                90
+#define  DSC_HFP_LEN                  177
+#define  DSC_HBP_LEN                  297
+
+#define  TOTAL_PIXEL_L_7E             0x50
+#define  TOTAL_PIXEL_H_7E             0x51  /* bit[7:6] are reserved */
+#define  ACTIVE_PIXEL_L_7E            0x52
+#define  ACTIVE_PIXEL_H_7E            0x53  /* bit[7:6] are reserved */
+#define  HORIZON_FRONT_PORCH_L_7E     0x54
+#define  HORIZON_FRONT_PORCH_H_7E     0x55
+#define  HORIZON_SYNC_WIDTH_L_7E      0x56
+#define  HORIZON_SYNC_WIDTH_H_7E      0x57
+#define  HORIZON_BACK_PORCH_L_7E      0x58
+#define  HORIZON_BACK_PORCH_H_7E      0x59
+
+#define  PPS_SIZE                     128
+#define  PPS_BLOCK_SIZE               32
+#define  R_PPS_REG_0                  0x80
+#define  R_I2C_1                      0x81
+
 /******** END of I2C Address 0x72 *********/
 
 /***************************************************************/
@@ -415,6 +443,7 @@ enum audio_wd_len {
 #define MAX_EDID_BLOCK	3
 #define EDID_TRY_CNT	3
 #define SUPPORT_PIXEL_CLOCK	300000
+#define SUPPORT_MIN_PIXEL_CLOCK	50000
 
 /***************** Display End *****************/
 
@@ -454,6 +483,7 @@ struct anx7625_data {
 	int hpd_high_cnt;
 	int dp_en;
 	int hdcp_cp;
+	bool dsc_en;
 	/* Lock for work queue */
 	struct mutex lock;
 	struct device *dev;
@@ -479,6 +509,8 @@ struct anx7625_data {
 	struct drm_connector *connector;
 	struct mipi_dsi_device *dsi;
 	struct drm_dp_aux aux;
+	struct drm_dsc_config dsc;
+	char pps_table[PPS_SIZE];
 };
 
 #endif  /* __ANX7625_H__ */
-- 
2.25.1
Re: [PATCH] drm/bridge:anx7625: Enable DSC feature
Posted by kernel test robot 10 months, 1 week ago
Hi Xin,

kernel test robot noticed the following build errors:

[auto build test ERROR on linus/master]
[also build test ERROR on drm-misc/drm-misc-next v6.14-rc2 next-20250214]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Xin-Ji/drm-bridge-anx7625-Enable-DSC-feature/20250213-203558
base:   linus/master
patch link:    https://lore.kernel.org/r/20250213123331.3016824-1-xji%40analogixsemi.com
patch subject: [PATCH] drm/bridge:anx7625: Enable DSC feature
config: i386-buildonly-randconfig-004-20250215 (https://download.01.org/0day-ci/archive/20250215/202502150643.fDWGayDK-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250215/202502150643.fDWGayDK-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202502150643.fDWGayDK-lkp@intel.com/

All errors (new ones prefixed by >>):

   ld: drivers/gpu/drm/bridge/analogix/anx7625.o: in function `anx7625_bridge_mode_fixup':
>> drivers/gpu/drm/bridge/analogix/anx7625.c:2562: undefined reference to `__udivdi3'
>> ld: drivers/gpu/drm/bridge/analogix/anx7625.c:2472: undefined reference to `__udivdi3'


vim +2562 drivers/gpu/drm/bridge/analogix/anx7625.c

  2440	
  2441	static bool anx7625_bridge_mode_fixup(struct drm_bridge *bridge,
  2442					      const struct drm_display_mode *mode,
  2443					      struct drm_display_mode *adj)
  2444	{
  2445		struct anx7625_data *ctx = bridge_to_anx7625(bridge);
  2446		struct device *dev = ctx->dev;
  2447		u32 hsync, hfp, hbp, hblanking;
  2448		u32 adj_hsync, adj_hfp, adj_hbp, adj_hblanking, delta_adj;
  2449		u32 vref, adj_clock;
  2450	
  2451		DRM_DEV_DEBUG_DRIVER(dev, "drm mode fixup set\n");
  2452	
  2453		hsync = mode->hsync_end - mode->hsync_start;
  2454		hfp = mode->hsync_start - mode->hdisplay;
  2455		hbp = mode->htotal - mode->hsync_end;
  2456		hblanking = mode->htotal - mode->hdisplay;
  2457	
  2458		dev_dbg(dev, "before mode fixup\n");
  2459		dev_dbg(dev, "hsync(%d), hfp(%d), hbp(%d), clock(%d)\n",
  2460			hsync, hfp, hbp, adj->clock);
  2461		dev_dbg(dev, "hsync_start(%d), hsync_end(%d), htot(%d)\n",
  2462			adj->hsync_start, adj->hsync_end, adj->htotal);
  2463		adj_hfp = hfp;
  2464		adj_hsync = hsync;
  2465		adj_hbp = hbp;
  2466		adj_hblanking = hblanking;
  2467	
  2468		if (mode->clock > DSC_PIXEL_CLOCK) {
  2469			adj_hsync = DSC_HSYNC_LEN;
  2470			adj_hfp = DSC_HFP_LEN;
  2471			adj_hbp = DSC_HBP_LEN;
> 2472			vref = (u64)adj->clock * 1000 * 1000 / (adj->htotal * adj->vtotal);
  2473			goto calculate_timing;
  2474		}
  2475	
  2476		/* No need fixup for external monitor */
  2477		if (!ctx->pdata.panel_bridge)
  2478			return true;
  2479	
  2480		/* HFP needs to be even */
  2481		if (hfp & 0x1) {
  2482			adj_hfp += 1;
  2483			adj_hblanking += 1;
  2484		}
  2485	
  2486		/* HBP needs to be even */
  2487		if (hbp & 0x1) {
  2488			adj_hbp -= 1;
  2489			adj_hblanking -= 1;
  2490		}
  2491	
  2492		/* HSYNC needs to be even */
  2493		if (hsync & 0x1) {
  2494			if (adj_hblanking < hblanking)
  2495				adj_hsync += 1;
  2496			else
  2497				adj_hsync -= 1;
  2498		}
  2499	
  2500		/*
  2501		 * Once illegal timing detected, use default HFP, HSYNC, HBP
  2502		 * This adjusting made for built-in eDP panel, for the externel
  2503		 * DP monitor, may need return false.
  2504		 */
  2505		if (hblanking < HBLANKING_MIN || (hfp < HP_MIN && hbp < HP_MIN)) {
  2506			adj_hsync = SYNC_LEN_DEF;
  2507			adj_hfp = HFP_HBP_DEF;
  2508			adj_hbp = HFP_HBP_DEF;
  2509			vref = adj->clock * 1000 / (adj->htotal * adj->vtotal);
  2510			if (hblanking < HBLANKING_MIN) {
  2511				delta_adj = HBLANKING_MIN - hblanking;
  2512				adj_clock = vref * delta_adj * adj->vtotal;
  2513				adj->clock += DIV_ROUND_UP(adj_clock, 1000);
  2514			} else {
  2515				delta_adj = hblanking - HBLANKING_MIN;
  2516				adj_clock = vref * delta_adj * adj->vtotal;
  2517				adj->clock -= DIV_ROUND_UP(adj_clock, 1000);
  2518			}
  2519	
  2520			DRM_WARN("illegal hblanking timing, use default.\n");
  2521			DRM_WARN("hfp(%d), hbp(%d), hsync(%d).\n", hfp, hbp, hsync);
  2522		} else if (adj_hfp < HP_MIN) {
  2523			/* Adjust hfp if hfp less than HP_MIN */
  2524			delta_adj = HP_MIN - adj_hfp;
  2525			adj_hfp = HP_MIN;
  2526	
  2527			/*
  2528			 * Balance total HBlanking pixel, if HBP does not have enough
  2529			 * space, adjust HSYNC length, otherwise adjust HBP
  2530			 */
  2531			if ((adj_hbp - delta_adj) < HP_MIN)
  2532				/* HBP not enough space */
  2533				adj_hsync -= delta_adj;
  2534			else
  2535				adj_hbp -= delta_adj;
  2536		} else if (adj_hbp < HP_MIN) {
  2537			delta_adj = HP_MIN - adj_hbp;
  2538			adj_hbp = HP_MIN;
  2539	
  2540			/*
  2541			 * Balance total HBlanking pixel, if HBP hasn't enough space,
  2542			 * adjust HSYNC length, otherwize adjust HBP
  2543			 */
  2544			if ((adj_hfp - delta_adj) < HP_MIN)
  2545				/* HFP not enough space */
  2546				adj_hsync -= delta_adj;
  2547			else
  2548				adj_hfp -= delta_adj;
  2549		}
  2550	
  2551	calculate_timing:
  2552	
  2553		dev_dbg(dev, "after mode fixup\n");
  2554		dev_dbg(dev, "hsync(%d), hfp(%d), hbp(%d), clock(%d)\n",
  2555			adj_hsync, adj_hfp, adj_hbp, adj->clock);
  2556	
  2557		/* Reconstruct timing */
  2558		adj->hsync_start = adj->hdisplay + adj_hfp;
  2559		adj->hsync_end = adj->hsync_start + adj_hsync;
  2560		adj->htotal = adj->hsync_end + adj_hbp;
  2561		if (mode->clock > DSC_PIXEL_CLOCK)
> 2562			adj->clock = (u64)vref * adj->htotal * adj->vtotal / 1000 / 1000;
  2563	
  2564		dev_dbg(dev, "hsync_start(%d), hsync_end(%d), htot(%d), clock(%d)\n",
  2565			adj->hsync_start, adj->hsync_end, adj->htotal, adj->clock);
  2566	
  2567		return true;
  2568	}
  2569	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH] drm/bridge:anx7625: Enable DSC feature
Posted by kernel test robot 10 months, 1 week ago
Hi Xin,

kernel test robot noticed the following build errors:

[auto build test ERROR on linus/master]
[also build test ERROR on drm-misc/drm-misc-next v6.14-rc2 next-20250214]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Xin-Ji/drm-bridge-anx7625-Enable-DSC-feature/20250213-203558
base:   linus/master
patch link:    https://lore.kernel.org/r/20250213123331.3016824-1-xji%40analogixsemi.com
patch subject: [PATCH] drm/bridge:anx7625: Enable DSC feature
config: arm-randconfig-003-20250215 (https://download.01.org/0day-ci/archive/20250215/202502150516.xZBLDIlz-lkp@intel.com/config)
compiler: arm-linux-gnueabi-gcc (GCC) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250215/202502150516.xZBLDIlz-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202502150516.xZBLDIlz-lkp@intel.com/

All errors (new ones prefixed by >>):

   arm-linux-gnueabi-ld: drivers/gpu/drm/bridge/analogix/anx7625.o: in function `anx7625_bridge_mode_fixup':
>> drivers/gpu/drm/bridge/analogix/anx7625.c:2472:(.text.anx7625_bridge_mode_fixup+0x9e): undefined reference to `__aeabi_uldivmod'
>> arm-linux-gnueabi-ld: drivers/gpu/drm/bridge/analogix/anx7625.c:2562:(.text.anx7625_bridge_mode_fixup+0x10e): undefined reference to `__aeabi_uldivmod'


vim +2472 drivers/gpu/drm/bridge/analogix/anx7625.c

  2440	
  2441	static bool anx7625_bridge_mode_fixup(struct drm_bridge *bridge,
  2442					      const struct drm_display_mode *mode,
  2443					      struct drm_display_mode *adj)
  2444	{
  2445		struct anx7625_data *ctx = bridge_to_anx7625(bridge);
  2446		struct device *dev = ctx->dev;
  2447		u32 hsync, hfp, hbp, hblanking;
  2448		u32 adj_hsync, adj_hfp, adj_hbp, adj_hblanking, delta_adj;
  2449		u32 vref, adj_clock;
  2450	
  2451		DRM_DEV_DEBUG_DRIVER(dev, "drm mode fixup set\n");
  2452	
  2453		hsync = mode->hsync_end - mode->hsync_start;
  2454		hfp = mode->hsync_start - mode->hdisplay;
  2455		hbp = mode->htotal - mode->hsync_end;
  2456		hblanking = mode->htotal - mode->hdisplay;
  2457	
  2458		dev_dbg(dev, "before mode fixup\n");
  2459		dev_dbg(dev, "hsync(%d), hfp(%d), hbp(%d), clock(%d)\n",
  2460			hsync, hfp, hbp, adj->clock);
  2461		dev_dbg(dev, "hsync_start(%d), hsync_end(%d), htot(%d)\n",
  2462			adj->hsync_start, adj->hsync_end, adj->htotal);
  2463		adj_hfp = hfp;
  2464		adj_hsync = hsync;
  2465		adj_hbp = hbp;
  2466		adj_hblanking = hblanking;
  2467	
  2468		if (mode->clock > DSC_PIXEL_CLOCK) {
  2469			adj_hsync = DSC_HSYNC_LEN;
  2470			adj_hfp = DSC_HFP_LEN;
  2471			adj_hbp = DSC_HBP_LEN;
> 2472			vref = (u64)adj->clock * 1000 * 1000 / (adj->htotal * adj->vtotal);
  2473			goto calculate_timing;
  2474		}
  2475	
  2476		/* No need fixup for external monitor */
  2477		if (!ctx->pdata.panel_bridge)
  2478			return true;
  2479	
  2480		/* HFP needs to be even */
  2481		if (hfp & 0x1) {
  2482			adj_hfp += 1;
  2483			adj_hblanking += 1;
  2484		}
  2485	
  2486		/* HBP needs to be even */
  2487		if (hbp & 0x1) {
  2488			adj_hbp -= 1;
  2489			adj_hblanking -= 1;
  2490		}
  2491	
  2492		/* HSYNC needs to be even */
  2493		if (hsync & 0x1) {
  2494			if (adj_hblanking < hblanking)
  2495				adj_hsync += 1;
  2496			else
  2497				adj_hsync -= 1;
  2498		}
  2499	
  2500		/*
  2501		 * Once illegal timing detected, use default HFP, HSYNC, HBP
  2502		 * This adjusting made for built-in eDP panel, for the externel
  2503		 * DP monitor, may need return false.
  2504		 */
  2505		if (hblanking < HBLANKING_MIN || (hfp < HP_MIN && hbp < HP_MIN)) {
  2506			adj_hsync = SYNC_LEN_DEF;
  2507			adj_hfp = HFP_HBP_DEF;
  2508			adj_hbp = HFP_HBP_DEF;
  2509			vref = adj->clock * 1000 / (adj->htotal * adj->vtotal);
  2510			if (hblanking < HBLANKING_MIN) {
  2511				delta_adj = HBLANKING_MIN - hblanking;
  2512				adj_clock = vref * delta_adj * adj->vtotal;
  2513				adj->clock += DIV_ROUND_UP(adj_clock, 1000);
  2514			} else {
  2515				delta_adj = hblanking - HBLANKING_MIN;
  2516				adj_clock = vref * delta_adj * adj->vtotal;
  2517				adj->clock -= DIV_ROUND_UP(adj_clock, 1000);
  2518			}
  2519	
  2520			DRM_WARN("illegal hblanking timing, use default.\n");
  2521			DRM_WARN("hfp(%d), hbp(%d), hsync(%d).\n", hfp, hbp, hsync);
  2522		} else if (adj_hfp < HP_MIN) {
  2523			/* Adjust hfp if hfp less than HP_MIN */
  2524			delta_adj = HP_MIN - adj_hfp;
  2525			adj_hfp = HP_MIN;
  2526	
  2527			/*
  2528			 * Balance total HBlanking pixel, if HBP does not have enough
  2529			 * space, adjust HSYNC length, otherwise adjust HBP
  2530			 */
  2531			if ((adj_hbp - delta_adj) < HP_MIN)
  2532				/* HBP not enough space */
  2533				adj_hsync -= delta_adj;
  2534			else
  2535				adj_hbp -= delta_adj;
  2536		} else if (adj_hbp < HP_MIN) {
  2537			delta_adj = HP_MIN - adj_hbp;
  2538			adj_hbp = HP_MIN;
  2539	
  2540			/*
  2541			 * Balance total HBlanking pixel, if HBP hasn't enough space,
  2542			 * adjust HSYNC length, otherwize adjust HBP
  2543			 */
  2544			if ((adj_hfp - delta_adj) < HP_MIN)
  2545				/* HFP not enough space */
  2546				adj_hsync -= delta_adj;
  2547			else
  2548				adj_hfp -= delta_adj;
  2549		}
  2550	
  2551	calculate_timing:
  2552	
  2553		dev_dbg(dev, "after mode fixup\n");
  2554		dev_dbg(dev, "hsync(%d), hfp(%d), hbp(%d), clock(%d)\n",
  2555			adj_hsync, adj_hfp, adj_hbp, adj->clock);
  2556	
  2557		/* Reconstruct timing */
  2558		adj->hsync_start = adj->hdisplay + adj_hfp;
  2559		adj->hsync_end = adj->hsync_start + adj_hsync;
  2560		adj->htotal = adj->hsync_end + adj_hbp;
  2561		if (mode->clock > DSC_PIXEL_CLOCK)
> 2562			adj->clock = (u64)vref * adj->htotal * adj->vtotal / 1000 / 1000;
  2563	
  2564		dev_dbg(dev, "hsync_start(%d), hsync_end(%d), htot(%d), clock(%d)\n",
  2565			adj->hsync_start, adj->hsync_end, adj->htotal, adj->clock);
  2566	
  2567		return true;
  2568	}
  2569	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH] drm/bridge:anx7625: Enable DSC feature
Posted by Dmitry Baryshkov 10 months, 1 week ago
On Thu, Feb 13, 2025 at 08:33:30PM +0800, Xin Ji wrote:
> As anx7625 MIPI RX bandwidth(maximum 1.5Gbps per lane) and internal
> pixel clock(maximum 300M) limitation. Anx7625 must enable DSC feature
> while MIPI source want to output 4K30 resolution.

This commit message is pretty hard to read and understand for a
non-native speaker. Please consider rewriting it so that it is easier to
understand it.

> 
> Signed-off-by: Xin Ji <xji@analogixsemi.com>
> ---
>  drivers/gpu/drm/bridge/analogix/anx7625.c | 300 ++++++++++++++++++----
>  drivers/gpu/drm/bridge/analogix/anx7625.h |  32 +++
>  2 files changed, 284 insertions(+), 48 deletions(-)
> 
> diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c
> index 4be34d5c7a3b..7d86ab02f71c 100644
> --- a/drivers/gpu/drm/bridge/analogix/anx7625.c
> +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c
> @@ -22,6 +22,7 @@
>  
>  #include <drm/display/drm_dp_aux_bus.h>
>  #include <drm/display/drm_dp_helper.h>
> +#include <drm/display/drm_dsc_helper.h>
>  #include <drm/display/drm_hdcp_helper.h>
>  #include <drm/drm_atomic_helper.h>
>  #include <drm/drm_bridge.h>
> @@ -476,6 +477,138 @@ static int anx7625_set_k_value(struct anx7625_data *ctx)
>  				 MIPI_DIGITAL_ADJ_1, 0x3D);
>  }
>  
> +static int anx7625_set_dsc_params(struct anx7625_data *ctx)
> +{
> +	int ret, i;
> +	u16 htotal, vtotal;
> +
> +	if (!ctx->dsc_en)
> +		return 0;
> +
> +	/* Htotal */
> +	htotal = ctx->dt.hactive.min + ctx->dt.hfront_porch.min +
> +		ctx->dt.hback_porch.min + ctx->dt.hsync_len.min;
> +	ret = anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
> +				HORIZONTAL_TOTAL_PIXELS_L, htotal);
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
> +				 HORIZONTAL_TOTAL_PIXELS_H, htotal >> 8);
> +	/* Hactive */
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
> +				 HORIZONTAL_ACTIVE_PIXELS_L,
> +				 ctx->dt.hactive.min);
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
> +				 HORIZONTAL_ACTIVE_PIXELS_H,
> +				 ctx->dt.hactive.min >> 8);
> +	/* HFP */
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
> +				 HORIZONTAL_FRONT_PORCH_L,
> +				 ctx->dt.hfront_porch.min);
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
> +				 HORIZONTAL_FRONT_PORCH_H,
> +				 ctx->dt.hfront_porch.min >> 8);
> +	/* HWS */
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
> +				 HORIZONTAL_SYNC_WIDTH_L,
> +				 ctx->dt.hsync_len.min);
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
> +				 HORIZONTAL_SYNC_WIDTH_H,
> +				 ctx->dt.hsync_len.min >> 8);
> +	/* HBP */
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
> +				 HORIZONTAL_BACK_PORCH_L,
> +				 ctx->dt.hback_porch.min);
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
> +				 HORIZONTAL_BACK_PORCH_H,
> +				 ctx->dt.hback_porch.min >> 8);
> +	/* Vtotal */
> +	vtotal = ctx->dt.vactive.min + ctx->dt.vfront_porch.min +
> +		 ctx->dt.vback_porch.min + ctx->dt.vsync_len.min;
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client, TOTAL_LINES_L,
> +				 vtotal);
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client, TOTAL_LINES_H,
> +				 vtotal >> 8);
> +	/* Vactive */
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client, ACTIVE_LINES_L,
> +				 ctx->dt.vactive.min);
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client, ACTIVE_LINES_H,
> +				 ctx->dt.vactive.min >> 8);
> +	/* VFP */
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
> +				 VERTICAL_FRONT_PORCH, ctx->dt.vfront_porch.min);
> +	/* VWS */
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
> +				 VERTICAL_SYNC_WIDTH, ctx->dt.vsync_len.min);
> +	/* VBP */
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
> +				 VERTICAL_BACK_PORCH, ctx->dt.vback_porch.min);

This code mostly duplicates anx7625_dsi_video_timing_config() with the
I2C client being the only difference. Please consider extracting a
common helper to write the timings.

> +
> +	/* Htotal */
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, TOTAL_PIXEL_L_7E,
> +				 htotal);
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, TOTAL_PIXEL_H_7E,
> +				 htotal >> 8);
> +	/* Hactive */
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
> +				 ACTIVE_PIXEL_L_7E, ctx->dt.hactive.min);
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
> +				 ACTIVE_PIXEL_H_7E, ctx->dt.hactive.min >> 8);
> +	/* HFP */
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
> +				 HORIZON_FRONT_PORCH_L_7E,
> +				 ctx->dt.hfront_porch.min);
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
> +				 HORIZON_FRONT_PORCH_H_7E,
> +				 ctx->dt.hfront_porch.min >> 8);
> +	/* HWS */
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
> +				 HORIZON_SYNC_WIDTH_L_7E,
> +				 ctx->dt.hsync_len.min);
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
> +				 HORIZON_SYNC_WIDTH_H_7E,
> +				 ctx->dt.hsync_len.min >> 8);
> +	/* HBP */
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
> +				 HORIZON_BACK_PORCH_L_7E,
> +				 ctx->dt.hback_porch.min);
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
> +				 HORIZON_BACK_PORCH_H_7E,
> +				 ctx->dt.hback_porch.min >> 8);
> +
> +	/* Config DSC decoder internal blank timing for decoder to start */
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p1_client,
> +				 H_BLANK_L,
> +				 dsc_div(htotal - ctx->dt.hactive.min));
> +	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p1_client,
> +				 H_BLANK_H,
> +				 dsc_div(htotal - ctx->dt.hactive.min) >> 8);
> +
> +	/* Compress ratio  RATIO bit[7:6] */
> +	ret |= anx7625_write_and(ctx, ctx->i2c.rx_p0_client, R_I2C_1, 0x3F);
> +	ret |= anx7625_write_or(ctx, ctx->i2c.rx_p0_client, R_I2C_1,
> +				(5 - DSC_COMPRESS_RATIO) << 6);
> +	/*PPS table*/
> +	for (i = 0; i < PPS_SIZE; i += PPS_BLOCK_SIZE)
> +		ret |= anx7625_reg_block_write(ctx, ctx->i2c.rx_p2_client,
> +					       R_PPS_REG_0 + i, PPS_BLOCK_SIZE,
> +					       &ctx->pps_table[i]);
> +
> +	return ret;
> +}
> +
> +static int anx7625_timing_write(struct anx7625_data *ctx,
> +				struct i2c_client *client,
> +				u8 reg_addr, u16 reg_val, bool high_byte)
> +{
> +	u8 reg_data;
> +
> +	if (ctx->dsc_en)
> +		reg_val = dsc_div(reg_val);
> +
> +	reg_data = (high_byte ? (reg_val >> 8) : reg_val) & 0xFF;
> +
> +	return anx7625_reg_write(ctx, client, reg_addr, reg_data);
> +}

Ugh, no. Calculate htotal in the calling function and call
anx7625_reg_write() as you were doing that before.

> +
>  static int anx7625_dsi_video_timing_config(struct anx7625_data *ctx)
>  {
>  	struct device *dev = ctx->dev;
> @@ -506,34 +639,33 @@ static int anx7625_dsi_video_timing_config(struct anx7625_data *ctx)
>  	ret |= anx7625_write_or(ctx, ctx->i2c.rx_p1_client,
>  				MIPI_LANE_CTRL_0, ctx->pdata.mipi_lanes - 1);
>  
> -	/* Htotal */
>  	htotal = ctx->dt.hactive.min + ctx->dt.hfront_porch.min +
> -		ctx->dt.hback_porch.min + ctx->dt.hsync_len.min;
> -	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client,
> -			HORIZONTAL_TOTAL_PIXELS_L, htotal & 0xFF);
> -	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client,
> -			HORIZONTAL_TOTAL_PIXELS_H, htotal >> 8);
> +			 ctx->dt.hback_porch.min + ctx->dt.hsync_len.min;
> +	/* Htotal */
> +	ret |= anx7625_timing_write(ctx, ctx->i2c.rx_p2_client,
> +			HORIZONTAL_TOTAL_PIXELS_L, htotal, false);
> +	ret |= anx7625_timing_write(ctx, ctx->i2c.rx_p2_client,
> +			HORIZONTAL_TOTAL_PIXELS_H, htotal, true);
>  	/* Hactive */
> -	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client,
> -			HORIZONTAL_ACTIVE_PIXELS_L, ctx->dt.hactive.min & 0xFF);
> -	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client,
> -			HORIZONTAL_ACTIVE_PIXELS_H, ctx->dt.hactive.min >> 8);
> +	ret |= anx7625_timing_write(ctx, ctx->i2c.rx_p2_client,
> +			HORIZONTAL_ACTIVE_PIXELS_L, ctx->dt.hactive.min, false);
> +	ret |= anx7625_timing_write(ctx, ctx->i2c.rx_p2_client,
> +			HORIZONTAL_ACTIVE_PIXELS_H, ctx->dt.hactive.min, true);
>  	/* HFP */
> -	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client,
> -			HORIZONTAL_FRONT_PORCH_L, ctx->dt.hfront_porch.min);
> -	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client,
> -			HORIZONTAL_FRONT_PORCH_H,
> -			ctx->dt.hfront_porch.min >> 8);
> +	ret |= anx7625_timing_write(ctx, ctx->i2c.rx_p2_client,
> +			HORIZONTAL_FRONT_PORCH_L, ctx->dt.hfront_porch.min, false);
> +	ret |= anx7625_timing_write(ctx, ctx->i2c.rx_p2_client,
> +			HORIZONTAL_FRONT_PORCH_H, ctx->dt.hfront_porch.min, true);

Are porches also compressed? I think blanking signal timings are
transferred as is, without compression.

>  	/* HWS */
> -	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client,
> -			HORIZONTAL_SYNC_WIDTH_L, ctx->dt.hsync_len.min);
> -	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client,
> -			HORIZONTAL_SYNC_WIDTH_H, ctx->dt.hsync_len.min >> 8);
> +	ret |= anx7625_timing_write(ctx, ctx->i2c.rx_p2_client,
> +			HORIZONTAL_SYNC_WIDTH_L, ctx->dt.hsync_len.min, false);
> +	ret |= anx7625_timing_write(ctx, ctx->i2c.rx_p2_client,
> +			HORIZONTAL_SYNC_WIDTH_H, ctx->dt.hsync_len.min, true);
>  	/* HBP */
> -	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client,
> -			HORIZONTAL_BACK_PORCH_L, ctx->dt.hback_porch.min);
> -	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client,
> -			HORIZONTAL_BACK_PORCH_H, ctx->dt.hback_porch.min >> 8);
> +	ret |= anx7625_timing_write(ctx, ctx->i2c.rx_p2_client,
> +			HORIZONTAL_BACK_PORCH_L, ctx->dt.hback_porch.min, false);
> +	ret |= anx7625_timing_write(ctx, ctx->i2c.rx_p2_client,
> +			HORIZONTAL_BACK_PORCH_H, ctx->dt.hback_porch.min, true);
>  	/* Vactive */
>  	ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p2_client, ACTIVE_LINES_L,
>  			ctx->dt.vactive.min);
> @@ -663,9 +795,15 @@ static int anx7625_dsi_config(struct anx7625_data *ctx)
>  
>  	DRM_DEV_DEBUG_DRIVER(dev, "config dsi.\n");
>  
> -	/* DSC disable */
> -	ret = anx7625_write_and(ctx, ctx->i2c.rx_p0_client,
> -				R_DSC_CTRL_0, ~DSC_EN);
> +	ret = anx7625_set_dsc_params(ctx);
> +	if (ctx->dsc_en)
> +		/* DSC enable */
> +		ret |= anx7625_write_or(ctx, ctx->i2c.rx_p0_client,
> +					R_DSC_CTRL_0, DSC_EN);
> +	else
> +		/* DSC disable */
> +		ret |= anx7625_write_and(ctx, ctx->i2c.rx_p0_client,
> +					 R_DSC_CTRL_0, ~DSC_EN);
>  
>  	ret |= anx7625_api_dsi_config(ctx);
>  
> @@ -2083,6 +2221,7 @@ static int anx7625_setup_dsi_device(struct anx7625_data *ctx)
>  		MIPI_DSI_MODE_VIDEO_HSE	|
>  		MIPI_DSI_HS_PKT_END_ALIGNED;
>  
> +	dsi->dsc = &ctx->dsc;
>  	ctx->dsi = dsi;
>  
>  	return 0;
> @@ -2186,20 +2325,68 @@ anx7625_bridge_mode_valid(struct drm_bridge *bridge,
>  	struct anx7625_data *ctx = bridge_to_anx7625(bridge);
>  	struct device *dev = ctx->dev;
>  
> -	DRM_DEV_DEBUG_DRIVER(dev, "drm mode checking\n");
> +	dev_dbg(dev, "drm mode checking\n");
> +	if (mode->clock > SUPPORT_PIXEL_CLOCK)
> +		return MODE_CLOCK_HIGH;
> +
> +	if (mode->clock < SUPPORT_MIN_PIXEL_CLOCK)
> +		return MODE_CLOCK_LOW;
>  
> -	/* Max 1200p at 5.4 Ghz, one lane, pixel clock 300M */
> -	if (mode->clock > SUPPORT_PIXEL_CLOCK) {
> -		DRM_DEV_DEBUG_DRIVER(dev,
> -				     "drm mode invalid, pixelclock too high.\n");
> +	if (mode->clock > DSC_PIXEL_CLOCK && (mode->hdisplay % 3 != 0))

Magic number 3. Also is it actually required? I think DSC standard has
no such requirement.

>  		return MODE_CLOCK_HIGH;
> -	}
>  
> -	DRM_DEV_DEBUG_DRIVER(dev, "drm mode valid.\n");
> +	dev_dbg(dev, "drm mode valid.\n");
>  
>  	return MODE_OK;
>  }
>  
> +static void anx7625_dsc_enable(struct anx7625_data *ctx, bool en)
> +{
> +	int ret;
> +	struct device *dev = ctx->dev;
> +
> +	ctx->dsc_en = en;
> +
> +	if (en) {
> +		ctx->dsc.dsc_version_major = 1;
> +		ctx->dsc.dsc_version_minor = 1;
> +		ctx->dsc.slice_height = 8;
> +		ctx->dsc.slice_width = ctx->dt.hactive.min / DSC_SLICE_NUM;
> +		ctx->dsc.slice_count = DSC_SLICE_NUM;
> +		ctx->dsc.bits_per_component = 8;
> +		ctx->dsc.bits_per_pixel = 8 << 4; /* 4 fractional bits */
> +		ctx->dsc.block_pred_enable = true;
> +		ctx->dsc.native_420 = false;
> +		ctx->dsc.native_422 = false;
> +		ctx->dsc.simple_422 = false;
> +		ctx->dsc.vbr_enable = false;
> +		ctx->dsc.rc_model_size = DSC_RC_MODEL_SIZE_CONST;
> +		ctx->dsc.pic_width = ctx->dt.hactive.min;
> +		ctx->dsc.pic_height = ctx->dt.vactive.min;
> +		ctx->dsc.convert_rgb = 1;
> +		ctx->dsc.vbr_enable = 0;

A lot of this params should be coming from the sink device. You have to
get them from the DPCD.

> +
> +		drm_dsc_set_rc_buf_thresh(&ctx->dsc);
> +		drm_dsc_set_const_params(&ctx->dsc);
> +
> +		ctx->dsc.initial_scale_value = drm_dsc_initial_scale_value(&ctx->dsc);
> +		ctx->dsc.line_buf_depth = ctx->dsc.bits_per_component + 1;
> +		ret = drm_dsc_setup_rc_params(&ctx->dsc, DRM_DSC_1_2_444);
> +		if (ret < 0)
> +			dev_warn(dev, "drm_dsc_setup_rc_params ret %d\n", ret);
> +
> +		drm_dsc_compute_rc_parameters(&ctx->dsc);

You have ignored return value. Please handle the function returning an
error.

> +
> +		drm_dsc_pps_payload_pack((struct drm_dsc_picture_parameter_set *)&ctx->pps_table,
> +					 &ctx->dsc);
> +		dev_dbg(dev, "anx7625 enable dsc\n");
> +	} else {
> +		ctx->dsc.dsc_version_major = 0;
> +		ctx->dsc.dsc_version_minor = 0;
> +		dev_dbg(dev, "anx7625 disable dsc\n");
> +	}
> +}
> +
>  static void anx7625_bridge_mode_set(struct drm_bridge *bridge,
>  				    const struct drm_display_mode *old_mode,
>  				    const struct drm_display_mode *mode)
> @@ -2244,6 +2431,11 @@ static void anx7625_bridge_mode_set(struct drm_bridge *bridge,
>  	DRM_DEV_DEBUG_DRIVER(dev, "vsync_end(%d),vtotal(%d).\n",
>  			     mode->vsync_end,
>  			     mode->vtotal);
> +
> +	if (mode->clock > DSC_PIXEL_CLOCK)

What if the sink doesn't support DSC? The decision whether to enable DSC
or not should be happening in the atomic_check(). Likewise mode_valid
should be able to check if your bridge and the sink can agree on DSC
params and reject modes if they can not.

> +		anx7625_dsc_enable(ctx, true);
> +	else
> +		anx7625_dsc_enable(ctx, false);
>  }
>  
>  static bool anx7625_bridge_mode_fixup(struct drm_bridge *bridge,
> @@ -2258,26 +2450,33 @@ static bool anx7625_bridge_mode_fixup(struct drm_bridge *bridge,
>  
>  	DRM_DEV_DEBUG_DRIVER(dev, "drm mode fixup set\n");
>  
> -	/* No need fixup for external monitor */
> -	if (!ctx->pdata.panel_bridge)
> -		return true;
> -
>  	hsync = mode->hsync_end - mode->hsync_start;
>  	hfp = mode->hsync_start - mode->hdisplay;
>  	hbp = mode->htotal - mode->hsync_end;
>  	hblanking = mode->htotal - mode->hdisplay;
>  
> -	DRM_DEV_DEBUG_DRIVER(dev, "before mode fixup\n");
> -	DRM_DEV_DEBUG_DRIVER(dev, "hsync(%d), hfp(%d), hbp(%d), clock(%d)\n",
> -			     hsync, hfp, hbp, adj->clock);
> -	DRM_DEV_DEBUG_DRIVER(dev, "hsync_start(%d), hsync_end(%d), htot(%d)\n",
> -			     adj->hsync_start, adj->hsync_end, adj->htotal);
> -
> +	dev_dbg(dev, "before mode fixup\n");
> +	dev_dbg(dev, "hsync(%d), hfp(%d), hbp(%d), clock(%d)\n",
> +		hsync, hfp, hbp, adj->clock);
> +	dev_dbg(dev, "hsync_start(%d), hsync_end(%d), htot(%d)\n",
> +		adj->hsync_start, adj->hsync_end, adj->htotal);

No. Please use drm_dbg_driver(), but not dev_dbg(). And anyway, this
should go to a separate commit.

>  	adj_hfp = hfp;
>  	adj_hsync = hsync;
>  	adj_hbp = hbp;
>  	adj_hblanking = hblanking;
>  
> +	if (mode->clock > DSC_PIXEL_CLOCK) {
> +		adj_hsync = DSC_HSYNC_LEN;
> +		adj_hfp = DSC_HFP_LEN;
> +		adj_hbp = DSC_HBP_LEN;
> +		vref = (u64)adj->clock * 1000 * 1000 / (adj->htotal * adj->vtotal);
> +		goto calculate_timing;
> +	}
> +
> +	/* No need fixup for external monitor */
> +	if (!ctx->pdata.panel_bridge)
> +		return true;
> +
>  	/* HFP needs to be even */
>  	if (hfp & 0x1) {
>  		adj_hfp += 1;
> @@ -2349,16 +2548,21 @@ static bool anx7625_bridge_mode_fixup(struct drm_bridge *bridge,
>  			adj_hfp -= delta_adj;
>  	}
>  
> -	DRM_DEV_DEBUG_DRIVER(dev, "after mode fixup\n");
> -	DRM_DEV_DEBUG_DRIVER(dev, "hsync(%d), hfp(%d), hbp(%d), clock(%d)\n",
> -			     adj_hsync, adj_hfp, adj_hbp, adj->clock);
> +calculate_timing:
> +
> +	dev_dbg(dev, "after mode fixup\n");
> +	dev_dbg(dev, "hsync(%d), hfp(%d), hbp(%d), clock(%d)\n",
> +		adj_hsync, adj_hfp, adj_hbp, adj->clock);
>  
>  	/* Reconstruct timing */
>  	adj->hsync_start = adj->hdisplay + adj_hfp;
>  	adj->hsync_end = adj->hsync_start + adj_hsync;
>  	adj->htotal = adj->hsync_end + adj_hbp;
> -	DRM_DEV_DEBUG_DRIVER(dev, "hsync_start(%d), hsync_end(%d), htot(%d)\n",
> -			     adj->hsync_start, adj->hsync_end, adj->htotal);
> +	if (mode->clock > DSC_PIXEL_CLOCK)
> +		adj->clock = (u64)vref * adj->htotal * adj->vtotal / 1000 / 1000;
> +
> +	dev_dbg(dev, "hsync_start(%d), hsync_end(%d), htot(%d), clock(%d)\n",
> +		adj->hsync_start, adj->hsync_end, adj->htotal, adj->clock);
>  
>  	return true;
>  }
> diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.h b/drivers/gpu/drm/bridge/analogix/anx7625.h
> index eb5580f1ab2f..db931f5800b1 100644
> --- a/drivers/gpu/drm/bridge/analogix/anx7625.h
> +++ b/drivers/gpu/drm/bridge/analogix/anx7625.h
> @@ -149,6 +149,8 @@
>  #define HFP_HBP_DEF		((HBLANKING_MIN - SYNC_LEN_DEF) / 2)
>  #define VIDEO_CONTROL_0	0x08
>  
> +#define  TOTAL_LINES_L          0x12
> +#define  TOTAL_LINES_H          0x13
>  #define  ACTIVE_LINES_L         0x14
>  #define  ACTIVE_LINES_H         0x15  /* Bit[7:6] are reserved */
>  #define  VERTICAL_FRONT_PORCH   0x16
> @@ -166,6 +168,32 @@
>  #define  HORIZONTAL_BACK_PORCH_L      0x21
>  #define  HORIZONTAL_BACK_PORCH_H      0x22  /* Bit[7:4] are reserved */
>  
> +#define  H_BLANK_L                    0x3E
> +#define  H_BLANK_H                    0x3F
> +#define  DSC_COMPRESS_RATIO           3
> +#define  dsc_div(X)                   ((X) / DSC_COMPRESS_RATIO)

This assumes 1:3 ratio. Does anx7625 support only 8bpp / 8bpc mode? Or
does 1:3 come from some other ratio?

> +#define  DSC_SLICE_NUM                2
> +#define  DSC_PIXEL_CLOCK              250000
> +#define  DSC_HSYNC_LEN                90
> +#define  DSC_HFP_LEN                  177
> +#define  DSC_HBP_LEN                  297
> +
> +#define  TOTAL_PIXEL_L_7E             0x50
> +#define  TOTAL_PIXEL_H_7E             0x51  /* bit[7:6] are reserved */
> +#define  ACTIVE_PIXEL_L_7E            0x52
> +#define  ACTIVE_PIXEL_H_7E            0x53  /* bit[7:6] are reserved */
> +#define  HORIZON_FRONT_PORCH_L_7E     0x54
> +#define  HORIZON_FRONT_PORCH_H_7E     0x55
> +#define  HORIZON_SYNC_WIDTH_L_7E      0x56
> +#define  HORIZON_SYNC_WIDTH_H_7E      0x57
> +#define  HORIZON_BACK_PORCH_L_7E      0x58
> +#define  HORIZON_BACK_PORCH_H_7E      0x59
> +
> +#define  PPS_SIZE                     128
> +#define  PPS_BLOCK_SIZE               32
> +#define  R_PPS_REG_0                  0x80
> +#define  R_I2C_1                      0x81
> +
>  /******** END of I2C Address 0x72 *********/
>  
>  /***************************************************************/
> @@ -415,6 +443,7 @@ enum audio_wd_len {
>  #define MAX_EDID_BLOCK	3
>  #define EDID_TRY_CNT	3
>  #define SUPPORT_PIXEL_CLOCK	300000
> +#define SUPPORT_MIN_PIXEL_CLOCK	50000
>  
>  /***************** Display End *****************/
>  
> @@ -454,6 +483,7 @@ struct anx7625_data {
>  	int hpd_high_cnt;
>  	int dp_en;
>  	int hdcp_cp;
> +	bool dsc_en;
>  	/* Lock for work queue */
>  	struct mutex lock;
>  	struct device *dev;
> @@ -479,6 +509,8 @@ struct anx7625_data {
>  	struct drm_connector *connector;
>  	struct mipi_dsi_device *dsi;
>  	struct drm_dp_aux aux;
> +	struct drm_dsc_config dsc;
> +	char pps_table[PPS_SIZE];

pps_table and drm_dsc_config can vary depending on the mode and
connected sink. THey should be a part of the atomic state rather than a
long-living anx7625_data. So does dsc_en.

>  };
>  
>  #endif  /* __ANX7625_H__ */
> -- 
> 2.25.1
> 

-- 
With best wishes
Dmitry