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_MIN_FPS 20000
|
||||||
#define HDMI_TX_MAX_FPS 120000
|
#define HDMI_TX_MAX_FPS 120000
|
||||||
|
#define HDMI_KHZ_TO_HZ 1000
|
||||||
|
|
||||||
#define HDMI_TX_VERSION_403 0x40000003 /* msm8998 */
|
#define HDMI_TX_VERSION_403 0x40000003 /* msm8998 */
|
||||||
#define HDMI_GET_MSB(x) (x >> 8)
|
#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,
|
static int hdmi_tx_get_audio_edid_blk(struct platform_device *pdev,
|
||||||
struct msm_ext_disp_audio_edid_blk *blk);
|
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_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 = {
|
static struct mdss_hw hdmi_tx_hw = {
|
||||||
.hw_ndx = MDSS_HW_HDMI,
|
.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 dss_module_power *power_data = NULL;
|
||||||
struct mdss_panel_info *pinfo;
|
struct mdss_panel_info *pinfo;
|
||||||
|
u32 new_clk_rate = 0;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
if (!hdmi_ctrl) {
|
if (!hdmi_ctrl) {
|
||||||
DEV_ERR("%s: invalid input\n", __func__);
|
pr_err("invalid input\n");
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto end;
|
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];
|
power_data = &hdmi_ctrl->pdata.power_data[HDMI_TX_CORE_PM];
|
||||||
if (!power_data) {
|
if (!power_data) {
|
||||||
DEV_ERR("%s: Error: invalid power data\n", __func__);
|
pr_err("Error: invalid power data\n");
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (power_data->clk_config->rate == pinfo->clk_rate) {
|
new_clk_rate = hdmi_tx_setup_tmds_clk_rate(pinfo->clk_rate,
|
||||||
rc = -EINVAL;
|
pinfo->out_format, hdmi_ctrl->panel.dc_enable);
|
||||||
|
|
||||||
|
if (power_data->clk_config->rate == new_clk_rate)
|
||||||
goto end;
|
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:
|
end:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -1316,6 +1323,35 @@ end:
|
||||||
return ret;
|
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(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(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,
|
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);
|
hdmi_tx_sysfs_wta_s3d_mode);
|
||||||
static DEVICE_ATTR(5v, S_IWUSR, NULL, hdmi_tx_sysfs_wta_5v);
|
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(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[] = {
|
static struct attribute *hdmi_tx_fs_attrs[] = {
|
||||||
&dev_attr_connected.attr,
|
&dev_attr_connected.attr,
|
||||||
|
@ -1351,6 +1389,7 @@ static struct attribute *hdmi_tx_fs_attrs[] = {
|
||||||
&dev_attr_s3d_mode.attr,
|
&dev_attr_s3d_mode.attr,
|
||||||
&dev_attr_5v.attr,
|
&dev_attr_5v.attr,
|
||||||
&dev_attr_hdr_stream.attr,
|
&dev_attr_hdr_stream.attr,
|
||||||
|
&dev_attr_hdmi_ppm.attr,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
static struct attribute_group hdmi_tx_fs_attrs_group = {
|
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);
|
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)
|
static int hdmi_tx_evt_handle_register(struct hdmi_tx_ctrl *hdmi_ctrl)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
@ -3796,6 +3909,13 @@ static int hdmi_tx_evt_handle_deep_color(struct hdmi_tx_ctrl *hdmi_ctrl)
|
||||||
return 0;
|
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,
|
static int hdmi_tx_event_handler(struct mdss_panel_data *panel_data,
|
||||||
int event, void *arg)
|
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_PANEL_OFF] = hdmi_tx_evt_handle_panel_off;
|
||||||
handler[MDSS_EVENT_CLOSE] = hdmi_tx_evt_handle_close;
|
handler[MDSS_EVENT_CLOSE] = hdmi_tx_evt_handle_close;
|
||||||
handler[MDSS_EVENT_DEEP_COLOR] = hdmi_tx_evt_handle_deep_color;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4614,6 +4614,20 @@ static int mdss_fb_set_metadata(struct msm_fb_data_type *mfd,
|
||||||
return ret;
|
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,
|
static int mdss_fb_get_hw_caps(struct msm_fb_data_type *mfd,
|
||||||
struct mdss_hw_caps *caps)
|
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);
|
ret = mdss_mdp_set_cfg(mfd, &cfg);
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -255,6 +255,8 @@ struct mdss_intf_recovery {
|
||||||
* Argument provided is new panel timing.
|
* Argument provided is new panel timing.
|
||||||
* @MDSS_EVENT_DEEP_COLOR: Set deep color.
|
* @MDSS_EVENT_DEEP_COLOR: Set deep color.
|
||||||
* Argument provided is bits per pixel (8/10/12)
|
* 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 {
|
enum mdss_intf_events {
|
||||||
MDSS_EVENT_RESET = 1,
|
MDSS_EVENT_RESET = 1,
|
||||||
|
@ -287,6 +289,7 @@ enum mdss_intf_events {
|
||||||
MDSS_EVENT_PANEL_TIMING_SWITCH,
|
MDSS_EVENT_PANEL_TIMING_SWITCH,
|
||||||
MDSS_EVENT_DEEP_COLOR,
|
MDSS_EVENT_DEEP_COLOR,
|
||||||
MDSS_EVENT_DISABLE_PANEL,
|
MDSS_EVENT_DISABLE_PANEL,
|
||||||
|
MDSS_EVENT_UPDATE_PANEL_PPM,
|
||||||
MDSS_EVENT_MAX,
|
MDSS_EVENT_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,12 @@
|
||||||
#define MSMFB_MDP_SET_CFG _IOW(MDP_IOCTL_MAGIC, 130, \
|
#define MSMFB_MDP_SET_CFG _IOW(MDP_IOCTL_MAGIC, 130, \
|
||||||
struct mdp_set_cfg)
|
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
|
* To allow proper structure padding for 64bit/32bit target
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Add table
Reference in a new issue