Merge "msm: mdss: add support to change HDMI PLL PPM"

This commit is contained in:
Linux Build Service Account 2016-11-28 14:56:40 -08:00 committed by Gerrit - the friendly Code Review server
commit cab5267d39
4 changed files with 162 additions and 8 deletions

View file

@ -71,6 +71,7 @@
#define HDMI_TX_MIN_FPS 20000
#define HDMI_TX_MAX_FPS 120000
#define HDMI_KHZ_TO_HZ 1000
#define HDMI_TX_VERSION_403 0x40000003 /* msm8998 */
#define HDMI_GET_MSB(x) (x >> 8)
@ -114,6 +115,7 @@ static int hdmi_tx_audio_info_setup(struct platform_device *pdev,
static int hdmi_tx_get_audio_edid_blk(struct platform_device *pdev,
struct msm_ext_disp_audio_edid_blk *blk);
static int hdmi_tx_get_cable_status(struct platform_device *pdev, u32 vote);
static int hdmi_tx_update_ppm(struct hdmi_tx_ctrl *hdmi_ctrl, s32 ppm);
static struct mdss_hw hdmi_tx_hw = {
.hw_ndx = MDSS_HW_HDMI,
@ -648,10 +650,11 @@ static int hdmi_tx_update_pixel_clk(struct hdmi_tx_ctrl *hdmi_ctrl)
{
struct dss_module_power *power_data = NULL;
struct mdss_panel_info *pinfo;
u32 new_clk_rate = 0;
int rc = 0;
if (!hdmi_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
pr_err("invalid input\n");
rc = -EINVAL;
goto end;
}
@ -660,21 +663,25 @@ static int hdmi_tx_update_pixel_clk(struct hdmi_tx_ctrl *hdmi_ctrl)
power_data = &hdmi_ctrl->pdata.power_data[HDMI_TX_CORE_PM];
if (!power_data) {
DEV_ERR("%s: Error: invalid power data\n", __func__);
pr_err("Error: invalid power data\n");
rc = -EINVAL;
goto end;
}
if (power_data->clk_config->rate == pinfo->clk_rate) {
rc = -EINVAL;
new_clk_rate = hdmi_tx_setup_tmds_clk_rate(pinfo->clk_rate,
pinfo->out_format, hdmi_ctrl->panel.dc_enable);
if (power_data->clk_config->rate == new_clk_rate)
goto end;
}
power_data->clk_config->rate = pinfo->clk_rate;
power_data->clk_config->rate = new_clk_rate;
DEV_DBG("%s: rate %ld\n", __func__, power_data->clk_config->rate);
pr_debug("rate %ld\n", power_data->clk_config->rate);
msm_dss_clk_set_rate(power_data->clk_config, power_data->num_clk);
rc = msm_dss_clk_set_rate(power_data->clk_config, power_data->num_clk);
if (rc < 0)
pr_err("failed to set clock rate %lu\n",
power_data->clk_config->rate);
end:
return rc;
}
@ -1316,6 +1323,35 @@ end:
return ret;
}
static ssize_t hdmi_tx_sysfs_wta_hdmi_ppm(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int ret, ppm;
struct hdmi_tx_ctrl *hdmi_ctrl
= hdmi_tx_get_drvdata_from_sysfs_dev(dev);
if (!hdmi_ctrl) {
pr_err("invalid input\n");
return -EINVAL;
}
mutex_lock(&hdmi_ctrl->tx_lock);
ret = kstrtoint(buf, 10, &ppm);
if (ret) {
pr_err("kstrtoint failed. rc=%d\n", ret);
goto end;
}
hdmi_tx_update_ppm(hdmi_ctrl, ppm);
ret = strnlen(buf, PAGE_SIZE);
pr_debug("write ppm %d\n", ppm);
end:
mutex_unlock(&hdmi_ctrl->tx_lock);
return ret;
}
static DEVICE_ATTR(connected, S_IRUGO, hdmi_tx_sysfs_rda_connected, NULL);
static DEVICE_ATTR(hot_plug, S_IWUSR, NULL, hdmi_tx_sysfs_wta_hot_plug);
static DEVICE_ATTR(sim_mode, S_IRUGO | S_IWUSR, hdmi_tx_sysfs_rda_sim_mode,
@ -1336,6 +1372,8 @@ static DEVICE_ATTR(s3d_mode, S_IRUGO | S_IWUSR, hdmi_tx_sysfs_rda_s3d_mode,
hdmi_tx_sysfs_wta_s3d_mode);
static DEVICE_ATTR(5v, S_IWUSR, NULL, hdmi_tx_sysfs_wta_5v);
static DEVICE_ATTR(hdr_stream, S_IWUSR, NULL, hdmi_tx_sysfs_wta_hdr_stream);
static DEVICE_ATTR(hdmi_ppm, S_IRUGO | S_IWUSR, NULL,
hdmi_tx_sysfs_wta_hdmi_ppm);
static struct attribute *hdmi_tx_fs_attrs[] = {
&dev_attr_connected.attr,
@ -1351,6 +1389,7 @@ static struct attribute *hdmi_tx_fs_attrs[] = {
&dev_attr_s3d_mode.attr,
&dev_attr_5v.attr,
&dev_attr_hdr_stream.attr,
&dev_attr_hdmi_ppm.attr,
NULL,
};
static struct attribute_group hdmi_tx_fs_attrs_group = {
@ -3566,6 +3605,80 @@ static void hdmi_tx_fps_work(struct work_struct *work)
hdmi_tx_update_fps(hdmi_ctrl);
}
static u64 hdmi_tx_clip_valid_pclk(struct hdmi_tx_ctrl *hdmi_ctrl, u64 pclk_in)
{
struct msm_hdmi_mode_timing_info timing = {0};
u32 pclk_delta, pclk;
u64 pclk_clip = pclk_in;
hdmi_get_supported_mode(&timing,
&hdmi_ctrl->ds_data, hdmi_ctrl->vic);
/* as per standard, 0.5% of deviation is allowed */
pclk = timing.pixel_freq * HDMI_KHZ_TO_HZ;
pclk_delta = pclk * 5 / 1000;
if (pclk_in < (pclk - pclk_delta))
pclk_clip = pclk - pclk_delta;
else if (pclk_in > (pclk + pclk_delta))
pclk_clip = pclk + pclk_delta;
if (pclk_in != pclk_clip)
pr_debug("the deviation is too big, so clip pclk from %lld to %lld\n",
pclk_in, pclk_clip);
return pclk_clip;
}
/**
* hdmi_tx_update_ppm() - Update the HDMI pixel clock as per the input ppm
*
* @ppm: ppm is parts per million multiplied by 1000.
* return: 0 on success, non-zero in case of failure.
*/
static int hdmi_tx_update_ppm(struct hdmi_tx_ctrl *hdmi_ctrl, s32 ppm)
{
struct mdss_panel_info *pinfo = NULL;
u64 cur_pclk, dst_pclk;
u64 clip_pclk;
int rc = 0;
if (!hdmi_ctrl) {
pr_err("invalid hdmi_ctrl\n");
return -EINVAL;
}
pinfo = &hdmi_ctrl->panel_data.panel_info;
/* only available in case HDMI is up */
if (!hdmi_tx_is_panel_on(hdmi_ctrl)) {
pr_err("hdmi is not on\n");
return -EINVAL;
}
/* get current pclk */
cur_pclk = pinfo->clk_rate;
/* get desired pclk */
dst_pclk = cur_pclk * (1000000000 + ppm);
do_div(dst_pclk, 1000000000);
clip_pclk = hdmi_tx_clip_valid_pclk(hdmi_ctrl, dst_pclk);
/* update pclk */
if (clip_pclk != cur_pclk) {
pr_debug("pclk changes from %llu to %llu when ppm is %d\n",
cur_pclk, clip_pclk, ppm);
pinfo->clk_rate = clip_pclk;
rc = hdmi_tx_update_pixel_clk(hdmi_ctrl);
if (rc < 0) {
pr_err("PPM update failed, reset clock rate\n");
pinfo->clk_rate = cur_pclk;
}
}
return rc;
}
static int hdmi_tx_evt_handle_register(struct hdmi_tx_ctrl *hdmi_ctrl)
{
int rc = 0;
@ -3796,6 +3909,13 @@ static int hdmi_tx_evt_handle_deep_color(struct hdmi_tx_ctrl *hdmi_ctrl)
return 0;
}
static int hdmi_tx_evt_handle_hdmi_ppm(struct hdmi_tx_ctrl *hdmi_ctrl)
{
s32 ppm = (s32) (unsigned long)hdmi_ctrl->evt_arg;
return hdmi_tx_update_ppm(hdmi_ctrl, ppm);
}
static int hdmi_tx_event_handler(struct mdss_panel_data *panel_data,
int event, void *arg)
{
@ -4497,6 +4617,7 @@ static int hdmi_tx_init_event_handler(struct hdmi_tx_ctrl *hdmi_ctrl)
handler[MDSS_EVENT_PANEL_OFF] = hdmi_tx_evt_handle_panel_off;
handler[MDSS_EVENT_CLOSE] = hdmi_tx_evt_handle_close;
handler[MDSS_EVENT_DEEP_COLOR] = hdmi_tx_evt_handle_deep_color;
handler[MDSS_EVENT_UPDATE_PANEL_PPM] = hdmi_tx_evt_handle_hdmi_ppm;
return 0;
}

View file

@ -4614,6 +4614,20 @@ static int mdss_fb_set_metadata(struct msm_fb_data_type *mfd,
return ret;
}
static int mdss_fb_set_panel_ppm(struct msm_fb_data_type *mfd, s32 ppm)
{
struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
int ret = 0;
if (!ctl)
return -EPERM;
ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_UPDATE_PANEL_PPM,
(void *) (unsigned long) ppm,
CTL_INTF_EVENT_FLAG_DEFAULT);
return ret;
}
static int mdss_fb_get_hw_caps(struct msm_fb_data_type *mfd,
struct mdss_hw_caps *caps)
{
@ -5160,6 +5174,16 @@ static int mdss_mdp_overlay_ioctl_handler(struct msm_fb_data_type *mfd,
}
ret = mdss_mdp_set_cfg(mfd, &cfg);
break;
case MSMFB_MDP_SET_PANEL_PPM:
ret = copy_from_user(&val, argp, sizeof(val));
if (ret) {
pr_err("copy failed MSMFB_MDP_SET_PANEL_PPM ret %d\n",
ret);
ret = -EFAULT;
break;
}
ret = mdss_fb_set_panel_ppm(mfd, val);
break;
default:
break;

View file

@ -255,6 +255,8 @@ struct mdss_intf_recovery {
* Argument provided is new panel timing.
* @MDSS_EVENT_DEEP_COLOR: Set deep color.
* Argument provided is bits per pixel (8/10/12)
* @MDSS_EVENT_UPDATE_PANEL_PPM: update pixel clock by input PPM.
* Argument provided is parts per million.
*/
enum mdss_intf_events {
MDSS_EVENT_RESET = 1,
@ -287,6 +289,7 @@ enum mdss_intf_events {
MDSS_EVENT_PANEL_TIMING_SWITCH,
MDSS_EVENT_DEEP_COLOR,
MDSS_EVENT_DISABLE_PANEL,
MDSS_EVENT_UPDATE_PANEL_PPM,
MDSS_EVENT_MAX,
};

View file

@ -30,6 +30,12 @@
#define MSMFB_MDP_SET_CFG _IOW(MDP_IOCTL_MAGIC, 130, \
struct mdp_set_cfg)
/*
* Ioctl for setting the PLL PPM.
* PLL PPM is passed by the user space using this IOCTL.
*/
#define MSMFB_MDP_SET_PANEL_PPM _IOW(MDP_IOCTL_MAGIC, 131, int)
/*
* To allow proper structure padding for 64bit/32bit target
*/