disp: msm: dsi: add support for dsi dynamic clock switch
This change adds support for dynamic switching of dsi clocks to avoid RF interference issues. Also with dynamic dsi clock switch feature coming into picture, now populate the supported refresh rate as list instead of providing a range. Modify the logic to enumerate all the modes in dsi driver, taking dynamic bit clocks, resolutions and refresh rates into account. Change-Id: I5b6e62bc935cf2234bdd96fcb3c7537b4e735fff Signed-off-by: Sandeep Panda <spanda@codeaurora.org> Signed-off-by: Ritesh Kumar <riteshk@codeaurora.org> Signed-off-by: Yujun Zhang <yujunzhang@codeaurora.org>
Esse commit está contido em:
@@ -1643,14 +1643,12 @@ static int dsi_display_debugfs_deinit(struct dsi_display *display)
|
||||
static void adjust_timing_by_ctrl_count(const struct dsi_display *display,
|
||||
struct dsi_display_mode *mode)
|
||||
{
|
||||
if (display->ctrl_count > 1) {
|
||||
mode->timing.h_active /= display->ctrl_count;
|
||||
mode->timing.h_front_porch /= display->ctrl_count;
|
||||
mode->timing.h_sync_width /= display->ctrl_count;
|
||||
mode->timing.h_back_porch /= display->ctrl_count;
|
||||
mode->timing.h_skew /= display->ctrl_count;
|
||||
mode->pixel_clk_khz /= display->ctrl_count;
|
||||
}
|
||||
mode->timing.h_active /= display->ctrl_count;
|
||||
mode->timing.h_front_porch /= display->ctrl_count;
|
||||
mode->timing.h_sync_width /= display->ctrl_count;
|
||||
mode->timing.h_back_porch /= display->ctrl_count;
|
||||
mode->timing.h_skew /= display->ctrl_count;
|
||||
mode->pixel_clk_khz /= display->ctrl_count;
|
||||
}
|
||||
|
||||
static int dsi_display_is_ulps_req_valid(struct dsi_display *display,
|
||||
@@ -2271,7 +2269,7 @@ static int dsi_display_set_clk_src(struct dsi_display *display)
|
||||
m_ctrl = &display->ctrl[display->clk_master_idx];
|
||||
|
||||
rc = dsi_ctrl_set_clock_source(m_ctrl->ctrl,
|
||||
&display->clock_info.src_clks);
|
||||
&display->clock_info.mux_clks);
|
||||
if (rc) {
|
||||
pr_err("[%s] failed to set source clocks for master, rc=%d\n",
|
||||
display->name, rc);
|
||||
@@ -2285,7 +2283,7 @@ static int dsi_display_set_clk_src(struct dsi_display *display)
|
||||
continue;
|
||||
|
||||
rc = dsi_ctrl_set_clock_source(ctrl->ctrl,
|
||||
&display->clock_info.src_clks);
|
||||
&display->clock_info.mux_clks);
|
||||
if (rc) {
|
||||
pr_err("[%s] failed to set source clocks, rc=%d\n",
|
||||
display->name, rc);
|
||||
@@ -2956,6 +2954,7 @@ static int dsi_display_clocks_init(struct dsi_display *display)
|
||||
struct dsi_clk_link_set *src = &display->clock_info.src_clks;
|
||||
struct dsi_clk_link_set *mux = &display->clock_info.mux_clks;
|
||||
struct dsi_clk_link_set *shadow = &display->clock_info.shadow_clks;
|
||||
struct dsi_dyn_clk_caps *dyn_clk_caps = &(display->panel->dyn_clk_caps);
|
||||
char *dsi_clock_name;
|
||||
|
||||
if (!strcmp(display->display_type, "primary"))
|
||||
@@ -2978,7 +2977,32 @@ static int dsi_display_clocks_init(struct dsi_display *display)
|
||||
rc = PTR_ERR(dsi_clk);
|
||||
|
||||
pr_err("failed to get %s, rc=%d\n", clk_name, rc);
|
||||
goto error;
|
||||
|
||||
if (dsi_display_check_prefix(mux_byte, clk_name)) {
|
||||
mux->byte_clk = NULL;
|
||||
goto error;
|
||||
}
|
||||
if (dsi_display_check_prefix(mux_pixel, clk_name)) {
|
||||
mux->pixel_clk = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (dyn_clk_caps->dyn_clk_support) {
|
||||
if (dsi_display_check_prefix(src_byte,
|
||||
clk_name))
|
||||
src->byte_clk = NULL;
|
||||
if (dsi_display_check_prefix(src_pixel,
|
||||
clk_name))
|
||||
src->pixel_clk = NULL;
|
||||
if (dsi_display_check_prefix(shadow_byte,
|
||||
clk_name))
|
||||
shadow->byte_clk = NULL;
|
||||
if (dsi_display_check_prefix(shadow_pixel,
|
||||
clk_name))
|
||||
shadow->pixel_clk = NULL;
|
||||
|
||||
dyn_clk_caps->dyn_clk_support = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (dsi_display_check_prefix(src_byte, clk_name)) {
|
||||
@@ -3762,6 +3786,305 @@ static bool dsi_display_is_seamless_dfps_possible(
|
||||
return true;
|
||||
}
|
||||
|
||||
static int dsi_display_update_dsi_bitrate(struct dsi_display *display,
|
||||
u32 bit_clk_rate)
|
||||
{
|
||||
int rc = 0;
|
||||
int i;
|
||||
|
||||
pr_debug("%s:bit rate:%d\n", __func__, bit_clk_rate);
|
||||
if (!display->panel) {
|
||||
pr_err("Invalid params\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (bit_clk_rate == 0) {
|
||||
pr_err("Invalid bit clock rate\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
display->config.bit_clk_rate_hz = bit_clk_rate;
|
||||
|
||||
for (i = 0; i < display->ctrl_count; i++) {
|
||||
struct dsi_display_ctrl *dsi_disp_ctrl = &display->ctrl[i];
|
||||
struct dsi_ctrl *ctrl = dsi_disp_ctrl->ctrl;
|
||||
u32 num_of_lanes = 0, bpp;
|
||||
u64 bit_rate, pclk_rate, bit_rate_per_lane, byte_clk_rate;
|
||||
struct dsi_host_common_cfg *host_cfg;
|
||||
|
||||
mutex_lock(&ctrl->ctrl_lock);
|
||||
|
||||
host_cfg = &display->panel->host_config;
|
||||
if (host_cfg->data_lanes & DSI_DATA_LANE_0)
|
||||
num_of_lanes++;
|
||||
if (host_cfg->data_lanes & DSI_DATA_LANE_1)
|
||||
num_of_lanes++;
|
||||
if (host_cfg->data_lanes & DSI_DATA_LANE_2)
|
||||
num_of_lanes++;
|
||||
if (host_cfg->data_lanes & DSI_DATA_LANE_3)
|
||||
num_of_lanes++;
|
||||
|
||||
if (num_of_lanes == 0) {
|
||||
pr_err("Invalid lane count\n");
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
bpp = dsi_pixel_format_to_bpp(host_cfg->dst_format);
|
||||
|
||||
bit_rate = display->config.bit_clk_rate_hz * num_of_lanes;
|
||||
bit_rate_per_lane = bit_rate;
|
||||
do_div(bit_rate_per_lane, num_of_lanes);
|
||||
pclk_rate = bit_rate;
|
||||
do_div(pclk_rate, bpp);
|
||||
byte_clk_rate = bit_rate_per_lane;
|
||||
do_div(byte_clk_rate, 8);
|
||||
pr_debug("bit_clk_rate = %llu, bit_clk_rate_per_lane = %llu\n",
|
||||
bit_rate, bit_rate_per_lane);
|
||||
pr_debug("byte_clk_rate = %llu, pclk_rate = %llu\n",
|
||||
byte_clk_rate, pclk_rate);
|
||||
|
||||
ctrl->clk_freq.byte_clk_rate = byte_clk_rate;
|
||||
ctrl->clk_freq.pix_clk_rate = pclk_rate;
|
||||
rc = dsi_clk_set_link_frequencies(display->dsi_clk_handle,
|
||||
ctrl->clk_freq, ctrl->cell_index);
|
||||
if (rc) {
|
||||
pr_err("Failed to update link frequencies\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ctrl->host_config.bit_clk_rate_hz = bit_clk_rate;
|
||||
error:
|
||||
mutex_unlock(&ctrl->ctrl_lock);
|
||||
|
||||
/* TODO: recover ctrl->clk_freq in case of failure */
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _dsi_display_calc_pipe_delay(struct dsi_display *display,
|
||||
struct dsi_dyn_clk_delay *delay,
|
||||
struct dsi_display_mode *mode)
|
||||
{
|
||||
u32 esc_clk_rate_hz;
|
||||
u32 pclk_to_esc_ratio, byte_to_esc_ratio, hr_bit_to_esc_ratio;
|
||||
u32 hsync_period = 0;
|
||||
struct dsi_display_ctrl *m_ctrl;
|
||||
struct dsi_ctrl *dsi_ctrl;
|
||||
struct dsi_phy_cfg *cfg;
|
||||
|
||||
m_ctrl = &display->ctrl[display->clk_master_idx];
|
||||
dsi_ctrl = m_ctrl->ctrl;
|
||||
|
||||
cfg = &(m_ctrl->phy->cfg);
|
||||
|
||||
esc_clk_rate_hz = dsi_ctrl->clk_freq.esc_clk_rate * 1000;
|
||||
pclk_to_esc_ratio = ((dsi_ctrl->clk_freq.pix_clk_rate * 1000) /
|
||||
esc_clk_rate_hz);
|
||||
byte_to_esc_ratio = ((dsi_ctrl->clk_freq.byte_clk_rate * 1000) /
|
||||
esc_clk_rate_hz);
|
||||
hr_bit_to_esc_ratio = ((dsi_ctrl->clk_freq.byte_clk_rate * 4 * 1000) /
|
||||
esc_clk_rate_hz);
|
||||
|
||||
hsync_period = DSI_H_TOTAL_DSC(&mode->timing);
|
||||
delay->pipe_delay = (hsync_period + 1) / pclk_to_esc_ratio;
|
||||
if (!display->panel->video_config.eof_bllp_lp11_en)
|
||||
delay->pipe_delay += (17 / pclk_to_esc_ratio) +
|
||||
((21 + (display->config.common_config.t_clk_pre + 1) +
|
||||
(display->config.common_config.t_clk_post + 1)) /
|
||||
byte_to_esc_ratio) +
|
||||
((((cfg->timing.lane_v3[8] >> 1) + 1) +
|
||||
((cfg->timing.lane_v3[6] >> 1) + 1) +
|
||||
((cfg->timing.lane_v3[3] * 4) +
|
||||
(cfg->timing.lane_v3[5] >> 1) + 1) +
|
||||
((cfg->timing.lane_v3[7] >> 1) + 1) +
|
||||
((cfg->timing.lane_v3[1] >> 1) + 1) +
|
||||
((cfg->timing.lane_v3[4] >> 1) + 1)) /
|
||||
hr_bit_to_esc_ratio);
|
||||
|
||||
delay->pipe_delay2 = 0;
|
||||
if (display->panel->host_config.force_hs_clk_lane)
|
||||
delay->pipe_delay2 = (6 / byte_to_esc_ratio) +
|
||||
((((cfg->timing.lane_v3[1] >> 1) + 1) +
|
||||
((cfg->timing.lane_v3[4] >> 1) + 1)) /
|
||||
hr_bit_to_esc_ratio);
|
||||
|
||||
/* 130 us pll delay recommended by h/w doc */
|
||||
delay->pll_delay = ((130 * esc_clk_rate_hz) / 1000000) * 2;
|
||||
}
|
||||
|
||||
static int _dsi_display_dyn_update_clks(struct dsi_display *display,
|
||||
struct link_clk_freq *bkp_freq)
|
||||
{
|
||||
int rc = 0, i;
|
||||
struct dsi_display_ctrl *m_ctrl, *ctrl;
|
||||
|
||||
m_ctrl = &display->ctrl[display->clk_master_idx];
|
||||
|
||||
dsi_clk_prepare_enable(&display->clock_info.src_clks);
|
||||
|
||||
rc = dsi_clk_update_parent(&display->clock_info.shadow_clks,
|
||||
&display->clock_info.mux_clks);
|
||||
if (rc) {
|
||||
pr_err("failed update mux parent to shadow\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; (i < display->ctrl_count) &&
|
||||
(i < MAX_DSI_CTRLS_PER_DISPLAY); i++) {
|
||||
ctrl = &display->ctrl[i];
|
||||
if (!ctrl->ctrl)
|
||||
continue;
|
||||
rc = dsi_clk_set_byte_clk_rate(display->dsi_clk_handle,
|
||||
ctrl->ctrl->clk_freq.byte_clk_rate, i);
|
||||
if (rc) {
|
||||
pr_err("failed to set byte rate for index:%d\n", i);
|
||||
goto recover_byte_clk;
|
||||
}
|
||||
rc = dsi_clk_set_pixel_clk_rate(display->dsi_clk_handle,
|
||||
ctrl->ctrl->clk_freq.pix_clk_rate, i);
|
||||
if (rc) {
|
||||
pr_err("failed to set pix rate for index:%d\n", i);
|
||||
goto recover_pix_clk;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; (i < display->ctrl_count) &&
|
||||
(i < MAX_DSI_CTRLS_PER_DISPLAY); i++) {
|
||||
ctrl = &display->ctrl[i];
|
||||
if (ctrl == m_ctrl)
|
||||
continue;
|
||||
dsi_phy_dynamic_refresh_trigger(ctrl->phy, false);
|
||||
}
|
||||
dsi_phy_dynamic_refresh_trigger(m_ctrl->phy, true);
|
||||
|
||||
/* wait for dynamic refresh done */
|
||||
for (i = 0; (i < display->ctrl_count) &&
|
||||
(i < MAX_DSI_CTRLS_PER_DISPLAY); i++) {
|
||||
ctrl = &display->ctrl[i];
|
||||
rc = dsi_ctrl_wait4dynamic_refresh_done(ctrl->ctrl);
|
||||
if (rc) {
|
||||
pr_err("wait4dynamic refresh failed for dsi:%d\n", i);
|
||||
goto recover_pix_clk;
|
||||
} else {
|
||||
pr_info("dynamic refresh done on dsi: %s\n",
|
||||
i ? "slave" : "master");
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; (i < display->ctrl_count) &&
|
||||
(i < MAX_DSI_CTRLS_PER_DISPLAY); i++) {
|
||||
ctrl = &display->ctrl[i];
|
||||
dsi_phy_dynamic_refresh_clear(ctrl->phy);
|
||||
}
|
||||
|
||||
rc = dsi_clk_update_parent(&display->clock_info.src_clks,
|
||||
&display->clock_info.mux_clks);
|
||||
if (rc)
|
||||
pr_err("could not switch back to src clks %d\n", rc);
|
||||
|
||||
dsi_clk_disable_unprepare(&display->clock_info.src_clks);
|
||||
|
||||
return rc;
|
||||
|
||||
recover_pix_clk:
|
||||
for (i = 0; (i < display->ctrl_count) &&
|
||||
(i < MAX_DSI_CTRLS_PER_DISPLAY); i++) {
|
||||
ctrl = &display->ctrl[i];
|
||||
if (!ctrl->ctrl)
|
||||
continue;
|
||||
dsi_clk_set_pixel_clk_rate(display->dsi_clk_handle,
|
||||
bkp_freq->pix_clk_rate, i);
|
||||
}
|
||||
|
||||
recover_byte_clk:
|
||||
for (i = 0; (i < display->ctrl_count) &&
|
||||
(i < MAX_DSI_CTRLS_PER_DISPLAY); i++) {
|
||||
ctrl = &display->ctrl[i];
|
||||
if (!ctrl->ctrl)
|
||||
continue;
|
||||
dsi_clk_set_byte_clk_rate(display->dsi_clk_handle,
|
||||
bkp_freq->byte_clk_rate, i);
|
||||
}
|
||||
|
||||
exit:
|
||||
dsi_clk_disable_unprepare(&display->clock_info.src_clks);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dsi_display_dynamic_clk_switch(struct dsi_display *display,
|
||||
struct dsi_display_mode *mode)
|
||||
{
|
||||
int rc = 0, mask, i;
|
||||
struct dsi_display_ctrl *m_ctrl, *ctrl;
|
||||
struct dsi_dyn_clk_delay delay;
|
||||
struct link_clk_freq bkp_freq;
|
||||
|
||||
dsi_panel_acquire_panel_lock(display->panel);
|
||||
|
||||
m_ctrl = &display->ctrl[display->clk_master_idx];
|
||||
|
||||
dsi_display_clk_ctrl(display->dsi_clk_handle, DSI_ALL_CLKS, DSI_CLK_ON);
|
||||
|
||||
/* mask PLL unlock, FIFO overflow and underflow errors */
|
||||
mask = BIT(DSI_PLL_UNLOCK_ERR) | BIT(DSI_FIFO_UNDERFLOW) |
|
||||
BIT(DSI_FIFO_OVERFLOW);
|
||||
dsi_display_mask_ctrl_error_interrupts(display, mask, true);
|
||||
|
||||
/* update the phy timings based on new mode */
|
||||
for (i = 0; i < display->ctrl_count; i++) {
|
||||
ctrl = &display->ctrl[i];
|
||||
dsi_phy_update_phy_timings(ctrl->phy, &display->config);
|
||||
}
|
||||
|
||||
/* back up existing rates to handle failure case */
|
||||
bkp_freq.byte_clk_rate = m_ctrl->ctrl->clk_freq.byte_clk_rate;
|
||||
bkp_freq.pix_clk_rate = m_ctrl->ctrl->clk_freq.pix_clk_rate;
|
||||
bkp_freq.esc_clk_rate = m_ctrl->ctrl->clk_freq.esc_clk_rate;
|
||||
|
||||
rc = dsi_display_update_dsi_bitrate(display, mode->timing.clk_rate_hz);
|
||||
if (rc) {
|
||||
pr_err("failed set link frequencies %d\n", rc);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* calculate pipe delays */
|
||||
_dsi_display_calc_pipe_delay(display, &delay, mode);
|
||||
|
||||
/* configure dynamic refresh ctrl registers */
|
||||
for (i = 0; i < display->ctrl_count; i++) {
|
||||
ctrl = &display->ctrl[i];
|
||||
if (!ctrl->phy)
|
||||
continue;
|
||||
if (ctrl == m_ctrl)
|
||||
dsi_phy_config_dynamic_refresh(ctrl->phy, &delay, true);
|
||||
else
|
||||
dsi_phy_config_dynamic_refresh(ctrl->phy, &delay,
|
||||
false);
|
||||
}
|
||||
|
||||
rc = _dsi_display_dyn_update_clks(display, &bkp_freq);
|
||||
|
||||
exit:
|
||||
dsi_display_mask_ctrl_error_interrupts(display, mask, false);
|
||||
|
||||
dsi_display_clk_ctrl(display->dsi_clk_handle, DSI_ALL_CLKS,
|
||||
DSI_CLK_OFF);
|
||||
|
||||
/* store newly calculated phy timings in mode private info */
|
||||
dsi_phy_dyn_refresh_cache_phy_timings(m_ctrl->phy,
|
||||
mode->priv_info->phy_timing_val,
|
||||
mode->priv_info->phy_timing_len);
|
||||
|
||||
dsi_panel_release_panel_lock(display->panel);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dsi_display_dfps_update(struct dsi_display *display,
|
||||
struct dsi_display_mode *dsi_mode)
|
||||
{
|
||||
@@ -4031,6 +4354,16 @@ static int dsi_display_set_mode_sub(struct dsi_display *display,
|
||||
display->name, rc);
|
||||
goto error;
|
||||
}
|
||||
} else if (mode->dsi_mode_flags & DSI_MODE_FLAG_DYN_CLK) {
|
||||
rc = dsi_display_dynamic_clk_switch(display, mode);
|
||||
if (rc)
|
||||
pr_err("dynamic clk change failed %d\n", rc);
|
||||
/*
|
||||
* skip rest of the opearations since
|
||||
* dsi_display_dynamic_clk_switch() already takes
|
||||
* care of them.
|
||||
*/
|
||||
return rc;
|
||||
}
|
||||
|
||||
display_for_each_ctrl(i, display) {
|
||||
@@ -4261,208 +4594,6 @@ static int dsi_display_force_update_dsi_clk(struct dsi_display *display)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dsi_display_request_update_dsi_bitrate(struct dsi_display *display,
|
||||
u32 bit_clk_rate)
|
||||
{
|
||||
int rc = 0;
|
||||
int i;
|
||||
|
||||
pr_debug("%s:bit rate:%d\n", __func__, bit_clk_rate);
|
||||
if (!display->panel) {
|
||||
pr_err("Invalid params\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (bit_clk_rate == 0) {
|
||||
pr_err("Invalid bit clock rate\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
display->config.bit_clk_rate_hz_override = bit_clk_rate;
|
||||
|
||||
display_for_each_ctrl(i, display) {
|
||||
struct dsi_display_ctrl *dsi_disp_ctrl = &display->ctrl[i];
|
||||
struct dsi_ctrl *ctrl = dsi_disp_ctrl->ctrl;
|
||||
u32 num_of_lanes = 0;
|
||||
u32 bpp = 3;
|
||||
u64 bit_rate, pclk_rate, bit_rate_per_lane, byte_clk_rate;
|
||||
struct dsi_host_common_cfg *host_cfg;
|
||||
|
||||
mutex_lock(&ctrl->ctrl_lock);
|
||||
|
||||
host_cfg = &display->panel->host_config;
|
||||
if (host_cfg->data_lanes & DSI_DATA_LANE_0)
|
||||
num_of_lanes++;
|
||||
if (host_cfg->data_lanes & DSI_DATA_LANE_1)
|
||||
num_of_lanes++;
|
||||
if (host_cfg->data_lanes & DSI_DATA_LANE_2)
|
||||
num_of_lanes++;
|
||||
if (host_cfg->data_lanes & DSI_DATA_LANE_3)
|
||||
num_of_lanes++;
|
||||
|
||||
if (num_of_lanes == 0) {
|
||||
pr_err("Invalid lane count\n");
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
bit_rate = display->config.bit_clk_rate_hz_override *
|
||||
num_of_lanes;
|
||||
bit_rate_per_lane = bit_rate;
|
||||
do_div(bit_rate_per_lane, num_of_lanes);
|
||||
pclk_rate = bit_rate;
|
||||
do_div(pclk_rate, (8 * bpp));
|
||||
byte_clk_rate = bit_rate_per_lane;
|
||||
do_div(byte_clk_rate, 8);
|
||||
pr_debug("bit_clk_rate = %llu, bit_clk_rate_per_lane = %llu\n",
|
||||
bit_rate, bit_rate_per_lane);
|
||||
pr_debug("byte_clk_rate = %llu, pclk_rate = %llu\n",
|
||||
byte_clk_rate, pclk_rate);
|
||||
|
||||
ctrl->clk_freq.byte_clk_rate = byte_clk_rate;
|
||||
ctrl->clk_freq.pix_clk_rate = pclk_rate;
|
||||
rc = dsi_clk_set_link_frequencies(display->dsi_clk_handle,
|
||||
ctrl->clk_freq, ctrl->cell_index);
|
||||
if (rc) {
|
||||
pr_err("Failed to update link frequencies\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ctrl->host_config.bit_clk_rate_hz_override = bit_clk_rate;
|
||||
error:
|
||||
mutex_unlock(&ctrl->ctrl_lock);
|
||||
|
||||
/* TODO: recover ctrl->clk_freq in case of failure */
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t dynamic_dsi_clock_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dsi_display *display;
|
||||
struct dsi_display_ctrl *m_ctrl;
|
||||
struct dsi_ctrl *ctrl;
|
||||
|
||||
display = dev_get_drvdata(dev);
|
||||
if (!display) {
|
||||
pr_err("Invalid display\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&display->display_lock);
|
||||
|
||||
m_ctrl = &display->ctrl[display->cmd_master_idx];
|
||||
ctrl = m_ctrl->ctrl;
|
||||
if (ctrl)
|
||||
display->cached_clk_rate = ctrl->clk_freq.byte_clk_rate
|
||||
* 8;
|
||||
|
||||
rc = snprintf(buf, PAGE_SIZE, "%d\n", display->cached_clk_rate);
|
||||
pr_debug("%s: read dsi clk rate %d\n", __func__,
|
||||
display->cached_clk_rate);
|
||||
|
||||
mutex_unlock(&display->display_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t dynamic_dsi_clock_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
int rc = 0;
|
||||
int clk_rate;
|
||||
struct dsi_display *display;
|
||||
|
||||
display = dev_get_drvdata(dev);
|
||||
if (!display) {
|
||||
pr_err("Invalid display\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = kstrtoint(buf, DSI_CLOCK_BITRATE_RADIX, &clk_rate);
|
||||
if (rc) {
|
||||
pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (clk_rate <= 0) {
|
||||
pr_err("%s: bitrate should be greater than 0\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (clk_rate == display->cached_clk_rate) {
|
||||
pr_info("%s: ignore duplicated DSI clk setting\n", __func__);
|
||||
return count;
|
||||
}
|
||||
|
||||
pr_info("%s: bitrate param value: '%d'\n", __func__, clk_rate);
|
||||
|
||||
mutex_lock(&display->display_lock);
|
||||
|
||||
display->cached_clk_rate = clk_rate;
|
||||
rc = dsi_display_request_update_dsi_bitrate(display, clk_rate);
|
||||
if (!rc) {
|
||||
pr_info("%s: bit clk is ready to be configured to '%d'\n",
|
||||
__func__, clk_rate);
|
||||
} else {
|
||||
pr_err("%s: Failed to prepare to configure '%d'. rc = %d\n",
|
||||
__func__, clk_rate, rc);
|
||||
/*Caching clock failed, so don't go on doing so.*/
|
||||
atomic_set(&display->clkrate_change_pending, 0);
|
||||
display->cached_clk_rate = 0;
|
||||
|
||||
mutex_unlock(&display->display_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
atomic_set(&display->clkrate_change_pending, 1);
|
||||
|
||||
mutex_unlock(&display->display_lock);
|
||||
|
||||
return count;
|
||||
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(dynamic_dsi_clock);
|
||||
|
||||
static struct attribute *dynamic_dsi_clock_fs_attrs[] = {
|
||||
&dev_attr_dynamic_dsi_clock.attr,
|
||||
NULL,
|
||||
};
|
||||
static struct attribute_group dynamic_dsi_clock_fs_attrs_group = {
|
||||
.attrs = dynamic_dsi_clock_fs_attrs,
|
||||
};
|
||||
|
||||
static int dsi_display_sysfs_init(struct dsi_display *display)
|
||||
{
|
||||
int rc = 0;
|
||||
struct device *dev = &display->pdev->dev;
|
||||
|
||||
if (display->panel->panel_mode == DSI_OP_CMD_MODE)
|
||||
rc = sysfs_create_group(&dev->kobj,
|
||||
&dynamic_dsi_clock_fs_attrs_group);
|
||||
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
static int dsi_display_sysfs_deinit(struct dsi_display *display)
|
||||
{
|
||||
struct device *dev = &display->pdev->dev;
|
||||
|
||||
if (display->panel->panel_mode == DSI_OP_CMD_MODE)
|
||||
sysfs_remove_group(&dev->kobj,
|
||||
&dynamic_dsi_clock_fs_attrs_group);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* dsi_display_bind - bind dsi device with controlling device
|
||||
* @dev: Pointer to base of platform device
|
||||
@@ -4514,12 +4645,6 @@ static int dsi_display_bind(struct device *dev,
|
||||
atomic_set(&display->clkrate_change_pending, 0);
|
||||
display->cached_clk_rate = 0;
|
||||
|
||||
rc = dsi_display_sysfs_init(display);
|
||||
if (rc) {
|
||||
pr_err("[%s] sysfs init failed, rc=%d\n", display->name, rc);
|
||||
goto error;
|
||||
}
|
||||
|
||||
memset(&info, 0x0, sizeof(info));
|
||||
|
||||
display_for_each_ctrl(i, display) {
|
||||
@@ -4665,7 +4790,6 @@ error_ctrl_deinit:
|
||||
(void)dsi_phy_drv_deinit(display_ctrl->phy);
|
||||
(void)dsi_ctrl_drv_deinit(display_ctrl->ctrl);
|
||||
}
|
||||
(void)dsi_display_sysfs_deinit(display);
|
||||
(void)dsi_display_debugfs_deinit(display);
|
||||
error:
|
||||
mutex_unlock(&display->display_lock);
|
||||
@@ -4725,7 +4849,6 @@ static void dsi_display_unbind(struct device *dev,
|
||||
}
|
||||
|
||||
atomic_set(&display->clkrate_change_pending, 0);
|
||||
(void)dsi_display_sysfs_deinit(display);
|
||||
(void)dsi_display_debugfs_deinit(display);
|
||||
|
||||
mutex_unlock(&display->display_lock);
|
||||
@@ -5480,7 +5603,8 @@ static int dsi_display_get_mode_count_no_lock(struct dsi_display *display,
|
||||
u32 *count)
|
||||
{
|
||||
struct dsi_dfps_capabilities dfps_caps;
|
||||
int num_dfps_rates, rc = 0;
|
||||
struct dsi_dyn_clk_caps *dyn_clk_caps;
|
||||
int num_dfps_rates, num_bit_clks, rc = 0;
|
||||
|
||||
if (!display || !display->panel) {
|
||||
pr_err("invalid display:%d panel:%d\n", display != NULL,
|
||||
@@ -5497,12 +5621,16 @@ static int dsi_display_get_mode_count_no_lock(struct dsi_display *display,
|
||||
return rc;
|
||||
}
|
||||
|
||||
num_dfps_rates = !dfps_caps.dfps_support ? 1 :
|
||||
dfps_caps.max_refresh_rate -
|
||||
dfps_caps.min_refresh_rate + 1;
|
||||
num_dfps_rates = !dfps_caps.dfps_support ? 1 : dfps_caps.dfps_list_len;
|
||||
|
||||
/* Inflate num_of_modes by fps in dfps */
|
||||
*count = display->panel->num_timing_nodes * num_dfps_rates;
|
||||
dyn_clk_caps = &(display->panel->dyn_clk_caps);
|
||||
|
||||
num_bit_clks = !dyn_clk_caps->dyn_clk_support ? 1 :
|
||||
dyn_clk_caps->bit_clk_list_len;
|
||||
|
||||
/* Inflate num_of_modes by fps and bit clks in dfps */
|
||||
*count = display->panel->num_timing_nodes *
|
||||
num_dfps_rates * num_bit_clks;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -5525,6 +5653,73 @@ int dsi_display_get_mode_count(struct dsi_display *display,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _dsi_display_populate_bit_clks(struct dsi_display *display,
|
||||
int start, int end, u32 *mode_idx)
|
||||
{
|
||||
struct dsi_dyn_clk_caps *dyn_clk_caps;
|
||||
struct dsi_display_mode *src, *dst;
|
||||
struct dsi_host_common_cfg *cfg;
|
||||
int i, j, total_modes, bpp, lanes = 0;
|
||||
|
||||
if (!display || !mode_idx)
|
||||
return;
|
||||
|
||||
dyn_clk_caps = &(display->panel->dyn_clk_caps);
|
||||
if (!dyn_clk_caps->dyn_clk_support)
|
||||
return;
|
||||
|
||||
cfg = &(display->panel->host_config);
|
||||
bpp = dsi_pixel_format_to_bpp(cfg->dst_format);
|
||||
|
||||
if (cfg->data_lanes & DSI_DATA_LANE_0)
|
||||
lanes++;
|
||||
if (cfg->data_lanes & DSI_DATA_LANE_1)
|
||||
lanes++;
|
||||
if (cfg->data_lanes & DSI_DATA_LANE_2)
|
||||
lanes++;
|
||||
if (cfg->data_lanes & DSI_DATA_LANE_3)
|
||||
lanes++;
|
||||
|
||||
dsi_display_get_mode_count_no_lock(display, &total_modes);
|
||||
|
||||
for (i = start; i < end; i++) {
|
||||
src = &display->modes[i];
|
||||
if (!src)
|
||||
return;
|
||||
/*
|
||||
* TODO: currently setting the first bit rate in
|
||||
* the list as preferred rate. But ideally should
|
||||
* be based on user or device tree preferrence.
|
||||
*/
|
||||
src->timing.clk_rate_hz = dyn_clk_caps->bit_clk_list[0];
|
||||
src->pixel_clk_khz =
|
||||
div_u64(src->timing.clk_rate_hz * lanes, bpp);
|
||||
src->pixel_clk_khz /= 1000;
|
||||
src->pixel_clk_khz *= display->ctrl_count;
|
||||
}
|
||||
|
||||
for (i = 1; i < dyn_clk_caps->bit_clk_list_len; i++) {
|
||||
if (*mode_idx >= total_modes)
|
||||
return;
|
||||
for (j = start; j < end; j++) {
|
||||
src = &display->modes[j];
|
||||
dst = &display->modes[*mode_idx];
|
||||
|
||||
if (!src || !dst) {
|
||||
pr_err("invalid mode index\n");
|
||||
return;
|
||||
}
|
||||
memcpy(dst, src, sizeof(struct dsi_display_mode));
|
||||
dst->timing.clk_rate_hz = dyn_clk_caps->bit_clk_list[i];
|
||||
dst->pixel_clk_khz =
|
||||
div_u64(dst->timing.clk_rate_hz * lanes, bpp);
|
||||
dst->pixel_clk_khz /= 1000;
|
||||
dst->pixel_clk_khz *= display->ctrl_count;
|
||||
(*mode_idx)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dsi_display_put_mode(struct dsi_display *display,
|
||||
struct dsi_display_mode *mode)
|
||||
{
|
||||
@@ -5537,7 +5732,8 @@ int dsi_display_get_modes(struct dsi_display *display,
|
||||
struct dsi_dfps_capabilities dfps_caps;
|
||||
u32 num_dfps_rates, panel_mode_count, total_mode_count;
|
||||
u32 mode_idx, array_idx = 0;
|
||||
int i, rc = -EINVAL;
|
||||
struct dsi_dyn_clk_caps *dyn_clk_caps;
|
||||
int i, start, end, rc = -EINVAL;
|
||||
|
||||
if (!display || !out_modes) {
|
||||
pr_err("Invalid params\n");
|
||||
@@ -5569,9 +5765,9 @@ int dsi_display_get_modes(struct dsi_display *display,
|
||||
goto error;
|
||||
}
|
||||
|
||||
num_dfps_rates = !dfps_caps.dfps_support ? 1 :
|
||||
dfps_caps.max_refresh_rate -
|
||||
dfps_caps.min_refresh_rate + 1;
|
||||
dyn_clk_caps = &(display->panel->dyn_clk_caps);
|
||||
|
||||
num_dfps_rates = !dfps_caps.dfps_support ? 1 : dfps_caps.dfps_list_len;
|
||||
|
||||
panel_mode_count = display->panel->num_timing_nodes;
|
||||
|
||||
@@ -5609,14 +5805,14 @@ int dsi_display_get_modes(struct dsi_display *display,
|
||||
panel_mode.timing.dsi_transfer_time_us;
|
||||
}
|
||||
|
||||
if (display->ctrl_count > 1) { /* TODO: remove if */
|
||||
panel_mode.timing.h_active *= display->ctrl_count;
|
||||
panel_mode.timing.h_front_porch *= display->ctrl_count;
|
||||
panel_mode.timing.h_sync_width *= display->ctrl_count;
|
||||
panel_mode.timing.h_back_porch *= display->ctrl_count;
|
||||
panel_mode.timing.h_skew *= display->ctrl_count;
|
||||
panel_mode.pixel_clk_khz *= display->ctrl_count;
|
||||
}
|
||||
panel_mode.timing.h_active *= display->ctrl_count;
|
||||
panel_mode.timing.h_front_porch *= display->ctrl_count;
|
||||
panel_mode.timing.h_sync_width *= display->ctrl_count;
|
||||
panel_mode.timing.h_back_porch *= display->ctrl_count;
|
||||
panel_mode.timing.h_skew *= display->ctrl_count;
|
||||
panel_mode.pixel_clk_khz *= display->ctrl_count;
|
||||
|
||||
start = array_idx;
|
||||
|
||||
for (i = 0; i < num_dfps_rates; i++) {
|
||||
struct dsi_display_mode *sub_mode =
|
||||
@@ -5630,24 +5826,23 @@ int dsi_display_get_modes(struct dsi_display *display,
|
||||
}
|
||||
|
||||
memcpy(sub_mode, &panel_mode, sizeof(panel_mode));
|
||||
|
||||
if (dfps_caps.dfps_support) {
|
||||
curr_refresh_rate =
|
||||
sub_mode->timing.refresh_rate;
|
||||
sub_mode->timing.refresh_rate =
|
||||
dfps_caps.min_refresh_rate +
|
||||
(i % num_dfps_rates);
|
||||
|
||||
dsi_display_get_dfps_timing(display,
|
||||
sub_mode, curr_refresh_rate);
|
||||
|
||||
sub_mode->pixel_clk_khz =
|
||||
(DSI_H_TOTAL_DSC(&sub_mode->timing) *
|
||||
DSI_V_TOTAL(&sub_mode->timing) *
|
||||
sub_mode->timing.refresh_rate) / 1000;
|
||||
}
|
||||
array_idx++;
|
||||
|
||||
if (!dfps_caps.dfps_support)
|
||||
continue;
|
||||
|
||||
curr_refresh_rate = sub_mode->timing.refresh_rate;
|
||||
sub_mode->timing.refresh_rate = dfps_caps.dfps_list[i];
|
||||
|
||||
dsi_display_get_dfps_timing(display, sub_mode,
|
||||
curr_refresh_rate);
|
||||
}
|
||||
end = array_idx;
|
||||
/*
|
||||
* if dynamic clk switch is supported then update all the bit
|
||||
* clk rates.
|
||||
*/
|
||||
_dsi_display_populate_bit_clks(display, start, end, &array_idx);
|
||||
}
|
||||
|
||||
exit:
|
||||
@@ -5771,7 +5966,8 @@ int dsi_display_find_mode(struct dsi_display *display,
|
||||
if (cmp->timing.v_active == m->timing.v_active &&
|
||||
cmp->timing.h_active == m->timing.h_active &&
|
||||
cmp->timing.refresh_rate == m->timing.refresh_rate &&
|
||||
cmp->panel_mode == m->panel_mode) {
|
||||
cmp->panel_mode == m->panel_mode &&
|
||||
cmp->pixel_clk_khz == m->pixel_clk_khz) {
|
||||
*out_mode = m;
|
||||
rc = 0;
|
||||
break;
|
||||
@@ -5780,9 +5976,10 @@ int dsi_display_find_mode(struct dsi_display *display,
|
||||
mutex_unlock(&display->display_lock);
|
||||
|
||||
if (!*out_mode) {
|
||||
pr_err("[%s] failed to find mode for v_active %u h_active %u rate %u\n",
|
||||
pr_err("[%s] failed to find mode for v_active %u h_active %u fps %u pclk %u\n",
|
||||
display->name, cmp->timing.v_active,
|
||||
cmp->timing.h_active, cmp->timing.refresh_rate);
|
||||
cmp->timing.h_active, cmp->timing.refresh_rate,
|
||||
cmp->pixel_clk_khz);
|
||||
rc = -ENOENT;
|
||||
}
|
||||
|
||||
@@ -5790,7 +5987,7 @@ int dsi_display_find_mode(struct dsi_display *display,
|
||||
}
|
||||
|
||||
/**
|
||||
* dsi_display_validate_mode_vrr() - Validate if varaible refresh case.
|
||||
* dsi_display_validate_mode_change() - Validate if varaible refresh case.
|
||||
* @display: DSI display handle.
|
||||
* @cur_dsi_mode: Current DSI mode.
|
||||
* @mode: Mode value structure to be validated.
|
||||
@@ -5798,16 +5995,15 @@ int dsi_display_find_mode(struct dsi_display *display,
|
||||
* is change in fps but vactive and hactive are same.
|
||||
* Return: error code.
|
||||
*/
|
||||
int dsi_display_validate_mode_vrr(struct dsi_display *display,
|
||||
struct dsi_display_mode *cur_dsi_mode,
|
||||
struct dsi_display_mode *mode)
|
||||
int dsi_display_validate_mode_change(struct dsi_display *display,
|
||||
struct dsi_display_mode *cur_mode,
|
||||
struct dsi_display_mode *adj_mode)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dsi_display_mode adj_mode, cur_mode;
|
||||
struct dsi_dfps_capabilities dfps_caps;
|
||||
u32 curr_refresh_rate;
|
||||
struct dsi_dyn_clk_caps *dyn_clk_caps;
|
||||
|
||||
if (!display || !mode) {
|
||||
if (!display || !adj_mode) {
|
||||
pr_err("Invalid params\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -5819,65 +6015,43 @@ int dsi_display_validate_mode_vrr(struct dsi_display *display,
|
||||
|
||||
mutex_lock(&display->display_lock);
|
||||
|
||||
adj_mode = *mode;
|
||||
cur_mode = *cur_dsi_mode;
|
||||
|
||||
if ((cur_mode.timing.refresh_rate != adj_mode.timing.refresh_rate) &&
|
||||
(cur_mode.timing.v_active == adj_mode.timing.v_active) &&
|
||||
(cur_mode.timing.h_active == adj_mode.timing.h_active)) {
|
||||
|
||||
curr_refresh_rate = cur_mode.timing.refresh_rate;
|
||||
rc = dsi_panel_get_dfps_caps(display->panel, &dfps_caps);
|
||||
if (rc) {
|
||||
pr_err("[%s] failed to get dfps caps from panel\n",
|
||||
display->name);
|
||||
goto error;
|
||||
if ((cur_mode->timing.v_active == adj_mode->timing.v_active) &&
|
||||
(cur_mode->timing.h_active == adj_mode->timing.h_active)) {
|
||||
/* dfps change use case */
|
||||
if (cur_mode->timing.refresh_rate !=
|
||||
adj_mode->timing.refresh_rate) {
|
||||
dsi_panel_get_dfps_caps(display->panel, &dfps_caps);
|
||||
if (!dfps_caps.dfps_support) {
|
||||
pr_err("invalid mode dfps not supported\n");
|
||||
rc = -ENOTSUPP;
|
||||
goto error;
|
||||
}
|
||||
pr_debug("Mode switch is seamless variable refresh\n");
|
||||
adj_mode->dsi_mode_flags |= DSI_MODE_FLAG_VRR;
|
||||
SDE_EVT32(cur_mode->timing.refresh_rate,
|
||||
adj_mode->timing.refresh_rate,
|
||||
cur_mode->timing.h_front_porch,
|
||||
adj_mode->timing.h_front_porch);
|
||||
}
|
||||
|
||||
cur_mode.timing.refresh_rate =
|
||||
adj_mode.timing.refresh_rate;
|
||||
|
||||
rc = dsi_display_get_dfps_timing(display,
|
||||
&cur_mode, curr_refresh_rate);
|
||||
if (rc) {
|
||||
pr_err("[%s] seamless vrr not possible rc=%d\n",
|
||||
display->name, rc);
|
||||
goto error;
|
||||
/* dynamic clk change use case */
|
||||
if (cur_mode->pixel_clk_khz != adj_mode->pixel_clk_khz) {
|
||||
dyn_clk_caps = &(display->panel->dyn_clk_caps);
|
||||
if (!dyn_clk_caps->dyn_clk_support) {
|
||||
pr_err("dyn clk change not supported\n");
|
||||
rc = -ENOTSUPP;
|
||||
goto error;
|
||||
}
|
||||
if (adj_mode->dsi_mode_flags & DSI_MODE_FLAG_VRR) {
|
||||
pr_err("dfps and dyn clk not supported in same commit\n");
|
||||
rc = -ENOTSUPP;
|
||||
goto error;
|
||||
}
|
||||
pr_debug("dynamic clk change detected\n");
|
||||
adj_mode->dsi_mode_flags |= DSI_MODE_FLAG_DYN_CLK;
|
||||
SDE_EVT32(cur_mode->pixel_clk_khz,
|
||||
adj_mode->pixel_clk_khz);
|
||||
}
|
||||
switch (dfps_caps.type) {
|
||||
/*
|
||||
* Ignore any round off factors in porch calculation.
|
||||
* Worse case is set to 5.
|
||||
*/
|
||||
case DSI_DFPS_IMMEDIATE_VFP:
|
||||
if (abs(DSI_V_TOTAL(&cur_mode.timing) -
|
||||
DSI_V_TOTAL(&adj_mode.timing)) > 5)
|
||||
pr_err("Mismatch vfp fps:%d new:%d given:%d\n",
|
||||
adj_mode.timing.refresh_rate,
|
||||
cur_mode.timing.v_front_porch,
|
||||
adj_mode.timing.v_front_porch);
|
||||
break;
|
||||
|
||||
case DSI_DFPS_IMMEDIATE_HFP:
|
||||
if (abs(DSI_H_TOTAL_DSC(&cur_mode.timing) -
|
||||
DSI_H_TOTAL_DSC(&adj_mode.timing)) > 5)
|
||||
pr_err("Mismatch hfp fps:%d new:%d given:%d\n",
|
||||
adj_mode.timing.refresh_rate,
|
||||
cur_mode.timing.h_front_porch,
|
||||
adj_mode.timing.h_front_porch);
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("Unsupported DFPS mode %d\n",
|
||||
dfps_caps.type);
|
||||
rc = -ENOTSUPP;
|
||||
}
|
||||
|
||||
pr_debug("Mode switch is seamless variable refresh\n");
|
||||
mode->dsi_mode_flags |= DSI_MODE_FLAG_VRR;
|
||||
SDE_EVT32(curr_refresh_rate, adj_mode.timing.refresh_rate,
|
||||
cur_mode.timing.h_front_porch,
|
||||
adj_mode.timing.h_front_porch);
|
||||
}
|
||||
|
||||
error:
|
||||
|
Referência em uma nova issue
Block a user