drivers/gpu/drm/bridge/analogix/anx7625.c | 300 ++++++++++++++++++---- drivers/gpu/drm/bridge/analogix/anx7625.h | 32 +++ 2 files changed, 284 insertions(+), 48 deletions(-)
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
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
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
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
© 2016 - 2025 Red Hat, Inc.