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> 146b6b6042SThierry Reding #include <linux/tegra-powergate.h> 156b6b6042SThierry Reding 166b6b6042SThierry Reding #include <drm/drm_dp_helper.h> 176b6b6042SThierry Reding 186b6b6042SThierry Reding #include "dc.h" 196b6b6042SThierry Reding #include "drm.h" 206b6b6042SThierry Reding #include "sor.h" 216b6b6042SThierry Reding 226b6b6042SThierry Reding struct tegra_sor { 236b6b6042SThierry Reding struct host1x_client client; 246b6b6042SThierry Reding struct tegra_output output; 256b6b6042SThierry Reding struct device *dev; 266b6b6042SThierry Reding 276b6b6042SThierry Reding void __iomem *regs; 286b6b6042SThierry Reding 296b6b6042SThierry Reding struct reset_control *rst; 306b6b6042SThierry Reding struct clk *clk_parent; 316b6b6042SThierry Reding struct clk *clk_safe; 326b6b6042SThierry Reding struct clk *clk_dp; 336b6b6042SThierry Reding struct clk *clk; 346b6b6042SThierry Reding 356b6b6042SThierry Reding struct tegra_dpaux *dpaux; 366b6b6042SThierry Reding 3786f5c52dSThierry Reding struct mutex lock; 386b6b6042SThierry Reding bool enabled; 39a82752e1SThierry Reding 40a82752e1SThierry Reding struct dentry *debugfs; 416b6b6042SThierry Reding }; 426b6b6042SThierry Reding 436b6b6042SThierry Reding static inline struct tegra_sor * 446b6b6042SThierry Reding host1x_client_to_sor(struct host1x_client *client) 456b6b6042SThierry Reding { 466b6b6042SThierry Reding return container_of(client, struct tegra_sor, client); 476b6b6042SThierry Reding } 486b6b6042SThierry Reding 496b6b6042SThierry Reding static inline struct tegra_sor *to_sor(struct tegra_output *output) 506b6b6042SThierry Reding { 516b6b6042SThierry Reding return container_of(output, struct tegra_sor, output); 526b6b6042SThierry Reding } 536b6b6042SThierry Reding 546b6b6042SThierry Reding static inline unsigned long tegra_sor_readl(struct tegra_sor *sor, 556b6b6042SThierry Reding unsigned long offset) 566b6b6042SThierry Reding { 576b6b6042SThierry Reding return readl(sor->regs + (offset << 2)); 586b6b6042SThierry Reding } 596b6b6042SThierry Reding 606b6b6042SThierry Reding static inline void tegra_sor_writel(struct tegra_sor *sor, unsigned long value, 616b6b6042SThierry Reding unsigned long offset) 626b6b6042SThierry Reding { 636b6b6042SThierry Reding writel(value, sor->regs + (offset << 2)); 646b6b6042SThierry Reding } 656b6b6042SThierry Reding 666b6b6042SThierry Reding static int tegra_sor_dp_train_fast(struct tegra_sor *sor, 676b6b6042SThierry Reding struct drm_dp_link *link) 686b6b6042SThierry Reding { 696b6b6042SThierry Reding unsigned long value; 706b6b6042SThierry Reding unsigned int i; 716b6b6042SThierry Reding u8 pattern; 726b6b6042SThierry Reding int err; 736b6b6042SThierry Reding 746b6b6042SThierry Reding /* setup lane parameters */ 756b6b6042SThierry Reding value = SOR_LANE_DRIVE_CURRENT_LANE3(0x40) | 766b6b6042SThierry Reding SOR_LANE_DRIVE_CURRENT_LANE2(0x40) | 776b6b6042SThierry Reding SOR_LANE_DRIVE_CURRENT_LANE1(0x40) | 786b6b6042SThierry Reding SOR_LANE_DRIVE_CURRENT_LANE0(0x40); 796b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_LANE_DRIVE_CURRENT_0); 806b6b6042SThierry Reding 816b6b6042SThierry Reding value = SOR_LANE_PREEMPHASIS_LANE3(0x0f) | 826b6b6042SThierry Reding SOR_LANE_PREEMPHASIS_LANE2(0x0f) | 836b6b6042SThierry Reding SOR_LANE_PREEMPHASIS_LANE1(0x0f) | 846b6b6042SThierry Reding SOR_LANE_PREEMPHASIS_LANE0(0x0f); 856b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_LANE_PREEMPHASIS_0); 866b6b6042SThierry Reding 876b6b6042SThierry Reding value = SOR_LANE_POST_CURSOR_LANE3(0x00) | 886b6b6042SThierry Reding SOR_LANE_POST_CURSOR_LANE2(0x00) | 896b6b6042SThierry Reding SOR_LANE_POST_CURSOR_LANE1(0x00) | 906b6b6042SThierry Reding SOR_LANE_POST_CURSOR_LANE0(0x00); 916b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_LANE_POST_CURSOR_0); 926b6b6042SThierry Reding 936b6b6042SThierry Reding /* disable LVDS mode */ 946b6b6042SThierry Reding tegra_sor_writel(sor, 0, SOR_LVDS); 956b6b6042SThierry Reding 966b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); 976b6b6042SThierry Reding value |= SOR_DP_PADCTL_TX_PU_ENABLE; 986b6b6042SThierry Reding value &= ~SOR_DP_PADCTL_TX_PU_MASK; 996b6b6042SThierry Reding value |= SOR_DP_PADCTL_TX_PU(2); /* XXX: don't hardcode? */ 1006b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); 1016b6b6042SThierry Reding 1026b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); 1036b6b6042SThierry Reding value |= SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 | 1046b6b6042SThierry Reding SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0; 1056b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); 1066b6b6042SThierry Reding 1076b6b6042SThierry Reding usleep_range(10, 100); 1086b6b6042SThierry Reding 1096b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); 1106b6b6042SThierry Reding value &= ~(SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 | 1116b6b6042SThierry Reding SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0); 1126b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); 1136b6b6042SThierry Reding 1146b6b6042SThierry Reding err = tegra_dpaux_prepare(sor->dpaux, DP_SET_ANSI_8B10B); 1156b6b6042SThierry Reding if (err < 0) 1166b6b6042SThierry Reding return err; 1176b6b6042SThierry Reding 1186b6b6042SThierry Reding for (i = 0, value = 0; i < link->num_lanes; i++) { 1196b6b6042SThierry Reding unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | 1206b6b6042SThierry Reding SOR_DP_TPG_SCRAMBLER_NONE | 1216b6b6042SThierry Reding SOR_DP_TPG_PATTERN_TRAIN1; 1226b6b6042SThierry Reding value = (value << 8) | lane; 1236b6b6042SThierry Reding } 1246b6b6042SThierry Reding 1256b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_TPG); 1266b6b6042SThierry Reding 1276b6b6042SThierry Reding pattern = DP_TRAINING_PATTERN_1; 1286b6b6042SThierry Reding 1296b6b6042SThierry Reding err = tegra_dpaux_train(sor->dpaux, link, pattern); 1306b6b6042SThierry Reding if (err < 0) 1316b6b6042SThierry Reding return err; 1326b6b6042SThierry Reding 1336b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_SPARE_0); 1346b6b6042SThierry Reding value |= SOR_DP_SPARE_SEQ_ENABLE; 1356b6b6042SThierry Reding value &= ~SOR_DP_SPARE_PANEL_INTERNAL; 1366b6b6042SThierry Reding value |= SOR_DP_SPARE_MACRO_SOR_CLK; 1376b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_SPARE_0); 1386b6b6042SThierry Reding 1396b6b6042SThierry Reding for (i = 0, value = 0; i < link->num_lanes; i++) { 1406b6b6042SThierry Reding unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | 1416b6b6042SThierry Reding SOR_DP_TPG_SCRAMBLER_NONE | 1426b6b6042SThierry Reding SOR_DP_TPG_PATTERN_TRAIN2; 1436b6b6042SThierry Reding value = (value << 8) | lane; 1446b6b6042SThierry Reding } 1456b6b6042SThierry Reding 1466b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_TPG); 1476b6b6042SThierry Reding 1486b6b6042SThierry Reding pattern = DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_2; 1496b6b6042SThierry Reding 1506b6b6042SThierry Reding err = tegra_dpaux_train(sor->dpaux, link, pattern); 1516b6b6042SThierry Reding if (err < 0) 1526b6b6042SThierry Reding return err; 1536b6b6042SThierry Reding 1546b6b6042SThierry Reding for (i = 0, value = 0; i < link->num_lanes; i++) { 1556b6b6042SThierry Reding unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | 1566b6b6042SThierry Reding SOR_DP_TPG_SCRAMBLER_GALIOS | 1576b6b6042SThierry Reding SOR_DP_TPG_PATTERN_NONE; 1586b6b6042SThierry Reding value = (value << 8) | lane; 1596b6b6042SThierry Reding } 1606b6b6042SThierry Reding 1616b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_TPG); 1626b6b6042SThierry Reding 1636b6b6042SThierry Reding pattern = DP_TRAINING_PATTERN_DISABLE; 1646b6b6042SThierry Reding 1656b6b6042SThierry Reding err = tegra_dpaux_train(sor->dpaux, link, pattern); 1666b6b6042SThierry Reding if (err < 0) 1676b6b6042SThierry Reding return err; 1686b6b6042SThierry Reding 1696b6b6042SThierry Reding return 0; 1706b6b6042SThierry Reding } 1716b6b6042SThierry Reding 1726b6b6042SThierry Reding static void tegra_sor_super_update(struct tegra_sor *sor) 1736b6b6042SThierry Reding { 1746b6b6042SThierry Reding tegra_sor_writel(sor, 0, SOR_SUPER_STATE_0); 1756b6b6042SThierry Reding tegra_sor_writel(sor, 1, SOR_SUPER_STATE_0); 1766b6b6042SThierry Reding tegra_sor_writel(sor, 0, SOR_SUPER_STATE_0); 1776b6b6042SThierry Reding } 1786b6b6042SThierry Reding 1796b6b6042SThierry Reding static void tegra_sor_update(struct tegra_sor *sor) 1806b6b6042SThierry Reding { 1816b6b6042SThierry Reding tegra_sor_writel(sor, 0, SOR_STATE_0); 1826b6b6042SThierry Reding tegra_sor_writel(sor, 1, SOR_STATE_0); 1836b6b6042SThierry Reding tegra_sor_writel(sor, 0, SOR_STATE_0); 1846b6b6042SThierry Reding } 1856b6b6042SThierry Reding 1866b6b6042SThierry Reding static int tegra_sor_setup_pwm(struct tegra_sor *sor, unsigned long timeout) 1876b6b6042SThierry Reding { 1886b6b6042SThierry Reding unsigned long value; 1896b6b6042SThierry Reding 1906b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PWM_DIV); 1916b6b6042SThierry Reding value &= ~SOR_PWM_DIV_MASK; 1926b6b6042SThierry Reding value |= 0x400; /* period */ 1936b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PWM_DIV); 1946b6b6042SThierry Reding 1956b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PWM_CTL); 1966b6b6042SThierry Reding value &= ~SOR_PWM_CTL_DUTY_CYCLE_MASK; 1976b6b6042SThierry Reding value |= 0x400; /* duty cycle */ 1986b6b6042SThierry Reding value &= ~SOR_PWM_CTL_CLK_SEL; /* clock source: PCLK */ 1996b6b6042SThierry Reding value |= SOR_PWM_CTL_TRIGGER; 2006b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PWM_CTL); 2016b6b6042SThierry Reding 2026b6b6042SThierry Reding timeout = jiffies + msecs_to_jiffies(timeout); 2036b6b6042SThierry Reding 2046b6b6042SThierry Reding while (time_before(jiffies, timeout)) { 2056b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PWM_CTL); 2066b6b6042SThierry Reding if ((value & SOR_PWM_CTL_TRIGGER) == 0) 2076b6b6042SThierry Reding return 0; 2086b6b6042SThierry Reding 2096b6b6042SThierry Reding usleep_range(25, 100); 2106b6b6042SThierry Reding } 2116b6b6042SThierry Reding 2126b6b6042SThierry Reding return -ETIMEDOUT; 2136b6b6042SThierry Reding } 2146b6b6042SThierry Reding 2156b6b6042SThierry Reding static int tegra_sor_attach(struct tegra_sor *sor) 2166b6b6042SThierry Reding { 2176b6b6042SThierry Reding unsigned long value, timeout; 2186b6b6042SThierry Reding 2196b6b6042SThierry Reding /* wake up in normal mode */ 2206b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); 2216b6b6042SThierry Reding value |= SOR_SUPER_STATE_HEAD_MODE_AWAKE; 2226b6b6042SThierry Reding value |= SOR_SUPER_STATE_MODE_NORMAL; 2236b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); 2246b6b6042SThierry Reding tegra_sor_super_update(sor); 2256b6b6042SThierry Reding 2266b6b6042SThierry Reding /* attach */ 2276b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); 2286b6b6042SThierry Reding value |= SOR_SUPER_STATE_ATTACHED; 2296b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); 2306b6b6042SThierry Reding tegra_sor_super_update(sor); 2316b6b6042SThierry Reding 2326b6b6042SThierry Reding timeout = jiffies + msecs_to_jiffies(250); 2336b6b6042SThierry Reding 2346b6b6042SThierry Reding while (time_before(jiffies, timeout)) { 2356b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_TEST); 2366b6b6042SThierry Reding if ((value & SOR_TEST_ATTACHED) != 0) 2376b6b6042SThierry Reding return 0; 2386b6b6042SThierry Reding 2396b6b6042SThierry Reding usleep_range(25, 100); 2406b6b6042SThierry Reding } 2416b6b6042SThierry Reding 2426b6b6042SThierry Reding return -ETIMEDOUT; 2436b6b6042SThierry Reding } 2446b6b6042SThierry Reding 2456b6b6042SThierry Reding static int tegra_sor_wakeup(struct tegra_sor *sor) 2466b6b6042SThierry Reding { 2476b6b6042SThierry Reding struct tegra_dc *dc = to_tegra_dc(sor->output.encoder.crtc); 2486b6b6042SThierry Reding unsigned long value, timeout; 2496b6b6042SThierry Reding 2506b6b6042SThierry Reding /* enable display controller outputs */ 2516b6b6042SThierry Reding value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); 2526b6b6042SThierry Reding value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | 2536b6b6042SThierry Reding PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; 2546b6b6042SThierry Reding tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); 2556b6b6042SThierry Reding 2566b6b6042SThierry Reding tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); 2576b6b6042SThierry Reding tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); 2586b6b6042SThierry Reding 2596b6b6042SThierry Reding timeout = jiffies + msecs_to_jiffies(250); 2606b6b6042SThierry Reding 2616b6b6042SThierry Reding /* wait for head to wake up */ 2626b6b6042SThierry Reding while (time_before(jiffies, timeout)) { 2636b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_TEST); 2646b6b6042SThierry Reding value &= SOR_TEST_HEAD_MODE_MASK; 2656b6b6042SThierry Reding 2666b6b6042SThierry Reding if (value == SOR_TEST_HEAD_MODE_AWAKE) 2676b6b6042SThierry Reding return 0; 2686b6b6042SThierry Reding 2696b6b6042SThierry Reding usleep_range(25, 100); 2706b6b6042SThierry Reding } 2716b6b6042SThierry Reding 2726b6b6042SThierry Reding return -ETIMEDOUT; 2736b6b6042SThierry Reding } 2746b6b6042SThierry Reding 2756b6b6042SThierry Reding static int tegra_sor_power_up(struct tegra_sor *sor, unsigned long timeout) 2766b6b6042SThierry Reding { 2776b6b6042SThierry Reding unsigned long value; 2786b6b6042SThierry Reding 2796b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PWR); 2806b6b6042SThierry Reding value |= SOR_PWR_TRIGGER | SOR_PWR_NORMAL_STATE_PU; 2816b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PWR); 2826b6b6042SThierry Reding 2836b6b6042SThierry Reding timeout = jiffies + msecs_to_jiffies(timeout); 2846b6b6042SThierry Reding 2856b6b6042SThierry Reding while (time_before(jiffies, timeout)) { 2866b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PWR); 2876b6b6042SThierry Reding if ((value & SOR_PWR_TRIGGER) == 0) 2886b6b6042SThierry Reding return 0; 2896b6b6042SThierry Reding 2906b6b6042SThierry Reding usleep_range(25, 100); 2916b6b6042SThierry Reding } 2926b6b6042SThierry Reding 2936b6b6042SThierry Reding return -ETIMEDOUT; 2946b6b6042SThierry Reding } 2956b6b6042SThierry Reding 2966b6b6042SThierry Reding static int tegra_output_sor_enable(struct tegra_output *output) 2976b6b6042SThierry Reding { 2986b6b6042SThierry Reding struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); 2996b6b6042SThierry Reding struct drm_display_mode *mode = &dc->base.mode; 3006b6b6042SThierry Reding unsigned int vbe, vse, hbe, hse, vbs, hbs, i; 3016b6b6042SThierry Reding struct tegra_sor *sor = to_sor(output); 3026b6b6042SThierry Reding unsigned long value; 30386f5c52dSThierry Reding int err = 0; 30486f5c52dSThierry Reding 30586f5c52dSThierry Reding mutex_lock(&sor->lock); 3066b6b6042SThierry Reding 3076b6b6042SThierry Reding if (sor->enabled) 30886f5c52dSThierry Reding goto unlock; 3096b6b6042SThierry Reding 3106b6b6042SThierry Reding err = clk_prepare_enable(sor->clk); 3116b6b6042SThierry Reding if (err < 0) 31286f5c52dSThierry Reding goto unlock; 3136b6b6042SThierry Reding 3146b6b6042SThierry Reding reset_control_deassert(sor->rst); 3156b6b6042SThierry Reding 3166b6b6042SThierry Reding if (sor->dpaux) { 3176b6b6042SThierry Reding err = tegra_dpaux_enable(sor->dpaux); 3186b6b6042SThierry Reding if (err < 0) 3196b6b6042SThierry Reding dev_err(sor->dev, "failed to enable DP: %d\n", err); 3206b6b6042SThierry Reding } 3216b6b6042SThierry Reding 3226b6b6042SThierry Reding err = clk_set_parent(sor->clk, sor->clk_safe); 3236b6b6042SThierry Reding if (err < 0) 3246b6b6042SThierry Reding dev_err(sor->dev, "failed to set safe parent clock: %d\n", err); 3256b6b6042SThierry Reding 3266b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_CLK_CNTRL); 3276b6b6042SThierry Reding value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK; 3286b6b6042SThierry Reding value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK; 3296b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_CLK_CNTRL); 3306b6b6042SThierry Reding 3316b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 3326b6b6042SThierry Reding value &= ~SOR_PLL_2_BANDGAP_POWERDOWN; 3336b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 3346b6b6042SThierry Reding usleep_range(20, 100); 3356b6b6042SThierry Reding 3366b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_3); 3376b6b6042SThierry Reding value |= SOR_PLL_3_PLL_VDD_MODE_V3_3; 3386b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_3); 3396b6b6042SThierry Reding 3406b6b6042SThierry Reding value = SOR_PLL_0_ICHPMP(0xf) | SOR_PLL_0_VCOCAP_RST | 3416b6b6042SThierry Reding SOR_PLL_0_PLLREG_LEVEL_V45 | SOR_PLL_0_RESISTOR_EXT; 3426b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_0); 3436b6b6042SThierry Reding 3446b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 3456b6b6042SThierry Reding value |= SOR_PLL_2_SEQ_PLLCAPPD; 3466b6b6042SThierry Reding value &= ~SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE; 3476b6b6042SThierry Reding value |= SOR_PLL_2_LVDS_ENABLE; 3486b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 3496b6b6042SThierry Reding 3506b6b6042SThierry Reding value = SOR_PLL_1_TERM_COMPOUT | SOR_PLL_1_TMDS_TERM; 3516b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_1); 3526b6b6042SThierry Reding 3536b6b6042SThierry Reding while (true) { 3546b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 3556b6b6042SThierry Reding if ((value & SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE) == 0) 3566b6b6042SThierry Reding break; 3576b6b6042SThierry Reding 3586b6b6042SThierry Reding usleep_range(250, 1000); 3596b6b6042SThierry Reding } 3606b6b6042SThierry Reding 3616b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 3626b6b6042SThierry Reding value &= ~SOR_PLL_2_POWERDOWN_OVERRIDE; 3636b6b6042SThierry Reding value &= ~SOR_PLL_2_PORT_POWERDOWN; 3646b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 3656b6b6042SThierry Reding 3666b6b6042SThierry Reding /* 3676b6b6042SThierry Reding * power up 3686b6b6042SThierry Reding */ 3696b6b6042SThierry Reding 3706b6b6042SThierry Reding /* set safe link bandwidth (1.62 Gbps) */ 3716b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_CLK_CNTRL); 3726b6b6042SThierry Reding value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK; 3736b6b6042SThierry Reding value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G1_62; 3746b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_CLK_CNTRL); 3756b6b6042SThierry Reding 3766b6b6042SThierry Reding /* step 1 */ 3776b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 3786b6b6042SThierry Reding value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE | SOR_PLL_2_PORT_POWERDOWN | 3796b6b6042SThierry Reding SOR_PLL_2_BANDGAP_POWERDOWN; 3806b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 3816b6b6042SThierry Reding 3826b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_0); 3836b6b6042SThierry Reding value |= SOR_PLL_0_VCOPD | SOR_PLL_0_POWER_OFF; 3846b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_0); 3856b6b6042SThierry Reding 3866b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); 3876b6b6042SThierry Reding value &= ~SOR_DP_PADCTL_PAD_CAL_PD; 3886b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); 3896b6b6042SThierry Reding 3906b6b6042SThierry Reding /* step 2 */ 3916b6b6042SThierry Reding err = tegra_io_rail_power_on(TEGRA_IO_RAIL_LVDS); 3926b6b6042SThierry Reding if (err < 0) { 3936b6b6042SThierry Reding dev_err(sor->dev, "failed to power on I/O rail: %d\n", err); 39486f5c52dSThierry Reding goto unlock; 3956b6b6042SThierry Reding } 3966b6b6042SThierry Reding 3976b6b6042SThierry Reding usleep_range(5, 100); 3986b6b6042SThierry Reding 3996b6b6042SThierry Reding /* step 3 */ 4006b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 4016b6b6042SThierry Reding value &= ~SOR_PLL_2_BANDGAP_POWERDOWN; 4026b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 4036b6b6042SThierry Reding 4046b6b6042SThierry Reding usleep_range(20, 100); 4056b6b6042SThierry Reding 4066b6b6042SThierry Reding /* step 4 */ 4076b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_0); 4086b6b6042SThierry Reding value &= ~SOR_PLL_0_POWER_OFF; 4096b6b6042SThierry Reding value &= ~SOR_PLL_0_VCOPD; 4106b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_0); 4116b6b6042SThierry Reding 4126b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 4136b6b6042SThierry Reding value &= ~SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE; 4146b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 4156b6b6042SThierry Reding 4166b6b6042SThierry Reding usleep_range(200, 1000); 4176b6b6042SThierry Reding 4186b6b6042SThierry Reding /* step 5 */ 4196b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 4206b6b6042SThierry Reding value &= ~SOR_PLL_2_PORT_POWERDOWN; 4216b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 4226b6b6042SThierry Reding 4236b6b6042SThierry Reding /* switch to DP clock */ 4246b6b6042SThierry Reding err = clk_set_parent(sor->clk, sor->clk_dp); 4256b6b6042SThierry Reding if (err < 0) 4266b6b6042SThierry Reding dev_err(sor->dev, "failed to set DP parent clock: %d\n", err); 4276b6b6042SThierry Reding 4286b6b6042SThierry Reding /* power dplanes (XXX parameterize based on link?) */ 4296b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); 4306b6b6042SThierry Reding value |= SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 | 4316b6b6042SThierry Reding SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2; 4326b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); 4336b6b6042SThierry Reding 4346b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0); 4356b6b6042SThierry Reding value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK; 4366b6b6042SThierry Reding value |= SOR_DP_LINKCTL_LANE_COUNT(4); 4376b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0); 4386b6b6042SThierry Reding 4396b6b6042SThierry Reding /* start lane sequencer */ 4406b6b6042SThierry Reding value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_DOWN | 4416b6b6042SThierry Reding SOR_LANE_SEQ_CTL_POWER_STATE_UP; 4426b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL); 4436b6b6042SThierry Reding 4446b6b6042SThierry Reding while (true) { 4456b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL); 4466b6b6042SThierry Reding if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0) 4476b6b6042SThierry Reding break; 4486b6b6042SThierry Reding 4496b6b6042SThierry Reding usleep_range(250, 1000); 4506b6b6042SThierry Reding } 4516b6b6042SThierry Reding 4526b6b6042SThierry Reding /* set link bandwidth (2.7 GHz, XXX: parameterize based on link?) */ 4536b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_CLK_CNTRL); 4546b6b6042SThierry Reding value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK; 4556b6b6042SThierry Reding value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G2_70; 4566b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_CLK_CNTRL); 4576b6b6042SThierry Reding 4586b6b6042SThierry Reding /* set linkctl */ 4596b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0); 4606b6b6042SThierry Reding value |= SOR_DP_LINKCTL_ENABLE; 4616b6b6042SThierry Reding 4626b6b6042SThierry Reding value &= ~SOR_DP_LINKCTL_TU_SIZE_MASK; 4636b6b6042SThierry Reding value |= SOR_DP_LINKCTL_TU_SIZE(59); /* XXX: don't hardcode? */ 4646b6b6042SThierry Reding 4656b6b6042SThierry Reding value |= SOR_DP_LINKCTL_ENHANCED_FRAME; 4666b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0); 4676b6b6042SThierry Reding 4686b6b6042SThierry Reding for (i = 0, value = 0; i < 4; i++) { 4696b6b6042SThierry Reding unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | 4706b6b6042SThierry Reding SOR_DP_TPG_SCRAMBLER_GALIOS | 4716b6b6042SThierry Reding SOR_DP_TPG_PATTERN_NONE; 4726b6b6042SThierry Reding value = (value << 8) | lane; 4736b6b6042SThierry Reding } 4746b6b6042SThierry Reding 4756b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_TPG); 4766b6b6042SThierry Reding 4776b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_CONFIG_0); 4786b6b6042SThierry Reding value &= ~SOR_DP_CONFIG_WATERMARK_MASK; 4796b6b6042SThierry Reding value |= SOR_DP_CONFIG_WATERMARK(14); /* XXX: don't hardcode? */ 4806b6b6042SThierry Reding 4816b6b6042SThierry Reding value &= ~SOR_DP_CONFIG_ACTIVE_SYM_COUNT_MASK; 4826b6b6042SThierry Reding value |= SOR_DP_CONFIG_ACTIVE_SYM_COUNT(47); /* XXX: don't hardcode? */ 4836b6b6042SThierry Reding 4846b6b6042SThierry Reding value &= ~SOR_DP_CONFIG_ACTIVE_SYM_FRAC_MASK; 4856b6b6042SThierry Reding value |= SOR_DP_CONFIG_ACTIVE_SYM_FRAC(9); /* XXX: don't hardcode? */ 4866b6b6042SThierry Reding 4876b6b6042SThierry Reding value &= ~SOR_DP_CONFIG_ACTIVE_SYM_POLARITY; /* XXX: don't hardcode? */ 4886b6b6042SThierry Reding 4896b6b6042SThierry Reding value |= SOR_DP_CONFIG_ACTIVE_SYM_ENABLE; 4906b6b6042SThierry Reding value |= SOR_DP_CONFIG_DISPARITY_NEGATIVE; /* XXX: don't hardcode? */ 4916b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_CONFIG_0); 4926b6b6042SThierry Reding 4936b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_AUDIO_HBLANK_SYMBOLS); 4946b6b6042SThierry Reding value &= ~SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK; 4956b6b6042SThierry Reding value |= 137; /* XXX: don't hardcode? */ 4966b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_AUDIO_HBLANK_SYMBOLS); 4976b6b6042SThierry Reding 4986b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_AUDIO_VBLANK_SYMBOLS); 4996b6b6042SThierry Reding value &= ~SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK; 5006b6b6042SThierry Reding value |= 2368; /* XXX: don't hardcode? */ 5016b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_AUDIO_VBLANK_SYMBOLS); 5026b6b6042SThierry Reding 5036b6b6042SThierry Reding /* enable pad calibration logic */ 5046b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); 5056b6b6042SThierry Reding value |= SOR_DP_PADCTL_PAD_CAL_PD; 5066b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); 5076b6b6042SThierry Reding 5086b6b6042SThierry Reding if (sor->dpaux) { 5096b6b6042SThierry Reding /* FIXME: properly convert to struct drm_dp_aux */ 5106b6b6042SThierry Reding struct drm_dp_aux *aux = (struct drm_dp_aux *)sor->dpaux; 5116b6b6042SThierry Reding struct drm_dp_link link; 5126b6b6042SThierry Reding u8 rate, lanes; 5136b6b6042SThierry Reding 5146b6b6042SThierry Reding err = drm_dp_link_probe(aux, &link); 5156b6b6042SThierry Reding if (err < 0) { 5166b6b6042SThierry Reding dev_err(sor->dev, "failed to probe eDP link: %d\n", 5176b6b6042SThierry Reding err); 51886f5c52dSThierry Reding goto unlock; 5196b6b6042SThierry Reding } 5206b6b6042SThierry Reding 5216b6b6042SThierry Reding err = drm_dp_link_power_up(aux, &link); 5226b6b6042SThierry Reding if (err < 0) { 5236b6b6042SThierry Reding dev_err(sor->dev, "failed to power up eDP link: %d\n", 5246b6b6042SThierry Reding err); 52586f5c52dSThierry Reding goto unlock; 5266b6b6042SThierry Reding } 5276b6b6042SThierry Reding 5286b6b6042SThierry Reding err = drm_dp_link_configure(aux, &link); 5296b6b6042SThierry Reding if (err < 0) { 5306b6b6042SThierry Reding dev_err(sor->dev, "failed to configure eDP link: %d\n", 5316b6b6042SThierry Reding err); 53286f5c52dSThierry Reding goto unlock; 5336b6b6042SThierry Reding } 5346b6b6042SThierry Reding 5356b6b6042SThierry Reding rate = drm_dp_link_rate_to_bw_code(link.rate); 5366b6b6042SThierry Reding lanes = link.num_lanes; 5376b6b6042SThierry Reding 5386b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_CLK_CNTRL); 5396b6b6042SThierry Reding value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK; 5406b6b6042SThierry Reding value |= SOR_CLK_CNTRL_DP_LINK_SPEED(rate); 5416b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_CLK_CNTRL); 5426b6b6042SThierry Reding 5436b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0); 5446b6b6042SThierry Reding value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK; 5456b6b6042SThierry Reding value |= SOR_DP_LINKCTL_LANE_COUNT(lanes); 5466b6b6042SThierry Reding 5476b6b6042SThierry Reding if (link.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) 5486b6b6042SThierry Reding value |= SOR_DP_LINKCTL_ENHANCED_FRAME; 5496b6b6042SThierry Reding 5506b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0); 5516b6b6042SThierry Reding 5526b6b6042SThierry Reding /* disable training pattern generator */ 5536b6b6042SThierry Reding 5546b6b6042SThierry Reding for (i = 0; i < link.num_lanes; i++) { 5556b6b6042SThierry Reding unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | 5566b6b6042SThierry Reding SOR_DP_TPG_SCRAMBLER_GALIOS | 5576b6b6042SThierry Reding SOR_DP_TPG_PATTERN_NONE; 5586b6b6042SThierry Reding value = (value << 8) | lane; 5596b6b6042SThierry Reding } 5606b6b6042SThierry Reding 5616b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_TPG); 5626b6b6042SThierry Reding 5636b6b6042SThierry Reding err = tegra_sor_dp_train_fast(sor, &link); 5646b6b6042SThierry Reding if (err < 0) { 5656b6b6042SThierry Reding dev_err(sor->dev, "DP fast link training failed: %d\n", 5666b6b6042SThierry Reding err); 56786f5c52dSThierry Reding goto unlock; 5686b6b6042SThierry Reding } 5696b6b6042SThierry Reding 5706b6b6042SThierry Reding dev_dbg(sor->dev, "fast link training succeeded\n"); 5716b6b6042SThierry Reding } 5726b6b6042SThierry Reding 5736b6b6042SThierry Reding err = tegra_sor_power_up(sor, 250); 5746b6b6042SThierry Reding if (err < 0) { 5756b6b6042SThierry Reding dev_err(sor->dev, "failed to power up SOR: %d\n", err); 57686f5c52dSThierry Reding goto unlock; 5776b6b6042SThierry Reding } 5786b6b6042SThierry Reding 5796b6b6042SThierry Reding /* start display controller in continuous mode */ 5806b6b6042SThierry Reding value = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS); 5816b6b6042SThierry Reding value |= WRITE_MUX; 5826b6b6042SThierry Reding tegra_dc_writel(dc, value, DC_CMD_STATE_ACCESS); 5836b6b6042SThierry Reding 5846b6b6042SThierry Reding tegra_dc_writel(dc, VSYNC_H_POSITION(1), DC_DISP_DISP_TIMING_OPTIONS); 5856b6b6042SThierry Reding tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, DC_CMD_DISPLAY_COMMAND); 5866b6b6042SThierry Reding 5876b6b6042SThierry Reding value = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS); 5886b6b6042SThierry Reding value &= ~WRITE_MUX; 5896b6b6042SThierry Reding tegra_dc_writel(dc, value, DC_CMD_STATE_ACCESS); 5906b6b6042SThierry Reding 5916b6b6042SThierry Reding /* 5926b6b6042SThierry Reding * configure panel (24bpp, vsync-, hsync-, DP-A protocol, complete 5936b6b6042SThierry Reding * raster, associate with display controller) 5946b6b6042SThierry Reding */ 5956b6b6042SThierry Reding value = SOR_STATE_ASY_PIXELDEPTH_BPP_24_444 | 5966b6b6042SThierry Reding SOR_STATE_ASY_VSYNCPOL | 5976b6b6042SThierry Reding SOR_STATE_ASY_HSYNCPOL | 5986b6b6042SThierry Reding SOR_STATE_ASY_PROTOCOL_DP_A | 5996b6b6042SThierry Reding SOR_STATE_ASY_CRC_MODE_COMPLETE | 6006b6b6042SThierry Reding SOR_STATE_ASY_OWNER(dc->pipe + 1); 6016b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_STATE_1); 6026b6b6042SThierry Reding 6036b6b6042SThierry Reding /* 6046b6b6042SThierry Reding * TODO: The video timing programming below doesn't seem to match the 6056b6b6042SThierry Reding * register definitions. 6066b6b6042SThierry Reding */ 6076b6b6042SThierry Reding 6086b6b6042SThierry Reding value = ((mode->vtotal & 0x7fff) << 16) | (mode->htotal & 0x7fff); 6096b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_HEAD_STATE_1(0)); 6106b6b6042SThierry Reding 6116b6b6042SThierry Reding vse = mode->vsync_end - mode->vsync_start - 1; 6126b6b6042SThierry Reding hse = mode->hsync_end - mode->hsync_start - 1; 6136b6b6042SThierry Reding 6146b6b6042SThierry Reding value = ((vse & 0x7fff) << 16) | (hse & 0x7fff); 6156b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_HEAD_STATE_2(0)); 6166b6b6042SThierry Reding 6176b6b6042SThierry Reding vbe = vse + (mode->vsync_start - mode->vdisplay); 6186b6b6042SThierry Reding hbe = hse + (mode->hsync_start - mode->hdisplay); 6196b6b6042SThierry Reding 6206b6b6042SThierry Reding value = ((vbe & 0x7fff) << 16) | (hbe & 0x7fff); 6216b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_HEAD_STATE_3(0)); 6226b6b6042SThierry Reding 6236b6b6042SThierry Reding vbs = vbe + mode->vdisplay; 6246b6b6042SThierry Reding hbs = hbe + mode->hdisplay; 6256b6b6042SThierry Reding 6266b6b6042SThierry Reding value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff); 6276b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_HEAD_STATE_4(0)); 6286b6b6042SThierry Reding 6296b6b6042SThierry Reding /* XXX interlaced mode */ 6306b6b6042SThierry Reding tegra_sor_writel(sor, 0x00000001, SOR_HEAD_STATE_5(0)); 6316b6b6042SThierry Reding 6326b6b6042SThierry Reding /* CSTM (LVDS, link A/B, upper) */ 6336b6b6042SThierry Reding value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_B | SOR_CSTM_LINK_ACT_B | 6346b6b6042SThierry Reding SOR_CSTM_UPPER; 6356b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_CSTM); 6366b6b6042SThierry Reding 6376b6b6042SThierry Reding /* PWM setup */ 6386b6b6042SThierry Reding err = tegra_sor_setup_pwm(sor, 250); 6396b6b6042SThierry Reding if (err < 0) { 6406b6b6042SThierry Reding dev_err(sor->dev, "failed to setup PWM: %d\n", err); 64186f5c52dSThierry Reding goto unlock; 6426b6b6042SThierry Reding } 6436b6b6042SThierry Reding 6446b6b6042SThierry Reding value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); 6456b6b6042SThierry Reding value |= SOR_ENABLE; 6466b6b6042SThierry Reding tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); 6476b6b6042SThierry Reding 6486b6b6042SThierry Reding tegra_sor_update(sor); 6496b6b6042SThierry Reding 6506b6b6042SThierry Reding err = tegra_sor_attach(sor); 6516b6b6042SThierry Reding if (err < 0) { 6526b6b6042SThierry Reding dev_err(sor->dev, "failed to attach SOR: %d\n", err); 65386f5c52dSThierry Reding goto unlock; 6546b6b6042SThierry Reding } 6556b6b6042SThierry Reding 6566b6b6042SThierry Reding err = tegra_sor_wakeup(sor); 6576b6b6042SThierry Reding if (err < 0) { 6586b6b6042SThierry Reding dev_err(sor->dev, "failed to enable DC: %d\n", err); 65986f5c52dSThierry Reding goto unlock; 6606b6b6042SThierry Reding } 6616b6b6042SThierry Reding 6626b6b6042SThierry Reding sor->enabled = true; 6636b6b6042SThierry Reding 66486f5c52dSThierry Reding unlock: 66586f5c52dSThierry Reding mutex_unlock(&sor->lock); 66686f5c52dSThierry Reding return err; 6676b6b6042SThierry Reding } 6686b6b6042SThierry Reding 6696b6b6042SThierry Reding static int tegra_sor_detach(struct tegra_sor *sor) 6706b6b6042SThierry Reding { 6716b6b6042SThierry Reding unsigned long value, timeout; 6726b6b6042SThierry Reding 6736b6b6042SThierry Reding /* switch to safe mode */ 6746b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); 6756b6b6042SThierry Reding value &= ~SOR_SUPER_STATE_MODE_NORMAL; 6766b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); 6776b6b6042SThierry Reding tegra_sor_super_update(sor); 6786b6b6042SThierry Reding 6796b6b6042SThierry Reding timeout = jiffies + msecs_to_jiffies(250); 6806b6b6042SThierry Reding 6816b6b6042SThierry Reding while (time_before(jiffies, timeout)) { 6826b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PWR); 6836b6b6042SThierry Reding if (value & SOR_PWR_MODE_SAFE) 6846b6b6042SThierry Reding break; 6856b6b6042SThierry Reding } 6866b6b6042SThierry Reding 6876b6b6042SThierry Reding if ((value & SOR_PWR_MODE_SAFE) == 0) 6886b6b6042SThierry Reding return -ETIMEDOUT; 6896b6b6042SThierry Reding 6906b6b6042SThierry Reding /* go to sleep */ 6916b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); 6926b6b6042SThierry Reding value &= ~SOR_SUPER_STATE_HEAD_MODE_MASK; 6936b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); 6946b6b6042SThierry Reding tegra_sor_super_update(sor); 6956b6b6042SThierry Reding 6966b6b6042SThierry Reding /* detach */ 6976b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); 6986b6b6042SThierry Reding value &= ~SOR_SUPER_STATE_ATTACHED; 6996b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); 7006b6b6042SThierry Reding tegra_sor_super_update(sor); 7016b6b6042SThierry Reding 7026b6b6042SThierry Reding timeout = jiffies + msecs_to_jiffies(250); 7036b6b6042SThierry Reding 7046b6b6042SThierry Reding while (time_before(jiffies, timeout)) { 7056b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_TEST); 7066b6b6042SThierry Reding if ((value & SOR_TEST_ATTACHED) == 0) 7076b6b6042SThierry Reding break; 7086b6b6042SThierry Reding 7096b6b6042SThierry Reding usleep_range(25, 100); 7106b6b6042SThierry Reding } 7116b6b6042SThierry Reding 7126b6b6042SThierry Reding if ((value & SOR_TEST_ATTACHED) != 0) 7136b6b6042SThierry Reding return -ETIMEDOUT; 7146b6b6042SThierry Reding 7156b6b6042SThierry Reding return 0; 7166b6b6042SThierry Reding } 7176b6b6042SThierry Reding 7186b6b6042SThierry Reding static int tegra_sor_power_down(struct tegra_sor *sor) 7196b6b6042SThierry Reding { 7206b6b6042SThierry Reding unsigned long value, timeout; 7216b6b6042SThierry Reding int err; 7226b6b6042SThierry Reding 7236b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PWR); 7246b6b6042SThierry Reding value &= ~SOR_PWR_NORMAL_STATE_PU; 7256b6b6042SThierry Reding value |= SOR_PWR_TRIGGER; 7266b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PWR); 7276b6b6042SThierry Reding 7286b6b6042SThierry Reding timeout = jiffies + msecs_to_jiffies(250); 7296b6b6042SThierry Reding 7306b6b6042SThierry Reding while (time_before(jiffies, timeout)) { 7316b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PWR); 7326b6b6042SThierry Reding if ((value & SOR_PWR_TRIGGER) == 0) 7336b6b6042SThierry Reding return 0; 7346b6b6042SThierry Reding 7356b6b6042SThierry Reding usleep_range(25, 100); 7366b6b6042SThierry Reding } 7376b6b6042SThierry Reding 7386b6b6042SThierry Reding if ((value & SOR_PWR_TRIGGER) != 0) 7396b6b6042SThierry Reding return -ETIMEDOUT; 7406b6b6042SThierry Reding 7416b6b6042SThierry Reding err = clk_set_parent(sor->clk, sor->clk_safe); 7426b6b6042SThierry Reding if (err < 0) 7436b6b6042SThierry Reding dev_err(sor->dev, "failed to set safe parent clock: %d\n", err); 7446b6b6042SThierry Reding 7456b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); 7466b6b6042SThierry Reding value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 | 7476b6b6042SThierry Reding SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2); 7486b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); 7496b6b6042SThierry Reding 7506b6b6042SThierry Reding /* stop lane sequencer */ 7516b6b6042SThierry Reding value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_DOWN | 7526b6b6042SThierry Reding SOR_LANE_SEQ_CTL_POWER_STATE_DOWN; 7536b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL); 7546b6b6042SThierry Reding 7556b6b6042SThierry Reding timeout = jiffies + msecs_to_jiffies(250); 7566b6b6042SThierry Reding 7576b6b6042SThierry Reding while (time_before(jiffies, timeout)) { 7586b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL); 7596b6b6042SThierry Reding if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0) 7606b6b6042SThierry Reding break; 7616b6b6042SThierry Reding 7626b6b6042SThierry Reding usleep_range(25, 100); 7636b6b6042SThierry Reding } 7646b6b6042SThierry Reding 7656b6b6042SThierry Reding if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0) 7666b6b6042SThierry Reding return -ETIMEDOUT; 7676b6b6042SThierry Reding 7686b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 7696b6b6042SThierry Reding value |= SOR_PLL_2_PORT_POWERDOWN; 7706b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 7716b6b6042SThierry Reding 7726b6b6042SThierry Reding usleep_range(20, 100); 7736b6b6042SThierry Reding 7746b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_0); 7756b6b6042SThierry Reding value |= SOR_PLL_0_POWER_OFF; 7766b6b6042SThierry Reding value |= SOR_PLL_0_VCOPD; 7776b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_0); 7786b6b6042SThierry Reding 7796b6b6042SThierry Reding value = tegra_sor_readl(sor, SOR_PLL_2); 7806b6b6042SThierry Reding value |= SOR_PLL_2_SEQ_PLLCAPPD; 7816b6b6042SThierry Reding value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE; 7826b6b6042SThierry Reding tegra_sor_writel(sor, value, SOR_PLL_2); 7836b6b6042SThierry Reding 7846b6b6042SThierry Reding usleep_range(20, 100); 7856b6b6042SThierry Reding 7866b6b6042SThierry Reding return 0; 7876b6b6042SThierry Reding } 7886b6b6042SThierry Reding 7896b6b6042SThierry Reding static int tegra_output_sor_disable(struct tegra_output *output) 7906b6b6042SThierry Reding { 7916b6b6042SThierry Reding struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); 7926b6b6042SThierry Reding struct tegra_sor *sor = to_sor(output); 7936b6b6042SThierry Reding unsigned long value; 79486f5c52dSThierry Reding int err = 0; 79586f5c52dSThierry Reding 79686f5c52dSThierry Reding mutex_lock(&sor->lock); 7976b6b6042SThierry Reding 7986b6b6042SThierry Reding if (!sor->enabled) 79986f5c52dSThierry Reding goto unlock; 8006b6b6042SThierry Reding 8016b6b6042SThierry Reding err = tegra_sor_detach(sor); 8026b6b6042SThierry Reding if (err < 0) { 8036b6b6042SThierry Reding dev_err(sor->dev, "failed to detach SOR: %d\n", err); 80486f5c52dSThierry Reding goto unlock; 8056b6b6042SThierry Reding } 8066b6b6042SThierry Reding 8076b6b6042SThierry Reding tegra_sor_writel(sor, 0, SOR_STATE_1); 8086b6b6042SThierry Reding tegra_sor_update(sor); 8096b6b6042SThierry Reding 8106b6b6042SThierry Reding /* 8116b6b6042SThierry Reding * The following accesses registers of the display controller, so make 8126b6b6042SThierry Reding * sure it's only executed when the output is attached to one. 8136b6b6042SThierry Reding */ 8146b6b6042SThierry Reding if (dc) { 8156b6b6042SThierry Reding /* 8166b6b6042SThierry Reding * XXX: We can't do this here because it causes the SOR to go 8176b6b6042SThierry Reding * into an erroneous state and the output will look scrambled 8186b6b6042SThierry Reding * the next time it is enabled. Presumably this is because we 8196b6b6042SThierry Reding * should be doing this only on the next VBLANK. A possible 8206b6b6042SThierry Reding * solution would be to queue a "power-off" event to trigger 8216b6b6042SThierry Reding * this code to be run during the next VBLANK. 8226b6b6042SThierry Reding */ 8236b6b6042SThierry Reding /* 8246b6b6042SThierry Reding value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); 8256b6b6042SThierry Reding value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | 8266b6b6042SThierry Reding PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); 8276b6b6042SThierry Reding tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); 8286b6b6042SThierry Reding */ 8296b6b6042SThierry Reding 8306b6b6042SThierry Reding value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); 8316b6b6042SThierry Reding value &= ~DISP_CTRL_MODE_MASK; 8326b6b6042SThierry Reding tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); 8336b6b6042SThierry Reding 8346b6b6042SThierry Reding value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); 8356b6b6042SThierry Reding value &= ~SOR_ENABLE; 8366b6b6042SThierry Reding tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); 8376b6b6042SThierry Reding 8386b6b6042SThierry Reding tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); 8396b6b6042SThierry Reding tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); 8406b6b6042SThierry Reding } 8416b6b6042SThierry Reding 8426b6b6042SThierry Reding err = tegra_sor_power_down(sor); 8436b6b6042SThierry Reding if (err < 0) { 8446b6b6042SThierry Reding dev_err(sor->dev, "failed to power down SOR: %d\n", err); 84586f5c52dSThierry Reding goto unlock; 8466b6b6042SThierry Reding } 8476b6b6042SThierry Reding 8486b6b6042SThierry Reding if (sor->dpaux) { 8496b6b6042SThierry Reding err = tegra_dpaux_disable(sor->dpaux); 8506b6b6042SThierry Reding if (err < 0) { 8516b6b6042SThierry Reding dev_err(sor->dev, "failed to disable DP: %d\n", err); 85286f5c52dSThierry Reding goto unlock; 8536b6b6042SThierry Reding } 8546b6b6042SThierry Reding } 8556b6b6042SThierry Reding 8566b6b6042SThierry Reding err = tegra_io_rail_power_off(TEGRA_IO_RAIL_LVDS); 8576b6b6042SThierry Reding if (err < 0) { 8586b6b6042SThierry Reding dev_err(sor->dev, "failed to power off I/O rail: %d\n", err); 85986f5c52dSThierry Reding goto unlock; 8606b6b6042SThierry Reding } 8616b6b6042SThierry Reding 8626b6b6042SThierry Reding reset_control_assert(sor->rst); 8636b6b6042SThierry Reding clk_disable_unprepare(sor->clk); 8646b6b6042SThierry Reding 8656b6b6042SThierry Reding sor->enabled = false; 8666b6b6042SThierry Reding 86786f5c52dSThierry Reding unlock: 86886f5c52dSThierry Reding mutex_unlock(&sor->lock); 86986f5c52dSThierry Reding return err; 8706b6b6042SThierry Reding } 8716b6b6042SThierry Reding 8726b6b6042SThierry Reding static int tegra_output_sor_setup_clock(struct tegra_output *output, 87391eded9bSThierry Reding struct clk *clk, unsigned long pclk, 87491eded9bSThierry Reding unsigned int *div) 8756b6b6042SThierry Reding { 8766b6b6042SThierry Reding struct tegra_sor *sor = to_sor(output); 8776b6b6042SThierry Reding int err; 8786b6b6042SThierry Reding 8796b6b6042SThierry Reding /* round to next MHz */ 88091eded9bSThierry Reding pclk = DIV_ROUND_UP(pclk, 1000000) * 1000000; 8816b6b6042SThierry Reding 8826b6b6042SThierry Reding err = clk_set_parent(clk, sor->clk_parent); 8836b6b6042SThierry Reding if (err < 0) { 8846b6b6042SThierry Reding dev_err(sor->dev, "failed to set parent clock: %d\n", err); 8856b6b6042SThierry Reding return err; 8866b6b6042SThierry Reding } 8876b6b6042SThierry Reding 8886b6b6042SThierry Reding err = clk_set_rate(sor->clk_parent, pclk); 8896b6b6042SThierry Reding if (err < 0) { 89091eded9bSThierry Reding dev_err(sor->dev, "failed to set clock rate to %lu Hz\n", pclk); 8916b6b6042SThierry Reding return err; 8926b6b6042SThierry Reding } 8936b6b6042SThierry Reding 89491eded9bSThierry Reding *div = 0; 89591eded9bSThierry Reding 8966b6b6042SThierry Reding return 0; 8976b6b6042SThierry Reding } 8986b6b6042SThierry Reding 8996b6b6042SThierry Reding static int tegra_output_sor_check_mode(struct tegra_output *output, 9006b6b6042SThierry Reding struct drm_display_mode *mode, 9016b6b6042SThierry Reding enum drm_mode_status *status) 9026b6b6042SThierry Reding { 9036b6b6042SThierry Reding /* 9046b6b6042SThierry Reding * FIXME: For now, always assume that the mode is okay. 9056b6b6042SThierry Reding */ 9066b6b6042SThierry Reding 9076b6b6042SThierry Reding *status = MODE_OK; 9086b6b6042SThierry Reding 9096b6b6042SThierry Reding return 0; 9106b6b6042SThierry Reding } 9116b6b6042SThierry Reding 9126b6b6042SThierry Reding static enum drm_connector_status 9136b6b6042SThierry Reding tegra_output_sor_detect(struct tegra_output *output) 9146b6b6042SThierry Reding { 9156b6b6042SThierry Reding struct tegra_sor *sor = to_sor(output); 9166b6b6042SThierry Reding 9176b6b6042SThierry Reding if (sor->dpaux) 9186b6b6042SThierry Reding return tegra_dpaux_detect(sor->dpaux); 9196b6b6042SThierry Reding 9206b6b6042SThierry Reding return connector_status_unknown; 9216b6b6042SThierry Reding } 9226b6b6042SThierry Reding 9236b6b6042SThierry Reding static const struct tegra_output_ops sor_ops = { 9246b6b6042SThierry Reding .enable = tegra_output_sor_enable, 9256b6b6042SThierry Reding .disable = tegra_output_sor_disable, 9266b6b6042SThierry Reding .setup_clock = tegra_output_sor_setup_clock, 9276b6b6042SThierry Reding .check_mode = tegra_output_sor_check_mode, 9286b6b6042SThierry Reding .detect = tegra_output_sor_detect, 9296b6b6042SThierry Reding }; 9306b6b6042SThierry Reding 931a82752e1SThierry Reding static int tegra_sor_crc_open(struct inode *inode, struct file *file) 932a82752e1SThierry Reding { 933a82752e1SThierry Reding file->private_data = inode->i_private; 934a82752e1SThierry Reding 935a82752e1SThierry Reding return 0; 936a82752e1SThierry Reding } 937a82752e1SThierry Reding 938a82752e1SThierry Reding static int tegra_sor_crc_release(struct inode *inode, struct file *file) 939a82752e1SThierry Reding { 940a82752e1SThierry Reding return 0; 941a82752e1SThierry Reding } 942a82752e1SThierry Reding 943a82752e1SThierry Reding static int tegra_sor_crc_wait(struct tegra_sor *sor, unsigned long timeout) 944a82752e1SThierry Reding { 945a82752e1SThierry Reding u32 value; 946a82752e1SThierry Reding 947a82752e1SThierry Reding timeout = jiffies + msecs_to_jiffies(timeout); 948a82752e1SThierry Reding 949a82752e1SThierry Reding while (time_before(jiffies, timeout)) { 950a82752e1SThierry Reding value = tegra_sor_readl(sor, SOR_CRC_A); 951a82752e1SThierry Reding if (value & SOR_CRC_A_VALID) 952a82752e1SThierry Reding return 0; 953a82752e1SThierry Reding 954a82752e1SThierry Reding usleep_range(100, 200); 955a82752e1SThierry Reding } 956a82752e1SThierry Reding 957a82752e1SThierry Reding return -ETIMEDOUT; 958a82752e1SThierry Reding } 959a82752e1SThierry Reding 960a82752e1SThierry Reding static ssize_t tegra_sor_crc_read(struct file *file, char __user *buffer, 961a82752e1SThierry Reding size_t size, loff_t *ppos) 962a82752e1SThierry Reding { 963a82752e1SThierry Reding struct tegra_sor *sor = file->private_data; 96486f5c52dSThierry Reding ssize_t num, err; 965a82752e1SThierry Reding char buf[10]; 966a82752e1SThierry Reding u32 value; 96786f5c52dSThierry Reding 96886f5c52dSThierry Reding mutex_lock(&sor->lock); 96986f5c52dSThierry Reding 97086f5c52dSThierry Reding if (!sor->enabled) { 97186f5c52dSThierry Reding err = -EAGAIN; 97286f5c52dSThierry Reding goto unlock; 97386f5c52dSThierry Reding } 974a82752e1SThierry Reding 975a82752e1SThierry Reding value = tegra_sor_readl(sor, SOR_STATE_1); 976a82752e1SThierry Reding value &= ~SOR_STATE_ASY_CRC_MODE_MASK; 977a82752e1SThierry Reding tegra_sor_writel(sor, value, SOR_STATE_1); 978a82752e1SThierry Reding 979a82752e1SThierry Reding value = tegra_sor_readl(sor, SOR_CRC_CNTRL); 980a82752e1SThierry Reding value |= SOR_CRC_CNTRL_ENABLE; 981a82752e1SThierry Reding tegra_sor_writel(sor, value, SOR_CRC_CNTRL); 982a82752e1SThierry Reding 983a82752e1SThierry Reding value = tegra_sor_readl(sor, SOR_TEST); 984a82752e1SThierry Reding value &= ~SOR_TEST_CRC_POST_SERIALIZE; 985a82752e1SThierry Reding tegra_sor_writel(sor, value, SOR_TEST); 986a82752e1SThierry Reding 987a82752e1SThierry Reding err = tegra_sor_crc_wait(sor, 100); 988a82752e1SThierry Reding if (err < 0) 98986f5c52dSThierry Reding goto unlock; 990a82752e1SThierry Reding 991a82752e1SThierry Reding tegra_sor_writel(sor, SOR_CRC_A_RESET, SOR_CRC_A); 992a82752e1SThierry Reding value = tegra_sor_readl(sor, SOR_CRC_B); 993a82752e1SThierry Reding 994a82752e1SThierry Reding num = scnprintf(buf, sizeof(buf), "%08x\n", value); 995a82752e1SThierry Reding 99686f5c52dSThierry Reding err = simple_read_from_buffer(buffer, size, ppos, buf, num); 99786f5c52dSThierry Reding 99886f5c52dSThierry Reding unlock: 99986f5c52dSThierry Reding mutex_unlock(&sor->lock); 100086f5c52dSThierry Reding return err; 1001a82752e1SThierry Reding } 1002a82752e1SThierry Reding 1003a82752e1SThierry Reding static const struct file_operations tegra_sor_crc_fops = { 1004a82752e1SThierry Reding .owner = THIS_MODULE, 1005a82752e1SThierry Reding .open = tegra_sor_crc_open, 1006a82752e1SThierry Reding .read = tegra_sor_crc_read, 1007a82752e1SThierry Reding .release = tegra_sor_crc_release, 1008a82752e1SThierry Reding }; 1009a82752e1SThierry Reding 1010*1b0c7b48SThierry Reding static int tegra_sor_debugfs_init(struct tegra_sor *sor, 1011*1b0c7b48SThierry Reding struct drm_minor *minor) 1012a82752e1SThierry Reding { 1013a82752e1SThierry Reding struct dentry *entry; 1014a82752e1SThierry Reding int err = 0; 1015a82752e1SThierry Reding 1016*1b0c7b48SThierry Reding sor->debugfs = debugfs_create_dir("sor", minor->debugfs_root); 1017a82752e1SThierry Reding if (!sor->debugfs) 1018a82752e1SThierry Reding return -ENOMEM; 1019a82752e1SThierry Reding 1020a82752e1SThierry Reding entry = debugfs_create_file("crc", 0644, sor->debugfs, sor, 1021a82752e1SThierry Reding &tegra_sor_crc_fops); 1022a82752e1SThierry Reding if (!entry) { 1023a82752e1SThierry Reding dev_err(sor->dev, 1024a82752e1SThierry Reding "cannot create /sys/kernel/debug/dri/%s/sor/crc\n", 1025*1b0c7b48SThierry Reding minor->debugfs_root->d_name.name); 1026a82752e1SThierry Reding err = -ENOMEM; 1027a82752e1SThierry Reding goto remove; 1028a82752e1SThierry Reding } 1029a82752e1SThierry Reding 1030a82752e1SThierry Reding return err; 1031a82752e1SThierry Reding 1032a82752e1SThierry Reding remove: 1033a82752e1SThierry Reding debugfs_remove(sor->debugfs); 1034a82752e1SThierry Reding sor->debugfs = NULL; 1035a82752e1SThierry Reding return err; 1036a82752e1SThierry Reding } 1037a82752e1SThierry Reding 1038a82752e1SThierry Reding static int tegra_sor_debugfs_exit(struct tegra_sor *sor) 1039a82752e1SThierry Reding { 10409578184eSThierry Reding debugfs_remove_recursive(sor->debugfs); 1041a82752e1SThierry Reding sor->debugfs = NULL; 1042a82752e1SThierry Reding 1043a82752e1SThierry Reding return 0; 1044a82752e1SThierry Reding } 1045a82752e1SThierry Reding 10466b6b6042SThierry Reding static int tegra_sor_init(struct host1x_client *client) 10476b6b6042SThierry Reding { 10489910f5c4SThierry Reding struct drm_device *drm = dev_get_drvdata(client->parent); 10496b6b6042SThierry Reding struct tegra_sor *sor = host1x_client_to_sor(client); 10506b6b6042SThierry Reding int err; 10516b6b6042SThierry Reding 10526b6b6042SThierry Reding if (!sor->dpaux) 10536b6b6042SThierry Reding return -ENODEV; 10546b6b6042SThierry Reding 10556b6b6042SThierry Reding sor->output.type = TEGRA_OUTPUT_EDP; 10566b6b6042SThierry Reding 10576b6b6042SThierry Reding sor->output.dev = sor->dev; 10586b6b6042SThierry Reding sor->output.ops = &sor_ops; 10596b6b6042SThierry Reding 10609910f5c4SThierry Reding err = tegra_output_init(drm, &sor->output); 10616b6b6042SThierry Reding if (err < 0) { 10626b6b6042SThierry Reding dev_err(sor->dev, "output setup failed: %d\n", err); 10636b6b6042SThierry Reding return err; 10646b6b6042SThierry Reding } 10656b6b6042SThierry Reding 1066a82752e1SThierry Reding if (IS_ENABLED(CONFIG_DEBUG_FS)) { 1067*1b0c7b48SThierry Reding err = tegra_sor_debugfs_init(sor, drm->primary); 1068a82752e1SThierry Reding if (err < 0) 1069a82752e1SThierry Reding dev_err(sor->dev, "debugfs setup failed: %d\n", err); 1070a82752e1SThierry Reding } 1071a82752e1SThierry Reding 10726b6b6042SThierry Reding if (sor->dpaux) { 10736b6b6042SThierry Reding err = tegra_dpaux_attach(sor->dpaux, &sor->output); 10746b6b6042SThierry Reding if (err < 0) { 10756b6b6042SThierry Reding dev_err(sor->dev, "failed to attach DP: %d\n", err); 10766b6b6042SThierry Reding return err; 10776b6b6042SThierry Reding } 10786b6b6042SThierry Reding } 10796b6b6042SThierry Reding 10806b6b6042SThierry Reding return 0; 10816b6b6042SThierry Reding } 10826b6b6042SThierry Reding 10836b6b6042SThierry Reding static int tegra_sor_exit(struct host1x_client *client) 10846b6b6042SThierry Reding { 10856b6b6042SThierry Reding struct tegra_sor *sor = host1x_client_to_sor(client); 10866b6b6042SThierry Reding int err; 10876b6b6042SThierry Reding 10886b6b6042SThierry Reding err = tegra_output_disable(&sor->output); 10896b6b6042SThierry Reding if (err < 0) { 10906b6b6042SThierry Reding dev_err(sor->dev, "output failed to disable: %d\n", err); 10916b6b6042SThierry Reding return err; 10926b6b6042SThierry Reding } 10936b6b6042SThierry Reding 10946b6b6042SThierry Reding if (sor->dpaux) { 10956b6b6042SThierry Reding err = tegra_dpaux_detach(sor->dpaux); 10966b6b6042SThierry Reding if (err < 0) { 10976b6b6042SThierry Reding dev_err(sor->dev, "failed to detach DP: %d\n", err); 10986b6b6042SThierry Reding return err; 10996b6b6042SThierry Reding } 11006b6b6042SThierry Reding } 11016b6b6042SThierry Reding 1102a82752e1SThierry Reding if (IS_ENABLED(CONFIG_DEBUG_FS)) { 1103a82752e1SThierry Reding err = tegra_sor_debugfs_exit(sor); 1104a82752e1SThierry Reding if (err < 0) 1105a82752e1SThierry Reding dev_err(sor->dev, "debugfs cleanup failed: %d\n", err); 1106a82752e1SThierry Reding } 1107a82752e1SThierry Reding 11086b6b6042SThierry Reding err = tegra_output_exit(&sor->output); 11096b6b6042SThierry Reding if (err < 0) { 11106b6b6042SThierry Reding dev_err(sor->dev, "output cleanup failed: %d\n", err); 11116b6b6042SThierry Reding return err; 11126b6b6042SThierry Reding } 11136b6b6042SThierry Reding 11146b6b6042SThierry Reding return 0; 11156b6b6042SThierry Reding } 11166b6b6042SThierry Reding 11176b6b6042SThierry Reding static const struct host1x_client_ops sor_client_ops = { 11186b6b6042SThierry Reding .init = tegra_sor_init, 11196b6b6042SThierry Reding .exit = tegra_sor_exit, 11206b6b6042SThierry Reding }; 11216b6b6042SThierry Reding 11226b6b6042SThierry Reding static int tegra_sor_probe(struct platform_device *pdev) 11236b6b6042SThierry Reding { 11246b6b6042SThierry Reding struct device_node *np; 11256b6b6042SThierry Reding struct tegra_sor *sor; 11266b6b6042SThierry Reding struct resource *regs; 11276b6b6042SThierry Reding int err; 11286b6b6042SThierry Reding 11296b6b6042SThierry Reding sor = devm_kzalloc(&pdev->dev, sizeof(*sor), GFP_KERNEL); 11306b6b6042SThierry Reding if (!sor) 11316b6b6042SThierry Reding return -ENOMEM; 11326b6b6042SThierry Reding 11336b6b6042SThierry Reding sor->output.dev = sor->dev = &pdev->dev; 11346b6b6042SThierry Reding 11356b6b6042SThierry Reding np = of_parse_phandle(pdev->dev.of_node, "nvidia,dpaux", 0); 11366b6b6042SThierry Reding if (np) { 11376b6b6042SThierry Reding sor->dpaux = tegra_dpaux_find_by_of_node(np); 11386b6b6042SThierry Reding of_node_put(np); 11396b6b6042SThierry Reding 11406b6b6042SThierry Reding if (!sor->dpaux) 11416b6b6042SThierry Reding return -EPROBE_DEFER; 11426b6b6042SThierry Reding } 11436b6b6042SThierry Reding 11446b6b6042SThierry Reding err = tegra_output_probe(&sor->output); 11456b6b6042SThierry Reding if (err < 0) 11466b6b6042SThierry Reding return err; 11476b6b6042SThierry Reding 11486b6b6042SThierry Reding regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 11496b6b6042SThierry Reding sor->regs = devm_ioremap_resource(&pdev->dev, regs); 11506b6b6042SThierry Reding if (IS_ERR(sor->regs)) 11516b6b6042SThierry Reding return PTR_ERR(sor->regs); 11526b6b6042SThierry Reding 11536b6b6042SThierry Reding sor->rst = devm_reset_control_get(&pdev->dev, "sor"); 11546b6b6042SThierry Reding if (IS_ERR(sor->rst)) 11556b6b6042SThierry Reding return PTR_ERR(sor->rst); 11566b6b6042SThierry Reding 11576b6b6042SThierry Reding sor->clk = devm_clk_get(&pdev->dev, NULL); 11586b6b6042SThierry Reding if (IS_ERR(sor->clk)) 11596b6b6042SThierry Reding return PTR_ERR(sor->clk); 11606b6b6042SThierry Reding 11616b6b6042SThierry Reding sor->clk_parent = devm_clk_get(&pdev->dev, "parent"); 11626b6b6042SThierry Reding if (IS_ERR(sor->clk_parent)) 11636b6b6042SThierry Reding return PTR_ERR(sor->clk_parent); 11646b6b6042SThierry Reding 11656b6b6042SThierry Reding err = clk_prepare_enable(sor->clk_parent); 11666b6b6042SThierry Reding if (err < 0) 11676b6b6042SThierry Reding return err; 11686b6b6042SThierry Reding 11696b6b6042SThierry Reding sor->clk_safe = devm_clk_get(&pdev->dev, "safe"); 11706b6b6042SThierry Reding if (IS_ERR(sor->clk_safe)) 11716b6b6042SThierry Reding return PTR_ERR(sor->clk_safe); 11726b6b6042SThierry Reding 11736b6b6042SThierry Reding err = clk_prepare_enable(sor->clk_safe); 11746b6b6042SThierry Reding if (err < 0) 11756b6b6042SThierry Reding return err; 11766b6b6042SThierry Reding 11776b6b6042SThierry Reding sor->clk_dp = devm_clk_get(&pdev->dev, "dp"); 11786b6b6042SThierry Reding if (IS_ERR(sor->clk_dp)) 11796b6b6042SThierry Reding return PTR_ERR(sor->clk_dp); 11806b6b6042SThierry Reding 11816b6b6042SThierry Reding err = clk_prepare_enable(sor->clk_dp); 11826b6b6042SThierry Reding if (err < 0) 11836b6b6042SThierry Reding return err; 11846b6b6042SThierry Reding 11856b6b6042SThierry Reding INIT_LIST_HEAD(&sor->client.list); 11866b6b6042SThierry Reding sor->client.ops = &sor_client_ops; 11876b6b6042SThierry Reding sor->client.dev = &pdev->dev; 11886b6b6042SThierry Reding 118986f5c52dSThierry Reding mutex_init(&sor->lock); 119086f5c52dSThierry Reding 11916b6b6042SThierry Reding err = host1x_client_register(&sor->client); 11926b6b6042SThierry Reding if (err < 0) { 11936b6b6042SThierry Reding dev_err(&pdev->dev, "failed to register host1x client: %d\n", 11946b6b6042SThierry Reding err); 11956b6b6042SThierry Reding return err; 11966b6b6042SThierry Reding } 11976b6b6042SThierry Reding 11986b6b6042SThierry Reding platform_set_drvdata(pdev, sor); 11996b6b6042SThierry Reding 12006b6b6042SThierry Reding return 0; 12016b6b6042SThierry Reding } 12026b6b6042SThierry Reding 12036b6b6042SThierry Reding static int tegra_sor_remove(struct platform_device *pdev) 12046b6b6042SThierry Reding { 12056b6b6042SThierry Reding struct tegra_sor *sor = platform_get_drvdata(pdev); 12066b6b6042SThierry Reding int err; 12076b6b6042SThierry Reding 12086b6b6042SThierry Reding err = host1x_client_unregister(&sor->client); 12096b6b6042SThierry Reding if (err < 0) { 12106b6b6042SThierry Reding dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", 12116b6b6042SThierry Reding err); 12126b6b6042SThierry Reding return err; 12136b6b6042SThierry Reding } 12146b6b6042SThierry Reding 12156b6b6042SThierry Reding clk_disable_unprepare(sor->clk_parent); 12166b6b6042SThierry Reding clk_disable_unprepare(sor->clk_safe); 12176b6b6042SThierry Reding clk_disable_unprepare(sor->clk_dp); 12186b6b6042SThierry Reding clk_disable_unprepare(sor->clk); 12196b6b6042SThierry Reding 12206b6b6042SThierry Reding return 0; 12216b6b6042SThierry Reding } 12226b6b6042SThierry Reding 12236b6b6042SThierry Reding static const struct of_device_id tegra_sor_of_match[] = { 12246b6b6042SThierry Reding { .compatible = "nvidia,tegra124-sor", }, 12256b6b6042SThierry Reding { }, 12266b6b6042SThierry Reding }; 12276b6b6042SThierry Reding 12286b6b6042SThierry Reding struct platform_driver tegra_sor_driver = { 12296b6b6042SThierry Reding .driver = { 12306b6b6042SThierry Reding .name = "tegra-sor", 12316b6b6042SThierry Reding .of_match_table = tegra_sor_of_match, 12326b6b6042SThierry Reding }, 12336b6b6042SThierry Reding .probe = tegra_sor_probe, 12346b6b6042SThierry Reding .remove = tegra_sor_remove, 12356b6b6042SThierry Reding }; 1236