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> 116fad8f66SThierry Reding #include <linux/gpio.h> 126b6b6042SThierry Reding #include <linux/io.h> 136b6b6042SThierry Reding #include <linux/platform_device.h> 146b6b6042SThierry Reding #include <linux/reset.h> 15306a7f91SThierry Reding 167232398aSThierry Reding #include <soc/tegra/pmc.h> 176b6b6042SThierry Reding 18*4aa3df71SThierry Reding #include <drm/drm_atomic_helper.h> 196b6b6042SThierry Reding #include <drm/drm_dp_helper.h> 206fad8f66SThierry Reding #include <drm/drm_panel.h> 216b6b6042SThierry Reding 226b6b6042SThierry Reding #include "dc.h" 236b6b6042SThierry Reding #include "drm.h" 246b6b6042SThierry Reding #include "sor.h" 256b6b6042SThierry Reding 266b6b6042SThierry Reding struct tegra_sor { 276b6b6042SThierry Reding struct host1x_client client; 286b6b6042SThierry Reding struct tegra_output output; 296b6b6042SThierry Reding struct device *dev; 306b6b6042SThierry Reding 316b6b6042SThierry Reding void __iomem *regs; 326b6b6042SThierry Reding 336b6b6042SThierry Reding struct reset_control *rst; 346b6b6042SThierry Reding struct clk *clk_parent; 356b6b6042SThierry Reding struct clk *clk_safe; 366b6b6042SThierry Reding struct clk *clk_dp; 376b6b6042SThierry Reding struct clk *clk; 386b6b6042SThierry Reding 396b6b6042SThierry Reding struct tegra_dpaux *dpaux; 406b6b6042SThierry Reding 4186f5c52dSThierry Reding struct mutex lock; 426b6b6042SThierry Reding bool enabled; 43a82752e1SThierry Reding 44a82752e1SThierry Reding struct dentry *debugfs; 456b6b6042SThierry Reding }; 466b6b6042SThierry Reding 4734fa183bSThierry Reding struct tegra_sor_config { 4834fa183bSThierry Reding u32 bits_per_pixel; 4934fa183bSThierry Reding 5034fa183bSThierry Reding u32 active_polarity; 5134fa183bSThierry Reding u32 active_count; 5234fa183bSThierry Reding u32 tu_size; 5334fa183bSThierry Reding u32 active_frac; 5434fa183bSThierry Reding u32 watermark; 557890b576SThierry Reding 567890b576SThierry Reding u32 hblank_symbols; 577890b576SThierry Reding u32 vblank_symbols; 5834fa183bSThierry Reding }; 5934fa183bSThierry Reding 606b6b6042SThierry Reding static inline struct tegra_sor * 616b6b6042SThierry Reding host1x_client_to_sor(struct host1x_client *client) 626b6b6042SThierry Reding { 636b6b6042SThierry Reding return container_of(client, struct tegra_sor, client); 646b6b6042SThierry Reding } 656b6b6042SThierry Reding 666b6b6042SThierry Reding static inline struct tegra_sor *to_sor(struct tegra_output *output) 676b6b6042SThierry Reding { 686b6b6042SThierry Reding return container_of(output, struct tegra_sor, output); 696b6b6042SThierry Reding } 706b6b6042SThierry Reding 716b6b6042SThierry Reding static inline unsigned long tegra_sor_readl(struct tegra_sor *sor, 726b6b6042SThierry Reding unsigned long offset) 736b6b6042SThierry Reding { 746b6b6042SThierry Reding return readl(sor->regs + (offset << 2)); 756b6b6042SThierry Reding } 766b6b6042SThierry Reding 776b6b6042SThierry Reding static inline void tegra_sor_writel(struct tegra_sor *sor, unsigned long value, 786b6b6042SThierry Reding unsigned long offset) 796b6b6042SThierry Reding { 806b6b6042SThierry Reding writel(value, sor->regs + (offset << 2)); 816b6b6042SThierry Reding } 826b6b6042SThierry Reding 836b6b6042SThierry Reding static int tegra_sor_dp_train_fast(struct tegra_sor *sor, 846b6b6042SThierry Reding struct drm_dp_link *link) 856b6b6042SThierry Reding { 866b6b6042SThierry Reding unsigned long value; 876b6b6042SThierry Reding unsigned int i; 886b6b6042SThierry Reding u8 pattern; 896b6b6042SThierry Reding int err; 906b6b6042SThierry Reding 916b6b6042SThierry Reding /* setup lane parameters */ 926b6b6042SThierry Reding value = SOR_LANE_DRIVE_CURRENT_LANE3(0x40) | 936b6b6042SThierry Reding SOR_LANE_DRIVE_CURRENT_LANE2(0x40) | 946b6b6042SThierry Reding SOR_LANE_DRIVE_CURRENT_LANE1(0x40) | 956b6b6042SThierry Reding SOR_LANE_DRIVE_CURRENT_LANE0(0x40); 966b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_LANE_DRIVE_CURRENT_0); 976b6b6042SThierry Reding 986b6b6042SThierry Reding value = SOR_LANE_PREEMPHASIS_LANE3(0x0f) | 996b6b6042SThierry Reding SOR_LANE_PREEMPHASIS_LANE2(0x0f) | 1006b6b6042SThierry Reding SOR_LANE_PREEMPHASIS_LANE1(0x0f) | 1016b6b6042SThierry Reding SOR_LANE_PREEMPHASIS_LANE0(0x0f); 1026b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_LANE_PREEMPHASIS_0); 1036b6b6042SThierry Reding 1046b6b6042SThierry Reding value = SOR_LANE_POST_CURSOR_LANE3(0x00) | 1056b6b6042SThierry Reding SOR_LANE_POST_CURSOR_LANE2(0x00) | 1066b6b6042SThierry Reding SOR_LANE_POST_CURSOR_LANE1(0x00) | 1076b6b6042SThierry Reding SOR_LANE_POST_CURSOR_LANE0(0x00); 1086b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_LANE_POST_CURSOR_0); 1096b6b6042SThierry Reding 1106b6b6042SThierry Reding /* disable LVDS mode */ 1116b6b6042SThierry Reding tegra_sor_writel(sor, 0, SOR_LVDS); 1126b6b6042SThierry Reding 1136b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); 1146b6b6042SThierry Reding value |= SOR_DP_PADCTL_TX_PU_ENABLE; 1156b6b6042SThierry Reding value &= ~SOR_DP_PADCTL_TX_PU_MASK; 1166b6b6042SThierry Reding value |= SOR_DP_PADCTL_TX_PU(2); /* XXX: don't hardcode? */ 1176b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); 1186b6b6042SThierry Reding 1196b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); 1206b6b6042SThierry Reding value |= SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 | 1216b6b6042SThierry Reding SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0; 1226b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); 1236b6b6042SThierry Reding 1246b6b6042SThierry Reding usleep_range(10, 100); 1256b6b6042SThierry Reding 1266b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); 1276b6b6042SThierry Reding value &= ~(SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 | 1286b6b6042SThierry Reding SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0); 1296b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); 1306b6b6042SThierry Reding 1316b6b6042SThierry Reding err = tegra_dpaux_prepare(sor->dpaux, DP_SET_ANSI_8B10B); 1326b6b6042SThierry Reding if (err < 0) 1336b6b6042SThierry Reding return err; 1346b6b6042SThierry Reding 1356b6b6042SThierry Reding for (i = 0, value = 0; i < link->num_lanes; i++) { 1366b6b6042SThierry Reding unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | 1376b6b6042SThierry Reding SOR_DP_TPG_SCRAMBLER_NONE | 1386b6b6042SThierry Reding SOR_DP_TPG_PATTERN_TRAIN1; 1396b6b6042SThierry Reding value = (value << 8) | lane; 1406b6b6042SThierry Reding } 1416b6b6042SThierry Reding 1426b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_TPG); 1436b6b6042SThierry Reding 1446b6b6042SThierry Reding pattern = DP_TRAINING_PATTERN_1; 1456b6b6042SThierry Reding 1466b6b6042SThierry Reding err = tegra_dpaux_train(sor->dpaux, link, pattern); 1476b6b6042SThierry Reding if (err < 0) 1486b6b6042SThierry Reding return err; 1496b6b6042SThierry Reding 1506b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_SPARE_0); 1516b6b6042SThierry Reding value |= SOR_DP_SPARE_SEQ_ENABLE; 1526b6b6042SThierry Reding value &= ~SOR_DP_SPARE_PANEL_INTERNAL; 1536b6b6042SThierry Reding value |= SOR_DP_SPARE_MACRO_SOR_CLK; 1546b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_SPARE_0); 1556b6b6042SThierry Reding 1566b6b6042SThierry Reding for (i = 0, value = 0; i < link->num_lanes; i++) { 1576b6b6042SThierry Reding unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | 1586b6b6042SThierry Reding SOR_DP_TPG_SCRAMBLER_NONE | 1596b6b6042SThierry Reding SOR_DP_TPG_PATTERN_TRAIN2; 1606b6b6042SThierry Reding value = (value << 8) | lane; 1616b6b6042SThierry Reding } 1626b6b6042SThierry Reding 1636b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_TPG); 1646b6b6042SThierry Reding 1656b6b6042SThierry Reding pattern = DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_2; 1666b6b6042SThierry Reding 1676b6b6042SThierry Reding err = tegra_dpaux_train(sor->dpaux, link, pattern); 1686b6b6042SThierry Reding if (err < 0) 1696b6b6042SThierry Reding return err; 1706b6b6042SThierry Reding 1716b6b6042SThierry Reding for (i = 0, value = 0; i < link->num_lanes; i++) { 1726b6b6042SThierry Reding unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | 1736b6b6042SThierry Reding SOR_DP_TPG_SCRAMBLER_GALIOS | 1746b6b6042SThierry Reding SOR_DP_TPG_PATTERN_NONE; 1756b6b6042SThierry Reding value = (value << 8) | lane; 1766b6b6042SThierry Reding } 1776b6b6042SThierry Reding 1786b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_TPG); 1796b6b6042SThierry Reding 1806b6b6042SThierry Reding pattern = DP_TRAINING_PATTERN_DISABLE; 1816b6b6042SThierry Reding 1826b6b6042SThierry Reding err = tegra_dpaux_train(sor->dpaux, link, pattern); 1836b6b6042SThierry Reding if (err < 0) 1846b6b6042SThierry Reding return err; 1856b6b6042SThierry Reding 1866b6b6042SThierry Reding return 0; 1876b6b6042SThierry Reding } 1886b6b6042SThierry Reding 1896b6b6042SThierry Reding static void tegra_sor_super_update(struct tegra_sor *sor) 1906b6b6042SThierry Reding { 1916b6b6042SThierry Reding tegra_sor_writel(sor, 0, SOR_SUPER_STATE_0); 1926b6b6042SThierry Reding tegra_sor_writel(sor, 1, SOR_SUPER_STATE_0); 1936b6b6042SThierry Reding tegra_sor_writel(sor, 0, SOR_SUPER_STATE_0); 1946b6b6042SThierry Reding } 1956b6b6042SThierry Reding 1966b6b6042SThierry Reding static void tegra_sor_update(struct tegra_sor *sor) 1976b6b6042SThierry Reding { 1986b6b6042SThierry Reding tegra_sor_writel(sor, 0, SOR_STATE_0); 1996b6b6042SThierry Reding tegra_sor_writel(sor, 1, SOR_STATE_0); 2006b6b6042SThierry Reding tegra_sor_writel(sor, 0, SOR_STATE_0); 2016b6b6042SThierry Reding } 2026b6b6042SThierry Reding 2036b6b6042SThierry Reding static int tegra_sor_setup_pwm(struct tegra_sor *sor, unsigned long timeout) 2046b6b6042SThierry Reding { 2056b6b6042SThierry Reding unsigned long value; 2066b6b6042SThierry Reding 2076b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PWM_DIV); 2086b6b6042SThierry Reding value &= ~SOR_PWM_DIV_MASK; 2096b6b6042SThierry Reding value |= 0x400; /* period */ 2106b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PWM_DIV); 2116b6b6042SThierry Reding 2126b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PWM_CTL); 2136b6b6042SThierry Reding value &= ~SOR_PWM_CTL_DUTY_CYCLE_MASK; 2146b6b6042SThierry Reding value |= 0x400; /* duty cycle */ 2156b6b6042SThierry Reding value &= ~SOR_PWM_CTL_CLK_SEL; /* clock source: PCLK */ 2166b6b6042SThierry Reding value |= SOR_PWM_CTL_TRIGGER; 2176b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PWM_CTL); 2186b6b6042SThierry Reding 2196b6b6042SThierry Reding timeout = jiffies + msecs_to_jiffies(timeout); 2206b6b6042SThierry Reding 2216b6b6042SThierry Reding while (time_before(jiffies, timeout)) { 2226b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PWM_CTL); 2236b6b6042SThierry Reding if ((value & SOR_PWM_CTL_TRIGGER) == 0) 2246b6b6042SThierry Reding return 0; 2256b6b6042SThierry Reding 2266b6b6042SThierry Reding usleep_range(25, 100); 2276b6b6042SThierry Reding } 2286b6b6042SThierry Reding 2296b6b6042SThierry Reding return -ETIMEDOUT; 2306b6b6042SThierry Reding } 2316b6b6042SThierry Reding 2326b6b6042SThierry Reding static int tegra_sor_attach(struct tegra_sor *sor) 2336b6b6042SThierry Reding { 2346b6b6042SThierry Reding unsigned long value, timeout; 2356b6b6042SThierry Reding 2366b6b6042SThierry Reding /* wake up in normal mode */ 2376b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); 2386b6b6042SThierry Reding value |= SOR_SUPER_STATE_HEAD_MODE_AWAKE; 2396b6b6042SThierry Reding value |= SOR_SUPER_STATE_MODE_NORMAL; 2406b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); 2416b6b6042SThierry Reding tegra_sor_super_update(sor); 2426b6b6042SThierry Reding 2436b6b6042SThierry Reding /* attach */ 2446b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); 2456b6b6042SThierry Reding value |= SOR_SUPER_STATE_ATTACHED; 2466b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); 2476b6b6042SThierry Reding tegra_sor_super_update(sor); 2486b6b6042SThierry Reding 2496b6b6042SThierry Reding timeout = jiffies + msecs_to_jiffies(250); 2506b6b6042SThierry Reding 2516b6b6042SThierry Reding while (time_before(jiffies, timeout)) { 2526b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_TEST); 2536b6b6042SThierry Reding if ((value & SOR_TEST_ATTACHED) != 0) 2546b6b6042SThierry Reding return 0; 2556b6b6042SThierry Reding 2566b6b6042SThierry Reding usleep_range(25, 100); 2576b6b6042SThierry Reding } 2586b6b6042SThierry Reding 2596b6b6042SThierry Reding return -ETIMEDOUT; 2606b6b6042SThierry Reding } 2616b6b6042SThierry Reding 2626b6b6042SThierry Reding static int tegra_sor_wakeup(struct tegra_sor *sor) 2636b6b6042SThierry Reding { 2646b6b6042SThierry Reding struct tegra_dc *dc = to_tegra_dc(sor->output.encoder.crtc); 2656b6b6042SThierry Reding unsigned long value, timeout; 2666b6b6042SThierry Reding 2676b6b6042SThierry Reding /* enable display controller outputs */ 2686b6b6042SThierry Reding value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); 2696b6b6042SThierry Reding value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | 2706b6b6042SThierry Reding PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; 2716b6b6042SThierry Reding tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); 2726b6b6042SThierry Reding 27362b9e063SThierry Reding tegra_dc_commit(dc); 2746b6b6042SThierry Reding 2756b6b6042SThierry Reding timeout = jiffies + msecs_to_jiffies(250); 2766b6b6042SThierry Reding 2776b6b6042SThierry Reding /* wait for head to wake up */ 2786b6b6042SThierry Reding while (time_before(jiffies, timeout)) { 2796b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_TEST); 2806b6b6042SThierry Reding value &= SOR_TEST_HEAD_MODE_MASK; 2816b6b6042SThierry Reding 2826b6b6042SThierry Reding if (value == SOR_TEST_HEAD_MODE_AWAKE) 2836b6b6042SThierry Reding return 0; 2846b6b6042SThierry Reding 2856b6b6042SThierry Reding usleep_range(25, 100); 2866b6b6042SThierry Reding } 2876b6b6042SThierry Reding 2886b6b6042SThierry Reding return -ETIMEDOUT; 2896b6b6042SThierry Reding } 2906b6b6042SThierry Reding 2916b6b6042SThierry Reding static int tegra_sor_power_up(struct tegra_sor *sor, unsigned long timeout) 2926b6b6042SThierry Reding { 2936b6b6042SThierry Reding unsigned long value; 2946b6b6042SThierry Reding 2956b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PWR); 2966b6b6042SThierry Reding value |= SOR_PWR_TRIGGER | SOR_PWR_NORMAL_STATE_PU; 2976b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PWR); 2986b6b6042SThierry Reding 2996b6b6042SThierry Reding timeout = jiffies + msecs_to_jiffies(timeout); 3006b6b6042SThierry Reding 3016b6b6042SThierry Reding while (time_before(jiffies, timeout)) { 3026b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PWR); 3036b6b6042SThierry Reding if ((value & SOR_PWR_TRIGGER) == 0) 3046b6b6042SThierry Reding return 0; 3056b6b6042SThierry Reding 3066b6b6042SThierry Reding usleep_range(25, 100); 3076b6b6042SThierry Reding } 3086b6b6042SThierry Reding 3096b6b6042SThierry Reding return -ETIMEDOUT; 3106b6b6042SThierry Reding } 3116b6b6042SThierry Reding 31234fa183bSThierry Reding struct tegra_sor_params { 31334fa183bSThierry Reding /* number of link clocks per line */ 31434fa183bSThierry Reding unsigned int num_clocks; 31534fa183bSThierry Reding /* ratio between input and output */ 31634fa183bSThierry Reding u64 ratio; 31734fa183bSThierry Reding /* precision factor */ 31834fa183bSThierry Reding u64 precision; 31934fa183bSThierry Reding 32034fa183bSThierry Reding unsigned int active_polarity; 32134fa183bSThierry Reding unsigned int active_count; 32234fa183bSThierry Reding unsigned int active_frac; 32334fa183bSThierry Reding unsigned int tu_size; 32434fa183bSThierry Reding unsigned int error; 32534fa183bSThierry Reding }; 32634fa183bSThierry Reding 32734fa183bSThierry Reding static int tegra_sor_compute_params(struct tegra_sor *sor, 32834fa183bSThierry Reding struct tegra_sor_params *params, 32934fa183bSThierry Reding unsigned int tu_size) 33034fa183bSThierry Reding { 33134fa183bSThierry Reding u64 active_sym, active_count, frac, approx; 33234fa183bSThierry Reding u32 active_polarity, active_frac = 0; 33334fa183bSThierry Reding const u64 f = params->precision; 33434fa183bSThierry Reding s64 error; 33534fa183bSThierry Reding 33634fa183bSThierry Reding active_sym = params->ratio * tu_size; 33734fa183bSThierry Reding active_count = div_u64(active_sym, f) * f; 33834fa183bSThierry Reding frac = active_sym - active_count; 33934fa183bSThierry Reding 34034fa183bSThierry Reding /* fraction < 0.5 */ 34134fa183bSThierry Reding if (frac >= (f / 2)) { 34234fa183bSThierry Reding active_polarity = 1; 34334fa183bSThierry Reding frac = f - frac; 34434fa183bSThierry Reding } else { 34534fa183bSThierry Reding active_polarity = 0; 34634fa183bSThierry Reding } 34734fa183bSThierry Reding 34834fa183bSThierry Reding if (frac != 0) { 34934fa183bSThierry Reding frac = div_u64(f * f, frac); /* 1/fraction */ 35034fa183bSThierry Reding if (frac <= (15 * f)) { 35134fa183bSThierry Reding active_frac = div_u64(frac, f); 35234fa183bSThierry Reding 35334fa183bSThierry Reding /* round up */ 35434fa183bSThierry Reding if (active_polarity) 35534fa183bSThierry Reding active_frac++; 35634fa183bSThierry Reding } else { 35734fa183bSThierry Reding active_frac = active_polarity ? 1 : 15; 35834fa183bSThierry Reding } 35934fa183bSThierry Reding } 36034fa183bSThierry Reding 36134fa183bSThierry Reding if (active_frac == 1) 36234fa183bSThierry Reding active_polarity = 0; 36334fa183bSThierry Reding 36434fa183bSThierry Reding if (active_polarity == 1) { 36534fa183bSThierry Reding if (active_frac) { 36634fa183bSThierry Reding approx = active_count + (active_frac * (f - 1)) * f; 36734fa183bSThierry Reding approx = div_u64(approx, active_frac * f); 36834fa183bSThierry Reding } else { 36934fa183bSThierry Reding approx = active_count + f; 37034fa183bSThierry Reding } 37134fa183bSThierry Reding } else { 37234fa183bSThierry Reding if (active_frac) 37334fa183bSThierry Reding approx = active_count + div_u64(f, active_frac); 37434fa183bSThierry Reding else 37534fa183bSThierry Reding approx = active_count; 37634fa183bSThierry Reding } 37734fa183bSThierry Reding 37834fa183bSThierry Reding error = div_s64(active_sym - approx, tu_size); 37934fa183bSThierry Reding error *= params->num_clocks; 38034fa183bSThierry Reding 38134fa183bSThierry Reding if (error <= 0 && abs64(error) < params->error) { 38234fa183bSThierry Reding params->active_count = div_u64(active_count, f); 38334fa183bSThierry Reding params->active_polarity = active_polarity; 38434fa183bSThierry Reding params->active_frac = active_frac; 38534fa183bSThierry Reding params->error = abs64(error); 38634fa183bSThierry Reding params->tu_size = tu_size; 38734fa183bSThierry Reding 38834fa183bSThierry Reding if (error == 0) 38934fa183bSThierry Reding return true; 39034fa183bSThierry Reding } 39134fa183bSThierry Reding 39234fa183bSThierry Reding return false; 39334fa183bSThierry Reding } 39434fa183bSThierry Reding 39534fa183bSThierry Reding static int tegra_sor_calc_config(struct tegra_sor *sor, 39634fa183bSThierry Reding struct drm_display_mode *mode, 39734fa183bSThierry Reding struct tegra_sor_config *config, 39834fa183bSThierry Reding struct drm_dp_link *link) 39934fa183bSThierry Reding { 40034fa183bSThierry Reding const u64 f = 100000, link_rate = link->rate * 1000; 40134fa183bSThierry Reding const u64 pclk = mode->clock * 1000; 4027890b576SThierry Reding u64 input, output, watermark, num; 40334fa183bSThierry Reding struct tegra_sor_params params; 40434fa183bSThierry Reding u32 num_syms_per_line; 40534fa183bSThierry Reding unsigned int i; 40634fa183bSThierry Reding 40734fa183bSThierry Reding if (!link_rate || !link->num_lanes || !pclk || !config->bits_per_pixel) 40834fa183bSThierry Reding return -EINVAL; 40934fa183bSThierry Reding 41034fa183bSThierry Reding output = link_rate * 8 * link->num_lanes; 41134fa183bSThierry Reding input = pclk * config->bits_per_pixel; 41234fa183bSThierry Reding 41334fa183bSThierry Reding if (input >= output) 41434fa183bSThierry Reding return -ERANGE; 41534fa183bSThierry Reding 41634fa183bSThierry Reding memset(¶ms, 0, sizeof(params)); 41734fa183bSThierry Reding params.ratio = div64_u64(input * f, output); 41834fa183bSThierry Reding params.num_clocks = div_u64(link_rate * mode->hdisplay, pclk); 41934fa183bSThierry Reding params.precision = f; 42034fa183bSThierry Reding params.error = 64 * f; 42134fa183bSThierry Reding params.tu_size = 64; 42234fa183bSThierry Reding 42334fa183bSThierry Reding for (i = params.tu_size; i >= 32; i--) 42434fa183bSThierry Reding if (tegra_sor_compute_params(sor, ¶ms, i)) 42534fa183bSThierry Reding break; 42634fa183bSThierry Reding 42734fa183bSThierry Reding if (params.active_frac == 0) { 42834fa183bSThierry Reding config->active_polarity = 0; 42934fa183bSThierry Reding config->active_count = params.active_count; 43034fa183bSThierry Reding 43134fa183bSThierry Reding if (!params.active_polarity) 43234fa183bSThierry Reding config->active_count--; 43334fa183bSThierry Reding 43434fa183bSThierry Reding config->tu_size = params.tu_size; 43534fa183bSThierry Reding config->active_frac = 1; 43634fa183bSThierry Reding } else { 43734fa183bSThierry Reding config->active_polarity = params.active_polarity; 43834fa183bSThierry Reding config->active_count = params.active_count; 43934fa183bSThierry Reding config->active_frac = params.active_frac; 44034fa183bSThierry Reding config->tu_size = params.tu_size; 44134fa183bSThierry Reding } 44234fa183bSThierry Reding 44334fa183bSThierry Reding dev_dbg(sor->dev, 44434fa183bSThierry Reding "polarity: %d active count: %d tu size: %d active frac: %d\n", 44534fa183bSThierry Reding config->active_polarity, config->active_count, 44634fa183bSThierry Reding config->tu_size, config->active_frac); 44734fa183bSThierry Reding 44834fa183bSThierry Reding watermark = params.ratio * config->tu_size * (f - params.ratio); 44934fa183bSThierry Reding watermark = div_u64(watermark, f); 45034fa183bSThierry Reding 45134fa183bSThierry Reding watermark = div_u64(watermark + params.error, f); 45234fa183bSThierry Reding config->watermark = watermark + (config->bits_per_pixel / 8) + 2; 45334fa183bSThierry Reding num_syms_per_line = (mode->hdisplay * config->bits_per_pixel) * 45434fa183bSThierry Reding (link->num_lanes * 8); 45534fa183bSThierry Reding 45634fa183bSThierry Reding if (config->watermark > 30) { 45734fa183bSThierry Reding config->watermark = 30; 45834fa183bSThierry Reding dev_err(sor->dev, 45934fa183bSThierry Reding "unable to compute TU size, forcing watermark to %u\n", 46034fa183bSThierry Reding config->watermark); 46134fa183bSThierry Reding } else if (config->watermark > num_syms_per_line) { 46234fa183bSThierry Reding config->watermark = num_syms_per_line; 46334fa183bSThierry Reding dev_err(sor->dev, "watermark too high, forcing to %u\n", 46434fa183bSThierry Reding config->watermark); 46534fa183bSThierry Reding } 46634fa183bSThierry Reding 4677890b576SThierry Reding /* compute the number of symbols per horizontal blanking interval */ 4687890b576SThierry Reding num = ((mode->htotal - mode->hdisplay) - 7) * link_rate; 4697890b576SThierry Reding config->hblank_symbols = div_u64(num, pclk); 4707890b576SThierry Reding 4717890b576SThierry Reding if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING) 4727890b576SThierry Reding config->hblank_symbols -= 3; 4737890b576SThierry Reding 4747890b576SThierry Reding config->hblank_symbols -= 12 / link->num_lanes; 4757890b576SThierry Reding 4767890b576SThierry Reding /* compute the number of symbols per vertical blanking interval */ 4777890b576SThierry Reding num = (mode->hdisplay - 25) * link_rate; 4787890b576SThierry Reding config->vblank_symbols = div_u64(num, pclk); 4797890b576SThierry Reding config->vblank_symbols -= 36 / link->num_lanes + 4; 4807890b576SThierry Reding 4817890b576SThierry Reding dev_dbg(sor->dev, "blank symbols: H:%u V:%u\n", config->hblank_symbols, 4827890b576SThierry Reding config->vblank_symbols); 4837890b576SThierry Reding 48434fa183bSThierry Reding return 0; 48534fa183bSThierry Reding } 48634fa183bSThierry Reding 4876fad8f66SThierry Reding static int tegra_sor_detach(struct tegra_sor *sor) 4886b6b6042SThierry Reding { 4896fad8f66SThierry Reding unsigned long value, timeout; 4906fad8f66SThierry Reding 4916fad8f66SThierry Reding /* switch to safe mode */ 4926fad8f66SThierry Reding value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); 4936fad8f66SThierry Reding value &= ~SOR_SUPER_STATE_MODE_NORMAL; 4946fad8f66SThierry Reding tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); 4956fad8f66SThierry Reding tegra_sor_super_update(sor); 4966fad8f66SThierry Reding 4976fad8f66SThierry Reding timeout = jiffies + msecs_to_jiffies(250); 4986fad8f66SThierry Reding 4996fad8f66SThierry Reding while (time_before(jiffies, timeout)) { 5006fad8f66SThierry Reding value = tegra_sor_readl(sor, SOR_PWR); 5016fad8f66SThierry Reding if (value & SOR_PWR_MODE_SAFE) 5026fad8f66SThierry Reding break; 5036fad8f66SThierry Reding } 5046fad8f66SThierry Reding 5056fad8f66SThierry Reding if ((value & SOR_PWR_MODE_SAFE) == 0) 5066fad8f66SThierry Reding return -ETIMEDOUT; 5076fad8f66SThierry Reding 5086fad8f66SThierry Reding /* go to sleep */ 5096fad8f66SThierry Reding value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); 5106fad8f66SThierry Reding value &= ~SOR_SUPER_STATE_HEAD_MODE_MASK; 5116fad8f66SThierry Reding tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); 5126fad8f66SThierry Reding tegra_sor_super_update(sor); 5136fad8f66SThierry Reding 5146fad8f66SThierry Reding /* detach */ 5156fad8f66SThierry Reding value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); 5166fad8f66SThierry Reding value &= ~SOR_SUPER_STATE_ATTACHED; 5176fad8f66SThierry Reding tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); 5186fad8f66SThierry Reding tegra_sor_super_update(sor); 5196fad8f66SThierry Reding 5206fad8f66SThierry Reding timeout = jiffies + msecs_to_jiffies(250); 5216fad8f66SThierry Reding 5226fad8f66SThierry Reding while (time_before(jiffies, timeout)) { 5236fad8f66SThierry Reding value = tegra_sor_readl(sor, SOR_TEST); 5246fad8f66SThierry Reding if ((value & SOR_TEST_ATTACHED) == 0) 5256fad8f66SThierry Reding break; 5266fad8f66SThierry Reding 5276fad8f66SThierry Reding usleep_range(25, 100); 5286fad8f66SThierry Reding } 5296fad8f66SThierry Reding 5306fad8f66SThierry Reding if ((value & SOR_TEST_ATTACHED) != 0) 5316fad8f66SThierry Reding return -ETIMEDOUT; 5326fad8f66SThierry Reding 5336fad8f66SThierry Reding return 0; 5346fad8f66SThierry Reding } 5356fad8f66SThierry Reding 5366fad8f66SThierry Reding static int tegra_sor_power_down(struct tegra_sor *sor) 5376fad8f66SThierry Reding { 5386fad8f66SThierry Reding unsigned long value, timeout; 5396fad8f66SThierry Reding int err; 5406fad8f66SThierry Reding 5416fad8f66SThierry Reding value = tegra_sor_readl(sor, SOR_PWR); 5426fad8f66SThierry Reding value &= ~SOR_PWR_NORMAL_STATE_PU; 5436fad8f66SThierry Reding value |= SOR_PWR_TRIGGER; 5446fad8f66SThierry Reding tegra_sor_writel(sor, value, SOR_PWR); 5456fad8f66SThierry Reding 5466fad8f66SThierry Reding timeout = jiffies + msecs_to_jiffies(250); 5476fad8f66SThierry Reding 5486fad8f66SThierry Reding while (time_before(jiffies, timeout)) { 5496fad8f66SThierry Reding value = tegra_sor_readl(sor, SOR_PWR); 5506fad8f66SThierry Reding if ((value & SOR_PWR_TRIGGER) == 0) 5516fad8f66SThierry Reding return 0; 5526fad8f66SThierry Reding 5536fad8f66SThierry Reding usleep_range(25, 100); 5546fad8f66SThierry Reding } 5556fad8f66SThierry Reding 5566fad8f66SThierry Reding if ((value & SOR_PWR_TRIGGER) != 0) 5576fad8f66SThierry Reding return -ETIMEDOUT; 5586fad8f66SThierry Reding 5596fad8f66SThierry Reding err = clk_set_parent(sor->clk, sor->clk_safe); 5606fad8f66SThierry Reding if (err < 0) 5616fad8f66SThierry Reding dev_err(sor->dev, "failed to set safe parent clock: %d\n", err); 5626fad8f66SThierry Reding 5636fad8f66SThierry Reding value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); 5646fad8f66SThierry Reding value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 | 5656fad8f66SThierry Reding SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2); 5666fad8f66SThierry Reding tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); 5676fad8f66SThierry Reding 5686fad8f66SThierry Reding /* stop lane sequencer */ 5696fad8f66SThierry Reding value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP | 5706fad8f66SThierry Reding SOR_LANE_SEQ_CTL_POWER_STATE_DOWN; 5716fad8f66SThierry Reding tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL); 5726fad8f66SThierry Reding 5736fad8f66SThierry Reding timeout = jiffies + msecs_to_jiffies(250); 5746fad8f66SThierry Reding 5756fad8f66SThierry Reding while (time_before(jiffies, timeout)) { 5766fad8f66SThierry Reding value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL); 5776fad8f66SThierry Reding if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0) 5786fad8f66SThierry Reding break; 5796fad8f66SThierry Reding 5806fad8f66SThierry Reding usleep_range(25, 100); 5816fad8f66SThierry Reding } 5826fad8f66SThierry Reding 5836fad8f66SThierry Reding if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0) 5846fad8f66SThierry Reding return -ETIMEDOUT; 5856fad8f66SThierry Reding 5866fad8f66SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 5876fad8f66SThierry Reding value |= SOR_PLL_2_PORT_POWERDOWN; 5886fad8f66SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 5896fad8f66SThierry Reding 5906fad8f66SThierry Reding usleep_range(20, 100); 5916fad8f66SThierry Reding 5926fad8f66SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_0); 5936fad8f66SThierry Reding value |= SOR_PLL_0_POWER_OFF; 5946fad8f66SThierry Reding value |= SOR_PLL_0_VCOPD; 5956fad8f66SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_0); 5966fad8f66SThierry Reding 5976fad8f66SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 5986fad8f66SThierry Reding value |= SOR_PLL_2_SEQ_PLLCAPPD; 5996fad8f66SThierry Reding value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE; 6006fad8f66SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 6016fad8f66SThierry Reding 6026fad8f66SThierry Reding usleep_range(20, 100); 6036fad8f66SThierry Reding 6046fad8f66SThierry Reding return 0; 6056fad8f66SThierry Reding } 6066fad8f66SThierry Reding 6076fad8f66SThierry Reding static int tegra_sor_crc_open(struct inode *inode, struct file *file) 6086fad8f66SThierry Reding { 6096fad8f66SThierry Reding file->private_data = inode->i_private; 6106fad8f66SThierry Reding 6116fad8f66SThierry Reding return 0; 6126fad8f66SThierry Reding } 6136fad8f66SThierry Reding 6146fad8f66SThierry Reding static int tegra_sor_crc_release(struct inode *inode, struct file *file) 6156fad8f66SThierry Reding { 6166fad8f66SThierry Reding return 0; 6176fad8f66SThierry Reding } 6186fad8f66SThierry Reding 6196fad8f66SThierry Reding static int tegra_sor_crc_wait(struct tegra_sor *sor, unsigned long timeout) 6206fad8f66SThierry Reding { 6216fad8f66SThierry Reding u32 value; 6226fad8f66SThierry Reding 6236fad8f66SThierry Reding timeout = jiffies + msecs_to_jiffies(timeout); 6246fad8f66SThierry Reding 6256fad8f66SThierry Reding while (time_before(jiffies, timeout)) { 6266fad8f66SThierry Reding value = tegra_sor_readl(sor, SOR_CRC_A); 6276fad8f66SThierry Reding if (value & SOR_CRC_A_VALID) 6286fad8f66SThierry Reding return 0; 6296fad8f66SThierry Reding 6306fad8f66SThierry Reding usleep_range(100, 200); 6316fad8f66SThierry Reding } 6326fad8f66SThierry Reding 6336fad8f66SThierry Reding return -ETIMEDOUT; 6346fad8f66SThierry Reding } 6356fad8f66SThierry Reding 6366fad8f66SThierry Reding static ssize_t tegra_sor_crc_read(struct file *file, char __user *buffer, 6376fad8f66SThierry Reding size_t size, loff_t *ppos) 6386fad8f66SThierry Reding { 6396fad8f66SThierry Reding struct tegra_sor *sor = file->private_data; 6406fad8f66SThierry Reding ssize_t num, err; 6416fad8f66SThierry Reding char buf[10]; 6426fad8f66SThierry Reding u32 value; 6436fad8f66SThierry Reding 6446fad8f66SThierry Reding mutex_lock(&sor->lock); 6456fad8f66SThierry Reding 6466fad8f66SThierry Reding if (!sor->enabled) { 6476fad8f66SThierry Reding err = -EAGAIN; 6486fad8f66SThierry Reding goto unlock; 6496fad8f66SThierry Reding } 6506fad8f66SThierry Reding 6516fad8f66SThierry Reding value = tegra_sor_readl(sor, SOR_STATE_1); 6526fad8f66SThierry Reding value &= ~SOR_STATE_ASY_CRC_MODE_MASK; 6536fad8f66SThierry Reding tegra_sor_writel(sor, value, SOR_STATE_1); 6546fad8f66SThierry Reding 6556fad8f66SThierry Reding value = tegra_sor_readl(sor, SOR_CRC_CNTRL); 6566fad8f66SThierry Reding value |= SOR_CRC_CNTRL_ENABLE; 6576fad8f66SThierry Reding tegra_sor_writel(sor, value, SOR_CRC_CNTRL); 6586fad8f66SThierry Reding 6596fad8f66SThierry Reding value = tegra_sor_readl(sor, SOR_TEST); 6606fad8f66SThierry Reding value &= ~SOR_TEST_CRC_POST_SERIALIZE; 6616fad8f66SThierry Reding tegra_sor_writel(sor, value, SOR_TEST); 6626fad8f66SThierry Reding 6636fad8f66SThierry Reding err = tegra_sor_crc_wait(sor, 100); 6646fad8f66SThierry Reding if (err < 0) 6656fad8f66SThierry Reding goto unlock; 6666fad8f66SThierry Reding 6676fad8f66SThierry Reding tegra_sor_writel(sor, SOR_CRC_A_RESET, SOR_CRC_A); 6686fad8f66SThierry Reding value = tegra_sor_readl(sor, SOR_CRC_B); 6696fad8f66SThierry Reding 6706fad8f66SThierry Reding num = scnprintf(buf, sizeof(buf), "%08x\n", value); 6716fad8f66SThierry Reding 6726fad8f66SThierry Reding err = simple_read_from_buffer(buffer, size, ppos, buf, num); 6736fad8f66SThierry Reding 6746fad8f66SThierry Reding unlock: 6756fad8f66SThierry Reding mutex_unlock(&sor->lock); 6766fad8f66SThierry Reding return err; 6776fad8f66SThierry Reding } 6786fad8f66SThierry Reding 6796fad8f66SThierry Reding static const struct file_operations tegra_sor_crc_fops = { 6806fad8f66SThierry Reding .owner = THIS_MODULE, 6816fad8f66SThierry Reding .open = tegra_sor_crc_open, 6826fad8f66SThierry Reding .read = tegra_sor_crc_read, 6836fad8f66SThierry Reding .release = tegra_sor_crc_release, 6846fad8f66SThierry Reding }; 6856fad8f66SThierry Reding 6866fad8f66SThierry Reding static int tegra_sor_debugfs_init(struct tegra_sor *sor, 6876fad8f66SThierry Reding struct drm_minor *minor) 6886fad8f66SThierry Reding { 6896fad8f66SThierry Reding struct dentry *entry; 6906fad8f66SThierry Reding int err = 0; 6916fad8f66SThierry Reding 6926fad8f66SThierry Reding sor->debugfs = debugfs_create_dir("sor", minor->debugfs_root); 6936fad8f66SThierry Reding if (!sor->debugfs) 6946fad8f66SThierry Reding return -ENOMEM; 6956fad8f66SThierry Reding 6966fad8f66SThierry Reding entry = debugfs_create_file("crc", 0644, sor->debugfs, sor, 6976fad8f66SThierry Reding &tegra_sor_crc_fops); 6986fad8f66SThierry Reding if (!entry) { 6996fad8f66SThierry Reding dev_err(sor->dev, 7006fad8f66SThierry Reding "cannot create /sys/kernel/debug/dri/%s/sor/crc\n", 7016fad8f66SThierry Reding minor->debugfs_root->d_name.name); 7026fad8f66SThierry Reding err = -ENOMEM; 7036fad8f66SThierry Reding goto remove; 7046fad8f66SThierry Reding } 7056fad8f66SThierry Reding 7066fad8f66SThierry Reding return err; 7076fad8f66SThierry Reding 7086fad8f66SThierry Reding remove: 7096fad8f66SThierry Reding debugfs_remove(sor->debugfs); 7106fad8f66SThierry Reding sor->debugfs = NULL; 7116fad8f66SThierry Reding return err; 7126fad8f66SThierry Reding } 7136fad8f66SThierry Reding 7144009c224SThierry Reding static void tegra_sor_debugfs_exit(struct tegra_sor *sor) 7156fad8f66SThierry Reding { 7166fad8f66SThierry Reding debugfs_remove_recursive(sor->debugfs); 7176fad8f66SThierry Reding sor->debugfs = NULL; 7186fad8f66SThierry Reding } 7196fad8f66SThierry Reding 7206fad8f66SThierry Reding static void tegra_sor_connector_dpms(struct drm_connector *connector, int mode) 7216fad8f66SThierry Reding { 7226fad8f66SThierry Reding } 7236fad8f66SThierry Reding 7246fad8f66SThierry Reding static enum drm_connector_status 7256fad8f66SThierry Reding tegra_sor_connector_detect(struct drm_connector *connector, bool force) 7266fad8f66SThierry Reding { 7276fad8f66SThierry Reding struct tegra_output *output = connector_to_output(connector); 7286fad8f66SThierry Reding struct tegra_sor *sor = to_sor(output); 7296fad8f66SThierry Reding 7306fad8f66SThierry Reding if (sor->dpaux) 7316fad8f66SThierry Reding return tegra_dpaux_detect(sor->dpaux); 7326fad8f66SThierry Reding 7336fad8f66SThierry Reding return connector_status_unknown; 7346fad8f66SThierry Reding } 7356fad8f66SThierry Reding 7366fad8f66SThierry Reding static const struct drm_connector_funcs tegra_sor_connector_funcs = { 7376fad8f66SThierry Reding .dpms = tegra_sor_connector_dpms, 7386fad8f66SThierry Reding .detect = tegra_sor_connector_detect, 7396fad8f66SThierry Reding .fill_modes = drm_helper_probe_single_connector_modes, 7406fad8f66SThierry Reding .destroy = tegra_output_connector_destroy, 741*4aa3df71SThierry Reding .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 7426fad8f66SThierry Reding }; 7436fad8f66SThierry Reding 7446fad8f66SThierry Reding static int tegra_sor_connector_get_modes(struct drm_connector *connector) 7456fad8f66SThierry Reding { 7466fad8f66SThierry Reding struct tegra_output *output = connector_to_output(connector); 7476fad8f66SThierry Reding struct tegra_sor *sor = to_sor(output); 7486fad8f66SThierry Reding int err; 7496fad8f66SThierry Reding 7506fad8f66SThierry Reding if (sor->dpaux) 7516fad8f66SThierry Reding tegra_dpaux_enable(sor->dpaux); 7526fad8f66SThierry Reding 7536fad8f66SThierry Reding err = tegra_output_connector_get_modes(connector); 7546fad8f66SThierry Reding 7556fad8f66SThierry Reding if (sor->dpaux) 7566fad8f66SThierry Reding tegra_dpaux_disable(sor->dpaux); 7576fad8f66SThierry Reding 7586fad8f66SThierry Reding return err; 7596fad8f66SThierry Reding } 7606fad8f66SThierry Reding 7616fad8f66SThierry Reding static enum drm_mode_status 7626fad8f66SThierry Reding tegra_sor_connector_mode_valid(struct drm_connector *connector, 7636fad8f66SThierry Reding struct drm_display_mode *mode) 7646fad8f66SThierry Reding { 7656fad8f66SThierry Reding return MODE_OK; 7666fad8f66SThierry Reding } 7676fad8f66SThierry Reding 7686fad8f66SThierry Reding static const struct drm_connector_helper_funcs tegra_sor_connector_helper_funcs = { 7696fad8f66SThierry Reding .get_modes = tegra_sor_connector_get_modes, 7706fad8f66SThierry Reding .mode_valid = tegra_sor_connector_mode_valid, 7716fad8f66SThierry Reding .best_encoder = tegra_output_connector_best_encoder, 7726fad8f66SThierry Reding }; 7736fad8f66SThierry Reding 7746fad8f66SThierry Reding static const struct drm_encoder_funcs tegra_sor_encoder_funcs = { 7756fad8f66SThierry Reding .destroy = tegra_output_encoder_destroy, 7766fad8f66SThierry Reding }; 7776fad8f66SThierry Reding 7786fad8f66SThierry Reding static void tegra_sor_encoder_dpms(struct drm_encoder *encoder, int mode) 7796fad8f66SThierry Reding { 7806fad8f66SThierry Reding } 7816fad8f66SThierry Reding 7826fad8f66SThierry Reding static bool tegra_sor_encoder_mode_fixup(struct drm_encoder *encoder, 7836fad8f66SThierry Reding const struct drm_display_mode *mode, 7846fad8f66SThierry Reding struct drm_display_mode *adjusted) 7856fad8f66SThierry Reding { 7866fad8f66SThierry Reding struct tegra_output *output = encoder_to_output(encoder); 7876fad8f66SThierry Reding struct tegra_dc *dc = to_tegra_dc(encoder->crtc); 7886fad8f66SThierry Reding unsigned long pclk = mode->clock * 1000; 7896fad8f66SThierry Reding struct tegra_sor *sor = to_sor(output); 7906fad8f66SThierry Reding int err; 7916fad8f66SThierry Reding 7926fad8f66SThierry Reding err = tegra_dc_setup_clock(dc, sor->clk_parent, pclk, 0); 7936fad8f66SThierry Reding if (err < 0) { 7946fad8f66SThierry Reding dev_err(output->dev, "failed to setup DC clock: %d\n", err); 7956fad8f66SThierry Reding return false; 7966fad8f66SThierry Reding } 7976fad8f66SThierry Reding 7986fad8f66SThierry Reding err = clk_set_rate(sor->clk_parent, pclk); 7996fad8f66SThierry Reding if (err < 0) { 8006fad8f66SThierry Reding dev_err(output->dev, "failed to set clock rate to %lu Hz\n", 8016fad8f66SThierry Reding pclk); 8026fad8f66SThierry Reding return false; 8036fad8f66SThierry Reding } 8046fad8f66SThierry Reding 8056fad8f66SThierry Reding return true; 8066fad8f66SThierry Reding } 8076fad8f66SThierry Reding 8086fad8f66SThierry Reding static void tegra_sor_encoder_prepare(struct drm_encoder *encoder) 8096fad8f66SThierry Reding { 8106fad8f66SThierry Reding } 8116fad8f66SThierry Reding 8126fad8f66SThierry Reding static void tegra_sor_encoder_commit(struct drm_encoder *encoder) 8136fad8f66SThierry Reding { 8146fad8f66SThierry Reding } 8156fad8f66SThierry Reding 8166fad8f66SThierry Reding static void tegra_sor_encoder_mode_set(struct drm_encoder *encoder, 8176fad8f66SThierry Reding struct drm_display_mode *mode, 8186fad8f66SThierry Reding struct drm_display_mode *adjusted) 8196fad8f66SThierry Reding { 8206fad8f66SThierry Reding struct tegra_output *output = encoder_to_output(encoder); 8216fad8f66SThierry Reding struct tegra_dc *dc = to_tegra_dc(encoder->crtc); 8226b6b6042SThierry Reding unsigned int vbe, vse, hbe, hse, vbs, hbs, i; 8236b6b6042SThierry Reding struct tegra_sor *sor = to_sor(output); 82434fa183bSThierry Reding struct tegra_sor_config config; 82534fa183bSThierry Reding struct drm_dp_link link; 82634fa183bSThierry Reding struct drm_dp_aux *aux; 8276b6b6042SThierry Reding unsigned long value; 82886f5c52dSThierry Reding int err = 0; 82986f5c52dSThierry Reding 83086f5c52dSThierry Reding mutex_lock(&sor->lock); 8316b6b6042SThierry Reding 8326b6b6042SThierry Reding if (sor->enabled) 83386f5c52dSThierry Reding goto unlock; 8346b6b6042SThierry Reding 8356b6b6042SThierry Reding err = clk_prepare_enable(sor->clk); 8366b6b6042SThierry Reding if (err < 0) 83786f5c52dSThierry Reding goto unlock; 8386b6b6042SThierry Reding 8396b6b6042SThierry Reding reset_control_deassert(sor->rst); 8406b6b6042SThierry Reding 8416fad8f66SThierry Reding if (output->panel) 8426fad8f66SThierry Reding drm_panel_prepare(output->panel); 8436fad8f66SThierry Reding 84434fa183bSThierry Reding /* FIXME: properly convert to struct drm_dp_aux */ 84534fa183bSThierry Reding aux = (struct drm_dp_aux *)sor->dpaux; 84634fa183bSThierry Reding 8476b6b6042SThierry Reding if (sor->dpaux) { 8486b6b6042SThierry Reding err = tegra_dpaux_enable(sor->dpaux); 8496b6b6042SThierry Reding if (err < 0) 8506b6b6042SThierry Reding dev_err(sor->dev, "failed to enable DP: %d\n", err); 85134fa183bSThierry Reding 85234fa183bSThierry Reding err = drm_dp_link_probe(aux, &link); 85334fa183bSThierry Reding if (err < 0) { 85434fa183bSThierry Reding dev_err(sor->dev, "failed to probe eDP link: %d\n", 85534fa183bSThierry Reding err); 8562263c460SDan Carpenter goto unlock; 85734fa183bSThierry Reding } 8586b6b6042SThierry Reding } 8596b6b6042SThierry Reding 8606b6b6042SThierry Reding err = clk_set_parent(sor->clk, sor->clk_safe); 8616b6b6042SThierry Reding if (err < 0) 8626b6b6042SThierry Reding dev_err(sor->dev, "failed to set safe parent clock: %d\n", err); 8636b6b6042SThierry Reding 86434fa183bSThierry Reding memset(&config, 0, sizeof(config)); 865054b1bd1SStéphane Marchesin config.bits_per_pixel = output->connector.display_info.bpc * 3; 86634fa183bSThierry Reding 86734fa183bSThierry Reding err = tegra_sor_calc_config(sor, mode, &config, &link); 86834fa183bSThierry Reding if (err < 0) 86934fa183bSThierry Reding dev_err(sor->dev, "failed to compute link configuration: %d\n", 87034fa183bSThierry Reding err); 87134fa183bSThierry Reding 8726b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_CLK_CNTRL); 8736b6b6042SThierry Reding value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK; 8746b6b6042SThierry Reding value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK; 8756b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_CLK_CNTRL); 8766b6b6042SThierry Reding 8776b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 8786b6b6042SThierry Reding value &= ~SOR_PLL_2_BANDGAP_POWERDOWN; 8796b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 8806b6b6042SThierry Reding usleep_range(20, 100); 8816b6b6042SThierry Reding 8826b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_3); 8836b6b6042SThierry Reding value |= SOR_PLL_3_PLL_VDD_MODE_V3_3; 8846b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_3); 8856b6b6042SThierry Reding 8866b6b6042SThierry Reding value = SOR_PLL_0_ICHPMP(0xf) | SOR_PLL_0_VCOCAP_RST | 8876b6b6042SThierry Reding SOR_PLL_0_PLLREG_LEVEL_V45 | SOR_PLL_0_RESISTOR_EXT; 8886b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_0); 8896b6b6042SThierry Reding 8906b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 8916b6b6042SThierry Reding value |= SOR_PLL_2_SEQ_PLLCAPPD; 8926b6b6042SThierry Reding value &= ~SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE; 8936b6b6042SThierry Reding value |= SOR_PLL_2_LVDS_ENABLE; 8946b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 8956b6b6042SThierry Reding 8966b6b6042SThierry Reding value = SOR_PLL_1_TERM_COMPOUT | SOR_PLL_1_TMDS_TERM; 8976b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_1); 8986b6b6042SThierry Reding 8996b6b6042SThierry Reding while (true) { 9006b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 9016b6b6042SThierry Reding if ((value & SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE) == 0) 9026b6b6042SThierry Reding break; 9036b6b6042SThierry Reding 9046b6b6042SThierry Reding usleep_range(250, 1000); 9056b6b6042SThierry Reding } 9066b6b6042SThierry Reding 9076b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 9086b6b6042SThierry Reding value &= ~SOR_PLL_2_POWERDOWN_OVERRIDE; 9096b6b6042SThierry Reding value &= ~SOR_PLL_2_PORT_POWERDOWN; 9106b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 9116b6b6042SThierry Reding 9126b6b6042SThierry Reding /* 9136b6b6042SThierry Reding * power up 9146b6b6042SThierry Reding */ 9156b6b6042SThierry Reding 9166b6b6042SThierry Reding /* set safe link bandwidth (1.62 Gbps) */ 9176b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_CLK_CNTRL); 9186b6b6042SThierry Reding value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK; 9196b6b6042SThierry Reding value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G1_62; 9206b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_CLK_CNTRL); 9216b6b6042SThierry Reding 9226b6b6042SThierry Reding /* step 1 */ 9236b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 9246b6b6042SThierry Reding value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE | SOR_PLL_2_PORT_POWERDOWN | 9256b6b6042SThierry Reding SOR_PLL_2_BANDGAP_POWERDOWN; 9266b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 9276b6b6042SThierry Reding 9286b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_0); 9296b6b6042SThierry Reding value |= SOR_PLL_0_VCOPD | SOR_PLL_0_POWER_OFF; 9306b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_0); 9316b6b6042SThierry Reding 9326b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); 9336b6b6042SThierry Reding value &= ~SOR_DP_PADCTL_PAD_CAL_PD; 9346b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); 9356b6b6042SThierry Reding 9366b6b6042SThierry Reding /* step 2 */ 9376b6b6042SThierry Reding err = tegra_io_rail_power_on(TEGRA_IO_RAIL_LVDS); 9386b6b6042SThierry Reding if (err < 0) { 9396b6b6042SThierry Reding dev_err(sor->dev, "failed to power on I/O rail: %d\n", err); 94086f5c52dSThierry Reding goto unlock; 9416b6b6042SThierry Reding } 9426b6b6042SThierry Reding 9436b6b6042SThierry Reding usleep_range(5, 100); 9446b6b6042SThierry Reding 9456b6b6042SThierry Reding /* step 3 */ 9466b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 9476b6b6042SThierry Reding value &= ~SOR_PLL_2_BANDGAP_POWERDOWN; 9486b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 9496b6b6042SThierry Reding 9506b6b6042SThierry Reding usleep_range(20, 100); 9516b6b6042SThierry Reding 9526b6b6042SThierry Reding /* step 4 */ 9536b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_0); 9546b6b6042SThierry Reding value &= ~SOR_PLL_0_POWER_OFF; 9556b6b6042SThierry Reding value &= ~SOR_PLL_0_VCOPD; 9566b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_0); 9576b6b6042SThierry Reding 9586b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 9596b6b6042SThierry Reding value &= ~SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE; 9606b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 9616b6b6042SThierry Reding 9626b6b6042SThierry Reding usleep_range(200, 1000); 9636b6b6042SThierry Reding 9646b6b6042SThierry Reding /* step 5 */ 9656b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 9666b6b6042SThierry Reding value &= ~SOR_PLL_2_PORT_POWERDOWN; 9676b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 9686b6b6042SThierry Reding 9696b6b6042SThierry Reding /* switch to DP clock */ 9706b6b6042SThierry Reding err = clk_set_parent(sor->clk, sor->clk_dp); 9716b6b6042SThierry Reding if (err < 0) 9726b6b6042SThierry Reding dev_err(sor->dev, "failed to set DP parent clock: %d\n", err); 9736b6b6042SThierry Reding 974899451b7SThierry Reding /* power DP lanes */ 9756b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); 976899451b7SThierry Reding 977899451b7SThierry Reding if (link.num_lanes <= 2) 978899451b7SThierry Reding value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_2); 979899451b7SThierry Reding else 980899451b7SThierry Reding value |= SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_2; 981899451b7SThierry Reding 982899451b7SThierry Reding if (link.num_lanes <= 1) 983899451b7SThierry Reding value &= ~SOR_DP_PADCTL_PD_TXD_1; 984899451b7SThierry Reding else 985899451b7SThierry Reding value |= SOR_DP_PADCTL_PD_TXD_1; 986899451b7SThierry Reding 987899451b7SThierry Reding if (link.num_lanes == 0) 988899451b7SThierry Reding value &= ~SOR_DP_PADCTL_PD_TXD_0; 989899451b7SThierry Reding else 990899451b7SThierry Reding value |= SOR_DP_PADCTL_PD_TXD_0; 991899451b7SThierry Reding 9926b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); 9936b6b6042SThierry Reding 9946b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0); 9956b6b6042SThierry Reding value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK; 9960c90a184SThierry Reding value |= SOR_DP_LINKCTL_LANE_COUNT(link.num_lanes); 9976b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0); 9986b6b6042SThierry Reding 9996b6b6042SThierry Reding /* start lane sequencer */ 10006b6b6042SThierry Reding value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_DOWN | 10016b6b6042SThierry Reding SOR_LANE_SEQ_CTL_POWER_STATE_UP; 10026b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL); 10036b6b6042SThierry Reding 10046b6b6042SThierry Reding while (true) { 10056b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL); 10066b6b6042SThierry Reding if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0) 10076b6b6042SThierry Reding break; 10086b6b6042SThierry Reding 10096b6b6042SThierry Reding usleep_range(250, 1000); 10106b6b6042SThierry Reding } 10116b6b6042SThierry Reding 1012a4263fedSThierry Reding /* set link bandwidth */ 10136b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_CLK_CNTRL); 10146b6b6042SThierry Reding value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK; 1015a4263fedSThierry Reding value |= drm_dp_link_rate_to_bw_code(link.rate) << 2; 10166b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_CLK_CNTRL); 10176b6b6042SThierry Reding 10186b6b6042SThierry Reding /* set linkctl */ 10196b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0); 10206b6b6042SThierry Reding value |= SOR_DP_LINKCTL_ENABLE; 10216b6b6042SThierry Reding 10226b6b6042SThierry Reding value &= ~SOR_DP_LINKCTL_TU_SIZE_MASK; 102334fa183bSThierry Reding value |= SOR_DP_LINKCTL_TU_SIZE(config.tu_size); 10246b6b6042SThierry Reding 10256b6b6042SThierry Reding value |= SOR_DP_LINKCTL_ENHANCED_FRAME; 10266b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0); 10276b6b6042SThierry Reding 10286b6b6042SThierry Reding for (i = 0, value = 0; i < 4; i++) { 10296b6b6042SThierry Reding unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | 10306b6b6042SThierry Reding SOR_DP_TPG_SCRAMBLER_GALIOS | 10316b6b6042SThierry Reding SOR_DP_TPG_PATTERN_NONE; 10326b6b6042SThierry Reding value = (value << 8) | lane; 10336b6b6042SThierry Reding } 10346b6b6042SThierry Reding 10356b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_TPG); 10366b6b6042SThierry Reding 10376b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_CONFIG_0); 10386b6b6042SThierry Reding value &= ~SOR_DP_CONFIG_WATERMARK_MASK; 103934fa183bSThierry Reding value |= SOR_DP_CONFIG_WATERMARK(config.watermark); 10406b6b6042SThierry Reding 10416b6b6042SThierry Reding value &= ~SOR_DP_CONFIG_ACTIVE_SYM_COUNT_MASK; 104234fa183bSThierry Reding value |= SOR_DP_CONFIG_ACTIVE_SYM_COUNT(config.active_count); 10436b6b6042SThierry Reding 10446b6b6042SThierry Reding value &= ~SOR_DP_CONFIG_ACTIVE_SYM_FRAC_MASK; 104534fa183bSThierry Reding value |= SOR_DP_CONFIG_ACTIVE_SYM_FRAC(config.active_frac); 10466b6b6042SThierry Reding 104734fa183bSThierry Reding if (config.active_polarity) 104834fa183bSThierry Reding value |= SOR_DP_CONFIG_ACTIVE_SYM_POLARITY; 104934fa183bSThierry Reding else 105034fa183bSThierry Reding value &= ~SOR_DP_CONFIG_ACTIVE_SYM_POLARITY; 10516b6b6042SThierry Reding 10526b6b6042SThierry Reding value |= SOR_DP_CONFIG_ACTIVE_SYM_ENABLE; 10531f64ae7cSThierry Reding value |= SOR_DP_CONFIG_DISPARITY_NEGATIVE; 10546b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_CONFIG_0); 10556b6b6042SThierry Reding 10566b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_AUDIO_HBLANK_SYMBOLS); 10576b6b6042SThierry Reding value &= ~SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK; 10587890b576SThierry Reding value |= config.hblank_symbols & 0xffff; 10596b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_AUDIO_HBLANK_SYMBOLS); 10606b6b6042SThierry Reding 10616b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_AUDIO_VBLANK_SYMBOLS); 10626b6b6042SThierry Reding value &= ~SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK; 10637890b576SThierry Reding value |= config.vblank_symbols & 0xffff; 10646b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_AUDIO_VBLANK_SYMBOLS); 10656b6b6042SThierry Reding 10666b6b6042SThierry Reding /* enable pad calibration logic */ 10676b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); 10686b6b6042SThierry Reding value |= SOR_DP_PADCTL_PAD_CAL_PD; 10696b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); 10706b6b6042SThierry Reding 10716b6b6042SThierry Reding if (sor->dpaux) { 10726b6b6042SThierry Reding u8 rate, lanes; 10736b6b6042SThierry Reding 10746b6b6042SThierry Reding err = drm_dp_link_probe(aux, &link); 10756b6b6042SThierry Reding if (err < 0) { 10766b6b6042SThierry Reding dev_err(sor->dev, "failed to probe eDP link: %d\n", 10776b6b6042SThierry Reding err); 107886f5c52dSThierry Reding goto unlock; 10796b6b6042SThierry Reding } 10806b6b6042SThierry Reding 10816b6b6042SThierry Reding err = drm_dp_link_power_up(aux, &link); 10826b6b6042SThierry Reding if (err < 0) { 10836b6b6042SThierry Reding dev_err(sor->dev, "failed to power up eDP link: %d\n", 10846b6b6042SThierry Reding err); 108586f5c52dSThierry Reding goto unlock; 10866b6b6042SThierry Reding } 10876b6b6042SThierry Reding 10886b6b6042SThierry Reding err = drm_dp_link_configure(aux, &link); 10896b6b6042SThierry Reding if (err < 0) { 10906b6b6042SThierry Reding dev_err(sor->dev, "failed to configure eDP link: %d\n", 10916b6b6042SThierry Reding err); 109286f5c52dSThierry Reding goto unlock; 10936b6b6042SThierry Reding } 10946b6b6042SThierry Reding 10956b6b6042SThierry Reding rate = drm_dp_link_rate_to_bw_code(link.rate); 10966b6b6042SThierry Reding lanes = link.num_lanes; 10976b6b6042SThierry Reding 10986b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_CLK_CNTRL); 10996b6b6042SThierry Reding value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK; 11006b6b6042SThierry Reding value |= SOR_CLK_CNTRL_DP_LINK_SPEED(rate); 11016b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_CLK_CNTRL); 11026b6b6042SThierry Reding 11036b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0); 11046b6b6042SThierry Reding value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK; 11056b6b6042SThierry Reding value |= SOR_DP_LINKCTL_LANE_COUNT(lanes); 11066b6b6042SThierry Reding 11076b6b6042SThierry Reding if (link.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) 11086b6b6042SThierry Reding value |= SOR_DP_LINKCTL_ENHANCED_FRAME; 11096b6b6042SThierry Reding 11106b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0); 11116b6b6042SThierry Reding 11126b6b6042SThierry Reding /* disable training pattern generator */ 11136b6b6042SThierry Reding 11146b6b6042SThierry Reding for (i = 0; i < link.num_lanes; i++) { 11156b6b6042SThierry Reding unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | 11166b6b6042SThierry Reding SOR_DP_TPG_SCRAMBLER_GALIOS | 11176b6b6042SThierry Reding SOR_DP_TPG_PATTERN_NONE; 11186b6b6042SThierry Reding value = (value << 8) | lane; 11196b6b6042SThierry Reding } 11206b6b6042SThierry Reding 11216b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_TPG); 11226b6b6042SThierry Reding 11236b6b6042SThierry Reding err = tegra_sor_dp_train_fast(sor, &link); 11246b6b6042SThierry Reding if (err < 0) { 11256b6b6042SThierry Reding dev_err(sor->dev, "DP fast link training failed: %d\n", 11266b6b6042SThierry Reding err); 112786f5c52dSThierry Reding goto unlock; 11286b6b6042SThierry Reding } 11296b6b6042SThierry Reding 11306b6b6042SThierry Reding dev_dbg(sor->dev, "fast link training succeeded\n"); 11316b6b6042SThierry Reding } 11326b6b6042SThierry Reding 11336b6b6042SThierry Reding err = tegra_sor_power_up(sor, 250); 11346b6b6042SThierry Reding if (err < 0) { 11356b6b6042SThierry Reding dev_err(sor->dev, "failed to power up SOR: %d\n", err); 113686f5c52dSThierry Reding goto unlock; 11376b6b6042SThierry Reding } 11386b6b6042SThierry Reding 11396b6b6042SThierry Reding /* start display controller in continuous mode */ 11406b6b6042SThierry Reding value = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS); 11416b6b6042SThierry Reding value |= WRITE_MUX; 11426b6b6042SThierry Reding tegra_dc_writel(dc, value, DC_CMD_STATE_ACCESS); 11436b6b6042SThierry Reding 11446b6b6042SThierry Reding tegra_dc_writel(dc, VSYNC_H_POSITION(1), DC_DISP_DISP_TIMING_OPTIONS); 11456b6b6042SThierry Reding tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, DC_CMD_DISPLAY_COMMAND); 11466b6b6042SThierry Reding 11476b6b6042SThierry Reding value = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS); 11486b6b6042SThierry Reding value &= ~WRITE_MUX; 11496b6b6042SThierry Reding tegra_dc_writel(dc, value, DC_CMD_STATE_ACCESS); 11506b6b6042SThierry Reding 11516b6b6042SThierry Reding /* 11526b6b6042SThierry Reding * configure panel (24bpp, vsync-, hsync-, DP-A protocol, complete 11536b6b6042SThierry Reding * raster, associate with display controller) 11546b6b6042SThierry Reding */ 11553f4f3b5fSThierry Reding value = SOR_STATE_ASY_PROTOCOL_DP_A | 11566b6b6042SThierry Reding SOR_STATE_ASY_CRC_MODE_COMPLETE | 11576b6b6042SThierry Reding SOR_STATE_ASY_OWNER(dc->pipe + 1); 115834fa183bSThierry Reding 11593f4f3b5fSThierry Reding if (mode->flags & DRM_MODE_FLAG_PHSYNC) 11603f4f3b5fSThierry Reding value &= ~SOR_STATE_ASY_HSYNCPOL; 11613f4f3b5fSThierry Reding 11623f4f3b5fSThierry Reding if (mode->flags & DRM_MODE_FLAG_NHSYNC) 11633f4f3b5fSThierry Reding value |= SOR_STATE_ASY_HSYNCPOL; 11643f4f3b5fSThierry Reding 11653f4f3b5fSThierry Reding if (mode->flags & DRM_MODE_FLAG_PVSYNC) 11663f4f3b5fSThierry Reding value &= ~SOR_STATE_ASY_VSYNCPOL; 11673f4f3b5fSThierry Reding 11683f4f3b5fSThierry Reding if (mode->flags & DRM_MODE_FLAG_NVSYNC) 11693f4f3b5fSThierry Reding value |= SOR_STATE_ASY_VSYNCPOL; 11703f4f3b5fSThierry Reding 117134fa183bSThierry Reding switch (config.bits_per_pixel) { 117234fa183bSThierry Reding case 24: 117334fa183bSThierry Reding value |= SOR_STATE_ASY_PIXELDEPTH_BPP_24_444; 117434fa183bSThierry Reding break; 117534fa183bSThierry Reding 117634fa183bSThierry Reding case 18: 117734fa183bSThierry Reding value |= SOR_STATE_ASY_PIXELDEPTH_BPP_18_444; 117834fa183bSThierry Reding break; 117934fa183bSThierry Reding 118034fa183bSThierry Reding default: 118134fa183bSThierry Reding BUG(); 118234fa183bSThierry Reding break; 118334fa183bSThierry Reding } 118434fa183bSThierry Reding 11856b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_STATE_1); 11866b6b6042SThierry Reding 11876b6b6042SThierry Reding /* 11886b6b6042SThierry Reding * TODO: The video timing programming below doesn't seem to match the 11896b6b6042SThierry Reding * register definitions. 11906b6b6042SThierry Reding */ 11916b6b6042SThierry Reding 11926b6b6042SThierry Reding value = ((mode->vtotal & 0x7fff) << 16) | (mode->htotal & 0x7fff); 11936b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_HEAD_STATE_1(0)); 11946b6b6042SThierry Reding 11956b6b6042SThierry Reding vse = mode->vsync_end - mode->vsync_start - 1; 11966b6b6042SThierry Reding hse = mode->hsync_end - mode->hsync_start - 1; 11976b6b6042SThierry Reding 11986b6b6042SThierry Reding value = ((vse & 0x7fff) << 16) | (hse & 0x7fff); 11996b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_HEAD_STATE_2(0)); 12006b6b6042SThierry Reding 12016b6b6042SThierry Reding vbe = vse + (mode->vsync_start - mode->vdisplay); 12026b6b6042SThierry Reding hbe = hse + (mode->hsync_start - mode->hdisplay); 12036b6b6042SThierry Reding 12046b6b6042SThierry Reding value = ((vbe & 0x7fff) << 16) | (hbe & 0x7fff); 12056b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_HEAD_STATE_3(0)); 12066b6b6042SThierry Reding 12076b6b6042SThierry Reding vbs = vbe + mode->vdisplay; 12086b6b6042SThierry Reding hbs = hbe + mode->hdisplay; 12096b6b6042SThierry Reding 12106b6b6042SThierry Reding value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff); 12116b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_HEAD_STATE_4(0)); 12126b6b6042SThierry Reding 12136b6b6042SThierry Reding /* CSTM (LVDS, link A/B, upper) */ 1214143b1df2SStéphane Marchesin value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_A | SOR_CSTM_LINK_ACT_B | 12156b6b6042SThierry Reding SOR_CSTM_UPPER; 12166b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_CSTM); 12176b6b6042SThierry Reding 12186b6b6042SThierry Reding /* PWM setup */ 12196b6b6042SThierry Reding err = tegra_sor_setup_pwm(sor, 250); 12206b6b6042SThierry Reding if (err < 0) { 12216b6b6042SThierry Reding dev_err(sor->dev, "failed to setup PWM: %d\n", err); 122286f5c52dSThierry Reding goto unlock; 12236b6b6042SThierry Reding } 12246b6b6042SThierry Reding 12256b6b6042SThierry Reding value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); 12266b6b6042SThierry Reding value |= SOR_ENABLE; 12276b6b6042SThierry Reding tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); 12286b6b6042SThierry Reding 12296b6b6042SThierry Reding tegra_sor_update(sor); 12306b6b6042SThierry Reding 12316b6b6042SThierry Reding err = tegra_sor_attach(sor); 12326b6b6042SThierry Reding if (err < 0) { 12336b6b6042SThierry Reding dev_err(sor->dev, "failed to attach SOR: %d\n", err); 123486f5c52dSThierry Reding goto unlock; 12356b6b6042SThierry Reding } 12366b6b6042SThierry Reding 12376b6b6042SThierry Reding err = tegra_sor_wakeup(sor); 12386b6b6042SThierry Reding if (err < 0) { 12396b6b6042SThierry Reding dev_err(sor->dev, "failed to enable DC: %d\n", err); 124086f5c52dSThierry Reding goto unlock; 12416b6b6042SThierry Reding } 12426b6b6042SThierry Reding 12436fad8f66SThierry Reding if (output->panel) 12446fad8f66SThierry Reding drm_panel_enable(output->panel); 12456fad8f66SThierry Reding 12466b6b6042SThierry Reding sor->enabled = true; 12476b6b6042SThierry Reding 124886f5c52dSThierry Reding unlock: 124986f5c52dSThierry Reding mutex_unlock(&sor->lock); 12506b6b6042SThierry Reding } 12516b6b6042SThierry Reding 12526fad8f66SThierry Reding static void tegra_sor_encoder_disable(struct drm_encoder *encoder) 12536b6b6042SThierry Reding { 12546fad8f66SThierry Reding struct tegra_output *output = encoder_to_output(encoder); 12556fad8f66SThierry Reding struct tegra_dc *dc = to_tegra_dc(encoder->crtc); 12566b6b6042SThierry Reding struct tegra_sor *sor = to_sor(output); 12576fad8f66SThierry Reding u32 value; 12586fad8f66SThierry Reding int err; 125986f5c52dSThierry Reding 126086f5c52dSThierry Reding mutex_lock(&sor->lock); 12616b6b6042SThierry Reding 12626b6b6042SThierry Reding if (!sor->enabled) 126386f5c52dSThierry Reding goto unlock; 12646b6b6042SThierry Reding 12656fad8f66SThierry Reding if (output->panel) 12666fad8f66SThierry Reding drm_panel_disable(output->panel); 12676fad8f66SThierry Reding 12686b6b6042SThierry Reding err = tegra_sor_detach(sor); 12696b6b6042SThierry Reding if (err < 0) { 12706b6b6042SThierry Reding dev_err(sor->dev, "failed to detach SOR: %d\n", err); 127186f5c52dSThierry Reding goto unlock; 12726b6b6042SThierry Reding } 12736b6b6042SThierry Reding 12746b6b6042SThierry Reding tegra_sor_writel(sor, 0, SOR_STATE_1); 12756b6b6042SThierry Reding tegra_sor_update(sor); 12766b6b6042SThierry Reding 12776b6b6042SThierry Reding /* 12786b6b6042SThierry Reding * The following accesses registers of the display controller, so make 12796b6b6042SThierry Reding * sure it's only executed when the output is attached to one. 12806b6b6042SThierry Reding */ 12816b6b6042SThierry Reding if (dc) { 12826b6b6042SThierry Reding value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); 12836b6b6042SThierry Reding value &= ~SOR_ENABLE; 12846b6b6042SThierry Reding tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); 12856b6b6042SThierry Reding 128662b9e063SThierry Reding tegra_dc_commit(dc); 12876b6b6042SThierry Reding } 12886b6b6042SThierry Reding 12896b6b6042SThierry Reding err = tegra_sor_power_down(sor); 12906b6b6042SThierry Reding if (err < 0) { 12916b6b6042SThierry Reding dev_err(sor->dev, "failed to power down SOR: %d\n", err); 129286f5c52dSThierry Reding goto unlock; 12936b6b6042SThierry Reding } 12946b6b6042SThierry Reding 12956b6b6042SThierry Reding if (sor->dpaux) { 12966b6b6042SThierry Reding err = tegra_dpaux_disable(sor->dpaux); 12976b6b6042SThierry Reding if (err < 0) { 12986b6b6042SThierry Reding dev_err(sor->dev, "failed to disable DP: %d\n", err); 129986f5c52dSThierry Reding goto unlock; 13006b6b6042SThierry Reding } 13016b6b6042SThierry Reding } 13026b6b6042SThierry Reding 13036b6b6042SThierry Reding err = tegra_io_rail_power_off(TEGRA_IO_RAIL_LVDS); 13046b6b6042SThierry Reding if (err < 0) { 13056b6b6042SThierry Reding dev_err(sor->dev, "failed to power off I/O rail: %d\n", err); 130686f5c52dSThierry Reding goto unlock; 13076b6b6042SThierry Reding } 13086b6b6042SThierry Reding 13096fad8f66SThierry Reding if (output->panel) 13106fad8f66SThierry Reding drm_panel_unprepare(output->panel); 13116fad8f66SThierry Reding 13126b6b6042SThierry Reding clk_disable_unprepare(sor->clk); 13136fad8f66SThierry Reding reset_control_assert(sor->rst); 13146b6b6042SThierry Reding 13156b6b6042SThierry Reding sor->enabled = false; 13166b6b6042SThierry Reding 131786f5c52dSThierry Reding unlock: 131886f5c52dSThierry Reding mutex_unlock(&sor->lock); 13196b6b6042SThierry Reding } 13206b6b6042SThierry Reding 13216fad8f66SThierry Reding static const struct drm_encoder_helper_funcs tegra_sor_encoder_helper_funcs = { 13226fad8f66SThierry Reding .dpms = tegra_sor_encoder_dpms, 13236fad8f66SThierry Reding .mode_fixup = tegra_sor_encoder_mode_fixup, 13246fad8f66SThierry Reding .prepare = tegra_sor_encoder_prepare, 13256fad8f66SThierry Reding .commit = tegra_sor_encoder_commit, 13266fad8f66SThierry Reding .mode_set = tegra_sor_encoder_mode_set, 13276fad8f66SThierry Reding .disable = tegra_sor_encoder_disable, 13286b6b6042SThierry Reding }; 13296b6b6042SThierry Reding 13306b6b6042SThierry Reding static int tegra_sor_init(struct host1x_client *client) 13316b6b6042SThierry Reding { 13329910f5c4SThierry Reding struct drm_device *drm = dev_get_drvdata(client->parent); 13336b6b6042SThierry Reding struct tegra_sor *sor = host1x_client_to_sor(client); 13346b6b6042SThierry Reding int err; 13356b6b6042SThierry Reding 13366b6b6042SThierry Reding if (!sor->dpaux) 13376b6b6042SThierry Reding return -ENODEV; 13386b6b6042SThierry Reding 13396b6b6042SThierry Reding sor->output.dev = sor->dev; 13406b6b6042SThierry Reding 13416fad8f66SThierry Reding drm_connector_init(drm, &sor->output.connector, 13426fad8f66SThierry Reding &tegra_sor_connector_funcs, 13436fad8f66SThierry Reding DRM_MODE_CONNECTOR_eDP); 13446fad8f66SThierry Reding drm_connector_helper_add(&sor->output.connector, 13456fad8f66SThierry Reding &tegra_sor_connector_helper_funcs); 13466fad8f66SThierry Reding sor->output.connector.dpms = DRM_MODE_DPMS_OFF; 13476fad8f66SThierry Reding 13486fad8f66SThierry Reding drm_encoder_init(drm, &sor->output.encoder, &tegra_sor_encoder_funcs, 13496fad8f66SThierry Reding DRM_MODE_ENCODER_TMDS); 13506fad8f66SThierry Reding drm_encoder_helper_add(&sor->output.encoder, 13516fad8f66SThierry Reding &tegra_sor_encoder_helper_funcs); 13526fad8f66SThierry Reding 13536fad8f66SThierry Reding drm_mode_connector_attach_encoder(&sor->output.connector, 13546fad8f66SThierry Reding &sor->output.encoder); 13556fad8f66SThierry Reding drm_connector_register(&sor->output.connector); 13566fad8f66SThierry Reding 1357ea130b24SThierry Reding err = tegra_output_init(drm, &sor->output); 1358ea130b24SThierry Reding if (err < 0) { 1359ea130b24SThierry Reding dev_err(client->dev, "failed to initialize output: %d\n", err); 1360ea130b24SThierry Reding return err; 1361ea130b24SThierry Reding } 13626fad8f66SThierry Reding 1363ea130b24SThierry Reding sor->output.encoder.possible_crtcs = 0x3; 13646b6b6042SThierry Reding 1365a82752e1SThierry Reding if (IS_ENABLED(CONFIG_DEBUG_FS)) { 13661b0c7b48SThierry Reding err = tegra_sor_debugfs_init(sor, drm->primary); 1367a82752e1SThierry Reding if (err < 0) 1368a82752e1SThierry Reding dev_err(sor->dev, "debugfs setup failed: %d\n", err); 1369a82752e1SThierry Reding } 1370a82752e1SThierry Reding 13716b6b6042SThierry Reding if (sor->dpaux) { 13726b6b6042SThierry Reding err = tegra_dpaux_attach(sor->dpaux, &sor->output); 13736b6b6042SThierry Reding if (err < 0) { 13746b6b6042SThierry Reding dev_err(sor->dev, "failed to attach DP: %d\n", err); 13756b6b6042SThierry Reding return err; 13766b6b6042SThierry Reding } 13776b6b6042SThierry Reding } 13786b6b6042SThierry Reding 13796fad8f66SThierry Reding err = clk_prepare_enable(sor->clk); 13806fad8f66SThierry Reding if (err < 0) { 13816fad8f66SThierry Reding dev_err(sor->dev, "failed to enable clock: %d\n", err); 13826fad8f66SThierry Reding return err; 13836fad8f66SThierry Reding } 13846fad8f66SThierry Reding 13856fad8f66SThierry Reding err = clk_prepare_enable(sor->clk_safe); 13866fad8f66SThierry Reding if (err < 0) 13876fad8f66SThierry Reding return err; 13886fad8f66SThierry Reding 13896fad8f66SThierry Reding err = clk_prepare_enable(sor->clk_dp); 13906fad8f66SThierry Reding if (err < 0) 13916fad8f66SThierry Reding return err; 13926fad8f66SThierry Reding 13936b6b6042SThierry Reding return 0; 13946b6b6042SThierry Reding } 13956b6b6042SThierry Reding 13966b6b6042SThierry Reding static int tegra_sor_exit(struct host1x_client *client) 13976b6b6042SThierry Reding { 13986b6b6042SThierry Reding struct tegra_sor *sor = host1x_client_to_sor(client); 13996b6b6042SThierry Reding int err; 14006b6b6042SThierry Reding 1401328ec69eSThierry Reding tegra_output_exit(&sor->output); 1402328ec69eSThierry Reding 14036b6b6042SThierry Reding if (sor->dpaux) { 14046b6b6042SThierry Reding err = tegra_dpaux_detach(sor->dpaux); 14056b6b6042SThierry Reding if (err < 0) { 14066b6b6042SThierry Reding dev_err(sor->dev, "failed to detach DP: %d\n", err); 14076b6b6042SThierry Reding return err; 14086b6b6042SThierry Reding } 14096b6b6042SThierry Reding } 14106b6b6042SThierry Reding 14116fad8f66SThierry Reding clk_disable_unprepare(sor->clk_safe); 14126fad8f66SThierry Reding clk_disable_unprepare(sor->clk_dp); 14136fad8f66SThierry Reding clk_disable_unprepare(sor->clk); 14146fad8f66SThierry Reding 14154009c224SThierry Reding if (IS_ENABLED(CONFIG_DEBUG_FS)) 14164009c224SThierry Reding tegra_sor_debugfs_exit(sor); 1417a82752e1SThierry Reding 14186b6b6042SThierry Reding return 0; 14196b6b6042SThierry Reding } 14206b6b6042SThierry Reding 14216b6b6042SThierry Reding static const struct host1x_client_ops sor_client_ops = { 14226b6b6042SThierry Reding .init = tegra_sor_init, 14236b6b6042SThierry Reding .exit = tegra_sor_exit, 14246b6b6042SThierry Reding }; 14256b6b6042SThierry Reding 14266b6b6042SThierry Reding static int tegra_sor_probe(struct platform_device *pdev) 14276b6b6042SThierry Reding { 14286b6b6042SThierry Reding struct device_node *np; 14296b6b6042SThierry Reding struct tegra_sor *sor; 14306b6b6042SThierry Reding struct resource *regs; 14316b6b6042SThierry Reding int err; 14326b6b6042SThierry Reding 14336b6b6042SThierry Reding sor = devm_kzalloc(&pdev->dev, sizeof(*sor), GFP_KERNEL); 14346b6b6042SThierry Reding if (!sor) 14356b6b6042SThierry Reding return -ENOMEM; 14366b6b6042SThierry Reding 14376b6b6042SThierry Reding sor->output.dev = sor->dev = &pdev->dev; 14386b6b6042SThierry Reding 14396b6b6042SThierry Reding np = of_parse_phandle(pdev->dev.of_node, "nvidia,dpaux", 0); 14406b6b6042SThierry Reding if (np) { 14416b6b6042SThierry Reding sor->dpaux = tegra_dpaux_find_by_of_node(np); 14426b6b6042SThierry Reding of_node_put(np); 14436b6b6042SThierry Reding 14446b6b6042SThierry Reding if (!sor->dpaux) 14456b6b6042SThierry Reding return -EPROBE_DEFER; 14466b6b6042SThierry Reding } 14476b6b6042SThierry Reding 14486b6b6042SThierry Reding err = tegra_output_probe(&sor->output); 14496b6b6042SThierry Reding if (err < 0) 14506b6b6042SThierry Reding return err; 14516b6b6042SThierry Reding 14526b6b6042SThierry Reding regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 14536b6b6042SThierry Reding sor->regs = devm_ioremap_resource(&pdev->dev, regs); 14546b6b6042SThierry Reding if (IS_ERR(sor->regs)) 14556b6b6042SThierry Reding return PTR_ERR(sor->regs); 14566b6b6042SThierry Reding 14576b6b6042SThierry Reding sor->rst = devm_reset_control_get(&pdev->dev, "sor"); 14586b6b6042SThierry Reding if (IS_ERR(sor->rst)) 14596b6b6042SThierry Reding return PTR_ERR(sor->rst); 14606b6b6042SThierry Reding 14616b6b6042SThierry Reding sor->clk = devm_clk_get(&pdev->dev, NULL); 14626b6b6042SThierry Reding if (IS_ERR(sor->clk)) 14636b6b6042SThierry Reding return PTR_ERR(sor->clk); 14646b6b6042SThierry Reding 14656b6b6042SThierry Reding sor->clk_parent = devm_clk_get(&pdev->dev, "parent"); 14666b6b6042SThierry Reding if (IS_ERR(sor->clk_parent)) 14676b6b6042SThierry Reding return PTR_ERR(sor->clk_parent); 14686b6b6042SThierry Reding 14696b6b6042SThierry Reding sor->clk_safe = devm_clk_get(&pdev->dev, "safe"); 14706b6b6042SThierry Reding if (IS_ERR(sor->clk_safe)) 14716b6b6042SThierry Reding return PTR_ERR(sor->clk_safe); 14726b6b6042SThierry Reding 14736b6b6042SThierry Reding sor->clk_dp = devm_clk_get(&pdev->dev, "dp"); 14746b6b6042SThierry Reding if (IS_ERR(sor->clk_dp)) 14756b6b6042SThierry Reding return PTR_ERR(sor->clk_dp); 14766b6b6042SThierry Reding 14776b6b6042SThierry Reding INIT_LIST_HEAD(&sor->client.list); 14786b6b6042SThierry Reding sor->client.ops = &sor_client_ops; 14796b6b6042SThierry Reding sor->client.dev = &pdev->dev; 14806b6b6042SThierry Reding 148186f5c52dSThierry Reding mutex_init(&sor->lock); 148286f5c52dSThierry Reding 14836b6b6042SThierry Reding err = host1x_client_register(&sor->client); 14846b6b6042SThierry Reding if (err < 0) { 14856b6b6042SThierry Reding dev_err(&pdev->dev, "failed to register host1x client: %d\n", 14866b6b6042SThierry Reding err); 14876b6b6042SThierry Reding return err; 14886b6b6042SThierry Reding } 14896b6b6042SThierry Reding 14906b6b6042SThierry Reding platform_set_drvdata(pdev, sor); 14916b6b6042SThierry Reding 14926b6b6042SThierry Reding return 0; 14936b6b6042SThierry Reding } 14946b6b6042SThierry Reding 14956b6b6042SThierry Reding static int tegra_sor_remove(struct platform_device *pdev) 14966b6b6042SThierry Reding { 14976b6b6042SThierry Reding struct tegra_sor *sor = platform_get_drvdata(pdev); 14986b6b6042SThierry Reding int err; 14996b6b6042SThierry Reding 15006b6b6042SThierry Reding err = host1x_client_unregister(&sor->client); 15016b6b6042SThierry Reding if (err < 0) { 15026b6b6042SThierry Reding dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", 15036b6b6042SThierry Reding err); 15046b6b6042SThierry Reding return err; 15056b6b6042SThierry Reding } 15066b6b6042SThierry Reding 1507328ec69eSThierry Reding tegra_output_remove(&sor->output); 15086b6b6042SThierry Reding 15096b6b6042SThierry Reding return 0; 15106b6b6042SThierry Reding } 15116b6b6042SThierry Reding 15126b6b6042SThierry Reding static const struct of_device_id tegra_sor_of_match[] = { 15136b6b6042SThierry Reding { .compatible = "nvidia,tegra124-sor", }, 15146b6b6042SThierry Reding { }, 15156b6b6042SThierry Reding }; 1516ef70728cSStephen Warren MODULE_DEVICE_TABLE(of, tegra_sor_of_match); 15176b6b6042SThierry Reding 15186b6b6042SThierry Reding struct platform_driver tegra_sor_driver = { 15196b6b6042SThierry Reding .driver = { 15206b6b6042SThierry Reding .name = "tegra-sor", 15216b6b6042SThierry Reding .of_match_table = tegra_sor_of_match, 15226b6b6042SThierry Reding }, 15236b6b6042SThierry Reding .probe = tegra_sor_probe, 15246b6b6042SThierry Reding .remove = tegra_sor_remove, 15256b6b6042SThierry Reding }; 1526