Merge "drm/msm: Add PLL_DELTA property to HDMI connector"
This commit is contained in:
commit
82fffc58bf
9 changed files with 286 additions and 6 deletions
|
@ -401,12 +401,111 @@ static const struct file_operations edid_vendor_name_fops = {
|
|||
.read = _sde_hdmi_edid_vendor_name_read,
|
||||
};
|
||||
|
||||
static u64 _sde_hdmi_clip_valid_pclk(struct drm_display_mode *mode, u64 pclk_in)
|
||||
{
|
||||
u32 pclk_delta, pclk;
|
||||
u64 pclk_clip = pclk_in;
|
||||
|
||||
/* as per standard, 0.5% of deviation is allowed */
|
||||
pclk = mode->clock * 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_warn("clip pclk from %lld to %lld\n", pclk_in, pclk_clip);
|
||||
|
||||
return pclk_clip;
|
||||
}
|
||||
|
||||
/**
|
||||
* _sde_hdmi_update_pll_delta() - Update the HDMI pixel clock as per input ppm
|
||||
*
|
||||
* @ppm: ppm is parts per million multiplied by 1000.
|
||||
* return: 0 on success, non-zero in case of failure.
|
||||
*
|
||||
* The input ppm will be clipped if it's more than or less than 5% of the TMDS
|
||||
* clock rate defined by HDMI spec.
|
||||
*/
|
||||
static int _sde_hdmi_update_pll_delta(struct sde_hdmi *display, s32 ppm)
|
||||
{
|
||||
struct hdmi *hdmi = display->ctrl.ctrl;
|
||||
struct drm_display_mode *current_mode = &display->mode;
|
||||
u64 cur_pclk, dst_pclk;
|
||||
u64 clip_pclk;
|
||||
int rc = 0;
|
||||
|
||||
if (!hdmi->power_on || !display->connected) {
|
||||
SDE_ERROR("HDMI display is not ready\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* get current pclk */
|
||||
cur_pclk = hdmi->pixclock;
|
||||
/* get desired pclk */
|
||||
dst_pclk = cur_pclk * (1000000000 + ppm);
|
||||
do_div(dst_pclk, 1000000000);
|
||||
|
||||
clip_pclk = _sde_hdmi_clip_valid_pclk(current_mode, dst_pclk);
|
||||
|
||||
/* update pclk */
|
||||
if (clip_pclk != cur_pclk) {
|
||||
SDE_DEBUG("PCLK changes from %llu to %llu when delta is %d\n",
|
||||
cur_pclk, clip_pclk, ppm);
|
||||
|
||||
rc = clk_set_rate(hdmi->pwr_clks[0], clip_pclk);
|
||||
if (rc < 0) {
|
||||
SDE_ERROR("PLL update failed, reset clock rate\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
hdmi->pixclock = clip_pclk;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t _sde_hdmi_debugfs_pll_delta_write(struct file *file,
|
||||
const char __user *user_buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct sde_hdmi *display = file->private_data;
|
||||
char buf[10];
|
||||
int ppm = 0;
|
||||
|
||||
if (!display)
|
||||
return -ENODEV;
|
||||
|
||||
if (count >= sizeof(buf))
|
||||
return -EFAULT;
|
||||
|
||||
if (copy_from_user(buf, user_buf, count))
|
||||
return -EFAULT;
|
||||
|
||||
buf[count] = 0; /* end of string */
|
||||
|
||||
if (kstrtoint(buf, 0, &ppm))
|
||||
return -EFAULT;
|
||||
|
||||
if (ppm)
|
||||
_sde_hdmi_update_pll_delta(display, ppm);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations pll_delta_fops = {
|
||||
.open = simple_open,
|
||||
.write = _sde_hdmi_debugfs_pll_delta_write,
|
||||
};
|
||||
|
||||
static int _sde_hdmi_debugfs_init(struct sde_hdmi *display)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dentry *dir, *dump_file, *edid_modes;
|
||||
struct dentry *edid_vsdb_info, *edid_hdr_info, *edid_hfvsdb_info;
|
||||
struct dentry *edid_vcdb_info, *edid_vendor_name;
|
||||
struct dentry *edid_vcdb_info, *edid_vendor_name, *pll_file;
|
||||
|
||||
dir = debugfs_create_dir(display->name, NULL);
|
||||
if (!dir) {
|
||||
|
@ -423,7 +522,19 @@ static int _sde_hdmi_debugfs_init(struct sde_hdmi *display)
|
|||
&dump_info_fops);
|
||||
if (IS_ERR_OR_NULL(dump_file)) {
|
||||
rc = PTR_ERR(dump_file);
|
||||
SDE_ERROR("[%s]debugfs create file failed, rc=%d\n",
|
||||
SDE_ERROR("[%s]debugfs create dump_info file failed, rc=%d\n",
|
||||
display->name, rc);
|
||||
goto error_remove_dir;
|
||||
}
|
||||
|
||||
pll_file = debugfs_create_file("pll_delta",
|
||||
0644,
|
||||
dir,
|
||||
display,
|
||||
&pll_delta_fops);
|
||||
if (IS_ERR_OR_NULL(pll_file)) {
|
||||
rc = PTR_ERR(pll_file);
|
||||
SDE_ERROR("[%s]debugfs create pll_delta file failed, rc=%d\n",
|
||||
display->name, rc);
|
||||
goto error_remove_dir;
|
||||
}
|
||||
|
@ -1324,6 +1435,28 @@ int sde_hdmi_get_info(struct msm_display_info *info,
|
|||
return rc;
|
||||
}
|
||||
|
||||
int sde_hdmi_set_property(struct drm_connector *connector,
|
||||
struct drm_connector_state *state,
|
||||
int property_index,
|
||||
uint64_t value,
|
||||
void *display)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!connector || !display) {
|
||||
SDE_ERROR("connector=%pK or display=%pK is NULL\n",
|
||||
connector, display);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDE_DEBUG("\n");
|
||||
|
||||
if (property_index == CONNECTOR_PROP_PLL_DELTA)
|
||||
rc = _sde_hdmi_update_pll_delta(display, value);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
u32 sde_hdmi_get_num_of_displays(void)
|
||||
{
|
||||
u32 count = 0;
|
||||
|
|
|
@ -80,6 +80,7 @@ struct sde_hdmi_ctrl {
|
|||
* @non_pluggable: If HDMI display is non pluggable
|
||||
* @num_of_modes: Number of modes supported by display if non pluggable.
|
||||
* @mode_list: Mode list if non pluggable.
|
||||
* @mode: Current display mode.
|
||||
* @connected: If HDMI display is connected.
|
||||
* @is_tpg_enabled: TPG state.
|
||||
* @hpd_work: HPD work structure.
|
||||
|
@ -103,6 +104,7 @@ struct sde_hdmi {
|
|||
bool non_pluggable;
|
||||
u32 num_of_modes;
|
||||
struct list_head mode_list;
|
||||
struct drm_display_mode mode;
|
||||
bool connected;
|
||||
bool is_tpg_enabled;
|
||||
|
||||
|
@ -269,6 +271,22 @@ int sde_hdmi_drm_deinit(struct sde_hdmi *display);
|
|||
int sde_hdmi_get_info(struct msm_display_info *info,
|
||||
void *display);
|
||||
|
||||
/**
|
||||
* sde_hdmi_set_property() - set the connector properties
|
||||
* @connector: Handle to the connector.
|
||||
* @state: Handle to the connector state.
|
||||
* @property_index: property index.
|
||||
* @value: property value.
|
||||
* @display: Handle to the display.
|
||||
*
|
||||
* Return: error code.
|
||||
*/
|
||||
int sde_hdmi_set_property(struct drm_connector *connector,
|
||||
struct drm_connector_state *state,
|
||||
int property_index,
|
||||
uint64_t value,
|
||||
void *display);
|
||||
|
||||
/**
|
||||
* sde_hdmi_bridge_init() - init sde hdmi bridge
|
||||
* @hdmi: Handle to the hdmi.
|
||||
|
@ -453,5 +471,15 @@ static inline int sde_hdmi_get_info(struct msm_display_info *info,
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int sde_hdmi_set_property(struct drm_connector *connector,
|
||||
struct drm_connector_state *state,
|
||||
int property_index,
|
||||
uint64_t value,
|
||||
void *display)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /*#else of CONFIG_DRM_SDE_HDMI*/
|
||||
#endif /* _SDE_HDMI_H_ */
|
||||
|
|
|
@ -30,8 +30,6 @@
|
|||
#define HDMI_AUDIO_INFO_FRAME_PACKET_VERSION 0x1
|
||||
#define HDMI_AUDIO_INFO_FRAME_PACKET_LENGTH 0x0A
|
||||
|
||||
#define HDMI_KHZ_TO_HZ 1000
|
||||
#define HDMI_MHZ_TO_HZ 1000000
|
||||
#define HDMI_ACR_N_MULTIPLIER 128
|
||||
#define DEFAULT_AUDIO_SAMPLE_RATE_HZ 48000
|
||||
|
||||
|
|
|
@ -568,6 +568,15 @@ static void _sde_hdmi_bridge_set_spd_infoframe(struct hdmi *hdmi,
|
|||
hdmi_write(hdmi, REG_HDMI_GEN_PKT_CTRL, packet_control);
|
||||
}
|
||||
|
||||
static inline void _sde_hdmi_save_mode(struct hdmi *hdmi,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct sde_connector *c_conn = to_sde_connector(hdmi->connector);
|
||||
struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display;
|
||||
|
||||
drm_mode_copy(&display->mode, mode);
|
||||
}
|
||||
|
||||
static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
|
@ -640,6 +649,8 @@ static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge,
|
|||
DRM_DEBUG("hdmi setup info frame\n");
|
||||
}
|
||||
_sde_hdmi_bridge_setup_scrambler(hdmi, mode);
|
||||
|
||||
_sde_hdmi_save_mode(hdmi, mode);
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = {
|
||||
|
|
|
@ -148,6 +148,7 @@ enum msm_mdp_conn_property {
|
|||
CONNECTOR_PROP_DST_Y,
|
||||
CONNECTOR_PROP_DST_W,
|
||||
CONNECTOR_PROP_DST_H,
|
||||
CONNECTOR_PROP_PLL_DELTA,
|
||||
|
||||
/* enum/bitmask properties */
|
||||
CONNECTOR_PROP_TOPOLOGY_NAME,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
|
@ -183,6 +183,55 @@ static void _msm_property_install_integer(struct msm_property_info *info,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* _msm_property_install_integer - install signed drm range property
|
||||
* @info: Pointer to property info container struct
|
||||
* @name: Property name
|
||||
* @flags: Other property type flags, e.g. DRM_MODE_PROP_IMMUTABLE
|
||||
* @min: Min property value
|
||||
* @max: Max property value
|
||||
* @init: Default Property value
|
||||
* @property_idx: Property index
|
||||
* @force_dirty: Whether or not to filter 'dirty' status on unchanged values
|
||||
*/
|
||||
static void _msm_property_install_signed_integer(struct msm_property_info *info,
|
||||
const char *name, int flags, int64_t min, int64_t max,
|
||||
int64_t init, uint32_t property_idx, bool force_dirty)
|
||||
{
|
||||
struct drm_property **prop;
|
||||
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
++info->install_request;
|
||||
|
||||
if (!name || (property_idx >= info->property_count)) {
|
||||
DRM_ERROR("invalid argument(s), %s\n", name ? name : "null");
|
||||
} else {
|
||||
prop = &info->property_array[property_idx];
|
||||
/*
|
||||
* Properties need to be attached to each drm object that
|
||||
* uses them, but only need to be created once
|
||||
*/
|
||||
if (*prop == 0) {
|
||||
*prop = drm_property_create_signed_range(info->dev,
|
||||
flags, name, min, max);
|
||||
if (*prop == 0)
|
||||
DRM_ERROR("create %s property failed\n", name);
|
||||
}
|
||||
|
||||
/* save init value for later */
|
||||
info->property_data[property_idx].default_value = I642U64(init);
|
||||
info->property_data[property_idx].force_dirty = force_dirty;
|
||||
|
||||
/* always attach property, if created */
|
||||
if (*prop) {
|
||||
drm_object_attach_property(info->base, *prop, init);
|
||||
++info->install_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void msm_property_install_range(struct msm_property_info *info,
|
||||
const char *name, int flags, uint64_t min, uint64_t max,
|
||||
uint64_t init, uint32_t property_idx)
|
||||
|
@ -199,6 +248,22 @@ void msm_property_install_volatile_range(struct msm_property_info *info,
|
|||
min, max, init, property_idx, true);
|
||||
}
|
||||
|
||||
void msm_property_install_signed_range(struct msm_property_info *info,
|
||||
const char *name, int flags, int64_t min, int64_t max,
|
||||
int64_t init, uint32_t property_idx)
|
||||
{
|
||||
_msm_property_install_signed_integer(info, name, flags,
|
||||
min, max, init, property_idx, false);
|
||||
}
|
||||
|
||||
void msm_property_install_volatile_signed_range(struct msm_property_info *info,
|
||||
const char *name, int flags, int64_t min, int64_t max,
|
||||
int64_t init, uint32_t property_idx)
|
||||
{
|
||||
_msm_property_install_signed_integer(info, name, flags,
|
||||
min, max, init, property_idx, true);
|
||||
}
|
||||
|
||||
void msm_property_install_rotation(struct msm_property_info *info,
|
||||
unsigned int supported_rotations, uint32_t property_idx)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
|
@ -207,6 +207,45 @@ void msm_property_install_volatile_range(struct msm_property_info *info,
|
|||
uint64_t init,
|
||||
uint32_t property_idx);
|
||||
|
||||
/**
|
||||
* msm_property_install_signed_range - install signed drm range property
|
||||
* @info: Pointer to property info container struct
|
||||
* @name: Property name
|
||||
* @flags: Other property type flags, e.g. DRM_MODE_PROP_IMMUTABLE
|
||||
* @min: Min property value
|
||||
* @max: Max property value
|
||||
* @init: Default Property value
|
||||
* @property_idx: Property index
|
||||
*/
|
||||
void msm_property_install_signed_range(struct msm_property_info *info,
|
||||
const char *name,
|
||||
int flags,
|
||||
int64_t min,
|
||||
int64_t max,
|
||||
int64_t init,
|
||||
uint32_t property_idx);
|
||||
|
||||
/**
|
||||
* msm_property_install_volatile_signed_range - install signed range property
|
||||
* This function is similar to msm_property_install_range, but assumes
|
||||
* that the property is meant for holding user pointers or descriptors
|
||||
* that may reference volatile data without having an updated value.
|
||||
* @info: Pointer to property info container struct
|
||||
* @name: Property name
|
||||
* @flags: Other property type flags, e.g. DRM_MODE_PROP_IMMUTABLE
|
||||
* @min: Min property value
|
||||
* @max: Max property value
|
||||
* @init: Default Property value
|
||||
* @property_idx: Property index
|
||||
*/
|
||||
void msm_property_install_volatile_signed_range(struct msm_property_info *info,
|
||||
const char *name,
|
||||
int flags,
|
||||
int64_t min,
|
||||
int64_t max,
|
||||
int64_t init,
|
||||
uint32_t property_idx);
|
||||
|
||||
/**
|
||||
* msm_property_install_rotation - install standard drm rotation property
|
||||
* @info: Pointer to property info container struct
|
||||
|
|
|
@ -586,6 +586,10 @@ struct drm_connector *sde_connector_init(struct drm_device *dev,
|
|||
msm_property_install_range(&c_conn->property_info, "RETIRE_FENCE",
|
||||
0x0, 0, INR_OPEN_MAX, 0, CONNECTOR_PROP_RETIRE_FENCE);
|
||||
|
||||
msm_property_install_volatile_signed_range(&c_conn->property_info,
|
||||
"PLL_DELTA", 0x0, INT_MIN, INT_MAX, 0,
|
||||
CONNECTOR_PROP_PLL_DELTA);
|
||||
|
||||
/* enum/bitmask properties */
|
||||
msm_property_install_enum(&c_conn->property_info, "topology_name",
|
||||
DRM_MODE_PROP_IMMUTABLE, 0, e_topology_name,
|
||||
|
|
|
@ -598,6 +598,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
|
|||
.get_modes = sde_hdmi_connector_get_modes,
|
||||
.mode_valid = sde_hdmi_mode_valid,
|
||||
.get_info = sde_hdmi_get_info,
|
||||
.set_property = sde_hdmi_set_property,
|
||||
};
|
||||
struct msm_display_info info = {0};
|
||||
struct drm_encoder *encoder;
|
||||
|
|
Loading…
Add table
Reference in a new issue