diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c index aba4cfb6d2e5..4146f71097c6 100644 --- a/drivers/gpu/drm/msm/dp/dp_catalog.c +++ b/drivers/gpu/drm/msm/dp/dp_catalog.c @@ -779,6 +779,7 @@ static void dp_catalog_panel_config_dto(struct dp_catalog_panel *panel, { struct dp_catalog_private *catalog; struct dp_io_data *io_data; + u32 dsc_dto; if (!panel) { pr_err("invalid input\n"); @@ -805,7 +806,12 @@ static void dp_catalog_panel_config_dto(struct dp_catalog_panel *panel, return; } - dp_write(catalog->exe_mode, io_data, MMSS_DP_DSC_DTO, ack << 1); + dsc_dto = dp_read(catalog->exe_mode, io_data, MMSS_DP_DSC_DTO); + if (ack) + dsc_dto = BIT(1); + else + dsc_dto &= ~BIT(1); + dp_write(catalog->exe_mode, io_data, MMSS_DP_DSC_DTO, dsc_dto); } static void dp_catalog_ctrl_lane_mapping(struct dp_catalog_ctrl *ctrl, @@ -1126,6 +1132,107 @@ static void dp_catalog_panel_tpg_cfg(struct dp_catalog_panel *panel, wmb(); /* ensure Timing generator is turned on */ } +static void dp_catalog_panel_dsc_cfg(struct dp_catalog_panel *panel) +{ + struct dp_catalog_private *catalog; + struct dp_io_data *io_data; + u32 reg, offset; + int i; + + if (!panel) { + pr_err("invalid input\n"); + return; + } + + if (panel->stream_id >= DP_STREAM_MAX) { + pr_err("invalid stream_id:%d\n", panel->stream_id); + return; + } + + catalog = dp_catalog_get_priv(panel); + + if (panel->stream_id == DP_STREAM_0) + io_data = catalog->io.dp_p0; + else + io_data = catalog->io.dp_p1; + + dp_write(catalog->exe_mode, io_data, MMSS_DP_DSC_DTO_COUNT, + panel->dsc.dto_count); + + reg = dp_read(catalog->exe_mode, io_data, MMSS_DP_DSC_DTO); + if (panel->dsc.dto_en) { + reg |= BIT(0); + reg |= (panel->dsc.dto_n << 8); + reg |= (panel->dsc.dto_d << 16); + } + dp_write(catalog->exe_mode, io_data, MMSS_DP_DSC_DTO, reg); + + io_data = catalog->io.dp_link; + + if (panel->stream_id == DP_STREAM_0) + offset = 0; + else + offset = DP1_COMPRESSION_MODE_CTRL - DP_COMPRESSION_MODE_CTRL; + + dp_write(catalog->exe_mode, io_data, DP_PPS_HB_0_3 + offset, 0x7F1000); + dp_write(catalog->exe_mode, io_data, DP_PPS_PB_0_3 + offset, 0xA22300); + + for (i = 0; i < panel->dsc.parity_word_len; i++) + dp_write(catalog->exe_mode, io_data, + DP_PPS_PB_4_7 + (i << 2) + offset, + panel->dsc.parity_word[i]); + + for (i = 0; i < panel->dsc.pps_word_len; i++) + dp_write(catalog->exe_mode, io_data, + DP_PPS_PPS_0_3 + (i << 2) + offset, + panel->dsc.pps_word[i]); + + reg = 0; + if (panel->dsc.dsc_en) { + reg = BIT(0); + reg |= (panel->dsc.eol_byte_num << 3); + reg |= (panel->dsc.slice_per_pkt << 5); + reg |= (panel->dsc.bytes_per_pkt << 16); + reg |= (panel->dsc.be_in_lane << 10); + } + dp_write(catalog->exe_mode, io_data, + DP_COMPRESSION_MODE_CTRL + offset, reg); + + pr_debug("compression:0x%x for stream:%d\n", + reg, panel->stream_id); +} + +static void dp_catalog_panel_pps_flush(struct dp_catalog_panel *panel) +{ + struct dp_catalog_private *catalog; + struct dp_io_data *io_data; + u32 dp_flush, offset; + + if (!panel) { + pr_err("invalid input\n"); + return; + } + + if (panel->stream_id >= DP_STREAM_MAX) { + pr_err("invalid stream_id:%d\n", panel->stream_id); + return; + } + + catalog = dp_catalog_get_priv(panel); + io_data = catalog->io.dp_link; + + if (panel->stream_id == DP_STREAM_0) + offset = 0; + else + offset = MMSS_DP1_FLUSH - MMSS_DP_FLUSH; + + dp_flush = dp_read(catalog->exe_mode, io_data, MMSS_DP_FLUSH + offset); + dp_flush |= BIT(0); + dp_write(catalog->exe_mode, io_data, MMSS_DP_FLUSH + offset, dp_flush); + + pr_debug("pps flush for stream:%d\n", panel->stream_id); +} + static void dp_catalog_ctrl_reset(struct dp_catalog_ctrl *ctrl) { u32 sw_reset; @@ -1457,6 +1564,39 @@ static u32 dp_catalog_ctrl_read_phy_pattern(struct dp_catalog_ctrl *ctrl) return dp_read(catalog->exe_mode, io_data, DP_MAINLINK_READY); } +static void dp_catalog_ctrl_fec_config(struct dp_catalog_ctrl *ctrl, + bool enable) +{ + struct dp_catalog_private *catalog; + struct dp_io_data *io_data = NULL; + u32 reg; + + if (!ctrl) { + pr_err("invalid input\n"); + return; + } + + catalog = dp_catalog_get_priv(ctrl); + io_data = catalog->io.dp_link; + + reg = dp_read(catalog->exe_mode, io_data, DP_MAINLINK_CTRL); + + /* + * fec_en = BIT(12) + * fec_seq_mode = BIT(22) + * sde_flush = BIT(23) | BIT(24) + * fb_boundary_sel = BIT(25) + */ + if (enable) + reg |= BIT(12) | BIT(22) | BIT(23) | BIT(24) | BIT(25); + else + reg &= ~BIT(12); + + dp_write(catalog->exe_mode, io_data, DP_MAINLINK_CTRL, reg); + /* make sure mainlink configuration is updated with fec sequence */ + wmb(); +} + static int dp_catalog_reg_dump(struct dp_catalog *dp_catalog, char *name, u8 **out_buf, u32 *out_buf_len) { @@ -2276,6 +2416,7 @@ struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser) .channel_alloc = dp_catalog_ctrl_channel_alloc, .update_rg = dp_catalog_ctrl_update_rg, .channel_dealloc = dp_catalog_ctrl_channel_dealloc, + .fec_config = dp_catalog_ctrl_fec_config, }; struct dp_catalog_audio audio = { .init = dp_catalog_audio_init, @@ -2296,6 +2437,8 @@ struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser) .update_transfer_unit = dp_catalog_panel_update_transfer_unit, .config_ctrl = dp_catalog_panel_config_ctrl, .config_dto = dp_catalog_panel_config_dto, + .dsc_cfg = dp_catalog_panel_dsc_cfg, + .pps_flush = dp_catalog_panel_pps_flush, }; if (!dev || !parser) { diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h index 4fd440dacd3c..c605238c8e9a 100644 --- a/drivers/gpu/drm/msm/dp/dp_catalog.h +++ b/drivers/gpu/drm/msm/dp/dp_catalog.h @@ -128,6 +128,7 @@ struct dp_catalog_ctrl { u32 y_frac_enum); void (*channel_dealloc)(struct dp_catalog_ctrl *ctrl, u32 ch, u32 ch_start_timeslot, u32 tot_ch_cnt); + void (*fec_config)(struct dp_catalog_ctrl *ctrl, bool enable); }; #define HEADER_BYTE_2_BIT 0 @@ -169,6 +170,26 @@ struct dp_catalog_audio { void (*safe_to_exit_level)(struct dp_catalog_audio *audio); }; +struct dp_dsc_cfg_data { + bool dsc_en; + char pps[128]; + u32 pps_len; + u32 pps_word[32]; + u32 pps_word_len; + u8 parity[32]; + u8 parity_len; + u32 parity_word[8]; + u32 parity_word_len; + u32 slice_per_pkt; + u32 bytes_per_pkt; + u32 eol_byte_num; + u32 be_in_lane; + u32 dto_en; + u32 dto_n; + u32 dto_d; + u32 dto_count; +}; + struct dp_catalog_panel { u32 total; u32 sync_start; @@ -198,6 +219,7 @@ struct dp_catalog_panel { enum dp_stream_id stream_id; bool widebus_en; + struct dp_dsc_cfg_data dsc; int (*timing_cfg)(struct dp_catalog_panel *panel); void (*config_hdr)(struct dp_catalog_panel *panel, bool en); @@ -209,6 +231,8 @@ struct dp_catalog_panel { void (*update_transfer_unit)(struct dp_catalog_panel *panel); void (*config_ctrl)(struct dp_catalog_panel *panel, u32 cfg); void (*config_dto)(struct dp_catalog_panel *panel, bool ack); + void (*dsc_cfg)(struct dp_catalog_panel *panel); + void (*pps_flush)(struct dp_catalog_panel *panel); }; struct dp_catalog; @@ -281,7 +305,7 @@ static inline u8 dp_header_get_parity(u32 data) u8 iData = 0; u8 i = 0; u8 parity_byte; - u8 num_byte = (data & 0xFF00) > 0 ? 8 : 2; + u8 num_byte = (data > 0xFF) ? 8 : 2; for (i = 0; i < num_byte; i++) { iData = (data >> i*4) & 0xF; diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c index 8fffd482aef0..7a9a41f3ae21 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c @@ -76,6 +76,7 @@ struct dp_ctrl_private { bool orientation; bool power_on; bool mst_mode; + bool fec_mode; atomic_t aborted; @@ -502,6 +503,7 @@ end: static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl) { int ret = 0; + const unsigned int fec_cfg_dpcd = 0x120; if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) goto end; @@ -513,6 +515,9 @@ static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl) */ ctrl->catalog->reset(ctrl->catalog); + if (ctrl->fec_mode) + drm_dp_dpcd_writeb(ctrl->aux->drm_aux, fec_cfg_dpcd, 0x01); + ret = dp_ctrl_link_train(ctrl); end: @@ -801,7 +806,8 @@ static void dp_ctrl_process_phy_test_request(struct dp_ctrl *dp_ctrl) ctrl->aux->init(ctrl->aux, ctrl->parser->aux_cfg); - ret = ctrl->dp_ctrl.on(&ctrl->dp_ctrl, ctrl->mst_mode, false); + ret = ctrl->dp_ctrl.on(&ctrl->dp_ctrl, ctrl->mst_mode, + ctrl->fec_mode, false); if (ret) pr_err("failed to enable DP controller\n"); @@ -991,6 +997,30 @@ static void dp_ctrl_mst_stream_setup(struct dp_ctrl_private *ctrl, lanes, bw_code, x_int, y_frac_enum); } +static void dp_ctrl_fec_dsc_setup(struct dp_ctrl_private *ctrl) +{ + u8 fec_sts = 0; + int rlen; + u32 dsc_enable; + const unsigned int fec_sts_dpcd = 0x280; + + if (ctrl->stream_count || !ctrl->fec_mode) + return; + + ctrl->catalog->fec_config(ctrl->catalog, ctrl->fec_mode); + + /* wait for controller to start fec sequence */ + usleep_range(900, 1000); + drm_dp_dpcd_readb(ctrl->aux->drm_aux, fec_sts_dpcd, &fec_sts); + pr_debug("sink fec status:%d\n", fec_sts); + + dsc_enable = ctrl->fec_mode ? 1 : 0; + rlen = drm_dp_dpcd_writeb(ctrl->aux->drm_aux, DP_DSC_ENABLE, + dsc_enable); + if (rlen < 1) + pr_debug("failed to enable sink dsc\n"); +} + static int dp_ctrl_stream_on(struct dp_ctrl *dp_ctrl, struct dp_panel *panel) { int rc = 0; @@ -1026,6 +1056,8 @@ static int dp_ctrl_stream_on(struct dp_ctrl *dp_ctrl, struct dp_panel *panel) dp_ctrl_wait4video_ready(ctrl); + dp_ctrl_fec_dsc_setup(ctrl); + ctrl->stream_count++; link_ready = ctrl->catalog->mainlink_ready(ctrl->catalog); @@ -1098,7 +1130,8 @@ static void dp_ctrl_stream_off(struct dp_ctrl *dp_ctrl, struct dp_panel *panel) ctrl->stream_count--; } -static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool mst_mode, bool shallow) +static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool mst_mode, + bool fec_mode, bool shallow) { int rc = 0; struct dp_ctrl_private *ctrl; @@ -1120,6 +1153,7 @@ static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool mst_mode, bool shallow) } ctrl->mst_mode = mst_mode; + ctrl->fec_mode = fec_mode; rate = ctrl->panel->link_info.rate; if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) { @@ -1165,6 +1199,7 @@ static void dp_ctrl_off(struct dp_ctrl *dp_ctrl) dp_ctrl_disable_link_clock(ctrl); ctrl->mst_mode = false; + ctrl->fec_mode = false; ctrl->power_on = false; memset(&ctrl->mst_ch_info, 0, sizeof(ctrl->mst_ch_info)); pr_debug("DP off done\n"); @@ -1242,6 +1277,7 @@ struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in) ctrl->catalog = in->catalog; ctrl->dev = in->dev; ctrl->mst_mode = false; + ctrl->fec_mode = false; dp_ctrl = &ctrl->dp_ctrl; diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h index dbb58b3f7df5..d26e9cc63d97 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h @@ -25,7 +25,8 @@ struct dp_ctrl { int (*init)(struct dp_ctrl *dp_ctrl, bool flip, bool reset); void (*deinit)(struct dp_ctrl *dp_ctrl); - int (*on)(struct dp_ctrl *dp_ctrl, bool mst_mode, bool shallow); + int (*on)(struct dp_ctrl *dp_ctrl, bool mst_mode, bool fec_en, + bool shallow); void (*off)(struct dp_ctrl *dp_ctrl); void (*abort)(struct dp_ctrl *dp_ctrl); void (*isr)(struct dp_ctrl *dp_ctrl); diff --git a/drivers/gpu/drm/msm/dp/dp_debug.c b/drivers/gpu/drm/msm/dp/dp_debug.c index 41411d1214c9..68126788570b 100644 --- a/drivers/gpu/drm/msm/dp/dp_debug.c +++ b/drivers/gpu/drm/msm/dp/dp_debug.c @@ -1927,6 +1927,22 @@ static int dp_debug_init(struct dp_debug *dp_debug) goto error_remove_dir; } + file = debugfs_create_bool("dsc_feature_enable", 0644, dir, + &debug->parser->dsc_feature_enable); + if (IS_ERR_OR_NULL(file)) { + rc = PTR_ERR(file); + pr_err("[%s] debugfs dsc_feature failed, rc=%d\n", + DEBUG_NAME, rc); + } + + file = debugfs_create_bool("fec_feature_enable", 0644, dir, + &debug->parser->fec_feature_enable); + if (IS_ERR_OR_NULL(file)) { + rc = PTR_ERR(file); + pr_err("[%s] debugfs fec_feature_enable failed, rc=%d\n", + DEBUG_NAME, rc); + } + file = debugfs_create_file("widebus_mode", 0644, dir, debug, &widebus_mode_fops); if (IS_ERR_OR_NULL(file)) { diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 0b40f0799e65..2c67f1e3af5f 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -686,7 +686,8 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) dp_display_process_mst_hpd_high(dp); mutex_lock(&dp->session_lock); - rc = dp->ctrl->on(dp->ctrl, dp->mst.mst_active, false); + rc = dp->ctrl->on(dp->ctrl, dp->mst.mst_active, + dp->panel->fec_en, false); if (rc) { mutex_unlock(&dp->session_lock); goto end; @@ -1400,7 +1401,7 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel) * So, we execute in shallow mode here to do only minimal * and required things. */ - rc = dp->ctrl->on(dp->ctrl, dp->mst.mst_active, true); + rc = dp->ctrl->on(dp->ctrl, dp->mst.mst_active, dp_panel->fec_en, true); if (rc) goto end; @@ -2202,6 +2203,26 @@ static int dp_display_mst_connector_update_edid(struct dp_display *dp_display, return rc; } +static int dp_display_update_pps(struct dp_display *dp_display, + struct drm_connector *connector, char *pps_cmd) +{ + struct sde_connector *sde_conn; + struct dp_panel *dp_panel; + struct dp_display_private *dp; + + dp = container_of(dp_display, struct dp_display_private, dp_display); + + sde_conn = to_sde_connector(connector); + if (!sde_conn->drv_panel) { + pr_err("invalid panel for connector:%d\n", connector->base.id); + return -EINVAL; + } + + dp_panel = sde_conn->drv_panel; + dp_panel->update_pps(dp_panel, pps_cmd); + return 0; +} + static int dp_display_get_mst_caps(struct dp_display *dp_display, struct dp_mst_caps *mst_caps) { @@ -2287,6 +2308,7 @@ static int dp_display_probe(struct platform_device *pdev) dp_display_mst_connector_update_edid; g_dp_display->get_mst_caps = dp_display_get_mst_caps; g_dp_display->set_stream_info = dp_display_set_stream_info; + g_dp_display->update_pps = dp_display_update_pps; g_dp_display->convert_to_dp_mode = dp_display_convert_to_dp_mode; g_dp_display->mst_get_connector_info = dp_display_mst_get_connector_info; diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h index 564ade599926..286b646d6d24 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.h +++ b/drivers/gpu/drm/msm/dp/dp_display.h @@ -118,6 +118,8 @@ struct dp_display { void (*convert_to_dp_mode)(struct dp_display *dp_display, void *panel, const struct drm_display_mode *drm_mode, struct dp_display_mode *dp_mode); + int (*update_pps)(struct dp_display *dp_display, + struct drm_connector *connector, char *pps_cmd); }; int dp_display_get_num_of_displays(void); diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c index 9b004fd1b9c0..dfc36d33b946 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_drm.c @@ -374,6 +374,7 @@ int dp_connector_get_mode_info(struct drm_connector *connector, struct msm_display_topology *topology; struct sde_connector *sde_conn; struct dp_panel *dp_panel; + struct dp_display_mode dp_mode; if (!drm_mode || !mode_info || !max_mixer_width || !connector) { pr_err("invalid params\n"); @@ -396,6 +397,15 @@ int dp_connector_get_mode_info(struct drm_connector *connector, mode_info->wide_bus_en = dp_panel->widebus_en; + if (dp_panel->dsc_en) { + dp_panel->convert_to_dp_mode(dp_panel, drm_mode, &dp_mode); + memcpy(&mode_info->comp_info, + &dp_mode.timing.comp_info, + sizeof(mode_info->comp_info)); + + topology->num_enc = topology->num_lm; + } + return 0; } @@ -596,3 +606,24 @@ enum drm_mode_status dp_connector_mode_valid(struct drm_connector *connector, return dp_disp->validate_mode(dp_disp, sde_conn->drv_panel, mode); } + +int dp_connector_update_pps(struct drm_connector *connector, + char *pps_cmd, void *display) +{ + struct dp_display *dp_disp; + struct sde_connector *sde_conn; + + if (!display || !connector) { + pr_err("invalid params\n"); + return -EINVAL; + } + + sde_conn = to_sde_connector(connector); + if (!sde_conn->drv_panel) { + pr_err("invalid dp panel\n"); + return MODE_ERROR; + } + + dp_disp = display; + return dp_disp->update_pps(dp_disp, connector, pps_cmd); +} diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h index 632d61122af2..449e2ede7ccc 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.h +++ b/drivers/gpu/drm/msm/dp/dp_drm.h @@ -127,6 +127,15 @@ void dp_drm_bridge_deinit(void *display); void convert_to_drm_mode(const struct dp_display_mode *dp_mode, struct drm_display_mode *drm_mode); +/** + * dp_connector_update_pps - update pps for given connector + * @dp_mode: Point to dp mode + * @pps_cmd: PPS packet + * @display: Pointer to private display structure + */ +int dp_connector_update_pps(struct drm_connector *connector, + char *pps_cmd, void *display); + /** * dp_mst_drm_bridge_init - initialize mst bridge * @display: Pointer to private display structure diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c index 42fa5ad2064a..532c17275f30 100644 --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c @@ -1299,6 +1299,7 @@ dp_mst_add_connector(struct drm_dp_mst_topology_mgr *mgr, .atomic_check = dp_mst_connector_atomic_check, .config_hdr = dp_mst_connector_config_hdr, .pre_destroy = dp_mst_connector_pre_destroy, + .update_pps = dp_connector_update_pps, }; struct dp_mst_private *dp_mst; struct drm_device *dev; diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c index c0ab201587f9..7e3d3a7c3002 100644 --- a/drivers/gpu/drm/msm/dp/dp_panel.c +++ b/drivers/gpu/drm/msm/dp/dp_panel.c @@ -885,11 +885,21 @@ static void dp_panel_calc_tu_parameters(struct dp_panel *dp_panel, in.nlanes = panel->link->link_params.lane_count; in.bpp = pinfo->bpp; in.pixel_enc = 444; - in.dsc_en = 0; + in.dsc_en = dp_panel->dsc_en; in.async_en = 0; - in.fec_en = 0; - in.compress_ratio = 0; - in.num_of_dsc_slices = 0; + in.fec_en = dp_panel->fec_en; + in.num_of_dsc_slices = pinfo->comp_info.dsc_info.slice_per_pkt; + + switch (pinfo->comp_info.comp_ratio) { + case MSM_DISPLAY_COMPRESSION_RATIO_2_TO_1: + in.compress_ratio = 200; + break; + case MSM_DISPLAY_COMPRESSION_RATIO_3_TO_1: + in.compress_ratio = 300; + break; + default: + in.compress_ratio = 100; + } _dp_panel_calc_tu(&in, tu_table); } @@ -943,6 +953,603 @@ static void dp_panel_config_tr_unit(struct dp_panel *dp_panel) catalog->update_transfer_unit(catalog); } +enum dp_dsc_ratio_type { + DSC_8BPC_8BPP, + DSC_10BPC_8BPP, + DSC_12BPC_8BPP, + DSC_RATIO_TYPE_MAX +}; + +static u32 dp_dsc_rc_buf_thresh[] = {0x0e, 0x1c, 0x2a, 0x38, 0x46, 0x54, + 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b, 0x7d, 0x7e}; + +/* + * DSC 1.1 + * Rate control - Min QP values for each ratio type in dp_dsc_ratio_type + */ +static char dp_dsc_rc_range_min_qp_1_1[][15] = { + {0, 0, 1, 1, 3, 3, 3, 3, 3, 3, 5, 5, 5, 7, 13}, + {0, 4, 5, 5, 7, 7, 7, 7, 7, 7, 9, 9, 9, 11, 17}, + {0, 4, 9, 9, 11, 11, 11, 11, 11, 11, 13, 13, 13, 15, 21}, + }; + +/* + * DSC 1.1 SCR + * Rate control - Min QP values for each ratio type in dp_dsc_ratio_type + */ +static char dp_dsc_rc_range_min_qp_1_1_scr1[][15] = { + {0, 0, 1, 1, 3, 3, 3, 3, 3, 3, 5, 5, 5, 9, 12}, + {0, 4, 5, 5, 7, 7, 7, 7, 7, 7, 9, 9, 9, 13, 16}, + {0, 4, 9, 9, 11, 11, 11, 11, 11, 11, 13, 13, 13, 17, 20}, + }; + +/* + * DSC 1.1 + * Rate control - Max QP values for each ratio type in dp_dsc_ratio_type + */ +static char dp_dsc_rc_range_max_qp_1_1[][15] = { + {4, 4, 5, 6, 7, 7, 7, 8, 9, 10, 11, 12, 13, 13, 15}, + {8, 8, 9, 10, 11, 11, 11, 12, 13, 14, 15, 16, 17, 17, 19}, + {12, 12, 13, 14, 15, 15, 15, 16, 17, 18, 19, 20, 21, 21, 23}, + }; + +/* + * DSC 1.1 SCR + * Rate control - Max QP values for each ratio type in dp_dsc_ratio_type + */ +static char dp_dsc_rc_range_max_qp_1_1_scr1[][15] = { + {4, 4, 5, 6, 7, 7, 7, 8, 9, 10, 10, 11, 11, 12, 13}, + {8, 8, 9, 10, 11, 11, 11, 12, 13, 14, 14, 15, 15, 16, 17}, + {12, 12, 13, 14, 15, 15, 15, 16, 17, 18, 18, 19, 19, 20, 21}, + }; + +/* + * DSC 1.1 and DSC 1.1 SCR + * Rate control - bpg offset values + */ +static char dp_dsc_rc_range_bpg_offset[] = {2, 0, 0, -2, -4, -6, -8, -8, + -8, -10, -10, -12, -12, -12, -12}; + +struct dp_dsc_dto_data { + enum msm_display_compression_ratio comp_ratio; + u32 org_bpp; /* bits */ + u32 dto_numerator; + u32 dto_denominator; +}; + +struct dp_dsc_dto_data dto_tbl[] = { + {MSM_DISPLAY_COMPRESSION_RATIO_2_TO_1, 24, 1, 2}, + {MSM_DISPLAY_COMPRESSION_RATIO_2_TO_1, 30, 5, 8}, + {MSM_DISPLAY_COMPRESSION_RATIO_3_TO_1, 24, 1, 3}, + {MSM_DISPLAY_COMPRESSION_RATIO_3_TO_1, 30, 5, 12}, +}; + +static void _dp_panel_get_dto_m_n(enum msm_display_compression_ratio ratio, + u32 org_bpp, u32 *dto_n, u32 *dto_d) +{ + u32 idx; + + for (idx = 0; idx < ARRAY_SIZE(dto_tbl); idx++) { + if (ratio == dto_tbl[idx].comp_ratio && + org_bpp == dto_tbl[idx].org_bpp) { + *dto_n = dto_tbl[idx].dto_numerator; + *dto_d = dto_tbl[idx].dto_denominator; + return; + } + } +} + +static int dp_panel_dsc_create_pps_buf_cmd(struct msm_display_dsc_info *dsc, + char *buf, int pps_id) +{ + char *bp = buf; + char data; + int i, bpp; + + *bp++ = (dsc->version & 0xff); /* pps0 */ + *bp++ = (pps_id & 0xff); /* pps1 */ + bp++; /* pps2, reserved */ + + data = dsc->line_buf_depth & 0x0f; + data |= ((dsc->bpc & 0xf) << 4); + *bp++ = data; /* pps3 */ + + bpp = dsc->bpp; + bpp <<= 4; /* 4 fraction bits */ + data = (bpp >> 8); + data &= 0x03; /* upper two bits */ + data |= ((dsc->block_pred_enable & 0x1) << 5); + data |= ((dsc->convert_rgb & 0x1) << 4); + data |= ((dsc->enable_422 & 0x1) << 3); + data |= ((dsc->vbr_enable & 0x1) << 2); + *bp++ = data; /* pps4 */ + *bp++ = (bpp & 0xff); /* pps5 */ + + *bp++ = ((dsc->pic_height >> 8) & 0xff); /* pps6 */ + *bp++ = (dsc->pic_height & 0x0ff); /* pps7 */ + *bp++ = ((dsc->pic_width >> 8) & 0xff); /* pps8 */ + *bp++ = (dsc->pic_width & 0x0ff); /* pps9 */ + + *bp++ = ((dsc->slice_height >> 8) & 0xff);/* pps10 */ + *bp++ = (dsc->slice_height & 0x0ff); /* pps11 */ + *bp++ = ((dsc->slice_width >> 8) & 0xff); /* pps12 */ + *bp++ = (dsc->slice_width & 0x0ff); /* pps13 */ + + *bp++ = ((dsc->chunk_size >> 8) & 0xff);/* pps14 */ + *bp++ = (dsc->chunk_size & 0x0ff); /* pps15 */ + + *bp++ = (dsc->initial_xmit_delay >> 8) & 0x3; /* pps16*/ + *bp++ = (dsc->initial_xmit_delay & 0xff);/* pps17 */ + + *bp++ = ((dsc->initial_dec_delay >> 8) & 0xff); /* pps18 */ + *bp++ = (dsc->initial_dec_delay & 0xff);/* pps19 */ + + bp++; /* pps20, reserved */ + + *bp++ = (dsc->initial_scale_value & 0x3f); /* pps21 */ + + *bp++ = ((dsc->scale_increment_interval >> 8) & 0xff); /* pps22 */ + *bp++ = (dsc->scale_increment_interval & 0xff); /* pps23 */ + + *bp++ = ((dsc->scale_decrement_interval >> 8) & 0xf); /* pps24 */ + *bp++ = (dsc->scale_decrement_interval & 0x0ff);/* pps25 */ + + bp++; /* pps26, reserved */ + + *bp++ = (dsc->first_line_bpg_offset & 0x1f);/* pps27 */ + + *bp++ = ((dsc->nfl_bpg_offset >> 8) & 0xff);/* pps28 */ + *bp++ = (dsc->nfl_bpg_offset & 0x0ff); /* pps29 */ + *bp++ = ((dsc->slice_bpg_offset >> 8) & 0xff);/* pps30 */ + *bp++ = (dsc->slice_bpg_offset & 0x0ff);/* pps31 */ + + *bp++ = ((dsc->initial_offset >> 8) & 0xff);/* pps32 */ + *bp++ = (dsc->initial_offset & 0x0ff); /* pps33 */ + + *bp++ = ((dsc->final_offset >> 8) & 0xff);/* pps34 */ + *bp++ = (dsc->final_offset & 0x0ff); /* pps35 */ + + *bp++ = (dsc->min_qp_flatness & 0x1f); /* pps36 */ + *bp++ = (dsc->max_qp_flatness & 0x1f); /* pps37 */ + + *bp++ = ((dsc->rc_model_size >> 8) & 0xff);/* pps38 */ + *bp++ = (dsc->rc_model_size & 0x0ff); /* pps39 */ + + *bp++ = (dsc->edge_factor & 0x0f); /* pps40 */ + + *bp++ = (dsc->quant_incr_limit0 & 0x1f); /* pps41 */ + *bp++ = (dsc->quant_incr_limit1 & 0x1f); /* pps42 */ + + data = ((dsc->tgt_offset_hi & 0xf) << 4); + data |= (dsc->tgt_offset_lo & 0x0f); + *bp++ = data; /* pps43 */ + + for (i = 0; i < ARRAY_SIZE(dp_dsc_rc_buf_thresh); i++) + *bp++ = (dsc->buf_thresh[i] & 0xff); /* pps44 - pps57 */ + + for (i = 0; i < 15; i++) { /* pps58 - pps87 */ + data = (dsc->range_min_qp[i] & 0x1f); + data <<= 3; + data |= ((dsc->range_max_qp[i] >> 2) & 0x07); + *bp++ = data; + data = (dsc->range_max_qp[i] & 0x03); + data <<= 6; + data |= (dsc->range_bpg_offset[i] & 0x3f); + *bp++ = data; + } + + return 88; +} + +static void dp_panel_dsc_prepare_pps_packet(struct dp_panel *dp_panel) +{ + struct dp_panel_private *panel; + struct dp_dsc_cfg_data *dsc; + u8 *pps, *parity; + u32 *pps_word, *parity_word; + int i, index_4; + + panel = container_of(dp_panel, struct dp_panel_private, dp_panel); + dsc = &panel->catalog->dsc; + pps = dsc->pps; + pps_word = dsc->pps_word; + parity = dsc->parity; + parity_word = dsc->parity_word; + + memset(parity, 0, sizeof(dsc->parity)); + + dsc->pps_word_len = dsc->pps_len >> 2; + dsc->parity_len = dsc->pps_word_len; + dsc->parity_word_len = (dsc->parity_len >> 2) + 1; + + for (i = 0; i < dsc->pps_word_len; i++) { + index_4 = i << 2; + pps_word[i] = pps[index_4 + 0] << 0 | + pps[index_4 + 1] << 8 | + pps[index_4 + 2] << 16 | + pps[index_4 + 3] << 24; + + parity[i] = dp_header_get_parity(pps_word[i]); + } + + for (i = 0; i < dsc->parity_word_len; i++) { + index_4 = i << 2; + parity_word[i] = parity[index_4 + 0] << 0 | + parity[index_4 + 1] << 8 | + parity[index_4 + 2] << 16 | + parity[index_4 + 3] << 24; + } +} + +static void _dp_panel_dsc_get_num_extra_pclk(struct msm_display_dsc_info *dsc, + enum msm_display_compression_ratio ratio) +{ + unsigned int dto_n, dto_d, remainder; + int ack_required, last_few_ack_required, accum_ack; + int last_few_pclk, last_few_pclk_required; + int start, temp, line_width = dsc->pic_width/2; + s64 temp1_fp, temp2_fp; + + _dp_panel_get_dto_m_n(ratio, dsc->bpc * 3, &dto_n, &dto_d); + + ack_required = dsc->pclk_per_line; + + /* number of pclk cycles left outside of the complete DTO set */ + last_few_pclk = line_width % dto_d; + + /* number of pclk cycles outside of the complete dto */ + temp1_fp = drm_fixp_from_fraction(line_width, dto_d); + temp2_fp = drm_fixp_from_fraction(dto_n, 1); + temp1_fp = drm_fixp_mul(temp1_fp, temp2_fp); + temp = drm_fixp2int(temp1_fp); + last_few_ack_required = ack_required - temp; + + /* + * check how many more pclk is needed to + * accommodate the last few ack required + */ + remainder = dto_n; + accum_ack = 0; + last_few_pclk_required = 0; + while (accum_ack < last_few_ack_required) { + last_few_pclk_required++; + + if (remainder >= dto_n) + start = remainder; + else + start = remainder + dto_d; + + remainder = start - dto_n; + if (remainder < dto_n) + accum_ack++; + } + + /* if fewer pclk than required */ + if (last_few_pclk < last_few_pclk_required) + dsc->extra_width = last_few_pclk_required - last_few_pclk; + else + dsc->extra_width = 0; + + pr_debug("extra pclks required: %d\n", dsc->extra_width); +} + +static void _dp_panel_dsc_bw_overhead_calc(struct dp_panel *dp_panel, + struct msm_display_dsc_info *dsc, + struct dp_display_mode *dp_mode, u32 dsc_byte_cnt) +{ + int num_slices, tot_num_eoc_symbols; + int tot_num_hor_bytes, tot_num_dummy_bytes; + int dwidth_dsc_bytes, eoc_bytes; + u32 num_lanes; + + num_lanes = dp_panel->link_info.num_lanes; + num_slices = dsc->slice_per_pkt; + + eoc_bytes = dsc_byte_cnt % num_lanes; + tot_num_eoc_symbols = num_lanes * num_slices; + tot_num_hor_bytes = dsc_byte_cnt * num_slices; + tot_num_dummy_bytes = (num_lanes - eoc_bytes) * num_slices; + + if (!eoc_bytes) + tot_num_dummy_bytes = 0; + + dwidth_dsc_bytes = tot_num_hor_bytes + tot_num_eoc_symbols + + tot_num_dummy_bytes; + + dp_mode->dsc_overhead_fp = drm_fixp_from_fraction(dwidth_dsc_bytes, + tot_num_hor_bytes); +} + +static void dp_panel_dsc_pclk_param_calc(struct dp_panel *dp_panel, + struct msm_display_dsc_info *dsc, + enum msm_display_compression_ratio ratio, + struct dp_display_mode *dp_mode) +{ + int slice_per_pkt, slice_per_intf, intf_width; + int bytes_in_slice, total_bytes_per_intf; + int comp_ratio; + s64 temp1_fp, temp2_fp; + s64 numerator_fp, denominator_fp; + s64 dsc_byte_count_fp; + u32 dsc_byte_count, temp1, temp2; + + intf_width = dp_mode->timing.h_active; + if (!dsc || !dsc->slice_width || !dsc->slice_per_pkt || + (intf_width < dsc->slice_width)) + return; + + slice_per_pkt = dsc->slice_per_pkt; + slice_per_intf = DIV_ROUND_UP(intf_width, dsc->slice_width); + + if (slice_per_pkt > slice_per_intf) + slice_per_pkt = 1; + + bytes_in_slice = DIV_ROUND_UP(dsc->slice_width * dsc->bpp, 8); + total_bytes_per_intf = bytes_in_slice * slice_per_intf; + + dsc->bytes_in_slice = bytes_in_slice; + dsc->bytes_per_pkt = bytes_in_slice * slice_per_pkt; + dsc->pkt_per_line = slice_per_intf / slice_per_pkt; + + switch (ratio) { + case MSM_DISPLAY_COMPRESSION_RATIO_2_TO_1: + comp_ratio = 200; + break; + case MSM_DISPLAY_COMPRESSION_RATIO_3_TO_1: + comp_ratio = 300; + break; + default: + comp_ratio = 100; + break; + } + + temp1_fp = drm_fixp_from_fraction(comp_ratio, 100); + temp2_fp = drm_fixp_from_fraction(slice_per_pkt * 8, 1); + denominator_fp = drm_fixp_mul(temp1_fp, temp2_fp); + numerator_fp = drm_fixp_from_fraction(intf_width * dsc->bpc * 3, 1); + dsc_byte_count_fp = drm_fixp_div(numerator_fp, denominator_fp); + dsc_byte_count = drm_fixp2int_ceil(dsc_byte_count_fp); + + temp1 = dsc_byte_count * slice_per_intf; + temp2 = temp1; + if (temp1 % 3 != 0) + temp1 += 3 - (temp1 % 3); + + dsc->eol_byte_num = temp1 - temp2; + + temp1_fp = drm_fixp_from_fraction(slice_per_intf, 6); + temp2_fp = drm_fixp_mul(dsc_byte_count_fp, temp1_fp); + dsc->pclk_per_line = drm_fixp2int_ceil(temp2_fp); + + _dp_panel_dsc_get_num_extra_pclk(dsc, ratio); + dsc->pclk_per_line--; + + _dp_panel_dsc_bw_overhead_calc(dp_panel, dsc, dp_mode, dsc_byte_count); +} + +static void dp_panel_dsc_populate_static_params( + struct msm_display_dsc_info *dsc) +{ + int bpp, bpc; + int mux_words_size; + int groups_per_line, groups_total; + int min_rate_buffer_size; + int hrd_delay; + int pre_num_extra_mux_bits, num_extra_mux_bits; + int slice_bits; + int data; + int final_value, final_scale; + int ratio_index, mod_offset; + + dsc->version = 0x11; + dsc->scr_rev = 0; + dsc->rc_model_size = 8192; + + if (dsc->version == 0x11 && dsc->scr_rev == 0x1) + dsc->first_line_bpg_offset = 15; + else + dsc->first_line_bpg_offset = 12; + + dsc->edge_factor = 6; + dsc->tgt_offset_hi = 3; + dsc->tgt_offset_lo = 3; + dsc->enable_422 = 0; + dsc->convert_rgb = 1; + dsc->vbr_enable = 0; + + dsc->buf_thresh = dp_dsc_rc_buf_thresh; + + bpp = dsc->bpp; + bpc = dsc->bpc; + + if (bpc == 12) + ratio_index = DSC_12BPC_8BPP; + else if (bpc == 10) + ratio_index = DSC_10BPC_8BPP; + else + ratio_index = DSC_8BPC_8BPP; + + if (dsc->version == 0x11 && dsc->scr_rev == 0x1) { + dsc->range_min_qp = + dp_dsc_rc_range_min_qp_1_1_scr1[ratio_index]; + dsc->range_max_qp = + dp_dsc_rc_range_max_qp_1_1_scr1[ratio_index]; + } else { + dsc->range_min_qp = dp_dsc_rc_range_min_qp_1_1[ratio_index]; + dsc->range_max_qp = dp_dsc_rc_range_max_qp_1_1[ratio_index]; + } + dsc->range_bpg_offset = dp_dsc_rc_range_bpg_offset; + + if (bpp <= 10) + dsc->initial_offset = 6144; + else + dsc->initial_offset = 2048; /* bpp = 12 */ + + if (bpc == 12) + mux_words_size = 64; + else + mux_words_size = 48; /* bpc == 8/10 */ + + dsc->line_buf_depth = bpc + 1; + + if (bpc == 8) { + dsc->input_10_bits = 0; + dsc->min_qp_flatness = 3; + dsc->max_qp_flatness = 12; + dsc->quant_incr_limit0 = 11; + dsc->quant_incr_limit1 = 11; + } else if (bpc == 10) { /* 10bpc */ + dsc->input_10_bits = 1; + dsc->min_qp_flatness = 7; + dsc->max_qp_flatness = 16; + dsc->quant_incr_limit0 = 15; + dsc->quant_incr_limit1 = 15; + } else { /* 12 bpc */ + dsc->input_10_bits = 0; + dsc->min_qp_flatness = 11; + dsc->max_qp_flatness = 20; + dsc->quant_incr_limit0 = 19; + dsc->quant_incr_limit1 = 19; + } + + mod_offset = dsc->slice_width % 3; + switch (mod_offset) { + case 0: + dsc->slice_last_group_size = 2; + break; + case 1: + dsc->slice_last_group_size = 0; + break; + case 2: + dsc->slice_last_group_size = 1; + break; + default: + break; + } + + dsc->det_thresh_flatness = 2 << (bpc - 8); + + dsc->initial_xmit_delay = dsc->rc_model_size / (2 * bpp); + + groups_per_line = DIV_ROUND_UP(dsc->slice_width, 3); + + dsc->chunk_size = dsc->slice_width * bpp / 8; + if ((dsc->slice_width * bpp) % 8) + dsc->chunk_size++; + + /* rbs-min */ + min_rate_buffer_size = dsc->rc_model_size - dsc->initial_offset + + dsc->initial_xmit_delay * bpp + + groups_per_line * dsc->first_line_bpg_offset; + + hrd_delay = DIV_ROUND_UP(min_rate_buffer_size, bpp); + + dsc->initial_dec_delay = hrd_delay - dsc->initial_xmit_delay; + + dsc->initial_scale_value = 8 * dsc->rc_model_size / + (dsc->rc_model_size - dsc->initial_offset); + + slice_bits = 8 * dsc->chunk_size * dsc->slice_height; + + groups_total = groups_per_line * dsc->slice_height; + + data = dsc->first_line_bpg_offset * 2048; + + dsc->nfl_bpg_offset = DIV_ROUND_UP(data, (dsc->slice_height - 1)); + + pre_num_extra_mux_bits = 3 * (mux_words_size + (4 * bpc + 4) - 2); + + num_extra_mux_bits = pre_num_extra_mux_bits - (mux_words_size - + ((slice_bits - pre_num_extra_mux_bits) % mux_words_size)); + + data = 2048 * (dsc->rc_model_size - dsc->initial_offset + + num_extra_mux_bits); + dsc->slice_bpg_offset = DIV_ROUND_UP(data, groups_total); + + data = dsc->initial_xmit_delay * bpp; + final_value = dsc->rc_model_size - data + num_extra_mux_bits; + + final_scale = 8 * dsc->rc_model_size / + (dsc->rc_model_size - final_value); + + dsc->final_offset = final_value; + + data = (final_scale - 9) * (dsc->nfl_bpg_offset + + dsc->slice_bpg_offset); + dsc->scale_increment_interval = (2048 * dsc->final_offset) / data; + + dsc->scale_decrement_interval = groups_per_line / + (dsc->initial_scale_value - 8); +} + +struct dp_dsc_slices_per_line { + u32 min_ppr; + u32 max_ppr; + u8 num_slices; +}; + +struct dp_dsc_slices_per_line slice_per_line_tbl[] = { + {0, 340, 1 }, + {340, 680, 2 }, + {680, 1360, 4 }, + {1360, 3200, 8 }, + {3200, 4800, 12 }, + {4800, 6400, 16 }, + {6400, 8000, 20 }, + {8000, 9600, 24 } +}; + +static int dp_panel_dsc_prepare_basic_params( + struct msm_compression_info *comp_info, + const struct dp_display_mode *dp_mode, + struct dp_panel *dp_panel) +{ + int i; + struct dp_dsc_slices_per_line *rec; + int slice_width; + u32 ppr = dp_mode->timing.pixel_clk_khz/1000; + + comp_info->dsc_info.slice_per_pkt = 0; + for (i = 0; i < ARRAY_SIZE(slice_per_line_tbl); i++) { + rec = &slice_per_line_tbl[i]; + if ((ppr > rec->min_ppr) && (ppr <= rec->max_ppr)) { + comp_info->dsc_info.slice_per_pkt = rec->num_slices; + break; + } + } + + if (comp_info->dsc_info.slice_per_pkt == 0) + return -EINVAL; + + slice_width = (dp_mode->timing.h_active / + comp_info->dsc_info.slice_per_pkt); + + comp_info->dsc_info.block_pred_enable = + dp_panel->sink_dsc_caps.block_pred_en; + comp_info->dsc_info.vbr_enable = 0; + comp_info->dsc_info.enable_422 = 0; + comp_info->dsc_info.convert_rgb = 1; + comp_info->dsc_info.input_10_bits = 0; + + comp_info->dsc_info.pic_width = dp_mode->timing.h_active; + comp_info->dsc_info.pic_height = dp_mode->timing.v_active; + comp_info->dsc_info.slice_width = slice_width; + + if (comp_info->dsc_info.pic_height % 16) + comp_info->dsc_info.slice_height = 12; + else + comp_info->dsc_info.slice_height = 16; + + comp_info->dsc_info.bpc = dp_mode->timing.bpp / 3; + comp_info->dsc_info.bpp = comp_info->dsc_info.bpc; + comp_info->dsc_info.full_frame_slices = + DIV_ROUND_UP(dp_mode->timing.h_active, slice_width); + + comp_info->comp_type = MSM_DISPLAY_COMPRESSION_DSC; + comp_info->comp_ratio = MSM_DISPLAY_COMPRESSION_RATIO_3_TO_1; + return 0; +} + static int dp_panel_read_dpcd(struct dp_panel *dp_panel, bool multi_func) { int rlen, rc = 0; @@ -1166,6 +1773,77 @@ end: return ret; } +static void dp_panel_decode_dsc_dpcd(struct dp_panel *dp_panel) +{ + s64 fec_overhead_fp = drm_fixp_from_fraction(1, 1); + + if (!dp_panel->dsc_feature_enable || !dp_panel->fec_feature_enable) { + pr_debug("source dsc is not supported\n"); + return; + } + + if (dp_panel->dsc_dpcd[0] && dp_panel->fec_dpcd) { + dp_panel->sink_dsc_caps.dsc_capable = true; + dp_panel->sink_dsc_caps.version = dp_panel->dsc_dpcd[1]; + dp_panel->sink_dsc_caps.block_pred_en = + dp_panel->dsc_dpcd[6] ? true : false; + + if (dp_panel->sink_dsc_caps.version >= 0x11) + dp_panel->dsc_en = true; + } else { + dp_panel->sink_dsc_caps.dsc_capable = false; + dp_panel->dsc_en = false; + } + + dp_panel->fec_en = dp_panel->dsc_en; + dp_panel->widebus_en = dp_panel->dsc_en; + + /* fec_overhead = 1.00 / 0.7488664 */ + if (dp_panel->fec_en) + fec_overhead_fp = drm_fixp_from_fraction(10000000, 7488664); + + dp_panel->fec_overhead_fp = fec_overhead_fp; +} + +static void dp_panel_read_sink_dsc_caps(struct dp_panel *dp_panel) +{ + int rlen; + struct dp_panel_private *panel; + const int fec_cap = 0x90; + + if (!dp_panel) { + pr_err("invalid input\n"); + return; + } + + dp_panel->dsc_en = false; + dp_panel->fec_en = false; + + panel = container_of(dp_panel, struct dp_panel_private, dp_panel); + + if (panel->parser->dsc_feature_enable) { + rlen = drm_dp_dpcd_read(panel->aux->drm_aux, DP_DSC_SUPPORT, + dp_panel->dsc_dpcd, (DP_RECEIVER_DSC_CAP_SIZE + 1)); + if (rlen < (DP_RECEIVER_DSC_CAP_SIZE + 1)) { + pr_debug("dsc dpcd read failed, rlen=%d\n", rlen); + return; + } + + print_hex_dump(KERN_DEBUG, "[drm-dp] SINK DSC DPCD: ", + DUMP_PREFIX_NONE, 8, 1, dp_panel->dsc_dpcd, rlen, + false); + + rlen = drm_dp_dpcd_read(panel->aux->drm_aux, fec_cap, + &dp_panel->fec_dpcd, 1); + if (rlen < 1) { + pr_err("fec dpcd read failed, rlen=%d\n", rlen); + return; + } + + dp_panel_decode_dsc_dpcd(dp_panel); + } +} + static int dp_panel_read_sink_caps(struct dp_panel *dp_panel, struct drm_connector *connector, bool multi_func) { @@ -1219,6 +1897,10 @@ static int dp_panel_read_sink_caps(struct dp_panel *dp_panel, } dp_panel->widebus_en = panel->parser->has_widebus; + dp_panel->dsc_feature_enable = panel->parser->dsc_feature_enable; + dp_panel->fec_feature_enable = panel->parser->fec_feature_enable; + + dp_panel_read_sink_dsc_caps(dp_panel); end: return rc; } @@ -1479,6 +2161,101 @@ end: return rc; } +static u32 _dp_panel_calc_be_in_lane(struct dp_panel *dp_panel) +{ + struct dp_panel_info *pinfo; + struct msm_compression_info *comp_info; + u32 dsc_htot_byte_cnt, mod_result; + u32 numerator, denominator; + s64 temp_fp; + u32 be_in_lane = 10; + + pinfo = &dp_panel->pinfo; + comp_info = &pinfo->comp_info; + + if (!dp_panel->mst_state) + return be_in_lane; + + switch (pinfo->comp_info.comp_ratio) { + case MSM_DISPLAY_COMPRESSION_RATIO_2_TO_1: + denominator = 16; /* 2 * bits-in-byte */ + break; + case MSM_DISPLAY_COMPRESSION_RATIO_3_TO_1: + denominator = 24; /* 3 * bits-in-byte */ + break; + default: + denominator = 8; /* 1 * bits-in-byte */ + } + + numerator = (pinfo->h_active + pinfo->h_back_porch + + pinfo->h_front_porch + pinfo->h_sync_width) * + pinfo->bpp; + temp_fp = drm_fixp_from_fraction(numerator, denominator); + dsc_htot_byte_cnt = drm_fixp2int_ceil(temp_fp); + + mod_result = dsc_htot_byte_cnt % 12; + if (mod_result == 0) + be_in_lane = 8; + else if (mod_result <= 3) + be_in_lane = 1; + else if (mod_result <= 6) + be_in_lane = 2; + else if (mod_result <= 9) + be_in_lane = 4; + else if (mod_result <= 11) + be_in_lane = 8; + else + be_in_lane = 10; + + return be_in_lane; +} + +static void dp_panel_config_dsc(struct dp_panel *dp_panel, bool enable) +{ + struct dp_catalog_panel *catalog; + struct dp_panel_private *panel; + struct dp_panel_info *pinfo; + struct msm_compression_info *comp_info; + struct dp_dsc_cfg_data *dsc; + int pps_len; + + panel = container_of(dp_panel, struct dp_panel_private, dp_panel); + + catalog = panel->catalog; + dsc = &catalog->dsc; + pinfo = &dp_panel->pinfo; + comp_info = &pinfo->comp_info; + + if (comp_info->comp_type == MSM_DISPLAY_COMPRESSION_DSC && enable) { + pps_len = dp_panel_dsc_create_pps_buf_cmd(&comp_info->dsc_info, + dsc->pps, 0); + dsc->pps_len = pps_len; + dp_panel_dsc_prepare_pps_packet(dp_panel); + + dsc->slice_per_pkt = comp_info->dsc_info.slice_per_pkt - 1; + dsc->bytes_per_pkt = comp_info->dsc_info.bytes_per_pkt; + dsc->eol_byte_num = comp_info->dsc_info.eol_byte_num; + dsc->dto_count = comp_info->dsc_info.pclk_per_line; + dsc->be_in_lane = _dp_panel_calc_be_in_lane(dp_panel); + dsc->dsc_en = true; + dsc->dto_en = true; + + _dp_panel_get_dto_m_n(comp_info->comp_ratio, pinfo->bpp, + &dsc->dto_n, &dsc->dto_d); + } else { + dsc->dsc_en = false; + dsc->dto_en = false; + dsc->dto_n = 0; + dsc->dto_d = 0; + } + + catalog->stream_id = dp_panel->stream_id; + catalog->dsc_cfg(catalog); + + if (catalog->dsc.dsc_en && enable) + catalog->pps_flush(catalog); +} + static int dp_panel_edid_register(struct dp_panel_private *panel) { int rc = 0; @@ -1852,6 +2629,7 @@ static int dp_panel_hw_cfg(struct dp_panel *dp_panel, bool enable) dp_panel_config_ctrl(dp_panel); dp_panel_config_misc(dp_panel); dp_panel_config_msa(dp_panel); + dp_panel_config_dsc(dp_panel, enable); dp_panel_config_tr_unit(dp_panel); dp_panel_config_timing(dp_panel); } @@ -1926,6 +2704,7 @@ static void dp_panel_convert_to_dp_mode(struct dp_panel *dp_panel, struct dp_display_mode *dp_mode) { const u32 num_components = 3, default_bpp = 24; + struct msm_compression_info *comp_info; dp_mode->timing.h_active = drm_mode->hdisplay; dp_mode->timing.h_back_porch = drm_mode->htotal - drm_mode->hsync_end; @@ -1962,6 +2741,35 @@ static void dp_panel_convert_to_dp_mode(struct dp_panel *dp_panel, dp_mode->timing.bpp, dp_mode->timing.pixel_clk_khz); dp_mode->timing.widebus_en = dp_panel->widebus_en; + + if (dp_panel->dsc_en) { + comp_info = &dp_mode->timing.comp_info; + + if (dp_panel_dsc_prepare_basic_params(comp_info, + dp_mode, dp_panel)) { + pr_debug("prepare DSC basic params failed\n"); + return; + } + + dp_panel_dsc_populate_static_params(&comp_info->dsc_info); + dp_panel_dsc_pclk_param_calc(dp_panel, + &comp_info->dsc_info, + comp_info->comp_ratio, + dp_mode); + } + dp_mode->fec_overhead_fp = dp_panel->fec_overhead_fp; +} + +static void dp_panel_update_pps(struct dp_panel *dp_panel, char *pps_cmd) +{ + struct dp_catalog_panel *catalog; + struct dp_panel_private *panel; + + panel = container_of(dp_panel, struct dp_panel_private, dp_panel); + + catalog = panel->catalog; + catalog->stream_id = dp_panel->stream_id; + catalog->pps_flush(catalog); } struct dp_panel *dp_panel_get(struct dp_panel_in *in) @@ -1997,13 +2805,19 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in) memcpy(panel->spd_product_description, product_desc, (sizeof(u8) * 16)); dp_panel->connector = in->connector; - dp_panel->widebus_en = panel->parser->has_widebus; + dp_panel->dsc_feature_enable = panel->parser->dsc_feature_enable; + dp_panel->fec_feature_enable = panel->parser->fec_feature_enable; if (in->base_panel) { memcpy(dp_panel->dpcd, in->base_panel->dpcd, DP_RECEIVER_CAP_SIZE + 1); memcpy(&dp_panel->link_info, &in->base_panel->link_info, sizeof(dp_panel->link_info)); + dp_panel->mst_state = in->base_panel->mst_state; + dp_panel->widebus_en = in->base_panel->widebus_en; + dp_panel->fec_en = in->base_panel->fec_en; + dp_panel->dsc_en = in->base_panel->dsc_en; + dp_panel->fec_overhead_fp = in->base_panel->fec_overhead_fp; } dp_panel->init = dp_panel_init_panel_info; @@ -2025,6 +2839,7 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in) dp_panel->update_edid = dp_panel_update_edid; dp_panel->read_mst_cap = dp_panel_read_mst_cap; dp_panel->convert_to_dp_mode = dp_panel_convert_to_dp_mode; + dp_panel->update_pps = dp_panel_update_pps; sde_conn = to_sde_connector(dp_panel->connector); sde_conn->drv_panel = dp_panel; diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h index f6701f169ae1..19af24757340 100644 --- a/drivers/gpu/drm/msm/dp/dp_panel.h +++ b/drivers/gpu/drm/msm/dp/dp_panel.h @@ -22,6 +22,10 @@ #include "dp_usbpd.h" #include "sde_edid_parser.h" #include "sde_connector.h" +#include "msm_drv.h" + +#define DP_RECEIVER_DSC_CAP_SIZE 15 +#define DP_RECEIVER_FEC_STATUS_SIZE 3 /* * A source initiated power down flag is set @@ -57,11 +61,14 @@ struct dp_panel_info { u32 pixel_clk_khz; u32 bpp; bool widebus_en; + struct msm_compression_info comp_info; }; struct dp_display_mode { struct dp_panel_info timing; u32 capabilities; + s64 fec_overhead_fp; + s64 dsc_overhead_fp; }; struct dp_panel; @@ -76,12 +83,21 @@ struct dp_panel_in { struct dp_parser *parser; }; +struct dp_dsc_caps { + bool dsc_capable; + u8 version; + bool block_pred_en; +}; + struct dp_audio; struct dp_panel { /* dpcd raw data */ u8 dpcd[DP_RECEIVER_CAP_SIZE + 1]; u8 ds_ports[DP_MAX_DOWNSTREAM_PORTS]; + u8 dsc_dpcd[DP_RECEIVER_DSC_CAP_SIZE + 1]; + u8 fec_dpcd; + u8 fec_sts_dpcd[DP_RECEIVER_FEC_STATUS_SIZE + 1]; struct drm_dp_link link_info; struct sde_edid_ctrl *edid_ctrl; @@ -109,9 +125,17 @@ struct dp_panel { struct dp_audio *audio; bool audio_supported; + + struct dp_dsc_caps sink_dsc_caps; + bool dsc_feature_enable; + bool fec_feature_enable; + bool dsc_en; + bool fec_en; bool widebus_en; bool mst_state; + s64 fec_overhead_fp; + int (*init)(struct dp_panel *dp_panel); int (*deinit)(struct dp_panel *dp_panel, u32 flags); int (*hw_cfg)(struct dp_panel *dp_panel, bool enable); @@ -141,6 +165,7 @@ struct dp_panel { void (*convert_to_dp_mode)(struct dp_panel *dp_panel, const struct drm_display_mode *drm_mode, struct dp_display_mode *dp_mode); + void (*update_pps)(struct dp_panel *dp_panel, char *pps_cmd); }; struct dp_tu_calc_input { diff --git a/drivers/gpu/drm/msm/dp/dp_parser.c b/drivers/gpu/drm/msm/dp/dp_parser.c index a315b736b379..138dd725b0f4 100644 --- a/drivers/gpu/drm/msm/dp/dp_parser.c +++ b/drivers/gpu/drm/msm/dp/dp_parser.c @@ -687,16 +687,37 @@ static int dp_parser_mst(struct dp_parser *parser) return 0; } -static int dp_parser_widebus(struct dp_parser *parser) +static void dp_parser_dsc(struct dp_parser *parser) +{ + struct device *dev = &parser->pdev->dev; + + parser->dsc_feature_enable = of_property_read_bool(dev->of_node, + "qcom,dsc-feature-enable"); + + pr_debug("dsc parsing successful. dsc:%d\n", + parser->dsc_feature_enable); +} + +static void dp_parser_fec(struct dp_parser *parser) +{ + struct device *dev = &parser->pdev->dev; + + parser->fec_feature_enable = of_property_read_bool(dev->of_node, + "qcom,fec-feature-enable"); + + pr_debug("fec parsing successful. fec:%d\n", + parser->fec_feature_enable); +} + +static void dp_parser_widebus(struct dp_parser *parser) { struct device *dev = &parser->pdev->dev; parser->has_widebus = of_property_read_bool(dev->of_node, "qcom,widebus-enable"); - pr_debug("widebus parsing successful. dsc:%d\n", parser->has_widebus); - - return 0; + pr_debug("widebus parsing successful. widebus:%d\n", + parser->has_widebus); } static int dp_parser_parse(struct dp_parser *parser) @@ -749,7 +770,9 @@ static int dp_parser_parse(struct dp_parser *parser) if (rc) goto err; - rc = dp_parser_widebus(parser); + dp_parser_dsc(parser); + dp_parser_fec(parser); + dp_parser_widebus(parser); err: return rc; } diff --git a/drivers/gpu/drm/msm/dp/dp_parser.h b/drivers/gpu/drm/msm/dp/dp_parser.h index 584ba60e45a3..c501944879d6 100644 --- a/drivers/gpu/drm/msm/dp/dp_parser.h +++ b/drivers/gpu/drm/msm/dp/dp_parser.h @@ -189,6 +189,12 @@ static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type) * @pinctrl: pin-control related data * @disp_data: controller's display related data * @hw_cfg: DP HW specific settings + * @has_mst: MST feature enable status + * @has_mst_sideband: MST sideband feature enable status + * @no_aux_switch: presence AUX switch status + * @dsc_feature_enable: DSC feature enable status + * @fec_feature_enable: FEC feature enable status + * @has_widebus: widebus (2PPC) feature eanble status * @parse: function to be called by client to parse device tree. * @get_io: function to be called by client to get io data. * @get_io_buf: function to be called by client to get io buffers. @@ -209,6 +215,8 @@ struct dp_parser { bool has_mst; bool has_mst_sideband; bool no_aux_switch; + bool dsc_feature_enable; + bool fec_feature_enable; bool has_widebus; int (*parse)(struct dp_parser *parser); diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h index 79a5638f5fe5..46404319d57a 100644 --- a/drivers/gpu/drm/msm/dp/dp_reg.h +++ b/drivers/gpu/drm/msm/dp/dp_reg.h @@ -99,6 +99,37 @@ #define MMSS_DP_PSR_CRC_B (0x00000158) #define DP_COMPRESSION_MODE_CTRL (0x00000180) +#define DP_PPS_HB_0_3 (0x00000184) +#define DP_PPS_PB_0_3 (0x00000188) +#define DP_PPS_PB_4_7 (0x0000018C) +#define DP_PPS_PB_8_11 (0x00000190) +#define DP_PPS_PB_12_15 (0x00000194) +#define DP_PPS_PB_16_19 (0x00000198) +#define DP_PPS_PB_20_23 (0x0000019C) +#define DP_PPS_PB_24_27 (0x000001A0) +#define DP_PPS_PB_28_31 (0x000001A4) +#define DP_PPS_PPS_0_3 (0x000001A8) +#define DP_PPS_PPS_4_7 (0x000001AC) +#define DP_PPS_PPS_8_11 (0x000001B0) +#define DP_PPS_PPS_12_15 (0x000001B4) +#define DP_PPS_PPS_16_19 (0x000001B8) +#define DP_PPS_PPS_20_23 (0x000001BC) +#define DP_PPS_PPS_24_27 (0x000001C0) +#define DP_PPS_PPS_28_31 (0x000001C4) +#define DP_PPS_PPS_32_35 (0x000001C8) +#define DP_PPS_PPS_36_39 (0x000001CC) +#define DP_PPS_PPS_40_43 (0x000001D0) +#define DP_PPS_PPS_44_47 (0x000001D4) +#define DP_PPS_PPS_48_51 (0x000001D8) +#define DP_PPS_PPS_52_55 (0x000001DC) +#define DP_PPS_PPS_56_59 (0x000001E0) +#define DP_PPS_PPS_60_63 (0x000001E4) +#define DP_PPS_PPS_64_67 (0x000001E8) +#define DP_PPS_PPS_68_71 (0x000001EC) +#define DP_PPS_PPS_72_75 (0x000001F0) +#define DP_PPS_PPS_76_79 (0x000001F4) +#define DP_PPS_PPS_80_83 (0x000001F8) +#define DP_PPS_PPS_84_87 (0x000001FC) #define MMSS_DP_AUDIO_CFG (0x00000200) #define MMSS_DP_AUDIO_STATUS (0x00000204) @@ -143,6 +174,7 @@ #define MMSS_DP_AUDIO_INFOFRAME_2 (0x000002B0) #define MMSS_DP_FLUSH (0x000002F8) +#define MMSS_DP1_FLUSH (0x000002FC) #define MMSS_DP_GENERIC0_0 (0x00000300) #define MMSS_DP_GENERIC0_1 (0x00000304) @@ -190,6 +222,39 @@ #define MMSS_DP1_SDP_CFG2 (0x000004E4) #define MMSS_DP1_SDP_CFG3 (0x000004E8) +#define DP1_COMPRESSION_MODE_CTRL (0x00000560) +#define DP1_PPS_HB_0_3 (0x00000564) +#define DP1_PPS_PB_0_3 (0x00000568) +#define DP1_PPS_PB_4_7 (0x0000056C) +#define DP1_PPS_PB_8_11 (0x00000570) +#define DP1_PPS_PB_12_15 (0x00000574) +#define DP1_PPS_PB_16_19 (0x00000578) +#define DP1_PPS_PB_20_23 (0x0000057C) +#define DP1_PPS_PB_24_27 (0x00000580) +#define DP1_PPS_PB_28_31 (0x00000584) +#define DP1_PPS_PPS_0_3 (0x00000588) +#define DP1_PPS_PPS_4_7 (0x0000058C) +#define DP1_PPS_PPS_8_11 (0x00000590) +#define DP1_PPS_PPS_12_15 (0x00000594) +#define DP1_PPS_PPS_16_19 (0x00000598) +#define DP1_PPS_PPS_20_23 (0x0000059C) +#define DP1_PPS_PPS_24_27 (0x000005A0) +#define DP1_PPS_PPS_28_31 (0x000005A4) +#define DP1_PPS_PPS_32_35 (0x000005A8) +#define DP1_PPS_PPS_36_39 (0x000005AC) +#define DP1_PPS_PPS_40_43 (0x000005B0) +#define DP1_PPS_PPS_44_47 (0x000005B4) +#define DP1_PPS_PPS_48_51 (0x000005B8) +#define DP1_PPS_PPS_52_55 (0x000005BC) +#define DP1_PPS_PPS_56_59 (0x000005C0) +#define DP1_PPS_PPS_60_63 (0x000005C4) +#define DP1_PPS_PPS_64_67 (0x000005C8) +#define DP1_PPS_PPS_68_71 (0x000005CC) +#define DP1_PPS_PPS_72_75 (0x000005D0) +#define DP1_PPS_PPS_76_79 (0x000005D4) +#define DP1_PPS_PPS_80_83 (0x000005D8) +#define DP1_PPS_PPS_84_87 (0x000005DC) + #define MMSS_DP_VSCEXT_0 (0x000002D0) #define MMSS_DP_VSCEXT_1 (0x000002D4) #define MMSS_DP_VSCEXT_2 (0x000002D8) @@ -223,6 +288,7 @@ #define MMSS_DP_TPG_MAIN_CONTROL (0x00000060) #define MMSS_DP_TPG_VIDEO_CONFIG (0x00000064) #define MMSS_DP_DSC_DTO (0x0000007C) +#define MMSS_DP_DSC_DTO_COUNT (0x00000084) #define MMSS_DP_ASYNC_FIFO_CONFIG (0x00000088) #define MMSS_DP1_BIST_ENABLE (0x00000000) @@ -247,6 +313,7 @@ #define MMSS_DP1_TPG_MAIN_CONTROL (0x00000060) #define MMSS_DP1_TPG_VIDEO_CONFIG (0x00000064) #define MMSS_DP1_DSC_DTO (0x0000007C) +#define MMSS_DP1_DSC_DTO_COUNT (0x00000084) #define MMSS_DP1_ASYNC_FIFO_CONFIG (0x00000088) /*DP PHY Register offsets */ diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c index d339441c2452..b2aad0e2002e 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.c +++ b/drivers/gpu/drm/msm/sde/sde_kms.c @@ -1429,6 +1429,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev, .cmd_transfer = NULL, .cont_splash_config = NULL, .get_panel_vfp = NULL, + .update_pps = dp_connector_update_pps, }; struct msm_display_info info; struct drm_encoder *encoder;