replace common qcom sources with samsung ones

This commit is contained in:
SaschaNes
2025-08-12 22:13:00 +02:00
parent ba24dcded9
commit 6f7753de11
5682 changed files with 2450203 additions and 103634 deletions

View File

@@ -43,6 +43,7 @@ enum dp_altmode_pin_assignment {
DPAM_HPD_F,
};
#if !defined(CONFIG_SECDP)
static int dp_altmode_set_usb_dp_mode(struct dp_altmode_private *altmode)
{
int rc = 0;
@@ -171,7 +172,7 @@ static int dp_altmode_notify(void *priv, void *data, size_t len)
if (altmode->dp_altmode.base.multi_func)
altmode->lanes = 2;
DP_DEBUG("Connected=%d, lanes=%d\n",altmode->connected,altmode->lanes);
DP_DEBUG("Connected=%d, lanes=%d\n", altmode->connected, altmode->lanes);
switch (orientation) {
case 0:
@@ -228,6 +229,7 @@ static void dp_altmode_register(void *priv)
else
DP_DEBUG("success\n");
}
#endif
static int dp_altmode_simulate_connect(struct dp_hpd *dp_hpd, bool hpd)
{
@@ -273,7 +275,9 @@ static int dp_altmode_simulate_attention(struct dp_hpd *dp_hpd, int vdo)
struct dp_hpd *dp_altmode_get(struct device *dev, struct dp_hpd_cb *cb)
{
#if !defined(CONFIG_SECDP)
int rc = 0;
#endif
struct dp_altmode_private *altmode;
struct dp_altmode *dp_altmode;
@@ -294,18 +298,22 @@ struct dp_hpd *dp_altmode_get(struct device *dev, struct dp_hpd_cb *cb)
dp_altmode->base.simulate_connect = dp_altmode_simulate_connect;
dp_altmode->base.simulate_attention = dp_altmode_simulate_attention;
#if !defined(CONFIG_SECDP)
rc = altmode_register_notifier(dev, dp_altmode_register, altmode);
if (rc < 0) {
DP_ERR("altmode probe notifier registration failed: %d\n", rc);
goto error;
}
#endif
DP_DEBUG("success\n");
return &dp_altmode->base;
#if !defined(CONFIG_SECDP)
error:
kfree(altmode);
return ERR_PTR(rc);
#endif
}
void dp_altmode_put(struct dp_hpd *dp_hpd)
@@ -320,8 +328,10 @@ void dp_altmode_put(struct dp_hpd *dp_hpd)
altmode = container_of(dp_altmode, struct dp_altmode_private,
dp_altmode);
#if !defined(CONFIG_SECDP)
altmode_deregister_client(altmode->amclient);
altmode_deregister_notifier(altmode->dev, altmode);
#endif
kfree(altmode);
}

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
*/
@@ -23,6 +23,20 @@
#include "dp_panel.h"
#include "dp_debug.h"
#if defined(CONFIG_SECDP)
#include "secdp.h"
#if defined(CONFIG_SECDP_BIGDATA)
#include <linux/secdp_bigdata.h>
#endif
#if defined(CONFIG_SECDP_SWITCH)
#include <linux/switch.h>
static struct switch_dev switch_secdp_audio = {
.name = "ch_hdmi_audio",
};
#endif
#endif/*CONFIG_SECDP*/
struct dp_audio_private {
struct platform_device *ext_pdev;
struct platform_device *pdev;
@@ -409,17 +423,16 @@ static int dp_audio_info_setup(struct platform_device *pdev,
return rc;
}
if (!audio->panel || !audio->panel->get_panel_on) {
DP_ERR("invalid panel data\n");
rc = -EINVAL;
return rc;
}
if (audio->dp_audio.tui_active) {
DP_DEBUG("TUI session active\n");
return 0;
}
#if defined(CONFIG_SECDP_BIGDATA)
secdp_bigdata_save_item(BD_AUD_CH, params->num_of_channels);
secdp_bigdata_save_item(BD_AUD_FREQ, params->sample_rate_hz);
#endif
mutex_lock(&audio->ops_lock);
audio->channels = params->num_of_channels;
@@ -443,6 +456,38 @@ static int dp_audio_info_setup(struct platform_device *pdev,
return rc;
}
#if defined(CONFIG_SECDP)
static void secdp_audio_use_one_sampling_freq(u8 *adb, int size)
{
const int one_adb_size = 3;
int adb_count;
if (size <= 0)
return;
adb_count = size / one_adb_size;
while (adb_count > 0) {
if (adb[1] & BIT(2))
adb[1] = BIT(2); /* 48kHz */
else if (adb[1] & BIT(1))
adb[1] = BIT(1); /* 44.1kHz */
else if (adb[1] & BIT(0))
adb[1] = BIT(0); /* 32kHz */
else if (adb[1] & BIT(3))
adb[1] = BIT(3); /* 88kHz */
else if (adb[1] & BIT(4))
adb[1] = BIT(4); /* 96kHz */
else if (adb[1] & BIT(5))
adb[1] = BIT(5); /* 176kHz */
else if (adb[1] & BIT(6))
adb[1] = BIT(6); /* 192kHz */
adb += one_adb_size;
adb_count--;
}
}
#endif
static int dp_audio_get_edid_blk(struct platform_device *pdev,
struct msm_ext_disp_audio_edid_blk *blk)
{
@@ -469,8 +514,19 @@ static int dp_audio_get_edid_blk(struct platform_device *pdev,
edid = audio->panel->edid_ctrl;
#if defined(CONFIG_SECDP)
if (secdp_adapter_is_legacy(audio->pdev))
secdp_audio_use_one_sampling_freq(edid->audio_data_block, edid->adb_size);
#endif
blk->audio_data_blk = edid->audio_data_block;
blk->audio_data_blk_size = edid->adb_size;
#if defined(CONFIG_SECDP)
print_hex_dump(KERN_DEBUG, "AUDIO_BLK: ",
DUMP_PREFIX_NONE, 16, 1, blk->audio_data_blk,
blk->audio_data_blk_size, false);
secdp_logger_hex_dump(blk->audio_data_blk, "AUDIO_BLK:",
blk->audio_data_blk_size);
#endif
blk->spk_alloc_data_blk = edid->spkr_alloc_data_block;
blk->spk_alloc_data_blk_size = edid->sadb_size;
@@ -518,11 +574,6 @@ static void dp_audio_teardown_done(struct platform_device *pdev)
if (IS_ERR(audio))
return;
if (!audio->panel || !audio->panel->get_panel_on) {
DP_ERR("invalid panel data\n");
return;
}
if (audio->dp_audio.tui_active) {
DP_DEBUG("TUI session active\n");
return;
@@ -605,6 +656,8 @@ static int dp_audio_register_ext_disp(struct dp_audio_private *audio)
struct msm_ext_disp_init_data *ext;
struct msm_ext_disp_audio_codec_ops *ops;
DP_ENTER("\n");
ext = &audio->ext_audio_data;
ops = &ext->codec_ops;
@@ -692,6 +745,10 @@ end:
return rc;
}
#if defined(CONFIG_SECDP_SWITCH)
extern int secdp_get_audio_ch(void);
#endif
static int dp_audio_notify(struct dp_audio_private *audio, u32 state)
{
int rc = 0;
@@ -704,12 +761,26 @@ static int dp_audio_notify(struct dp_audio_private *audio, u32 state)
goto end;
}
DP_INFO("audio stream %d is %s\n", ext->codec.stream_id,
(state == EXT_DISPLAY_CABLE_CONNECT) ? "connected" : "disconnected");
reinit_completion(&audio->hpd_comp);
rc = ext->intf_ops.audio_notify(audio->ext_pdev,
&ext->codec, state);
if (rc)
goto end;
#if defined(CONFIG_SECDP_SWITCH)
{
if (!audio->dp_audio.has_mst) {
int audio_ch = state ? secdp_get_audio_ch() : -1;
switch_set_state(&switch_secdp_audio, audio_ch);
DP_INFO("secdp audio state:0x%x\n", audio_ch);
}
}
#endif
if (atomic_read(&audio->acked))
goto end;
@@ -773,6 +844,15 @@ static int dp_audio_on(struct dp_audio *dp_audio)
return -EINVAL;
}
#if defined(CONFIG_SECDP)
if (!secdp_get_cable_status(&audio->pdev->dev)) {
DP_INFO("cable is out\n");
return -EINVAL;
}
#endif
DP_ENTER("\n");
dp_audio_register_ext_disp(audio);
ext = &audio->ext_audio_data;
@@ -804,6 +884,8 @@ static int dp_audio_off(struct dp_audio *dp_audio, bool skip_wait)
return -EINVAL;
}
DP_ENTER("\n");
audio = container_of(dp_audio, struct dp_audio_private, dp_audio);
if (!atomic_read(&audio->session_on)) {
@@ -813,6 +895,13 @@ static int dp_audio_off(struct dp_audio *dp_audio, bool skip_wait)
ext = &audio->ext_audio_data;
#if defined(CONFIG_SECDP)
if (!atomic_read(&audio->session_on)) {
DP_INFO("dp audio already off\n");
return rc;
}
#endif
work_pending = cancel_delayed_work_sync(&audio->notify_delayed_work);
if (work_pending)
DP_DEBUG("pending notification work completed\n");
@@ -845,6 +934,43 @@ static void dp_audio_notify_work_fn(struct work_struct *work)
dp_audio_notify(audio, EXT_DISPLAY_CABLE_CONNECT);
}
#if defined(CONFIG_SECDP_SWITCH)
int secdp_audio_register_switch(struct dp_audio *dp_audio)
{
int rc = 0;
if (dp_audio->has_mst) {
DP_INFO("skip switch register\n");
rc = -ENODEV;
goto end;
}
rc = switch_dev_register(&switch_secdp_audio);
if (rc) {
DP_INFO("Failed to register secdp_audio switch %d\n", rc);
rc = -ENODEV;
goto end;
}
DP_INFO("secdp_audio register success\n");
end:
return rc;
}
static void secdp_audio_unregister_switch(struct dp_audio_private *audio)
{
struct dp_audio *dp_audio = &audio->dp_audio;
if (dp_audio->has_mst) {
DP_DEBUG("skip switch unregister\n");
return;
}
switch_dev_unregister(&switch_secdp_audio);
DP_INFO("secdp_audio unregister success\n");
}
#endif
static int dp_audio_create_notify_workqueue(struct dp_audio_private *audio)
{
audio->notify_workqueue = create_workqueue("sdm_dp_audio_notify");
@@ -862,6 +988,10 @@ static void dp_audio_destroy_notify_workqueue(struct dp_audio_private *audio)
{
if (audio->notify_workqueue)
destroy_workqueue(audio->notify_workqueue);
#if defined(CONFIG_SECDP_SWITCH)
secdp_audio_unregister_switch(audio);
#endif
}
struct dp_audio *dp_audio_get(struct platform_device *pdev,

View File

@@ -22,6 +22,9 @@ struct dp_audio {
u32 lane_count;
u32 bw_code;
bool tui_active;
#if defined(CONFIG_SECDP_SWITCH)
bool has_mst;
#endif
/**
* on()
@@ -73,4 +76,9 @@ struct dp_audio *dp_audio_get(struct platform_device *pdev,
* @dp_audio: an instance of dp_audio.
*/
void dp_audio_put(struct dp_audio *dp_audio);
#if defined(CONFIG_SECDP_SWITCH)
int secdp_audio_register_switch(struct dp_audio *dp_audio);
#endif
#endif /* _DP_AUDIO_H_ */

View File

@@ -16,6 +16,12 @@
#include "dp_aux.h"
#include "dp_hpd.h"
#include "dp_debug.h"
#if defined(CONFIG_SECDP)
#if defined(CONFIG_SECDP_BIGDATA)
#include <linux/secdp_bigdata.h>
#endif
#include "secdp.h"
#endif
#define DP_AUX_ENUM_STR(x) #x
#define DP_AUX_IPC_NUM_PAGES 10
@@ -69,7 +75,11 @@ struct dp_aux_private {
struct dp_aux dp_aux;
struct dp_catalog_aux *catalog;
struct dp_aux_cfg *cfg;
#if !defined(CONFIG_SECDP)
struct device_node *aux_switch_node;
#else
struct secdp_misc *sec;
#endif
struct mutex mutex;
struct completion comp;
struct drm_dp_aux drm_aux;
@@ -104,9 +114,11 @@ static void dp_aux_hex_dump(struct drm_dp_aux *drm_aux,
int i, linelen, remaining = msg->size;
const int rowsize = 16;
u8 linebuf[64];
#if !defined(CONFIG_SECDP)
struct dp_aux_private *aux = container_of(drm_aux,
struct dp_aux_private, drm_aux);
struct dp_aux *dp_aux = &aux->dp_aux;
#endif
snprintf(prefix, sizeof(prefix), "%s %s %4xh(%2zu): ",
(msg->request & DP_AUX_I2C_MOT) ? "I2C" : "NAT",
@@ -120,10 +132,12 @@ static void dp_aux_hex_dump(struct drm_dp_aux *drm_aux,
hex_dump_to_buffer(msg->buffer + i, linelen, rowsize, 1,
linebuf, sizeof(linebuf), false);
#if !defined(CONFIG_SECDP)
if (msg->size == 1 && msg->address == 0)
DP_DEBUG_V("%s%s\n", prefix, linebuf);
else
DP_AUX_DEBUG(dp_aux, "%s%s\n", prefix, linebuf);
#endif
}
}
@@ -209,6 +223,21 @@ static u32 dp_aux_write(struct dp_aux_private *aux,
return len;
}
#if defined(CONFIG_SECDP)
#define DDC_SEGMENT_ADDR 0x30
static bool secdp_check_seg_addr(struct dp_aux_private *aux,
struct drm_dp_aux_msg *msg)
{
if (msg->address == DDC_SEGMENT_ADDR &&
!(msg->request & DP_AUX_I2C_READ) &&
msg->size == 1)
return true;
return false;
}
#endif
static int dp_aux_cmd_fifo_tx(struct dp_aux_private *aux,
struct drm_dp_aux_msg *msg)
{
@@ -239,6 +268,15 @@ static int dp_aux_cmd_fifo_tx(struct dp_aux_private *aux,
if (aux->aux_error_num == DP_AUX_ERR_NONE) {
ret = len;
} else {
#if defined(CONFIG_SECDP)
if (secdp_check_seg_addr(aux, msg)) {
DP_AUX_ERR(dp_aux, "ignore %s during [%s]\n",
dp_aux_get_error(aux->aux_error_num), prefix);
aux->aux_error_num = DP_AUX_ERR_NONE;
return msg->size;
}
#endif
DP_AUX_WARN_RATELIMITED(dp_aux, "aux err [%s] during [%s]\n",
dp_aux_get_error(aux->aux_error_num), prefix);
ret = -EINVAL;
@@ -298,6 +336,13 @@ static void dp_aux_native_handler(struct dp_aux_private *aux)
aux->catalog->clear_hw_interrupts(aux->catalog);
}
#if defined(CONFIG_SECDP_BIGDATA)
if (aux->aux_error_num == DP_AUX_ERR_NONE)
secdp_bigdata_clr_error_cnt(ERR_AUX);
else
secdp_bigdata_inc_error_cnt(ERR_AUX);
#endif
complete(&aux->comp);
}
@@ -327,6 +372,13 @@ static void dp_aux_i2c_handler(struct dp_aux_private *aux)
}
}
#if defined(CONFIG_SECDP_BIGDATA)
if (aux->aux_error_num == DP_AUX_ERR_NONE)
secdp_bigdata_clr_error_cnt(ERR_AUX);
else
secdp_bigdata_inc_error_cnt(ERR_AUX);
#endif
complete(&aux->comp);
}
@@ -361,6 +413,8 @@ static void dp_aux_reconfig(struct dp_aux *dp_aux)
return;
}
DP_ENTER("\n");
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
aux->catalog->update_aux_cfg(aux->catalog,
@@ -377,6 +431,8 @@ static void dp_aux_abort_transaction(struct dp_aux *dp_aux, bool abort)
return;
}
DP_ENTER("\n");
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
atomic_set(&aux->aborted, abort);
@@ -558,6 +614,20 @@ static ssize_t dp_aux_transfer(struct drm_dp_aux *drm_aux,
ret = dp_aux_cmd_fifo_tx(aux, msg);
if ((ret < 0) && !atomic_read(&aux->aborted)) {
#if defined(CONFIG_SECDP)
if (!secdp_get_cable_status(aux->dev) || !secdp_get_hpd_status(aux->dev)) {
DP_INFO("hpd_low or cable_lost %ld\n", ret);
/*
* don't need to repeat aux.
* exit loop in drm_dp_dpcd_access()
*/
msg->reply = aux->native ?
DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
ret = msg->size;
aux->retry_cnt = 0;
goto unlock_exit;
}
#endif
aux->retry_cnt++;
if (!(aux->retry_cnt % retry_count))
aux->catalog->update_aux_cfg(aux->catalog,
@@ -661,6 +731,8 @@ static void dp_aux_init(struct dp_aux *dp_aux, struct dp_aux_cfg *aux_cfg)
return;
}
DP_ENTER("\n");
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
if (aux->enabled)
@@ -673,6 +745,8 @@ static void dp_aux_init(struct dp_aux *dp_aux, struct dp_aux_cfg *aux_cfg)
atomic_set(&aux->aborted, 0);
aux->retry_cnt = 0;
aux->enabled = true;
DP_LEAVE("\n");
}
static void dp_aux_deinit(struct dp_aux *dp_aux)
@@ -684,6 +758,8 @@ static void dp_aux_deinit(struct dp_aux *dp_aux)
return;
}
DP_ENTER("\n");
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
if (!aux->enabled)
@@ -692,6 +768,8 @@ static void dp_aux_deinit(struct dp_aux *dp_aux)
atomic_set(&aux->aborted, 1);
aux->catalog->enable(aux->catalog, false);
aux->enabled = false;
DP_LEAVE("\n");
}
static int dp_aux_register(struct dp_aux *dp_aux, struct drm_device *drm_dev)
@@ -724,6 +802,7 @@ static int dp_aux_register(struct dp_aux *dp_aux, struct drm_device *drm_dev)
/* if bridge is defined, override transfer function */
if (aux->aux_bridge && aux->aux_bridge->transfer)
aux->drm_aux.transfer = dp_aux_bridge_transfer;
exit:
return ret;
}
@@ -876,10 +955,17 @@ end:
}
#endif
#if !defined(CONFIG_SECDP)
struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
struct dp_parser *parser, struct device_node *aux_switch,
struct dp_aux_bridge *aux_bridge, void *ipc_log_context,
enum dp_aux_switch_type switch_type)
#else
struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
struct dp_parser *parser, struct device_node *aux_switch,
struct dp_aux_bridge *aux_bridge, void *ipc_log_context,
enum dp_aux_switch_type switch_type, void *sec)
#endif
{
int rc = 0;
struct dp_aux_private *aux;
@@ -891,6 +977,8 @@ struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
goto error;
}
DP_ENTER("\n");
aux = devm_kzalloc(dev, sizeof(*aux), GFP_KERNEL);
if (!aux) {
rc = -ENOMEM;
@@ -904,7 +992,11 @@ struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
aux->dev = dev;
aux->catalog = catalog;
aux->cfg = parser->aux_cfg;
#if !defined(CONFIG_SECDP)
aux->aux_switch_node = aux_switch;
#else
aux->sec = (struct secdp_misc *)sec;
#endif
aux->aux_bridge = aux_bridge;
dp_aux = &aux->dp_aux;
aux->retry_cnt = 0;

View File

@@ -68,10 +68,17 @@ struct dp_aux {
int (*switch_unregister_notifier)(struct notifier_block *nb, struct device_node *node);
};
#if !defined(CONFIG_SECDP)
struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
struct dp_parser *parser, struct device_node *aux_switch,
struct dp_aux_bridge *aux_bridge, void *ipc_log_context,
enum dp_aux_switch_type switch_type);
#else
struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
struct dp_parser *parser, struct device_node *aux_switch,
struct dp_aux_bridge *aux_bridge, void *ipc_log_context,
enum dp_aux_switch_type switch_type, void *sec);
#endif
void dp_aux_put(struct dp_aux *aux);
#endif /*__DP_AUX_H_*/

View File

