16b6b6042SThierry Reding /* 26b6b6042SThierry Reding * Copyright (C) 2013 NVIDIA Corporation 36b6b6042SThierry Reding * 46b6b6042SThierry Reding * This program is free software; you can redistribute it and/or modify 56b6b6042SThierry Reding * it under the terms of the GNU General Public License version 2 as 66b6b6042SThierry Reding * published by the Free Software Foundation. 76b6b6042SThierry Reding */ 86b6b6042SThierry Reding 96b6b6042SThierry Reding #include <linux/clk.h> 10a82752e1SThierry Reding #include <linux/debugfs.h> 116b6b6042SThierry Reding #include <linux/io.h> 126b6b6042SThierry Reding #include <linux/platform_device.h> 136b6b6042SThierry Reding #include <linux/reset.h> 14306a7f91SThierry Reding 157232398aSThierry Reding #include <soc/tegra/pmc.h> 166b6b6042SThierry Reding 176b6b6042SThierry Reding #include <drm/drm_dp_helper.h> 186b6b6042SThierry Reding 196b6b6042SThierry Reding #include "dc.h" 206b6b6042SThierry Reding #include "drm.h" 216b6b6042SThierry Reding #include "sor.h" 226b6b6042SThierry Reding 236b6b6042SThierry Reding struct tegra_sor { 246b6b6042SThierry Reding struct host1x_client client; 256b6b6042SThierry Reding struct tegra_output output; 266b6b6042SThierry Reding struct device *dev; 276b6b6042SThierry Reding 286b6b6042SThierry Reding void __iomem *regs; 296b6b6042SThierry Reding 306b6b6042SThierry Reding struct reset_control *rst; 316b6b6042SThierry Reding struct clk *clk_parent; 326b6b6042SThierry Reding struct clk *clk_safe; 336b6b6042SThierry Reding struct clk *clk_dp; 346b6b6042SThierry Reding struct clk *clk; 356b6b6042SThierry Reding 366b6b6042SThierry Reding struct tegra_dpaux *dpaux; 376b6b6042SThierry Reding 3886f5c52dSThierry Reding struct mutex lock; 396b6b6042SThierry Reding bool enabled; 40a82752e1SThierry Reding 41a82752e1SThierry Reding struct dentry *debugfs; 426b6b6042SThierry Reding }; 436b6b6042SThierry Reding 4434fa183bSThierry Reding struct tegra_sor_config { 4534fa183bSThierry Reding u32 bits_per_pixel; 4634fa183bSThierry Reding 4734fa183bSThierry Reding u32 active_polarity; 4834fa183bSThierry Reding u32 active_count; 4934fa183bSThierry Reding u32 tu_size; 5034fa183bSThierry Reding u32 active_frac; 5134fa183bSThierry Reding u32 watermark; 527890b576SThierry Reding 537890b576SThierry Reding u32 hblank_symbols; 547890b576SThierry Reding u32 vblank_symbols; 5534fa183bSThierry Reding }; 5634fa183bSThierry Reding 576b6b6042SThierry Reding static inline struct tegra_sor * 586b6b6042SThierry Reding host1x_client_to_sor(struct host1x_client *client) 596b6b6042SThierry Reding { 606b6b6042SThierry Reding return container_of(client, struct tegra_sor, client); 616b6b6042SThierry Reding } 626b6b6042SThierry Reding 636b6b6042SThierry Reding static inline struct tegra_sor *to_sor(struct tegra_output *output) 646b6b6042SThierry Reding { 656b6b6042SThierry Reding return container_of(output, struct tegra_sor, output); 666b6b6042SThierry Reding } 676b6b6042SThierry Reding 686b6b6042SThierry Reding static inline unsigned long tegra_sor_readl(struct tegra_sor *sor, 696b6b6042SThierry Reding unsigned long offset) 706b6b6042SThierry Reding { 716b6b6042SThierry Reding return readl(sor->regs + (offset << 2)); 726b6b6042SThierry Reding } 736b6b6042SThierry Reding 746b6b6042SThierry Reding static inline void tegra_sor_writel(struct tegra_sor *sor, unsigned long value, 756b6b6042SThierry Reding unsigned long offset) 766b6b6042SThierry Reding { 776b6b6042SThierry Reding writel(value, sor->regs + (offset << 2)); 786b6b6042SThierry Reding } 796b6b6042SThierry Reding 806b6b6042SThierry Reding static int tegra_sor_dp_train_fast(struct tegra_sor *sor, 816b6b6042SThierry Reding struct drm_dp_link *link) 826b6b6042SThierry Reding { 836b6b6042SThierry Reding unsigned long value; 846b6b6042SThierry Reding unsigned int i; 856b6b6042SThierry Reding u8 pattern; 866b6b6042SThierry Reding int err; 876b6b6042SThierry Reding 886b6b6042SThierry Reding /* setup lane parameters */ 896b6b6042SThierry Reding value = SOR_LANE_DRIVE_CURRENT_LANE3(0x40) | 906b6b6042SThierry Reding SOR_LANE_DRIVE_CURRENT_LANE2(0x40) | 916b6b6042SThierry Reding SOR_LANE_DRIVE_CURRENT_LANE1(0x40) | 926b6b6042SThierry Reding SOR_LANE_DRIVE_CURRENT_LANE0(0x40); 936b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_LANE_DRIVE_CURRENT_0); 946b6b6042SThierry Reding 956b6b6042SThierry Reding value = SOR_LANE_PREEMPHASIS_LANE3(0x0f) | 966b6b6042SThierry Reding SOR_LANE_PREEMPHASIS_LANE2(0x0f) | 976b6b6042SThierry Reding SOR_LANE_PREEMPHASIS_LANE1(0x0f) | 986b6b6042SThierry Reding SOR_LANE_PREEMPHASIS_LANE0(0x0f); 996b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_LANE_PREEMPHASIS_0); 1006b6b6042SThierry Reding 1016b6b6042SThierry Reding value = SOR_LANE_POST_CURSOR_LANE3(0x00) | 1026b6b6042SThierry Reding SOR_LANE_POST_CURSOR_LANE2(0x00) | 1036b6b6042SThierry Reding SOR_LANE_POST_CURSOR_LANE1(0x00) | 1046b6b6042SThierry Reding SOR_LANE_POST_CURSOR_LANE0(0x00); 1056b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_LANE_POST_CURSOR_0); 1066b6b6042SThierry Reding 1076b6b6042SThierry Reding /* disable LVDS mode */ 1086b6b6042SThierry Reding tegra_sor_writel(sor, 0, SOR_LVDS); 1096b6b6042SThierry Reding 1106b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); 1116b6b6042SThierry Reding value |= SOR_DP_PADCTL_TX_PU_ENABLE; 1126b6b6042SThierry Reding value &= ~SOR_DP_PADCTL_TX_PU_MASK; 1136b6b6042SThierry Reding value |= SOR_DP_PADCTL_TX_PU(2); /* XXX: don't hardcode? */ 1146b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); 1156b6b6042SThierry Reding 1166b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); 1176b6b6042SThierry Reding value |= SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 | 1186b6b6042SThierry Reding SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0; 1196b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); 1206b6b6042SThierry Reding 1216b6b6042SThierry Reding usleep_range(10, 100); 1226b6b6042SThierry Reding 1236b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); 1246b6b6042SThierry Reding value &= ~(SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 | 1256b6b6042SThierry Reding SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0); 1266b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); 1276b6b6042SThierry Reding 1286b6b6042SThierry Reding err = tegra_dpaux_prepare(sor->dpaux, DP_SET_ANSI_8B10B); 1296b6b6042SThierry Reding if (err < 0) 1306b6b6042SThierry Reding return err; 1316b6b6042SThierry Reding 1326b6b6042SThierry Reding for (i = 0, value = 0; i < link->num_lanes; i++) { 1336b6b6042SThierry Reding unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | 1346b6b6042SThierry Reding SOR_DP_TPG_SCRAMBLER_NONE | 1356b6b6042SThierry Reding SOR_DP_TPG_PATTERN_TRAIN1; 1366b6b6042SThierry Reding value = (value << 8) | lane; 1376b6b6042SThierry Reding } 1386b6b6042SThierry Reding 1396b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_TPG); 1406b6b6042SThierry Reding 1416b6b6042SThierry Reding pattern = DP_TRAINING_PATTERN_1; 1426b6b6042SThierry Reding 1436b6b6042SThierry Reding err = tegra_dpaux_train(sor->dpaux, link, pattern); 1446b6b6042SThierry Reding if (err < 0) 1456b6b6042SThierry Reding return err; 1466b6b6042SThierry Reding 1476b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_SPARE_0); 1486b6b6042SThierry Reding value |= SOR_DP_SPARE_SEQ_ENABLE; 1496b6b6042SThierry Reding value &= ~SOR_DP_SPARE_PANEL_INTERNAL; 1506b6b6042SThierry Reding value |= SOR_DP_SPARE_MACRO_SOR_CLK; 1516b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_SPARE_0); 1526b6b6042SThierry Reding 1536b6b6042SThierry Reding for (i = 0, value = 0; i < link->num_lanes; i++) { 1546b6b6042SThierry Reding unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | 1556b6b6042SThierry Reding SOR_DP_TPG_SCRAMBLER_NONE | 1566b6b6042SThierry Reding SOR_DP_TPG_PATTERN_TRAIN2; 1576b6b6042SThierry Reding value = (value << 8) | lane; 1586b6b6042SThierry Reding } 1596b6b6042SThierry Reding 1606b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_TPG); 1616b6b6042SThierry Reding 1626b6b6042SThierry Reding pattern = DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_2; 1636b6b6042SThierry Reding 1646b6b6042SThierry Reding err = tegra_dpaux_train(sor->dpaux, link, pattern); 1656b6b6042SThierry Reding if (err < 0) 1666b6b6042SThierry Reding return err; 1676b6b6042SThierry Reding 1686b6b6042SThierry Reding for (i = 0, value = 0; i < link->num_lanes; i++) { 1696b6b6042SThierry Reding unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | 1706b6b6042SThierry Reding SOR_DP_TPG_SCRAMBLER_GALIOS | 1716b6b6042SThierry Reding SOR_DP_TPG_PATTERN_NONE; 1726b6b6042SThierry Reding value = (value << 8) | lane; 1736b6b6042SThierry Reding } 1746b6b6042SThierry Reding 1756b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_TPG); 1766b6b6042SThierry Reding 1776b6b6042SThierry Reding pattern = DP_TRAINING_PATTERN_DISABLE; 1786b6b6042SThierry Reding 1796b6b6042SThierry Reding err = tegra_dpaux_train(sor->dpaux, link, pattern); 1806b6b6042SThierry Reding if (err < 0) 1816b6b6042SThierry Reding return err; 1826b6b6042SThierry Reding 1836b6b6042SThierry Reding return 0; 1846b6b6042SThierry Reding } 1856b6b6042SThierry Reding 1866b6b6042SThierry Reding static void tegra_sor_super_update(struct tegra_sor *sor) 1876b6b6042SThierry Reding { 1886b6b6042SThierry Reding tegra_sor_writel(sor, 0, SOR_SUPER_STATE_0); 1896b6b6042SThierry Reding tegra_sor_writel(sor, 1, SOR_SUPER_STATE_0); 1906b6b6042SThierry Reding tegra_sor_writel(sor, 0, SOR_SUPER_STATE_0); 1916b6b6042SThierry Reding } 1926b6b6042SThierry Reding 1936b6b6042SThierry Reding static void tegra_sor_update(struct tegra_sor *sor) 1946b6b6042SThierry Reding { 1956b6b6042SThierry Reding tegra_sor_writel(sor, 0, SOR_STATE_0); 1966b6b6042SThierry Reding tegra_sor_writel(sor, 1, SOR_STATE_0); 1976b6b6042SThierry Reding tegra_sor_writel(sor, 0, SOR_STATE_0); 1986b6b6042SThierry Reding } 1996b6b6042SThierry Reding 2006b6b6042SThierry Reding static int tegra_sor_setup_pwm(struct tegra_sor *sor, unsigned long timeout) 2016b6b6042SThierry Reding { 2026b6b6042SThierry Reding unsigned long value; 2036b6b6042SThierry Reding 2046b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PWM_DIV); 2056b6b6042SThierry Reding value &= ~SOR_PWM_DIV_MASK; 2066b6b6042SThierry Reding value |= 0x400; /* period */ 2076b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PWM_DIV); 2086b6b6042SThierry Reding 2096b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PWM_CTL); 2106b6b6042SThierry Reding value &= ~SOR_PWM_CTL_DUTY_CYCLE_MASK; 2116b6b6042SThierry Reding value |= 0x400; /* duty cycle */ 2126b6b6042SThierry Reding value &= ~SOR_PWM_CTL_CLK_SEL; /* clock source: PCLK */ 2136b6b6042SThierry Reding value |= SOR_PWM_CTL_TRIGGER; 2146b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PWM_CTL); 2156b6b6042SThierry Reding 2166b6b6042SThierry Reding timeout = jiffies + msecs_to_jiffies(timeout); 2176b6b6042SThierry Reding 2186b6b6042SThierry Reding while (time_before(jiffies, timeout)) { 2196b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PWM_CTL); 2206b6b6042SThierry Reding if ((value & SOR_PWM_CTL_TRIGGER) == 0) 2216b6b6042SThierry Reding return 0; 2226b6b6042SThierry Reding 2236b6b6042SThierry Reding usleep_range(25, 100); 2246b6b6042SThierry Reding } 2256b6b6042SThierry Reding 2266b6b6042SThierry Reding return -ETIMEDOUT; 2276b6b6042SThierry Reding } 2286b6b6042SThierry Reding 2296b6b6042SThierry Reding static int tegra_sor_attach(struct tegra_sor *sor) 2306b6b6042SThierry Reding { 2316b6b6042SThierry Reding unsigned long value, timeout; 2326b6b6042SThierry Reding 2336b6b6042SThierry Reding /* wake up in normal mode */ 2346b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); 2356b6b6042SThierry Reding value |= SOR_SUPER_STATE_HEAD_MODE_AWAKE; 2366b6b6042SThierry Reding value |= SOR_SUPER_STATE_MODE_NORMAL; 2376b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); 2386b6b6042SThierry Reding tegra_sor_super_update(sor); 2396b6b6042SThierry Reding 2406b6b6042SThierry Reding /* attach */ 2416b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); 2426b6b6042SThierry Reding value |= SOR_SUPER_STATE_ATTACHED; 2436b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); 2446b6b6042SThierry Reding tegra_sor_super_update(sor); 2456b6b6042SThierry Reding 2466b6b6042SThierry Reding timeout = jiffies + msecs_to_jiffies(250); 2476b6b6042SThierry Reding 2486b6b6042SThierry Reding while (time_before(jiffies, timeout)) { 2496b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_TEST); 2506b6b6042SThierry Reding if ((value & SOR_TEST_ATTACHED) != 0) 2516b6b6042SThierry Reding return 0; 2526b6b6042SThierry Reding 2536b6b6042SThierry Reding usleep_range(25, 100); 2546b6b6042SThierry Reding } 2556b6b6042SThierry Reding 2566b6b6042SThierry Reding return -ETIMEDOUT; 2576b6b6042SThierry Reding } 2586b6b6042SThierry Reding 2596b6b6042SThierry Reding static int tegra_sor_wakeup(struct tegra_sor *sor) 2606b6b6042SThierry Reding { 2616b6b6042SThierry Reding struct tegra_dc *dc = to_tegra_dc(sor->output.encoder.crtc); 2626b6b6042SThierry Reding unsigned long value, timeout; 2636b6b6042SThierry Reding 2646b6b6042SThierry Reding /* enable display controller outputs */ 2656b6b6042SThierry Reding value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); 2666b6b6042SThierry Reding value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | 2676b6b6042SThierry Reding PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; 2686b6b6042SThierry Reding tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); 2696b6b6042SThierry Reding 270*62b9e063SThierry Reding tegra_dc_commit(dc); 2716b6b6042SThierry Reding 2726b6b6042SThierry Reding timeout = jiffies + msecs_to_jiffies(250); 2736b6b6042SThierry Reding 2746b6b6042SThierry Reding /* wait for head to wake up */ 2756b6b6042SThierry Reding while (time_before(jiffies, timeout)) { 2766b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_TEST); 2776b6b6042SThierry Reding value &= SOR_TEST_HEAD_MODE_MASK; 2786b6b6042SThierry Reding 2796b6b6042SThierry Reding if (value == SOR_TEST_HEAD_MODE_AWAKE) 2806b6b6042SThierry Reding return 0; 2816b6b6042SThierry Reding 2826b6b6042SThierry Reding usleep_range(25, 100); 2836b6b6042SThierry Reding } 2846b6b6042SThierry Reding 2856b6b6042SThierry Reding return -ETIMEDOUT; 2866b6b6042SThierry Reding } 2876b6b6042SThierry Reding 2886b6b6042SThierry Reding static int tegra_sor_power_up(struct tegra_sor *sor, unsigned long timeout) 2896b6b6042SThierry Reding { 2906b6b6042SThierry Reding unsigned long value; 2916b6b6042SThierry Reding 2926b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PWR); 2936b6b6042SThierry Reding value |= SOR_PWR_TRIGGER | SOR_PWR_NORMAL_STATE_PU; 2946b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PWR); 2956b6b6042SThierry Reding 2966b6b6042SThierry Reding timeout = jiffies + msecs_to_jiffies(timeout); 2976b6b6042SThierry Reding 2986b6b6042SThierry Reding while (time_before(jiffies, timeout)) { 2996b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PWR); 3006b6b6042SThierry Reding if ((value & SOR_PWR_TRIGGER) == 0) 3016b6b6042SThierry Reding return 0; 3026b6b6042SThierry Reding 3036b6b6042SThierry Reding usleep_range(25, 100); 3046b6b6042SThierry Reding } 3056b6b6042SThierry Reding 3066b6b6042SThierry Reding return -ETIMEDOUT; 3076b6b6042SThierry Reding } 3086b6b6042SThierry Reding 30934fa183bSThierry Reding struct tegra_sor_params { 31034fa183bSThierry Reding /* number of link clocks per line */ 31134fa183bSThierry Reding unsigned int num_clocks; 31234fa183bSThierry Reding /* ratio between input and output */ 31334fa183bSThierry Reding u64 ratio; 31434fa183bSThierry Reding /* precision factor */ 31534fa183bSThierry Reding u64 precision; 31634fa183bSThierry Reding 31734fa183bSThierry Reding unsigned int active_polarity; 31834fa183bSThierry Reding unsigned int active_count; 31934fa183bSThierry Reding unsigned int active_frac; 32034fa183bSThierry Reding unsigned int tu_size; 32134fa183bSThierry Reding unsigned int error; 32234fa183bSThierry Reding }; 32334fa183bSThierry Reding 32434fa183bSThierry Reding static int tegra_sor_compute_params(struct tegra_sor *sor, 32534fa183bSThierry Reding struct tegra_sor_params *params, 32634fa183bSThierry Reding unsigned int tu_size) 32734fa183bSThierry Reding { 32834fa183bSThierry Reding u64 active_sym, active_count, frac, approx; 32934fa183bSThierry Reding u32 active_polarity, active_frac = 0; 33034fa183bSThierry Reding const u64 f = params->precision; 33134fa183bSThierry Reding s64 error; 33234fa183bSThierry Reding 33334fa183bSThierry Reding active_sym = params->ratio * tu_size; 33434fa183bSThierry Reding active_count = div_u64(active_sym, f) * f; 33534fa183bSThierry Reding frac = active_sym - active_count; 33634fa183bSThierry Reding 33734fa183bSThierry Reding /* fraction < 0.5 */ 33834fa183bSThierry Reding if (frac >= (f / 2)) { 33934fa183bSThierry Reding active_polarity = 1; 34034fa183bSThierry Reding frac = f - frac; 34134fa183bSThierry Reding } else { 34234fa183bSThierry Reding active_polarity = 0; 34334fa183bSThierry Reding } 34434fa183bSThierry Reding 34534fa183bSThierry Reding if (frac != 0) { 34634fa183bSThierry Reding frac = div_u64(f * f, frac); /* 1/fraction */ 34734fa183bSThierry Reding if (frac <= (15 * f)) { 34834fa183bSThierry Reding active_frac = div_u64(frac, f); 34934fa183bSThierry Reding 35034fa183bSThierry Reding /* round up */ 35134fa183bSThierry Reding if (active_polarity) 35234fa183bSThierry Reding active_frac++; 35334fa183bSThierry Reding } else { 35434fa183bSThierry Reding active_frac = active_polarity ? 1 : 15; 35534fa183bSThierry Reding } 35634fa183bSThierry Reding } 35734fa183bSThierry Reding 35834fa183bSThierry Reding if (active_frac == 1) 35934fa183bSThierry Reding active_polarity = 0; 36034fa183bSThierry Reding 36134fa183bSThierry Reding if (active_polarity == 1) { 36234fa183bSThierry Reding if (active_frac) { 36334fa183bSThierry Reding approx = active_count + (active_frac * (f - 1)) * f; 36434fa183bSThierry Reding approx = div_u64(approx, active_frac * f); 36534fa183bSThierry Reding } else { 36634fa183bSThierry Reding approx = active_count + f; 36734fa183bSThierry Reding } 36834fa183bSThierry Reding } else { 36934fa183bSThierry Reding if (active_frac) 37034fa183bSThierry Reding approx = active_count + div_u64(f, active_frac); 37134fa183bSThierry Reding else 37234fa183bSThierry Reding approx = active_count; 37334fa183bSThierry Reding } 37434fa183bSThierry Reding 37534fa183bSThierry Reding error = div_s64(active_sym - approx, tu_size); 37634fa183bSThierry Reding error *= params->num_clocks; 37734fa183bSThierry Reding 37834fa183bSThierry Reding if (error <= 0 && abs64(error) < params->error) { 37934fa183bSThierry Reding params->active_count = div_u64(active_count, f); 38034fa183bSThierry Reding params->active_polarity = active_polarity; 38134fa183bSThierry Reding params->active_frac = active_frac; 38234fa183bSThierry Reding params->error = abs64(error); 38334fa183bSThierry Reding params->tu_size = tu_size; 38434fa183bSThierry Reding 38534fa183bSThierry Reding if (error == 0) 38634fa183bSThierry Reding return true; 38734fa183bSThierry Reding } 38834fa183bSThierry Reding 38934fa183bSThierry Reding return false; 39034fa183bSThierry Reding } 39134fa183bSThierry Reding 39234fa183bSThierry Reding static int tegra_sor_calc_config(struct tegra_sor *sor, 39334fa183bSThierry Reding struct drm_display_mode *mode, 39434fa183bSThierry Reding struct tegra_sor_config *config, 39534fa183bSThierry Reding struct drm_dp_link *link) 39634fa183bSThierry Reding { 39734fa183bSThierry Reding const u64 f = 100000, link_rate = link->rate * 1000; 39834fa183bSThierry Reding const u64 pclk = mode->clock * 1000; 3997890b576SThierry Reding u64 input, output, watermark, num; 40034fa183bSThierry Reding struct tegra_sor_params params; 40134fa183bSThierry Reding u32 num_syms_per_line; 40234fa183bSThierry Reding unsigned int i; 40334fa183bSThierry Reding 40434fa183bSThierry Reding if (!link_rate || !link->num_lanes || !pclk || !config->bits_per_pixel) 40534fa183bSThierry Reding return -EINVAL; 40634fa183bSThierry Reding 40734fa183bSThierry Reding output = link_rate * 8 * link->num_lanes; 40834fa183bSThierry Reding input = pclk * config->bits_per_pixel; 40934fa183bSThierry Reding 41034fa183bSThierry Reding if (input >= output) 41134fa183bSThierry Reding return -ERANGE; 41234fa183bSThierry Reding 41334fa183bSThierry Reding memset(¶ms, 0, sizeof(params)); 41434fa183bSThierry Reding params.ratio = div64_u64(input * f, output); 41534fa183bSThierry Reding params.num_clocks = div_u64(link_rate * mode->hdisplay, pclk); 41634fa183bSThierry Reding params.precision = f; 41734fa183bSThierry Reding params.error = 64 * f; 41834fa183bSThierry Reding params.tu_size = 64; 41934fa183bSThierry Reding 42034fa183bSThierry Reding for (i = params.tu_size; i >= 32; i--) 42134fa183bSThierry Reding if (tegra_sor_compute_params(sor, ¶ms, i)) 42234fa183bSThierry Reding break; 42334fa183bSThierry Reding 42434fa183bSThierry Reding if (params.active_frac == 0) { 42534fa183bSThierry Reding config->active_polarity = 0; 42634fa183bSThierry Reding config->active_count = params.active_count; 42734fa183bSThierry Reding 42834fa183bSThierry Reding if (!params.active_polarity) 42934fa183bSThierry Reding config->active_count--; 43034fa183bSThierry Reding 43134fa183bSThierry Reding config->tu_size = params.tu_size; 43234fa183bSThierry Reding config->active_frac = 1; 43334fa183bSThierry Reding } else { 43434fa183bSThierry Reding config->active_polarity = params.active_polarity; 43534fa183bSThierry Reding config->active_count = params.active_count; 43634fa183bSThierry Reding config->active_frac = params.active_frac; 43734fa183bSThierry Reding config->tu_size = params.tu_size; 43834fa183bSThierry Reding } 43934fa183bSThierry Reding 44034fa183bSThierry Reding dev_dbg(sor->dev, 44134fa183bSThierry Reding "polarity: %d active count: %d tu size: %d active frac: %d\n", 44234fa183bSThierry Reding config->active_polarity, config->active_count, 44334fa183bSThierry Reding config->tu_size, config->active_frac); 44434fa183bSThierry Reding 44534fa183bSThierry Reding watermark = params.ratio * config->tu_size * (f - params.ratio); 44634fa183bSThierry Reding watermark = div_u64(watermark, f); 44734fa183bSThierry Reding 44834fa183bSThierry Reding watermark = div_u64(watermark + params.error, f); 44934fa183bSThierry Reding config->watermark = watermark + (config->bits_per_pixel / 8) + 2; 45034fa183bSThierry Reding num_syms_per_line = (mode->hdisplay * config->bits_per_pixel) * 45134fa183bSThierry Reding (link->num_lanes * 8); 45234fa183bSThierry Reding 45334fa183bSThierry Reding if (config->watermark > 30) { 45434fa183bSThierry Reding config->watermark = 30; 45534fa183bSThierry Reding dev_err(sor->dev, 45634fa183bSThierry Reding "unable to compute TU size, forcing watermark to %u\n", 45734fa183bSThierry Reding config->watermark); 45834fa183bSThierry Reding } else if (config->watermark > num_syms_per_line) { 45934fa183bSThierry Reding config->watermark = num_syms_per_line; 46034fa183bSThierry Reding dev_err(sor->dev, "watermark too high, forcing to %u\n", 46134fa183bSThierry Reding config->watermark); 46234fa183bSThierry Reding } 46334fa183bSThierry Reding 4647890b576SThierry Reding /* compute the number of symbols per horizontal blanking interval */ 4657890b576SThierry Reding num = ((mode->htotal - mode->hdisplay) - 7) * link_rate; 4667890b576SThierry Reding config->hblank_symbols = div_u64(num, pclk); 4677890b576SThierry Reding 4687890b576SThierry Reding if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING) 4697890b576SThierry Reding config->hblank_symbols -= 3; 4707890b576SThierry Reding 4717890b576SThierry Reding config->hblank_symbols -= 12 / link->num_lanes; 4727890b576SThierry Reding 4737890b576SThierry Reding /* compute the number of symbols per vertical blanking interval */ 4747890b576SThierry Reding num = (mode->hdisplay - 25) * link_rate; 4757890b576SThierry Reding config->vblank_symbols = div_u64(num, pclk); 4767890b576SThierry Reding config->vblank_symbols -= 36 / link->num_lanes + 4; 4777890b576SThierry Reding 4787890b576SThierry Reding dev_dbg(sor->dev, "blank symbols: H:%u V:%u\n", config->hblank_symbols, 4797890b576SThierry Reding config->vblank_symbols); 4807890b576SThierry Reding 48134fa183bSThierry Reding return 0; 48234fa183bSThierry Reding } 48334fa183bSThierry Reding 4846b6b6042SThierry Reding static int tegra_output_sor_enable(struct tegra_output *output) 4856b6b6042SThierry Reding { 4866b6b6042SThierry Reding struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); 4876b6b6042SThierry Reding struct drm_display_mode *mode = &dc->base.mode; 4886b6b6042SThierry Reding unsigned int vbe, vse, hbe, hse, vbs, hbs, i; 4896b6b6042SThierry Reding struct tegra_sor *sor = to_sor(output); 49034fa183bSThierry Reding struct tegra_sor_config config; 49134fa183bSThierry Reding struct drm_dp_link link; 49234fa183bSThierry Reding struct drm_dp_aux *aux; 4936b6b6042SThierry Reding unsigned long value; 49486f5c52dSThierry Reding int err = 0; 49586f5c52dSThierry Reding 49686f5c52dSThierry Reding mutex_lock(&sor->lock); 4976b6b6042SThierry Reding 4986b6b6042SThierry Reding if (sor->enabled) 49986f5c52dSThierry Reding goto unlock; 5006b6b6042SThierry Reding 5016b6b6042SThierry Reding err = clk_prepare_enable(sor->clk); 5026b6b6042SThierry Reding if (err < 0) 50386f5c52dSThierry Reding goto unlock; 5046b6b6042SThierry Reding 5056b6b6042SThierry Reding reset_control_deassert(sor->rst); 5066b6b6042SThierry Reding 50734fa183bSThierry Reding /* FIXME: properly convert to struct drm_dp_aux */ 50834fa183bSThierry Reding aux = (struct drm_dp_aux *)sor->dpaux; 50934fa183bSThierry Reding 5106b6b6042SThierry Reding if (sor->dpaux) { 5116b6b6042SThierry Reding err = tegra_dpaux_enable(sor->dpaux); 5126b6b6042SThierry Reding if (err < 0) 5136b6b6042SThierry Reding dev_err(sor->dev, "failed to enable DP: %d\n", err); 51434fa183bSThierry Reding 51534fa183bSThierry Reding err = drm_dp_link_probe(aux, &link); 51634fa183bSThierry Reding if (err < 0) { 51734fa183bSThierry Reding dev_err(sor->dev, "failed to probe eDP link: %d\n", 51834fa183bSThierry Reding err); 5192263c460SDan Carpenter goto unlock; 52034fa183bSThierry Reding } 5216b6b6042SThierry Reding } 5226b6b6042SThierry Reding 5236b6b6042SThierry Reding err = clk_set_parent(sor->clk, sor->clk_safe); 5246b6b6042SThierry Reding if (err < 0) 5256b6b6042SThierry Reding dev_err(sor->dev, "failed to set safe parent clock: %d\n", err); 5266b6b6042SThierry Reding 52734fa183bSThierry Reding memset(&config, 0, sizeof(config)); 528054b1bd1SStéphane Marchesin config.bits_per_pixel = output->connector.display_info.bpc * 3; 52934fa183bSThierry Reding 53034fa183bSThierry Reding err = tegra_sor_calc_config(sor, mode, &config, &link); 53134fa183bSThierry Reding if (err < 0) 53234fa183bSThierry Reding dev_err(sor->dev, "failed to compute link configuration: %d\n", 53334fa183bSThierry Reding err); 53434fa183bSThierry Reding 5356b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_CLK_CNTRL); 5366b6b6042SThierry Reding value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK; 5376b6b6042SThierry Reding value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK; 5386b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_CLK_CNTRL); 5396b6b6042SThierry Reding 5406b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 5416b6b6042SThierry Reding value &= ~SOR_PLL_2_BANDGAP_POWERDOWN; 5426b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 5436b6b6042SThierry Reding usleep_range(20, 100); 5446b6b6042SThierry Reding 5456b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_3); 5466b6b6042SThierry Reding value |= SOR_PLL_3_PLL_VDD_MODE_V3_3; 5476b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_3); 5486b6b6042SThierry Reding 5496b6b6042SThierry Reding value = SOR_PLL_0_ICHPMP(0xf) | SOR_PLL_0_VCOCAP_RST | 5506b6b6042SThierry Reding SOR_PLL_0_PLLREG_LEVEL_V45 | SOR_PLL_0_RESISTOR_EXT; 5516b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_0); 5526b6b6042SThierry Reding 5536b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 5546b6b6042SThierry Reding value |= SOR_PLL_2_SEQ_PLLCAPPD; 5556b6b6042SThierry Reding value &= ~SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE; 5566b6b6042SThierry Reding value |= SOR_PLL_2_LVDS_ENABLE; 5576b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 5586b6b6042SThierry Reding 5596b6b6042SThierry Reding value = SOR_PLL_1_TERM_COMPOUT | SOR_PLL_1_TMDS_TERM; 5606b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_1); 5616b6b6042SThierry Reding 5626b6b6042SThierry Reding while (true) { 5636b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 5646b6b6042SThierry Reding if ((value & SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE) == 0) 5656b6b6042SThierry Reding break; 5666b6b6042SThierry Reding 5676b6b6042SThierry Reding usleep_range(250, 1000); 5686b6b6042SThierry Reding } 5696b6b6042SThierry Reding 5706b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 5716b6b6042SThierry Reding value &= ~SOR_PLL_2_POWERDOWN_OVERRIDE; 5726b6b6042SThierry Reding value &= ~SOR_PLL_2_PORT_POWERDOWN; 5736b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 5746b6b6042SThierry Reding 5756b6b6042SThierry Reding /* 5766b6b6042SThierry Reding * power up 5776b6b6042SThierry Reding */ 5786b6b6042SThierry Reding 5796b6b6042SThierry Reding /* set safe link bandwidth (1.62 Gbps) */ 5806b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_CLK_CNTRL); 5816b6b6042SThierry Reding value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK; 5826b6b6042SThierry Reding value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G1_62; 5836b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_CLK_CNTRL); 5846b6b6042SThierry Reding 5856b6b6042SThierry Reding /* step 1 */ 5866b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 5876b6b6042SThierry Reding value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE | SOR_PLL_2_PORT_POWERDOWN | 5886b6b6042SThierry Reding SOR_PLL_2_BANDGAP_POWERDOWN; 5896b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 5906b6b6042SThierry Reding 5916b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_0); 5926b6b6042SThierry Reding value |= SOR_PLL_0_VCOPD | SOR_PLL_0_POWER_OFF; 5936b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_0); 5946b6b6042SThierry Reding 5956b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); 5966b6b6042SThierry Reding value &= ~SOR_DP_PADCTL_PAD_CAL_PD; 5976b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); 5986b6b6042SThierry Reding 5996b6b6042SThierry Reding /* step 2 */ 6006b6b6042SThierry Reding err = tegra_io_rail_power_on(TEGRA_IO_RAIL_LVDS); 6016b6b6042SThierry Reding if (err < 0) { 6026b6b6042SThierry Reding dev_err(sor->dev, "failed to power on I/O rail: %d\n", err); 60386f5c52dSThierry Reding goto unlock; 6046b6b6042SThierry Reding } 6056b6b6042SThierry Reding 6066b6b6042SThierry Reding usleep_range(5, 100); 6076b6b6042SThierry Reding 6086b6b6042SThierry Reding /* step 3 */ 6096b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 6106b6b6042SThierry Reding value &= ~SOR_PLL_2_BANDGAP_POWERDOWN; 6116b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 6126b6b6042SThierry Reding 6136b6b6042SThierry Reding usleep_range(20, 100); 6146b6b6042SThierry Reding 6156b6b6042SThierry Reding /* step 4 */ 6166b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_0); 6176b6b6042SThierry Reding value &= ~SOR_PLL_0_POWER_OFF; 6186b6b6042SThierry Reding value &= ~SOR_PLL_0_VCOPD; 6196b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_0); 6206b6b6042SThierry Reding 6216b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 6226b6b6042SThierry Reding value &= ~SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE; 6236b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 6246b6b6042SThierry Reding 6256b6b6042SThierry Reding usleep_range(200, 1000); 6266b6b6042SThierry Reding 6276b6b6042SThierry Reding /* step 5 */ 6286b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 6296b6b6042SThierry Reding value &= ~SOR_PLL_2_PORT_POWERDOWN; 6306b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 6316b6b6042SThierry Reding 6326b6b6042SThierry Reding /* switch to DP clock */ 6336b6b6042SThierry Reding err = clk_set_parent(sor->clk, sor->clk_dp); 6346b6b6042SThierry Reding if (err < 0) 6356b6b6042SThierry Reding dev_err(sor->dev, "failed to set DP parent clock: %d\n", err); 6366b6b6042SThierry Reding 637899451b7SThierry Reding /* power DP lanes */ 6386b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); 639899451b7SThierry Reding 640899451b7SThierry Reding if (link.num_lanes <= 2) 641899451b7SThierry Reding value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_2); 642899451b7SThierry Reding else 643899451b7SThierry Reding value |= SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_2; 644899451b7SThierry Reding 645899451b7SThierry Reding if (link.num_lanes <= 1) 646899451b7SThierry Reding value &= ~SOR_DP_PADCTL_PD_TXD_1; 647899451b7SThierry Reding else 648899451b7SThierry Reding value |= SOR_DP_PADCTL_PD_TXD_1; 649899451b7SThierry Reding 650899451b7SThierry Reding if (link.num_lanes == 0) 651899451b7SThierry Reding value &= ~SOR_DP_PADCTL_PD_TXD_0; 652899451b7SThierry Reding else 653899451b7SThierry Reding value |= SOR_DP_PADCTL_PD_TXD_0; 654899451b7SThierry Reding 6556b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); 6566b6b6042SThierry Reding 6576b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0); 6586b6b6042SThierry Reding value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK; 6590c90a184SThierry Reding value |= SOR_DP_LINKCTL_LANE_COUNT(link.num_lanes); 6606b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0); 6616b6b6042SThierry Reding 6626b6b6042SThierry Reding /* start lane sequencer */ 6636b6b6042SThierry Reding value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_DOWN | 6646b6b6042SThierry Reding SOR_LANE_SEQ_CTL_POWER_STATE_UP; 6656b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL); 6666b6b6042SThierry Reding 6676b6b6042SThierry Reding while (true) { 6686b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL); 6696b6b6042SThierry Reding if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0) 6706b6b6042SThierry Reding break; 6716b6b6042SThierry Reding 6726b6b6042SThierry Reding usleep_range(250, 1000); 6736b6b6042SThierry Reding } 6746b6b6042SThierry Reding 675a4263fedSThierry Reding /* set link bandwidth */ 6766b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_CLK_CNTRL); 6776b6b6042SThierry Reding value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK; 678a4263fedSThierry Reding value |= drm_dp_link_rate_to_bw_code(link.rate) << 2; 6796b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_CLK_CNTRL); 6806b6b6042SThierry Reding 6816b6b6042SThierry Reding /* set linkctl */ 6826b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0); 6836b6b6042SThierry Reding value |= SOR_DP_LINKCTL_ENABLE; 6846b6b6042SThierry Reding 6856b6b6042SThierry Reding value &= ~SOR_DP_LINKCTL_TU_SIZE_MASK; 68634fa183bSThierry Reding value |= SOR_DP_LINKCTL_TU_SIZE(config.tu_size); 6876b6b6042SThierry Reding 6886b6b6042SThierry Reding value |= SOR_DP_LINKCTL_ENHANCED_FRAME; 6896b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0); 6906b6b6042SThierry Reding 6916b6b6042SThierry Reding for (i = 0, value = 0; i < 4; i++) { 6926b6b6042SThierry Reding unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | 6936b6b6042SThierry Reding SOR_DP_TPG_SCRAMBLER_GALIOS | 6946b6b6042SThierry Reding SOR_DP_TPG_PATTERN_NONE; 6956b6b6042SThierry Reding value = (value << 8) | lane; 6966b6b6042SThierry Reding } 6976b6b6042SThierry Reding 6986b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_TPG); 6996b6b6042SThierry Reding 7006b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_CONFIG_0); 7016b6b6042SThierry Reding value &= ~SOR_DP_CONFIG_WATERMARK_MASK; 70234fa183bSThierry Reding value |= SOR_DP_CONFIG_WATERMARK(config.watermark); 7036b6b6042SThierry Reding 7046b6b6042SThierry Reding value &= ~SOR_DP_CONFIG_ACTIVE_SYM_COUNT_MASK; 70534fa183bSThierry Reding value |= SOR_DP_CONFIG_ACTIVE_SYM_COUNT(config.active_count); 7066b6b6042SThierry Reding 7076b6b6042SThierry Reding value &= ~SOR_DP_CONFIG_ACTIVE_SYM_FRAC_MASK; 70834fa183bSThierry Reding value |= SOR_DP_CONFIG_ACTIVE_SYM_FRAC(config.active_frac); 7096b6b6042SThierry Reding 71034fa183bSThierry Reding if (config.active_polarity) 71134fa183bSThierry Reding value |= SOR_DP_CONFIG_ACTIVE_SYM_POLARITY; 71234fa183bSThierry Reding else 71334fa183bSThierry Reding value &= ~SOR_DP_CONFIG_ACTIVE_SYM_POLARITY; 7146b6b6042SThierry Reding 7156b6b6042SThierry Reding value |= SOR_DP_CONFIG_ACTIVE_SYM_ENABLE; 7161f64ae7cSThierry Reding value |= SOR_DP_CONFIG_DISPARITY_NEGATIVE; 7176b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_CONFIG_0); 7186b6b6042SThierry Reding 7196b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_AUDIO_HBLANK_SYMBOLS); 7206b6b6042SThierry Reding value &= ~SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK; 7217890b576SThierry Reding value |= config.hblank_symbols & 0xffff; 7226b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_AUDIO_HBLANK_SYMBOLS); 7236b6b6042SThierry Reding 7246b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_AUDIO_VBLANK_SYMBOLS); 7256b6b6042SThierry Reding value &= ~SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK; 7267890b576SThierry Reding value |= config.vblank_symbols & 0xffff; 7276b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_AUDIO_VBLANK_SYMBOLS); 7286b6b6042SThierry Reding 7296b6b6042SThierry Reding /* enable pad calibration logic */ 7306b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); 7316b6b6042SThierry Reding value |= SOR_DP_PADCTL_PAD_CAL_PD; 7326b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); 7336b6b6042SThierry Reding 7346b6b6042SThierry Reding if (sor->dpaux) { 7356b6b6042SThierry Reding u8 rate, lanes; 7366b6b6042SThierry Reding 7376b6b6042SThierry Reding err = drm_dp_link_probe(aux, &link); 7386b6b6042SThierry Reding if (err < 0) { 7396b6b6042SThierry Reding dev_err(sor->dev, "failed to probe eDP link: %d\n", 7406b6b6042SThierry Reding err); 74186f5c52dSThierry Reding goto unlock; 7426b6b6042SThierry Reding } 7436b6b6042SThierry Reding 7446b6b6042SThierry Reding err = drm_dp_link_power_up(aux, &link); 7456b6b6042SThierry Reding if (err < 0) { 7466b6b6042SThierry Reding dev_err(sor->dev, "failed to power up eDP link: %d\n", 7476b6b6042SThierry Reding err); 74886f5c52dSThierry Reding goto unlock; 7496b6b6042SThierry Reding } 7506b6b6042SThierry Reding 7516b6b6042SThierry Reding err = drm_dp_link_configure(aux, &link); 7526b6b6042SThierry Reding if (err < 0) { 7536b6b6042SThierry Reding dev_err(sor->dev, "failed to configure eDP link: %d\n", 7546b6b6042SThierry Reding err); 75586f5c52dSThierry Reding goto unlock; 7566b6b6042SThierry Reding } 7576b6b6042SThierry Reding 7586b6b6042SThierry Reding rate = drm_dp_link_rate_to_bw_code(link.rate); 7596b6b6042SThierry Reding lanes = link.num_lanes; 7606b6b6042SThierry Reding 7616b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_CLK_CNTRL); 7626b6b6042SThierry Reding value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK; 7636b6b6042SThierry Reding value |= SOR_CLK_CNTRL_DP_LINK_SPEED(rate); 7646b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_CLK_CNTRL); 7656b6b6042SThierry Reding 7666b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0); 7676b6b6042SThierry Reding value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK; 7686b6b6042SThierry Reding value |= SOR_DP_LINKCTL_LANE_COUNT(lanes); 7696b6b6042SThierry Reding 7706b6b6042SThierry Reding if (link.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) 7716b6b6042SThierry Reding value |= SOR_DP_LINKCTL_ENHANCED_FRAME; 7726b6b6042SThierry Reding 7736b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0); 7746b6b6042SThierry Reding 7756b6b6042SThierry Reding /* disable training pattern generator */ 7766b6b6042SThierry Reding 7776b6b6042SThierry Reding for (i = 0; i < link.num_lanes; i++) { 7786b6b6042SThierry Reding unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | 7796b6b6042SThierry Reding SOR_DP_TPG_SCRAMBLER_GALIOS | 7806b6b6042SThierry Reding SOR_DP_TPG_PATTERN_NONE; 7816b6b6042SThierry Reding value = (value << 8) | lane; 7826b6b6042SThierry Reding } 7836b6b6042SThierry Reding 7846b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_TPG); 7856b6b6042SThierry Reding 7866b6b6042SThierry Reding err = tegra_sor_dp_train_fast(sor, &link); 7876b6b6042SThierry Reding if (err < 0) { 7886b6b6042SThierry Reding dev_err(sor->dev, "DP fast link training failed: %d\n", 7896b6b6042SThierry Reding err); 79086f5c52dSThierry Reding goto unlock; 7916b6b6042SThierry Reding } 7926b6b6042SThierry Reding 7936b6b6042SThierry Reding dev_dbg(sor->dev, "fast link training succeeded\n"); 7946b6b6042SThierry Reding } 7956b6b6042SThierry Reding 7966b6b6042SThierry Reding err = tegra_sor_power_up(sor, 250); 7976b6b6042SThierry Reding if (err < 0) { 7986b6b6042SThierry Reding dev_err(sor->dev, "failed to power up SOR: %d\n", err); 79986f5c52dSThierry Reding goto unlock; 8006b6b6042SThierry Reding } 8016b6b6042SThierry Reding 8026b6b6042SThierry Reding /* start display controller in continuous mode */ 8036b6b6042SThierry Reding value = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS); 8046b6b6042SThierry Reding value |= WRITE_MUX; 8056b6b6042SThierry Reding tegra_dc_writel(dc, value, DC_CMD_STATE_ACCESS); 8066b6b6042SThierry Reding 8076b6b6042SThierry Reding tegra_dc_writel(dc, VSYNC_H_POSITION(1), DC_DISP_DISP_TIMING_OPTIONS); 8086b6b6042SThierry Reding tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, DC_CMD_DISPLAY_COMMAND); 8096b6b6042SThierry Reding 8106b6b6042SThierry Reding value = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS); 8116b6b6042SThierry Reding value &= ~WRITE_MUX; 8126b6b6042SThierry Reding tegra_dc_writel(dc, value, DC_CMD_STATE_ACCESS); 8136b6b6042SThierry Reding 8146b6b6042SThierry Reding /* 8156b6b6042SThierry Reding * configure panel (24bpp, vsync-, hsync-, DP-A protocol, complete 8166b6b6042SThierry Reding * raster, associate with display controller) 8176b6b6042SThierry Reding */ 8183f4f3b5fSThierry Reding value = SOR_STATE_ASY_PROTOCOL_DP_A | 8196b6b6042SThierry Reding SOR_STATE_ASY_CRC_MODE_COMPLETE | 8206b6b6042SThierry Reding SOR_STATE_ASY_OWNER(dc->pipe + 1); 82134fa183bSThierry Reding 8223f4f3b5fSThierry Reding if (mode->flags & DRM_MODE_FLAG_PHSYNC) 8233f4f3b5fSThierry Reding value &= ~SOR_STATE_ASY_HSYNCPOL; 8243f4f3b5fSThierry Reding 8253f4f3b5fSThierry Reding if (mode->flags & DRM_MODE_FLAG_NHSYNC) 8263f4f3b5fSThierry Reding value |= SOR_STATE_ASY_HSYNCPOL; 8273f4f3b5fSThierry Reding 8283f4f3b5fSThierry Reding if (mode->flags & DRM_MODE_FLAG_PVSYNC) 8293f4f3b5fSThierry Reding value &= ~SOR_STATE_ASY_VSYNCPOL; 8303f4f3b5fSThierry Reding 8313f4f3b5fSThierry Reding if (mode->flags & DRM_MODE_FLAG_NVSYNC) 8323f4f3b5fSThierry Reding value |= SOR_STATE_ASY_VSYNCPOL; 8333f4f3b5fSThierry Reding 83434fa183bSThierry Reding switch (config.bits_per_pixel) { 83534fa183bSThierry Reding case 24: 83634fa183bSThierry Reding value |= SOR_STATE_ASY_PIXELDEPTH_BPP_24_444; 83734fa183bSThierry Reding break; 83834fa183bSThierry Reding 83934fa183bSThierry Reding case 18: 84034fa183bSThierry Reding value |= SOR_STATE_ASY_PIXELDEPTH_BPP_18_444; 84134fa183bSThierry Reding break; 84234fa183bSThierry Reding 84334fa183bSThierry Reding default: 84434fa183bSThierry Reding BUG(); 84534fa183bSThierry Reding break; 84634fa183bSThierry Reding } 84734fa183bSThierry Reding 8486b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_STATE_1); 8496b6b6042SThierry Reding 8506b6b6042SThierry Reding /* 8516b6b6042SThierry Reding * TODO: The video timing programming below doesn't seem to match the 8526b6b6042SThierry Reding * register definitions. 8536b6b6042SThierry Reding */ 8546b6b6042SThierry Reding 8556b6b6042SThierry Reding value = ((mode->vtotal & 0x7fff) << 16) | (mode->htotal & 0x7fff); 8566b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_HEAD_STATE_1(0)); 8576b6b6042SThierry Reding 8586b6b6042SThierry Reding vse = mode->vsync_end - mode->vsync_start - 1; 8596b6b6042SThierry Reding hse = mode->hsync_end - mode->hsync_start - 1; 8606b6b6042SThierry Reding 8616b6b6042SThierry Reding value = ((vse & 0x7fff) << 16) | (hse & 0x7fff); 8626b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_HEAD_STATE_2(0)); 8636b6b6042SThierry Reding 8646b6b6042SThierry Reding vbe = vse + (mode->vsync_start - mode->vdisplay); 8656b6b6042SThierry Reding hbe = hse + (mode->hsync_start - mode->hdisplay); 8666b6b6042SThierry Reding 8676b6b6042SThierry Reding value = ((vbe & 0x7fff) << 16) | (hbe & 0x7fff); 8686b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_HEAD_STATE_3(0)); 8696b6b6042SThierry Reding 8706b6b6042SThierry Reding vbs = vbe + mode->vdisplay; 8716b6b6042SThierry Reding hbs = hbe + mode->hdisplay; 8726b6b6042SThierry Reding 8736b6b6042SThierry Reding value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff); 8746b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_HEAD_STATE_4(0)); 8756b6b6042SThierry Reding 8766b6b6042SThierry Reding /* CSTM (LVDS, link A/B, upper) */ 877143b1df2SStéphane Marchesin value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_A | SOR_CSTM_LINK_ACT_B | 8786b6b6042SThierry Reding SOR_CSTM_UPPER; 8796b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_CSTM); 8806b6b6042SThierry Reding 8816b6b6042SThierry Reding /* PWM setup */ 8826b6b6042SThierry Reding err = tegra_sor_setup_pwm(sor, 250); 8836b6b6042SThierry Reding if (err < 0) { 8846b6b6042SThierry Reding dev_err(sor->dev, "failed to setup PWM: %d\n", err); 88586f5c52dSThierry Reding goto unlock; 8866b6b6042SThierry Reding } 8876b6b6042SThierry Reding 8886b6b6042SThierry Reding value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); 8896b6b6042SThierry Reding value |= SOR_ENABLE; 8906b6b6042SThierry Reding tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); 8916b6b6042SThierry Reding 8926b6b6042SThierry Reding tegra_sor_update(sor); 8936b6b6042SThierry Reding 8946b6b6042SThierry Reding err = tegra_sor_attach(sor); 8956b6b6042SThierry Reding if (err < 0) { 8966b6b6042SThierry Reding dev_err(sor->dev, "failed to attach SOR: %d\n", err); 89786f5c52dSThierry Reding goto unlock; 8986b6b6042SThierry Reding } 8996b6b6042SThierry Reding 9006b6b6042SThierry Reding err = tegra_sor_wakeup(sor); 9016b6b6042SThierry Reding if (err < 0) { 9026b6b6042SThierry Reding dev_err(sor->dev, "failed to enable DC: %d\n", err); 90386f5c52dSThierry Reding goto unlock; 9046b6b6042SThierry Reding } 9056b6b6042SThierry Reding 9066b6b6042SThierry Reding sor->enabled = true; 9076b6b6042SThierry Reding 90886f5c52dSThierry Reding unlock: 90986f5c52dSThierry Reding mutex_unlock(&sor->lock); 91086f5c52dSThierry Reding return err; 9116b6b6042SThierry Reding } 9126b6b6042SThierry Reding 9136b6b6042SThierry Reding static int tegra_sor_detach(struct tegra_sor *sor) 9146b6b6042SThierry Reding { 9156b6b6042SThierry Reding unsigned long value, timeout; 9166b6b6042SThierry Reding 9176b6b6042SThierry Reding /* switch to safe mode */ 9186b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); 9196b6b6042SThierry Reding value &= ~SOR_SUPER_STATE_MODE_NORMAL; 9206b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); 9216b6b6042SThierry Reding tegra_sor_super_update(sor); 9226b6b6042SThierry Reding 9236b6b6042SThierry Reding timeout = jiffies + msecs_to_jiffies(250); 9246b6b6042SThierry Reding 9256b6b6042SThierry Reding while (time_before(jiffies, timeout)) { 9266b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PWR); 9276b6b6042SThierry Reding if (value & SOR_PWR_MODE_SAFE) 9286b6b6042SThierry Reding break; 9296b6b6042SThierry Reding } 9306b6b6042SThierry Reding 9316b6b6042SThierry Reding if ((value & SOR_PWR_MODE_SAFE) == 0) 9326b6b6042SThierry Reding return -ETIMEDOUT; 9336b6b6042SThierry Reding 9346b6b6042SThierry Reding /* go to sleep */ 9356b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); 9366b6b6042SThierry Reding value &= ~SOR_SUPER_STATE_HEAD_MODE_MASK; 9376b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); 9386b6b6042SThierry Reding tegra_sor_super_update(sor); 9396b6b6042SThierry Reding 9406b6b6042SThierry Reding /* detach */ 9416b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); 9426b6b6042SThierry Reding value &= ~SOR_SUPER_STATE_ATTACHED; 9436b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); 9446b6b6042SThierry Reding tegra_sor_super_update(sor); 9456b6b6042SThierry Reding 9466b6b6042SThierry Reding timeout = jiffies + msecs_to_jiffies(250); 9476b6b6042SThierry Reding 9486b6b6042SThierry Reding while (time_before(jiffies, timeout)) { 9496b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_TEST); 9506b6b6042SThierry Reding if ((value & SOR_TEST_ATTACHED) == 0) 9516b6b6042SThierry Reding break; 9526b6b6042SThierry Reding 9536b6b6042SThierry Reding usleep_range(25, 100); 9546b6b6042SThierry Reding } 9556b6b6042SThierry Reding 9566b6b6042SThierry Reding if ((value & SOR_TEST_ATTACHED) != 0) 9576b6b6042SThierry Reding return -ETIMEDOUT; 9586b6b6042SThierry Reding 9596b6b6042SThierry Reding return 0; 9606b6b6042SThierry Reding } 9616b6b6042SThierry Reding 9626b6b6042SThierry Reding static int tegra_sor_power_down(struct tegra_sor *sor) 9636b6b6042SThierry Reding { 9646b6b6042SThierry Reding unsigned long value, timeout; 9656b6b6042SThierry Reding int err; 9666b6b6042SThierry Reding 9676b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PWR); 9686b6b6042SThierry Reding value &= ~SOR_PWR_NORMAL_STATE_PU; 9696b6b6042SThierry Reding value |= SOR_PWR_TRIGGER; 9706b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PWR); 9716b6b6042SThierry Reding 9726b6b6042SThierry Reding timeout = jiffies + msecs_to_jiffies(250); 9736b6b6042SThierry Reding 9746b6b6042SThierry Reding while (time_before(jiffies, timeout)) { 9756b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PWR); 9766b6b6042SThierry Reding if ((value & SOR_PWR_TRIGGER) == 0) 9776b6b6042SThierry Reding return 0; 9786b6b6042SThierry Reding 9796b6b6042SThierry Reding usleep_range(25, 100); 9806b6b6042SThierry Reding } 9816b6b6042SThierry Reding 9826b6b6042SThierry Reding if ((value & SOR_PWR_TRIGGER) != 0) 9836b6b6042SThierry Reding return -ETIMEDOUT; 9846b6b6042SThierry Reding 9856b6b6042SThierry Reding err = clk_set_parent(sor->clk, sor->clk_safe); 9866b6b6042SThierry Reding if (err < 0) 9876b6b6042SThierry Reding dev_err(sor->dev, "failed to set safe parent clock: %d\n", err); 9886b6b6042SThierry Reding 9896b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); 9906b6b6042SThierry Reding value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 | 9916b6b6042SThierry Reding SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2); 9926b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); 9936b6b6042SThierry Reding 9946b6b6042SThierry Reding /* stop lane sequencer */ 995ca185c68SStéphane Marchesin value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP | 9966b6b6042SThierry Reding SOR_LANE_SEQ_CTL_POWER_STATE_DOWN; 9976b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL); 9986b6b6042SThierry Reding 9996b6b6042SThierry Reding timeout = jiffies + msecs_to_jiffies(250); 10006b6b6042SThierry Reding 10016b6b6042SThierry Reding while (time_before(jiffies, timeout)) { 10026b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL); 10036b6b6042SThierry Reding if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0) 10046b6b6042SThierry Reding break; 10056b6b6042SThierry Reding 10066b6b6042SThierry Reding usleep_range(25, 100); 10076b6b6042SThierry Reding } 10086b6b6042SThierry Reding 10096b6b6042SThierry Reding if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0) 10106b6b6042SThierry Reding return -ETIMEDOUT; 10116b6b6042SThierry Reding 10126b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 10136b6b6042SThierry Reding value |= SOR_PLL_2_PORT_POWERDOWN; 10146b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 10156b6b6042SThierry Reding 10166b6b6042SThierry Reding usleep_range(20, 100); 10176b6b6042SThierry Reding 10186b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_0); 10196b6b6042SThierry Reding value |= SOR_PLL_0_POWER_OFF; 10206b6b6042SThierry Reding value |= SOR_PLL_0_VCOPD; 10216b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_0); 10226b6b6042SThierry Reding 10236b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 10246b6b6042SThierry Reding value |= SOR_PLL_2_SEQ_PLLCAPPD; 10256b6b6042SThierry Reding value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE; 10266b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 10276b6b6042SThierry Reding 10286b6b6042SThierry Reding usleep_range(20, 100); 10296b6b6042SThierry Reding 10306b6b6042SThierry Reding return 0; 10316b6b6042SThierry Reding } 10326b6b6042SThierry Reding 10336b6b6042SThierry Reding static int tegra_output_sor_disable(struct tegra_output *output) 10346b6b6042SThierry Reding { 10356b6b6042SThierry Reding struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); 10366b6b6042SThierry Reding struct tegra_sor *sor = to_sor(output); 10376b6b6042SThierry Reding unsigned long value; 103886f5c52dSThierry Reding int err = 0; 103986f5c52dSThierry Reding 104086f5c52dSThierry Reding mutex_lock(&sor->lock); 10416b6b6042SThierry Reding 10426b6b6042SThierry Reding if (!sor->enabled) 104386f5c52dSThierry Reding goto unlock; 10446b6b6042SThierry Reding 10456b6b6042SThierry Reding err = tegra_sor_detach(sor); 10466b6b6042SThierry Reding if (err < 0) { 10476b6b6042SThierry Reding dev_err(sor->dev, "failed to detach SOR: %d\n", err); 104886f5c52dSThierry Reding goto unlock; 10496b6b6042SThierry Reding } 10506b6b6042SThierry Reding 10516b6b6042SThierry Reding tegra_sor_writel(sor, 0, SOR_STATE_1); 10526b6b6042SThierry Reding tegra_sor_update(sor); 10536b6b6042SThierry Reding 10546b6b6042SThierry Reding /* 10556b6b6042SThierry Reding * The following accesses registers of the display controller, so make 10566b6b6042SThierry Reding * sure it's only executed when the output is attached to one. 10576b6b6042SThierry Reding */ 10586b6b6042SThierry Reding if (dc) { 10596b6b6042SThierry Reding /* 10606b6b6042SThierry Reding * XXX: We can't do this here because it causes the SOR to go 10616b6b6042SThierry Reding * into an erroneous state and the output will look scrambled 10626b6b6042SThierry Reding * the next time it is enabled. Presumably this is because we 10636b6b6042SThierry Reding * should be doing this only on the next VBLANK. A possible 10646b6b6042SThierry Reding * solution would be to queue a "power-off" event to trigger 10656b6b6042SThierry Reding * this code to be run during the next VBLANK. 10666b6b6042SThierry Reding */ 10676b6b6042SThierry Reding /* 10686b6b6042SThierry Reding value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); 10696b6b6042SThierry Reding value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | 10706b6b6042SThierry Reding PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); 10716b6b6042SThierry Reding tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); 10726b6b6042SThierry Reding */ 10736b6b6042SThierry Reding 10746b6b6042SThierry Reding value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); 10756b6b6042SThierry Reding value &= ~DISP_CTRL_MODE_MASK; 10766b6b6042SThierry Reding tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); 10776b6b6042SThierry Reding 10786b6b6042SThierry Reding value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); 10796b6b6042SThierry Reding value &= ~SOR_ENABLE; 10806b6b6042SThierry Reding tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); 10816b6b6042SThierry Reding 1082*62b9e063SThierry Reding tegra_dc_commit(dc); 10836b6b6042SThierry Reding } 10846b6b6042SThierry Reding 10856b6b6042SThierry Reding err = tegra_sor_power_down(sor); 10866b6b6042SThierry Reding if (err < 0) { 10876b6b6042SThierry Reding dev_err(sor->dev, "failed to power down SOR: %d\n", err); 108886f5c52dSThierry Reding goto unlock; 10896b6b6042SThierry Reding } 10906b6b6042SThierry Reding 10916b6b6042SThierry Reding if (sor->dpaux) { 10926b6b6042SThierry Reding err = tegra_dpaux_disable(sor->dpaux); 10936b6b6042SThierry Reding if (err < 0) { 10946b6b6042SThierry Reding dev_err(sor->dev, "failed to disable DP: %d\n", err); 109586f5c52dSThierry Reding goto unlock; 10966b6b6042SThierry Reding } 10976b6b6042SThierry Reding } 10986b6b6042SThierry Reding 10996b6b6042SThierry Reding err = tegra_io_rail_power_off(TEGRA_IO_RAIL_LVDS); 11006b6b6042SThierry Reding if (err < 0) { 11016b6b6042SThierry Reding dev_err(sor->dev, "failed to power off I/O rail: %d\n", err); 110286f5c52dSThierry Reding goto unlock; 11036b6b6042SThierry Reding } 11046b6b6042SThierry Reding 11056b6b6042SThierry Reding reset_control_assert(sor->rst); 11066b6b6042SThierry Reding clk_disable_unprepare(sor->clk); 11076b6b6042SThierry Reding 11086b6b6042SThierry Reding sor->enabled = false; 11096b6b6042SThierry Reding 111086f5c52dSThierry Reding unlock: 111186f5c52dSThierry Reding mutex_unlock(&sor->lock); 111286f5c52dSThierry Reding return err; 11136b6b6042SThierry Reding } 11146b6b6042SThierry Reding 11156b6b6042SThierry Reding static int tegra_output_sor_setup_clock(struct tegra_output *output, 111691eded9bSThierry Reding struct clk *clk, unsigned long pclk, 111791eded9bSThierry Reding unsigned int *div) 11186b6b6042SThierry Reding { 11196b6b6042SThierry Reding struct tegra_sor *sor = to_sor(output); 11206b6b6042SThierry Reding int err; 11216b6b6042SThierry Reding 11226b6b6042SThierry Reding err = clk_set_parent(clk, sor->clk_parent); 11236b6b6042SThierry Reding if (err < 0) { 11246b6b6042SThierry Reding dev_err(sor->dev, "failed to set parent clock: %d\n", err); 11256b6b6042SThierry Reding return err; 11266b6b6042SThierry Reding } 11276b6b6042SThierry Reding 11286b6b6042SThierry Reding err = clk_set_rate(sor->clk_parent, pclk); 11296b6b6042SThierry Reding if (err < 0) { 113091eded9bSThierry Reding dev_err(sor->dev, "failed to set clock rate to %lu Hz\n", pclk); 11316b6b6042SThierry Reding return err; 11326b6b6042SThierry Reding } 11336b6b6042SThierry Reding 113491eded9bSThierry Reding *div = 0; 113591eded9bSThierry Reding 11366b6b6042SThierry Reding return 0; 11376b6b6042SThierry Reding } 11386b6b6042SThierry Reding 11396b6b6042SThierry Reding static int tegra_output_sor_check_mode(struct tegra_output *output, 11406b6b6042SThierry Reding struct drm_display_mode *mode, 11416b6b6042SThierry Reding enum drm_mode_status *status) 11426b6b6042SThierry Reding { 11436b6b6042SThierry Reding /* 11446b6b6042SThierry Reding * FIXME: For now, always assume that the mode is okay. 11456b6b6042SThierry Reding */ 11466b6b6042SThierry Reding 11476b6b6042SThierry Reding *status = MODE_OK; 11486b6b6042SThierry Reding 11496b6b6042SThierry Reding return 0; 11506b6b6042SThierry Reding } 11516b6b6042SThierry Reding 11526b6b6042SThierry Reding static enum drm_connector_status 11536b6b6042SThierry Reding tegra_output_sor_detect(struct tegra_output *output) 11546b6b6042SThierry Reding { 11556b6b6042SThierry Reding struct tegra_sor *sor = to_sor(output); 11566b6b6042SThierry Reding 11576b6b6042SThierry Reding if (sor->dpaux) 11586b6b6042SThierry Reding return tegra_dpaux_detect(sor->dpaux); 11596b6b6042SThierry Reding 11606b6b6042SThierry Reding return connector_status_unknown; 11616b6b6042SThierry Reding } 11626b6b6042SThierry Reding 11636b6b6042SThierry Reding static const struct tegra_output_ops sor_ops = { 11646b6b6042SThierry Reding .enable = tegra_output_sor_enable, 11656b6b6042SThierry Reding .disable = tegra_output_sor_disable, 11666b6b6042SThierry Reding .setup_clock = tegra_output_sor_setup_clock, 11676b6b6042SThierry Reding .check_mode = tegra_output_sor_check_mode, 11686b6b6042SThierry Reding .detect = tegra_output_sor_detect, 11696b6b6042SThierry Reding }; 11706b6b6042SThierry Reding 1171a82752e1SThierry Reding static int tegra_sor_crc_open(struct inode *inode, struct file *file) 1172a82752e1SThierry Reding { 1173a82752e1SThierry Reding file->private_data = inode->i_private; 1174a82752e1SThierry Reding 1175a82752e1SThierry Reding return 0; 1176a82752e1SThierry Reding } 1177a82752e1SThierry Reding 1178a82752e1SThierry Reding static int tegra_sor_crc_release(struct inode *inode, struct file *file) 1179a82752e1SThierry Reding { 1180a82752e1SThierry Reding return 0; 1181a82752e1SThierry Reding } 1182a82752e1SThierry Reding 1183a82752e1SThierry Reding static int tegra_sor_crc_wait(struct tegra_sor *sor, unsigned long timeout) 1184a82752e1SThierry Reding { 1185a82752e1SThierry Reding u32 value; 1186a82752e1SThierry Reding 1187a82752e1SThierry Reding timeout = jiffies + msecs_to_jiffies(timeout); 1188a82752e1SThierry Reding 1189a82752e1SThierry Reding while (time_before(jiffies, timeout)) { 1190a82752e1SThierry Reding value = tegra_sor_readl(sor, SOR_CRC_A); 1191a82752e1SThierry Reding if (value & SOR_CRC_A_VALID) 1192a82752e1SThierry Reding return 0; 1193a82752e1SThierry Reding 1194a82752e1SThierry Reding usleep_range(100, 200); 1195a82752e1SThierry Reding } 1196a82752e1SThierry Reding 1197a82752e1SThierry Reding return -ETIMEDOUT; 1198a82752e1SThierry Reding } 1199a82752e1SThierry Reding 1200a82752e1SThierry Reding static ssize_t tegra_sor_crc_read(struct file *file, char __user *buffer, 1201a82752e1SThierry Reding size_t size, loff_t *ppos) 1202a82752e1SThierry Reding { 1203a82752e1SThierry Reding struct tegra_sor *sor = file->private_data; 120486f5c52dSThierry Reding ssize_t num, err; 1205a82752e1SThierry Reding char buf[10]; 1206a82752e1SThierry Reding u32 value; 120786f5c52dSThierry Reding 120886f5c52dSThierry Reding mutex_lock(&sor->lock); 120986f5c52dSThierry Reding 121086f5c52dSThierry Reding if (!sor->enabled) { 121186f5c52dSThierry Reding err = -EAGAIN; 121286f5c52dSThierry Reding goto unlock; 121386f5c52dSThierry Reding } 1214a82752e1SThierry Reding 1215a82752e1SThierry Reding value = tegra_sor_readl(sor, SOR_STATE_1); 1216a82752e1SThierry Reding value &= ~SOR_STATE_ASY_CRC_MODE_MASK; 1217a82752e1SThierry Reding tegra_sor_writel(sor, value, SOR_STATE_1); 1218a82752e1SThierry Reding 1219a82752e1SThierry Reding value = tegra_sor_readl(sor, SOR_CRC_CNTRL); 1220a82752e1SThierry Reding value |= SOR_CRC_CNTRL_ENABLE; 1221a82752e1SThierry Reding tegra_sor_writel(sor, value, SOR_CRC_CNTRL); 1222a82752e1SThierry Reding 1223a82752e1SThierry Reding value = tegra_sor_readl(sor, SOR_TEST); 1224a82752e1SThierry Reding value &= ~SOR_TEST_CRC_POST_SERIALIZE; 1225a82752e1SThierry Reding tegra_sor_writel(sor, value, SOR_TEST); 1226a82752e1SThierry Reding 1227a82752e1SThierry Reding err = tegra_sor_crc_wait(sor, 100); 1228a82752e1SThierry Reding if (err < 0) 122986f5c52dSThierry Reding goto unlock; 1230a82752e1SThierry Reding 1231a82752e1SThierry Reding tegra_sor_writel(sor, SOR_CRC_A_RESET, SOR_CRC_A); 1232a82752e1SThierry Reding value = tegra_sor_readl(sor, SOR_CRC_B); 1233a82752e1SThierry Reding 1234a82752e1SThierry Reding num = scnprintf(buf, sizeof(buf), "%08x\n", value); 1235a82752e1SThierry Reding 123686f5c52dSThierry Reding err = simple_read_from_buffer(buffer, size, ppos, buf, num); 123786f5c52dSThierry Reding 123886f5c52dSThierry Reding unlock: 123986f5c52dSThierry Reding mutex_unlock(&sor->lock); 124086f5c52dSThierry Reding return err; 1241a82752e1SThierry Reding } 1242a82752e1SThierry Reding 1243a82752e1SThierry Reding static const struct file_operations tegra_sor_crc_fops = { 1244a82752e1SThierry Reding .owner = THIS_MODULE, 1245a82752e1SThierry Reding .open = tegra_sor_crc_open, 1246a82752e1SThierry Reding .read = tegra_sor_crc_read, 1247a82752e1SThierry Reding .release = tegra_sor_crc_release, 1248a82752e1SThierry Reding }; 1249a82752e1SThierry Reding 12501b0c7b48SThierry Reding static int tegra_sor_debugfs_init(struct tegra_sor *sor, 12511b0c7b48SThierry Reding struct drm_minor *minor) 1252a82752e1SThierry Reding { 1253a82752e1SThierry Reding struct dentry *entry; 1254a82752e1SThierry Reding int err = 0; 1255a82752e1SThierry Reding 12561b0c7b48SThierry Reding sor->debugfs = debugfs_create_dir("sor", minor->debugfs_root); 1257a82752e1SThierry Reding if (!sor->debugfs) 1258a82752e1SThierry Reding return -ENOMEM; 1259a82752e1SThierry Reding 1260a82752e1SThierry Reding entry = debugfs_create_file("crc", 0644, sor->debugfs, sor, 1261a82752e1SThierry Reding &tegra_sor_crc_fops); 1262a82752e1SThierry Reding if (!entry) { 1263a82752e1SThierry Reding dev_err(sor->dev, 1264a82752e1SThierry Reding "cannot create /sys/kernel/debug/dri/%s/sor/crc\n", 12651b0c7b48SThierry Reding minor->debugfs_root->d_name.name); 1266a82752e1SThierry Reding err = -ENOMEM; 1267a82752e1SThierry Reding goto remove; 1268a82752e1SThierry Reding } 1269a82752e1SThierry Reding 1270a82752e1SThierry Reding return err; 1271a82752e1SThierry Reding 1272a82752e1SThierry Reding remove: 1273a82752e1SThierry Reding debugfs_remove(sor->debugfs); 1274a82752e1SThierry Reding sor->debugfs = NULL; 1275a82752e1SThierry Reding return err; 1276a82752e1SThierry Reding } 1277a82752e1SThierry Reding 1278a82752e1SThierry Reding static int tegra_sor_debugfs_exit(struct tegra_sor *sor) 1279a82752e1SThierry Reding { 12809578184eSThierry Reding debugfs_remove_recursive(sor->debugfs); 1281a82752e1SThierry Reding sor->debugfs = NULL; 1282a82752e1SThierry Reding 1283a82752e1SThierry Reding return 0; 1284a82752e1SThierry Reding } 1285a82752e1SThierry Reding 12866b6b6042SThierry Reding static int tegra_sor_init(struct host1x_client *client) 12876b6b6042SThierry Reding { 12889910f5c4SThierry Reding struct drm_device *drm = dev_get_drvdata(client->parent); 12896b6b6042SThierry Reding struct tegra_sor *sor = host1x_client_to_sor(client); 12906b6b6042SThierry Reding int err; 12916b6b6042SThierry Reding 12926b6b6042SThierry Reding if (!sor->dpaux) 12936b6b6042SThierry Reding return -ENODEV; 12946b6b6042SThierry Reding 12956b6b6042SThierry Reding sor->output.type = TEGRA_OUTPUT_EDP; 12966b6b6042SThierry Reding 12976b6b6042SThierry Reding sor->output.dev = sor->dev; 12986b6b6042SThierry Reding sor->output.ops = &sor_ops; 12996b6b6042SThierry Reding 13009910f5c4SThierry Reding err = tegra_output_init(drm, &sor->output); 13016b6b6042SThierry Reding if (err < 0) { 13026b6b6042SThierry Reding dev_err(sor->dev, "output setup failed: %d\n", err); 13036b6b6042SThierry Reding return err; 13046b6b6042SThierry Reding } 13056b6b6042SThierry Reding 1306a82752e1SThierry Reding if (IS_ENABLED(CONFIG_DEBUG_FS)) { 13071b0c7b48SThierry Reding err = tegra_sor_debugfs_init(sor, drm->primary); 1308a82752e1SThierry Reding if (err < 0) 1309a82752e1SThierry Reding dev_err(sor->dev, "debugfs setup failed: %d\n", err); 1310a82752e1SThierry Reding } 1311a82752e1SThierry Reding 13126b6b6042SThierry Reding if (sor->dpaux) { 13136b6b6042SThierry Reding err = tegra_dpaux_attach(sor->dpaux, &sor->output); 13146b6b6042SThierry Reding if (err < 0) { 13156b6b6042SThierry Reding dev_err(sor->dev, "failed to attach DP: %d\n", err); 13166b6b6042SThierry Reding return err; 13176b6b6042SThierry Reding } 13186b6b6042SThierry Reding } 13196b6b6042SThierry Reding 13206b6b6042SThierry Reding return 0; 13216b6b6042SThierry Reding } 13226b6b6042SThierry Reding 13236b6b6042SThierry Reding static int tegra_sor_exit(struct host1x_client *client) 13246b6b6042SThierry Reding { 13256b6b6042SThierry Reding struct tegra_sor *sor = host1x_client_to_sor(client); 13266b6b6042SThierry Reding int err; 13276b6b6042SThierry Reding 13286b6b6042SThierry Reding err = tegra_output_disable(&sor->output); 13296b6b6042SThierry Reding if (err < 0) { 13306b6b6042SThierry Reding dev_err(sor->dev, "output failed to disable: %d\n", err); 13316b6b6042SThierry Reding return err; 13326b6b6042SThierry Reding } 13336b6b6042SThierry Reding 13346b6b6042SThierry Reding if (sor->dpaux) { 13356b6b6042SThierry Reding err = tegra_dpaux_detach(sor->dpaux); 13366b6b6042SThierry Reding if (err < 0) { 13376b6b6042SThierry Reding dev_err(sor->dev, "failed to detach DP: %d\n", err); 13386b6b6042SThierry Reding return err; 13396b6b6042SThierry Reding } 13406b6b6042SThierry Reding } 13416b6b6042SThierry Reding 1342a82752e1SThierry Reding if (IS_ENABLED(CONFIG_DEBUG_FS)) { 1343a82752e1SThierry Reding err = tegra_sor_debugfs_exit(sor); 1344a82752e1SThierry Reding if (err < 0) 1345a82752e1SThierry Reding dev_err(sor->dev, "debugfs cleanup failed: %d\n", err); 1346a82752e1SThierry Reding } 1347a82752e1SThierry Reding 13486b6b6042SThierry Reding err = tegra_output_exit(&sor->output); 13496b6b6042SThierry Reding if (err < 0) { 13506b6b6042SThierry Reding dev_err(sor->dev, "output cleanup failed: %d\n", err); 13516b6b6042SThierry Reding return err; 13526b6b6042SThierry Reding } 13536b6b6042SThierry Reding 13546b6b6042SThierry Reding return 0; 13556b6b6042SThierry Reding } 13566b6b6042SThierry Reding 13576b6b6042SThierry Reding static const struct host1x_client_ops sor_client_ops = { 13586b6b6042SThierry Reding .init = tegra_sor_init, 13596b6b6042SThierry Reding .exit = tegra_sor_exit, 13606b6b6042SThierry Reding }; 13616b6b6042SThierry Reding 13626b6b6042SThierry Reding static int tegra_sor_probe(struct platform_device *pdev) 13636b6b6042SThierry Reding { 13646b6b6042SThierry Reding struct device_node *np; 13656b6b6042SThierry Reding struct tegra_sor *sor; 13666b6b6042SThierry Reding struct resource *regs; 13676b6b6042SThierry Reding int err; 13686b6b6042SThierry Reding 13696b6b6042SThierry Reding sor = devm_kzalloc(&pdev->dev, sizeof(*sor), GFP_KERNEL); 13706b6b6042SThierry Reding if (!sor) 13716b6b6042SThierry Reding return -ENOMEM; 13726b6b6042SThierry Reding 13736b6b6042SThierry Reding sor->output.dev = sor->dev = &pdev->dev; 13746b6b6042SThierry Reding 13756b6b6042SThierry Reding np = of_parse_phandle(pdev->dev.of_node, "nvidia,dpaux", 0); 13766b6b6042SThierry Reding if (np) { 13776b6b6042SThierry Reding sor->dpaux = tegra_dpaux_find_by_of_node(np); 13786b6b6042SThierry Reding of_node_put(np); 13796b6b6042SThierry Reding 13806b6b6042SThierry Reding if (!sor->dpaux) 13816b6b6042SThierry Reding return -EPROBE_DEFER; 13826b6b6042SThierry Reding } 13836b6b6042SThierry Reding 13846b6b6042SThierry Reding err = tegra_output_probe(&sor->output); 13856b6b6042SThierry Reding if (err < 0) 13866b6b6042SThierry Reding return err; 13876b6b6042SThierry Reding 13886b6b6042SThierry Reding regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 13896b6b6042SThierry Reding sor->regs = devm_ioremap_resource(&pdev->dev, regs); 13906b6b6042SThierry Reding if (IS_ERR(sor->regs)) 13916b6b6042SThierry Reding return PTR_ERR(sor->regs); 13926b6b6042SThierry Reding 13936b6b6042SThierry Reding sor->rst = devm_reset_control_get(&pdev->dev, "sor"); 13946b6b6042SThierry Reding if (IS_ERR(sor->rst)) 13956b6b6042SThierry Reding return PTR_ERR(sor->rst); 13966b6b6042SThierry Reding 13976b6b6042SThierry Reding sor->clk = devm_clk_get(&pdev->dev, NULL); 13986b6b6042SThierry Reding if (IS_ERR(sor->clk)) 13996b6b6042SThierry Reding return PTR_ERR(sor->clk); 14006b6b6042SThierry Reding 14016b6b6042SThierry Reding sor->clk_parent = devm_clk_get(&pdev->dev, "parent"); 14026b6b6042SThierry Reding if (IS_ERR(sor->clk_parent)) 14036b6b6042SThierry Reding return PTR_ERR(sor->clk_parent); 14046b6b6042SThierry Reding 14056b6b6042SThierry Reding err = clk_prepare_enable(sor->clk_parent); 14066b6b6042SThierry Reding if (err < 0) 14076b6b6042SThierry Reding return err; 14086b6b6042SThierry Reding 14096b6b6042SThierry Reding sor->clk_safe = devm_clk_get(&pdev->dev, "safe"); 14106b6b6042SThierry Reding if (IS_ERR(sor->clk_safe)) 14116b6b6042SThierry Reding return PTR_ERR(sor->clk_safe); 14126b6b6042SThierry Reding 14136b6b6042SThierry Reding err = clk_prepare_enable(sor->clk_safe); 14146b6b6042SThierry Reding if (err < 0) 14156b6b6042SThierry Reding return err; 14166b6b6042SThierry Reding 14176b6b6042SThierry Reding sor->clk_dp = devm_clk_get(&pdev->dev, "dp"); 14186b6b6042SThierry Reding if (IS_ERR(sor->clk_dp)) 14196b6b6042SThierry Reding return PTR_ERR(sor->clk_dp); 14206b6b6042SThierry Reding 14216b6b6042SThierry Reding err = clk_prepare_enable(sor->clk_dp); 14226b6b6042SThierry Reding if (err < 0) 14236b6b6042SThierry Reding return err; 14246b6b6042SThierry Reding 14256b6b6042SThierry Reding INIT_LIST_HEAD(&sor->client.list); 14266b6b6042SThierry Reding sor->client.ops = &sor_client_ops; 14276b6b6042SThierry Reding sor->client.dev = &pdev->dev; 14286b6b6042SThierry Reding 142986f5c52dSThierry Reding mutex_init(&sor->lock); 143086f5c52dSThierry Reding 14316b6b6042SThierry Reding err = host1x_client_register(&sor->client); 14326b6b6042SThierry Reding if (err < 0) { 14336b6b6042SThierry Reding dev_err(&pdev->dev, "failed to register host1x client: %d\n", 14346b6b6042SThierry Reding err); 14356b6b6042SThierry Reding return err; 14366b6b6042SThierry Reding } 14376b6b6042SThierry Reding 14386b6b6042SThierry Reding platform_set_drvdata(pdev, sor); 14396b6b6042SThierry Reding 14406b6b6042SThierry Reding return 0; 14416b6b6042SThierry Reding } 14426b6b6042SThierry Reding 14436b6b6042SThierry Reding static int tegra_sor_remove(struct platform_device *pdev) 14446b6b6042SThierry Reding { 14456b6b6042SThierry Reding struct tegra_sor *sor = platform_get_drvdata(pdev); 14466b6b6042SThierry Reding int err; 14476b6b6042SThierry Reding 14486b6b6042SThierry Reding err = host1x_client_unregister(&sor->client); 14496b6b6042SThierry Reding if (err < 0) { 14506b6b6042SThierry Reding dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", 14516b6b6042SThierry Reding err); 14526b6b6042SThierry Reding return err; 14536b6b6042SThierry Reding } 14546b6b6042SThierry Reding 14556b6b6042SThierry Reding clk_disable_unprepare(sor->clk_parent); 14566b6b6042SThierry Reding clk_disable_unprepare(sor->clk_safe); 14576b6b6042SThierry Reding clk_disable_unprepare(sor->clk_dp); 14586b6b6042SThierry Reding clk_disable_unprepare(sor->clk); 14596b6b6042SThierry Reding 14606b6b6042SThierry Reding return 0; 14616b6b6042SThierry Reding } 14626b6b6042SThierry Reding 14636b6b6042SThierry Reding static const struct of_device_id tegra_sor_of_match[] = { 14646b6b6042SThierry Reding { .compatible = "nvidia,tegra124-sor", }, 14656b6b6042SThierry Reding { }, 14666b6b6042SThierry Reding }; 1467ef70728cSStephen Warren MODULE_DEVICE_TABLE(of, tegra_sor_of_match); 14686b6b6042SThierry Reding 14696b6b6042SThierry Reding struct platform_driver tegra_sor_driver = { 14706b6b6042SThierry Reding .driver = { 14716b6b6042SThierry Reding .name = "tegra-sor", 14726b6b6042SThierry Reding .of_match_table = tegra_sor_of_match, 14736b6b6042SThierry Reding }, 14746b6b6042SThierry Reding .probe = tegra_sor_probe, 14756b6b6042SThierry Reding .remove = tegra_sor_remove, 14766b6b6042SThierry Reding }; 1477