18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * tc358767 eDP bridge driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 CogentEmbedded Inc 68c2ecf20Sopenharmony_ci * Author: Andrey Gusakov <andrey.gusakov@cogentembedded.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 2016 Pengutronix, Philipp Zabel <p.zabel@pengutronix.de> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Copyright (C) 2016 Zodiac Inflight Innovations 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Initially based on: drivers/gpu/drm/i2c/tda998x_drv.c 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Copyright (C) 2012 Texas Instruments 158c2ecf20Sopenharmony_ci * Author: Rob Clark <robdclark@gmail.com> 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 198c2ecf20Sopenharmony_ci#include <linux/clk.h> 208c2ecf20Sopenharmony_ci#include <linux/device.h> 218c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 228c2ecf20Sopenharmony_ci#include <linux/i2c.h> 238c2ecf20Sopenharmony_ci#include <linux/kernel.h> 248c2ecf20Sopenharmony_ci#include <linux/module.h> 258c2ecf20Sopenharmony_ci#include <linux/regmap.h> 268c2ecf20Sopenharmony_ci#include <linux/slab.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 298c2ecf20Sopenharmony_ci#include <drm/drm_bridge.h> 308c2ecf20Sopenharmony_ci#include <drm/drm_dp_helper.h> 318c2ecf20Sopenharmony_ci#include <drm/drm_edid.h> 328c2ecf20Sopenharmony_ci#include <drm/drm_of.h> 338c2ecf20Sopenharmony_ci#include <drm/drm_panel.h> 348c2ecf20Sopenharmony_ci#include <drm/drm_print.h> 358c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* Registers */ 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* Display Parallel Interface */ 408c2ecf20Sopenharmony_ci#define DPIPXLFMT 0x0440 418c2ecf20Sopenharmony_ci#define VS_POL_ACTIVE_LOW (1 << 10) 428c2ecf20Sopenharmony_ci#define HS_POL_ACTIVE_LOW (1 << 9) 438c2ecf20Sopenharmony_ci#define DE_POL_ACTIVE_HIGH (0 << 8) 448c2ecf20Sopenharmony_ci#define SUB_CFG_TYPE_CONFIG1 (0 << 2) /* LSB aligned */ 458c2ecf20Sopenharmony_ci#define SUB_CFG_TYPE_CONFIG2 (1 << 2) /* Loosely Packed */ 468c2ecf20Sopenharmony_ci#define SUB_CFG_TYPE_CONFIG3 (2 << 2) /* LSB aligned 8-bit */ 478c2ecf20Sopenharmony_ci#define DPI_BPP_RGB888 (0 << 0) 488c2ecf20Sopenharmony_ci#define DPI_BPP_RGB666 (1 << 0) 498c2ecf20Sopenharmony_ci#define DPI_BPP_RGB565 (2 << 0) 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* Video Path */ 528c2ecf20Sopenharmony_ci#define VPCTRL0 0x0450 538c2ecf20Sopenharmony_ci#define VSDELAY GENMASK(31, 20) 548c2ecf20Sopenharmony_ci#define OPXLFMT_RGB666 (0 << 8) 558c2ecf20Sopenharmony_ci#define OPXLFMT_RGB888 (1 << 8) 568c2ecf20Sopenharmony_ci#define FRMSYNC_DISABLED (0 << 4) /* Video Timing Gen Disabled */ 578c2ecf20Sopenharmony_ci#define FRMSYNC_ENABLED (1 << 4) /* Video Timing Gen Enabled */ 588c2ecf20Sopenharmony_ci#define MSF_DISABLED (0 << 0) /* Magic Square FRC disabled */ 598c2ecf20Sopenharmony_ci#define MSF_ENABLED (1 << 0) /* Magic Square FRC enabled */ 608c2ecf20Sopenharmony_ci#define HTIM01 0x0454 618c2ecf20Sopenharmony_ci#define HPW GENMASK(8, 0) 628c2ecf20Sopenharmony_ci#define HBPR GENMASK(24, 16) 638c2ecf20Sopenharmony_ci#define HTIM02 0x0458 648c2ecf20Sopenharmony_ci#define HDISPR GENMASK(10, 0) 658c2ecf20Sopenharmony_ci#define HFPR GENMASK(24, 16) 668c2ecf20Sopenharmony_ci#define VTIM01 0x045c 678c2ecf20Sopenharmony_ci#define VSPR GENMASK(7, 0) 688c2ecf20Sopenharmony_ci#define VBPR GENMASK(23, 16) 698c2ecf20Sopenharmony_ci#define VTIM02 0x0460 708c2ecf20Sopenharmony_ci#define VFPR GENMASK(23, 16) 718c2ecf20Sopenharmony_ci#define VDISPR GENMASK(10, 0) 728c2ecf20Sopenharmony_ci#define VFUEN0 0x0464 738c2ecf20Sopenharmony_ci#define VFUEN BIT(0) /* Video Frame Timing Upload */ 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* System */ 768c2ecf20Sopenharmony_ci#define TC_IDREG 0x0500 778c2ecf20Sopenharmony_ci#define SYSSTAT 0x0508 788c2ecf20Sopenharmony_ci#define SYSCTRL 0x0510 798c2ecf20Sopenharmony_ci#define DP0_AUDSRC_NO_INPUT (0 << 3) 808c2ecf20Sopenharmony_ci#define DP0_AUDSRC_I2S_RX (1 << 3) 818c2ecf20Sopenharmony_ci#define DP0_VIDSRC_NO_INPUT (0 << 0) 828c2ecf20Sopenharmony_ci#define DP0_VIDSRC_DSI_RX (1 << 0) 838c2ecf20Sopenharmony_ci#define DP0_VIDSRC_DPI_RX (2 << 0) 848c2ecf20Sopenharmony_ci#define DP0_VIDSRC_COLOR_BAR (3 << 0) 858c2ecf20Sopenharmony_ci#define SYSRSTENB 0x050c 868c2ecf20Sopenharmony_ci#define ENBI2C (1 << 0) 878c2ecf20Sopenharmony_ci#define ENBLCD0 (1 << 2) 888c2ecf20Sopenharmony_ci#define ENBBM (1 << 3) 898c2ecf20Sopenharmony_ci#define ENBDSIRX (1 << 4) 908c2ecf20Sopenharmony_ci#define ENBREG (1 << 5) 918c2ecf20Sopenharmony_ci#define ENBHDCP (1 << 8) 928c2ecf20Sopenharmony_ci#define GPIOM 0x0540 938c2ecf20Sopenharmony_ci#define GPIOC 0x0544 948c2ecf20Sopenharmony_ci#define GPIOO 0x0548 958c2ecf20Sopenharmony_ci#define GPIOI 0x054c 968c2ecf20Sopenharmony_ci#define INTCTL_G 0x0560 978c2ecf20Sopenharmony_ci#define INTSTS_G 0x0564 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci#define INT_SYSERR BIT(16) 1008c2ecf20Sopenharmony_ci#define INT_GPIO_H(x) (1 << (x == 0 ? 2 : 10)) 1018c2ecf20Sopenharmony_ci#define INT_GPIO_LC(x) (1 << (x == 0 ? 3 : 11)) 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci#define INT_GP0_LCNT 0x0584 1048c2ecf20Sopenharmony_ci#define INT_GP1_LCNT 0x0588 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* Control */ 1078c2ecf20Sopenharmony_ci#define DP0CTL 0x0600 1088c2ecf20Sopenharmony_ci#define VID_MN_GEN BIT(6) /* Auto-generate M/N values */ 1098c2ecf20Sopenharmony_ci#define EF_EN BIT(5) /* Enable Enhanced Framing */ 1108c2ecf20Sopenharmony_ci#define VID_EN BIT(1) /* Video transmission enable */ 1118c2ecf20Sopenharmony_ci#define DP_EN BIT(0) /* Enable DPTX function */ 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* Clocks */ 1148c2ecf20Sopenharmony_ci#define DP0_VIDMNGEN0 0x0610 1158c2ecf20Sopenharmony_ci#define DP0_VIDMNGEN1 0x0614 1168c2ecf20Sopenharmony_ci#define DP0_VMNGENSTATUS 0x0618 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/* Main Channel */ 1198c2ecf20Sopenharmony_ci#define DP0_SECSAMPLE 0x0640 1208c2ecf20Sopenharmony_ci#define DP0_VIDSYNCDELAY 0x0644 1218c2ecf20Sopenharmony_ci#define VID_SYNC_DLY GENMASK(15, 0) 1228c2ecf20Sopenharmony_ci#define THRESH_DLY GENMASK(31, 16) 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci#define DP0_TOTALVAL 0x0648 1258c2ecf20Sopenharmony_ci#define H_TOTAL GENMASK(15, 0) 1268c2ecf20Sopenharmony_ci#define V_TOTAL GENMASK(31, 16) 1278c2ecf20Sopenharmony_ci#define DP0_STARTVAL 0x064c 1288c2ecf20Sopenharmony_ci#define H_START GENMASK(15, 0) 1298c2ecf20Sopenharmony_ci#define V_START GENMASK(31, 16) 1308c2ecf20Sopenharmony_ci#define DP0_ACTIVEVAL 0x0650 1318c2ecf20Sopenharmony_ci#define H_ACT GENMASK(15, 0) 1328c2ecf20Sopenharmony_ci#define V_ACT GENMASK(31, 16) 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci#define DP0_SYNCVAL 0x0654 1358c2ecf20Sopenharmony_ci#define VS_WIDTH GENMASK(30, 16) 1368c2ecf20Sopenharmony_ci#define HS_WIDTH GENMASK(14, 0) 1378c2ecf20Sopenharmony_ci#define SYNCVAL_HS_POL_ACTIVE_LOW (1 << 15) 1388c2ecf20Sopenharmony_ci#define SYNCVAL_VS_POL_ACTIVE_LOW (1 << 31) 1398c2ecf20Sopenharmony_ci#define DP0_MISC 0x0658 1408c2ecf20Sopenharmony_ci#define TU_SIZE_RECOMMENDED (63) /* LSCLK cycles per TU */ 1418c2ecf20Sopenharmony_ci#define MAX_TU_SYMBOL GENMASK(28, 23) 1428c2ecf20Sopenharmony_ci#define TU_SIZE GENMASK(21, 16) 1438c2ecf20Sopenharmony_ci#define BPC_6 (0 << 5) 1448c2ecf20Sopenharmony_ci#define BPC_8 (1 << 5) 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci/* AUX channel */ 1478c2ecf20Sopenharmony_ci#define DP0_AUXCFG0 0x0660 1488c2ecf20Sopenharmony_ci#define DP0_AUXCFG0_BSIZE GENMASK(11, 8) 1498c2ecf20Sopenharmony_ci#define DP0_AUXCFG0_ADDR_ONLY BIT(4) 1508c2ecf20Sopenharmony_ci#define DP0_AUXCFG1 0x0664 1518c2ecf20Sopenharmony_ci#define AUX_RX_FILTER_EN BIT(16) 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci#define DP0_AUXADDR 0x0668 1548c2ecf20Sopenharmony_ci#define DP0_AUXWDATA(i) (0x066c + (i) * 4) 1558c2ecf20Sopenharmony_ci#define DP0_AUXRDATA(i) (0x067c + (i) * 4) 1568c2ecf20Sopenharmony_ci#define DP0_AUXSTATUS 0x068c 1578c2ecf20Sopenharmony_ci#define AUX_BYTES GENMASK(15, 8) 1588c2ecf20Sopenharmony_ci#define AUX_STATUS GENMASK(7, 4) 1598c2ecf20Sopenharmony_ci#define AUX_TIMEOUT BIT(1) 1608c2ecf20Sopenharmony_ci#define AUX_BUSY BIT(0) 1618c2ecf20Sopenharmony_ci#define DP0_AUXI2CADR 0x0698 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci/* Link Training */ 1648c2ecf20Sopenharmony_ci#define DP0_SRCCTRL 0x06a0 1658c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_SCRMBLDIS BIT(13) 1668c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_EN810B BIT(12) 1678c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_NOTP (0 << 8) 1688c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_TP1 (1 << 8) 1698c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_TP2 (2 << 8) 1708c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_LANESKEW BIT(7) 1718c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_SSCG BIT(3) 1728c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_LANES_1 (0 << 2) 1738c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_LANES_2 (1 << 2) 1748c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_BW27 (1 << 1) 1758c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_BW162 (0 << 1) 1768c2ecf20Sopenharmony_ci#define DP0_SRCCTRL_AUTOCORRECT BIT(0) 1778c2ecf20Sopenharmony_ci#define DP0_LTSTAT 0x06d0 1788c2ecf20Sopenharmony_ci#define LT_LOOPDONE BIT(13) 1798c2ecf20Sopenharmony_ci#define LT_STATUS_MASK (0x1f << 8) 1808c2ecf20Sopenharmony_ci#define LT_CHANNEL1_EQ_BITS (DP_CHANNEL_EQ_BITS << 4) 1818c2ecf20Sopenharmony_ci#define LT_INTERLANE_ALIGN_DONE BIT(3) 1828c2ecf20Sopenharmony_ci#define LT_CHANNEL0_EQ_BITS (DP_CHANNEL_EQ_BITS) 1838c2ecf20Sopenharmony_ci#define DP0_SNKLTCHGREQ 0x06d4 1848c2ecf20Sopenharmony_ci#define DP0_LTLOOPCTRL 0x06d8 1858c2ecf20Sopenharmony_ci#define DP0_SNKLTCTRL 0x06e4 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci#define DP1_SRCCTRL 0x07a0 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/* PHY */ 1908c2ecf20Sopenharmony_ci#define DP_PHY_CTRL 0x0800 1918c2ecf20Sopenharmony_ci#define DP_PHY_RST BIT(28) /* DP PHY Global Soft Reset */ 1928c2ecf20Sopenharmony_ci#define BGREN BIT(25) /* AUX PHY BGR Enable */ 1938c2ecf20Sopenharmony_ci#define PWR_SW_EN BIT(24) /* PHY Power Switch Enable */ 1948c2ecf20Sopenharmony_ci#define PHY_M1_RST BIT(12) /* Reset PHY1 Main Channel */ 1958c2ecf20Sopenharmony_ci#define PHY_RDY BIT(16) /* PHY Main Channels Ready */ 1968c2ecf20Sopenharmony_ci#define PHY_M0_RST BIT(8) /* Reset PHY0 Main Channel */ 1978c2ecf20Sopenharmony_ci#define PHY_2LANE BIT(2) /* PHY Enable 2 lanes */ 1988c2ecf20Sopenharmony_ci#define PHY_A0_EN BIT(1) /* PHY Aux Channel0 Enable */ 1998c2ecf20Sopenharmony_ci#define PHY_M0_EN BIT(0) /* PHY Main Channel0 Enable */ 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci/* PLL */ 2028c2ecf20Sopenharmony_ci#define DP0_PLLCTRL 0x0900 2038c2ecf20Sopenharmony_ci#define DP1_PLLCTRL 0x0904 /* not defined in DS */ 2048c2ecf20Sopenharmony_ci#define PXL_PLLCTRL 0x0908 2058c2ecf20Sopenharmony_ci#define PLLUPDATE BIT(2) 2068c2ecf20Sopenharmony_ci#define PLLBYP BIT(1) 2078c2ecf20Sopenharmony_ci#define PLLEN BIT(0) 2088c2ecf20Sopenharmony_ci#define PXL_PLLPARAM 0x0914 2098c2ecf20Sopenharmony_ci#define IN_SEL_REFCLK (0 << 14) 2108c2ecf20Sopenharmony_ci#define SYS_PLLPARAM 0x0918 2118c2ecf20Sopenharmony_ci#define REF_FREQ_38M4 (0 << 8) /* 38.4 MHz */ 2128c2ecf20Sopenharmony_ci#define REF_FREQ_19M2 (1 << 8) /* 19.2 MHz */ 2138c2ecf20Sopenharmony_ci#define REF_FREQ_26M (2 << 8) /* 26 MHz */ 2148c2ecf20Sopenharmony_ci#define REF_FREQ_13M (3 << 8) /* 13 MHz */ 2158c2ecf20Sopenharmony_ci#define SYSCLK_SEL_LSCLK (0 << 4) 2168c2ecf20Sopenharmony_ci#define LSCLK_DIV_1 (0 << 0) 2178c2ecf20Sopenharmony_ci#define LSCLK_DIV_2 (1 << 0) 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/* Test & Debug */ 2208c2ecf20Sopenharmony_ci#define TSTCTL 0x0a00 2218c2ecf20Sopenharmony_ci#define COLOR_R GENMASK(31, 24) 2228c2ecf20Sopenharmony_ci#define COLOR_G GENMASK(23, 16) 2238c2ecf20Sopenharmony_ci#define COLOR_B GENMASK(15, 8) 2248c2ecf20Sopenharmony_ci#define ENI2CFILTER BIT(4) 2258c2ecf20Sopenharmony_ci#define COLOR_BAR_MODE GENMASK(1, 0) 2268c2ecf20Sopenharmony_ci#define COLOR_BAR_MODE_BARS 2 2278c2ecf20Sopenharmony_ci#define PLL_DBG 0x0a04 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic bool tc_test_pattern; 2308c2ecf20Sopenharmony_cimodule_param_named(test, tc_test_pattern, bool, 0644); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistruct tc_edp_link { 2338c2ecf20Sopenharmony_ci u8 dpcd[DP_RECEIVER_CAP_SIZE]; 2348c2ecf20Sopenharmony_ci unsigned int rate; 2358c2ecf20Sopenharmony_ci u8 num_lanes; 2368c2ecf20Sopenharmony_ci u8 assr; 2378c2ecf20Sopenharmony_ci bool scrambler_dis; 2388c2ecf20Sopenharmony_ci bool spread; 2398c2ecf20Sopenharmony_ci}; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistruct tc_data { 2428c2ecf20Sopenharmony_ci struct device *dev; 2438c2ecf20Sopenharmony_ci struct regmap *regmap; 2448c2ecf20Sopenharmony_ci struct drm_dp_aux aux; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci struct drm_bridge bridge; 2478c2ecf20Sopenharmony_ci struct drm_bridge *panel_bridge; 2488c2ecf20Sopenharmony_ci struct drm_connector connector; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* link settings */ 2518c2ecf20Sopenharmony_ci struct tc_edp_link link; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* current mode */ 2548c2ecf20Sopenharmony_ci struct drm_display_mode mode; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci u32 rev; 2578c2ecf20Sopenharmony_ci u8 assr; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci struct gpio_desc *sd_gpio; 2608c2ecf20Sopenharmony_ci struct gpio_desc *reset_gpio; 2618c2ecf20Sopenharmony_ci struct clk *refclk; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* do we have IRQ */ 2648c2ecf20Sopenharmony_ci bool have_irq; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* HPD pin number (0 or 1) or -ENODEV */ 2678c2ecf20Sopenharmony_ci int hpd_pin; 2688c2ecf20Sopenharmony_ci}; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic inline struct tc_data *aux_to_tc(struct drm_dp_aux *a) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci return container_of(a, struct tc_data, aux); 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic inline struct tc_data *bridge_to_tc(struct drm_bridge *b) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci return container_of(b, struct tc_data, bridge); 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic inline struct tc_data *connector_to_tc(struct drm_connector *c) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci return container_of(c, struct tc_data, connector); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic inline int tc_poll_timeout(struct tc_data *tc, unsigned int addr, 2868c2ecf20Sopenharmony_ci unsigned int cond_mask, 2878c2ecf20Sopenharmony_ci unsigned int cond_value, 2888c2ecf20Sopenharmony_ci unsigned long sleep_us, u64 timeout_us) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci unsigned int val; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci return regmap_read_poll_timeout(tc->regmap, addr, val, 2938c2ecf20Sopenharmony_ci (val & cond_mask) == cond_value, 2948c2ecf20Sopenharmony_ci sleep_us, timeout_us); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic int tc_aux_wait_busy(struct tc_data *tc) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci return tc_poll_timeout(tc, DP0_AUXSTATUS, AUX_BUSY, 0, 100, 100000); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic int tc_aux_write_data(struct tc_data *tc, const void *data, 3038c2ecf20Sopenharmony_ci size_t size) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci u32 auxwdata[DP_AUX_MAX_PAYLOAD_BYTES / sizeof(u32)] = { 0 }; 3068c2ecf20Sopenharmony_ci int ret, count = ALIGN(size, sizeof(u32)); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci memcpy(auxwdata, data, size); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci ret = regmap_raw_write(tc->regmap, DP0_AUXWDATA(0), auxwdata, count); 3118c2ecf20Sopenharmony_ci if (ret) 3128c2ecf20Sopenharmony_ci return ret; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return size; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic int tc_aux_read_data(struct tc_data *tc, void *data, size_t size) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci u32 auxrdata[DP_AUX_MAX_PAYLOAD_BYTES / sizeof(u32)]; 3208c2ecf20Sopenharmony_ci int ret, count = ALIGN(size, sizeof(u32)); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci ret = regmap_raw_read(tc->regmap, DP0_AUXRDATA(0), auxrdata, count); 3238c2ecf20Sopenharmony_ci if (ret) 3248c2ecf20Sopenharmony_ci return ret; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci memcpy(data, auxrdata, size); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return size; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic u32 tc_auxcfg0(struct drm_dp_aux_msg *msg, size_t size) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci u32 auxcfg0 = msg->request; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (size) 3368c2ecf20Sopenharmony_ci auxcfg0 |= FIELD_PREP(DP0_AUXCFG0_BSIZE, size - 1); 3378c2ecf20Sopenharmony_ci else 3388c2ecf20Sopenharmony_ci auxcfg0 |= DP0_AUXCFG0_ADDR_ONLY; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci return auxcfg0; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic ssize_t tc_aux_transfer(struct drm_dp_aux *aux, 3448c2ecf20Sopenharmony_ci struct drm_dp_aux_msg *msg) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct tc_data *tc = aux_to_tc(aux); 3478c2ecf20Sopenharmony_ci size_t size = min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES - 1, msg->size); 3488c2ecf20Sopenharmony_ci u8 request = msg->request & ~DP_AUX_I2C_MOT; 3498c2ecf20Sopenharmony_ci u32 auxstatus; 3508c2ecf20Sopenharmony_ci int ret; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci ret = tc_aux_wait_busy(tc); 3538c2ecf20Sopenharmony_ci if (ret) 3548c2ecf20Sopenharmony_ci return ret; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci switch (request) { 3578c2ecf20Sopenharmony_ci case DP_AUX_NATIVE_READ: 3588c2ecf20Sopenharmony_ci case DP_AUX_I2C_READ: 3598c2ecf20Sopenharmony_ci break; 3608c2ecf20Sopenharmony_ci case DP_AUX_NATIVE_WRITE: 3618c2ecf20Sopenharmony_ci case DP_AUX_I2C_WRITE: 3628c2ecf20Sopenharmony_ci if (size) { 3638c2ecf20Sopenharmony_ci ret = tc_aux_write_data(tc, msg->buffer, size); 3648c2ecf20Sopenharmony_ci if (ret < 0) 3658c2ecf20Sopenharmony_ci return ret; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci break; 3688c2ecf20Sopenharmony_ci default: 3698c2ecf20Sopenharmony_ci return -EINVAL; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* Store address */ 3738c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP0_AUXADDR, msg->address); 3748c2ecf20Sopenharmony_ci if (ret) 3758c2ecf20Sopenharmony_ci return ret; 3768c2ecf20Sopenharmony_ci /* Start transfer */ 3778c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP0_AUXCFG0, tc_auxcfg0(msg, size)); 3788c2ecf20Sopenharmony_ci if (ret) 3798c2ecf20Sopenharmony_ci return ret; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci ret = tc_aux_wait_busy(tc); 3828c2ecf20Sopenharmony_ci if (ret) 3838c2ecf20Sopenharmony_ci return ret; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci ret = regmap_read(tc->regmap, DP0_AUXSTATUS, &auxstatus); 3868c2ecf20Sopenharmony_ci if (ret) 3878c2ecf20Sopenharmony_ci return ret; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (auxstatus & AUX_TIMEOUT) 3908c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3918c2ecf20Sopenharmony_ci /* 3928c2ecf20Sopenharmony_ci * For some reason address-only DP_AUX_I2C_WRITE (MOT), still 3938c2ecf20Sopenharmony_ci * reports 1 byte transferred in its status. To deal we that 3948c2ecf20Sopenharmony_ci * we ignore aux_bytes field if we know that this was an 3958c2ecf20Sopenharmony_ci * address-only transfer 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_ci if (size) 3988c2ecf20Sopenharmony_ci size = FIELD_GET(AUX_BYTES, auxstatus); 3998c2ecf20Sopenharmony_ci msg->reply = FIELD_GET(AUX_STATUS, auxstatus); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci switch (request) { 4028c2ecf20Sopenharmony_ci case DP_AUX_NATIVE_READ: 4038c2ecf20Sopenharmony_ci case DP_AUX_I2C_READ: 4048c2ecf20Sopenharmony_ci if (size) 4058c2ecf20Sopenharmony_ci return tc_aux_read_data(tc, msg->buffer, size); 4068c2ecf20Sopenharmony_ci break; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci return size; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic const char * const training_pattern1_errors[] = { 4138c2ecf20Sopenharmony_ci "No errors", 4148c2ecf20Sopenharmony_ci "Aux write error", 4158c2ecf20Sopenharmony_ci "Aux read error", 4168c2ecf20Sopenharmony_ci "Max voltage reached error", 4178c2ecf20Sopenharmony_ci "Loop counter expired error", 4188c2ecf20Sopenharmony_ci "res", "res", "res" 4198c2ecf20Sopenharmony_ci}; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic const char * const training_pattern2_errors[] = { 4228c2ecf20Sopenharmony_ci "No errors", 4238c2ecf20Sopenharmony_ci "Aux write error", 4248c2ecf20Sopenharmony_ci "Aux read error", 4258c2ecf20Sopenharmony_ci "Clock recovery failed error", 4268c2ecf20Sopenharmony_ci "Loop counter expired error", 4278c2ecf20Sopenharmony_ci "res", "res", "res" 4288c2ecf20Sopenharmony_ci}; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic u32 tc_srcctrl(struct tc_data *tc) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci /* 4338c2ecf20Sopenharmony_ci * No training pattern, skew lane 1 data by two LSCLK cycles with 4348c2ecf20Sopenharmony_ci * respect to lane 0 data, AutoCorrect Mode = 0 4358c2ecf20Sopenharmony_ci */ 4368c2ecf20Sopenharmony_ci u32 reg = DP0_SRCCTRL_NOTP | DP0_SRCCTRL_LANESKEW | DP0_SRCCTRL_EN810B; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (tc->link.scrambler_dis) 4398c2ecf20Sopenharmony_ci reg |= DP0_SRCCTRL_SCRMBLDIS; /* Scrambler Disabled */ 4408c2ecf20Sopenharmony_ci if (tc->link.spread) 4418c2ecf20Sopenharmony_ci reg |= DP0_SRCCTRL_SSCG; /* Spread Spectrum Enable */ 4428c2ecf20Sopenharmony_ci if (tc->link.num_lanes == 2) 4438c2ecf20Sopenharmony_ci reg |= DP0_SRCCTRL_LANES_2; /* Two Main Channel Lanes */ 4448c2ecf20Sopenharmony_ci if (tc->link.rate != 162000) 4458c2ecf20Sopenharmony_ci reg |= DP0_SRCCTRL_BW27; /* 2.7 Gbps link */ 4468c2ecf20Sopenharmony_ci return reg; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic int tc_pllupdate(struct tc_data *tc, unsigned int pllctrl) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci int ret; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, pllctrl, PLLUPDATE | PLLEN); 4548c2ecf20Sopenharmony_ci if (ret) 4558c2ecf20Sopenharmony_ci return ret; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* Wait for PLL to lock: up to 2.09 ms, depending on refclk */ 4588c2ecf20Sopenharmony_ci usleep_range(3000, 6000); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci return 0; 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic int tc_pxl_pll_en(struct tc_data *tc, u32 refclk, u32 pixelclock) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci int ret; 4668c2ecf20Sopenharmony_ci int i_pre, best_pre = 1; 4678c2ecf20Sopenharmony_ci int i_post, best_post = 1; 4688c2ecf20Sopenharmony_ci int div, best_div = 1; 4698c2ecf20Sopenharmony_ci int mul, best_mul = 1; 4708c2ecf20Sopenharmony_ci int delta, best_delta; 4718c2ecf20Sopenharmony_ci int ext_div[] = {1, 2, 3, 5, 7}; 4728c2ecf20Sopenharmony_ci int best_pixelclock = 0; 4738c2ecf20Sopenharmony_ci int vco_hi = 0; 4748c2ecf20Sopenharmony_ci u32 pxl_pllparam; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci dev_dbg(tc->dev, "PLL: requested %d pixelclock, ref %d\n", pixelclock, 4778c2ecf20Sopenharmony_ci refclk); 4788c2ecf20Sopenharmony_ci best_delta = pixelclock; 4798c2ecf20Sopenharmony_ci /* Loop over all possible ext_divs, skipping invalid configurations */ 4808c2ecf20Sopenharmony_ci for (i_pre = 0; i_pre < ARRAY_SIZE(ext_div); i_pre++) { 4818c2ecf20Sopenharmony_ci /* 4828c2ecf20Sopenharmony_ci * refclk / ext_pre_div should be in the 1 to 200 MHz range. 4838c2ecf20Sopenharmony_ci * We don't allow any refclk > 200 MHz, only check lower bounds. 4848c2ecf20Sopenharmony_ci */ 4858c2ecf20Sopenharmony_ci if (refclk / ext_div[i_pre] < 1000000) 4868c2ecf20Sopenharmony_ci continue; 4878c2ecf20Sopenharmony_ci for (i_post = 0; i_post < ARRAY_SIZE(ext_div); i_post++) { 4888c2ecf20Sopenharmony_ci for (div = 1; div <= 16; div++) { 4898c2ecf20Sopenharmony_ci u32 clk; 4908c2ecf20Sopenharmony_ci u64 tmp; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci tmp = pixelclock * ext_div[i_pre] * 4938c2ecf20Sopenharmony_ci ext_div[i_post] * div; 4948c2ecf20Sopenharmony_ci do_div(tmp, refclk); 4958c2ecf20Sopenharmony_ci mul = tmp; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci /* Check limits */ 4988c2ecf20Sopenharmony_ci if ((mul < 1) || (mul > 128)) 4998c2ecf20Sopenharmony_ci continue; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci clk = (refclk / ext_div[i_pre] / div) * mul; 5028c2ecf20Sopenharmony_ci /* 5038c2ecf20Sopenharmony_ci * refclk * mul / (ext_pre_div * pre_div) 5048c2ecf20Sopenharmony_ci * should be in the 150 to 650 MHz range 5058c2ecf20Sopenharmony_ci */ 5068c2ecf20Sopenharmony_ci if ((clk > 650000000) || (clk < 150000000)) 5078c2ecf20Sopenharmony_ci continue; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci clk = clk / ext_div[i_post]; 5108c2ecf20Sopenharmony_ci delta = clk - pixelclock; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (abs(delta) < abs(best_delta)) { 5138c2ecf20Sopenharmony_ci best_pre = i_pre; 5148c2ecf20Sopenharmony_ci best_post = i_post; 5158c2ecf20Sopenharmony_ci best_div = div; 5168c2ecf20Sopenharmony_ci best_mul = mul; 5178c2ecf20Sopenharmony_ci best_delta = delta; 5188c2ecf20Sopenharmony_ci best_pixelclock = clk; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci if (best_pixelclock == 0) { 5248c2ecf20Sopenharmony_ci dev_err(tc->dev, "Failed to calc clock for %d pixelclock\n", 5258c2ecf20Sopenharmony_ci pixelclock); 5268c2ecf20Sopenharmony_ci return -EINVAL; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci dev_dbg(tc->dev, "PLL: got %d, delta %d\n", best_pixelclock, 5308c2ecf20Sopenharmony_ci best_delta); 5318c2ecf20Sopenharmony_ci dev_dbg(tc->dev, "PLL: %d / %d / %d * %d / %d\n", refclk, 5328c2ecf20Sopenharmony_ci ext_div[best_pre], best_div, best_mul, ext_div[best_post]); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* if VCO >= 300 MHz */ 5358c2ecf20Sopenharmony_ci if (refclk / ext_div[best_pre] / best_div * best_mul >= 300000000) 5368c2ecf20Sopenharmony_ci vco_hi = 1; 5378c2ecf20Sopenharmony_ci /* see DS */ 5388c2ecf20Sopenharmony_ci if (best_div == 16) 5398c2ecf20Sopenharmony_ci best_div = 0; 5408c2ecf20Sopenharmony_ci if (best_mul == 128) 5418c2ecf20Sopenharmony_ci best_mul = 0; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* Power up PLL and switch to bypass */ 5448c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, PXL_PLLCTRL, PLLBYP | PLLEN); 5458c2ecf20Sopenharmony_ci if (ret) 5468c2ecf20Sopenharmony_ci return ret; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci pxl_pllparam = vco_hi << 24; /* For PLL VCO >= 300 MHz = 1 */ 5498c2ecf20Sopenharmony_ci pxl_pllparam |= ext_div[best_pre] << 20; /* External Pre-divider */ 5508c2ecf20Sopenharmony_ci pxl_pllparam |= ext_div[best_post] << 16; /* External Post-divider */ 5518c2ecf20Sopenharmony_ci pxl_pllparam |= IN_SEL_REFCLK; /* Use RefClk as PLL input */ 5528c2ecf20Sopenharmony_ci pxl_pllparam |= best_div << 8; /* Divider for PLL RefClk */ 5538c2ecf20Sopenharmony_ci pxl_pllparam |= best_mul; /* Multiplier for PLL */ 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, PXL_PLLPARAM, pxl_pllparam); 5568c2ecf20Sopenharmony_ci if (ret) 5578c2ecf20Sopenharmony_ci return ret; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* Force PLL parameter update and disable bypass */ 5608c2ecf20Sopenharmony_ci return tc_pllupdate(tc, PXL_PLLCTRL); 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic int tc_pxl_pll_dis(struct tc_data *tc) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci /* Enable PLL bypass, power down PLL */ 5668c2ecf20Sopenharmony_ci return regmap_write(tc->regmap, PXL_PLLCTRL, PLLBYP); 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic int tc_stream_clock_calc(struct tc_data *tc) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci /* 5728c2ecf20Sopenharmony_ci * If the Stream clock and Link Symbol clock are 5738c2ecf20Sopenharmony_ci * asynchronous with each other, the value of M changes over 5748c2ecf20Sopenharmony_ci * time. This way of generating link clock and stream 5758c2ecf20Sopenharmony_ci * clock is called Asynchronous Clock mode. The value M 5768c2ecf20Sopenharmony_ci * must change while the value N stays constant. The 5778c2ecf20Sopenharmony_ci * value of N in this Asynchronous Clock mode must be set 5788c2ecf20Sopenharmony_ci * to 2^15 or 32,768. 5798c2ecf20Sopenharmony_ci * 5808c2ecf20Sopenharmony_ci * LSCLK = 1/10 of high speed link clock 5818c2ecf20Sopenharmony_ci * 5828c2ecf20Sopenharmony_ci * f_STRMCLK = M/N * f_LSCLK 5838c2ecf20Sopenharmony_ci * M/N = f_STRMCLK / f_LSCLK 5848c2ecf20Sopenharmony_ci * 5858c2ecf20Sopenharmony_ci */ 5868c2ecf20Sopenharmony_ci return regmap_write(tc->regmap, DP0_VIDMNGEN1, 32768); 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic int tc_set_syspllparam(struct tc_data *tc) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci unsigned long rate; 5928c2ecf20Sopenharmony_ci u32 pllparam = SYSCLK_SEL_LSCLK | LSCLK_DIV_2; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci rate = clk_get_rate(tc->refclk); 5958c2ecf20Sopenharmony_ci switch (rate) { 5968c2ecf20Sopenharmony_ci case 38400000: 5978c2ecf20Sopenharmony_ci pllparam |= REF_FREQ_38M4; 5988c2ecf20Sopenharmony_ci break; 5998c2ecf20Sopenharmony_ci case 26000000: 6008c2ecf20Sopenharmony_ci pllparam |= REF_FREQ_26M; 6018c2ecf20Sopenharmony_ci break; 6028c2ecf20Sopenharmony_ci case 19200000: 6038c2ecf20Sopenharmony_ci pllparam |= REF_FREQ_19M2; 6048c2ecf20Sopenharmony_ci break; 6058c2ecf20Sopenharmony_ci case 13000000: 6068c2ecf20Sopenharmony_ci pllparam |= REF_FREQ_13M; 6078c2ecf20Sopenharmony_ci break; 6088c2ecf20Sopenharmony_ci default: 6098c2ecf20Sopenharmony_ci dev_err(tc->dev, "Invalid refclk rate: %lu Hz\n", rate); 6108c2ecf20Sopenharmony_ci return -EINVAL; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci return regmap_write(tc->regmap, SYS_PLLPARAM, pllparam); 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_cistatic int tc_aux_link_setup(struct tc_data *tc) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci int ret; 6198c2ecf20Sopenharmony_ci u32 dp0_auxcfg1; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci /* Setup DP-PHY / PLL */ 6228c2ecf20Sopenharmony_ci ret = tc_set_syspllparam(tc); 6238c2ecf20Sopenharmony_ci if (ret) 6248c2ecf20Sopenharmony_ci goto err; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP_PHY_CTRL, 6278c2ecf20Sopenharmony_ci BGREN | PWR_SW_EN | PHY_A0_EN); 6288c2ecf20Sopenharmony_ci if (ret) 6298c2ecf20Sopenharmony_ci goto err; 6308c2ecf20Sopenharmony_ci /* 6318c2ecf20Sopenharmony_ci * Initially PLLs are in bypass. Force PLL parameter update, 6328c2ecf20Sopenharmony_ci * disable PLL bypass, enable PLL 6338c2ecf20Sopenharmony_ci */ 6348c2ecf20Sopenharmony_ci ret = tc_pllupdate(tc, DP0_PLLCTRL); 6358c2ecf20Sopenharmony_ci if (ret) 6368c2ecf20Sopenharmony_ci goto err; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci ret = tc_pllupdate(tc, DP1_PLLCTRL); 6398c2ecf20Sopenharmony_ci if (ret) 6408c2ecf20Sopenharmony_ci goto err; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci ret = tc_poll_timeout(tc, DP_PHY_CTRL, PHY_RDY, PHY_RDY, 100, 100000); 6438c2ecf20Sopenharmony_ci if (ret == -ETIMEDOUT) { 6448c2ecf20Sopenharmony_ci dev_err(tc->dev, "Timeout waiting for PHY to become ready"); 6458c2ecf20Sopenharmony_ci return ret; 6468c2ecf20Sopenharmony_ci } else if (ret) { 6478c2ecf20Sopenharmony_ci goto err; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci /* Setup AUX link */ 6518c2ecf20Sopenharmony_ci dp0_auxcfg1 = AUX_RX_FILTER_EN; 6528c2ecf20Sopenharmony_ci dp0_auxcfg1 |= 0x06 << 8; /* Aux Bit Period Calculator Threshold */ 6538c2ecf20Sopenharmony_ci dp0_auxcfg1 |= 0x3f << 0; /* Aux Response Timeout Timer */ 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP0_AUXCFG1, dp0_auxcfg1); 6568c2ecf20Sopenharmony_ci if (ret) 6578c2ecf20Sopenharmony_ci goto err; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci return 0; 6608c2ecf20Sopenharmony_cierr: 6618c2ecf20Sopenharmony_ci dev_err(tc->dev, "tc_aux_link_setup failed: %d\n", ret); 6628c2ecf20Sopenharmony_ci return ret; 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_cistatic int tc_get_display_props(struct tc_data *tc) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci u8 revision, num_lanes; 6688c2ecf20Sopenharmony_ci unsigned int rate; 6698c2ecf20Sopenharmony_ci int ret; 6708c2ecf20Sopenharmony_ci u8 reg; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci /* Read DP Rx Link Capability */ 6738c2ecf20Sopenharmony_ci ret = drm_dp_dpcd_read(&tc->aux, DP_DPCD_REV, tc->link.dpcd, 6748c2ecf20Sopenharmony_ci DP_RECEIVER_CAP_SIZE); 6758c2ecf20Sopenharmony_ci if (ret < 0) 6768c2ecf20Sopenharmony_ci goto err_dpcd_read; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci revision = tc->link.dpcd[DP_DPCD_REV]; 6798c2ecf20Sopenharmony_ci rate = drm_dp_max_link_rate(tc->link.dpcd); 6808c2ecf20Sopenharmony_ci num_lanes = drm_dp_max_lane_count(tc->link.dpcd); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci if (rate != 162000 && rate != 270000) { 6838c2ecf20Sopenharmony_ci dev_dbg(tc->dev, "Falling to 2.7 Gbps rate\n"); 6848c2ecf20Sopenharmony_ci rate = 270000; 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci tc->link.rate = rate; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci if (num_lanes > 2) { 6908c2ecf20Sopenharmony_ci dev_dbg(tc->dev, "Falling to 2 lanes\n"); 6918c2ecf20Sopenharmony_ci num_lanes = 2; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci tc->link.num_lanes = num_lanes; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci ret = drm_dp_dpcd_readb(&tc->aux, DP_MAX_DOWNSPREAD, ®); 6978c2ecf20Sopenharmony_ci if (ret < 0) 6988c2ecf20Sopenharmony_ci goto err_dpcd_read; 6998c2ecf20Sopenharmony_ci tc->link.spread = reg & DP_MAX_DOWNSPREAD_0_5; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci ret = drm_dp_dpcd_readb(&tc->aux, DP_MAIN_LINK_CHANNEL_CODING, ®); 7028c2ecf20Sopenharmony_ci if (ret < 0) 7038c2ecf20Sopenharmony_ci goto err_dpcd_read; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci tc->link.scrambler_dis = false; 7068c2ecf20Sopenharmony_ci /* read assr */ 7078c2ecf20Sopenharmony_ci ret = drm_dp_dpcd_readb(&tc->aux, DP_EDP_CONFIGURATION_SET, ®); 7088c2ecf20Sopenharmony_ci if (ret < 0) 7098c2ecf20Sopenharmony_ci goto err_dpcd_read; 7108c2ecf20Sopenharmony_ci tc->link.assr = reg & DP_ALTERNATE_SCRAMBLER_RESET_ENABLE; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci dev_dbg(tc->dev, "DPCD rev: %d.%d, rate: %s, lanes: %d, framing: %s\n", 7138c2ecf20Sopenharmony_ci revision >> 4, revision & 0x0f, 7148c2ecf20Sopenharmony_ci (tc->link.rate == 162000) ? "1.62Gbps" : "2.7Gbps", 7158c2ecf20Sopenharmony_ci tc->link.num_lanes, 7168c2ecf20Sopenharmony_ci drm_dp_enhanced_frame_cap(tc->link.dpcd) ? 7178c2ecf20Sopenharmony_ci "enhanced" : "default"); 7188c2ecf20Sopenharmony_ci dev_dbg(tc->dev, "Downspread: %s, scrambler: %s\n", 7198c2ecf20Sopenharmony_ci tc->link.spread ? "0.5%" : "0.0%", 7208c2ecf20Sopenharmony_ci tc->link.scrambler_dis ? "disabled" : "enabled"); 7218c2ecf20Sopenharmony_ci dev_dbg(tc->dev, "Display ASSR: %d, TC358767 ASSR: %d\n", 7228c2ecf20Sopenharmony_ci tc->link.assr, tc->assr); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci return 0; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cierr_dpcd_read: 7278c2ecf20Sopenharmony_ci dev_err(tc->dev, "failed to read DPCD: %d\n", ret); 7288c2ecf20Sopenharmony_ci return ret; 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cistatic int tc_set_video_mode(struct tc_data *tc, 7328c2ecf20Sopenharmony_ci const struct drm_display_mode *mode) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci int ret; 7358c2ecf20Sopenharmony_ci int vid_sync_dly; 7368c2ecf20Sopenharmony_ci int max_tu_symbol; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci int left_margin = mode->htotal - mode->hsync_end; 7398c2ecf20Sopenharmony_ci int right_margin = mode->hsync_start - mode->hdisplay; 7408c2ecf20Sopenharmony_ci int hsync_len = mode->hsync_end - mode->hsync_start; 7418c2ecf20Sopenharmony_ci int upper_margin = mode->vtotal - mode->vsync_end; 7428c2ecf20Sopenharmony_ci int lower_margin = mode->vsync_start - mode->vdisplay; 7438c2ecf20Sopenharmony_ci int vsync_len = mode->vsync_end - mode->vsync_start; 7448c2ecf20Sopenharmony_ci u32 dp0_syncval; 7458c2ecf20Sopenharmony_ci u32 bits_per_pixel = 24; 7468c2ecf20Sopenharmony_ci u32 in_bw, out_bw; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci /* 7498c2ecf20Sopenharmony_ci * Recommended maximum number of symbols transferred in a transfer unit: 7508c2ecf20Sopenharmony_ci * DIV_ROUND_UP((input active video bandwidth in bytes) * tu_size, 7518c2ecf20Sopenharmony_ci * (output active video bandwidth in bytes)) 7528c2ecf20Sopenharmony_ci * Must be less than tu_size. 7538c2ecf20Sopenharmony_ci */ 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci in_bw = mode->clock * bits_per_pixel / 8; 7568c2ecf20Sopenharmony_ci out_bw = tc->link.num_lanes * tc->link.rate; 7578c2ecf20Sopenharmony_ci max_tu_symbol = DIV_ROUND_UP(in_bw * TU_SIZE_RECOMMENDED, out_bw); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci dev_dbg(tc->dev, "set mode %dx%d\n", 7608c2ecf20Sopenharmony_ci mode->hdisplay, mode->vdisplay); 7618c2ecf20Sopenharmony_ci dev_dbg(tc->dev, "H margin %d,%d sync %d\n", 7628c2ecf20Sopenharmony_ci left_margin, right_margin, hsync_len); 7638c2ecf20Sopenharmony_ci dev_dbg(tc->dev, "V margin %d,%d sync %d\n", 7648c2ecf20Sopenharmony_ci upper_margin, lower_margin, vsync_len); 7658c2ecf20Sopenharmony_ci dev_dbg(tc->dev, "total: %dx%d\n", mode->htotal, mode->vtotal); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci /* 7698c2ecf20Sopenharmony_ci * LCD Ctl Frame Size 7708c2ecf20Sopenharmony_ci * datasheet is not clear of vsdelay in case of DPI 7718c2ecf20Sopenharmony_ci * assume we do not need any delay when DPI is a source of 7728c2ecf20Sopenharmony_ci * sync signals 7738c2ecf20Sopenharmony_ci */ 7748c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, VPCTRL0, 7758c2ecf20Sopenharmony_ci FIELD_PREP(VSDELAY, 0) | 7768c2ecf20Sopenharmony_ci OPXLFMT_RGB888 | FRMSYNC_DISABLED | MSF_DISABLED); 7778c2ecf20Sopenharmony_ci if (ret) 7788c2ecf20Sopenharmony_ci return ret; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, HTIM01, 7818c2ecf20Sopenharmony_ci FIELD_PREP(HBPR, ALIGN(left_margin, 2)) | 7828c2ecf20Sopenharmony_ci FIELD_PREP(HPW, ALIGN(hsync_len, 2))); 7838c2ecf20Sopenharmony_ci if (ret) 7848c2ecf20Sopenharmony_ci return ret; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, HTIM02, 7878c2ecf20Sopenharmony_ci FIELD_PREP(HDISPR, ALIGN(mode->hdisplay, 2)) | 7888c2ecf20Sopenharmony_ci FIELD_PREP(HFPR, ALIGN(right_margin, 2))); 7898c2ecf20Sopenharmony_ci if (ret) 7908c2ecf20Sopenharmony_ci return ret; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, VTIM01, 7938c2ecf20Sopenharmony_ci FIELD_PREP(VBPR, upper_margin) | 7948c2ecf20Sopenharmony_ci FIELD_PREP(VSPR, vsync_len)); 7958c2ecf20Sopenharmony_ci if (ret) 7968c2ecf20Sopenharmony_ci return ret; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, VTIM02, 7998c2ecf20Sopenharmony_ci FIELD_PREP(VFPR, lower_margin) | 8008c2ecf20Sopenharmony_ci FIELD_PREP(VDISPR, mode->vdisplay)); 8018c2ecf20Sopenharmony_ci if (ret) 8028c2ecf20Sopenharmony_ci return ret; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, VFUEN0, VFUEN); /* update settings */ 8058c2ecf20Sopenharmony_ci if (ret) 8068c2ecf20Sopenharmony_ci return ret; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci /* Test pattern settings */ 8098c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, TSTCTL, 8108c2ecf20Sopenharmony_ci FIELD_PREP(COLOR_R, 120) | 8118c2ecf20Sopenharmony_ci FIELD_PREP(COLOR_G, 20) | 8128c2ecf20Sopenharmony_ci FIELD_PREP(COLOR_B, 99) | 8138c2ecf20Sopenharmony_ci ENI2CFILTER | 8148c2ecf20Sopenharmony_ci FIELD_PREP(COLOR_BAR_MODE, COLOR_BAR_MODE_BARS)); 8158c2ecf20Sopenharmony_ci if (ret) 8168c2ecf20Sopenharmony_ci return ret; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci /* DP Main Stream Attributes */ 8198c2ecf20Sopenharmony_ci vid_sync_dly = hsync_len + left_margin + mode->hdisplay; 8208c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP0_VIDSYNCDELAY, 8218c2ecf20Sopenharmony_ci FIELD_PREP(THRESH_DLY, max_tu_symbol) | 8228c2ecf20Sopenharmony_ci FIELD_PREP(VID_SYNC_DLY, vid_sync_dly)); 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP0_TOTALVAL, 8258c2ecf20Sopenharmony_ci FIELD_PREP(H_TOTAL, mode->htotal) | 8268c2ecf20Sopenharmony_ci FIELD_PREP(V_TOTAL, mode->vtotal)); 8278c2ecf20Sopenharmony_ci if (ret) 8288c2ecf20Sopenharmony_ci return ret; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP0_STARTVAL, 8318c2ecf20Sopenharmony_ci FIELD_PREP(H_START, left_margin + hsync_len) | 8328c2ecf20Sopenharmony_ci FIELD_PREP(V_START, upper_margin + vsync_len)); 8338c2ecf20Sopenharmony_ci if (ret) 8348c2ecf20Sopenharmony_ci return ret; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP0_ACTIVEVAL, 8378c2ecf20Sopenharmony_ci FIELD_PREP(V_ACT, mode->vdisplay) | 8388c2ecf20Sopenharmony_ci FIELD_PREP(H_ACT, mode->hdisplay)); 8398c2ecf20Sopenharmony_ci if (ret) 8408c2ecf20Sopenharmony_ci return ret; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci dp0_syncval = FIELD_PREP(VS_WIDTH, vsync_len) | 8438c2ecf20Sopenharmony_ci FIELD_PREP(HS_WIDTH, hsync_len); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_NVSYNC) 8468c2ecf20Sopenharmony_ci dp0_syncval |= SYNCVAL_VS_POL_ACTIVE_LOW; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_NHSYNC) 8498c2ecf20Sopenharmony_ci dp0_syncval |= SYNCVAL_HS_POL_ACTIVE_LOW; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP0_SYNCVAL, dp0_syncval); 8528c2ecf20Sopenharmony_ci if (ret) 8538c2ecf20Sopenharmony_ci return ret; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DPIPXLFMT, 8568c2ecf20Sopenharmony_ci VS_POL_ACTIVE_LOW | HS_POL_ACTIVE_LOW | 8578c2ecf20Sopenharmony_ci DE_POL_ACTIVE_HIGH | SUB_CFG_TYPE_CONFIG1 | 8588c2ecf20Sopenharmony_ci DPI_BPP_RGB888); 8598c2ecf20Sopenharmony_ci if (ret) 8608c2ecf20Sopenharmony_ci return ret; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP0_MISC, 8638c2ecf20Sopenharmony_ci FIELD_PREP(MAX_TU_SYMBOL, max_tu_symbol) | 8648c2ecf20Sopenharmony_ci FIELD_PREP(TU_SIZE, TU_SIZE_RECOMMENDED) | 8658c2ecf20Sopenharmony_ci BPC_8); 8668c2ecf20Sopenharmony_ci if (ret) 8678c2ecf20Sopenharmony_ci return ret; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci return 0; 8708c2ecf20Sopenharmony_ci} 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_cistatic int tc_wait_link_training(struct tc_data *tc) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci u32 value; 8758c2ecf20Sopenharmony_ci int ret; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci ret = tc_poll_timeout(tc, DP0_LTSTAT, LT_LOOPDONE, 8788c2ecf20Sopenharmony_ci LT_LOOPDONE, 500, 100000); 8798c2ecf20Sopenharmony_ci if (ret) { 8808c2ecf20Sopenharmony_ci dev_err(tc->dev, "Link training timeout waiting for LT_LOOPDONE!\n"); 8818c2ecf20Sopenharmony_ci return ret; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci ret = regmap_read(tc->regmap, DP0_LTSTAT, &value); 8858c2ecf20Sopenharmony_ci if (ret) 8868c2ecf20Sopenharmony_ci return ret; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci return (value >> 8) & 0x7; 8898c2ecf20Sopenharmony_ci} 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_cistatic int tc_main_link_enable(struct tc_data *tc) 8928c2ecf20Sopenharmony_ci{ 8938c2ecf20Sopenharmony_ci struct drm_dp_aux *aux = &tc->aux; 8948c2ecf20Sopenharmony_ci struct device *dev = tc->dev; 8958c2ecf20Sopenharmony_ci u32 dp_phy_ctrl; 8968c2ecf20Sopenharmony_ci u32 value; 8978c2ecf20Sopenharmony_ci int ret; 8988c2ecf20Sopenharmony_ci u8 tmp[DP_LINK_STATUS_SIZE]; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci dev_dbg(tc->dev, "link enable\n"); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci ret = regmap_read(tc->regmap, DP0CTL, &value); 9038c2ecf20Sopenharmony_ci if (ret) 9048c2ecf20Sopenharmony_ci return ret; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci if (WARN_ON(value & DP_EN)) { 9078c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP0CTL, 0); 9088c2ecf20Sopenharmony_ci if (ret) 9098c2ecf20Sopenharmony_ci return ret; 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP0_SRCCTRL, tc_srcctrl(tc)); 9138c2ecf20Sopenharmony_ci if (ret) 9148c2ecf20Sopenharmony_ci return ret; 9158c2ecf20Sopenharmony_ci /* SSCG and BW27 on DP1 must be set to the same as on DP0 */ 9168c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP1_SRCCTRL, 9178c2ecf20Sopenharmony_ci (tc->link.spread ? DP0_SRCCTRL_SSCG : 0) | 9188c2ecf20Sopenharmony_ci ((tc->link.rate != 162000) ? DP0_SRCCTRL_BW27 : 0)); 9198c2ecf20Sopenharmony_ci if (ret) 9208c2ecf20Sopenharmony_ci return ret; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci ret = tc_set_syspllparam(tc); 9238c2ecf20Sopenharmony_ci if (ret) 9248c2ecf20Sopenharmony_ci return ret; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci /* Setup Main Link */ 9278c2ecf20Sopenharmony_ci dp_phy_ctrl = BGREN | PWR_SW_EN | PHY_A0_EN | PHY_M0_EN; 9288c2ecf20Sopenharmony_ci if (tc->link.num_lanes == 2) 9298c2ecf20Sopenharmony_ci dp_phy_ctrl |= PHY_2LANE; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP_PHY_CTRL, dp_phy_ctrl); 9328c2ecf20Sopenharmony_ci if (ret) 9338c2ecf20Sopenharmony_ci return ret; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci /* PLL setup */ 9368c2ecf20Sopenharmony_ci ret = tc_pllupdate(tc, DP0_PLLCTRL); 9378c2ecf20Sopenharmony_ci if (ret) 9388c2ecf20Sopenharmony_ci return ret; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci ret = tc_pllupdate(tc, DP1_PLLCTRL); 9418c2ecf20Sopenharmony_ci if (ret) 9428c2ecf20Sopenharmony_ci return ret; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci /* Reset/Enable Main Links */ 9458c2ecf20Sopenharmony_ci dp_phy_ctrl |= DP_PHY_RST | PHY_M1_RST | PHY_M0_RST; 9468c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP_PHY_CTRL, dp_phy_ctrl); 9478c2ecf20Sopenharmony_ci usleep_range(100, 200); 9488c2ecf20Sopenharmony_ci dp_phy_ctrl &= ~(DP_PHY_RST | PHY_M1_RST | PHY_M0_RST); 9498c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP_PHY_CTRL, dp_phy_ctrl); 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci ret = tc_poll_timeout(tc, DP_PHY_CTRL, PHY_RDY, PHY_RDY, 500, 100000); 9528c2ecf20Sopenharmony_ci if (ret) { 9538c2ecf20Sopenharmony_ci dev_err(dev, "timeout waiting for phy become ready"); 9548c2ecf20Sopenharmony_ci return ret; 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci /* Set misc: 8 bits per color */ 9588c2ecf20Sopenharmony_ci ret = regmap_update_bits(tc->regmap, DP0_MISC, BPC_8, BPC_8); 9598c2ecf20Sopenharmony_ci if (ret) 9608c2ecf20Sopenharmony_ci return ret; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci /* 9638c2ecf20Sopenharmony_ci * ASSR mode 9648c2ecf20Sopenharmony_ci * on TC358767 side ASSR configured through strap pin 9658c2ecf20Sopenharmony_ci * seems there is no way to change this setting from SW 9668c2ecf20Sopenharmony_ci * 9678c2ecf20Sopenharmony_ci * check is tc configured for same mode 9688c2ecf20Sopenharmony_ci */ 9698c2ecf20Sopenharmony_ci if (tc->assr != tc->link.assr) { 9708c2ecf20Sopenharmony_ci dev_dbg(dev, "Trying to set display to ASSR: %d\n", 9718c2ecf20Sopenharmony_ci tc->assr); 9728c2ecf20Sopenharmony_ci /* try to set ASSR on display side */ 9738c2ecf20Sopenharmony_ci tmp[0] = tc->assr; 9748c2ecf20Sopenharmony_ci ret = drm_dp_dpcd_writeb(aux, DP_EDP_CONFIGURATION_SET, tmp[0]); 9758c2ecf20Sopenharmony_ci if (ret < 0) 9768c2ecf20Sopenharmony_ci goto err_dpcd_read; 9778c2ecf20Sopenharmony_ci /* read back */ 9788c2ecf20Sopenharmony_ci ret = drm_dp_dpcd_readb(aux, DP_EDP_CONFIGURATION_SET, tmp); 9798c2ecf20Sopenharmony_ci if (ret < 0) 9808c2ecf20Sopenharmony_ci goto err_dpcd_read; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci if (tmp[0] != tc->assr) { 9838c2ecf20Sopenharmony_ci dev_dbg(dev, "Failed to switch display ASSR to %d, falling back to unscrambled mode\n", 9848c2ecf20Sopenharmony_ci tc->assr); 9858c2ecf20Sopenharmony_ci /* trying with disabled scrambler */ 9868c2ecf20Sopenharmony_ci tc->link.scrambler_dis = true; 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci } 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci /* Setup Link & DPRx Config for Training */ 9918c2ecf20Sopenharmony_ci tmp[0] = drm_dp_link_rate_to_bw_code(tc->link.rate); 9928c2ecf20Sopenharmony_ci tmp[1] = tc->link.num_lanes; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci if (drm_dp_enhanced_frame_cap(tc->link.dpcd)) 9958c2ecf20Sopenharmony_ci tmp[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci ret = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, tmp, 2); 9988c2ecf20Sopenharmony_ci if (ret < 0) 9998c2ecf20Sopenharmony_ci goto err_dpcd_write; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci /* DOWNSPREAD_CTRL */ 10028c2ecf20Sopenharmony_ci tmp[0] = tc->link.spread ? DP_SPREAD_AMP_0_5 : 0x00; 10038c2ecf20Sopenharmony_ci /* MAIN_LINK_CHANNEL_CODING_SET */ 10048c2ecf20Sopenharmony_ci tmp[1] = DP_SET_ANSI_8B10B; 10058c2ecf20Sopenharmony_ci ret = drm_dp_dpcd_write(aux, DP_DOWNSPREAD_CTRL, tmp, 2); 10068c2ecf20Sopenharmony_ci if (ret < 0) 10078c2ecf20Sopenharmony_ci goto err_dpcd_write; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci /* Reset voltage-swing & pre-emphasis */ 10108c2ecf20Sopenharmony_ci tmp[0] = tmp[1] = DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | 10118c2ecf20Sopenharmony_ci DP_TRAIN_PRE_EMPH_LEVEL_0; 10128c2ecf20Sopenharmony_ci ret = drm_dp_dpcd_write(aux, DP_TRAINING_LANE0_SET, tmp, 2); 10138c2ecf20Sopenharmony_ci if (ret < 0) 10148c2ecf20Sopenharmony_ci goto err_dpcd_write; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci /* Clock-Recovery */ 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci /* Set DPCD 0x102 for Training Pattern 1 */ 10198c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP0_SNKLTCTRL, 10208c2ecf20Sopenharmony_ci DP_LINK_SCRAMBLING_DISABLE | 10218c2ecf20Sopenharmony_ci DP_TRAINING_PATTERN_1); 10228c2ecf20Sopenharmony_ci if (ret) 10238c2ecf20Sopenharmony_ci return ret; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP0_LTLOOPCTRL, 10268c2ecf20Sopenharmony_ci (15 << 28) | /* Defer Iteration Count */ 10278c2ecf20Sopenharmony_ci (15 << 24) | /* Loop Iteration Count */ 10288c2ecf20Sopenharmony_ci (0xd << 0)); /* Loop Timer Delay */ 10298c2ecf20Sopenharmony_ci if (ret) 10308c2ecf20Sopenharmony_ci return ret; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP0_SRCCTRL, 10338c2ecf20Sopenharmony_ci tc_srcctrl(tc) | DP0_SRCCTRL_SCRMBLDIS | 10348c2ecf20Sopenharmony_ci DP0_SRCCTRL_AUTOCORRECT | 10358c2ecf20Sopenharmony_ci DP0_SRCCTRL_TP1); 10368c2ecf20Sopenharmony_ci if (ret) 10378c2ecf20Sopenharmony_ci return ret; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci /* Enable DP0 to start Link Training */ 10408c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP0CTL, 10418c2ecf20Sopenharmony_ci (drm_dp_enhanced_frame_cap(tc->link.dpcd) ? 10428c2ecf20Sopenharmony_ci EF_EN : 0) | DP_EN); 10438c2ecf20Sopenharmony_ci if (ret) 10448c2ecf20Sopenharmony_ci return ret; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci /* wait */ 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci ret = tc_wait_link_training(tc); 10498c2ecf20Sopenharmony_ci if (ret < 0) 10508c2ecf20Sopenharmony_ci return ret; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci if (ret) { 10538c2ecf20Sopenharmony_ci dev_err(tc->dev, "Link training phase 1 failed: %s\n", 10548c2ecf20Sopenharmony_ci training_pattern1_errors[ret]); 10558c2ecf20Sopenharmony_ci return -ENODEV; 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci /* Channel Equalization */ 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci /* Set DPCD 0x102 for Training Pattern 2 */ 10618c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP0_SNKLTCTRL, 10628c2ecf20Sopenharmony_ci DP_LINK_SCRAMBLING_DISABLE | 10638c2ecf20Sopenharmony_ci DP_TRAINING_PATTERN_2); 10648c2ecf20Sopenharmony_ci if (ret) 10658c2ecf20Sopenharmony_ci return ret; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP0_SRCCTRL, 10688c2ecf20Sopenharmony_ci tc_srcctrl(tc) | DP0_SRCCTRL_SCRMBLDIS | 10698c2ecf20Sopenharmony_ci DP0_SRCCTRL_AUTOCORRECT | 10708c2ecf20Sopenharmony_ci DP0_SRCCTRL_TP2); 10718c2ecf20Sopenharmony_ci if (ret) 10728c2ecf20Sopenharmony_ci return ret; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci /* wait */ 10758c2ecf20Sopenharmony_ci ret = tc_wait_link_training(tc); 10768c2ecf20Sopenharmony_ci if (ret < 0) 10778c2ecf20Sopenharmony_ci return ret; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci if (ret) { 10808c2ecf20Sopenharmony_ci dev_err(tc->dev, "Link training phase 2 failed: %s\n", 10818c2ecf20Sopenharmony_ci training_pattern2_errors[ret]); 10828c2ecf20Sopenharmony_ci return -ENODEV; 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci /* 10868c2ecf20Sopenharmony_ci * Toshiba's documentation suggests to first clear DPCD 0x102, then 10878c2ecf20Sopenharmony_ci * clear the training pattern bit in DP0_SRCCTRL. Testing shows 10888c2ecf20Sopenharmony_ci * that the link sometimes drops if those steps are done in that order, 10898c2ecf20Sopenharmony_ci * but if the steps are done in reverse order, the link stays up. 10908c2ecf20Sopenharmony_ci * 10918c2ecf20Sopenharmony_ci * So we do the steps differently than documented here. 10928c2ecf20Sopenharmony_ci */ 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci /* Clear Training Pattern, set AutoCorrect Mode = 1 */ 10958c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP0_SRCCTRL, tc_srcctrl(tc) | 10968c2ecf20Sopenharmony_ci DP0_SRCCTRL_AUTOCORRECT); 10978c2ecf20Sopenharmony_ci if (ret) 10988c2ecf20Sopenharmony_ci return ret; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci /* Clear DPCD 0x102 */ 11018c2ecf20Sopenharmony_ci /* Note: Can Not use DP0_SNKLTCTRL (0x06E4) short cut */ 11028c2ecf20Sopenharmony_ci tmp[0] = tc->link.scrambler_dis ? DP_LINK_SCRAMBLING_DISABLE : 0x00; 11038c2ecf20Sopenharmony_ci ret = drm_dp_dpcd_writeb(aux, DP_TRAINING_PATTERN_SET, tmp[0]); 11048c2ecf20Sopenharmony_ci if (ret < 0) 11058c2ecf20Sopenharmony_ci goto err_dpcd_write; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci /* Check link status */ 11088c2ecf20Sopenharmony_ci ret = drm_dp_dpcd_read_link_status(aux, tmp); 11098c2ecf20Sopenharmony_ci if (ret < 0) 11108c2ecf20Sopenharmony_ci goto err_dpcd_read; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci ret = 0; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci value = tmp[0] & DP_CHANNEL_EQ_BITS; 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci if (value != DP_CHANNEL_EQ_BITS) { 11178c2ecf20Sopenharmony_ci dev_err(tc->dev, "Lane 0 failed: %x\n", value); 11188c2ecf20Sopenharmony_ci ret = -ENODEV; 11198c2ecf20Sopenharmony_ci } 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci if (tc->link.num_lanes == 2) { 11228c2ecf20Sopenharmony_ci value = (tmp[0] >> 4) & DP_CHANNEL_EQ_BITS; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci if (value != DP_CHANNEL_EQ_BITS) { 11258c2ecf20Sopenharmony_ci dev_err(tc->dev, "Lane 1 failed: %x\n", value); 11268c2ecf20Sopenharmony_ci ret = -ENODEV; 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci if (!(tmp[2] & DP_INTERLANE_ALIGN_DONE)) { 11308c2ecf20Sopenharmony_ci dev_err(tc->dev, "Interlane align failed\n"); 11318c2ecf20Sopenharmony_ci ret = -ENODEV; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci } 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci if (ret) { 11368c2ecf20Sopenharmony_ci dev_err(dev, "0x0202 LANE0_1_STATUS: 0x%02x\n", tmp[0]); 11378c2ecf20Sopenharmony_ci dev_err(dev, "0x0203 LANE2_3_STATUS 0x%02x\n", tmp[1]); 11388c2ecf20Sopenharmony_ci dev_err(dev, "0x0204 LANE_ALIGN_STATUS_UPDATED: 0x%02x\n", tmp[2]); 11398c2ecf20Sopenharmony_ci dev_err(dev, "0x0205 SINK_STATUS: 0x%02x\n", tmp[3]); 11408c2ecf20Sopenharmony_ci dev_err(dev, "0x0206 ADJUST_REQUEST_LANE0_1: 0x%02x\n", tmp[4]); 11418c2ecf20Sopenharmony_ci dev_err(dev, "0x0207 ADJUST_REQUEST_LANE2_3: 0x%02x\n", tmp[5]); 11428c2ecf20Sopenharmony_ci return ret; 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci return 0; 11468c2ecf20Sopenharmony_cierr_dpcd_read: 11478c2ecf20Sopenharmony_ci dev_err(tc->dev, "Failed to read DPCD: %d\n", ret); 11488c2ecf20Sopenharmony_ci return ret; 11498c2ecf20Sopenharmony_cierr_dpcd_write: 11508c2ecf20Sopenharmony_ci dev_err(tc->dev, "Failed to write DPCD: %d\n", ret); 11518c2ecf20Sopenharmony_ci return ret; 11528c2ecf20Sopenharmony_ci} 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_cistatic int tc_main_link_disable(struct tc_data *tc) 11558c2ecf20Sopenharmony_ci{ 11568c2ecf20Sopenharmony_ci int ret; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci dev_dbg(tc->dev, "link disable\n"); 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP0_SRCCTRL, 0); 11618c2ecf20Sopenharmony_ci if (ret) 11628c2ecf20Sopenharmony_ci return ret; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci return regmap_write(tc->regmap, DP0CTL, 0); 11658c2ecf20Sopenharmony_ci} 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_cistatic int tc_stream_enable(struct tc_data *tc) 11688c2ecf20Sopenharmony_ci{ 11698c2ecf20Sopenharmony_ci int ret; 11708c2ecf20Sopenharmony_ci u32 value; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci dev_dbg(tc->dev, "enable video stream\n"); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci /* PXL PLL setup */ 11758c2ecf20Sopenharmony_ci if (tc_test_pattern) { 11768c2ecf20Sopenharmony_ci ret = tc_pxl_pll_en(tc, clk_get_rate(tc->refclk), 11778c2ecf20Sopenharmony_ci 1000 * tc->mode.clock); 11788c2ecf20Sopenharmony_ci if (ret) 11798c2ecf20Sopenharmony_ci return ret; 11808c2ecf20Sopenharmony_ci } 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci ret = tc_set_video_mode(tc, &tc->mode); 11838c2ecf20Sopenharmony_ci if (ret) 11848c2ecf20Sopenharmony_ci return ret; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci /* Set M/N */ 11878c2ecf20Sopenharmony_ci ret = tc_stream_clock_calc(tc); 11888c2ecf20Sopenharmony_ci if (ret) 11898c2ecf20Sopenharmony_ci return ret; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci value = VID_MN_GEN | DP_EN; 11928c2ecf20Sopenharmony_ci if (drm_dp_enhanced_frame_cap(tc->link.dpcd)) 11938c2ecf20Sopenharmony_ci value |= EF_EN; 11948c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP0CTL, value); 11958c2ecf20Sopenharmony_ci if (ret) 11968c2ecf20Sopenharmony_ci return ret; 11978c2ecf20Sopenharmony_ci /* 11988c2ecf20Sopenharmony_ci * VID_EN assertion should be delayed by at least N * LSCLK 11998c2ecf20Sopenharmony_ci * cycles from the time VID_MN_GEN is enabled in order to 12008c2ecf20Sopenharmony_ci * generate stable values for VID_M. LSCLK is 270 MHz or 12018c2ecf20Sopenharmony_ci * 162 MHz, VID_N is set to 32768 in tc_stream_clock_calc(), 12028c2ecf20Sopenharmony_ci * so a delay of at least 203 us should suffice. 12038c2ecf20Sopenharmony_ci */ 12048c2ecf20Sopenharmony_ci usleep_range(500, 1000); 12058c2ecf20Sopenharmony_ci value |= VID_EN; 12068c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, DP0CTL, value); 12078c2ecf20Sopenharmony_ci if (ret) 12088c2ecf20Sopenharmony_ci return ret; 12098c2ecf20Sopenharmony_ci /* Set input interface */ 12108c2ecf20Sopenharmony_ci value = DP0_AUDSRC_NO_INPUT; 12118c2ecf20Sopenharmony_ci if (tc_test_pattern) 12128c2ecf20Sopenharmony_ci value |= DP0_VIDSRC_COLOR_BAR; 12138c2ecf20Sopenharmony_ci else 12148c2ecf20Sopenharmony_ci value |= DP0_VIDSRC_DPI_RX; 12158c2ecf20Sopenharmony_ci ret = regmap_write(tc->regmap, SYSCTRL, value); 12168c2ecf20Sopenharmony_ci if (ret) 12178c2ecf20Sopenharmony_ci return ret; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci return 0; 12208c2ecf20Sopenharmony_ci} 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_cistatic int tc_stream_disable(struct tc_data *tc) 12238c2ecf20Sopenharmony_ci{ 12248c2ecf20Sopenharmony_ci int ret; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci dev_dbg(tc->dev, "disable video stream\n"); 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci ret = regmap_update_bits(tc->regmap, DP0CTL, VID_EN, 0); 12298c2ecf20Sopenharmony_ci if (ret) 12308c2ecf20Sopenharmony_ci return ret; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci tc_pxl_pll_dis(tc); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci return 0; 12358c2ecf20Sopenharmony_ci} 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_cistatic void tc_bridge_enable(struct drm_bridge *bridge) 12388c2ecf20Sopenharmony_ci{ 12398c2ecf20Sopenharmony_ci struct tc_data *tc = bridge_to_tc(bridge); 12408c2ecf20Sopenharmony_ci int ret; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci ret = tc_get_display_props(tc); 12438c2ecf20Sopenharmony_ci if (ret < 0) { 12448c2ecf20Sopenharmony_ci dev_err(tc->dev, "failed to read display props: %d\n", ret); 12458c2ecf20Sopenharmony_ci return; 12468c2ecf20Sopenharmony_ci } 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci ret = tc_main_link_enable(tc); 12498c2ecf20Sopenharmony_ci if (ret < 0) { 12508c2ecf20Sopenharmony_ci dev_err(tc->dev, "main link enable error: %d\n", ret); 12518c2ecf20Sopenharmony_ci return; 12528c2ecf20Sopenharmony_ci } 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci ret = tc_stream_enable(tc); 12558c2ecf20Sopenharmony_ci if (ret < 0) { 12568c2ecf20Sopenharmony_ci dev_err(tc->dev, "main link stream start error: %d\n", ret); 12578c2ecf20Sopenharmony_ci tc_main_link_disable(tc); 12588c2ecf20Sopenharmony_ci return; 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci} 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_cistatic void tc_bridge_disable(struct drm_bridge *bridge) 12638c2ecf20Sopenharmony_ci{ 12648c2ecf20Sopenharmony_ci struct tc_data *tc = bridge_to_tc(bridge); 12658c2ecf20Sopenharmony_ci int ret; 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci ret = tc_stream_disable(tc); 12688c2ecf20Sopenharmony_ci if (ret < 0) 12698c2ecf20Sopenharmony_ci dev_err(tc->dev, "main link stream stop error: %d\n", ret); 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci ret = tc_main_link_disable(tc); 12728c2ecf20Sopenharmony_ci if (ret < 0) 12738c2ecf20Sopenharmony_ci dev_err(tc->dev, "main link disable error: %d\n", ret); 12748c2ecf20Sopenharmony_ci} 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_cistatic bool tc_bridge_mode_fixup(struct drm_bridge *bridge, 12778c2ecf20Sopenharmony_ci const struct drm_display_mode *mode, 12788c2ecf20Sopenharmony_ci struct drm_display_mode *adj) 12798c2ecf20Sopenharmony_ci{ 12808c2ecf20Sopenharmony_ci /* Fixup sync polarities, both hsync and vsync are active low */ 12818c2ecf20Sopenharmony_ci adj->flags = mode->flags; 12828c2ecf20Sopenharmony_ci adj->flags |= (DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC); 12838c2ecf20Sopenharmony_ci adj->flags &= ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC); 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci return true; 12868c2ecf20Sopenharmony_ci} 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_cistatic enum drm_mode_status tc_mode_valid(struct drm_bridge *bridge, 12898c2ecf20Sopenharmony_ci const struct drm_display_info *info, 12908c2ecf20Sopenharmony_ci const struct drm_display_mode *mode) 12918c2ecf20Sopenharmony_ci{ 12928c2ecf20Sopenharmony_ci struct tc_data *tc = bridge_to_tc(bridge); 12938c2ecf20Sopenharmony_ci u32 req, avail; 12948c2ecf20Sopenharmony_ci u32 bits_per_pixel = 24; 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci /* DPI interface clock limitation: upto 154 MHz */ 12978c2ecf20Sopenharmony_ci if (mode->clock > 154000) 12988c2ecf20Sopenharmony_ci return MODE_CLOCK_HIGH; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci req = mode->clock * bits_per_pixel / 8; 13018c2ecf20Sopenharmony_ci avail = tc->link.num_lanes * tc->link.rate; 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci if (req > avail) 13048c2ecf20Sopenharmony_ci return MODE_BAD; 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci return MODE_OK; 13078c2ecf20Sopenharmony_ci} 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_cistatic void tc_bridge_mode_set(struct drm_bridge *bridge, 13108c2ecf20Sopenharmony_ci const struct drm_display_mode *mode, 13118c2ecf20Sopenharmony_ci const struct drm_display_mode *adj) 13128c2ecf20Sopenharmony_ci{ 13138c2ecf20Sopenharmony_ci struct tc_data *tc = bridge_to_tc(bridge); 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci tc->mode = *mode; 13168c2ecf20Sopenharmony_ci} 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_cistatic struct edid *tc_get_edid(struct drm_bridge *bridge, 13198c2ecf20Sopenharmony_ci struct drm_connector *connector) 13208c2ecf20Sopenharmony_ci{ 13218c2ecf20Sopenharmony_ci struct tc_data *tc = bridge_to_tc(bridge); 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci return drm_get_edid(connector, &tc->aux.ddc); 13248c2ecf20Sopenharmony_ci} 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_cistatic int tc_connector_get_modes(struct drm_connector *connector) 13278c2ecf20Sopenharmony_ci{ 13288c2ecf20Sopenharmony_ci struct tc_data *tc = connector_to_tc(connector); 13298c2ecf20Sopenharmony_ci int num_modes; 13308c2ecf20Sopenharmony_ci struct edid *edid; 13318c2ecf20Sopenharmony_ci int ret; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci ret = tc_get_display_props(tc); 13348c2ecf20Sopenharmony_ci if (ret < 0) { 13358c2ecf20Sopenharmony_ci dev_err(tc->dev, "failed to read display props: %d\n", ret); 13368c2ecf20Sopenharmony_ci return 0; 13378c2ecf20Sopenharmony_ci } 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci if (tc->panel_bridge) { 13408c2ecf20Sopenharmony_ci num_modes = drm_bridge_get_modes(tc->panel_bridge, connector); 13418c2ecf20Sopenharmony_ci if (num_modes > 0) 13428c2ecf20Sopenharmony_ci return num_modes; 13438c2ecf20Sopenharmony_ci } 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci edid = tc_get_edid(&tc->bridge, connector); 13468c2ecf20Sopenharmony_ci num_modes = drm_add_edid_modes(connector, edid); 13478c2ecf20Sopenharmony_ci kfree(edid); 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci return num_modes; 13508c2ecf20Sopenharmony_ci} 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs tc_connector_helper_funcs = { 13538c2ecf20Sopenharmony_ci .get_modes = tc_connector_get_modes, 13548c2ecf20Sopenharmony_ci}; 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_cistatic enum drm_connector_status tc_bridge_detect(struct drm_bridge *bridge) 13578c2ecf20Sopenharmony_ci{ 13588c2ecf20Sopenharmony_ci struct tc_data *tc = bridge_to_tc(bridge); 13598c2ecf20Sopenharmony_ci bool conn; 13608c2ecf20Sopenharmony_ci u32 val; 13618c2ecf20Sopenharmony_ci int ret; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci ret = regmap_read(tc->regmap, GPIOI, &val); 13648c2ecf20Sopenharmony_ci if (ret) 13658c2ecf20Sopenharmony_ci return connector_status_unknown; 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci conn = val & BIT(tc->hpd_pin); 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci if (conn) 13708c2ecf20Sopenharmony_ci return connector_status_connected; 13718c2ecf20Sopenharmony_ci else 13728c2ecf20Sopenharmony_ci return connector_status_disconnected; 13738c2ecf20Sopenharmony_ci} 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_cistatic enum drm_connector_status 13768c2ecf20Sopenharmony_citc_connector_detect(struct drm_connector *connector, bool force) 13778c2ecf20Sopenharmony_ci{ 13788c2ecf20Sopenharmony_ci struct tc_data *tc = connector_to_tc(connector); 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci if (tc->hpd_pin >= 0) 13818c2ecf20Sopenharmony_ci return tc_bridge_detect(&tc->bridge); 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci if (tc->panel_bridge) 13848c2ecf20Sopenharmony_ci return connector_status_connected; 13858c2ecf20Sopenharmony_ci else 13868c2ecf20Sopenharmony_ci return connector_status_unknown; 13878c2ecf20Sopenharmony_ci} 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs tc_connector_funcs = { 13908c2ecf20Sopenharmony_ci .detect = tc_connector_detect, 13918c2ecf20Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 13928c2ecf20Sopenharmony_ci .destroy = drm_connector_cleanup, 13938c2ecf20Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 13948c2ecf20Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 13958c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 13968c2ecf20Sopenharmony_ci}; 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_cistatic int tc_bridge_attach(struct drm_bridge *bridge, 13998c2ecf20Sopenharmony_ci enum drm_bridge_attach_flags flags) 14008c2ecf20Sopenharmony_ci{ 14018c2ecf20Sopenharmony_ci u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; 14028c2ecf20Sopenharmony_ci struct tc_data *tc = bridge_to_tc(bridge); 14038c2ecf20Sopenharmony_ci struct drm_device *drm = bridge->dev; 14048c2ecf20Sopenharmony_ci int ret; 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci if (tc->panel_bridge) { 14078c2ecf20Sopenharmony_ci /* If a connector is required then this driver shall create it */ 14088c2ecf20Sopenharmony_ci ret = drm_bridge_attach(tc->bridge.encoder, tc->panel_bridge, 14098c2ecf20Sopenharmony_ci &tc->bridge, flags | DRM_BRIDGE_ATTACH_NO_CONNECTOR); 14108c2ecf20Sopenharmony_ci if (ret) 14118c2ecf20Sopenharmony_ci return ret; 14128c2ecf20Sopenharmony_ci } 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) 14158c2ecf20Sopenharmony_ci return 0; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci /* Create DP/eDP connector */ 14188c2ecf20Sopenharmony_ci drm_connector_helper_add(&tc->connector, &tc_connector_helper_funcs); 14198c2ecf20Sopenharmony_ci ret = drm_connector_init(drm, &tc->connector, &tc_connector_funcs, tc->bridge.type); 14208c2ecf20Sopenharmony_ci if (ret) 14218c2ecf20Sopenharmony_ci return ret; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci /* Don't poll if don't have HPD connected */ 14248c2ecf20Sopenharmony_ci if (tc->hpd_pin >= 0) { 14258c2ecf20Sopenharmony_ci if (tc->have_irq) 14268c2ecf20Sopenharmony_ci tc->connector.polled = DRM_CONNECTOR_POLL_HPD; 14278c2ecf20Sopenharmony_ci else 14288c2ecf20Sopenharmony_ci tc->connector.polled = DRM_CONNECTOR_POLL_CONNECT | 14298c2ecf20Sopenharmony_ci DRM_CONNECTOR_POLL_DISCONNECT; 14308c2ecf20Sopenharmony_ci } 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci drm_display_info_set_bus_formats(&tc->connector.display_info, 14338c2ecf20Sopenharmony_ci &bus_format, 1); 14348c2ecf20Sopenharmony_ci tc->connector.display_info.bus_flags = 14358c2ecf20Sopenharmony_ci DRM_BUS_FLAG_DE_HIGH | 14368c2ecf20Sopenharmony_ci DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE | 14378c2ecf20Sopenharmony_ci DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE; 14388c2ecf20Sopenharmony_ci drm_connector_attach_encoder(&tc->connector, tc->bridge.encoder); 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci return 0; 14418c2ecf20Sopenharmony_ci} 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_cistatic const struct drm_bridge_funcs tc_bridge_funcs = { 14448c2ecf20Sopenharmony_ci .attach = tc_bridge_attach, 14458c2ecf20Sopenharmony_ci .mode_valid = tc_mode_valid, 14468c2ecf20Sopenharmony_ci .mode_set = tc_bridge_mode_set, 14478c2ecf20Sopenharmony_ci .enable = tc_bridge_enable, 14488c2ecf20Sopenharmony_ci .disable = tc_bridge_disable, 14498c2ecf20Sopenharmony_ci .mode_fixup = tc_bridge_mode_fixup, 14508c2ecf20Sopenharmony_ci .detect = tc_bridge_detect, 14518c2ecf20Sopenharmony_ci .get_edid = tc_get_edid, 14528c2ecf20Sopenharmony_ci}; 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_cistatic bool tc_readable_reg(struct device *dev, unsigned int reg) 14558c2ecf20Sopenharmony_ci{ 14568c2ecf20Sopenharmony_ci return reg != SYSCTRL; 14578c2ecf20Sopenharmony_ci} 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_cistatic const struct regmap_range tc_volatile_ranges[] = { 14608c2ecf20Sopenharmony_ci regmap_reg_range(DP0_AUXWDATA(0), DP0_AUXSTATUS), 14618c2ecf20Sopenharmony_ci regmap_reg_range(DP0_LTSTAT, DP0_SNKLTCHGREQ), 14628c2ecf20Sopenharmony_ci regmap_reg_range(DP_PHY_CTRL, DP_PHY_CTRL), 14638c2ecf20Sopenharmony_ci regmap_reg_range(DP0_PLLCTRL, PXL_PLLCTRL), 14648c2ecf20Sopenharmony_ci regmap_reg_range(VFUEN0, VFUEN0), 14658c2ecf20Sopenharmony_ci regmap_reg_range(INTSTS_G, INTSTS_G), 14668c2ecf20Sopenharmony_ci regmap_reg_range(GPIOI, GPIOI), 14678c2ecf20Sopenharmony_ci}; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_cistatic const struct regmap_access_table tc_volatile_table = { 14708c2ecf20Sopenharmony_ci .yes_ranges = tc_volatile_ranges, 14718c2ecf20Sopenharmony_ci .n_yes_ranges = ARRAY_SIZE(tc_volatile_ranges), 14728c2ecf20Sopenharmony_ci}; 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_cistatic bool tc_writeable_reg(struct device *dev, unsigned int reg) 14758c2ecf20Sopenharmony_ci{ 14768c2ecf20Sopenharmony_ci return (reg != TC_IDREG) && 14778c2ecf20Sopenharmony_ci (reg != DP0_LTSTAT) && 14788c2ecf20Sopenharmony_ci (reg != DP0_SNKLTCHGREQ); 14798c2ecf20Sopenharmony_ci} 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_cistatic const struct regmap_config tc_regmap_config = { 14828c2ecf20Sopenharmony_ci .name = "tc358767", 14838c2ecf20Sopenharmony_ci .reg_bits = 16, 14848c2ecf20Sopenharmony_ci .val_bits = 32, 14858c2ecf20Sopenharmony_ci .reg_stride = 4, 14868c2ecf20Sopenharmony_ci .max_register = PLL_DBG, 14878c2ecf20Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 14888c2ecf20Sopenharmony_ci .readable_reg = tc_readable_reg, 14898c2ecf20Sopenharmony_ci .volatile_table = &tc_volatile_table, 14908c2ecf20Sopenharmony_ci .writeable_reg = tc_writeable_reg, 14918c2ecf20Sopenharmony_ci .reg_format_endian = REGMAP_ENDIAN_BIG, 14928c2ecf20Sopenharmony_ci .val_format_endian = REGMAP_ENDIAN_LITTLE, 14938c2ecf20Sopenharmony_ci}; 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_cistatic irqreturn_t tc_irq_handler(int irq, void *arg) 14968c2ecf20Sopenharmony_ci{ 14978c2ecf20Sopenharmony_ci struct tc_data *tc = arg; 14988c2ecf20Sopenharmony_ci u32 val; 14998c2ecf20Sopenharmony_ci int r; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci r = regmap_read(tc->regmap, INTSTS_G, &val); 15028c2ecf20Sopenharmony_ci if (r) 15038c2ecf20Sopenharmony_ci return IRQ_NONE; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci if (!val) 15068c2ecf20Sopenharmony_ci return IRQ_NONE; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci if (val & INT_SYSERR) { 15098c2ecf20Sopenharmony_ci u32 stat = 0; 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci regmap_read(tc->regmap, SYSSTAT, &stat); 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci dev_err(tc->dev, "syserr %x\n", stat); 15148c2ecf20Sopenharmony_ci } 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci if (tc->hpd_pin >= 0 && tc->bridge.dev) { 15178c2ecf20Sopenharmony_ci /* 15188c2ecf20Sopenharmony_ci * H is triggered when the GPIO goes high. 15198c2ecf20Sopenharmony_ci * 15208c2ecf20Sopenharmony_ci * LC is triggered when the GPIO goes low and stays low for 15218c2ecf20Sopenharmony_ci * the duration of LCNT 15228c2ecf20Sopenharmony_ci */ 15238c2ecf20Sopenharmony_ci bool h = val & INT_GPIO_H(tc->hpd_pin); 15248c2ecf20Sopenharmony_ci bool lc = val & INT_GPIO_LC(tc->hpd_pin); 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci dev_dbg(tc->dev, "GPIO%d: %s %s\n", tc->hpd_pin, 15278c2ecf20Sopenharmony_ci h ? "H" : "", lc ? "LC" : ""); 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci if (h || lc) 15308c2ecf20Sopenharmony_ci drm_kms_helper_hotplug_event(tc->bridge.dev); 15318c2ecf20Sopenharmony_ci } 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci regmap_write(tc->regmap, INTSTS_G, val); 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci return IRQ_HANDLED; 15368c2ecf20Sopenharmony_ci} 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_cistatic int tc_probe_edp_bridge_endpoint(struct tc_data *tc) 15398c2ecf20Sopenharmony_ci{ 15408c2ecf20Sopenharmony_ci struct device *dev = tc->dev; 15418c2ecf20Sopenharmony_ci struct drm_panel *panel; 15428c2ecf20Sopenharmony_ci int ret; 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci /* port@2 is the output port */ 15458c2ecf20Sopenharmony_ci ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &panel, NULL); 15468c2ecf20Sopenharmony_ci if (ret && ret != -ENODEV) 15478c2ecf20Sopenharmony_ci return ret; 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci if (panel) { 15508c2ecf20Sopenharmony_ci struct drm_bridge *panel_bridge; 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci panel_bridge = devm_drm_panel_bridge_add(dev, panel); 15538c2ecf20Sopenharmony_ci if (IS_ERR(panel_bridge)) 15548c2ecf20Sopenharmony_ci return PTR_ERR(panel_bridge); 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci tc->panel_bridge = panel_bridge; 15578c2ecf20Sopenharmony_ci tc->bridge.type = DRM_MODE_CONNECTOR_eDP; 15588c2ecf20Sopenharmony_ci } else { 15598c2ecf20Sopenharmony_ci tc->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; 15608c2ecf20Sopenharmony_ci } 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci return 0; 15638c2ecf20Sopenharmony_ci} 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_cistatic void tc_clk_disable(void *data) 15668c2ecf20Sopenharmony_ci{ 15678c2ecf20Sopenharmony_ci struct clk *refclk = data; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci clk_disable_unprepare(refclk); 15708c2ecf20Sopenharmony_ci} 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_cistatic int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) 15738c2ecf20Sopenharmony_ci{ 15748c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 15758c2ecf20Sopenharmony_ci struct tc_data *tc; 15768c2ecf20Sopenharmony_ci int ret; 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci tc = devm_kzalloc(dev, sizeof(*tc), GFP_KERNEL); 15798c2ecf20Sopenharmony_ci if (!tc) 15808c2ecf20Sopenharmony_ci return -ENOMEM; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci tc->dev = dev; 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci ret = tc_probe_edp_bridge_endpoint(tc); 15858c2ecf20Sopenharmony_ci if (ret) 15868c2ecf20Sopenharmony_ci return ret; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci tc->refclk = devm_clk_get(dev, "ref"); 15898c2ecf20Sopenharmony_ci if (IS_ERR(tc->refclk)) { 15908c2ecf20Sopenharmony_ci ret = PTR_ERR(tc->refclk); 15918c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get refclk: %d\n", ret); 15928c2ecf20Sopenharmony_ci return ret; 15938c2ecf20Sopenharmony_ci } 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci ret = clk_prepare_enable(tc->refclk); 15968c2ecf20Sopenharmony_ci if (ret) 15978c2ecf20Sopenharmony_ci return ret; 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci ret = devm_add_action_or_reset(dev, tc_clk_disable, tc->refclk); 16008c2ecf20Sopenharmony_ci if (ret) 16018c2ecf20Sopenharmony_ci return ret; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci /* tRSTW = 100 cycles , at 13 MHz that is ~7.69 us */ 16048c2ecf20Sopenharmony_ci usleep_range(10, 15); 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci /* Shut down GPIO is optional */ 16078c2ecf20Sopenharmony_ci tc->sd_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH); 16088c2ecf20Sopenharmony_ci if (IS_ERR(tc->sd_gpio)) 16098c2ecf20Sopenharmony_ci return PTR_ERR(tc->sd_gpio); 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci if (tc->sd_gpio) { 16128c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(tc->sd_gpio, 0); 16138c2ecf20Sopenharmony_ci usleep_range(5000, 10000); 16148c2ecf20Sopenharmony_ci } 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci /* Reset GPIO is optional */ 16178c2ecf20Sopenharmony_ci tc->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 16188c2ecf20Sopenharmony_ci if (IS_ERR(tc->reset_gpio)) 16198c2ecf20Sopenharmony_ci return PTR_ERR(tc->reset_gpio); 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci if (tc->reset_gpio) { 16228c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(tc->reset_gpio, 1); 16238c2ecf20Sopenharmony_ci usleep_range(5000, 10000); 16248c2ecf20Sopenharmony_ci } 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci tc->regmap = devm_regmap_init_i2c(client, &tc_regmap_config); 16278c2ecf20Sopenharmony_ci if (IS_ERR(tc->regmap)) { 16288c2ecf20Sopenharmony_ci ret = PTR_ERR(tc->regmap); 16298c2ecf20Sopenharmony_ci dev_err(dev, "Failed to initialize regmap: %d\n", ret); 16308c2ecf20Sopenharmony_ci return ret; 16318c2ecf20Sopenharmony_ci } 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci ret = of_property_read_u32(dev->of_node, "toshiba,hpd-pin", 16348c2ecf20Sopenharmony_ci &tc->hpd_pin); 16358c2ecf20Sopenharmony_ci if (ret) { 16368c2ecf20Sopenharmony_ci tc->hpd_pin = -ENODEV; 16378c2ecf20Sopenharmony_ci } else { 16388c2ecf20Sopenharmony_ci if (tc->hpd_pin < 0 || tc->hpd_pin > 1) { 16398c2ecf20Sopenharmony_ci dev_err(dev, "failed to parse HPD number\n"); 16408c2ecf20Sopenharmony_ci return -EINVAL; 16418c2ecf20Sopenharmony_ci } 16428c2ecf20Sopenharmony_ci } 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci if (client->irq > 0) { 16458c2ecf20Sopenharmony_ci /* enable SysErr */ 16468c2ecf20Sopenharmony_ci regmap_write(tc->regmap, INTCTL_G, INT_SYSERR); 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(dev, client->irq, 16498c2ecf20Sopenharmony_ci NULL, tc_irq_handler, 16508c2ecf20Sopenharmony_ci IRQF_ONESHOT, 16518c2ecf20Sopenharmony_ci "tc358767-irq", tc); 16528c2ecf20Sopenharmony_ci if (ret) { 16538c2ecf20Sopenharmony_ci dev_err(dev, "failed to register dp interrupt\n"); 16548c2ecf20Sopenharmony_ci return ret; 16558c2ecf20Sopenharmony_ci } 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci tc->have_irq = true; 16588c2ecf20Sopenharmony_ci } 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci ret = regmap_read(tc->regmap, TC_IDREG, &tc->rev); 16618c2ecf20Sopenharmony_ci if (ret) { 16628c2ecf20Sopenharmony_ci dev_err(tc->dev, "can not read device ID: %d\n", ret); 16638c2ecf20Sopenharmony_ci return ret; 16648c2ecf20Sopenharmony_ci } 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci if ((tc->rev != 0x6601) && (tc->rev != 0x6603)) { 16678c2ecf20Sopenharmony_ci dev_err(tc->dev, "invalid device ID: 0x%08x\n", tc->rev); 16688c2ecf20Sopenharmony_ci return -EINVAL; 16698c2ecf20Sopenharmony_ci } 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci tc->assr = (tc->rev == 0x6601); /* Enable ASSR for eDP panels */ 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci if (!tc->reset_gpio) { 16748c2ecf20Sopenharmony_ci /* 16758c2ecf20Sopenharmony_ci * If the reset pin isn't present, do a software reset. It isn't 16768c2ecf20Sopenharmony_ci * as thorough as the hardware reset, as we can't reset the I2C 16778c2ecf20Sopenharmony_ci * communication block for obvious reasons, but it's getting the 16788c2ecf20Sopenharmony_ci * chip into a defined state. 16798c2ecf20Sopenharmony_ci */ 16808c2ecf20Sopenharmony_ci regmap_update_bits(tc->regmap, SYSRSTENB, 16818c2ecf20Sopenharmony_ci ENBLCD0 | ENBBM | ENBDSIRX | ENBREG | ENBHDCP, 16828c2ecf20Sopenharmony_ci 0); 16838c2ecf20Sopenharmony_ci regmap_update_bits(tc->regmap, SYSRSTENB, 16848c2ecf20Sopenharmony_ci ENBLCD0 | ENBBM | ENBDSIRX | ENBREG | ENBHDCP, 16858c2ecf20Sopenharmony_ci ENBLCD0 | ENBBM | ENBDSIRX | ENBREG | ENBHDCP); 16868c2ecf20Sopenharmony_ci usleep_range(5000, 10000); 16878c2ecf20Sopenharmony_ci } 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci if (tc->hpd_pin >= 0) { 16908c2ecf20Sopenharmony_ci u32 lcnt_reg = tc->hpd_pin == 0 ? INT_GP0_LCNT : INT_GP1_LCNT; 16918c2ecf20Sopenharmony_ci u32 h_lc = INT_GPIO_H(tc->hpd_pin) | INT_GPIO_LC(tc->hpd_pin); 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci /* Set LCNT to 2ms */ 16948c2ecf20Sopenharmony_ci regmap_write(tc->regmap, lcnt_reg, 16958c2ecf20Sopenharmony_ci clk_get_rate(tc->refclk) * 2 / 1000); 16968c2ecf20Sopenharmony_ci /* We need the "alternate" mode for HPD */ 16978c2ecf20Sopenharmony_ci regmap_write(tc->regmap, GPIOM, BIT(tc->hpd_pin)); 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci if (tc->have_irq) { 17008c2ecf20Sopenharmony_ci /* enable H & LC */ 17018c2ecf20Sopenharmony_ci regmap_update_bits(tc->regmap, INTCTL_G, h_lc, h_lc); 17028c2ecf20Sopenharmony_ci } 17038c2ecf20Sopenharmony_ci } 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci ret = tc_aux_link_setup(tc); 17068c2ecf20Sopenharmony_ci if (ret) 17078c2ecf20Sopenharmony_ci return ret; 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci /* Register DP AUX channel */ 17108c2ecf20Sopenharmony_ci tc->aux.name = "TC358767 AUX i2c adapter"; 17118c2ecf20Sopenharmony_ci tc->aux.dev = tc->dev; 17128c2ecf20Sopenharmony_ci tc->aux.transfer = tc_aux_transfer; 17138c2ecf20Sopenharmony_ci ret = drm_dp_aux_register(&tc->aux); 17148c2ecf20Sopenharmony_ci if (ret) 17158c2ecf20Sopenharmony_ci return ret; 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci tc->bridge.funcs = &tc_bridge_funcs; 17188c2ecf20Sopenharmony_ci if (tc->hpd_pin >= 0) 17198c2ecf20Sopenharmony_ci tc->bridge.ops |= DRM_BRIDGE_OP_DETECT; 17208c2ecf20Sopenharmony_ci tc->bridge.ops |= DRM_BRIDGE_OP_EDID; 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci tc->bridge.of_node = dev->of_node; 17238c2ecf20Sopenharmony_ci drm_bridge_add(&tc->bridge); 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci i2c_set_clientdata(client, tc); 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci return 0; 17288c2ecf20Sopenharmony_ci} 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_cistatic int tc_remove(struct i2c_client *client) 17318c2ecf20Sopenharmony_ci{ 17328c2ecf20Sopenharmony_ci struct tc_data *tc = i2c_get_clientdata(client); 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci drm_bridge_remove(&tc->bridge); 17358c2ecf20Sopenharmony_ci drm_dp_aux_unregister(&tc->aux); 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci return 0; 17388c2ecf20Sopenharmony_ci} 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_cistatic const struct i2c_device_id tc358767_i2c_ids[] = { 17418c2ecf20Sopenharmony_ci { "tc358767", 0 }, 17428c2ecf20Sopenharmony_ci { } 17438c2ecf20Sopenharmony_ci}; 17448c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tc358767_i2c_ids); 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_cistatic const struct of_device_id tc358767_of_ids[] = { 17478c2ecf20Sopenharmony_ci { .compatible = "toshiba,tc358767", }, 17488c2ecf20Sopenharmony_ci { } 17498c2ecf20Sopenharmony_ci}; 17508c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tc358767_of_ids); 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_cistatic struct i2c_driver tc358767_driver = { 17538c2ecf20Sopenharmony_ci .driver = { 17548c2ecf20Sopenharmony_ci .name = "tc358767", 17558c2ecf20Sopenharmony_ci .of_match_table = tc358767_of_ids, 17568c2ecf20Sopenharmony_ci }, 17578c2ecf20Sopenharmony_ci .id_table = tc358767_i2c_ids, 17588c2ecf20Sopenharmony_ci .probe = tc_probe, 17598c2ecf20Sopenharmony_ci .remove = tc_remove, 17608c2ecf20Sopenharmony_ci}; 17618c2ecf20Sopenharmony_cimodule_i2c_driver(tc358767_driver); 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andrey Gusakov <andrey.gusakov@cogentembedded.com>"); 17648c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("tc358767 eDP encoder driver"); 17658c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1766