Merge "msm: mdss: add support to change HDMI PLL PPM"
This commit is contained in:
commit
cab5267d39
4 changed files with 162 additions and 8 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
Loading…
Add table
Reference in a new issue