summaryrefslogtreecommitdiffstats
path: root/drivers/video/tegra/dc/dp.c
diff options
context:
space:
mode:
authorShu Zhong <shuz@nvidia.com>2018-08-20 19:48:02 -0400
committermobile promotions <svcmobile_promotions@nvidia.com>2018-11-10 21:16:46 -0500
commit446d6934c28e82a3f64a0d4de6f7fd6fd93dab62 (patch)
tree779aaa7612db4aa2f86988bc81729e75a6e8a41e /drivers/video/tegra/dc/dp.c
parentfe17829a12d57dd6a5f53a83f292703d0761581e (diff)
dp: add Type-C extcon support
In Type-C multi-function pin assignments, the four USB3.x SS data lanes are divided up for both Alt Mode and USB signaling. For DP, this means that the number of available TX lanes is also dependent on the current pin configuration when driving DP Alt Mode over Type-C. The number of connected DP lanes can be derived through CR/EQ fallback, but this is too late given our current SW architecture. Mode filtering occurs when the display driver populates the modedb in response to an HPD_PLUG event, but LT is only triggered on the subsequent modeset. In order to resolve this dependency, this change adds support to the DP driver so that it can query the number of connected lanes from the ucsi_ccg extcon provider, before filtering. Bug 2024637 TDS-3502 Change-Id: I9d0966bc3de5f9323f7ebe18d1fecbe972f6a063 Signed-off-by: Shu Zhong <shuz@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/1815326 (cherry picked from commit a6cac5548d273874b0fe99d62931df26b30ab7aa) Reviewed-on: https://git-master.nvidia.com/r/1928660 GVS: Gerrit_Virtual_Submit Reviewed-by: Mitch Luban <mluban@nvidia.com> Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com> Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
Diffstat (limited to 'drivers/video/tegra/dc/dp.c')
-rw-r--r--drivers/video/tegra/dc/dp.c218
1 files changed, 215 insertions, 3 deletions
diff --git a/drivers/video/tegra/dc/dp.c b/drivers/video/tegra/dc/dp.c
index 3bb9631ee..ab57fb318 100644
--- a/drivers/video/tegra/dc/dp.c
+++ b/drivers/video/tegra/dc/dp.c
@@ -1245,8 +1245,9 @@ int tegra_dc_dp_get_max_lane_count(struct tegra_dc_dp_data *dp, u8 *dpcd_data)
1245 /* 1245 /*
1246 * The max lane count supported is a product of everything below: 1246 * The max lane count supported is a product of everything below:
1247 * 1) The max lane count supported by the DPRX. 1247 * 1) The max lane count supported by the DPRX.
1248 * 2) The max lane count supported by the DPTX. 1248 * 2) The # of connected lanes reported by the extcon provider (Type-C).
1249 * 3) If test configuration parameters are set on the DPTX side, these 1249 * 3) The max lane count supported by the DPTX.
1250 * 4) If test configuration parameters are set on the DPTX side, these
1250 * parameters need to be accounted for as well. 1251 * parameters need to be accounted for as well.
1251 */ 1252 */
1252 1253
@@ -1263,10 +1264,13 @@ int tegra_dc_dp_get_max_lane_count(struct tegra_dc_dp_data *dp, u8 *dpcd_data)
1263 max_lane_count = max_lane_count & NV_DPCD_MAX_LANE_COUNT_MASK; 1264 max_lane_count = max_lane_count & NV_DPCD_MAX_LANE_COUNT_MASK;
1264 1265
1265 /* Constraint #2 */ 1266 /* Constraint #2 */
1267 max_lane_count = min(max_lane_count, dp->typec_lane_count);
1268
1269 /* Constraint #3 */
1266 if (dp->pdata && dp->pdata->lanes > 0) 1270 if (dp->pdata && dp->pdata->lanes > 0)
1267 max_lane_count = min(max_lane_count, (u8)dp->pdata->lanes); 1271 max_lane_count = min(max_lane_count, (u8)dp->pdata->lanes);
1268 1272
1269 /* Constraint #3 */ 1273 /* Constraint #4 */
1270 if (dp->test_max_lanes > 0) 1274 if (dp->test_max_lanes > 0)
1271 max_lane_count = min(max_lane_count, (u8)dp->test_max_lanes); 1275 max_lane_count = min(max_lane_count, (u8)dp->test_max_lanes);
1272 1276
@@ -1691,6 +1695,198 @@ done:
1691 1695
1692} 1696}
1693 1697
1698static inline struct tegra_dc_extcon_cable
1699 *tegra_dp_get_typec_ecable(struct tegra_dc *dc)
1700{
1701 if (!dc || !dc->out || !dc->out->dp_out)
1702 return NULL;
1703
1704 return &dc->out->dp_out->typec_ecable;
1705}
1706
1707static void tegra_dp_wait_for_typec_connect(struct tegra_dc_dp_data *dp)
1708{
1709#if KERNEL_VERSION(4, 9, 0) <= LINUX_VERSION_CODE
1710 struct tegra_dc_extcon_cable *typec_ecable;
1711 struct tegra_dc *dc;
1712 union extcon_property_value lane_count = {0};
1713 int ret;
1714 bool ecable_connected;
1715
1716 if (!dp || !dp->dc) {
1717 pr_err("%s: all arguments must be non-NULL!\n", __func__);
1718 return;
1719 }
1720 dc = dp->dc;
1721
1722 if (dc->out->type == TEGRA_DC_OUT_FAKE_DP)
1723 return;
1724
1725 typec_ecable = tegra_dp_get_typec_ecable(dc);
1726 if (!typec_ecable || !typec_ecable->edev)
1727 return;
1728
1729 mutex_lock(&typec_ecable->lock);
1730
1731 ecable_connected = typec_ecable->connected;
1732 if (!ecable_connected)
1733 reinit_completion(&typec_ecable->comp);
1734
1735 mutex_unlock(&typec_ecable->lock);
1736
1737 if (!ecable_connected) {
1738 /*
1739 * See the comment above these fields in dp.h for why this is
1740 * required.
1741 */
1742 if (unlikely(!dp->typec_notified_once &&
1743 dp->typec_timed_out_once))
1744 return;
1745
1746 if (!wait_for_completion_timeout(&typec_ecable->comp,
1747 msecs_to_jiffies(1000))) {
1748 dev_info(&dc->ndev->dev,
1749 "dp: typec extcon wait timeout\n");
1750 dp->typec_timed_out_once = true;
1751
1752 goto typec_lane_count_err;
1753 }
1754 }
1755
1756 ret = extcon_get_property(typec_ecable->edev, EXTCON_DISP_DP,
1757 EXTCON_PROP_DISP_DP_LANE, &lane_count);
1758 if (ret) {
1759 dev_err(&dc->ndev->dev,
1760 "dp: extcon get lane prop error - ret=%d\n", ret);
1761
1762 goto typec_lane_count_err;
1763 }
1764
1765 if (lane_count.intval > 0)
1766 dp->typec_lane_count = lane_count.intval;
1767
1768 return;
1769typec_lane_count_err:
1770 dp->typec_lane_count = 4;
1771#endif
1772}
1773
1774static int tegra_dp_typec_ecable_notifier(struct notifier_block *nb,
1775 unsigned long event, void *data)
1776{
1777 struct tegra_dc_extcon_cable *typec_ecable =
1778 container_of(nb, struct tegra_dc_extcon_cable, nb);
1779 struct tegra_dc_dp_data *dp =
1780 (struct tegra_dc_dp_data *)typec_ecable->drv_data;
1781
1782 /* See the comment above this field in dp.h for why this is required. */
1783 dp->typec_notified_once = true;
1784
1785 mutex_lock(&typec_ecable->lock);
1786
1787 typec_ecable->connected = !!event;
1788 if (event) /* connected */
1789 complete(&typec_ecable->comp);
1790
1791 mutex_unlock(&typec_ecable->lock);
1792
1793 return NOTIFY_DONE;
1794}
1795
1796static int tegra_dp_register_typec_ecable(struct tegra_dc_dp_data *dp)
1797{
1798 struct tegra_dc_extcon_cable *typec_ecable;
1799 int ret;
1800
1801#if KERNEL_VERSION(4, 9, 0) <= LINUX_VERSION_CODE
1802 union extcon_property_value lane_count = {0};
1803 int init_cable_state;
1804#endif
1805
1806 if (!dp || !dp->dc) {
1807 pr_err("%s: all arguments must be non-NULL!\n", __func__);
1808 return -EINVAL;
1809 }
1810
1811 /* Assume that all TX lanes are dedicated for DP by default. */
1812 dp->typec_lane_count = 4;
1813
1814 dp->typec_notified_once = false;
1815 dp->typec_timed_out_once = false;
1816
1817 typec_ecable = tegra_dp_get_typec_ecable(dp->dc);
1818 if (!typec_ecable || !typec_ecable->edev)
1819 return 0;
1820
1821 mutex_init(&typec_ecable->lock);
1822 init_completion(&typec_ecable->comp);
1823
1824 typec_ecable->drv_data = dp;
1825 typec_ecable->connected = false;
1826
1827 typec_ecable->nb.notifier_call = tegra_dp_typec_ecable_notifier;
1828 ret = extcon_register_notifier(typec_ecable->edev, EXTCON_DISP_DP,
1829 &typec_ecable->nb);
1830 if (ret < 0) {
1831 dev_err(&dp->dc->ndev->dev,
1832 "dp: typec extcon notifier registration failed\n");
1833 return ret;
1834 }
1835
1836#if KERNEL_VERSION(4, 9, 0) <= LINUX_VERSION_CODE
1837 /*
1838 * Query the initial Type-C cable state here in case ucsi_ccg updated it
1839 * before we were able to register the extcon notifier.
1840 */
1841
1842 mutex_lock(&typec_ecable->lock);
1843
1844 init_cable_state = extcon_get_state(typec_ecable->edev, EXTCON_DISP_DP);
1845 if (init_cable_state < 0) {
1846 dev_err(&dp->dc->ndev->dev,
1847 "dp: failed to get initial cable state\n");
1848 } else if (init_cable_state) { /* connected */
1849 ret = extcon_get_property(typec_ecable->edev, EXTCON_DISP_DP,
1850 EXTCON_PROP_DISP_DP_LANE, &lane_count);
1851 if (ret) {
1852 dev_err(&dp->dc->ndev->dev,
1853 "dp: failed to get initial lane count\n");
1854 } else if (lane_count.intval > 0) {
1855 typec_ecable->connected = true;
1856 dp->typec_lane_count = lane_count.intval;
1857 }
1858 }
1859
1860 mutex_unlock(&typec_ecable->lock);
1861#endif
1862
1863 return 0;
1864}
1865
1866static void tegra_dp_unregister_typec_ecable(struct tegra_dc *dc)
1867{
1868 struct tegra_dc_extcon_cable *typec_ecable;
1869 int ret;
1870
1871 if (!dc) {
1872 pr_err("%s: all arguments must be non-NULL!\n", __func__);
1873 return;
1874 }
1875
1876 typec_ecable = tegra_dp_get_typec_ecable(dc);
1877 if (!typec_ecable || !typec_ecable->edev)
1878 return;
1879
1880 ret = extcon_unregister_notifier(typec_ecable->edev, EXTCON_DISP_DP,
1881 &typec_ecable->nb);
1882 if (ret < 0)
1883 dev_err(&dc->ndev->dev,
1884 "dp: typec extcon notifier unregistration failed\n");
1885
1886 typec_ecable->drv_data = NULL;
1887 typec_ecable->connected = false;
1888}
1889
1694static irqreturn_t tegra_dp_irq(int irq, void *ptr) 1890static irqreturn_t tegra_dp_irq(int irq, void *ptr)
1695{ 1891{
1696 struct tegra_dc_dp_data *dp = ptr; 1892 struct tegra_dc_dp_data *dp = ptr;
@@ -1903,6 +2099,12 @@ static int tegra_dc_dp_init(struct tegra_dc *dc)
1903 dp->parent_clk = parent_clk; 2099 dp->parent_clk = parent_clk;
1904 dp->mode = &dc->mode; 2100 dp->mode = &dc->mode;
1905 2101
2102 err = tegra_dp_register_typec_ecable(dp);
2103 if (err) {
2104 dev_err(&dc->ndev->dev, "dp: typec ecable register failed\n");
2105 goto err_audio_switch;
2106 }
2107
1906 if (dp_instance) { 2108 if (dp_instance) {
1907 snprintf(dp->hpd_switch_name, CHAR_BUF_SIZE_MAX, 2109 snprintf(dp->hpd_switch_name, CHAR_BUF_SIZE_MAX,
1908 "dp%d", dp_instance); 2110 "dp%d", dp_instance);
@@ -2215,6 +2417,14 @@ static void tegra_dp_hpd_op_edid_ready(void *drv_data)
2215 tegra_dp_auto_set_edid_checksum(dp); 2417 tegra_dp_auto_set_edid_checksum(dp);
2216 } 2418 }
2217 2419
2420 /*
2421 * For Type-C, wait until ucsi_ccg notifies us that an extcon CONNECT
2422 * event has occurred. This ensures that we're in DP configuration, and
2423 * that the EXTCON_PROP_DISP_DP_LANE property has been populated by
2424 * ucsi_ccg before we proceed with our subsequent mode filtering.
2425 */
2426 tegra_dp_wait_for_typec_connect(dp);
2427
2218 /* Early enables DC with first mode from the monitor specs */ 2428 /* Early enables DC with first mode from the monitor specs */
2219 if (dp->early_enable) { 2429 if (dp->early_enable) {
2220 struct tegra_hpd_data *data = &dp->hpd_data; 2430 struct tegra_hpd_data *data = &dp->hpd_data;
@@ -2797,6 +3007,8 @@ static void tegra_dc_dp_destroy(struct tegra_dc *dc)
2797 3007
2798 dp->prod_list = NULL; 3008 dp->prod_list = NULL;
2799 3009
3010 tegra_dp_unregister_typec_ecable(dc);
3011
2800 tegra_dc_out_destroy(dc); 3012 tegra_dc_out_destroy(dc);
2801 3013
2802 tegra_dc_dp_debugfs_remove(dp); 3014 tegra_dc_dp_debugfs_remove(dp);