diff options
| author | Aravind Venkateswaran <aravindh@codeaurora.org> | 2017-01-11 16:38:44 -0800 |
|---|---|---|
| committer | Aravind Venkateswaran <aravindh@codeaurora.org> | 2017-01-18 23:22:41 -0800 |
| commit | 262e423fdf095e832bb177a0999851f8ee552819 (patch) | |
| tree | be8a2a3ee45eeb457cb87d2e26c55ec4cf0ba0ce /drivers/video/fbdev/msm | |
| parent | 31975b700c56af6a6edc3f13e83e8f10bc9dacf9 (diff) | |
msm: mdss: dp: add support to read per frame CRC values
Display Port (DP) controller has the ability to calculate CRC values
for every frame. In addition, most DP sinks also have the capability
to calculate per frame CRC values. Add support to enable/disable
calculation of per-frame CRC values for both the DP controller and
the DP sink. Add support to read the computed CRC values via a new
sysfs node. See below for usage instructions:
To enable/disable per frame CRC calculation:
* echo "ctl_cr_en=[0/1]" > /sys/class/graphics/<fbi>/frame_crc
* echo "sink_cr_en=[0/1]" > /sys/class/graphics/<fbi>/frame_crc
To read computed CRC values:
* cat /sys/class/graphics/<fbi>/frame_crc
CRs-Fixed: 1109812
Change-Id: I41271db64463b26cbbd9baba43d098bc30da33bf
Signed-off-by: Aravind Venkateswaran <aravindh@codeaurora.org>
Diffstat (limited to 'drivers/video/fbdev/msm')
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dp.c | 81 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dp.h | 23 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dp_aux.c | 118 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dp_util.c | 39 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dp_util.h | 7 |
5 files changed, 268 insertions, 0 deletions
diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index 3590679da102..57841db8bb62 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -2307,6 +2307,84 @@ static ssize_t mdss_dp_rda_config(struct device *dev, return ret; } +static ssize_t mdss_dp_wta_frame_crc(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret; + u32 val; + struct mdss_dp_drv_pdata *dp = mdss_dp_get_drvdata(dev); + char const *ctl_crc_key = "ctl_crc_en="; + char const *sink_crc_key = "sink_crc_en="; + bool ctl_crc_en, sink_crc_en; + + if (!dp) { + pr_err("invalid data\n"); + goto end; + } + + if (!dp->power_on) { + pr_err("DP controller not powered on\n"); + goto end; + } + + ret = mdss_dp_parse_config_value(buf, ctl_crc_key, &val); + if (ret) { + pr_debug("%s config not found\n", ctl_crc_key); + goto sink_crc; + } + ctl_crc_en = val ? true : false; + mdss_dp_config_ctl_frame_crc(dp, ctl_crc_en); + +sink_crc: + ret = mdss_dp_parse_config_value(buf, sink_crc_key, &val); + if (ret) { + pr_debug("%s config not found\n", sink_crc_key); + goto end; + } + sink_crc_en = val ? true : false; + mdss_dp_aux_config_sink_frame_crc(dp, sink_crc_en); + +end: + return count; +} + +static ssize_t mdss_dp_print_crc_values(struct mdss_dp_drv_pdata *dp, + char *buf, ssize_t len) +{ + char line[] = "------------------------------"; + + mdss_dp_read_ctl_frame_crc(dp); + mdss_dp_aux_read_sink_frame_crc(dp); + + return snprintf(buf, PAGE_SIZE, + "\t\t|R_Cr\t\t|G_y\t\t|B_Cb\n%s%s\nctl(%s)\t|0x%08x\t|0x%08x\t|0x%08x\nsink(%s)\t|0x%08x\t|0x%08x\t|0x%08x\n", + line, line, dp->ctl_crc.en ? "enabled" : "disabled", + dp->ctl_crc.r_cr, dp->ctl_crc.g_y, dp->ctl_crc.b_cb, + dp->sink_crc.en ? "enabled" : "disabled", + dp->sink_crc.r_cr, dp->sink_crc.g_y, dp->sink_crc.b_cb); +} + +static ssize_t mdss_dp_rda_frame_crc(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + struct mdss_dp_drv_pdata *dp = mdss_dp_get_drvdata(dev); + + if (!dp) { + pr_err("invalid input\n"); + return -EINVAL; + } + + if (!dp->power_on) { + pr_err("DP controller not powered on\n"); + return 0; + } + + ret = mdss_dp_print_crc_values(dp, buf, PAGE_SIZE); + + return ret; +} + static DEVICE_ATTR(connected, S_IRUGO, mdss_dp_rda_connected, NULL); static DEVICE_ATTR(s3d_mode, S_IRUGO | S_IWUSR, mdss_dp_sysfs_rda_s3d_mode, mdss_dp_sysfs_wta_s3d_mode); @@ -2316,6 +2394,8 @@ static DEVICE_ATTR(psm, S_IRUGO | S_IWUSR, mdss_dp_rda_psm, mdss_dp_wta_psm); static DEVICE_ATTR(config, S_IRUGO | S_IWUSR, mdss_dp_rda_config, mdss_dp_wta_config); +static DEVICE_ATTR(frame_crc, S_IRUGO | S_IWUSR, mdss_dp_rda_frame_crc, + mdss_dp_wta_frame_crc); static struct attribute *mdss_dp_fs_attrs[] = { &dev_attr_connected.attr, @@ -2323,6 +2403,7 @@ static struct attribute *mdss_dp_fs_attrs[] = { &dev_attr_hpd.attr, &dev_attr_psm.attr, &dev_attr_config.attr, + &dev_attr_frame_crc.attr, NULL, }; diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h index e871dbabbdb3..7e1088d0e00a 100644 --- a/drivers/video/fbdev/msm/mdss_dp.h +++ b/drivers/video/fbdev/msm/mdss_dp.h @@ -406,6 +406,13 @@ struct mdss_dp_event_data { spinlock_t event_lock; }; +struct mdss_dp_crc_data { + bool en; + u32 r_cr; + u32 g_y; + u32 b_cb; +}; + struct mdss_dp_drv_pdata { /* device driver */ int (*on) (struct mdss_panel_data *pdata); @@ -446,6 +453,8 @@ struct mdss_dp_drv_pdata { bool override_config; u32 mask1; u32 mask2; + struct mdss_dp_crc_data ctl_crc; + struct mdss_dp_crc_data sink_crc; struct mdss_panel_data panel_data; struct mdss_util_intf *mdss_util; @@ -927,6 +936,17 @@ static inline char const *mdss_dp_notification_status_to_string( } } +static inline void mdss_dp_reset_frame_crc_data(struct mdss_dp_crc_data *crc) +{ + if (!crc) + return; + + crc->r_cr = 0; + crc->g_y = 0; + crc->b_cb = 0; + crc->en = false; +} + void mdss_dp_phy_initialize(struct mdss_dp_drv_pdata *dp); void mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *dp); @@ -955,5 +975,8 @@ bool mdss_dp_aux_is_lane_count_valid(u32 lane_count); int mdss_dp_aux_link_status_read(struct mdss_dp_drv_pdata *ep, int len); void mdss_dp_aux_update_voltage_and_pre_emphasis_lvl( struct mdss_dp_drv_pdata *dp); +int mdss_dp_aux_read_sink_frame_crc(struct mdss_dp_drv_pdata *dp); +int mdss_dp_aux_config_sink_frame_crc(struct mdss_dp_drv_pdata *dp, + bool enable); #endif /* MDSS_DP_H */ diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index 4a3e1e0b259d..85820e1df97a 100644 --- a/drivers/video/fbdev/msm/mdss_dp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -2127,6 +2127,124 @@ void mdss_dp_fill_link_cfg(struct mdss_dp_drv_pdata *ep) } +/** + * mdss_dp_aux_config_sink_frame_crc() - enable/disable per frame CRC calc + * @dp: Display Port Driver data + * @enable: true - start CRC calculation, false - stop CRC calculation + * + * Program the sink DPCD register 0x270 to start/stop CRC calculation. + * This would take effect with the next frame. + */ +int mdss_dp_aux_config_sink_frame_crc(struct mdss_dp_drv_pdata *dp, + bool enable) +{ + int rlen; + struct edp_buf *rp; + u8 *bp; + u8 buf[4]; + u8 crc_supported; + u32 const test_sink_addr = 0x270; + u32 const test_sink_misc_addr = 0x246; + + if (dp->sink_crc.en == enable) { + pr_debug("sink crc already %s\n", + enable ? "enabled" : "disabled"); + return 0; + } + + rlen = dp_aux_read_buf(dp, test_sink_misc_addr, 1, 0); + if (rlen < 1) { + pr_err("failed to TEST_SINK_ADDR\n"); + return -EPERM; + } + rp = &dp->rxp; + bp = rp->data; + crc_supported = bp[0] & BIT(5); + pr_debug("crc supported=%s\n", crc_supported ? "true" : "false"); + + if (!crc_supported) { + pr_err("sink does not support CRC generation\n"); + return -EINVAL; + } + + buf[0] = enable ? 1 : 0; + dp_aux_write_buf(dp, test_sink_addr, buf, BIT(0), 0); + + if (!enable) + mdss_dp_reset_frame_crc_data(&dp->sink_crc); + dp->sink_crc.en = enable; + pr_debug("TEST_SINK_START (CRC calculation) %s\n", + enable ? "enabled" : "disabled"); + + return 0; +} + +/** + * mdss_dp_aux_read_sink_frame_crc() - read frame CRC values from the sink + * @dp: Display Port Driver data + */ +int mdss_dp_aux_read_sink_frame_crc(struct mdss_dp_drv_pdata *dp) +{ + int rlen; + struct edp_buf *rp; + u8 *bp; + u32 addr, len; + struct mdss_dp_crc_data *crc = &dp->sink_crc; + + addr = 0x270; /* TEST_SINK */ + len = 1; /* one byte */ + rlen = dp_aux_read_buf(dp, addr, len, 0); + if (rlen < len) { + pr_err("failed to read TEST SINK\n"); + return -EPERM; + } + rp = &dp->rxp; + bp = rp->data; + if (!(bp[0] & BIT(0))) { + pr_err("Sink side CRC calculation not enabled, TEST_SINK=0x%08x\n", + (u32)bp[0]); + return -EINVAL; + } + + addr = 0x240; /* TEST_CRC_R_Cr */ + len = 2; /* 2 bytes */ + rlen = dp_aux_read_buf(dp, addr, len, 0); + if (rlen < len) { + pr_err("failed to read TEST_CRC_R_Cr\n"); + return -EPERM; + } + rp = &dp->rxp; + bp = rp->data; + crc->r_cr = bp[0] | (bp[1] << 8); + + addr = 0x242; /* TEST_CRC_G_Y */ + len = 2; /* 2 bytes */ + rlen = dp_aux_read_buf(dp, addr, len, 0); + if (rlen < len) { + pr_err("failed to read TEST_CRC_G_Y\n"); + return -EPERM; + } + rp = &dp->rxp; + bp = rp->data; + crc->g_y = bp[0] | (bp[1] << 8); + + addr = 0x244; /* TEST_CRC_B_Cb */ + len = 2; /* 2 bytes */ + rlen = dp_aux_read_buf(dp, addr, len, 0); + if (rlen < len) { + pr_err("failed to read TEST_CRC_B_Cb\n"); + return -EPERM; + } + rp = &dp->rxp; + bp = rp->data; + crc->b_cb = bp[0] | (bp[1] << 8); + + pr_debug("r_cr=0x%08x\t g_y=0x%08x\t b_cb=0x%08x\n", + crc->r_cr, crc->g_y, crc->b_cb); + + return 0; +} + void mdss_dp_aux_init(struct mdss_dp_drv_pdata *ep) { mutex_init(&ep->aux_mutex); diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c index 26420836e530..1e7010dc47e9 100644 --- a/drivers/video/fbdev/msm/mdss_dp_util.c +++ b/drivers/video/fbdev/msm/mdss_dp_util.c @@ -202,6 +202,45 @@ void mdss_dp_configuration_ctrl(struct dss_io_data *ctrl_io, u32 data) writel_relaxed(data, ctrl_io->base + DP_CONFIGURATION_CTRL); } +void mdss_dp_config_ctl_frame_crc(struct mdss_dp_drv_pdata *dp, bool enable) +{ + if (dp->ctl_crc.en == enable) { + pr_debug("CTL crc already %s\n", + enable ? "enabled" : "disabled"); + return; + } + + writel_relaxed(BIT(8), dp->ctrl_io.base + MMSS_DP_TIMING_ENGINE_EN); + if (!enable) + mdss_dp_reset_frame_crc_data(&dp->ctl_crc); + dp->ctl_crc.en = enable; + + pr_debug("CTL crc %s\n", enable ? "enabled" : "disabled"); +} + +int mdss_dp_read_ctl_frame_crc(struct mdss_dp_drv_pdata *dp) +{ + u32 data; + u32 crc_rg = 0; + struct mdss_dp_crc_data *crc = &dp->ctl_crc; + + data = readl_relaxed(dp->ctrl_io.base + MMSS_DP_TIMING_ENGINE_EN); + if (!(data & BIT(8))) { + pr_debug("frame CRC calculation not enabled\n"); + return -EPERM; + } + + crc_rg = readl_relaxed(dp->ctrl_io.base + MMSS_DP_PSR_CRC_RG); + crc->r_cr = crc_rg & 0xFFFF; + crc->g_y = crc_rg >> 16; + crc->b_cb = readl_relaxed(dp->ctrl_io.base + MMSS_DP_PSR_CRC_B); + + pr_debug("r_cr=0x%08x\t g_y=0x%08x\t b_cb=0x%08x\n", + crc->r_cr, crc->g_y, crc->b_cb); + + return 0; +} + /* DP state controller*/ void mdss_dp_state_ctrl(struct dss_io_data *ctrl_io, u32 data) { diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h index e08a16a02174..cb62d145960f 100644 --- a/drivers/video/fbdev/msm/mdss_dp_util.h +++ b/drivers/video/fbdev/msm/mdss_dp_util.h @@ -74,6 +74,9 @@ #define MMSS_DP_AUDIO_TIMING_RBR_48 (0x00000494) #define MMSS_DP_AUDIO_TIMING_HBR_48 (0x00000498) +#define MMSS_DP_PSR_CRC_RG (0x00000554) +#define MMSS_DP_PSR_CRC_B (0x00000558) + #define MMSS_DP_AUDIO_CFG (0x00000600) #define MMSS_DP_AUDIO_STATUS (0x00000604) #define MMSS_DP_AUDIO_PKT_CTRL (0x00000608) @@ -136,6 +139,8 @@ #define MMSS_DP_GENERIC1_8 (0x00000748) #define MMSS_DP_GENERIC1_9 (0x0000074C) +#define MMSS_DP_TIMING_ENGINE_EN (0x00000A10) + /*DP PHY Register offsets */ #define DP_PHY_REVISION_ID0 (0x00000000) #define DP_PHY_REVISION_ID1 (0x00000004) @@ -321,5 +326,7 @@ void mdss_dp_set_safe_to_exit_level(struct dss_io_data *ctrl_io, uint32_t lane_cnt); int mdss_dp_aux_read_rx_status(struct mdss_dp_drv_pdata *dp, u8 *rx_status); void mdss_dp_phy_send_test_pattern(struct mdss_dp_drv_pdata *dp); +void mdss_dp_config_ctl_frame_crc(struct mdss_dp_drv_pdata *dp, bool enable); +int mdss_dp_read_ctl_frame_crc(struct mdss_dp_drv_pdata *dp); #endif /* __DP_UTIL_H__ */ |
