Merge "drm/msm: Add PLL_DELTA property to HDMI connector"

This commit is contained in:
Linux Build Service Account 2017-05-01 23:57:09 -07:00 committed by Gerrit - the friendly Code Review server
commit 82fffc58bf
9 changed files with 286 additions and 6 deletions

View file

@ -401,12 +401,111 @@ static const struct file_operations edid_vendor_name_fops = {
.read = _sde_hdmi_edid_vendor_name_read, .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) static int _sde_hdmi_debugfs_init(struct sde_hdmi *display)
{ {
int rc = 0; int rc = 0;
struct dentry *dir, *dump_file, *edid_modes; struct dentry *dir, *dump_file, *edid_modes;
struct dentry *edid_vsdb_info, *edid_hdr_info, *edid_hfvsdb_info; 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); dir = debugfs_create_dir(display->name, NULL);
if (!dir) { if (!dir) {
@ -423,7 +522,19 @@ static int _sde_hdmi_debugfs_init(struct sde_hdmi *display)
&dump_info_fops); &dump_info_fops);
if (IS_ERR_OR_NULL(dump_file)) { if (IS_ERR_OR_NULL(dump_file)) {
rc = PTR_ERR(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); display->name, rc);
goto error_remove_dir; goto error_remove_dir;
} }
@ -1324,6 +1435,28 @@ int sde_hdmi_get_info(struct msm_display_info *info,
return rc; 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 sde_hdmi_get_num_of_displays(void)
{ {
u32 count = 0; u32 count = 0;

View file

@ -80,6 +80,7 @@ struct sde_hdmi_ctrl {
* @non_pluggable: If HDMI display is non pluggable * @non_pluggable: If HDMI display is non pluggable
* @num_of_modes: Number of modes supported by display if non pluggable. * @num_of_modes: Number of modes supported by display if non pluggable.
* @mode_list: Mode list if non pluggable. * @mode_list: Mode list if non pluggable.
* @mode: Current display mode.
* @connected: If HDMI display is connected. * @connected: If HDMI display is connected.
* @is_tpg_enabled: TPG state. * @is_tpg_enabled: TPG state.
* @hpd_work: HPD work structure. * @hpd_work: HPD work structure.
@ -103,6 +104,7 @@ struct sde_hdmi {
bool non_pluggable; bool non_pluggable;
u32 num_of_modes; u32 num_of_modes;
struct list_head mode_list; struct list_head mode_list;
struct drm_display_mode mode;
bool connected; bool connected;
bool is_tpg_enabled; 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, int sde_hdmi_get_info(struct msm_display_info *info,
void *display); 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 * sde_hdmi_bridge_init() - init sde hdmi bridge
* @hdmi: Handle to the hdmi. * @hdmi: Handle to the hdmi.
@ -453,5 +471,15 @@ static inline int sde_hdmi_get_info(struct msm_display_info *info,
{ {
return 0; 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 /*#else of CONFIG_DRM_SDE_HDMI*/
#endif /* _SDE_HDMI_H_ */ #endif /* _SDE_HDMI_H_ */

View file

@ -30,8 +30,6 @@
#define HDMI_AUDIO_INFO_FRAME_PACKET_VERSION 0x1 #define HDMI_AUDIO_INFO_FRAME_PACKET_VERSION 0x1
#define HDMI_AUDIO_INFO_FRAME_PACKET_LENGTH 0x0A #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 HDMI_ACR_N_MULTIPLIER 128
#define DEFAULT_AUDIO_SAMPLE_RATE_HZ 48000 #define DEFAULT_AUDIO_SAMPLE_RATE_HZ 48000

View file

@ -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); 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, static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge,
struct drm_display_mode *mode, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_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"); DRM_DEBUG("hdmi setup info frame\n");
} }
_sde_hdmi_bridge_setup_scrambler(hdmi, mode); _sde_hdmi_bridge_setup_scrambler(hdmi, mode);
_sde_hdmi_save_mode(hdmi, mode);
} }
static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = { static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = {

View file

@ -148,6 +148,7 @@ enum msm_mdp_conn_property {
CONNECTOR_PROP_DST_Y, CONNECTOR_PROP_DST_Y,
CONNECTOR_PROP_DST_W, CONNECTOR_PROP_DST_W,
CONNECTOR_PROP_DST_H, CONNECTOR_PROP_DST_H,
CONNECTOR_PROP_PLL_DELTA,
/* enum/bitmask properties */ /* enum/bitmask properties */
CONNECTOR_PROP_TOPOLOGY_NAME, CONNECTOR_PROP_TOPOLOGY_NAME,

View file

@ -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 * 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 * 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, void msm_property_install_range(struct msm_property_info *info,
const char *name, int flags, uint64_t min, uint64_t max, const char *name, int flags, uint64_t min, uint64_t max,
uint64_t init, uint32_t property_idx) 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); 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, void msm_property_install_rotation(struct msm_property_info *info,
unsigned int supported_rotations, uint32_t property_idx) unsigned int supported_rotations, uint32_t property_idx)
{ {

View file

@ -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 * 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 * 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, uint64_t init,
uint32_t property_idx); 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 * msm_property_install_rotation - install standard drm rotation property
* @info: Pointer to property info container struct * @info: Pointer to property info container struct

View file

@ -586,6 +586,10 @@ struct drm_connector *sde_connector_init(struct drm_device *dev,
msm_property_install_range(&c_conn->property_info, "RETIRE_FENCE", msm_property_install_range(&c_conn->property_info, "RETIRE_FENCE",
0x0, 0, INR_OPEN_MAX, 0, CONNECTOR_PROP_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 */ /* enum/bitmask properties */
msm_property_install_enum(&c_conn->property_info, "topology_name", msm_property_install_enum(&c_conn->property_info, "topology_name",
DRM_MODE_PROP_IMMUTABLE, 0, e_topology_name, DRM_MODE_PROP_IMMUTABLE, 0, e_topology_name,

View file

@ -598,6 +598,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
.get_modes = sde_hdmi_connector_get_modes, .get_modes = sde_hdmi_connector_get_modes,
.mode_valid = sde_hdmi_mode_valid, .mode_valid = sde_hdmi_mode_valid,
.get_info = sde_hdmi_get_info, .get_info = sde_hdmi_get_info,
.set_property = sde_hdmi_set_property,
}; };
struct msm_display_info info = {0}; struct msm_display_info info = {0};
struct drm_encoder *encoder; struct drm_encoder *encoder;