@@ -13,6 +13,9 @@
#include "dp_debug.h"
#include "dp_link.h"
#include "dp_lphw_hpd.h"
#if defined(CONFIG_SECDP)
#include "secdp.h"
#endif
#define DP_GET_MSB(x) (x >> 8)
#define DP_GET_LSB(x) (x & 0xff)
@@ -41,24 +44,13 @@
#define DP_INTR_MASK2 (DP_INTERRUPT_STATUS2 << 2)
/**
* FIFO errors should be enabled in development environment
* to capture issues during internal testing.
* In production environment, driver should ignore the errors
* and let the black screen persist and end user should replug
* to recover from this.
*/
#define DP_INTERRUPT_STATUS3_DEV \
#define DP_INTERRUPT_STATUS3 \
(DP_INTR_SST_ML_FIFO_OVERFLOW | DP_INTR_MST0_ML_FIFO_OVERFLOW | \
DP_INTR_MST1_ML_FIFO_OVERFLOW | DP_INTR_DP1_FRAME_END | DP_INTR_SDP0_COLLISION | \
DP_INTR_SDP1_COLLISION)
#define DP_INTERRUPT_STATUS3_PROD \
(DP_INTR_DP1_FRAME_END | DP_INTR_SDP0_COLLISION | DP_INTR_SDP1_COLLISION)
#define DP_INTR_MASK3_DEV (DP_INTERRUPT_STATUS3_DEV << 2)
#define DP_INTR_MASK3_PROD (DP_INTERRUPT_STATUS3_PROD << 2)
#define DP_INTR_MASK3 (DP_INTERRUPT_STATUS3 << 2)
#define DP_INTERRUPT_STATUS5 \
(DP_INTR_MST_DP0_VCPF_SENT | DP_INTR_MST_DP1_VCPF_SENT)
@@ -178,6 +170,11 @@ static u32 dp_read_hw(struct dp_catalog_private *catalog,
{
u32 data = 0;
#if defined(CONFIG_SECDP)
if (secdp_phy_reset_check(catalog->dev))
return 0;
#endif
data = readl_relaxed(io_data->io.base + offset);
return data;
@@ -186,6 +183,11 @@ static u32 dp_read_hw(struct dp_catalog_private *catalog,
static void dp_write_hw(struct dp_catalog_private *catalog,
struct dp_io_data *io_data, u32 offset, u32 data)
{
#if defined(CONFIG_SECDP)
if (secdp_phy_reset_check(catalog->dev))
return;
#endif
writel_relaxed(data, io_data->io.base + offset);
}
@@ -302,7 +304,16 @@ static int dp_catalog_aux_clear_trans(struct dp_catalog_aux *aux, bool read)
if (read) {
data = dp_read(DP_AUX_TRANS_CTRL);
#if defined(CONFIG_SECDP)
/* Prevent_CXX Major defect - Invalid Assignment: The type size
* of both side variables are different:
* "data" is 4 ( unsigned int ) and "data & 0xfffffffffffffdffUL
* " is 8 ( unsigned long )
*/
data &= ((u32)~BIT(9));
#else
data &= ~BIT(9);
#endif
dp_write(DP_AUX_TRANS_CTRL, data);
} else {
dp_write(DP_AUX_TRANS_CTRL, 0);
@@ -326,6 +337,10 @@ static void dp_catalog_aux_clear_hw_interrupts(struct dp_catalog_aux *aux)
io_data = catalog->io.dp_phy;
data = dp_read(DP_PHY_AUX_INTERRUPT_STATUS);
#if defined(CONFIG_SECDP)
if (data)
DP_DEBUG("PHY_AUX_INTERRUPT_STATUS=0x%08x\n", data);
#endif
dp_write(DP_PHY_AUX_INTERRUPT_CLEAR, 0x1f);
wmb(); /* make sure 0x1f is written before next write */
@@ -404,6 +419,13 @@ static void dp_catalog_aux_update_cfg(struct dp_catalog_aux *aux,
catalog = dp_catalog_get_priv(aux);
#if defined(CONFIG_SECDP)
if (!secdp_get_cable_status(catalog->dev)) {
DP_INFO("cable is out\n");
return;
}
#endif
io_data = catalog->io.dp_phy;
current_index = cfg[type].current_index;
@@ -1098,6 +1120,8 @@ static void dp_catalog_ctrl_state_ctrl(struct dp_catalog_ctrl *ctrl, u32 state)
return;
}
DP_ENTER("\n");
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_link;
@@ -1199,6 +1223,8 @@ static void dp_catalog_panel_config_dto(struct dp_catalog_panel *panel,
return;
}
DP_ENTER("\n");
catalog = dp_catalog_get_priv(panel);
io_data = catalog->io.dp_link;
@@ -1275,6 +1301,8 @@ static void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog_ctrl *ctrl,
return;
}
DP_ENTER("en:%d\n", enable);
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_link;
@@ -1693,7 +1721,9 @@ static void dp_catalog_panel_dp_flush(struct dp_catalog_panel *panel,
static void dp_catalog_panel_pps_flush(struct dp_catalog_panel *panel)
{
dp_catalog_panel_dp_flush(panel, DP_PPS_FLUSH);
#if !defined(CONFIG_SECDP)
DP_DEBUG("pps flush for stream:%d\n", panel->stream_id);
#endif
}
static void dp_catalog_panel_dhdr_flush(struct dp_catalog_panel *panel)
@@ -1810,8 +1840,6 @@ end:
static void dp_catalog_ctrl_enable_irq(struct dp_catalog_ctrl *ctrl,
bool enable)
{
u32 DP_INTR_MASK3 = DP_INTR_MASK3_PROD;
u32 DP_INTERRUPT_STATUS3 = DP_INTERRUPT_STATUS3_PROD;
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
@@ -1823,11 +1851,6 @@ static void dp_catalog_ctrl_enable_irq(struct dp_catalog_ctrl *ctrl,
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_ahb;
if (catalog->parser->fifo_error_enable) {
DP_INTR_MASK3 = DP_INTR_MASK3_DEV;
DP_INTERRUPT_STATUS3 = DP_INTERRUPT_STATUS3_DEV;
}
if (enable) {
dp_write(DP_INTR_STATUS, DP_INTR_MASK1);
dp_write(DP_INTR_STATUS2, DP_INTR_MASK2);
@@ -1856,8 +1879,7 @@ static void dp_catalog_ctrl_enable_irq(struct dp_catalog_ctrl *ctrl,
static void dp_catalog_ctrl_get_interrupt(struct dp_catalog_ctrl *ctrl)
{
u32 ack = 0, DP_INTR_MASK3 = DP_INTR_MASK3_PROD;
u32 DP_INTERRUPT_STATUS3 = DP_INTERRUPT_STATUS3_PROD;
u32 ack = 0;
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
@@ -1869,11 +1891,6 @@ static void dp_catalog_ctrl_get_interrupt(struct dp_catalog_ctrl *ctrl)
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_ahb;
if (catalog->parser->fifo_error_enable) {
DP_INTR_MASK3 = DP_INTR_MASK3_DEV;
DP_INTERRUPT_STATUS3 = DP_INTERRUPT_STATUS3_DEV;
}
ctrl->isr = dp_read(DP_INTR_STATUS2);
ctrl->isr &= ~DP_INTR_MASK2;
ack = ctrl->isr & DP_INTERRUPT_STATUS2;
@@ -1980,6 +1997,22 @@ static void dp_catalog_ctrl_update_vx_px(struct dp_catalog_ctrl *ctrl,
value1 = vm_pre_emphasis[v_level][p_level];
}
#if defined(SECDP_SELF_TEST)
if (secdp_self_test_status(ST_VOLTAGE_TUN) >= 0) {
u8 val = secdp_self_test_get_arg(ST_VOLTAGE_TUN)[v_level*4 + p_level];
DP_INFO("[vx] value0: %02x => %02x\n", value0, val);
value0 = val;
}
if (secdp_self_test_status(ST_PREEM_TUN) >= 0) {
u8 val = secdp_self_test_get_arg(ST_PREEM_TUN)[v_level*4 + p_level];
DP_INFO("[px] value0: %02x => %02x\n", value1, val);
value1 = val;
}
#endif
/* program default setting first */
io_data = catalog->io.dp_ln_tx0;
@@ -3189,6 +3222,22 @@ struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser)
ctrl.valid_lt_params = false;
}
#if defined(CONFIG_SECDP)
if (parser->valid_preshoot_params) {
ctrl.preshoot0_rbr_hbr = parser->preshoot0_rbr_hbr;
ctrl.preshoot1_rbr_hbr = parser->preshoot1_rbr_hbr;
ctrl.preshoot0_hbr2_3 = parser->preshoot0_hbr2_3;
ctrl.preshoot1_hbr2_3 = parser->preshoot1_hbr2_3;
ctrl.valid_preshoot_params = true;
} else {
ctrl.preshoot0_rbr_hbr = NULL;
ctrl.preshoot1_rbr_hbr = NULL;
ctrl.preshoot0_hbr2_3 = NULL;
ctrl.preshoot1_hbr2_3 = NULL;
ctrl.valid_preshoot_params = false;
}
#endif
dp_catalog = &catalog->dp_catalog;
dp_catalog->parser = parser;

View File

@@ -99,6 +99,14 @@ struct dp_catalog_ctrl {
u8 *pre_emp_hbr_rbr;
bool valid_lt_params;
#if defined(CONFIG_SECDP)
u8 *preshoot0_hbr2_3;
u8 *preshoot1_hbr2_3;
u8 *preshoot0_rbr_hbr;
u8 *preshoot1_rbr_hbr;
bool valid_preshoot_params;
#endif
void (*state_ctrl)(struct dp_catalog_ctrl *ctrl, u32 state);
void (*config_ctrl)(struct dp_catalog_ctrl *ctrl, u8 ln_cnt);
void (*lane_mapping)(struct dp_catalog_ctrl *ctrl, bool flipped,

View File

@@ -12,6 +12,13 @@
#include <linux/rational.h>
#include <drm/drm_fixed.h>
#if defined(CONFIG_SECDP)
#include "secdp.h"
#endif
#if defined(CONFIG_SECDP_DBG)
#include "dp_parser.h"
#endif
#define dp_catalog_get_priv_v420(x) ({ \
struct dp_catalog *catalog; \
catalog = container_of(x, struct dp_catalog, x); \
@@ -52,6 +59,62 @@ struct dp_catalog_private_v420 {
struct dp_catalog *dpc;
};
#if defined(CONFIG_SECDP)
#define PRESHOOT_ADDR 0xE8
static void _secdp_catalog420_preshoot_adjust(
struct dp_catalog_ctrl *ctrl, u32 version,
u8 v_level, u8 p_level, bool high)
{
struct dp_catalog_private_v420 *catalog;
struct dp_io_data *io_data;
int i, idx;
u8 value[DP_HW_PRESHOOT_MAX];
if (version < 0x10020003) {
DP_ERR("version not matched %x\n", version);
return;
}
if (!ctrl->valid_preshoot_params) {
//DP_ERR("invalid preshoot params\n");
return;
}
catalog = dp_catalog_get_priv_v420(ctrl);
idx = v_level * MAX_VOLTAGE_LEVELS + p_level;
if (high) {
value[DP_HW_PRESHOOT_0] = ctrl->preshoot0_hbr2_3[idx];
value[DP_HW_PRESHOOT_1] = ctrl->preshoot1_hbr2_3[idx];
} else {
value[DP_HW_PRESHOOT_0] = ctrl->preshoot0_rbr_hbr[idx];
value[DP_HW_PRESHOOT_1] = ctrl->preshoot1_rbr_hbr[idx];
}
for (i = 0; i < DP_HW_PRESHOOT_MAX; i++) {
if (i == DP_HW_PRESHOOT_0)
io_data = catalog->io->dp_ln_tx0;
else if (i == DP_HW_PRESHOOT_1)
io_data = catalog->io->dp_ln_tx1;
else
DP_ERR("cannot be here\n");
value[i] |= BIT(5);
/*
* USB3_DP_PHY_DP_QSERDES_TX0_PRE_EMPH
* USB3_DP_PHY_DP_QSERDES_TX1_PRE_EMPH
*/
dp_write(PRESHOOT_ADDR, value[i]);
DP_INFO("%s [%s] %02x\n", secdp_preshoot_to_string(i),
(!high ? "low" : "high"), value[i]);
}
}
#endif/*CONFIG_SECDP*/
static void dp_catalog_aux_setup_v420(struct dp_catalog_aux *aux,
struct dp_aux_cfg *cfg)
{
@@ -64,6 +127,8 @@ static void dp_catalog_aux_setup_v420(struct dp_catalog_aux *aux,
return;
}
DP_ENTER("\n");
catalog = dp_catalog_get_priv_v420(aux);
io_data = catalog->io->dp_phy;
@@ -248,6 +313,22 @@ static void dp_catalog_ctrl_update_vx_px_v420(struct dp_catalog_ctrl *ctrl,
value1 = vm_pre_emphasis[v_level][p_level];
}
#if defined(SECDP_SELF_TEST)
if (secdp_self_test_status(ST_VOLTAGE_TUN) >= 0) {
u8 val = secdp_self_test_get_arg(ST_VOLTAGE_TUN)[v_level*4 + p_level];
DP_INFO("[vx] value0: 0x%02x => 0x%02x\n", value0, val);
value0 = val;
}
if (secdp_self_test_status(ST_PREEM_TUN) >= 0) {
u8 val = secdp_self_test_get_arg(ST_PREEM_TUN)[v_level*4 + p_level];
DP_INFO("[px] value0: 0x%02x => 0x%02x\n", value1, val);
value1 = val;
}
#endif
/* program default setting first */
io_data = catalog->io->dp_ln_tx0;
dp_write(TXn_TX_DRV_LVL_V420, 0x2A);
@@ -277,6 +358,10 @@ static void dp_catalog_ctrl_update_vx_px_v420(struct dp_catalog_ctrl *ctrl,
DP_ERR("invalid vx (0x%x=0x%x), px (0x%x=0x%x\n",
v_level, value0, p_level, value1);
}
#if defined(CONFIG_SECDP)
_secdp_catalog420_preshoot_adjust(ctrl, version, v_level, p_level, high);
#endif
}
static void dp_catalog_ctrl_lane_pnswap_v420(struct dp_catalog_ctrl *ctrl,
@@ -325,6 +410,8 @@ struct dp_catalog_sub *dp_catalog_get_v420(struct device *dev,
return ERR_PTR(-EINVAL);
}
DP_ENTER("\n");
catalog_priv = devm_kzalloc(dev, sizeof(*catalog_priv), GFP_KERNEL);
if (!catalog_priv)
return ERR_PTR(-ENOMEM);

View File

@@ -13,7 +13,12 @@
#include "dp_ctrl.h"
#include "dp_debug.h"
#include "sde_dbg.h"
#include "dp_panel_tu.h"
#if defined(CONFIG_SECDP)
#if defined(CONFIG_SECDP_BIGDATA)
#include <linux/secdp_bigdata.h>
#endif
#include "secdp.h"
#endif
#define DP_MST_DEBUG(fmt, ...) DP_DEBUG(fmt, ##__VA_ARGS__)
@@ -66,6 +71,10 @@ struct dp_ctrl_private {
struct dp_parser *parser;
struct dp_catalog_ctrl *catalog;
struct dp_pll *pll;
#if defined(CONFIG_SECDP)
struct secdp_misc *sec;
bool link_train_status;
#endif
struct completion idle_comp;
struct completion video_comp;
@@ -134,6 +143,8 @@ static void dp_ctrl_push_idle(struct dp_ctrl_private *ctrl,
if (!ctrl->power_on)
return;
DP_ENTER("\n");
if (!ctrl->mst_mode) {
state = ST_PUSH_IDLE;
goto trigger_idle;
@@ -244,6 +255,11 @@ static void dp_ctrl_update_hw_vx_px(struct dp_ctrl_private *ctrl)
ctrl->link->link_params.bw_code == DP_LINK_BW_8_1)
high = true;
#if defined(CONFIG_SECDP)
secdp_redriver_linkinfo(ctrl->power, link->link_params.bw_code,
link->phy_params.v_level, link->phy_params.p_level);
#endif
ctrl->catalog->update_vx_px(ctrl->catalog,
link->phy_params.v_level, link->phy_params.p_level, high);
}
@@ -449,6 +465,11 @@ static int dp_ctrl_link_rate_down_shift(struct dp_ctrl_private *ctrl)
DP_DEBUG("new bw code=0x%x\n", ctrl->link->link_params.bw_code);
#if defined(CONFIG_SECDP_BIGDATA)
secdp_bigdata_save_item(BD_CUR_LINK_RATE,
ctrl->link->link_params.bw_code);
#endif
return ret;
}
@@ -553,9 +574,26 @@ static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl)
u8 const encoding = 0x1, downspread = 0x00;
struct drm_dp_link link_info = {0};
#if defined(CONFIG_SECDP)
if (!secdp_get_cable_status(ctrl->dev) || !secdp_get_hpd_status(ctrl->dev)) {
DP_INFO("cable is out\n");
return -EIO;
}
DP_ENTER("\n");
ctrl->link_train_status = false;
#endif
ctrl->link->phy_params.p_level = 0;
ctrl->link->phy_params.v_level = 0;
#if defined(CONFIG_SECDP)
if (secdp_check_hmd_dev(ctrl->sec, "PicoVR")) {
DP_INFO("pico REAL Plus!\n");
ctrl->link->phy_params.v_level = 2; /*800mV*/
}
#endif
link_info.num_lanes = ctrl->link->link_params.lane_count;
link_info.rate = drm_dp_bw_code_to_link_rate(
ctrl->link->link_params.bw_code);
@@ -601,11 +639,22 @@ static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl)
DP_INFO("link training #2 successful\n");
end:
#if defined(CONFIG_SECDP)
if (!secdp_get_cable_status(ctrl->dev) || !secdp_get_hpd_status(ctrl->dev)) {
DP_INFO("cable is out <2>\n");
return -EIO;
}
#endif
dp_ctrl_state_ctrl(ctrl, 0);
/* Make sure to clear the current pattern before starting a new one */
wmb();
dp_ctrl_clear_training_pattern(ctrl);
#if defined(CONFIG_SECDP)
if (!ret)
ctrl->link_train_status = true;
#endif
return ret;
}
@@ -613,6 +662,8 @@ static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl)
{
int ret = 0;
DP_ENTER("\n");
if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN)
goto end;
@@ -628,7 +679,10 @@ static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl)
0x01);
ret = dp_ctrl_link_train(ctrl);
#if defined(CONFIG_SECDP_BIGDATA)
if (ret)
secdp_bigdata_inc_error_cnt(ERR_LINK_TRAIN);
#endif
end:
return ret;
}
@@ -694,6 +748,8 @@ static void dp_ctrl_disable_link_clock(struct dp_ctrl_private *ctrl)
{
int rc = 0;
DP_ENTER("\n");
ctrl->power->clk_enable(ctrl->power, DP_LINK_PM, false);
if (ctrl->pll->pll_unprepare) {
rc = ctrl->pll->pll_unprepare(ctrl->pll);
@@ -714,6 +770,15 @@ static void dp_ctrl_select_training_pattern(struct dp_ctrl_private *ctrl,
else
pattern = DP_TRAINING_PATTERN_2;
DP_INFO("+ pattern:%d, downgrade:%d\n", pattern, downgrade);
#ifdef SECDP_MAX_HBR2
if (pattern == DP_TRAINING_PATTERN_4) {
DP_INFO("TPS4 to TPS3\n");
downgrade = true;
}
#endif
if (!downgrade)
goto end;
@@ -728,6 +793,7 @@ static void dp_ctrl_select_training_pattern(struct dp_ctrl_private *ctrl,
break;
}
end:
DP_INFO("- pattern:%d\n", pattern);
ctrl->training_2_pattern = pattern;
}
@@ -739,6 +805,8 @@ static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow)
struct dp_catalog_ctrl *catalog;
struct dp_link_params *link_params;
DP_ENTER("\n");
catalog = ctrl->catalog;
link_params = &ctrl->link->link_params;
@@ -746,6 +814,13 @@ static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow)
link_params->lane_count);
while (1) {
#if defined(CONFIG_SECDP)
if (!secdp_get_cable_status(ctrl->dev) || !secdp_get_hpd_status(ctrl->dev)) {
DP_INFO("cable is out\n");
rc = -EIO;
break;
}
#endif
DP_DEBUG("bw_code=%d, lane_count=%d\n",
link_params->bw_code, link_params->lane_count);
@@ -785,6 +860,14 @@ static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow)
break;
}
#if defined(CONFIG_SECDP) && !defined(SECDP_AUDIO_CTS)
if ((ctrl->link->link_params.bw_code == DP_LINK_BW_1_62 && downgrade) ||
!secdp_get_cable_status(ctrl->dev) || !secdp_get_hpd_status(ctrl->dev)) {
rc = -EIO;
break;
}
#endif
if (!link_train_max_retries || atomic_read(&ctrl->aborted)) {
dp_ctrl_disable_link_clock(ctrl);
break;
@@ -813,6 +896,8 @@ static int dp_ctrl_enable_stream_clocks(struct dp_ctrl_private *ctrl,
enum dp_pm_type clk_type;
char clk_name[32] = "";
DP_ENTER("\n");
ret = ctrl->power->set_pixel_clk_parent(ctrl->power,
dp_panel->stream_id);
@@ -850,6 +935,8 @@ static int dp_ctrl_disable_stream_clocks(struct dp_ctrl_private *ctrl,
{
int ret = 0;
DP_ENTER("\n");
if (dp_panel->stream_id == DP_STREAM_0) {
return ctrl->power->clk_enable(ctrl->power,
DP_STREAM0_PM, false);
@@ -873,6 +960,8 @@ static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip, bool reset)
return -EINVAL;
}
DP_ENTER("\n");
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
ctrl->orientation = flip;
@@ -904,6 +993,8 @@ static void dp_ctrl_host_deinit(struct dp_ctrl *dp_ctrl)
return;
}
DP_ENTER("\n");
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
ctrl->catalog->enable_irq(ctrl->catalog, false);
@@ -982,6 +1073,8 @@ static int dp_ctrl_link_maintenance(struct dp_ctrl *dp_ctrl)
return -EINVAL;
}
DP_ENTER("\n");
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
ctrl->aux->state &= ~DP_STATE_LINK_MAINTENANCE_COMPLETED;
@@ -1104,13 +1197,105 @@ static void dp_ctrl_send_phy_test_pattern(struct dp_ctrl_private *ctrl)
dp_link_get_phy_test_pattern(pattern_requested));
}
static void dp_ctrl_mst_calculate_rg(struct dp_ctrl_private *ctrl,
struct dp_panel *panel, u32 *p_x_int, u32 *p_y_frac_enum)
{
u64 min_slot_cnt, max_slot_cnt;
u64 raw_target_sc, target_sc_fixp;
u64 ts_denom, ts_enum, ts_int;
u64 pclk = panel->pinfo.pixel_clk_khz;
u64 lclk = 0;
u64 lanes = ctrl->link->link_params.lane_count;
u64 bpp = panel->pinfo.bpp;
u64 pbn = panel->pinfo.pbn_no_overhead; // before dsc/fec overhead
u64 numerator, denominator, temp, temp1, temp2;
u32 x_int = 0, y_frac_enum = 0;
u64 target_strm_sym, ts_int_fixp, ts_frac_fixp, y_frac_enum_fixp;
lclk = drm_dp_bw_code_to_link_rate(ctrl->link->link_params.bw_code);
if (panel->pinfo.comp_info.enabled)
bpp = panel->pinfo.comp_info.tgt_bpp;
/* min_slot_cnt */
numerator = pclk * bpp * 64 * 1000;
denominator = lclk * lanes * 8 * 1000;
min_slot_cnt = drm_fixp_from_fraction(numerator, denominator);
/* max_slot_cnt */
numerator = pbn * 54 * 1000;
denominator = lclk * lanes;
max_slot_cnt = drm_fixp_from_fraction(numerator, denominator);
/* raw_target_sc */
numerator = max_slot_cnt + min_slot_cnt;
denominator = drm_fixp_from_fraction(2, 1);
raw_target_sc = drm_fixp_div(numerator, denominator);
DP_DEBUG("raw_target_sc before overhead:0x%llx\n", raw_target_sc);
DP_DEBUG("dsc_overhead_fp:0x%llx\n", panel->pinfo.dsc_overhead_fp);
/* apply fec and dsc overhead factor */
if (panel->pinfo.dsc_overhead_fp)
raw_target_sc = drm_fixp_mul(raw_target_sc,
panel->pinfo.dsc_overhead_fp);
if (panel->fec_overhead_fp)
raw_target_sc = drm_fixp_mul(raw_target_sc,
panel->fec_overhead_fp);
DP_DEBUG("raw_target_sc after overhead:0x%llx\n", raw_target_sc);
/* target_sc */
temp = drm_fixp_from_fraction(256 * lanes, 1);
numerator = drm_fixp_mul(raw_target_sc, temp);
denominator = drm_fixp_from_fraction(256 * lanes, 1);
target_sc_fixp = drm_fixp_div(numerator, denominator);
ts_enum = 256 * lanes;
ts_denom = drm_fixp_from_fraction(256 * lanes, 1);
ts_int = drm_fixp2int(target_sc_fixp);
temp = drm_fixp2int_ceil(raw_target_sc);
if (temp != ts_int) {
temp = drm_fixp_from_fraction(ts_int, 1);
temp1 = raw_target_sc - temp;
temp2 = drm_fixp_mul(temp1, ts_denom);
ts_enum = drm_fixp2int(temp2);
}
/* target_strm_sym */
ts_int_fixp = drm_fixp_from_fraction(ts_int, 1);
ts_frac_fixp = drm_fixp_from_fraction(ts_enum, drm_fixp2int(ts_denom));
temp = ts_int_fixp + ts_frac_fixp;
temp1 = drm_fixp_from_fraction(lanes, 1);
target_strm_sym = drm_fixp_mul(temp, temp1);
/* x_int */
x_int = drm_fixp2int(target_strm_sym);
/* y_enum_frac */
temp = drm_fixp_from_fraction(x_int, 1);
temp1 = target_strm_sym - temp;
temp2 = drm_fixp_from_fraction(256, 1);
y_frac_enum_fixp = drm_fixp_mul(temp1, temp2);
temp1 = drm_fixp2int(y_frac_enum_fixp);
temp2 = drm_fixp2int_ceil(y_frac_enum_fixp);
y_frac_enum = (u32)((temp1 == temp2) ? temp1 : temp1 + 1);
panel->mst_target_sc = raw_target_sc;
*p_x_int = x_int;
*p_y_frac_enum = y_frac_enum;
DP_DEBUG("x_int: %d, y_frac_enum: %d\n", x_int, y_frac_enum);
}
static void dp_ctrl_mst_stream_setup(struct dp_ctrl_private *ctrl,
struct dp_panel *panel)
{
u32 x_int, y_frac_enum;
u32 x_int, y_frac_enum, lanes, bw_code;
int i;
struct dp_tu_mst_rg_in mst_rg_calc_in;
struct dp_tu_mst_rg_out mst_rg_calc_out;
if (!ctrl->mst_mode)
return;
@@ -1124,21 +1309,10 @@ static void dp_ctrl_mst_stream_setup(struct dp_ctrl_private *ctrl,
ctrl->mst_ch_info.slot_info[i].tot_slots);
}
mst_rg_calc_in.lclk_khz = drm_dp_bw_code_to_link_rate(ctrl->link->link_params.bw_code);
mst_rg_calc_in.pclk_khz = panel->pinfo.pixel_clk_khz;
mst_rg_calc_in.nlanes = ctrl->link->link_params.lane_count;
mst_rg_calc_in.src_bpp = panel->pinfo.bpp;
mst_rg_calc_in.tgt_bpp = panel->pinfo.comp_info.tgt_bpp;
mst_rg_calc_in.fec_en = panel->fec_overhead_fp ? 1 : 0;
mst_rg_calc_in.dsc_en = panel->pinfo.comp_info.enabled ? 1 : 0;
mst_rg_calc_in.fec_overhead_fp = panel->fec_overhead_fp;
mst_rg_calc_in.dsc_overhead_fp = panel->pinfo.dsc_overhead_fp;
mst_rg_calc_in.pbn = panel->pbn;
lanes = ctrl->link->link_params.lane_count;
bw_code = ctrl->link->link_params.bw_code;
dp_tu_mst_rg_calc(&mst_rg_calc_in, &mst_rg_calc_out);
x_int = mst_rg_calc_out.x_int;
y_frac_enum = mst_rg_calc_out.y_frac_enum;
dp_ctrl_mst_calculate_rg(ctrl, panel, &x_int, &y_frac_enum);
ctrl->catalog->update_rg(ctrl->catalog, panel->stream_id,
x_int, y_frac_enum);
@@ -1148,8 +1322,7 @@ static void dp_ctrl_mst_stream_setup(struct dp_ctrl_private *ctrl,
panel->channel_start_slot, panel->channel_total_slots);
DP_MST_DEBUG("mst lane_cnt:%d, bw:%d, x_int:%d, y_frac:%d\n",
ctrl->link->link_params.lane_count,
ctrl->link->link_params.bw_code, x_int, y_frac_enum);
lanes, bw_code, x_int, y_frac_enum);
}
static void dp_ctrl_dsc_setup(struct dp_ctrl_private *ctrl, struct dp_panel *panel)
@@ -1157,7 +1330,6 @@ static void dp_ctrl_dsc_setup(struct dp_ctrl_private *ctrl, struct dp_panel *pan
int rlen;
u32 dsc_enable;
struct dp_panel_info *pinfo = &panel->pinfo;
u8 dsc_status = 0;
if (!ctrl->fec_mode)
return;
@@ -1171,14 +1343,6 @@ static void dp_ctrl_dsc_setup(struct dp_ctrl_private *ctrl, struct dp_panel *pan
if (ctrl->mst_mode && (panel->stream_id == DP_STREAM_1) && !dsc_enable)
return;
if (dsc_enable && ctrl->mst_mode && (ctrl->stream_count > 1)) {
drm_dp_dpcd_readb(ctrl->aux->drm_aux, DP_DSC_ENABLE, &dsc_status);
// Avoid writing DP_DSC_ENABLE if it is already enabled.
if (dsc_status & DP_DECOMPRESSION_EN)
return;
}
rlen = drm_dp_dpcd_writeb(ctrl->aux->drm_aux, DP_DSC_ENABLE,
dsc_enable);
if (rlen < 1)
@@ -1194,6 +1358,8 @@ static int dp_ctrl_stream_on(struct dp_ctrl *dp_ctrl, struct dp_panel *panel)
if (!dp_ctrl || !panel)
return -EINVAL;
DP_ENTER("\n");
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
if (!ctrl->power_on) {
@@ -1256,6 +1422,8 @@ static void dp_ctrl_mst_stream_pre_off(struct dp_ctrl *dp_ctrl,
if (!ctrl->mst_mode)
return;
DP_ENTER("\n");
for (i = DP_STREAM_0; i < DP_STREAM_MAX; i++) {
ctrl->catalog->channel_alloc(ctrl->catalog,
i,
@@ -1283,6 +1451,8 @@ static void dp_ctrl_stream_pre_off(struct dp_ctrl *dp_ctrl,
return;
}
DP_ENTER("\n");
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
dp_ctrl_push_idle(ctrl, panel->stream_id);
@@ -1297,6 +1467,8 @@ static void dp_ctrl_stream_off(struct dp_ctrl *dp_ctrl, struct dp_panel *panel)
if (!dp_ctrl || !panel)
return;
DP_ENTER("\n");
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
if (!ctrl->power_on)
@@ -1309,6 +1481,16 @@ static void dp_ctrl_stream_off(struct dp_ctrl *dp_ctrl, struct dp_panel *panel)
ctrl->stream_count--;
}
#if defined(CONFIG_SECDP)
bool secdp_get_link_train_status(struct dp_ctrl *dp_ctrl)
{
struct dp_ctrl_private *ctrl;
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
return ctrl->link_train_status;
}
#endif/*CONFIG_SECDP*/
static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool mst_mode,
bool fec_mode, bool dsc_mode, bool shallow)
{
@@ -1342,13 +1524,17 @@ static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool mst_mode,
if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
DP_DEBUG("using phy test link parameters\n");
} else {
#if defined(CONFIG_SECDP) && defined(SECDP_OPTIMAL_LINK_RATE)
if (!ctrl->panel->tbox && !ctrl->parser->mst_support)
rate = secdp_gen_link_clk(ctrl->panel);
#endif
ctrl->link->link_params.bw_code =
drm_dp_link_rate_to_bw_code(rate);
ctrl->link->link_params.lane_count =
ctrl->panel->link_info.num_lanes;
}
DP_DEBUG("bw_code=%d, lane_count=%d\n",
DP_INFO("bw_code=%d, lane_count=%d\n",
ctrl->link->link_params.bw_code,
ctrl->link->link_params.lane_count);
@@ -1375,6 +1561,8 @@ static void dp_ctrl_off(struct dp_ctrl *dp_ctrl)
if (!ctrl->power_on)
return;
DP_ENTER("power_on: %d\n", ctrl->power_on);
ctrl->catalog->fec_config(ctrl->catalog, false);
dp_ctrl_configure_source_link_params(ctrl, false);
dp_ctrl_state_ctrl(ctrl, 0);
@@ -1500,6 +1688,9 @@ struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in)
ctrl->aux = in->aux;
ctrl->link = in->link;
ctrl->catalog = in->catalog;
#if defined(CONFIG_SECDP)
ctrl->sec = in->sec;
#endif
ctrl->pll = in->pll;
ctrl->dev = in->dev;
ctrl->mst_mode = false;

View File

@@ -46,9 +46,16 @@ struct dp_ctrl_in {
struct dp_power *power;
struct dp_catalog_ctrl *catalog;
struct dp_pll *pll;
#if defined(CONFIG_SECDP)
struct secdp_misc *sec;
#endif
};
struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in);
void dp_ctrl_put(struct dp_ctrl *dp_ctrl);
#if defined(CONFIG_SECDP)
bool secdp_get_link_train_status(struct dp_ctrl *dp_ctrl);
#endif
#endif /* _DP_CTRL_H_ */

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
*/
@@ -25,6 +25,9 @@
#include "dp_hpd.h"
#include "dp_mst_sim.h"
#include "dp_mst_drm.h"
#if defined(CONFIG_SECDP)
#include "secdp.h"
#endif
#define DEBUG_NAME "drm_dp"
@@ -163,6 +166,16 @@ static ssize_t dp_debug_write_edid(struct file *file,
if (copy_from_user(buf, user_buff, size))
goto bail;
#if defined(CONFIG_SECDP)
if (!strncmp(buf, "reset", 5)) {
DP_DEBUG("disable SIM_MODE_EDID\n");
dp_debug_disable_sim_mode(debug, DP_SIM_MODE_EDID);
kfree(buf);
mutex_unlock(&debug->lock);
return rc;
}
#endif
edid_size = size / char_to_nib;
buf_t = buf;
size = edid_size;
@@ -962,10 +975,8 @@ static ssize_t dp_debug_mst_sideband_mode_write(struct file *file,
/* Leave room for termination char */
len = min_t(size_t, count, SZ_8 - 1);
if (copy_from_user(buf, user_buff, len)) {
mutex_unlock(&debug->lock);
if (copy_from_user(buf, user_buff, len))
return -EFAULT;
}
buf[len] = '\0';
@@ -2408,15 +2419,6 @@ static int dp_debug_init_configs(struct dp_debug_private *debug,
}
static int dp_debug_init_fifo_error(struct dp_debug_private *debug,
struct dentry *dir)
{
int rc = 0;
debugfs_create_bool("fifo_error_enable", 0644, dir, &debug->parser->fifo_error_enable);
return rc;
}
static int dp_debug_init(struct dp_debug *dp_debug)
{
int rc = 0;
@@ -2491,10 +2493,6 @@ static int dp_debug_init(struct dp_debug *dp_debug)
if (rc)
goto error_remove_dir;
rc = dp_debug_init_fifo_error(debug, dir);
if (rc)
goto error_remove_dir;
return 0;
error_remove_dir:

View File

@@ -45,6 +45,7 @@
DP_ERR_V(fmt, ##__VA_ARGS__); \
} while (0)
#if !defined(CONFIG_SECDP)
#define DP_DEBUG_V(fmt, ...) \
do { \
if (drm_debug_enabled(DRM_UT_KMS)) \
@@ -80,6 +81,47 @@
#define DP_ERR_RATELIMITED_V(fmt, ...) \
pr_err_ratelimited("[drm:%s][msm-dp-err][%-4d]"fmt, __func__, \
current->pid, ##__VA_ARGS__)
#else
#define DP_DEBUG_V(fmt, ...) \
do { \
if (drm_debug_enabled(DRM_UT_KMS)) \
DRM_DEBUG("[msm-dp-debug][%-4d]"fmt, current->pid, \
##__VA_ARGS__); \
else \
pr_debug(fmt, ##__VA_ARGS__); \
} while (0)
#define DP_INFO_V(fmt, ...) \
do { \
if (drm_debug_enabled(DRM_UT_KMS)) \
DRM_INFO("[msm-dp-info][%-4d]"fmt, current->pid, \
##__VA_ARGS__); \
else \
pr_info(fmt, ##__VA_ARGS__); \
} while (0)
#define DP_WARN_V(fmt, ...) pr_warn(fmt, ##__VA_ARGS__)
#define DP_WARN_RATELIMITED_V(fmt, ...) pr_warn(fmt, ##__VA_ARGS__)
#define DP_ERR_V(fmt, ...) pr_err(fmt, ##__VA_ARGS__)
#define DP_ERR_RATELIMITED_V(fmt, ...) pr_err(fmt, ##__VA_ARGS__)
#endif
#if defined(CONFIG_SECDP_DBG)
extern bool secdp_func_trace;
#define DP_ENTER(fmt, ...) \
do { \
if (secdp_func_trace) \
pr_debug("+++ " pr_fmt(fmt), ##__VA_ARGS__); \
} while (0)
#define DP_LEAVE(fmt, ...) \
do { \
if (secdp_func_trace) \
pr_debug("--- " pr_fmt(fmt), ##__VA_ARGS__); \
} while (0)
#else
#define DP_ENTER(fmt, ...) do {} while (0)
#define DP_LEAVE(fmt, ...) do {} while (0)
#endif
#define DEFAULT_DISCONNECT_DELAY_MS 0
#define MAX_DISCONNECT_DELAY_MS 10000

File diff suppressed because it is too large Load Diff

View File

@@ -161,4 +161,11 @@ static inline int dp_display_mmrm_callback(struct mmrm_client_notifier_data *not
return 0;
}
#endif /* CONFIG_DRM_MSM_DP */
#if defined(CONFIG_SECDP_DBG)
int secdp_debug_set_ssc(struct secdp_misc *sec, bool onoff);
bool secdp_debug_get_ssc(struct secdp_misc *sec);
int secdp_show_hmd_dev(struct secdp_misc *sec, char *buf);
#endif/*CONFIG_SECDP_DBG*/
#endif /* _DP_DISPLAY_H_ */

View File

@@ -14,6 +14,9 @@
#include "dp_drm.h"
#include "dp_mst_drm.h"
#include "dp_debug.h"
#if defined(CONFIG_SECDP)
#include "secdp.h"
#endif
#define DP_MST_DEBUG(fmt, ...) DP_DEBUG(fmt, ##__VA_ARGS__)
@@ -85,6 +88,8 @@ static void dp_bridge_pre_enable(struct drm_bridge *drm_bridge)
return;
}
DP_ENTER("\n");
bridge = to_dp_bridge(drm_bridge);
dp = bridge->display;
@@ -133,6 +138,8 @@ static void dp_bridge_enable(struct drm_bridge *drm_bridge)
return;
}
DP_ENTER("\n");
bridge = to_dp_bridge(drm_bridge);
if (!bridge->connector) {
DP_ERR("Invalid connector\n");
@@ -163,6 +170,8 @@ static void dp_bridge_disable(struct drm_bridge *drm_bridge)
return;
}
DP_ENTER("\n");
bridge = to_dp_bridge(drm_bridge);
if (!bridge->connector) {
DP_ERR("Invalid connector\n");
@@ -202,6 +211,8 @@ static void dp_bridge_post_disable(struct drm_bridge *drm_bridge)
return;
}
DP_ENTER("\n");
bridge = to_dp_bridge(drm_bridge);
if (!bridge->connector) {
DP_ERR("Invalid connector\n");
@@ -381,7 +392,7 @@ int dp_connector_set_colorspace(struct drm_connector *connector,
sde_conn = to_sde_connector(connector);
if (!sde_conn->drv_panel) {
pr_err("invalid dp panel\n");
DP_ERR("invalid dp panel\n");
return -EINVAL;
}
@@ -601,7 +612,12 @@ int dp_connector_atomic_check(struct drm_connector *connector,
* to configure the new colorspace in HW
*/
if (c_state->colorspace != old_state->colorspace) {
#if !defined(CONFIG_SECDP)
DP_DEBUG("colorspace has been updated\n");
#else
DP_INFO("colorspace has been updated %d %d\n",
old_state->colorspace, c_state->colorspace);
#endif
sde_conn->colorspace_updated = true;
}
@@ -641,7 +657,7 @@ int dp_connector_get_modes(struct drm_connector *connector,
*/
rc = dp->get_modes(dp, sde_conn->drv_panel, dp_mode);
if (!rc) {
DP_WARN("failed to get DP sink modes, adding failsafe");
DP_WARN("failed to get DP sink modes, adding failsafe\n");
init_failsafe_mode(dp_mode);
}
if (dp_mode->timing.pixel_clk_khz) /* valid DP mode */

View File

@@ -19,6 +19,9 @@
#include <linux/of_gpio.h>
#include "dp_gpio_hpd.h"
#include "dp_debug.h"
#if defined(CONFIG_SECDP)
#include "secdp.h"
#endif
struct dp_gpio_hpd_private {
struct device *dev;
@@ -40,6 +43,8 @@ static int dp_gpio_hpd_connect(struct dp_gpio_hpd_private *gpio_hpd, bool hpd)
goto error;
}
DP_ENTER("\n");
gpio_hpd->base.hpd_high = hpd;
gpio_hpd->base.alt_mode_cfg_done = hpd;
gpio_hpd->base.hpd_irq = false;
@@ -71,6 +76,8 @@ static int dp_gpio_hpd_attention(struct dp_gpio_hpd_private *gpio_hpd)
goto error;
}
DP_ENTER("\n");
gpio_hpd->base.hpd_irq = true;
if (gpio_hpd->cb && gpio_hpd->cb->attention)
@@ -127,6 +134,8 @@ static void dp_gpio_hpd_work(struct work_struct *work)
struct dp_gpio_hpd_private, work);
int ret;
DP_ENTER("\n");
if (gpio_hpd->hpd) {
devm_free_irq(gpio_hpd->dev,
gpio_hpd->irq, gpio_hpd);
@@ -162,6 +171,8 @@ static int dp_gpio_hpd_simulate_connect(struct dp_hpd *dp_hpd, bool hpd)
goto error;
}
DP_ENTER("\n");
gpio_hpd = container_of(dp_hpd, struct dp_gpio_hpd_private, base);
dp_gpio_hpd_connect(gpio_hpd, hpd);
@@ -180,6 +191,8 @@ static int dp_gpio_hpd_simulate_attention(struct dp_hpd *dp_hpd, int vdo)
goto error;
}
DP_ENTER("\n");
gpio_hpd = container_of(dp_hpd, struct dp_gpio_hpd_private, base);
dp_gpio_hpd_attention(gpio_hpd);
@@ -238,6 +251,8 @@ struct dp_hpd *dp_gpio_hpd_get(struct device *dev,
goto error;
}
DP_ENTER("\n");
gpio_hpd = devm_kzalloc(dev, sizeof(*gpio_hpd), GFP_KERNEL);
if (!gpio_hpd) {
rc = -ENOMEM;
@@ -295,6 +310,8 @@ void dp_gpio_hpd_put(struct dp_hpd *dp_hpd)
if (!dp_hpd)
return;
DP_ENTER("\n");
gpio_hpd = container_of(dp_hpd, struct dp_gpio_hpd_private, base);
gpio_free(gpio_hpd->gpio_cfg.gpio);

View File

@@ -22,6 +22,10 @@
#include "sde_hdcp_2x.h"
#include "dp_debug.h"
#if defined(CONFIG_SECDP_DBG)
#include <linux/secdp_logger.h>
#endif
#define DP_INTR_STATUS2 (0x00000024)
#define DP_INTR_STATUS3 (0x00000028)
#define dp_read(offset) readl_relaxed((offset))
@@ -656,6 +660,10 @@ static int dp_hdcp2p2_read_rx_status(struct dp_hdcp2p2_ctrl *ctrl,
}
cp_irq = buf & BIT(2);
#ifdef SECDP_TEST_HDCP2P2_REAUTH
cp_irq = true;
DP_DEBUG("[HDCP2P2_REAUTH_TEST]\n");
#endif
DP_DEBUG("cp_irq=0x%x\n", cp_irq);
buf = 0;
@@ -668,6 +676,9 @@ static int dp_hdcp2p2_read_rx_status(struct dp_hdcp2p2_ctrl *ctrl,
goto error;
}
*rx_status = buf;
#ifdef SECDP_TEST_HDCP2P2_REAUTH
*rx_status = 0x8;
#endif
DP_DEBUG("rx_status=0x%x\n", *rx_status);
}
@@ -795,6 +806,15 @@ static bool dp_hdcp2p2_supported(void *input)
goto error;
}
#if defined(CONFIG_SECDP)
{
u32 i;
for (i = 0; i < DP_HDCP_RXCAPS_LENGTH; i++)
DP_DEBUG("rxcaps[%d] 0x%x\n", i, buf[i]);
}
#endif
DP_DEBUG("HDCP_CAPABLE=%lu\n", (buf[2] & BIT(1)) >> 1);
DP_DEBUG("VERSION=%d\n", buf[0]);

View File

@@ -16,6 +16,9 @@
#include "dp_lphw_hpd.h"
#include "dp_debug.h"
#include "dp_bridge_hpd.h"
#if defined(CONFIG_SECDP)
#include "secdp.h"
#endif
static void dp_hpd_host_init(struct dp_hpd *dp_hpd,
struct dp_catalog_hpd *catalog)
@@ -48,6 +51,8 @@ struct dp_hpd *dp_hpd_get(struct device *dev, struct dp_parser *parser,
{
struct dp_hpd *dp_hpd = NULL;
DP_ENTER("\n");
if (aux_bridge && (aux_bridge->flag & DP_AUX_BRIDGE_HPD)) {
dp_hpd = dp_bridge_hpd_get(dev, cb, aux_bridge);
if (!IS_ERR(dp_hpd)) {
@@ -100,6 +105,8 @@ void dp_hpd_put(struct dp_hpd *dp_hpd)
if (!dp_hpd)
return;
DP_ENTER("\n");
switch (dp_hpd->type) {
case DP_HPD_USBPD:
dp_usbpd_put(dp_hpd);

View File

@@ -27,6 +27,12 @@
#include "dp_link.h"
#include "dp_panel.h"
#include "dp_debug.h"
#if defined(CONFIG_SECDP)
#include "secdp.h"
#if defined(CONFIG_SECDP_BIGDATA)
#include <linux/secdp_bigdata.h>
#endif
#endif
enum dynamic_range {
DP_DYNAMIC_RANGE_RGB_VESA = 0x00,
@@ -903,6 +909,12 @@ static void dp_link_parse_sink_status_field(struct dp_link_private *link)
link->link_status);
if (len < DP_LINK_STATUS_SIZE)
DP_ERR("DP link status read failed\n");
#if defined(CONFIG_SECDP)
else
DP_INFO("[202h-207h] %02x-%02x-%02x-%02x-%02x-%02x\n",
link->link_status[0], link->link_status[1], link->link_status[2],
link->link_status[3], link->link_status[4], link->link_status[5]);
#endif
dp_link_parse_request(link);
}
@@ -965,6 +977,8 @@ static int dp_link_psm_config(struct dp_link *dp_link,
return -EINVAL;
}
DP_INFO("psm_config %d\n", enable);
link = container_of(dp_link, struct dp_link_private, dp_link);
if (enable)
@@ -1268,6 +1282,106 @@ static void dp_link_reset_data(struct dp_link_private *link)
link->dp_link.test_response = 0;
}
#if defined(CONFIG_SECDP)
bool secdp_get_poor_connection_status(struct dp_link *dp_link)
{
return dp_link->poor_connection;
}
void secdp_clear_link_status_cnt(struct dp_link *dp_link)
{
dp_link->poor_connection = false;
dp_link->status_update_cnt = 0;
}
/** refer to dp_link_parse_sink_status_field() */
void secdp_read_link_status(struct dp_link *dp_link)
{
struct dp_link_private *link;
int len = 0;
if (!dp_link) {
DP_ERR("invalid input\n");
goto exit;
}
link = container_of(dp_link, struct dp_link_private, dp_link);
if (!link) {
DP_ERR("link is null\n");
goto exit;
}
DP_ENTER("\n");
len = drm_dp_dpcd_read_link_status(link->aux->drm_aux,
link->link_status);
if (len < DP_LINK_STATUS_SIZE) {
DP_ERR("DP link status read failed %d\n", len);
goto exit;
}
DP_INFO("[202h-207h] %02x-%02x-%02x-%02x-%02x-%02x\n",
link->link_status[0], link->link_status[1], link->link_status[2],
link->link_status[3], link->link_status[4], link->link_status[5]);
exit:
return;
}
/**
* @retval true if connection is stable
* @retval false if connection is unstable(poor)
*/
bool secdp_check_link_stable(struct dp_link *dp_link)
{
bool stable = false;
struct dp_link_private *link;
if (!dp_link) {
DP_ERR("invalid input\n");
goto exit;
}
link = container_of(dp_link, struct dp_link_private, dp_link);
if (!link) {
DP_ERR("link is null\n");
goto exit;
}
if (!(get_link_status(link->link_status, DP_LANE_ALIGN_STATUS_UPDATED) &
DP_INTERLANE_ALIGN_DONE)) {
DP_ERR("[204h] interlane_align_done is zero!\n");
goto exit;
}
stable = true;
exit:
if (stable)
DP_DEBUG("DP connection is stable!\n");
#if defined(CONFIG_SECDP_BIGDATA)
if (!stable)
secdp_bigdata_inc_error_cnt(ERR_INF_IRQHPD);
#endif
return stable;
}
#if defined(CONFIG_SECDP_DBG)
int secdp_show_link_param(struct dp_link *dp_link, char *buf)
{
int rc = 0;
rc += scnprintf(buf + rc, PAGE_SIZE - rc,
"v_level: %u\np_level: %u\nlane_cnt: %u\nbw_code: 0x%x\n",
dp_link->phy_params.v_level,
dp_link->phy_params.p_level,
dp_link->link_params.lane_count,
dp_link->link_params.bw_code);
return rc;
}
#endif/*CONFIG_SECDP_DBG*/
#endif/*CONFIG_SECDP*/
/**
* dp_link_process_request() - handle HPD IRQ transition to HIGH
* @link: pointer to link module data
@@ -1292,6 +1406,14 @@ static int dp_link_process_request(struct dp_link *dp_link)
dp_link_parse_sink_status_field(link);
#if defined(CONFIG_SECDP)
if (secdp_get_power_status(link->dev) && !secdp_check_link_stable(dp_link)) {
dp_link->status_update_cnt++;
DP_INFO("[link_request] status_update_cnt %d\n",
dp_link->status_update_cnt);
secdp_link_backoff_start();
}
#endif
if (dp_link_is_test_edid_read(link)) {
dp_link->sink_request |= DP_TEST_LINK_EDID_READ;
goto exit;

View File

@@ -148,6 +148,11 @@ struct dp_link {
u32 sink_request;
u32 test_response;
#if defined(CONFIG_SECDP)
bool poor_connection;
int status_update_cnt;
#endif
struct dp_link_sink_count sink_count;
struct dp_link_test_video test_video;
struct dp_link_test_audio test_audio;
@@ -223,6 +228,9 @@ static inline u32 dp_link_bit_depth_to_bpp(u32 tbd)
break;
case DP_TEST_BIT_DEPTH_UNKNOWN:
default:
#if defined(CONFIG_SECDP)
pr_debug("%s: tbd(%d)\n", __func__, tbd);
#endif
bpp = 0;
}
@@ -245,4 +253,15 @@ struct dp_link *dp_link_get(struct device *dev, struct dp_aux *aux, u32 dp_core_
*/
void dp_link_put(struct dp_link *dp_link);
#if defined(CONFIG_SECDP)
void secdp_clear_link_status_cnt(struct dp_link *dp_link);
void secdp_read_link_status(struct dp_link *dp_link);
bool secdp_check_link_stable(struct dp_link *dp_link);
bool secdp_get_poor_connection_status(struct dp_link *dp_link);
#if defined(CONFIG_SECDP_DBG)
int secdp_show_link_param(struct dp_link *dp_link, char *buf);
#endif/*CONFIG_SECDP_DBG*/
#endif/*CONFIG_SECDP*/
#endif /* _DP_LINK_H_ */

View File

@@ -52,6 +52,9 @@
#include "dp_drm.h"
#include "dp_debug.h"
#include "dp_parser.h"
#if defined(CONFIG_SECDP)
#include "secdp.h"
#endif
#define DP_MST_DEBUG(fmt, ...) DP_DEBUG(fmt, ##__VA_ARGS__)
#define DP_MST_INFO(fmt, ...) DP_INFO(fmt, ##__VA_ARGS__)
@@ -61,7 +64,6 @@
#define MAX_DP_MST_DRM_ENCODERS 2
#define MAX_DP_MST_DRM_BRIDGES 2
#define HPD_STRING_SIZE 30
#define UPDATE_PAYLOAD_RETRY_CNT 3
#define DP_MST_CONN_ID(bridge) ((bridge)->connector ? \
(bridge)->connector->base.id : 0)
@@ -343,8 +345,10 @@ static int dp_mst_calc_pbn_mode(struct dp_display_mode *dp_mode)
pbn = drm_fixp2int(pbn_fp);
pinfo->pbn = pbn;
#if !defined(CONFIG_SECDP)
DP_DEBUG_V("pbn before overhead:%d pbn final:%d, bpp:%d\n", pinfo->pbn_no_overhead, pbn,
bpp);
#endif
return pbn;
}
@@ -657,7 +661,7 @@ static int _dp_mst_bridge_pre_enable_part1(struct dp_mst_bridge *dp_bridge)
struct drm_dp_mst_atomic_payload *payload;
#endif
bool ret;
int pbn, slots, i;
int pbn, slots;
int rc = 0;
DP_MST_DEBUG_V("enter\n");
@@ -690,18 +694,8 @@ static int _dp_mst_bridge_pre_enable_part1(struct dp_mst_bridge *dp_bridge)
drm_dp_mst_update_slots(mst_state, DP_CAP_ANSI_8B10B);
for (i = 0; i < UPDATE_PAYLOAD_RETRY_CNT; i++) {
rc = mst->mst_fw_cbs->update_payload_part1(&mst->mst_mgr,
mst_state, payload);
if (rc && (i != UPDATE_PAYLOAD_RETRY_CNT-1)) {
DP_WARN("payload allocation failure for conn:%d, retries:%d\n",
DP_MST_CONN_ID(dp_bridge), i+1);
msleep(100);
} else {
break;
}
}
rc = mst->mst_fw_cbs->update_payload_part1(&mst->mst_mgr,
mst_state, payload);
if (rc) {
DP_ERR("payload allocation failure for conn:%d\n", DP_MST_CONN_ID(dp_bridge));
goto end;
@@ -1258,6 +1252,9 @@ static int dp_mst_connector_get_modes(struct drm_connector *connector,
struct dp_display_mode *dp_mode = NULL;
int rc = 0;
struct edid *edid = NULL;
#if defined(CONFIG_SECDP)
u8 i;
#endif
DP_MST_DEBUG_V("enter:\n");
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, connector->base.id);
@@ -1280,6 +1277,14 @@ static int dp_mst_connector_get_modes(struct drm_connector *connector,
mutex_lock(&mst->edid_lock);
c_conn->cached_edid = edid;
#if defined(CONFIG_SECDP)
for (i = 0; i <= edid->extensions; i++) {
print_hex_dump(KERN_DEBUG, "EDID: ", DUMP_PREFIX_NONE, 16, 1,
edid + i, EDID_LENGTH, false);
secdp_logger_hex_dump(edid + i, "EDID:", EDID_LENGTH);
}
#endif
duplicate_edid:
edid = drm_edid_duplicate(c_conn->cached_edid);
@@ -1405,8 +1410,10 @@ int dp_mst_connector_get_mode_info(struct drm_connector *connector,
rc = dp_connector_get_mode_info(connector, drm_mode, NULL, mode_info,
display, avail_res);
#if !defined(CONFIG_SECDP)
DP_MST_DEBUG_V("mst connector:%d get mode info. rc:%d\n",
connector->base.id, rc);
#endif
DP_MST_DEBUG_V("exit:\n");
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, connector->base.id);

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,6 @@
#include "sde_edid_parser.h"
#include "sde_connector.h"
#include "msm_drv.h"
#include "dp_panel_tu.h"
#define DP_RECEIVER_DSC_CAP_SIZE 15
#define DP_RECEIVER_FEC_STATUS_SIZE 3
@@ -94,6 +93,9 @@ struct dp_panel_in {
struct drm_connector *connector;
struct dp_panel *base_panel;
struct dp_parser *parser;
#if defined(CONFIG_SECDP)
struct secdp_misc *sec;
#endif
};
struct dp_dsc_caps {
@@ -132,6 +134,13 @@ struct dp_panel {
u32 link_bw_code;
u32 max_supported_bpp;
#if defined(CONFIG_SECDP)
bool tbox;
u8 monitor_name[14]; /* max 13 chars + null */
u32 dsp_type;
struct dp_panel_info max_timing_info;
#endif
/* By default, stream_id is assigned to DP_INVALID_STREAM.
* Client sets the stream id value using set_stream_id interface.
*/
@@ -208,6 +217,21 @@ struct dp_panel {
void (*set_lttpr_mode)(struct dp_panel *dp_panel, bool is_transparent);
};
struct dp_tu_calc_input {
u64 lclk; /* 162, 270, 540 and 810 */
u64 pclk_khz; /* in KHz */
u64 hactive; /* active h-width */
u64 hporch; /* bp + fp + pulse */
int nlanes; /* no.of.lanes */
int bpp; /* bits */
int pixel_enc; /* 444, 420, 422 */
int dsc_en; /* dsc on/off */
int async_en; /* async mode */
int fec_en; /* fec */
int compress_ratio; /* 2:1 = 200, 3:1 = 300, 3.75:1 = 375 */
int num_of_dsc_slices; /* number of slices per line */
};
struct dp_vc_tu_mapping_table {
u32 vic;
u8 lanes;
@@ -251,5 +275,12 @@ static inline bool is_lane_count_valid(u32 lane_count)
struct dp_panel *dp_panel_get(struct dp_panel_in *in);
void dp_panel_put(struct dp_panel *dp_panel);
void dp_panel_get_dto_params(u32 pclk_factor, struct dp_dsc_dto_params *dsc_params);
void dp_panel_calc_tu_test(struct dp_tu_calc_input *in,
struct dp_vc_tu_mapping_table *tu_table);
#define SECDP_OPTIMAL_LINK_RATE /* use optimum link_rate, not max link_rate */
#if defined(CONFIG_SECDP) && defined(SECDP_OPTIMAL_LINK_RATE)
u32 secdp_gen_link_clk(struct dp_panel *dp_panel);
#endif
#endif /* _DP_PANEL_H_ */

File diff suppressed because it is too large Load Diff

View File

@@ -1,98 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
*/
#ifndef _DP_PANEL_TU_H_
#define _DP_PANEL_TU_H_
#include <linux/kernel.h>
#include <linux/types.h>
struct dp_tu_calc_input {
u64 lclk; /* 162, 270, 540 and 810 MHz*/
u64 pclk_khz; /* in KHz */
u64 hactive; /* active h-width */
u64 hporch; /* bp + fp + pulse */
int nlanes; /* no.of.lanes */
int bpp; /* bits */
int pixel_enc; /* 444, 420, 422 */
int dsc_en; /* dsc on/off */
int async_en; /* async mode */
int fec_en; /* fec */
int compress_ratio; /* 2:1 = 200, 3:1 = 300, 3.75:1 = 375 */
int num_of_dsc_slices; /* number of slices per line */
s64 comp_bpp; /* compressed bpp = uncomp_bpp / compression_ratio */
int ppc_div_factor; /* pass in ppc mode 2/4 */
};
struct dp_tu_calc_output {
u32 valid_boundary_link;
u32 delay_start_link;
bool boundary_moderation_en;
u32 valid_lower_boundary_link;
u32 upper_boundary_count;
u32 lower_boundary_count;
u32 tu_size_minus1;
};
struct dp_tu_compression_info {
u32 src_bpp;
u32 tgt_bpp;
u16 pic_width;
int pclk_per_line;
int ppc_div_factor;
};
struct dp_tu_dhdr_info {
u32 mdp_clk;
u32 lclk;
u32 pclk;
u32 h_active;
u32 nlanes;
s64 mst_target_sc;
bool mst_en;
bool fec_en;
};
struct dp_tu_mst_rg_in {
u64 lclk_khz;
u64 pclk_khz;
u8 nlanes;
u8 src_bpp;
u8 tgt_bpp;
bool fec_en;
bool dsc_en;
s64 fec_overhead_fp;
s64 dsc_overhead_fp;
u64 pbn;
/*
* margin_ovrd: SW control for handling margin
* Only enabled when running 4k60fps displays on 2x MST stream
*/
bool margin_ovrd;
};
struct dp_tu_mst_rg_out {
u64 min_sc_fp;
u64 max_sc_fp;
u64 target_sc_fp;
u64 ts_int;
u32 x_int;
u32 y_frac_enum;
};
struct dp_dsc_dto_params {
u32 tgt_bpp;
u32 src_bpp;
u32 num;
u32 denom;
};
void dp_tu_calculate(struct dp_tu_calc_input *in, struct dp_tu_calc_output *tu_table);
u32 dp_tu_dsc_get_num_extra_pclk(u32 pclk_factor, struct dp_tu_compression_info *input);
u32 dp_tu_dhdr_pkt_limit(struct dp_tu_dhdr_info *input);
void dp_tu_mst_rg_calc(struct dp_tu_mst_rg_in *in, struct dp_tu_mst_rg_out *out);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021-2024, Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2021-2023, Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
*/
@@ -219,6 +219,42 @@ static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type)
}
}
#if defined(CONFIG_SECDP)
enum secdp_phy_pre_emphasis_type {
PHY_PRE_EMP0, /* 0 db */
PHY_PRE_EMP1, /* 3.5 db */
PHY_PRE_EMP2, /* 6.0 db */
PHY_PRE_EMP3, /* 9.5 db */
// MAX_PRE_EMP_LEVELS,
};
enum secdp_phy_voltage_type {
PHY_VOLTAGE_SWING0, /* 0.4 v */
PHY_VOLTAGE_SWING1, /* 0.6 v */
PHY_VOLTAGE_SWING2, /* 0.8 v */
PHY_VOLTAGE_SWING3, /* 1.2 v, optional */
MAX_VOLTAGE_LEVELS,
};
#if IS_ENABLED(CONFIG_COMBO_REDRIVER_PS5169)
enum secdp_ps5169_pre_emphasis_type {
PHY_PS5169_EMP0, /* 0 db */
PHY_PS5169_EMP1, /* 3.5 db */
PHY_PS5169_EMP2, /* 6.0 db */
PHY_PS5169_EMP3, /* 9.5 db */
MAX_PS5169_EMP_LEVELS,
};
enum secdp_PS5169_voltage_type {
PHY_PS5169_SWING0, /* 0.4 v */
PHY_PS5169_SWING1, /* 0.6 v */
PHY_PS5169_SWING2, /* 0.8 v */
PHY_PS5169_SWING3, /* 1.2 v, optional */
MAX_PS5169_SWING_LEVELS,
};
#endif/*CONFIG_COMBO_REDRIVER_PS5169*/
#endif/*CONFIG_SECDP*/
/**
* struct dp_parser - DP parser's data exposed to clients
*
@@ -237,7 +273,6 @@ static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type)
* @dsc_feature_enable: DSC feature enable status
* @fec_feature_enable: FEC feature enable status
* @dsc_continuous_pps: PPS sent every frame by HW
* @fifo_error_enable : fifo error enable status
* @has_widebus: widebus (2PPC) feature eanble status
*@mst_fixed_port: mst port_num reserved for fixed topology
* @qos_cpu_mask: CPU mask for QOS
@@ -273,7 +308,6 @@ struct dp_parser {
bool dsc_feature_enable;
bool fec_feature_enable;
bool dsc_continuous_pps;
bool fifo_error_enable;
bool has_widebus;
bool has_4ppc_enabled;
bool gpio_aux_switch;
@@ -291,6 +325,35 @@ struct dp_parser {
u8 *pre_emp_hbr_rbr;
bool valid_lt_params;
#if defined(CONFIG_SECDP)
struct regulator *aux_pullup_vreg;
bool cc_dir_inv; /* CC_DIR is inversed, e.g, T865 */
bool aux_sel_inv; /* inverse control of AUX_SEL e.g, D2Xq hwid 01,02 */
int use_redrv; /* ptn36502 needs NOT AUX switch SEL control */
int dex_dft_res; /* DeX default resolution, e.g, HG950 */
bool prefer_support; /* true if prefer resolution has high priority */
bool mrr_fps_nolimit; /* true if mirroring refresh rate has no limit */
bool rf_tx_backoff; /* true if it RF TX Backoff is supported, for SHELL-less type connector */
bool mst_support; /* true if MST is supported */
u8 *preshoot0_hbr2_3;
u8 *preshoot1_hbr2_3;
u8 *preshoot0_rbr_hbr;
u8 *preshoot1_rbr_hbr;
bool valid_preshoot_params;
#if IS_ENABLED(CONFIG_COMBO_REDRIVER_PS5169)
u8 ps5169_rbr_eq0[MAX_PS5169_SWING_LEVELS][MAX_PS5169_EMP_LEVELS];
u8 ps5169_rbr_eq1[MAX_PS5169_SWING_LEVELS][MAX_PS5169_EMP_LEVELS];
u8 ps5169_hbr_eq0[MAX_PS5169_SWING_LEVELS][MAX_PS5169_EMP_LEVELS];
u8 ps5169_hbr_eq1[MAX_PS5169_SWING_LEVELS][MAX_PS5169_EMP_LEVELS];
u8 ps5169_hbr2_eq0[MAX_PS5169_SWING_LEVELS][MAX_PS5169_EMP_LEVELS];
u8 ps5169_hbr2_eq1[MAX_PS5169_SWING_LEVELS][MAX_PS5169_EMP_LEVELS];
u8 ps5169_hbr3_eq0[MAX_PS5169_SWING_LEVELS][MAX_PS5169_EMP_LEVELS];
u8 ps5169_hbr3_eq1[MAX_PS5169_SWING_LEVELS][MAX_PS5169_EMP_LEVELS];
#endif/*CONFIG_COMBO_REDRIVER_PS5169*/
#endif/*CONFIG_SECDP*/
int (*parse)(struct dp_parser *parser);
struct dp_io_data *(*get_io)(struct dp_parser *parser, char *name);
void (*get_io_buf)(struct dp_parser *parser, char *name);
@@ -331,4 +394,132 @@ struct dp_parser *dp_parser_get(struct platform_device *pdev);
* @parser: pointer to the parser's data.
*/
void dp_parser_put(struct dp_parser *parser);
#if defined(CONFIG_SECDP)
enum secdp_hw_preshoot_t {
DP_HW_PRESHOOT_0,
DP_HW_PRESHOOT_1,
DP_HW_PRESHOOT_MAX,
};
static inline char *secdp_preshoot_to_string(int hw)
{
switch (hw) {
case DP_HW_PRESHOOT_0:
return DP_ENUM_STR(DP_HW_PRESHOOT_0);
case DP_HW_PRESHOOT_1:
return DP_ENUM_STR(DP_HW_PRESHOOT_1);
default:
return "unknown";
}
}
#if IS_ENABLED(CONFIG_COMBO_REDRIVER_PS5169)
enum secdp_ps5169_eq_t {
DP_PS5169_EQ0,
DP_PS5169_EQ1,
DP_PS5169_EQ_MAX,
};
enum secdp_ps5169_link_rate_t {
DP_PS5169_RATE_RBR,
DP_PS5169_RATE_HBR,
DP_PS5169_RATE_HBR2,
DP_PS5169_RATE_HBR3,
DP_PS5169_RATE_MAX,
};
#endif/*CONFIG_COMBO_REDRIVER_PS5169*/
#endif/*CONFIG_SECDP*/
#if defined(CONFIG_SECDP_DBG)
enum secdp_link_rate_t {
DP_LR_NONE = 0x0,
DP_LR_HBR_RBR = 0x1,
DP_LR_HBR2_3 = 0x2,
};
static inline char *secdp_link_rate_to_string(int lr)
{
switch (lr) {
case DP_LR_HBR_RBR:
return DP_ENUM_STR(DP_LR_HBR_RBR);
case DP_LR_HBR2_3:
return DP_ENUM_STR(DP_LR_HBR2_3);
default:
return "unknown";
}
}
enum secdp_phy_param_t {
DP_PARAM_NONE = 0x0,
DP_PARAM_VX = 0x1, /* voltage swing */
DP_PARAM_PX = 0x2, /* pre-emphasis */
};
static inline char *secdp_phy_type_to_string(int param)
{
switch (param) {
case DP_PARAM_VX:
return DP_ENUM_STR(DP_PARAM_VX);
case DP_PARAM_PX:
return DP_ENUM_STR(DP_PARAM_PX);
default:
return "unknown";
}
}
/* voltage swing, pre-emphasis */
int secdp_parse_vxpx_show(struct dp_parser *parser, enum secdp_link_rate_t lr,
enum secdp_phy_param_t vxpx, char *buf);
int secdp_parse_vxpx_store(struct dp_parser *parser, enum secdp_link_rate_t lr,
enum secdp_phy_param_t vxpx, char *buf);
int secdp_show_phy_param(struct dp_parser *parser, char *buf);
int secdp_parse_preshoot_show(struct dp_parser *parser, enum secdp_link_rate_t lr,
enum secdp_hw_preshoot_t prst, char *buf);
int secdp_parse_preshoot_store(struct dp_parser *parser, enum secdp_link_rate_t lr,
enum secdp_hw_preshoot_t prst, char *buf);
int secdp_show_preshoot_param(struct dp_parser *parser, char *buf);
#if IS_ENABLED(CONFIG_COMBO_REDRIVER_PS5169)
static inline char *secdp_ps5169_eq_to_string(int hw)
{
switch (hw) {
case DP_PS5169_EQ0:
return DP_ENUM_STR(DP_PS5169_EQ0);
case DP_PS5169_EQ1:
return DP_ENUM_STR(DP_PS5169_EQ1);
default:
return "unknown";
}
}
static inline char *secdp_ps5169_rate_to_string(int hw)
{
switch (hw) {
case DP_PS5169_RATE_RBR:
return DP_ENUM_STR(DP_PS5169_RATE_RBR);
case DP_PS5169_RATE_HBR:
return DP_ENUM_STR(DP_PS5169_RATE_HBR);
case DP_PS5169_RATE_HBR2:
return DP_ENUM_STR(DP_PS5169_RATE_HBR2);
case DP_PS5169_RATE_HBR3:
return DP_ENUM_STR(DP_PS5169_RATE_HBR3);
default:
return "unknown";
}
}
int secdp_parse_ps5169_show(struct dp_parser *parser, enum secdp_ps5169_eq_t eq,
enum secdp_ps5169_link_rate_t link_rate, char *buf);
int secdp_parse_ps5169_store(struct dp_parser *parser, enum secdp_ps5169_eq_t eq,
enum secdp_ps5169_link_rate_t link_rate, char *buf);
int secdp_show_ps5169_param(struct dp_parser *parser, char *buf);
#endif/*CONFIG_COMBO_REDRIVER_PS5169*/
/* AUX configuration */
int secdp_aux_cfg_show(struct dp_parser *parser, char *buf);
int secdp_aux_cfg_store(struct dp_parser *parser, char *buf);
#endif/*CONFIG_SECDP_DBG*/
#endif

View File

@@ -9,6 +9,10 @@
#include <linux/of_platform.h>
#include "dp_debug.h"
#include "dp_pll.h"
#if defined(CONFIG_SECDP)
#include <linux/secdp_logger.h>
#endif
struct dp_pll_ver_spec_info {
u32 revision;

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021-2025, Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2021-2024, Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
*/
@@ -16,6 +16,28 @@
#include "dp_debug.h"
#include "dp_pll.h"
#if defined(CONFIG_SECDP)
#include <linux/regulator/consumer.h>
#include <linux/delay.h>
#include "secdp.h"
#if IS_ENABLED(CONFIG_SBU_SWITCH_CONTROL)// && IS_ENABLED(CONFIG_IF_CB_MANAGER)
#include <linux/usb/typec/manager/if_cb_manager.h>
#endif
#if IS_ENABLED(CONFIG_COMBO_REDRIVER_PTN36502)
#include <linux/combo_redriver/ptn36502.h>
#elif IS_ENABLED(CONFIG_COMBO_REDRIVER_PS5169)
#include <linux/combo_redriver/ps5169.h>
#endif
#define DP_LINK_BW_RBR 0x06
#define DP_LINK_BW_HBR 0x0a
#define DP_LINK_BW_HBR2 0x14 /* 1.2 */
#define DP_LINK_BW_HBR3 0x1e /* 1.4 */
#endif/*CONFIG_SECDP*/
#define DP_CLIENT_NAME_SIZE 20
#define XO_CLK_KHZ 19200
@@ -39,8 +61,32 @@ struct dp_power_private {
bool strm0_clks_parked;
bool strm1_clks_parked;
bool link_clks_parked;
#if defined(CONFIG_SECDP)
bool aux_pullup_on;
struct mutex dp_clk_lock;
#if IS_ENABLED(CONFIG_SBU_SWITCH_CONTROL)
enum sbu_switch_status sbu_status;
#endif
void (*redrv_onoff)(struct dp_power_private *power,
bool enable, int lane);
void (*redrv_aux_ctrl)(struct dp_power_private *power, int cross);
void (*redrv_notify_linkinfo)(struct dp_power_private *power,
u32 bw_code, u8 v_level, u8 p_level);
#endif
};
#if defined(CONFIG_SECDP)
#define DP_ENUM_STR(x) #x
enum redriver_switch_t {
REDRIVER_SWITCH_UNKNOWN = -1,
REDRIVER_SWITCH_RESET = 0,
REDRIVER_SWITCH_CROSS,
REDRIVER_SWITCH_THROU,
};
#endif
static int dp_power_regulator_init(struct dp_power_private *power)
{
int rc = 0, i = 0, j = 0;
@@ -89,6 +135,86 @@ static void dp_power_regulator_deinit(struct dp_power_private *power)
}
}
#if defined(CONFIG_SECDP)
/* factory use only
* ref: qusb_phy_enable_power()
*/
static int secdp_aux_pullup_vreg_enable(struct dp_power_private *power, bool on)
{
struct regulator *aux_pu_vreg;
int rc = 0;
if (!power || !power->parser) {
DP_ERR("error! power is null\n");
goto exit;
}
aux_pu_vreg = power->parser->aux_pullup_vreg;
if (!aux_pu_vreg) {
DP_ERR("error! vdda33 is null\n");
goto exit;
}
DP_ENTER("on:%d\n", on);
#define QUSB2PHY_3P3_VOL_MIN 3104000 /* uV */
#define QUSB2PHY_3P3_VOL_MAX 3105000 /* uV */
#define QUSB2PHY_3P3_HPM_LOAD 30000 /* uA */
if (on) {
if (power->aux_pullup_on) {
DP_INFO("already on\n");
goto exit;
}
rc = regulator_set_load(aux_pu_vreg, QUSB2PHY_3P3_HPM_LOAD);
if (rc < 0) {
DP_ERR("Unable to set HPM of vdda33:%d\n", rc);
goto exit;
}
rc = regulator_set_voltage(aux_pu_vreg, QUSB2PHY_3P3_VOL_MIN,
QUSB2PHY_3P3_VOL_MAX);
if (rc) {
DP_ERR("Unable to set voltage for vdda33:%d\n", rc);
goto put_vdda33_lpm;
}
rc = regulator_enable(aux_pu_vreg);
if (rc) {
DP_ERR("Unable to enable vdda33:%d\n", rc);
goto unset_vdd33;
}
DP_INFO("[AUX_PU] on success\n");
power->aux_pullup_on = true;
} else {
rc = regulator_disable(aux_pu_vreg);
if (rc)
DP_ERR("Unable to disable vdda33:%d\n", rc);
unset_vdd33:
rc = regulator_set_voltage(aux_pu_vreg, 0,
QUSB2PHY_3P3_VOL_MAX);
if (rc)
DP_ERR("Unable to set 0 voltage for vdda33:%d\n", rc);
put_vdda33_lpm:
rc = regulator_set_load(aux_pu_vreg, 0);
if (!rc)
DP_INFO("[AUX_PU] off success\n");
else
DP_ERR("Unable to set 0 HPM of vdda33:%d\n", rc);
power->aux_pullup_on = false;
}
exit:
return rc;
}
#endif
static void dp_power_phy_gdsc(struct dp_power *dp_power, bool on)
{
int rc = 0;
@@ -96,6 +222,8 @@ static void dp_power_phy_gdsc(struct dp_power *dp_power, bool on)
if (IS_ERR_OR_NULL(dp_power->dp_phy_gdsc))
return;
DP_ENTER("on:%d\n", on);
if (on)
rc = regulator_enable(dp_power->dp_phy_gdsc);
else
@@ -104,6 +232,8 @@ static void dp_power_phy_gdsc(struct dp_power *dp_power, bool on)
if (rc)
DP_ERR("Fail to %s dp_phy_gdsc regulator ret =%d\n",
on ? "enable" : "disable", rc);
DP_LEAVE("rc:%d\n", rc);
}
static int dp_power_regulator_ctrl(struct dp_power_private *power, bool enable)
@@ -144,6 +274,11 @@ static int dp_power_regulator_ctrl(struct dp_power_private *power, bool enable)
goto error;
}
}
#if defined(CONFIG_SECDP)
secdp_aux_pullup_vreg_enable(power, enable);
#endif
error:
return rc;
}
@@ -154,11 +289,17 @@ static int dp_power_pinctrl_set(struct dp_power_private *power, bool active)
struct pinctrl_state *pin_state;
struct dp_parser *parser;
#if IS_ENABLED(CONFIG_SBU_SWITCH_CONTROL)
return 0;
#endif
parser = power->parser;
if (IS_ERR_OR_NULL(parser->pinctrl.pin))
return 0;
DP_DEBUG("pinctrl:%d\n", active);
pin_state = active ? parser->pinctrl.state_active
: parser->pinctrl.state_suspend;
if (!IS_ERR_OR_NULL(pin_state)) {
@@ -168,6 +309,8 @@ static int dp_power_pinctrl_set(struct dp_power_private *power, bool active)
DP_ERR("can not set %s pins\n",
active ? "dp_active"
: "dp_sleep");
else
DP_DEBUG("%s success\n", active ? "dp_active" : "dp_sleep");
} else {
DP_ERR("invalid '%s' pinstate\n",
active ? "dp_active"
@@ -368,7 +511,11 @@ static int dp_power_clk_set_rate(struct dp_power_private *power,
{
int rc = 0;
struct dss_module_power *mp;
#if defined(CONFIG_SECDP)
static bool prev[DP_MAX_PM];
mutex_lock(&power->dp_clk_lock);
#endif
if (!power) {
DP_ERR("invalid power data\n");
rc = -EINVAL;
@@ -377,6 +524,13 @@ static int dp_power_clk_set_rate(struct dp_power_private *power,
mp = &power->parser->mp[module];
#if defined(CONFIG_SECDP)
if (prev[module] == enable) {
DP_DEBUG("%d clk already %s\n", module, enable ? "enabled" : "disabled");
goto exit;
}
#endif
if (enable) {
rc = msm_dss_clk_set_rate(mp->clk_config, mp->num_clk);
if (rc) {
@@ -398,7 +552,14 @@ static int dp_power_clk_set_rate(struct dp_power_private *power,
dp_power_park_module(power, module);
}
#if defined(CONFIG_SECDP)
prev[module] = enable;
#endif
exit:
#if defined(CONFIG_SECDP)
mutex_unlock(&power->dp_clk_lock);
#endif
return rc;
}
@@ -530,6 +691,8 @@ static int dp_power_request_gpios(struct dp_power_private *power)
return -EINVAL;
}
DP_ENTER("\n");
dev = &power->pdev->dev;
mp = &power->parser->mp[DP_CORE_PM];
@@ -541,11 +704,18 @@ static int dp_power_request_gpios(struct dp_power_private *power)
if (rc) {
DP_ERR("request %s gpio failed, rc=%d\n",
gpio_names[i], rc);
#if defined(CONFIG_SECDP)
if (!strncmp(gpio_names[i], "usbplug_cc",
strlen(gpio_names[i]))) {
DP_ERR("keep going\n");
continue;
}
#endif
goto error;
}
}
}
DP_LEAVE("\n");
return 0;
error:
for (i = 0; i < ARRAY_SIZE(gpio_names); i++) {
@@ -562,6 +732,7 @@ static bool dp_power_find_gpio(const char *gpio1, const char *gpio2)
return !!strnstr(gpio1, gpio2, strlen(gpio1));
}
#if !defined(CONFIG_SECDP)
static void dp_power_set_gpio(struct dp_power_private *power, bool flip)
{
int i;
@@ -587,10 +758,465 @@ static void dp_power_set_gpio(struct dp_power_private *power, bool flip)
config++;
}
}
#else
int secdp_power_request_gpios(struct dp_power *dp_power)
{
int rc;
struct dp_power_private *power;
if (!dp_power) {
DP_ERR("invalid power data\n");
rc = -EINVAL;
goto exit;
}
power = container_of(dp_power, struct dp_power_private, dp_power);
rc = dp_power_request_gpios(power);
exit:
return rc;
}
#if IS_ENABLED(CONFIG_COMBO_REDRIVER_PTN36502) || IS_ENABLED(CONFIG_COMBO_REDRIVER_PS5169)
static inline char *secdp_redriver_switch_to_string(int event)
{
switch (event) {
case REDRIVER_SWITCH_UNKNOWN:
return DP_ENUM_STR(REDRIVER_SWITCH_UNKNOWN);
case REDRIVER_SWITCH_RESET:
return DP_ENUM_STR(REDRIVER_SWITCH_RESET);
case REDRIVER_SWITCH_CROSS:
return DP_ENUM_STR(REDRIVER_SWITCH_CROSS);
case REDRIVER_SWITCH_THROU:
return DP_ENUM_STR(REDRIVER_SWITCH_THROU);
default:
return "unknown";
}
}
#endif
#if IS_ENABLED(CONFIG_COMBO_REDRIVER_PTN36502)
static void secdp_ptn36502_aux_ctrl(struct dp_power_private *power, int cross)
{
DP_DEBUG("cross:%s\n", secdp_redriver_switch_to_string(cross));
switch (cross) {
case REDRIVER_SWITCH_CROSS:
ptn36502_config(AUX_CROSS_MODE, 0);
break;
case REDRIVER_SWITCH_THROU:
ptn36502_config(AUX_THRU_MODE, 0);
break;
case REDRIVER_SWITCH_RESET:
ptn36502_config(SAFE_STATE, 0);
break;
default:
DP_INFO("unknown: %d\n", cross);
break;
}
}
static void secdp_ptn36502_onoff(struct dp_power_private *power, bool enable, int lane)
{
DP_DEBUG("en:%d, lane:%d\n", enable, lane);
if (enable) {
int val = -1;
if (lane == 2)
ptn36502_config(DP2_LANE_USB3_MODE, 1);
else if (lane == 4)
ptn36502_config(DP4_LANE_MODE, 1);
else {
DP_ERR("error! unknown lane: %d\n", lane);
goto exit;
}
val = ptn36502_i2c_read(Chip_ID);
DP_INFO("Chip_ID: 0x%x\n", val);
val = ptn36502_i2c_read(Chip_Rev);
DP_INFO("Chip_Rev: 0x%x\n", val);
} else {
ptn36502_config(SAFE_STATE, 0);
}
exit:
return;
}
static void secdp_ptn36502_notify_linkinfo(struct dp_power_private *power, u32 bw_code, u8 v_level, u8 p_level)
{
DP_ENTER("0x%x,%d,%d, do nothing!\n", bw_code, v_level, p_level);
//.TODO:
}
#elif IS_ENABLED(CONFIG_COMBO_REDRIVER_PS5169)
static void secdp_ps5169_aux_ctrl(struct dp_power_private *power, int cross)
{
/*
* ps5169 does not support AUX switching function.
* It needs to be done by AUX switch IC
*/
DP_ENTER("cross: %s, do nothing!\n",
secdp_redriver_switch_to_string(cross));
}
static void secdp_ps5169_onoff(struct dp_power_private *power, bool enable, int lane)
{
DP_DEBUG("en:%d, lane:%d\n", enable, lane);
if (enable) {
if (lane == 2)
ps5169_config(DP2_LANE_USB_MODE, 1);
else if (lane == 4)
ps5169_config(DP_ONLY_MODE, 1);
else {
DP_ERR("error! unknown lane: %d\n", lane);
goto exit;
}
DP_INFO("Chip_ID1: 0x%x, Chip_Rev1: 0x%x\n",
ps5169_i2c_read(Chip_ID1), ps5169_i2c_read(Chip_Rev1));
DP_INFO("Chip_ID2: 0x%x, Chip_Rev2: 0x%x\n",
ps5169_i2c_read(Chip_ID2), ps5169_i2c_read(Chip_Rev2));
} else {
ps5169_config(CLEAR_STATE, 0);
}
exit:
return;
}
static void secdp_ps5169_notify_linkinfo(struct dp_power_private *power,
u32 bw_code, u8 v_level, u8 p_level)
{
struct dp_parser *parser = power->parser;
u8 eq0, eq1;
switch (bw_code) {
case DP_LINK_BW_RBR:
eq0 = parser->ps5169_rbr_eq0[v_level][p_level];
eq1 = parser->ps5169_rbr_eq1[v_level][p_level];
break;
case DP_LINK_BW_HBR:
eq0 = parser->ps5169_hbr_eq0[v_level][p_level];
eq1 = parser->ps5169_hbr_eq1[v_level][p_level];
break;
case DP_LINK_BW_HBR2:
eq0 = parser->ps5169_hbr2_eq0[v_level][p_level];
eq1 = parser->ps5169_hbr2_eq1[v_level][p_level];
break;
case DP_LINK_BW_HBR3:
default:
eq0 = parser->ps5169_hbr3_eq0[v_level][p_level];
eq1 = parser->ps5169_hbr3_eq1[v_level][p_level];
break;
}
DP_DEBUG("bw:0x%x, v:%d, p:%d, eq0:0x%x, eq1:0x%x\n",
bw_code, v_level, p_level, eq0, eq1);
ps5169_notify_dplink(eq0, eq1);
}
#endif
void secdp_redriver_onoff(struct dp_power *dp_power,
bool enable, int lane)
{
struct dp_power_private *power;
power = container_of(dp_power, struct dp_power_private, dp_power);
if (power && power->redrv_onoff)
power->redrv_onoff(power, enable, lane);
}
void secdp_redriver_linkinfo(struct dp_power *dp_power,
u32 rate, u8 v_level, u8 p_level)
{
struct dp_power_private *power;
power = container_of(dp_power, struct dp_power_private, dp_power);
if (power && power->redrv_notify_linkinfo)
power->redrv_notify_linkinfo(power, rate, v_level, p_level);
}
static void secdp_redriver_register(struct dp_power_private *power)
{
int use_redrv;
if (!power || !power->parser) {
DP_ERR("invalid power!\n");
goto end;
}
use_redrv = power->parser->use_redrv;
DP_DEBUG("++ use_redrv(%d)\n", use_redrv);
if (!use_redrv) {
DP_INFO("nothing registered!\n");
goto end;
}
#if IS_ENABLED(CONFIG_COMBO_REDRIVER_PTN36502)
power->redrv_onoff = secdp_ptn36502_onoff;
power->redrv_aux_ctrl = secdp_ptn36502_aux_ctrl;
power->redrv_notify_linkinfo = secdp_ptn36502_notify_linkinfo;
DP_INFO("ptn36502 API registered!\n");
#elif IS_ENABLED(CONFIG_COMBO_REDRIVER_PS5169)
power->redrv_onoff = secdp_ps5169_onoff;
power->redrv_aux_ctrl = secdp_ps5169_aux_ctrl;
power->redrv_notify_linkinfo = secdp_ps5169_notify_linkinfo;
DP_INFO("ps5169 API registered!\n");
#endif
end:
return;
}
#if !IS_ENABLED(CONFIG_SBU_SWITCH_CONTROL)
/* turn on EDP_AUX switch
* ===================================================
* | usbplug-cc(dir) | orientation | flip | aux-sel |
* ===================================================
* | 0 | CC1 | false | 0 |
* | 1 | CC2 | true | 1 |
* ===================================================
*/
static void _secdp_power_set_gpio(struct dp_power_private *power, bool flip)
{
int i, rc = 0;
/*int dir = (flip == false) ? 0 : 1;*/
struct dss_module_power *mp = &power->parser->mp[DP_CORE_PM];
struct dss_gpio *config = mp->gpio_config;
struct dp_parser *parser = power->parser;
bool sel_val = false;
// DP_DEBUG("flip:%d, aux_inv:%d, redrv:%d\n",
// flip, parser->aux_sel_inv, parser->use_redrv);
if (parser->aux_sel_inv)
sel_val = true;
for (i = 0; i <= DP_GPIO_CMN_MAX; i++) {
if (gpio_is_valid(config->gpio)) {
if (dp_power_find_gpio(config->gpio_name, "aux-sel")) {
if (parser->use_redrv == SECDP_REDRV_PTN36502) {
rc = gpio_direction_output(config->gpio, 0);
} else {
/* SECDP_REDRV_PS5169 or SECDP_REDRV_NONE */
bool val = (bool)gpio_get_value(config->gpio);
if ((!flip && (val == sel_val)) ||
(flip && (val == !sel_val))) {
DP_DEBUG("%s: already %d %d, skip\n",
config->gpio_name, flip, val);
break;
}
rc = gpio_direction_output(config->gpio,
(!flip ? sel_val : !sel_val));
}
usleep_range(100, 120);
DP_INFO("[aux-sel] set %d (f:%d,i:%d,r:%d) %d\n",
gpio_get_value(config->gpio),
flip, parser->aux_sel_inv, parser->use_redrv, rc);
break;
}
}
config++;
}
usleep_range(100, 120);
config = mp->gpio_config;
for (i = 0; i <= DP_GPIO_CMN_MAX; i++) {
if (gpio_is_valid(config->gpio)) {
if (dp_power_find_gpio(config->gpio_name, "aux-en")) {
if (!gpio_get_value(config->gpio)) {
DP_DEBUG("%s: already enabled, skip\n",
config->gpio_name);
break;
}
rc = gpio_direction_output(config->gpio, 0);
usleep_range(100, 120);
DP_INFO("[aux-en] set %d (f:%d,i:%d,r:%d) %d\n",
gpio_get_value(config->gpio),
flip, parser->aux_sel_inv, parser->use_redrv, rc);
break;
}
}
config++;
}
}
/* turn off EDP_AUX switch */
static void _secdp_power_unset_gpio(struct dp_power_private *power)
{
int i, rc = 0;
struct dss_module_power *mp = &power->parser->mp[DP_CORE_PM];
struct dss_gpio *config = mp->gpio_config;
DP_ENTER("\n");
for (i = 0; i < mp->num_gpio; i++) {
if (gpio_is_valid(config->gpio)) {
if (dp_power_find_gpio(config->gpio_name, "aux-en")) {
if (gpio_get_value(config->gpio)) {
DP_DEBUG("%s: already disabled, skip\n",
config->gpio_name);
break;
}
rc = gpio_direction_output(config->gpio, 1);
usleep_range(100, 120);
DP_INFO("[aux-en] set %d, %d\n",
gpio_get_value(config->gpio), rc);
break;
}
}
config++;
}
config = mp->gpio_config;
for (i = 0; i < mp->num_gpio; i++) {
if (gpio_is_valid(config->gpio)) {
if (dp_power_find_gpio(config->gpio_name, "aux-sel")) {
if (!gpio_get_value(config->gpio)) {
DP_DEBUG("%s: already 0, skip\n",
config->gpio_name);
break;
}
rc = gpio_direction_output(config->gpio, 0);
usleep_range(100, 120);
DP_INFO("[aux-sel] set %d, %d\n",
gpio_get_value(config->gpio), rc);
break;
}
}
config++;
}
}
#else/*CONFIG_SBU_SWITCH_CONTROL*/
static void _secdp_sbu_switch_on(struct dp_power_private *power, bool flip)
{
int cc_sbu = !flip ? CLOSE_SBU_CC1_ACTIVE : CLOSE_SBU_CC2_ACTIVE;
if (power->sbu_status == cc_sbu)
return;
usbpd_sbu_switch_control(cc_sbu);
power->sbu_status = cc_sbu;
}
static void _secdp_sbu_switch_off(struct dp_power_private *power)
{
if (power->sbu_status == OPEN_SBU)
return;
usbpd_sbu_switch_control(OPEN_SBU);
power->sbu_status = OPEN_SBU;
}
#endif
void secdp_power_set_gpio(struct dp_power *dp_power, bool flip)
{
struct dp_power_private *power;
power = container_of(dp_power, struct dp_power_private, dp_power);
#if !IS_ENABLED(CONFIG_SBU_SWITCH_CONTROL)
_secdp_power_set_gpio(power, flip);
#else
_secdp_sbu_switch_on(power, flip);
#endif
}
void secdp_power_unset_gpio(struct dp_power *dp_power)
{
struct dp_power_private *power;
power = container_of(dp_power, struct dp_power_private, dp_power);
#if !IS_ENABLED(CONFIG_SBU_SWITCH_CONTROL)
_secdp_power_unset_gpio(power);
#else
_secdp_sbu_switch_off(power);
#endif
}
#if defined(CONFIG_SECDP_FACTORY_DPSWITCH_TEST)
static void secdp_redriver_aux_ctrl(struct dp_power_private *power,
int cross)
{
if (power && power->redrv_aux_ctrl)
power->redrv_aux_ctrl(power, cross);
}
/*
* @aux_sel : 1 or 0
*/
void secdp_config_gpios_factory(struct dp_power *dp_power, int aux_sel, bool on)
{
struct dp_power_private *power;
power = container_of(dp_power, struct dp_power_private, dp_power);
DP_DEBUG("sel:%d, on:%d\n", aux_sel, on);
if (on) {
secdp_aux_pullup_vreg_enable(power, true);
secdp_power_set_gpio(dp_power, aux_sel);
if (aux_sel == 1)
secdp_redriver_aux_ctrl(power, REDRIVER_SWITCH_CROSS);
else if (aux_sel == 0)
secdp_redriver_aux_ctrl(power, REDRIVER_SWITCH_THROU);
else
DP_ERR("unknown <%d>\n", aux_sel);
} else {
secdp_redriver_aux_ctrl(power, REDRIVER_SWITCH_RESET);
secdp_power_unset_gpio(dp_power);
secdp_aux_pullup_vreg_enable(power, false);
}
}
#endif
enum dp_hpd_plug_orientation secdp_get_plug_orientation(struct dp_power *dp_power)
{
struct dp_power_private *power;
struct dp_parser *parser;
struct dss_module_power *mp;
struct dss_gpio *config;
int i, dir;
power = container_of(dp_power, struct dp_power_private, dp_power);
parser = power->parser;
mp = &power->parser->mp[DP_CORE_PM];
config = mp->gpio_config;
for (i = 0; i < mp->num_gpio; i++) {
if (gpio_is_valid(config->gpio)) {
if (dp_power_find_gpio(config->gpio_name,
"usbplug-cc")) {
dir = gpio_get_value(config->gpio);
if (parser->cc_dir_inv)
dir = !dir;
DP_INFO("cc_dir_inv:%d, orientation:%s\n",
parser->cc_dir_inv, !dir ? "CC1" : "CC2");
if (dir == 0)
return ORIENTATION_CC1;
else /* if (dir == 1) */
return ORIENTATION_CC2;
}
}
config++;
}
/*cannot be here*/
return ORIENTATION_NONE;
}
#endif
static int dp_power_config_gpios(struct dp_power_private *power, bool flip,
bool enable)
{
#if !defined(CONFIG_SECDP)
int rc = 0, i;
struct dss_module_power *mp;
struct dss_gpio *config;
@@ -614,6 +1240,14 @@ static int dp_power_config_gpios(struct dp_power_private *power, bool flip,
}
}
}
#else
struct dp_power *dp_power = &power->dp_power;
if (enable)
secdp_power_set_gpio(dp_power, flip);
else
secdp_power_unset_gpio(dp_power);
#endif
return 0;
}
@@ -667,8 +1301,19 @@ static int dp_power_client_init(struct dp_power *dp_power,
dp_power->phandle = phandle;
dp_power->drm_dev = drm_dev;
#if defined(CONFIG_SECDP)
rc = dp_power_pinctrl_set(power, false);
if (rc) {
DP_ERR("failed to set pinctrl state\n");
goto error_client;
}
#endif
return 0;
#if defined(CONFIG_SECDP)
error_client:
dp_power_clk_init(power, false);
#endif
error_clk:
dp_power_regulator_deinit(power);
error_power:
@@ -708,12 +1353,10 @@ static int dp_power_park_clocks(struct dp_power *dp_power)
goto error;
}
if (power->parser->has_mst) {
rc = dp_power_park_module(power, DP_STREAM1_PM);
if (rc) {
DP_ERR("failed to park stream 1. err=%d\n", rc);
goto error;
}
rc = dp_power_park_module(power, DP_STREAM1_PM);
if (rc) {
DP_ERR("failed to park stream 1. err=%d\n", rc);
goto error;
}
rc = dp_power_park_module(power, DP_LINK_PM);
@@ -808,6 +1451,8 @@ static int dp_power_init(struct dp_power *dp_power, bool flip)
goto exit;
}
DP_ENTER("\n");
power = container_of(dp_power, struct dp_power_private, dp_power);
rc = dp_power_regulator_ctrl(power, true);
@@ -865,6 +1510,8 @@ static int dp_power_deinit(struct dp_power *dp_power)
goto exit;
}
DP_ENTER("\n");
power = container_of(dp_power, struct dp_power_private, dp_power);
if (power->link_clks_on)
@@ -955,6 +1602,11 @@ struct dp_power *dp_power_get(struct dp_parser *parser, struct dp_pll *pll)
DP_DEBUG("Optional GDSC regulator is missing\n");
}
#if defined(CONFIG_SECDP)
secdp_redriver_register(power);
mutex_init(&power->dp_clk_lock);
#endif
return dp_power;
error:
return ERR_PTR(rc);

View File

@@ -68,4 +68,20 @@ struct dp_power *dp_power_get(struct dp_parser *parser, struct dp_pll *pll);
* @power: pointer to the power module's data
*/
void dp_power_put(struct dp_power *power);
#if defined(CONFIG_SECDP)
enum dp_hpd_plug_orientation secdp_get_plug_orientation(struct dp_power *dp_power);
int secdp_power_request_gpios(struct dp_power *dp_power);
void secdp_power_set_gpio(struct dp_power *dp_power, bool flip);
void secdp_power_unset_gpio(struct dp_power *dp_power);
#if defined(CONFIG_SECDP_FACTORY_DPSWITCH_TEST)
void secdp_config_gpios_factory(struct dp_power *dp_power, int aux_sel, bool out_en);
#endif
void secdp_redriver_onoff(struct dp_power *dp_power, bool enable, int lane);
void secdp_redriver_linkinfo(struct dp_power *dp_power, u32 rate, u8 v_level, u8 p_level);
#endif/*CONFIG_SECDP*/
#endif /* _DP_POWER_H_ */

View File

@@ -0,0 +1,430 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2021, 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
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __SECDP_H
#define __SECDP_H
#if IS_ENABLED(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
#include <linux/usb/typec/manager/usb_typec_manager_notifier.h>
#endif
#if IS_ENABLED(CONFIG_PDIC_NOTIFIER)
#include <linux/usb/typec/common/pdic_notifier.h>
#endif
#include <linux/secdp_logger.h>
#include <linux/device.h>
#include <linux/pm_wakeup.h>
#include <linux/sched/clock.h>
/*#define MODEM_RF_INFO*/
#if defined(MODEM_RF_INFO) && IS_ENABLED(CONFIG_DEV_RIL_BRIDGE)
#include <linux/dev_ril_bridge.h>
struct rf_information {
u8 rat;
u32 band;
u32 arfcn;
} __packed;
#endif
#include "dp_hpd.h"
#include "dp_power.h"
#include "dp_panel.h"
#include "dp_catalog.h"
#include "dp_parser.h"
#include "dp_link.h"
#include "secdp_sysfs.h"
/*defined at kmodule/usb/typec/common/pdic_core.h*/
#define SAMSUNG_VENDOR_ID 0x04E8
#define DEXDOCK_PRODUCT_ID 0xA020 /* EE-MG950 DeX Station */
#define DEXPAD_PRODUCT_ID 0xA029 /* EE-M5100 DeX Pad */
#define DEXCABLE_PRODUCT_ID 0xA048 /* EE-I3100 DeX Cable */
#define HG950_PRODUCT_ID 0xA025 /* EE-HG950 HDMI Adapter */
#define MPA2_PRODUCT_ID 0xA027 /* EE-P5000 Multiport Adapter */
#define MPA3_PRODUCT_ID 0xA056 /* EE-P3200 Multiport Adapter */
#define MPA4_PRODUCT_ID 0xA066 /* EE-P5400 Multiport Adapter */
#define SECDP_ENUM_STR(x) #x
#define SECDP_USB_CONCURRENCY
#define SECDP_USE_WAKELOCK
#define SECDP_MAX_HBR2
/*#define SECDP_AUDIO_CTS*/
/*#define SECDP_HDCP_DISABLE*/
/*#define SECDP_TEST_HDCP2P2_REAUTH*/
/*#define NOT_SUPPORT_DEX_RES_CHANGE*/
#define REMOVE_YUV420_AT_PREFER
#define SYSFS_BW_CODE
#define DPCD_IEEE_OUI 0x500
#define DPCD_DEVID_STR 0x503
#define LEN_BRANCH_REV 3
#define DPCD_BRANCH_HW_REV 0x509
#define DPCD_BRANCH_SW_REV_MAJOR 0x50A
#define DPCD_BRANCH_SW_REV_MINOR 0x50B
#define MAX_CNT_LINK_STATUS_UPDATE 4
#define MAX_CNT_HDCP_RETRY 10
/* MST: max resolution, max refresh rate, max pclk */
#define MST_MAX_COLS 3840
#define MST_MAX_ROWS 2160
#define MST_MAX_FPS 30
#define MST_MAX_PCLK 300000
#define PDIC_DP_NOTI_REG_DELAY 1000
/* displayport self test */
#if defined(CONFIG_SECDP_DBG)
#define SECDP_SELF_TEST
#endif
#ifdef SECDP_SELF_TEST
#define ST_EDID_SIZE 256
#define ST_ARG_CNT 20
#define ST_TEST_EXIT 555
enum {
ST_CLEAR_CMD,
ST_LANE_CNT,
ST_LINK_RATE,
ST_CONNECTION_TEST,
ST_HDCP_TEST,
ST_PREEM_TUN,
ST_VOLTAGE_TUN,
ST_MAX,
};
struct secdp_sef_test_item {
char cmd_str[20];
int arg[ST_ARG_CNT];
int arg_cnt;
char arg_str[100];
bool enabled;
void (*clear)(void);
};
int secdp_self_test_status(int cmd);
void secdp_self_test_start_reconnect(struct secdp_sysfs *dp_sysfs, void (*func)(struct secdp_misc *sec));
void secdp_self_test_start_hdcp_test(struct secdp_sysfs *dp_sysfs, void (*func_on)(void),
void (*func_off)(void));
//void secdp_self_register_clear_func(int cmd, void (*func)(void));
int *secdp_self_test_get_arg(int cmd);
#endif/*SECDP_SELF_TEST*/
/* monitor aspect ratio */
enum mon_aspect_ratio_t {
MON_RATIO_NA = -1,
MON_RATIO_3_2,
MON_RATIO_4_3,
MON_RATIO_5_3,
MON_RATIO_5_4,
MON_RATIO_10P5_9,
MON_RATIO_11_10,
MON_RATIO_16_9,
MON_RATIO_16_10, /* same with 8:5 */
MON_RATIO_21_9,
MON_RATIO_21_10,
MON_RATIO_32_9,
MON_RATIO_32_10,
};
static inline char *secdp_aspect_ratio_to_string(enum mon_aspect_ratio_t ratio)
{
switch (ratio) {
case MON_RATIO_3_2: return DP_ENUM_STR(MON_RATIO_3_2);
case MON_RATIO_4_3: return DP_ENUM_STR(MON_RATIO_4_3);
case MON_RATIO_5_3: return DP_ENUM_STR(MON_RATIO_5_3);
case MON_RATIO_5_4: return DP_ENUM_STR(MON_RATIO_5_4);
case MON_RATIO_10P5_9: return DP_ENUM_STR(MON_RATIO_10P5_9);
case MON_RATIO_11_10: return DP_ENUM_STR(MON_RATIO_11_10);
case MON_RATIO_16_9: return DP_ENUM_STR(MON_RATIO_16_9);
case MON_RATIO_16_10: return DP_ENUM_STR(MON_RATIO_16_10);
case MON_RATIO_21_9: return DP_ENUM_STR(MON_RATIO_21_9);
case MON_RATIO_21_10: return DP_ENUM_STR(MON_RATIO_21_10);
case MON_RATIO_32_9: return DP_ENUM_STR(MON_RATIO_32_9);
case MON_RATIO_32_10: return DP_ENUM_STR(MON_RATIO_32_10);
case MON_RATIO_NA: return DP_ENUM_STR(MON_RATIO_NA);
default: return "unknown";
}
}
/* adapter type : SST or MST */
enum secdp_adapter_t {
SECDP_ADT_UNKNOWN = -1,
SECDP_ADT_SST = 10,
SECDP_ADT_MST = 11,
};
/* dex supported resolutions */
enum dex_support_res_t {
DEX_RES_NOT_SUPPORT = 0,
DEX_RES_1600X900, /* HD+ */
DEX_RES_1920X1080, /* FHD */
DEX_RES_1920X1200, /* WUXGA */
DEX_RES_2560X1080, /* UW-UXGA */
DEX_RES_2560X1440, /* QHD */
DEX_RES_2560X1600, /* WQXGA */
DEX_RES_3440X1440, /* UW-QHD */
DEX_RES_END,
};
#define DEX_RES_DFT DEX_RES_1920X1080 /* DeX default timing */
#define DEX_DFT_COL 1920
#define DEX_DFT_ROW 1080
#define DEX_RES_MAX DEX_RES_3440X1440 /* DeX max timing */
#define DEX_MAX_COL 3440
#define DEX_MAX_ROW 1440
#define DEX_REFRESH_MIN 50
#define DEX_REFRESH_MAX 60
#define MIRROR_REFRESH_MIN 24
static inline char *secdp_dex_res_to_string(int res)
{
switch (res) {
case DEX_RES_NOT_SUPPORT:
return DP_ENUM_STR(DEX_RES_NOT_SUPPORT);
case DEX_RES_1600X900:
return DP_ENUM_STR(DEX_RES_1600X900);
case DEX_RES_1920X1080:
return DP_ENUM_STR(DEX_RES_1920X1080);
case DEX_RES_1920X1200:
return DP_ENUM_STR(DEX_RES_1920X1200);
case DEX_RES_2560X1080:
return DP_ENUM_STR(DEX_RES_2560X1080);
case DEX_RES_2560X1440:
return DP_ENUM_STR(DEX_RES_2560X1440);
case DEX_RES_2560X1600:
return DP_ENUM_STR(DEX_RES_2560X1600);
case DEX_RES_3440X1440:
return DP_ENUM_STR(DEX_RES_3440X1440);
default:
return "unknown";
}
}
enum DEX_STATUS {
DEX_DISABLED = 0,
DEX_ENABLED,
DEX_MODE_CHANGING,
};
/** redriver devices */
enum secdp_redrv_dev {
SECDP_REDRV_NONE = 0,
SECDP_REDRV_PTN36502, /* don't need AUX_SEL control */
SECDP_REDRV_PS5169, /* need AUX_SEL control */
};
static inline char *secdp_redrv_to_string(int res)
{
switch (res) {
case SECDP_REDRV_NONE:
return DP_ENUM_STR(SECDP_REDRV_NONE);
case SECDP_REDRV_PTN36502:
return DP_ENUM_STR(SECDP_REDRV_PTN36502);
case SECDP_REDRV_PS5169:
return DP_ENUM_STR(SECDP_REDRV_PS5169);
default:
return "unknown";
}
}
struct secdp_adapter {
uint ven_id;
uint prod_id;
char ieee_oui[4]; /* DPCD 500h ~ 502h */
char devid_str[7]; /* DPCD 503h ~ 508h */
char fw_ver[10]; /* firmware ver, 0:h/w, 1:s/w major, 2:s/w minor */
bool ss_genuine;
bool ss_legacy;
enum dex_support_res_t dex_type;
};
#define MON_NAME_LEN 14 /* monitor name length, max 13 chars + null */
#define MAX_NUM_HMD 32
#define DEX_TAG_HMD "HMD"
struct secdp_sink_dev {
uint ven_id; /* vendor id from PDIC */
uint prod_id; /* product id from PDIC */
char monitor_name[MON_NAME_LEN]; /* from EDID */
};
struct secdp_pdic_noti {
struct delayed_work reg_work;
struct notifier_block nb;
bool registered;
bool reset; /* true if PDIC or SSUSB get reset after DP connection */
};
struct secdp_prefer {
enum mon_aspect_ratio_t ratio;
bool exist; /* true if preferred resolution */
int hdisp; /* horizontal pixel of preferred resolution */
int vdisp; /* vertical pixel of preferred resolution */
int refresh; /* refresh rate of preferred resolution */
};
struct secdp_dex {
struct class *sysfs_class;
enum dex_support_res_t res; /* dex supported resolution */
enum DEX_STATUS prev; /* previously known as "dex_now" */
enum DEX_STATUS curr; /* previously known as "dex_en" */
int setting_ui; /* "dex_set", true if setting has Dex mode */
bool ignore_prefer_ratio; /* true if prefer ratio does not match to dex ratio */
bool adapter_check_skip;
/*
* 2 if resolution is changed during dex mode change.
* And once dex framework reads the dex_node_stauts using dex node,
* it's assigned to same value with curr.
*/
enum DEX_STATUS status; /* previously known as "dex_node_status" */
bool reconnecting; /* true if dex is under reconnecting */
};
struct secdp_display_timing {
u32 active_h;
u32 active_v;
u32 refresh_rate;
bool interlaced;
int clock; /* pixel clock, refer to "struct drm_display_mode" */
enum dex_support_res_t dex_res; /* dex supported resolution */
enum mon_aspect_ratio_t mon_ratio; /* monitor aspect ratio */
int supported; /* for unit test */
u64 total;
};
struct secdp_hmd {
struct secdp_sink_dev list[MAX_NUM_HMD]; /* supported HMD dev list */
struct mutex lock;
bool exist; /* true if connected sink is known HMD device */
};
struct secdp_hdcp {
struct delayed_work start_work;
int retry; /* count if dp link is unstable during hdcp */
u32 fail_cnt;
};
struct secdp_hpd {
struct delayed_work noti_work;
bool noti_deferred;
atomic_t val; /* 1 if hpd high, 0 if hpd low" */
bool prev_evt;
};
struct secdp_debug {
bool prefer_check_skip;
};
struct secdp_misc {
struct device *dev;
struct delayed_work link_status_work;
struct delayed_work link_backoff_work;
bool backoff_start;
struct delayed_work poor_discon_work;
struct device *uevent_dev;
#ifdef MODEM_RF_INFO
struct rf_information rf_info;
struct notifier_block modem_rfinfo_nb;
#endif
bool extdisp_off;
bool cable_connected; /* previously known as "cable_connected_phy" */
bool link_conf; /* previously known as "sec_link_conf" */
struct secdp_hpd hpd;
int mode_cnt;
struct secdp_adapter adapter;
struct secdp_pdic_noti pdic_noti;
struct secdp_display_timing prf_timing; /* preferred timing */
struct secdp_display_timing mrr_timing; /* max "mirror" timing */
struct secdp_display_timing dex_timing; /* max "dex" timing */
struct secdp_prefer prefer;
struct secdp_hdcp hdcp;
struct secdp_debug debug;
struct secdp_sysfs *sysfs;
struct secdp_dex dex;
struct secdp_hmd hmd;
struct completion dp_off_comp;
struct completion dp_discon_comp;
bool dp_disconnecting;
bool lpm_booting;
struct mutex notify_lock;
struct mutex attention_lock;
struct mutex notifier_lock;
atomic_t noti_status;
struct notifier_block reboot_nb;
bool reboot; /* true if rebooted or shutdown */
#ifdef SECDP_USE_WAKELOCK
struct wakeup_source *ws;
#endif
#ifdef SECDP_SELF_TEST
struct delayed_work self_test_reconnect_work;
struct delayed_work self_test_hdcp_test_work;
void (*self_test_reconnect_cb)(struct secdp_misc *sec);
void (*self_test_hdcp_on_cb)(void);
void (*self_test_hdcp_off_cb)(void);
#endif
};
bool secdp_adapter_check_parade(struct secdp_misc *sec);
bool secdp_adapter_check_ps176(struct secdp_misc *sec);
bool secdp_adapter_check_ps176_legacy(struct secdp_misc *sec);
bool secdp_adapter_check_realtek(struct secdp_misc *sec);
bool secdp_get_lpm_mode(struct secdp_misc *sec);
int secdp_send_deferred_hpd_noti(struct secdp_misc *sec);
int secdp_pdic_noti_register_ex(struct secdp_misc *sec, bool retry);
bool secdp_phy_reset_check(struct device *dev);
bool secdp_get_power_status(struct device *dev);
bool secdp_get_cable_status(struct device *dev);
int secdp_get_hpd_status(struct device *dev);
struct drm_connector *secdp_get_connector(struct secdp_misc *sec);
bool secdp_check_hmd_dev(struct secdp_misc *sec, const char *name_to_search);
int secdp_store_hmd_dev(struct secdp_misc *sec, char *buf, size_t len, int num);
void secdp_timing_init(struct secdp_misc *sec);
void secdp_extdisp_on(struct secdp_misc *sec);
void secdp_extdisp_off(struct secdp_misc *sec);
void secdp_reconnect(struct secdp_misc *sec);
bool secdp_check_reconnect(struct secdp_misc *sec);
void secdp_link_backoff_start(void);
void secdp_link_backoff_stop(void);
bool secdp_adapter_is_legacy(struct platform_device *pdev);
bool secdp_panel_hdr_supported(void);
#endif/*__SECDP_H*/

View File

@@ -0,0 +1,360 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2021 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* DP bigdata
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/secdp_bigdata.h>
#define EDID_BUF_SIZE 512
#define ERR_DATA_BUF_SIZE 1024
#define COL_NAME_SIZE 20
enum DP_ITEM_TYPE {
INT = 1,
HEX = 2,
STR = 4,
CHR = 8,
ERR = 16,
};
enum DP_STATUS {
STATUS_NO_CONNECTION,
STATUS_CONNECTION,
STATUS_ERROR_OCCURRED,
};
struct bd_item_info {
char name[COL_NAME_SIZE];
char type;
void *data;
int str_max_len;
};
struct bd_error_data {
int limit;
int count;
};
static char err_data_buf[ERR_DATA_BUF_SIZE];
static struct bd_item_info item_to_column[BD_ITEM_MAX];
static enum DP_STATUS dp_status;
static void secdp_bigdata_save_data(void);
static void secdp_bigdata_init_item(enum DP_BD_ITEM_LIST item, char *col_name, enum DP_ITEM_TYPE type, ...);
static void secdp_bigdata_init_error(enum DP_BD_ITEM_LIST item, char *col_name, int err_limit);
static void secdp_bigdata_save_item_int(enum DP_BD_ITEM_LIST item, int val);
static void secdp_bigdata_save_item_hex(enum DP_BD_ITEM_LIST item, int val);
static void secdp_bigdata_save_item_char(enum DP_BD_ITEM_LIST item, char val);
static void secdp_bigdata_save_item_str(enum DP_BD_ITEM_LIST item, char *val);
ssize_t _secdp_bigdata_show(const struct class *class,
const struct class_attribute *attr, char *buf)
{
if (dp_status == STATUS_NO_CONNECTION)
return 0;
return scnprintf(buf, ERR_DATA_BUF_SIZE, "%s", err_data_buf);
}
ssize_t _secdp_bigdata_store(const struct class *class,
const struct class_attribute *attr, const char *buf, size_t size)
{
if ((buf[0] | 0x20) == 'c')
dp_status = STATUS_NO_CONNECTION;
return size;
}
void secdp_bigdata_init(struct class *dp_class)
{
secdp_bigdata_init_item(BD_LINK_CONFIGURE, "LINK_CFG", CHR);
secdp_bigdata_init_item(BD_ADAPTER_HWID, "ADT_HWID", HEX);
secdp_bigdata_init_item(BD_ADAPTER_FWVER, "ADT_FWVER", HEX);
secdp_bigdata_init_item(BD_ADAPTER_TYPE, "ADT_TYPE", STR, 20);
secdp_bigdata_init_item(BD_MAX_LANE_COUNT, "MLANE_CNT", INT);
secdp_bigdata_init_item(BD_MAX_LINK_RATE, "MLINK_RATE", INT);
secdp_bigdata_init_item(BD_CUR_LANE_COUNT, "CLANE_CNT", INT);
secdp_bigdata_init_item(BD_CUR_LINK_RATE, "CLINK_RATE", INT);
secdp_bigdata_init_item(BD_HDCP_VER, "HDCP_VER", STR, 10);
secdp_bigdata_init_item(BD_ORIENTATION, "ORIENTATION", STR, 10);
secdp_bigdata_init_item(BD_RESOLUTION, "RESOLUTION", STR, 20);
secdp_bigdata_init_item(BD_EDID, "EDID", STR, EDID_BUF_SIZE);
secdp_bigdata_init_item(BD_ADT_VID, "ADT_VID", HEX);
secdp_bigdata_init_item(BD_ADT_PID, "ADT_PID", HEX);
secdp_bigdata_init_item(BD_DP_MODE, "DP_MODE", STR, 10);
secdp_bigdata_init_item(BD_SINK_NAME, "SINK_NAME", STR, 14);
secdp_bigdata_init_item(BD_AUD_CH, "AUD_CH", INT);
secdp_bigdata_init_item(BD_AUD_FREQ, "AUD_FREQ", INT);
secdp_bigdata_init_item(BD_AUD_BIT, "AUD_BIT", INT);
secdp_bigdata_init_error(ERR_AUX, "ERR_AUX", 3);
secdp_bigdata_init_error(ERR_EDID, "ERR_EDID", 1);
secdp_bigdata_init_error(ERR_HDCP_AUTH, "ERR_HDCP", 5);
secdp_bigdata_init_error(ERR_LINK_TRAIN, "ERR_LT_TRAIN", 1);
secdp_bigdata_init_error(ERR_INF_IRQHPD, "ERR_INF_IRQHPD", 10);
}
static void secdp_bigdata_init_item_str(enum DP_BD_ITEM_LIST item, char *val, int max_len)
{
kfree(item_to_column[item].data);
item_to_column[item].data = kzalloc(max_len + 1, GFP_KERNEL);
if (!item_to_column[item].data)
return;
item_to_column[item].str_max_len = max_len;
strlcpy((char *)item_to_column[item].data, val, max_len + 1);
}
static void secdp_bigdata_init_item(enum DP_BD_ITEM_LIST item, char *col_name, enum DP_ITEM_TYPE type, ...)
{
va_list vl;
va_start(vl, type);
strlcpy(item_to_column[item].name, col_name, COL_NAME_SIZE);
item_to_column[item].type = type;
switch (type) {
case INT:
case HEX:
secdp_bigdata_save_item_int(item, -1);
break;
case STR:
secdp_bigdata_init_item_str(item, "X", (int)va_arg(vl, int));
break;
case CHR:
secdp_bigdata_save_item_char(item, 'X');
break;
default:
break;
}
va_end(vl);
}
static void secdp_bigdata_init_error(enum DP_BD_ITEM_LIST item, char *col_name, int err_limit)
{
struct bd_error_data *err = kzalloc(sizeof(struct bd_error_data), GFP_KERNEL);
if (err)
err->limit = err_limit;
strlcpy(item_to_column[item].name, col_name, COL_NAME_SIZE);
item_to_column[item].type = ERR;
item_to_column[item].data = err;
}
static void secdp_bigdata_save_item_int(enum DP_BD_ITEM_LIST item, int val)
{
if (!item_to_column[item].data) {
item_to_column[item].data = kzalloc(sizeof(int), GFP_KERNEL);
if (!item_to_column[item].data)
return;
}
*((int *)item_to_column[item].data) = val;
}
static void secdp_bigdata_save_item_hex(enum DP_BD_ITEM_LIST item, int val)
{
secdp_bigdata_save_item_int(item, val);
}
static void secdp_bigdata_save_item_char(enum DP_BD_ITEM_LIST item, char val)
{
if (!item_to_column[item].data) {
item_to_column[item].data = kzalloc(sizeof(char), GFP_KERNEL);
if (!item_to_column[item].data)
return;
}
*((char *)item_to_column[item].data) = val;
}
static void secdp_bigdata_save_item_str(enum DP_BD_ITEM_LIST item, char *val)
{
if (!item_to_column[item].data || !val)
return;
if (item == BD_EDID && val[0] != 'X') {
int ret = 0;
int i;
int ext_blk_cnt = val[0x7e] ? 1 : 0;
int edid_size = 128 * (ext_blk_cnt + 1);
for (i = 0; i < edid_size; i++) {
ret += scnprintf(((char *)item_to_column[item].data) + ret,
EDID_BUF_SIZE + 1 - ret, "%02x",
val[i]);
}
} else {
strlcpy((char *)item_to_column[item].data, val,
item_to_column[item].str_max_len + 1);
}
}
void secdp_bigdata_save_item(enum DP_BD_ITEM_LIST item, ...)
{
va_list vl;
if (item >= BD_ITEM_MAX || item < 0)
return;
va_start(vl, item);
switch (item_to_column[item].type) {
case INT:
secdp_bigdata_save_item_hex(item, (int)va_arg(vl, int));
break;
case HEX:
secdp_bigdata_save_item_int(item, (int)va_arg(vl, int));
break;
case STR:
secdp_bigdata_save_item_str(item, (char *)va_arg(vl, char *));
break;
case CHR:
secdp_bigdata_save_item_char(item, (char)va_arg(vl, int));
break;
default:
break;
}
va_end(vl);
}
void secdp_bigdata_inc_error_cnt(enum DP_BD_ITEM_LIST err)
{
if (err >= BD_ITEM_MAX || err < 0)
return;
if (item_to_column[err].data && item_to_column[err].type == ERR)
((struct bd_error_data *)item_to_column[err].data)->count++;
}
void secdp_bigdata_clr_error_cnt(enum DP_BD_ITEM_LIST err)
{
if (err >= BD_ITEM_MAX || err < 0)
return;
if (item_to_column[err].data && item_to_column[err].type == ERR)
((struct bd_error_data *)item_to_column[err].data)->count = 0;
}
static void secdp_bigdata_save_data(void)
{
int i;
int ret = 0;
for (i = 0; i < BD_ITEM_MAX; i++) {
switch (item_to_column[i].type) {
case INT:
ret += scnprintf(err_data_buf + ret, ERR_DATA_BUF_SIZE - ret,
"\"%s\":\"%d\",",
item_to_column[i].name,
(item_to_column[i].data != NULL) ?
*((int *)item_to_column[i].data) : -1);
break;
case HEX:
ret += scnprintf(err_data_buf + ret, ERR_DATA_BUF_SIZE - ret,
"\"%s\":\"0x%x\",",
item_to_column[i].name,
(item_to_column[i].data != NULL) ?
*((int *)item_to_column[i].data) : -1);
break;
case STR:
ret += scnprintf(err_data_buf + ret, ERR_DATA_BUF_SIZE - ret,
"\"%s\":\"%s\",",
item_to_column[i].name,
(item_to_column[i].data != NULL) ?
(char *)item_to_column[i].data : "X");
break;
case CHR:
ret += scnprintf(err_data_buf + ret, ERR_DATA_BUF_SIZE - ret,
"\"%s\":\"%c\",",
item_to_column[i].name,
(item_to_column[i].data != NULL) ?
*((char *)item_to_column[i].data) : 'X');
break;
case ERR:
ret += scnprintf(err_data_buf + ret, ERR_DATA_BUF_SIZE - ret,
"\"%s\":\"%d\",",
item_to_column[i].name,
(item_to_column[i].data != NULL) ?
((struct bd_error_data *)item_to_column[i].data)->count : 0);
break;
default:
break;
}
}
if (ret > 0)
err_data_buf[ret - 1] = '\n';
}
static int secdp_bigdata_check_err(void)
{
int i;
struct bd_error_data *e_data;
for (i = 0; i < BD_ITEM_MAX; i++) {
if (item_to_column[i].type == ERR) {
e_data = item_to_column[i].data;
if (e_data != NULL && e_data->count >= e_data->limit)
return 1;
}
}
return 0;
}
void secdp_bigdata_connection(void)
{
int i;
if (dp_status != STATUS_ERROR_OCCURRED)
dp_status = STATUS_CONNECTION;
for (i = 0; i < BD_ITEM_MAX; i++) {
switch (item_to_column[i].type) {
case INT:
case HEX:
secdp_bigdata_save_item_int(i, -1);
break;
case STR:
secdp_bigdata_save_item_str(i, "X");
break;
case CHR:
secdp_bigdata_save_item_char(i, 'X');
break;
case ERR:
secdp_bigdata_clr_error_cnt(i);
break;
default:
break;
}
}
}
void secdp_bigdata_disconnection(void)
{
if (secdp_bigdata_check_err()) {
dp_status = STATUS_ERROR_OCCURRED;
secdp_bigdata_save_data();
}
if (dp_status != STATUS_ERROR_OCCURRED)
secdp_bigdata_save_data();
}

View File

@@ -0,0 +1,266 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2022 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* SECDP logger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/stat.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/time.h>
#include <linux/uaccess.h>
#include <linux/ktime.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 10, 0)
#include <linux/sched/clock.h>
#else
#include <linux/sched.h>
#endif
#include <linux/secdp_logger.h>
#include "secdp_unit_test.h"
#define BUF_SIZE SZ_64K
#define MAX_STR_LEN 160
#define PROC_FILE_NAME "dplog"
#define LOG_PREFIX "secdp"
static char log_buf[BUF_SIZE];
static unsigned int g_curpos;
static int is_secdp_logger_init;
static int is_buf_full;
static int log_max_count = -1;
static unsigned int max_mode_count;
static struct mutex dplog_lock;
static struct proc_dir_entry *g_entry;
static void dp_logger_print_date_time(void)
{
char tmp[64] = {0x0, };
struct tm tm;
struct timespec64 ts;
unsigned long sec;
ktime_get_real_ts64(&ts);
sec = ts.tv_sec - (sys_tz.tz_minuteswest * 60);
time64_to_tm(sec, 0, &tm);
snprintf(tmp, sizeof(tmp), "!@[%02d-%02d %02d:%02d:%02d.%03lu]",
tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
ts.tv_nsec / 1000000);
secdp_logger_print("%s\n", tmp);
}
/* set max log count, if count is -1, no limit */
void secdp_logger_set_max_count(int count)
{
log_max_count = count;
dp_logger_print_date_time();
}
static void _secdp_logger_print(const char *fmt, va_list args)
{
int len;
char buf[MAX_STR_LEN] = {0, };
u64 time;
unsigned long nsec;
volatile unsigned int curpos;
mutex_lock(&dplog_lock);
time = local_clock();
nsec = do_div(time, 1000000000);
len = snprintf(buf, sizeof(buf), "[%5lu.%06ld] ",
(unsigned long)time, nsec / 1000);
len += vsnprintf(buf + len, MAX_STR_LEN - len, fmt, args);
if (len > MAX_STR_LEN)
len = MAX_STR_LEN;
curpos = g_curpos;
if (curpos + len >= BUF_SIZE) {
g_curpos = curpos = 0;
is_buf_full = 1;
}
memcpy(log_buf + curpos, buf, len);
g_curpos += len;
mutex_unlock(&dplog_lock);
}
void secdp_logger_print(const char *fmt, ...)
{
va_list args;
if (!is_secdp_logger_init)
return;
if (!log_max_count)
return;
if (log_max_count > 0)
log_max_count--;
va_start(args, fmt);
_secdp_logger_print(fmt, args);
va_end(args);
}
/* set max num of modes print */
void secdp_logger_set_mode_max_count(unsigned int num)
{
max_mode_count = num + 1;
}
void secdp_logger_dec_mode_count(void)
{
if (!is_secdp_logger_init)
return;
if (max_mode_count > 0)
max_mode_count--;
}
void secdp_logger_print_mode(const char *fmt, ...)
{
va_list args;
if (!is_secdp_logger_init)
return;
if (!max_mode_count)
return;
va_start(args, fmt);
_secdp_logger_print(fmt, args);
va_end(args);
}
void secdp_logger_hex_dump(void *buf, void *pref, size_t size)
{
uint8_t *ptr = buf;
size_t i;
char tmp[128] = {0x0, };
char *ptmp = tmp;
int len;
if (!is_secdp_logger_init)
return;
if (!log_max_count)
return;
if (log_max_count > 0)
log_max_count--;
for (i = 0; i < size; i++) {
len = snprintf(ptmp, 4, "%02x ", *ptr++);
ptmp = ptmp + len;
if (((i+1)%16) == 0) {
secdp_logger_print("%s%s\n", (char *)pref, tmp);
ptmp = tmp;
}
}
if (i % 16) {
len = ptmp - tmp;
tmp[len] = 0x0;
secdp_logger_print("%s%s\n", (char *)pref, tmp);
}
}
static ssize_t secdp_logger_read(struct file *file, char __user *buf,
size_t len, loff_t *offset)
{
loff_t pos = *offset;
ssize_t count;
size_t size;
volatile unsigned int curpos;
mutex_lock(&dplog_lock);
curpos = g_curpos;
if (is_buf_full || BUF_SIZE <= curpos)
size = BUF_SIZE;
else
size = (size_t)curpos;
if (pos >= size) {
count = 0;
goto end;
}
count = min(len, size);
if ((pos + count) > size)
count = size - pos;
if (copy_to_user(buf, log_buf + pos, count)) {
count = -EFAULT;
goto end;
}
*offset += count;
end:
mutex_unlock(&dplog_lock);
return count;
}
#if KERNEL_VERSION(5, 6, 0) <= LINUX_VERSION_CODE
static const struct proc_ops secdp_logger_ops = {
.proc_read = secdp_logger_read,
.proc_lseek = default_llseek,
};
#else
static const struct file_operations secdp_logger_ops = {
.owner = THIS_MODULE,
.read = secdp_logger_read,
.llseek = default_llseek,
};
#endif
int secdp_logger_init(void)
{
struct proc_dir_entry *entry;
if (is_secdp_logger_init)
return 0;
entry = proc_create(PROC_FILE_NAME, 0444, NULL, &secdp_logger_ops);
if (!entry) {
pr_err("%s: failed to create proc entry\n", __func__);
return 0;
}
proc_set_size(entry, BUF_SIZE);
is_secdp_logger_init = 1;
g_entry = entry;
mutex_init(&dplog_lock);
secdp_logger_print("dp logger init ok\n");
return 0;
}
void secdp_logger_deinit(void)
{
if (!g_entry)
return;
proc_remove(g_entry);
is_secdp_logger_init = 0;
g_entry = NULL;
mutex_destroy(&dplog_lock);
secdp_logger_print("dp logger deinit ok\n");
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,51 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2021, 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
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __SECDP_SYSFS_H
#define __SECDP_SYSFS_H
#include "secdp.h"
struct secdp_sysfs_in {
struct device *dev;
struct dp_parser *parser;
struct dp_ctrl *ctrl;
struct dp_link *link;
struct dp_panel *panel;
struct dp_power *power;
struct dp_catalog *catalog;
struct secdp_misc *sec;
};
struct secdp_sysfs {
struct class dp_class;
};
/**
* secdp_sysfs_get() - get the functionalities of secdp sysfs module
*
*
* return: a pointer to dp_link struct
*/
struct secdp_sysfs *secdp_sysfs_get(struct secdp_sysfs_in *in);
/**
* secdp_sysfs_put() - releases the dp test module's resources
*
* @dp_link: an instance of dp_link module
*
*/
void secdp_sysfs_put(struct device *dev, struct secdp_sysfs *dp_sysfs);
#endif /*__SECDP_SYSFS_H*/

View File

@@ -0,0 +1,162 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2021, 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
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include "dp_debug.h"
#include "dp_display.h"
#include "sde_edid_parser.h"
#include "secdp.h"
static u8 g_test_edid[] = {
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x09, 0xD1, 0x54, 0x7F, 0x45, 0x54, 0x00, 0x00,
0x14, 0x1B, 0x01, 0x03, 0x80, 0x46, 0x28, 0x78, 0x2E, 0xDF, 0x50, 0xA3, 0x54, 0x35, 0xB5, 0x26,
0x0F, 0x50, 0x54, 0xA5, 0x6B, 0x80, 0xD1, 0xC0, 0x81, 0xC0, 0x81, 0x00, 0x81, 0x80, 0xA9, 0xC0,
0xB3, 0x00, 0x01, 0x01, 0x01, 0x01, 0x51, 0xD0, 0x00, 0xA0, 0xF0, 0x70, 0x3E, 0x80, 0x30, 0x20,
0x35, 0x00, 0xBA, 0x89, 0x21, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x53, 0x35, 0x48,
0x30, 0x31, 0x38, 0x39, 0x31, 0x53, 0x4C, 0x30, 0x0A, 0x20, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x18,
0x4C, 0x1E, 0x8C, 0x3C, 0x00, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFC,
0x00, 0x42, 0x65, 0x6E, 0x51, 0x20, 0x53, 0x57, 0x33, 0x32, 0x30, 0x0A, 0x20, 0x20, 0x01, 0x42,
0x02, 0x03, 0x45, 0xF1, 0x56, 0x61, 0x60, 0x5D, 0x5E, 0x5F, 0x10, 0x05, 0x04, 0x03, 0x02, 0x07,
0x06, 0x0F, 0x1F, 0x20, 0x21, 0x22, 0x14, 0x13, 0x12, 0x16, 0x01, 0x23, 0x09, 0x07, 0x07, 0xE6,
0x06, 0x05, 0x01, 0x60, 0x5A, 0x44, 0x6D, 0x03, 0x0C, 0x00, 0x10, 0x00, 0x38, 0x44, 0x20, 0x00,
0x60, 0x01, 0x02, 0x03, 0x67, 0xD8, 0x5D, 0xC4, 0x01, 0x78, 0x80, 0x01, 0xE4, 0x0F, 0x03, 0x00,
0x00, 0xE3, 0x05, 0xC3, 0x00, 0x02, 0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C, 0x45,
0x00, 0xBA, 0x89, 0x21, 0x00, 0x00, 0x1E, 0x56, 0x5E, 0x00, 0xA0, 0xA0, 0xA0, 0x29, 0x50, 0x30,
0x20, 0x35, 0x00, 0xBA, 0x89, 0x21, 0x00, 0x00, 0x1A, 0xF4, 0x51, 0x00, 0xA0, 0xF0, 0x70, 0x19,
0x80, 0x30, 0x20, 0x35, 0x00, 0xBA, 0x89, 0x21, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0xAB,
};
static struct secdp_display_timing g_parsed_res[] = {
/* active_h, active_v, refresh_rate, interlaced */
{0, 640, 480, 60, false}, /* 640x480 60hz */
{0, 640, 480, 75, false}, /* 640x480 75hz */
{0, 720, 400, 70, false}, /* 720x400 70hz */
{0, 720, 480, 60, true}, /* 720x480i 60hz */
{0, 720, 480, 60, false}, /* 720x480 60hz */
{0, 720, 576, 50, true}, /* 720x576i 50hz */
{0, 720, 576, 50, false}, /* 720x576 50hz */
{0, 800, 600, 60, false}, /* 800x600 60hz */
{0, 800, 600, 75, false}, /* 800x600 75hz */
{0, 832, 624, 75, false}, /* 832x624 75hz */
{0, 1024, 768, 60, false}, /* 1024x768 60hz */
{0, 1024, 768, 75, false}, /* 1024x768 75hz */
{0, 1152, 864, 75, false}, /* 1152x864 75hz */
{0, 1280, 720, 50, false}, /* 1280x720 50hz */
{0, 1280, 720, 60, false}, /* 1280x720 60hz */
{0, 1280, 800, 60, false}, /* 1280x800 60hz */
{0, 1280, 1024, 60, false}, /* 1280x1024 60hz */
{0, 1280, 1024, 75, false}, /* 1280x1024 75hz */
{0, 1440, 480, 60, false}, /* 1440x480 60hz */
{0, 1600, 900, 60, false}, /* 1600x900 60hz */
{0, 1680, 1050, 60, false}, /* 1680x1050 60hz */
{0, 1920, 1080, 50, true}, /* 1920x1080i 50hz */
{0, 1920, 1080, 60, true}, /* 1920x1080i 60hz */
{0, 1920, 1080, 24, false}, /* 1920x1080 24hz */
{0, 1920, 1080, 25, false}, /* 1920x1080 25hz */
{0, 1920, 1080, 30, false}, /* 1920x1080 30hz */
{0, 1920, 1080, 50, false}, /* 1920x1080 50hz */
{0, 1920, 1080, 60, false}, /* 1920x1080 60hz */
{0, 2560, 1440, 60, false}, /* 2560x1440 60hz */
{0, 3840, 2160, 24, false}, /* 3840x2160 24hz */
{0, 3840, 2160, 25, false}, /* 3840x2160 25hz */
{0, 3840, 2160, 30, false}, /* 3840x2160 30hz */
{0, 3840, 2160, 50, false}, /* 3840x2160 50hz */
{0, 3840, 2160, 60, false}, /* 3840x2160 60hz */
};
static void drm_mode_remove(struct drm_connector *connector,
struct drm_display_mode *mode)
{
list_del(&mode->head);
drm_mode_destroy(connector->dev, mode);
}
bool secdp_unit_test_edid_parse(struct secdp_misc *sec)
{
int rc, i, parsed_res_cnt = 0, table_size;
bool ret = false;
struct sde_edid_ctrl *edid_ctrl = NULL;
struct drm_display_mode *mode, *t;
struct drm_connector *connector;
connector = secdp_get_connector(sec);
if (!connector) {
DP_ERR("fail to get connector\n");
goto exit;
}
table_size = ARRAY_SIZE(g_parsed_res);
edid_ctrl = sde_edid_init();
if (!edid_ctrl) {
DP_ERR("edid_ctrl alloc failed\n");
goto exit;
}
mutex_lock(&connector->dev->mode_config.mutex);
edid_ctrl->edid = (struct edid *)g_test_edid;
rc = _sde_edid_update_modes(connector, edid_ctrl);
DP_INFO("_sde_edid_update_modes, rc: %d\n", rc);
/* init g_parsed_res */
for (i = 0; i < table_size; i++)
g_parsed_res[i].supported = false;
/* check resolutions */
list_for_each_entry(mode, &connector->probed_modes, head) {
DP_INFO("checking %s @ %d Hz..\n", mode->name, drm_mode_vrefresh(mode));
for (i = 0; i < table_size; i++) {
bool interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
if (g_parsed_res[i].active_h == mode->hdisplay &&
g_parsed_res[i].active_v == mode->vdisplay &&
g_parsed_res[i].refresh_rate == drm_mode_vrefresh(mode) &&
g_parsed_res[i].interlaced == interlaced) {
/*all conditions are met, mark it as supported*/
g_parsed_res[i].supported = true;
}
}
}
list_for_each_entry_safe(mode, t, &connector->probed_modes, head)
drm_mode_remove(connector, mode);
mutex_unlock(&connector->dev->mode_config.mutex);
kfree(edid_ctrl);
/* count how many resolutions are marked as supported */
for (i = 0; i < table_size; i++) {
if (g_parsed_res[i].supported)
parsed_res_cnt++;
}
/* check if num of supported resolutions are found without errors */
if (parsed_res_cnt != table_size) {
DP_ERR("count is not matched! res_cnt: %d, table_size: %d\n",
parsed_res_cnt, table_size);
goto exit;
}
ret = true;
exit:
DP_INFO("returns %s\n", ret ? "true" : "false");
return ret;
}

View File

@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2021, 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
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __SECDP_UNIT_TEST_H
#define __SECDP_UNIT_TEST_H
#include "secdp.h"
bool secdp_unit_test_edid_parse(struct secdp_misc *sec);
#endif/*__SECDP_UNIT_TEST_H*/