17eb8f069SAndrzej Hajda /* 27eb8f069SAndrzej Hajda * Samsung SoC MIPI DSI Master driver. 37eb8f069SAndrzej Hajda * 47eb8f069SAndrzej Hajda * Copyright (c) 2014 Samsung Electronics Co., Ltd 57eb8f069SAndrzej Hajda * 67eb8f069SAndrzej Hajda * Contacts: Tomasz Figa <t.figa@samsung.com> 77eb8f069SAndrzej Hajda * 87eb8f069SAndrzej Hajda * This program is free software; you can redistribute it and/or modify 97eb8f069SAndrzej Hajda * it under the terms of the GNU General Public License version 2 as 107eb8f069SAndrzej Hajda * published by the Free Software Foundation. 117eb8f069SAndrzej Hajda */ 127eb8f069SAndrzej Hajda 137eb8f069SAndrzej Hajda #include <drm/drmP.h> 147eb8f069SAndrzej Hajda #include <drm/drm_crtc_helper.h> 157eb8f069SAndrzej Hajda #include <drm/drm_mipi_dsi.h> 167eb8f069SAndrzej Hajda #include <drm/drm_panel.h> 177eb8f069SAndrzej Hajda 187eb8f069SAndrzej Hajda #include <linux/clk.h> 19e17ddeccSYoungJun Cho #include <linux/gpio/consumer.h> 207eb8f069SAndrzej Hajda #include <linux/irq.h> 219a320415SYoungJun Cho #include <linux/of_device.h> 22e17ddeccSYoungJun Cho #include <linux/of_gpio.h> 237eb8f069SAndrzej Hajda #include <linux/phy/phy.h> 247eb8f069SAndrzej Hajda #include <linux/regulator/consumer.h> 25f37cd5e8SInki Dae #include <linux/component.h> 267eb8f069SAndrzej Hajda 277eb8f069SAndrzej Hajda #include <video/mipi_display.h> 287eb8f069SAndrzej Hajda #include <video/videomode.h> 297eb8f069SAndrzej Hajda 30e17ddeccSYoungJun Cho #include "exynos_drm_crtc.h" 317eb8f069SAndrzej Hajda #include "exynos_drm_drv.h" 327eb8f069SAndrzej Hajda 337eb8f069SAndrzej Hajda /* returns true iff both arguments logically differs */ 347eb8f069SAndrzej Hajda #define NEQV(a, b) (!(a) ^ !(b)) 357eb8f069SAndrzej Hajda 367eb8f069SAndrzej Hajda #define DSIM_STATUS_REG 0x0 /* Status register */ 377eb8f069SAndrzej Hajda #define DSIM_SWRST_REG 0x4 /* Software reset register */ 387eb8f069SAndrzej Hajda #define DSIM_CLKCTRL_REG 0x8 /* Clock control register */ 397eb8f069SAndrzej Hajda #define DSIM_TIMEOUT_REG 0xc /* Time out register */ 407eb8f069SAndrzej Hajda #define DSIM_CONFIG_REG 0x10 /* Configuration register */ 417eb8f069SAndrzej Hajda #define DSIM_ESCMODE_REG 0x14 /* Escape mode register */ 427eb8f069SAndrzej Hajda 437eb8f069SAndrzej Hajda /* Main display image resolution register */ 447eb8f069SAndrzej Hajda #define DSIM_MDRESOL_REG 0x18 457eb8f069SAndrzej Hajda #define DSIM_MVPORCH_REG 0x1c /* Main display Vporch register */ 467eb8f069SAndrzej Hajda #define DSIM_MHPORCH_REG 0x20 /* Main display Hporch register */ 477eb8f069SAndrzej Hajda #define DSIM_MSYNC_REG 0x24 /* Main display sync area register */ 487eb8f069SAndrzej Hajda 497eb8f069SAndrzej Hajda /* Sub display image resolution register */ 507eb8f069SAndrzej Hajda #define DSIM_SDRESOL_REG 0x28 517eb8f069SAndrzej Hajda #define DSIM_INTSRC_REG 0x2c /* Interrupt source register */ 527eb8f069SAndrzej Hajda #define DSIM_INTMSK_REG 0x30 /* Interrupt mask register */ 537eb8f069SAndrzej Hajda #define DSIM_PKTHDR_REG 0x34 /* Packet Header FIFO register */ 547eb8f069SAndrzej Hajda #define DSIM_PAYLOAD_REG 0x38 /* Payload FIFO register */ 557eb8f069SAndrzej Hajda #define DSIM_RXFIFO_REG 0x3c /* Read FIFO register */ 567eb8f069SAndrzej Hajda #define DSIM_FIFOTHLD_REG 0x40 /* FIFO threshold level register */ 577eb8f069SAndrzej Hajda #define DSIM_FIFOCTRL_REG 0x44 /* FIFO status and control register */ 587eb8f069SAndrzej Hajda 597eb8f069SAndrzej Hajda /* FIFO memory AC characteristic register */ 607eb8f069SAndrzej Hajda #define DSIM_PLLCTRL_REG 0x4c /* PLL control register */ 617eb8f069SAndrzej Hajda #define DSIM_PHYACCHR_REG 0x54 /* D-PHY AC characteristic register */ 627eb8f069SAndrzej Hajda #define DSIM_PHYACCHR1_REG 0x58 /* D-PHY AC characteristic register1 */ 639a320415SYoungJun Cho #define DSIM_PHYCTRL_REG 0x5c 649a320415SYoungJun Cho #define DSIM_PHYTIMING_REG 0x64 659a320415SYoungJun Cho #define DSIM_PHYTIMING1_REG 0x68 669a320415SYoungJun Cho #define DSIM_PHYTIMING2_REG 0x6c 677eb8f069SAndrzej Hajda 687eb8f069SAndrzej Hajda /* DSIM_STATUS */ 697eb8f069SAndrzej Hajda #define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0) 707eb8f069SAndrzej Hajda #define DSIM_STOP_STATE_CLK (1 << 8) 717eb8f069SAndrzej Hajda #define DSIM_TX_READY_HS_CLK (1 << 10) 727eb8f069SAndrzej Hajda #define DSIM_PLL_STABLE (1 << 31) 737eb8f069SAndrzej Hajda 747eb8f069SAndrzej Hajda /* DSIM_SWRST */ 757eb8f069SAndrzej Hajda #define DSIM_FUNCRST (1 << 16) 767eb8f069SAndrzej Hajda #define DSIM_SWRST (1 << 0) 777eb8f069SAndrzej Hajda 787eb8f069SAndrzej Hajda /* DSIM_TIMEOUT */ 797eb8f069SAndrzej Hajda #define DSIM_LPDR_TIMEOUT(x) ((x) << 0) 807eb8f069SAndrzej Hajda #define DSIM_BTA_TIMEOUT(x) ((x) << 16) 817eb8f069SAndrzej Hajda 827eb8f069SAndrzej Hajda /* DSIM_CLKCTRL */ 837eb8f069SAndrzej Hajda #define DSIM_ESC_PRESCALER(x) (((x) & 0xffff) << 0) 847eb8f069SAndrzej Hajda #define DSIM_ESC_PRESCALER_MASK (0xffff << 0) 857eb8f069SAndrzej Hajda #define DSIM_LANE_ESC_CLK_EN_CLK (1 << 19) 867eb8f069SAndrzej Hajda #define DSIM_LANE_ESC_CLK_EN_DATA(x) (((x) & 0xf) << 20) 877eb8f069SAndrzej Hajda #define DSIM_LANE_ESC_CLK_EN_DATA_MASK (0xf << 20) 887eb8f069SAndrzej Hajda #define DSIM_BYTE_CLKEN (1 << 24) 897eb8f069SAndrzej Hajda #define DSIM_BYTE_CLK_SRC(x) (((x) & 0x3) << 25) 907eb8f069SAndrzej Hajda #define DSIM_BYTE_CLK_SRC_MASK (0x3 << 25) 917eb8f069SAndrzej Hajda #define DSIM_PLL_BYPASS (1 << 27) 927eb8f069SAndrzej Hajda #define DSIM_ESC_CLKEN (1 << 28) 937eb8f069SAndrzej Hajda #define DSIM_TX_REQUEST_HSCLK (1 << 31) 947eb8f069SAndrzej Hajda 957eb8f069SAndrzej Hajda /* DSIM_CONFIG */ 967eb8f069SAndrzej Hajda #define DSIM_LANE_EN_CLK (1 << 0) 977eb8f069SAndrzej Hajda #define DSIM_LANE_EN(x) (((x) & 0xf) << 1) 987eb8f069SAndrzej Hajda #define DSIM_NUM_OF_DATA_LANE(x) (((x) & 0x3) << 5) 997eb8f069SAndrzej Hajda #define DSIM_SUB_PIX_FORMAT(x) (((x) & 0x7) << 8) 1007eb8f069SAndrzej Hajda #define DSIM_MAIN_PIX_FORMAT_MASK (0x7 << 12) 1017eb8f069SAndrzej Hajda #define DSIM_MAIN_PIX_FORMAT_RGB888 (0x7 << 12) 1027eb8f069SAndrzej Hajda #define DSIM_MAIN_PIX_FORMAT_RGB666 (0x6 << 12) 1037eb8f069SAndrzej Hajda #define DSIM_MAIN_PIX_FORMAT_RGB666_P (0x5 << 12) 1047eb8f069SAndrzej Hajda #define DSIM_MAIN_PIX_FORMAT_RGB565 (0x4 << 12) 1057eb8f069SAndrzej Hajda #define DSIM_SUB_VC (((x) & 0x3) << 16) 1067eb8f069SAndrzej Hajda #define DSIM_MAIN_VC (((x) & 0x3) << 18) 1077eb8f069SAndrzej Hajda #define DSIM_HSA_MODE (1 << 20) 1087eb8f069SAndrzej Hajda #define DSIM_HBP_MODE (1 << 21) 1097eb8f069SAndrzej Hajda #define DSIM_HFP_MODE (1 << 22) 1107eb8f069SAndrzej Hajda #define DSIM_HSE_MODE (1 << 23) 1117eb8f069SAndrzej Hajda #define DSIM_AUTO_MODE (1 << 24) 1127eb8f069SAndrzej Hajda #define DSIM_VIDEO_MODE (1 << 25) 1137eb8f069SAndrzej Hajda #define DSIM_BURST_MODE (1 << 26) 1147eb8f069SAndrzej Hajda #define DSIM_SYNC_INFORM (1 << 27) 1157eb8f069SAndrzej Hajda #define DSIM_EOT_DISABLE (1 << 28) 1167eb8f069SAndrzej Hajda #define DSIM_MFLUSH_VS (1 << 29) 11778d3a8c6SInki Dae /* This flag is valid only for exynos3250/3472/4415/5260/5430 */ 11878d3a8c6SInki Dae #define DSIM_CLKLANE_STOP (1 << 30) 1197eb8f069SAndrzej Hajda 1207eb8f069SAndrzej Hajda /* DSIM_ESCMODE */ 1217eb8f069SAndrzej Hajda #define DSIM_TX_TRIGGER_RST (1 << 4) 1227eb8f069SAndrzej Hajda #define DSIM_TX_LPDT_LP (1 << 6) 1237eb8f069SAndrzej Hajda #define DSIM_CMD_LPDT_LP (1 << 7) 1247eb8f069SAndrzej Hajda #define DSIM_FORCE_BTA (1 << 16) 1257eb8f069SAndrzej Hajda #define DSIM_FORCE_STOP_STATE (1 << 20) 1267eb8f069SAndrzej Hajda #define DSIM_STOP_STATE_CNT(x) (((x) & 0x7ff) << 21) 1277eb8f069SAndrzej Hajda #define DSIM_STOP_STATE_CNT_MASK (0x7ff << 21) 1287eb8f069SAndrzej Hajda 1297eb8f069SAndrzej Hajda /* DSIM_MDRESOL */ 1307eb8f069SAndrzej Hajda #define DSIM_MAIN_STAND_BY (1 << 31) 1317eb8f069SAndrzej Hajda #define DSIM_MAIN_VRESOL(x) (((x) & 0x7ff) << 16) 1327eb8f069SAndrzej Hajda #define DSIM_MAIN_HRESOL(x) (((x) & 0X7ff) << 0) 1337eb8f069SAndrzej Hajda 1347eb8f069SAndrzej Hajda /* DSIM_MVPORCH */ 1357eb8f069SAndrzej Hajda #define DSIM_CMD_ALLOW(x) ((x) << 28) 1367eb8f069SAndrzej Hajda #define DSIM_STABLE_VFP(x) ((x) << 16) 1377eb8f069SAndrzej Hajda #define DSIM_MAIN_VBP(x) ((x) << 0) 1387eb8f069SAndrzej Hajda #define DSIM_CMD_ALLOW_MASK (0xf << 28) 1397eb8f069SAndrzej Hajda #define DSIM_STABLE_VFP_MASK (0x7ff << 16) 1407eb8f069SAndrzej Hajda #define DSIM_MAIN_VBP_MASK (0x7ff << 0) 1417eb8f069SAndrzej Hajda 1427eb8f069SAndrzej Hajda /* DSIM_MHPORCH */ 1437eb8f069SAndrzej Hajda #define DSIM_MAIN_HFP(x) ((x) << 16) 1447eb8f069SAndrzej Hajda #define DSIM_MAIN_HBP(x) ((x) << 0) 1457eb8f069SAndrzej Hajda #define DSIM_MAIN_HFP_MASK ((0xffff) << 16) 1467eb8f069SAndrzej Hajda #define DSIM_MAIN_HBP_MASK ((0xffff) << 0) 1477eb8f069SAndrzej Hajda 1487eb8f069SAndrzej Hajda /* DSIM_MSYNC */ 1497eb8f069SAndrzej Hajda #define DSIM_MAIN_VSA(x) ((x) << 22) 1507eb8f069SAndrzej Hajda #define DSIM_MAIN_HSA(x) ((x) << 0) 1517eb8f069SAndrzej Hajda #define DSIM_MAIN_VSA_MASK ((0x3ff) << 22) 1527eb8f069SAndrzej Hajda #define DSIM_MAIN_HSA_MASK ((0xffff) << 0) 1537eb8f069SAndrzej Hajda 1547eb8f069SAndrzej Hajda /* DSIM_SDRESOL */ 1557eb8f069SAndrzej Hajda #define DSIM_SUB_STANDY(x) ((x) << 31) 1567eb8f069SAndrzej Hajda #define DSIM_SUB_VRESOL(x) ((x) << 16) 1577eb8f069SAndrzej Hajda #define DSIM_SUB_HRESOL(x) ((x) << 0) 1587eb8f069SAndrzej Hajda #define DSIM_SUB_STANDY_MASK ((0x1) << 31) 1597eb8f069SAndrzej Hajda #define DSIM_SUB_VRESOL_MASK ((0x7ff) << 16) 1607eb8f069SAndrzej Hajda #define DSIM_SUB_HRESOL_MASK ((0x7ff) << 0) 1617eb8f069SAndrzej Hajda 1627eb8f069SAndrzej Hajda /* DSIM_INTSRC */ 1637eb8f069SAndrzej Hajda #define DSIM_INT_PLL_STABLE (1 << 31) 1647eb8f069SAndrzej Hajda #define DSIM_INT_SW_RST_RELEASE (1 << 30) 1657eb8f069SAndrzej Hajda #define DSIM_INT_SFR_FIFO_EMPTY (1 << 29) 1667eb8f069SAndrzej Hajda #define DSIM_INT_BTA (1 << 25) 1677eb8f069SAndrzej Hajda #define DSIM_INT_FRAME_DONE (1 << 24) 1687eb8f069SAndrzej Hajda #define DSIM_INT_RX_TIMEOUT (1 << 21) 1697eb8f069SAndrzej Hajda #define DSIM_INT_BTA_TIMEOUT (1 << 20) 1707eb8f069SAndrzej Hajda #define DSIM_INT_RX_DONE (1 << 18) 1717eb8f069SAndrzej Hajda #define DSIM_INT_RX_TE (1 << 17) 1727eb8f069SAndrzej Hajda #define DSIM_INT_RX_ACK (1 << 16) 1737eb8f069SAndrzej Hajda #define DSIM_INT_RX_ECC_ERR (1 << 15) 1747eb8f069SAndrzej Hajda #define DSIM_INT_RX_CRC_ERR (1 << 14) 1757eb8f069SAndrzej Hajda 1767eb8f069SAndrzej Hajda /* DSIM_FIFOCTRL */ 1777eb8f069SAndrzej Hajda #define DSIM_RX_DATA_FULL (1 << 25) 1787eb8f069SAndrzej Hajda #define DSIM_RX_DATA_EMPTY (1 << 24) 1797eb8f069SAndrzej Hajda #define DSIM_SFR_HEADER_FULL (1 << 23) 1807eb8f069SAndrzej Hajda #define DSIM_SFR_HEADER_EMPTY (1 << 22) 1817eb8f069SAndrzej Hajda #define DSIM_SFR_PAYLOAD_FULL (1 << 21) 1827eb8f069SAndrzej Hajda #define DSIM_SFR_PAYLOAD_EMPTY (1 << 20) 1837eb8f069SAndrzej Hajda #define DSIM_I80_HEADER_FULL (1 << 19) 1847eb8f069SAndrzej Hajda #define DSIM_I80_HEADER_EMPTY (1 << 18) 1857eb8f069SAndrzej Hajda #define DSIM_I80_PAYLOAD_FULL (1 << 17) 1867eb8f069SAndrzej Hajda #define DSIM_I80_PAYLOAD_EMPTY (1 << 16) 1877eb8f069SAndrzej Hajda #define DSIM_SD_HEADER_FULL (1 << 15) 1887eb8f069SAndrzej Hajda #define DSIM_SD_HEADER_EMPTY (1 << 14) 1897eb8f069SAndrzej Hajda #define DSIM_SD_PAYLOAD_FULL (1 << 13) 1907eb8f069SAndrzej Hajda #define DSIM_SD_PAYLOAD_EMPTY (1 << 12) 1917eb8f069SAndrzej Hajda #define DSIM_MD_HEADER_FULL (1 << 11) 1927eb8f069SAndrzej Hajda #define DSIM_MD_HEADER_EMPTY (1 << 10) 1937eb8f069SAndrzej Hajda #define DSIM_MD_PAYLOAD_FULL (1 << 9) 1947eb8f069SAndrzej Hajda #define DSIM_MD_PAYLOAD_EMPTY (1 << 8) 1957eb8f069SAndrzej Hajda #define DSIM_RX_FIFO (1 << 4) 1967eb8f069SAndrzej Hajda #define DSIM_SFR_FIFO (1 << 3) 1977eb8f069SAndrzej Hajda #define DSIM_I80_FIFO (1 << 2) 1987eb8f069SAndrzej Hajda #define DSIM_SD_FIFO (1 << 1) 1997eb8f069SAndrzej Hajda #define DSIM_MD_FIFO (1 << 0) 2007eb8f069SAndrzej Hajda 2017eb8f069SAndrzej Hajda /* DSIM_PHYACCHR */ 2027eb8f069SAndrzej Hajda #define DSIM_AFC_EN (1 << 14) 2037eb8f069SAndrzej Hajda #define DSIM_AFC_CTL(x) (((x) & 0x7) << 5) 2047eb8f069SAndrzej Hajda 2057eb8f069SAndrzej Hajda /* DSIM_PLLCTRL */ 2067eb8f069SAndrzej Hajda #define DSIM_FREQ_BAND(x) ((x) << 24) 2077eb8f069SAndrzej Hajda #define DSIM_PLL_EN (1 << 23) 2087eb8f069SAndrzej Hajda #define DSIM_PLL_P(x) ((x) << 13) 2097eb8f069SAndrzej Hajda #define DSIM_PLL_M(x) ((x) << 4) 2107eb8f069SAndrzej Hajda #define DSIM_PLL_S(x) ((x) << 1) 2117eb8f069SAndrzej Hajda 2129a320415SYoungJun Cho /* DSIM_PHYCTRL */ 2139a320415SYoungJun Cho #define DSIM_PHYCTRL_ULPS_EXIT(x) (((x) & 0x1ff) << 0) 2149a320415SYoungJun Cho 2159a320415SYoungJun Cho /* DSIM_PHYTIMING */ 2169a320415SYoungJun Cho #define DSIM_PHYTIMING_LPX(x) ((x) << 8) 2179a320415SYoungJun Cho #define DSIM_PHYTIMING_HS_EXIT(x) ((x) << 0) 2189a320415SYoungJun Cho 2199a320415SYoungJun Cho /* DSIM_PHYTIMING1 */ 2209a320415SYoungJun Cho #define DSIM_PHYTIMING1_CLK_PREPARE(x) ((x) << 24) 2219a320415SYoungJun Cho #define DSIM_PHYTIMING1_CLK_ZERO(x) ((x) << 16) 2229a320415SYoungJun Cho #define DSIM_PHYTIMING1_CLK_POST(x) ((x) << 8) 2239a320415SYoungJun Cho #define DSIM_PHYTIMING1_CLK_TRAIL(x) ((x) << 0) 2249a320415SYoungJun Cho 2259a320415SYoungJun Cho /* DSIM_PHYTIMING2 */ 2269a320415SYoungJun Cho #define DSIM_PHYTIMING2_HS_PREPARE(x) ((x) << 16) 2279a320415SYoungJun Cho #define DSIM_PHYTIMING2_HS_ZERO(x) ((x) << 8) 2289a320415SYoungJun Cho #define DSIM_PHYTIMING2_HS_TRAIL(x) ((x) << 0) 2299a320415SYoungJun Cho 2307eb8f069SAndrzej Hajda #define DSI_MAX_BUS_WIDTH 4 2317eb8f069SAndrzej Hajda #define DSI_NUM_VIRTUAL_CHANNELS 4 2327eb8f069SAndrzej Hajda #define DSI_TX_FIFO_SIZE 2048 2337eb8f069SAndrzej Hajda #define DSI_RX_FIFO_SIZE 256 2347eb8f069SAndrzej Hajda #define DSI_XFER_TIMEOUT_MS 100 2357eb8f069SAndrzej Hajda #define DSI_RX_FIFO_EMPTY 0x30800002 2367eb8f069SAndrzej Hajda 2377eb8f069SAndrzej Hajda enum exynos_dsi_transfer_type { 2387eb8f069SAndrzej Hajda EXYNOS_DSI_TX, 2397eb8f069SAndrzej Hajda EXYNOS_DSI_RX, 2407eb8f069SAndrzej Hajda }; 2417eb8f069SAndrzej Hajda 2427eb8f069SAndrzej Hajda struct exynos_dsi_transfer { 2437eb8f069SAndrzej Hajda struct list_head list; 2447eb8f069SAndrzej Hajda struct completion completed; 2457eb8f069SAndrzej Hajda int result; 2467eb8f069SAndrzej Hajda u8 data_id; 2477eb8f069SAndrzej Hajda u8 data[2]; 2487eb8f069SAndrzej Hajda u16 flags; 2497eb8f069SAndrzej Hajda 2507eb8f069SAndrzej Hajda const u8 *tx_payload; 2517eb8f069SAndrzej Hajda u16 tx_len; 2527eb8f069SAndrzej Hajda u16 tx_done; 2537eb8f069SAndrzej Hajda 2547eb8f069SAndrzej Hajda u8 *rx_payload; 2557eb8f069SAndrzej Hajda u16 rx_len; 2567eb8f069SAndrzej Hajda u16 rx_done; 2577eb8f069SAndrzej Hajda }; 2587eb8f069SAndrzej Hajda 2597eb8f069SAndrzej Hajda #define DSIM_STATE_ENABLED BIT(0) 2607eb8f069SAndrzej Hajda #define DSIM_STATE_INITIALIZED BIT(1) 2617eb8f069SAndrzej Hajda #define DSIM_STATE_CMD_LPM BIT(2) 2627eb8f069SAndrzej Hajda 2639a320415SYoungJun Cho struct exynos_dsi_driver_data { 2649a320415SYoungJun Cho unsigned int plltmr_reg; 2659a320415SYoungJun Cho 2669a320415SYoungJun Cho unsigned int has_freqband:1; 26778d3a8c6SInki Dae unsigned int has_clklane_stop:1; 2689a320415SYoungJun Cho }; 2699a320415SYoungJun Cho 2707eb8f069SAndrzej Hajda struct exynos_dsi { 2712900c69cSAndrzej Hajda struct exynos_drm_display display; 2727eb8f069SAndrzej Hajda struct mipi_dsi_host dsi_host; 2737eb8f069SAndrzej Hajda struct drm_connector connector; 2747eb8f069SAndrzej Hajda struct drm_encoder *encoder; 2757eb8f069SAndrzej Hajda struct device_node *panel_node; 2767eb8f069SAndrzej Hajda struct drm_panel *panel; 2777eb8f069SAndrzej Hajda struct device *dev; 2787eb8f069SAndrzej Hajda 2797eb8f069SAndrzej Hajda void __iomem *reg_base; 2807eb8f069SAndrzej Hajda struct phy *phy; 2817eb8f069SAndrzej Hajda struct clk *pll_clk; 2827eb8f069SAndrzej Hajda struct clk *bus_clk; 2837eb8f069SAndrzej Hajda struct regulator_bulk_data supplies[2]; 2847eb8f069SAndrzej Hajda int irq; 285e17ddeccSYoungJun Cho int te_gpio; 2867eb8f069SAndrzej Hajda 2877eb8f069SAndrzej Hajda u32 pll_clk_rate; 2887eb8f069SAndrzej Hajda u32 burst_clk_rate; 2897eb8f069SAndrzej Hajda u32 esc_clk_rate; 2907eb8f069SAndrzej Hajda u32 lanes; 2917eb8f069SAndrzej Hajda u32 mode_flags; 2927eb8f069SAndrzej Hajda u32 format; 2937eb8f069SAndrzej Hajda struct videomode vm; 2947eb8f069SAndrzej Hajda 2957eb8f069SAndrzej Hajda int state; 2967eb8f069SAndrzej Hajda struct drm_property *brightness; 2977eb8f069SAndrzej Hajda struct completion completed; 2987eb8f069SAndrzej Hajda 2997eb8f069SAndrzej Hajda spinlock_t transfer_lock; /* protects transfer_list */ 3007eb8f069SAndrzej Hajda struct list_head transfer_list; 3019a320415SYoungJun Cho 3029a320415SYoungJun Cho struct exynos_dsi_driver_data *driver_data; 3037eb8f069SAndrzej Hajda }; 3047eb8f069SAndrzej Hajda 3057eb8f069SAndrzej Hajda #define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host) 3067eb8f069SAndrzej Hajda #define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector) 3077eb8f069SAndrzej Hajda 308473462a1SInki Dae static struct exynos_dsi_driver_data exynos3_dsi_driver_data = { 309473462a1SInki Dae .plltmr_reg = 0x50, 310473462a1SInki Dae .has_freqband = 1, 311473462a1SInki Dae .has_clklane_stop = 1, 312473462a1SInki Dae }; 313473462a1SInki Dae 3149a320415SYoungJun Cho static struct exynos_dsi_driver_data exynos4_dsi_driver_data = { 3159a320415SYoungJun Cho .plltmr_reg = 0x50, 3169a320415SYoungJun Cho .has_freqband = 1, 31778d3a8c6SInki Dae .has_clklane_stop = 1, 3189a320415SYoungJun Cho }; 3199a320415SYoungJun Cho 3209a320415SYoungJun Cho static struct exynos_dsi_driver_data exynos5_dsi_driver_data = { 3219a320415SYoungJun Cho .plltmr_reg = 0x58, 3229a320415SYoungJun Cho }; 3239a320415SYoungJun Cho 3249a320415SYoungJun Cho static struct of_device_id exynos_dsi_of_match[] = { 325473462a1SInki Dae { .compatible = "samsung,exynos3250-mipi-dsi", 326473462a1SInki Dae .data = &exynos3_dsi_driver_data }, 3279a320415SYoungJun Cho { .compatible = "samsung,exynos4210-mipi-dsi", 3289a320415SYoungJun Cho .data = &exynos4_dsi_driver_data }, 3299a320415SYoungJun Cho { .compatible = "samsung,exynos5410-mipi-dsi", 3309a320415SYoungJun Cho .data = &exynos5_dsi_driver_data }, 3319a320415SYoungJun Cho { } 3329a320415SYoungJun Cho }; 3339a320415SYoungJun Cho 3349a320415SYoungJun Cho static inline struct exynos_dsi_driver_data *exynos_dsi_get_driver_data( 3359a320415SYoungJun Cho struct platform_device *pdev) 3369a320415SYoungJun Cho { 3379a320415SYoungJun Cho const struct of_device_id *of_id = 3389a320415SYoungJun Cho of_match_device(exynos_dsi_of_match, &pdev->dev); 3399a320415SYoungJun Cho 3409a320415SYoungJun Cho return (struct exynos_dsi_driver_data *)of_id->data; 3419a320415SYoungJun Cho } 3429a320415SYoungJun Cho 3437eb8f069SAndrzej Hajda static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi) 3447eb8f069SAndrzej Hajda { 3457eb8f069SAndrzej Hajda if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300))) 3467eb8f069SAndrzej Hajda return; 3477eb8f069SAndrzej Hajda 3487eb8f069SAndrzej Hajda dev_err(dsi->dev, "timeout waiting for reset\n"); 3497eb8f069SAndrzej Hajda } 3507eb8f069SAndrzej Hajda 3517eb8f069SAndrzej Hajda static void exynos_dsi_reset(struct exynos_dsi *dsi) 3527eb8f069SAndrzej Hajda { 3537eb8f069SAndrzej Hajda reinit_completion(&dsi->completed); 3547eb8f069SAndrzej Hajda writel(DSIM_SWRST, dsi->reg_base + DSIM_SWRST_REG); 3557eb8f069SAndrzej Hajda } 3567eb8f069SAndrzej Hajda 3577eb8f069SAndrzej Hajda #ifndef MHZ 3587eb8f069SAndrzej Hajda #define MHZ (1000*1000) 3597eb8f069SAndrzej Hajda #endif 3607eb8f069SAndrzej Hajda 3617eb8f069SAndrzej Hajda static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi, 3627eb8f069SAndrzej Hajda unsigned long fin, unsigned long fout, u8 *p, u16 *m, u8 *s) 3637eb8f069SAndrzej Hajda { 3647eb8f069SAndrzej Hajda unsigned long best_freq = 0; 3657eb8f069SAndrzej Hajda u32 min_delta = 0xffffffff; 3667eb8f069SAndrzej Hajda u8 p_min, p_max; 3677eb8f069SAndrzej Hajda u8 _p, uninitialized_var(best_p); 3687eb8f069SAndrzej Hajda u16 _m, uninitialized_var(best_m); 3697eb8f069SAndrzej Hajda u8 _s, uninitialized_var(best_s); 3707eb8f069SAndrzej Hajda 3717eb8f069SAndrzej Hajda p_min = DIV_ROUND_UP(fin, (12 * MHZ)); 3727eb8f069SAndrzej Hajda p_max = fin / (6 * MHZ); 3737eb8f069SAndrzej Hajda 3747eb8f069SAndrzej Hajda for (_p = p_min; _p <= p_max; ++_p) { 3757eb8f069SAndrzej Hajda for (_s = 0; _s <= 5; ++_s) { 3767eb8f069SAndrzej Hajda u64 tmp; 3777eb8f069SAndrzej Hajda u32 delta; 3787eb8f069SAndrzej Hajda 3797eb8f069SAndrzej Hajda tmp = (u64)fout * (_p << _s); 3807eb8f069SAndrzej Hajda do_div(tmp, fin); 3817eb8f069SAndrzej Hajda _m = tmp; 3827eb8f069SAndrzej Hajda if (_m < 41 || _m > 125) 3837eb8f069SAndrzej Hajda continue; 3847eb8f069SAndrzej Hajda 3857eb8f069SAndrzej Hajda tmp = (u64)_m * fin; 3867eb8f069SAndrzej Hajda do_div(tmp, _p); 3877eb8f069SAndrzej Hajda if (tmp < 500 * MHZ || tmp > 1000 * MHZ) 3887eb8f069SAndrzej Hajda continue; 3897eb8f069SAndrzej Hajda 3907eb8f069SAndrzej Hajda tmp = (u64)_m * fin; 3917eb8f069SAndrzej Hajda do_div(tmp, _p << _s); 3927eb8f069SAndrzej Hajda 3937eb8f069SAndrzej Hajda delta = abs(fout - tmp); 3947eb8f069SAndrzej Hajda if (delta < min_delta) { 3957eb8f069SAndrzej Hajda best_p = _p; 3967eb8f069SAndrzej Hajda best_m = _m; 3977eb8f069SAndrzej Hajda best_s = _s; 3987eb8f069SAndrzej Hajda min_delta = delta; 3997eb8f069SAndrzej Hajda best_freq = tmp; 4007eb8f069SAndrzej Hajda } 4017eb8f069SAndrzej Hajda } 4027eb8f069SAndrzej Hajda } 4037eb8f069SAndrzej Hajda 4047eb8f069SAndrzej Hajda if (best_freq) { 4057eb8f069SAndrzej Hajda *p = best_p; 4067eb8f069SAndrzej Hajda *m = best_m; 4077eb8f069SAndrzej Hajda *s = best_s; 4087eb8f069SAndrzej Hajda } 4097eb8f069SAndrzej Hajda 4107eb8f069SAndrzej Hajda return best_freq; 4117eb8f069SAndrzej Hajda } 4127eb8f069SAndrzej Hajda 4137eb8f069SAndrzej Hajda static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi, 4147eb8f069SAndrzej Hajda unsigned long freq) 4157eb8f069SAndrzej Hajda { 4169a320415SYoungJun Cho struct exynos_dsi_driver_data *driver_data = dsi->driver_data; 4177eb8f069SAndrzej Hajda unsigned long fin, fout; 4189a320415SYoungJun Cho int timeout; 4197eb8f069SAndrzej Hajda u8 p, s; 4207eb8f069SAndrzej Hajda u16 m; 4217eb8f069SAndrzej Hajda u32 reg; 4227eb8f069SAndrzej Hajda 4237eb8f069SAndrzej Hajda clk_set_rate(dsi->pll_clk, dsi->pll_clk_rate); 4247eb8f069SAndrzej Hajda 4257eb8f069SAndrzej Hajda fin = clk_get_rate(dsi->pll_clk); 4267eb8f069SAndrzej Hajda if (!fin) { 4277eb8f069SAndrzej Hajda dev_err(dsi->dev, "failed to get PLL clock frequency\n"); 4287eb8f069SAndrzej Hajda return 0; 4297eb8f069SAndrzej Hajda } 4307eb8f069SAndrzej Hajda 4317eb8f069SAndrzej Hajda dev_dbg(dsi->dev, "PLL input frequency: %lu\n", fin); 4327eb8f069SAndrzej Hajda 4337eb8f069SAndrzej Hajda fout = exynos_dsi_pll_find_pms(dsi, fin, freq, &p, &m, &s); 4347eb8f069SAndrzej Hajda if (!fout) { 4357eb8f069SAndrzej Hajda dev_err(dsi->dev, 4367eb8f069SAndrzej Hajda "failed to find PLL PMS for requested frequency\n"); 4378525b5ecSYoungJun Cho return 0; 4387eb8f069SAndrzej Hajda } 4399a320415SYoungJun Cho dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s); 4409a320415SYoungJun Cho 4419a320415SYoungJun Cho writel(500, dsi->reg_base + driver_data->plltmr_reg); 4429a320415SYoungJun Cho 4439a320415SYoungJun Cho reg = DSIM_PLL_EN | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s); 4449a320415SYoungJun Cho 4459a320415SYoungJun Cho if (driver_data->has_freqband) { 4469a320415SYoungJun Cho static const unsigned long freq_bands[] = { 4479a320415SYoungJun Cho 100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ, 4489a320415SYoungJun Cho 270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ, 4499a320415SYoungJun Cho 510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ, 4509a320415SYoungJun Cho 770 * MHZ, 870 * MHZ, 950 * MHZ, 4519a320415SYoungJun Cho }; 4529a320415SYoungJun Cho int band; 4537eb8f069SAndrzej Hajda 4547eb8f069SAndrzej Hajda for (band = 0; band < ARRAY_SIZE(freq_bands); ++band) 4557eb8f069SAndrzej Hajda if (fout < freq_bands[band]) 4567eb8f069SAndrzej Hajda break; 4577eb8f069SAndrzej Hajda 4589a320415SYoungJun Cho dev_dbg(dsi->dev, "band %d\n", band); 4597eb8f069SAndrzej Hajda 4609a320415SYoungJun Cho reg |= DSIM_FREQ_BAND(band); 4619a320415SYoungJun Cho } 4627eb8f069SAndrzej Hajda 4637eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG); 4647eb8f069SAndrzej Hajda 4657eb8f069SAndrzej Hajda timeout = 1000; 4667eb8f069SAndrzej Hajda do { 4677eb8f069SAndrzej Hajda if (timeout-- == 0) { 4687eb8f069SAndrzej Hajda dev_err(dsi->dev, "PLL failed to stabilize\n"); 4698525b5ecSYoungJun Cho return 0; 4707eb8f069SAndrzej Hajda } 4717eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_STATUS_REG); 4727eb8f069SAndrzej Hajda } while ((reg & DSIM_PLL_STABLE) == 0); 4737eb8f069SAndrzej Hajda 4747eb8f069SAndrzej Hajda return fout; 4757eb8f069SAndrzej Hajda } 4767eb8f069SAndrzej Hajda 4777eb8f069SAndrzej Hajda static int exynos_dsi_enable_clock(struct exynos_dsi *dsi) 4787eb8f069SAndrzej Hajda { 4797eb8f069SAndrzej Hajda unsigned long hs_clk, byte_clk, esc_clk; 4807eb8f069SAndrzej Hajda unsigned long esc_div; 4817eb8f069SAndrzej Hajda u32 reg; 4827eb8f069SAndrzej Hajda 4837eb8f069SAndrzej Hajda hs_clk = exynos_dsi_set_pll(dsi, dsi->burst_clk_rate); 4847eb8f069SAndrzej Hajda if (!hs_clk) { 4857eb8f069SAndrzej Hajda dev_err(dsi->dev, "failed to configure DSI PLL\n"); 4867eb8f069SAndrzej Hajda return -EFAULT; 4877eb8f069SAndrzej Hajda } 4887eb8f069SAndrzej Hajda 4897eb8f069SAndrzej Hajda byte_clk = hs_clk / 8; 4907eb8f069SAndrzej Hajda esc_div = DIV_ROUND_UP(byte_clk, dsi->esc_clk_rate); 4917eb8f069SAndrzej Hajda esc_clk = byte_clk / esc_div; 4927eb8f069SAndrzej Hajda 4937eb8f069SAndrzej Hajda if (esc_clk > 20 * MHZ) { 4947eb8f069SAndrzej Hajda ++esc_div; 4957eb8f069SAndrzej Hajda esc_clk = byte_clk / esc_div; 4967eb8f069SAndrzej Hajda } 4977eb8f069SAndrzej Hajda 4987eb8f069SAndrzej Hajda dev_dbg(dsi->dev, "hs_clk = %lu, byte_clk = %lu, esc_clk = %lu\n", 4997eb8f069SAndrzej Hajda hs_clk, byte_clk, esc_clk); 5007eb8f069SAndrzej Hajda 5017eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_CLKCTRL_REG); 5027eb8f069SAndrzej Hajda reg &= ~(DSIM_ESC_PRESCALER_MASK | DSIM_LANE_ESC_CLK_EN_CLK 5037eb8f069SAndrzej Hajda | DSIM_LANE_ESC_CLK_EN_DATA_MASK | DSIM_PLL_BYPASS 5047eb8f069SAndrzej Hajda | DSIM_BYTE_CLK_SRC_MASK); 5057eb8f069SAndrzej Hajda reg |= DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN 5067eb8f069SAndrzej Hajda | DSIM_ESC_PRESCALER(esc_div) 5077eb8f069SAndrzej Hajda | DSIM_LANE_ESC_CLK_EN_CLK 5087eb8f069SAndrzej Hajda | DSIM_LANE_ESC_CLK_EN_DATA(BIT(dsi->lanes) - 1) 5097eb8f069SAndrzej Hajda | DSIM_BYTE_CLK_SRC(0) 5107eb8f069SAndrzej Hajda | DSIM_TX_REQUEST_HSCLK; 5117eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_CLKCTRL_REG); 5127eb8f069SAndrzej Hajda 5137eb8f069SAndrzej Hajda return 0; 5147eb8f069SAndrzej Hajda } 5157eb8f069SAndrzej Hajda 5169a320415SYoungJun Cho static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi) 5179a320415SYoungJun Cho { 5189a320415SYoungJun Cho struct exynos_dsi_driver_data *driver_data = dsi->driver_data; 5199a320415SYoungJun Cho u32 reg; 5209a320415SYoungJun Cho 5219a320415SYoungJun Cho if (driver_data->has_freqband) 5229a320415SYoungJun Cho return; 5239a320415SYoungJun Cho 5249a320415SYoungJun Cho /* B D-PHY: D-PHY Master & Slave Analog Block control */ 5259a320415SYoungJun Cho reg = DSIM_PHYCTRL_ULPS_EXIT(0x0af); 5269a320415SYoungJun Cho writel(reg, dsi->reg_base + DSIM_PHYCTRL_REG); 5279a320415SYoungJun Cho 5289a320415SYoungJun Cho /* 5299a320415SYoungJun Cho * T LPX: Transmitted length of any Low-Power state period 5309a320415SYoungJun Cho * T HS-EXIT: Time that the transmitter drives LP-11 following a HS 5319a320415SYoungJun Cho * burst 5329a320415SYoungJun Cho */ 5339a320415SYoungJun Cho reg = DSIM_PHYTIMING_LPX(0x06) | DSIM_PHYTIMING_HS_EXIT(0x0b); 5349a320415SYoungJun Cho writel(reg, dsi->reg_base + DSIM_PHYTIMING_REG); 5359a320415SYoungJun Cho 5369a320415SYoungJun Cho /* 5379a320415SYoungJun Cho * T CLK-PREPARE: Time that the transmitter drives the Clock Lane LP-00 5389a320415SYoungJun Cho * Line state immediately before the HS-0 Line state starting the 5399a320415SYoungJun Cho * HS transmission 5409a320415SYoungJun Cho * T CLK-ZERO: Time that the transmitter drives the HS-0 state prior to 5419a320415SYoungJun Cho * transmitting the Clock. 5429a320415SYoungJun Cho * T CLK_POST: Time that the transmitter continues to send HS clock 5439a320415SYoungJun Cho * after the last associated Data Lane has transitioned to LP Mode 5449a320415SYoungJun Cho * Interval is defined as the period from the end of T HS-TRAIL to 5459a320415SYoungJun Cho * the beginning of T CLK-TRAIL 5469a320415SYoungJun Cho * T CLK-TRAIL: Time that the transmitter drives the HS-0 state after 5479a320415SYoungJun Cho * the last payload clock bit of a HS transmission burst 5489a320415SYoungJun Cho */ 5499a320415SYoungJun Cho reg = DSIM_PHYTIMING1_CLK_PREPARE(0x07) | 5509a320415SYoungJun Cho DSIM_PHYTIMING1_CLK_ZERO(0x27) | 5519a320415SYoungJun Cho DSIM_PHYTIMING1_CLK_POST(0x0d) | 5529a320415SYoungJun Cho DSIM_PHYTIMING1_CLK_TRAIL(0x08); 5539a320415SYoungJun Cho writel(reg, dsi->reg_base + DSIM_PHYTIMING1_REG); 5549a320415SYoungJun Cho 5559a320415SYoungJun Cho /* 5569a320415SYoungJun Cho * T HS-PREPARE: Time that the transmitter drives the Data Lane LP-00 5579a320415SYoungJun Cho * Line state immediately before the HS-0 Line state starting the 5589a320415SYoungJun Cho * HS transmission 5599a320415SYoungJun Cho * T HS-ZERO: Time that the transmitter drives the HS-0 state prior to 5609a320415SYoungJun Cho * transmitting the Sync sequence. 5619a320415SYoungJun Cho * T HS-TRAIL: Time that the transmitter drives the flipped differential 5629a320415SYoungJun Cho * state after last payload data bit of a HS transmission burst 5639a320415SYoungJun Cho */ 5649a320415SYoungJun Cho reg = DSIM_PHYTIMING2_HS_PREPARE(0x09) | DSIM_PHYTIMING2_HS_ZERO(0x0d) | 5659a320415SYoungJun Cho DSIM_PHYTIMING2_HS_TRAIL(0x0b); 5669a320415SYoungJun Cho writel(reg, dsi->reg_base + DSIM_PHYTIMING2_REG); 5679a320415SYoungJun Cho } 5689a320415SYoungJun Cho 5697eb8f069SAndrzej Hajda static void exynos_dsi_disable_clock(struct exynos_dsi *dsi) 5707eb8f069SAndrzej Hajda { 5717eb8f069SAndrzej Hajda u32 reg; 5727eb8f069SAndrzej Hajda 5737eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_CLKCTRL_REG); 5747eb8f069SAndrzej Hajda reg &= ~(DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK 5757eb8f069SAndrzej Hajda | DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN); 5767eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_CLKCTRL_REG); 5777eb8f069SAndrzej Hajda 5787eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_PLLCTRL_REG); 5797eb8f069SAndrzej Hajda reg &= ~DSIM_PLL_EN; 5807eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG); 5817eb8f069SAndrzej Hajda } 5827eb8f069SAndrzej Hajda 5837eb8f069SAndrzej Hajda static int exynos_dsi_init_link(struct exynos_dsi *dsi) 5847eb8f069SAndrzej Hajda { 58578d3a8c6SInki Dae struct exynos_dsi_driver_data *driver_data = dsi->driver_data; 5867eb8f069SAndrzej Hajda int timeout; 5877eb8f069SAndrzej Hajda u32 reg; 5887eb8f069SAndrzej Hajda u32 lanes_mask; 5897eb8f069SAndrzej Hajda 5907eb8f069SAndrzej Hajda /* Initialize FIFO pointers */ 5917eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_FIFOCTRL_REG); 5927eb8f069SAndrzej Hajda reg &= ~0x1f; 5937eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_FIFOCTRL_REG); 5947eb8f069SAndrzej Hajda 5957eb8f069SAndrzej Hajda usleep_range(9000, 11000); 5967eb8f069SAndrzej Hajda 5977eb8f069SAndrzej Hajda reg |= 0x1f; 5987eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_FIFOCTRL_REG); 5997eb8f069SAndrzej Hajda 6007eb8f069SAndrzej Hajda usleep_range(9000, 11000); 6017eb8f069SAndrzej Hajda 6027eb8f069SAndrzej Hajda /* DSI configuration */ 6037eb8f069SAndrzej Hajda reg = 0; 6047eb8f069SAndrzej Hajda 6052f36e33aSYoungJun Cho /* 6062f36e33aSYoungJun Cho * The first bit of mode_flags specifies display configuration. 6072f36e33aSYoungJun Cho * If this bit is set[= MIPI_DSI_MODE_VIDEO], dsi will support video 6082f36e33aSYoungJun Cho * mode, otherwise it will support command mode. 6092f36e33aSYoungJun Cho */ 6107eb8f069SAndrzej Hajda if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { 6117eb8f069SAndrzej Hajda reg |= DSIM_VIDEO_MODE; 6127eb8f069SAndrzej Hajda 6132f36e33aSYoungJun Cho /* 6142f36e33aSYoungJun Cho * The user manual describes that following bits are ignored in 6152f36e33aSYoungJun Cho * command mode. 6162f36e33aSYoungJun Cho */ 6177eb8f069SAndrzej Hajda if (!(dsi->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH)) 6187eb8f069SAndrzej Hajda reg |= DSIM_MFLUSH_VS; 6197eb8f069SAndrzej Hajda if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) 6207eb8f069SAndrzej Hajda reg |= DSIM_SYNC_INFORM; 6217eb8f069SAndrzej Hajda if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) 6227eb8f069SAndrzej Hajda reg |= DSIM_BURST_MODE; 6237eb8f069SAndrzej Hajda if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT) 6247eb8f069SAndrzej Hajda reg |= DSIM_AUTO_MODE; 6257eb8f069SAndrzej Hajda if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSE) 6267eb8f069SAndrzej Hajda reg |= DSIM_HSE_MODE; 6277eb8f069SAndrzej Hajda if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HFP)) 6287eb8f069SAndrzej Hajda reg |= DSIM_HFP_MODE; 6297eb8f069SAndrzej Hajda if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HBP)) 6307eb8f069SAndrzej Hajda reg |= DSIM_HBP_MODE; 6317eb8f069SAndrzej Hajda if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSA)) 6327eb8f069SAndrzej Hajda reg |= DSIM_HSA_MODE; 6337eb8f069SAndrzej Hajda } 6347eb8f069SAndrzej Hajda 6352f36e33aSYoungJun Cho if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET)) 6362f36e33aSYoungJun Cho reg |= DSIM_EOT_DISABLE; 6372f36e33aSYoungJun Cho 6387eb8f069SAndrzej Hajda switch (dsi->format) { 6397eb8f069SAndrzej Hajda case MIPI_DSI_FMT_RGB888: 6407eb8f069SAndrzej Hajda reg |= DSIM_MAIN_PIX_FORMAT_RGB888; 6417eb8f069SAndrzej Hajda break; 6427eb8f069SAndrzej Hajda case MIPI_DSI_FMT_RGB666: 6437eb8f069SAndrzej Hajda reg |= DSIM_MAIN_PIX_FORMAT_RGB666; 6447eb8f069SAndrzej Hajda break; 6457eb8f069SAndrzej Hajda case MIPI_DSI_FMT_RGB666_PACKED: 6467eb8f069SAndrzej Hajda reg |= DSIM_MAIN_PIX_FORMAT_RGB666_P; 6477eb8f069SAndrzej Hajda break; 6487eb8f069SAndrzej Hajda case MIPI_DSI_FMT_RGB565: 6497eb8f069SAndrzej Hajda reg |= DSIM_MAIN_PIX_FORMAT_RGB565; 6507eb8f069SAndrzej Hajda break; 6517eb8f069SAndrzej Hajda default: 6527eb8f069SAndrzej Hajda dev_err(dsi->dev, "invalid pixel format\n"); 6537eb8f069SAndrzej Hajda return -EINVAL; 6547eb8f069SAndrzej Hajda } 6557eb8f069SAndrzej Hajda 6567eb8f069SAndrzej Hajda reg |= DSIM_NUM_OF_DATA_LANE(dsi->lanes - 1); 6577eb8f069SAndrzej Hajda 6587eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_CONFIG_REG); 6597eb8f069SAndrzej Hajda 6607eb8f069SAndrzej Hajda reg |= DSIM_LANE_EN_CLK; 6617eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_CONFIG_REG); 6627eb8f069SAndrzej Hajda 6637eb8f069SAndrzej Hajda lanes_mask = BIT(dsi->lanes) - 1; 6647eb8f069SAndrzej Hajda reg |= DSIM_LANE_EN(lanes_mask); 6657eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_CONFIG_REG); 6667eb8f069SAndrzej Hajda 66778d3a8c6SInki Dae /* 66878d3a8c6SInki Dae * Use non-continuous clock mode if the periparal wants and 66978d3a8c6SInki Dae * host controller supports 67078d3a8c6SInki Dae * 67178d3a8c6SInki Dae * In non-continous clock mode, host controller will turn off 67278d3a8c6SInki Dae * the HS clock between high-speed transmissions to reduce 67378d3a8c6SInki Dae * power consumption. 67478d3a8c6SInki Dae */ 67578d3a8c6SInki Dae if (driver_data->has_clklane_stop && 67678d3a8c6SInki Dae dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) { 67778d3a8c6SInki Dae reg |= DSIM_CLKLANE_STOP; 67878d3a8c6SInki Dae writel(reg, dsi->reg_base + DSIM_CONFIG_REG); 67978d3a8c6SInki Dae } 68078d3a8c6SInki Dae 6817eb8f069SAndrzej Hajda /* Check clock and data lane state are stop state */ 6827eb8f069SAndrzej Hajda timeout = 100; 6837eb8f069SAndrzej Hajda do { 6847eb8f069SAndrzej Hajda if (timeout-- == 0) { 6857eb8f069SAndrzej Hajda dev_err(dsi->dev, "waiting for bus lanes timed out\n"); 6867eb8f069SAndrzej Hajda return -EFAULT; 6877eb8f069SAndrzej Hajda } 6887eb8f069SAndrzej Hajda 6897eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_STATUS_REG); 6907eb8f069SAndrzej Hajda if ((reg & DSIM_STOP_STATE_DAT(lanes_mask)) 6917eb8f069SAndrzej Hajda != DSIM_STOP_STATE_DAT(lanes_mask)) 6927eb8f069SAndrzej Hajda continue; 6937eb8f069SAndrzej Hajda } while (!(reg & (DSIM_STOP_STATE_CLK | DSIM_TX_READY_HS_CLK))); 6947eb8f069SAndrzej Hajda 6957eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_ESCMODE_REG); 6967eb8f069SAndrzej Hajda reg &= ~DSIM_STOP_STATE_CNT_MASK; 6977eb8f069SAndrzej Hajda reg |= DSIM_STOP_STATE_CNT(0xf); 6987eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_ESCMODE_REG); 6997eb8f069SAndrzej Hajda 7007eb8f069SAndrzej Hajda reg = DSIM_BTA_TIMEOUT(0xff) | DSIM_LPDR_TIMEOUT(0xffff); 7017eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_TIMEOUT_REG); 7027eb8f069SAndrzej Hajda 7037eb8f069SAndrzej Hajda return 0; 7047eb8f069SAndrzej Hajda } 7057eb8f069SAndrzej Hajda 7067eb8f069SAndrzej Hajda static void exynos_dsi_set_display_mode(struct exynos_dsi *dsi) 7077eb8f069SAndrzej Hajda { 7087eb8f069SAndrzej Hajda struct videomode *vm = &dsi->vm; 7097eb8f069SAndrzej Hajda u32 reg; 7107eb8f069SAndrzej Hajda 7117eb8f069SAndrzej Hajda if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { 7127eb8f069SAndrzej Hajda reg = DSIM_CMD_ALLOW(0xf) 7137eb8f069SAndrzej Hajda | DSIM_STABLE_VFP(vm->vfront_porch) 7147eb8f069SAndrzej Hajda | DSIM_MAIN_VBP(vm->vback_porch); 7157eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_MVPORCH_REG); 7167eb8f069SAndrzej Hajda 7177eb8f069SAndrzej Hajda reg = DSIM_MAIN_HFP(vm->hfront_porch) 7187eb8f069SAndrzej Hajda | DSIM_MAIN_HBP(vm->hback_porch); 7197eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_MHPORCH_REG); 7207eb8f069SAndrzej Hajda 7217eb8f069SAndrzej Hajda reg = DSIM_MAIN_VSA(vm->vsync_len) 7227eb8f069SAndrzej Hajda | DSIM_MAIN_HSA(vm->hsync_len); 7237eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_MSYNC_REG); 7247eb8f069SAndrzej Hajda } 7257eb8f069SAndrzej Hajda 7267eb8f069SAndrzej Hajda reg = DSIM_MAIN_HRESOL(vm->hactive) | DSIM_MAIN_VRESOL(vm->vactive); 7277eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_MDRESOL_REG); 7287eb8f069SAndrzej Hajda 7297eb8f069SAndrzej Hajda dev_dbg(dsi->dev, "LCD size = %dx%d\n", vm->hactive, vm->vactive); 7307eb8f069SAndrzej Hajda } 7317eb8f069SAndrzej Hajda 7327eb8f069SAndrzej Hajda static void exynos_dsi_set_display_enable(struct exynos_dsi *dsi, bool enable) 7337eb8f069SAndrzej Hajda { 7347eb8f069SAndrzej Hajda u32 reg; 7357eb8f069SAndrzej Hajda 7367eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_MDRESOL_REG); 7377eb8f069SAndrzej Hajda if (enable) 7387eb8f069SAndrzej Hajda reg |= DSIM_MAIN_STAND_BY; 7397eb8f069SAndrzej Hajda else 7407eb8f069SAndrzej Hajda reg &= ~DSIM_MAIN_STAND_BY; 7417eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_MDRESOL_REG); 7427eb8f069SAndrzej Hajda } 7437eb8f069SAndrzej Hajda 7447eb8f069SAndrzej Hajda static int exynos_dsi_wait_for_hdr_fifo(struct exynos_dsi *dsi) 7457eb8f069SAndrzej Hajda { 7467eb8f069SAndrzej Hajda int timeout = 2000; 7477eb8f069SAndrzej Hajda 7487eb8f069SAndrzej Hajda do { 7497eb8f069SAndrzej Hajda u32 reg = readl(dsi->reg_base + DSIM_FIFOCTRL_REG); 7507eb8f069SAndrzej Hajda 7517eb8f069SAndrzej Hajda if (!(reg & DSIM_SFR_HEADER_FULL)) 7527eb8f069SAndrzej Hajda return 0; 7537eb8f069SAndrzej Hajda 7547eb8f069SAndrzej Hajda if (!cond_resched()) 7557eb8f069SAndrzej Hajda usleep_range(950, 1050); 7567eb8f069SAndrzej Hajda } while (--timeout); 7577eb8f069SAndrzej Hajda 7587eb8f069SAndrzej Hajda return -ETIMEDOUT; 7597eb8f069SAndrzej Hajda } 7607eb8f069SAndrzej Hajda 7617eb8f069SAndrzej Hajda static void exynos_dsi_set_cmd_lpm(struct exynos_dsi *dsi, bool lpm) 7627eb8f069SAndrzej Hajda { 7637eb8f069SAndrzej Hajda u32 v = readl(dsi->reg_base + DSIM_ESCMODE_REG); 7647eb8f069SAndrzej Hajda 7657eb8f069SAndrzej Hajda if (lpm) 7667eb8f069SAndrzej Hajda v |= DSIM_CMD_LPDT_LP; 7677eb8f069SAndrzej Hajda else 7687eb8f069SAndrzej Hajda v &= ~DSIM_CMD_LPDT_LP; 7697eb8f069SAndrzej Hajda 7707eb8f069SAndrzej Hajda writel(v, dsi->reg_base + DSIM_ESCMODE_REG); 7717eb8f069SAndrzej Hajda } 7727eb8f069SAndrzej Hajda 7737eb8f069SAndrzej Hajda static void exynos_dsi_force_bta(struct exynos_dsi *dsi) 7747eb8f069SAndrzej Hajda { 7757eb8f069SAndrzej Hajda u32 v = readl(dsi->reg_base + DSIM_ESCMODE_REG); 7767eb8f069SAndrzej Hajda 7777eb8f069SAndrzej Hajda v |= DSIM_FORCE_BTA; 7787eb8f069SAndrzej Hajda writel(v, dsi->reg_base + DSIM_ESCMODE_REG); 7797eb8f069SAndrzej Hajda } 7807eb8f069SAndrzej Hajda 7817eb8f069SAndrzej Hajda static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi, 7827eb8f069SAndrzej Hajda struct exynos_dsi_transfer *xfer) 7837eb8f069SAndrzej Hajda { 7847eb8f069SAndrzej Hajda struct device *dev = dsi->dev; 7857eb8f069SAndrzej Hajda const u8 *payload = xfer->tx_payload + xfer->tx_done; 7867eb8f069SAndrzej Hajda u16 length = xfer->tx_len - xfer->tx_done; 7877eb8f069SAndrzej Hajda bool first = !xfer->tx_done; 7887eb8f069SAndrzej Hajda u32 reg; 7897eb8f069SAndrzej Hajda 7907eb8f069SAndrzej Hajda dev_dbg(dev, "< xfer %p: tx len %u, done %u, rx len %u, done %u\n", 7917eb8f069SAndrzej Hajda xfer, xfer->tx_len, xfer->tx_done, xfer->rx_len, xfer->rx_done); 7927eb8f069SAndrzej Hajda 7937eb8f069SAndrzej Hajda if (length > DSI_TX_FIFO_SIZE) 7947eb8f069SAndrzej Hajda length = DSI_TX_FIFO_SIZE; 7957eb8f069SAndrzej Hajda 7967eb8f069SAndrzej Hajda xfer->tx_done += length; 7977eb8f069SAndrzej Hajda 7987eb8f069SAndrzej Hajda /* Send payload */ 7997eb8f069SAndrzej Hajda while (length >= 4) { 8007eb8f069SAndrzej Hajda reg = (payload[3] << 24) | (payload[2] << 16) 8017eb8f069SAndrzej Hajda | (payload[1] << 8) | payload[0]; 8027eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_PAYLOAD_REG); 8037eb8f069SAndrzej Hajda payload += 4; 8047eb8f069SAndrzej Hajda length -= 4; 8057eb8f069SAndrzej Hajda } 8067eb8f069SAndrzej Hajda 8077eb8f069SAndrzej Hajda reg = 0; 8087eb8f069SAndrzej Hajda switch (length) { 8097eb8f069SAndrzej Hajda case 3: 8107eb8f069SAndrzej Hajda reg |= payload[2] << 16; 8117eb8f069SAndrzej Hajda /* Fall through */ 8127eb8f069SAndrzej Hajda case 2: 8137eb8f069SAndrzej Hajda reg |= payload[1] << 8; 8147eb8f069SAndrzej Hajda /* Fall through */ 8157eb8f069SAndrzej Hajda case 1: 8167eb8f069SAndrzej Hajda reg |= payload[0]; 8177eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_PAYLOAD_REG); 8187eb8f069SAndrzej Hajda break; 8197eb8f069SAndrzej Hajda case 0: 8207eb8f069SAndrzej Hajda /* Do nothing */ 8217eb8f069SAndrzej Hajda break; 8227eb8f069SAndrzej Hajda } 8237eb8f069SAndrzej Hajda 8247eb8f069SAndrzej Hajda /* Send packet header */ 8257eb8f069SAndrzej Hajda if (!first) 8267eb8f069SAndrzej Hajda return; 8277eb8f069SAndrzej Hajda 8287eb8f069SAndrzej Hajda reg = (xfer->data[1] << 16) | (xfer->data[0] << 8) | xfer->data_id; 8297eb8f069SAndrzej Hajda if (exynos_dsi_wait_for_hdr_fifo(dsi)) { 8307eb8f069SAndrzej Hajda dev_err(dev, "waiting for header FIFO timed out\n"); 8317eb8f069SAndrzej Hajda return; 8327eb8f069SAndrzej Hajda } 8337eb8f069SAndrzej Hajda 8347eb8f069SAndrzej Hajda if (NEQV(xfer->flags & MIPI_DSI_MSG_USE_LPM, 8357eb8f069SAndrzej Hajda dsi->state & DSIM_STATE_CMD_LPM)) { 8367eb8f069SAndrzej Hajda exynos_dsi_set_cmd_lpm(dsi, xfer->flags & MIPI_DSI_MSG_USE_LPM); 8377eb8f069SAndrzej Hajda dsi->state ^= DSIM_STATE_CMD_LPM; 8387eb8f069SAndrzej Hajda } 8397eb8f069SAndrzej Hajda 8407eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_PKTHDR_REG); 8417eb8f069SAndrzej Hajda 8427eb8f069SAndrzej Hajda if (xfer->flags & MIPI_DSI_MSG_REQ_ACK) 8437eb8f069SAndrzej Hajda exynos_dsi_force_bta(dsi); 8447eb8f069SAndrzej Hajda } 8457eb8f069SAndrzej Hajda 8467eb8f069SAndrzej Hajda static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi, 8477eb8f069SAndrzej Hajda struct exynos_dsi_transfer *xfer) 8487eb8f069SAndrzej Hajda { 8497eb8f069SAndrzej Hajda u8 *payload = xfer->rx_payload + xfer->rx_done; 8507eb8f069SAndrzej Hajda bool first = !xfer->rx_done; 8517eb8f069SAndrzej Hajda struct device *dev = dsi->dev; 8527eb8f069SAndrzej Hajda u16 length; 8537eb8f069SAndrzej Hajda u32 reg; 8547eb8f069SAndrzej Hajda 8557eb8f069SAndrzej Hajda if (first) { 8567eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_RXFIFO_REG); 8577eb8f069SAndrzej Hajda 8587eb8f069SAndrzej Hajda switch (reg & 0x3f) { 8597eb8f069SAndrzej Hajda case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE: 8607eb8f069SAndrzej Hajda case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE: 8617eb8f069SAndrzej Hajda if (xfer->rx_len >= 2) { 8627eb8f069SAndrzej Hajda payload[1] = reg >> 16; 8637eb8f069SAndrzej Hajda ++xfer->rx_done; 8647eb8f069SAndrzej Hajda } 8657eb8f069SAndrzej Hajda /* Fall through */ 8667eb8f069SAndrzej Hajda case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE: 8677eb8f069SAndrzej Hajda case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE: 8687eb8f069SAndrzej Hajda payload[0] = reg >> 8; 8697eb8f069SAndrzej Hajda ++xfer->rx_done; 8707eb8f069SAndrzej Hajda xfer->rx_len = xfer->rx_done; 8717eb8f069SAndrzej Hajda xfer->result = 0; 8727eb8f069SAndrzej Hajda goto clear_fifo; 8737eb8f069SAndrzej Hajda case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT: 8747eb8f069SAndrzej Hajda dev_err(dev, "DSI Error Report: 0x%04x\n", 8757eb8f069SAndrzej Hajda (reg >> 8) & 0xffff); 8767eb8f069SAndrzej Hajda xfer->result = 0; 8777eb8f069SAndrzej Hajda goto clear_fifo; 8787eb8f069SAndrzej Hajda } 8797eb8f069SAndrzej Hajda 8807eb8f069SAndrzej Hajda length = (reg >> 8) & 0xffff; 8817eb8f069SAndrzej Hajda if (length > xfer->rx_len) { 8827eb8f069SAndrzej Hajda dev_err(dev, 8837eb8f069SAndrzej Hajda "response too long (%u > %u bytes), stripping\n", 8847eb8f069SAndrzej Hajda xfer->rx_len, length); 8857eb8f069SAndrzej Hajda length = xfer->rx_len; 8867eb8f069SAndrzej Hajda } else if (length < xfer->rx_len) 8877eb8f069SAndrzej Hajda xfer->rx_len = length; 8887eb8f069SAndrzej Hajda } 8897eb8f069SAndrzej Hajda 8907eb8f069SAndrzej Hajda length = xfer->rx_len - xfer->rx_done; 8917eb8f069SAndrzej Hajda xfer->rx_done += length; 8927eb8f069SAndrzej Hajda 8937eb8f069SAndrzej Hajda /* Receive payload */ 8947eb8f069SAndrzej Hajda while (length >= 4) { 8957eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_RXFIFO_REG); 8967eb8f069SAndrzej Hajda payload[0] = (reg >> 0) & 0xff; 8977eb8f069SAndrzej Hajda payload[1] = (reg >> 8) & 0xff; 8987eb8f069SAndrzej Hajda payload[2] = (reg >> 16) & 0xff; 8997eb8f069SAndrzej Hajda payload[3] = (reg >> 24) & 0xff; 9007eb8f069SAndrzej Hajda payload += 4; 9017eb8f069SAndrzej Hajda length -= 4; 9027eb8f069SAndrzej Hajda } 9037eb8f069SAndrzej Hajda 9047eb8f069SAndrzej Hajda if (length) { 9057eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_RXFIFO_REG); 9067eb8f069SAndrzej Hajda switch (length) { 9077eb8f069SAndrzej Hajda case 3: 9087eb8f069SAndrzej Hajda payload[2] = (reg >> 16) & 0xff; 9097eb8f069SAndrzej Hajda /* Fall through */ 9107eb8f069SAndrzej Hajda case 2: 9117eb8f069SAndrzej Hajda payload[1] = (reg >> 8) & 0xff; 9127eb8f069SAndrzej Hajda /* Fall through */ 9137eb8f069SAndrzej Hajda case 1: 9147eb8f069SAndrzej Hajda payload[0] = reg & 0xff; 9157eb8f069SAndrzej Hajda } 9167eb8f069SAndrzej Hajda } 9177eb8f069SAndrzej Hajda 9187eb8f069SAndrzej Hajda if (xfer->rx_done == xfer->rx_len) 9197eb8f069SAndrzej Hajda xfer->result = 0; 9207eb8f069SAndrzej Hajda 9217eb8f069SAndrzej Hajda clear_fifo: 9227eb8f069SAndrzej Hajda length = DSI_RX_FIFO_SIZE / 4; 9237eb8f069SAndrzej Hajda do { 9247eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_RXFIFO_REG); 9257eb8f069SAndrzej Hajda if (reg == DSI_RX_FIFO_EMPTY) 9267eb8f069SAndrzej Hajda break; 9277eb8f069SAndrzej Hajda } while (--length); 9287eb8f069SAndrzej Hajda } 9297eb8f069SAndrzej Hajda 9307eb8f069SAndrzej Hajda static void exynos_dsi_transfer_start(struct exynos_dsi *dsi) 9317eb8f069SAndrzej Hajda { 9327eb8f069SAndrzej Hajda unsigned long flags; 9337eb8f069SAndrzej Hajda struct exynos_dsi_transfer *xfer; 9347eb8f069SAndrzej Hajda bool start = false; 9357eb8f069SAndrzej Hajda 9367eb8f069SAndrzej Hajda again: 9377eb8f069SAndrzej Hajda spin_lock_irqsave(&dsi->transfer_lock, flags); 9387eb8f069SAndrzej Hajda 9397eb8f069SAndrzej Hajda if (list_empty(&dsi->transfer_list)) { 9407eb8f069SAndrzej Hajda spin_unlock_irqrestore(&dsi->transfer_lock, flags); 9417eb8f069SAndrzej Hajda return; 9427eb8f069SAndrzej Hajda } 9437eb8f069SAndrzej Hajda 9447eb8f069SAndrzej Hajda xfer = list_first_entry(&dsi->transfer_list, 9457eb8f069SAndrzej Hajda struct exynos_dsi_transfer, list); 9467eb8f069SAndrzej Hajda 9477eb8f069SAndrzej Hajda spin_unlock_irqrestore(&dsi->transfer_lock, flags); 9487eb8f069SAndrzej Hajda 9497eb8f069SAndrzej Hajda if (xfer->tx_len && xfer->tx_done == xfer->tx_len) 9507eb8f069SAndrzej Hajda /* waiting for RX */ 9517eb8f069SAndrzej Hajda return; 9527eb8f069SAndrzej Hajda 9537eb8f069SAndrzej Hajda exynos_dsi_send_to_fifo(dsi, xfer); 9547eb8f069SAndrzej Hajda 9557eb8f069SAndrzej Hajda if (xfer->tx_len || xfer->rx_len) 9567eb8f069SAndrzej Hajda return; 9577eb8f069SAndrzej Hajda 9587eb8f069SAndrzej Hajda xfer->result = 0; 9597eb8f069SAndrzej Hajda complete(&xfer->completed); 9607eb8f069SAndrzej Hajda 9617eb8f069SAndrzej Hajda spin_lock_irqsave(&dsi->transfer_lock, flags); 9627eb8f069SAndrzej Hajda 9637eb8f069SAndrzej Hajda list_del_init(&xfer->list); 9647eb8f069SAndrzej Hajda start = !list_empty(&dsi->transfer_list); 9657eb8f069SAndrzej Hajda 9667eb8f069SAndrzej Hajda spin_unlock_irqrestore(&dsi->transfer_lock, flags); 9677eb8f069SAndrzej Hajda 9687eb8f069SAndrzej Hajda if (start) 9697eb8f069SAndrzej Hajda goto again; 9707eb8f069SAndrzej Hajda } 9717eb8f069SAndrzej Hajda 9727eb8f069SAndrzej Hajda static bool exynos_dsi_transfer_finish(struct exynos_dsi *dsi) 9737eb8f069SAndrzej Hajda { 9747eb8f069SAndrzej Hajda struct exynos_dsi_transfer *xfer; 9757eb8f069SAndrzej Hajda unsigned long flags; 9767eb8f069SAndrzej Hajda bool start = true; 9777eb8f069SAndrzej Hajda 9787eb8f069SAndrzej Hajda spin_lock_irqsave(&dsi->transfer_lock, flags); 9797eb8f069SAndrzej Hajda 9807eb8f069SAndrzej Hajda if (list_empty(&dsi->transfer_list)) { 9817eb8f069SAndrzej Hajda spin_unlock_irqrestore(&dsi->transfer_lock, flags); 9827eb8f069SAndrzej Hajda return false; 9837eb8f069SAndrzej Hajda } 9847eb8f069SAndrzej Hajda 9857eb8f069SAndrzej Hajda xfer = list_first_entry(&dsi->transfer_list, 9867eb8f069SAndrzej Hajda struct exynos_dsi_transfer, list); 9877eb8f069SAndrzej Hajda 9887eb8f069SAndrzej Hajda spin_unlock_irqrestore(&dsi->transfer_lock, flags); 9897eb8f069SAndrzej Hajda 9907eb8f069SAndrzej Hajda dev_dbg(dsi->dev, 9917eb8f069SAndrzej Hajda "> xfer %p, tx_len %u, tx_done %u, rx_len %u, rx_done %u\n", 9927eb8f069SAndrzej Hajda xfer, xfer->tx_len, xfer->tx_done, xfer->rx_len, xfer->rx_done); 9937eb8f069SAndrzej Hajda 9947eb8f069SAndrzej Hajda if (xfer->tx_done != xfer->tx_len) 9957eb8f069SAndrzej Hajda return true; 9967eb8f069SAndrzej Hajda 9977eb8f069SAndrzej Hajda if (xfer->rx_done != xfer->rx_len) 9987eb8f069SAndrzej Hajda exynos_dsi_read_from_fifo(dsi, xfer); 9997eb8f069SAndrzej Hajda 10007eb8f069SAndrzej Hajda if (xfer->rx_done != xfer->rx_len) 10017eb8f069SAndrzej Hajda return true; 10027eb8f069SAndrzej Hajda 10037eb8f069SAndrzej Hajda spin_lock_irqsave(&dsi->transfer_lock, flags); 10047eb8f069SAndrzej Hajda 10057eb8f069SAndrzej Hajda list_del_init(&xfer->list); 10067eb8f069SAndrzej Hajda start = !list_empty(&dsi->transfer_list); 10077eb8f069SAndrzej Hajda 10087eb8f069SAndrzej Hajda spin_unlock_irqrestore(&dsi->transfer_lock, flags); 10097eb8f069SAndrzej Hajda 10107eb8f069SAndrzej Hajda if (!xfer->rx_len) 10117eb8f069SAndrzej Hajda xfer->result = 0; 10127eb8f069SAndrzej Hajda complete(&xfer->completed); 10137eb8f069SAndrzej Hajda 10147eb8f069SAndrzej Hajda return start; 10157eb8f069SAndrzej Hajda } 10167eb8f069SAndrzej Hajda 10177eb8f069SAndrzej Hajda static void exynos_dsi_remove_transfer(struct exynos_dsi *dsi, 10187eb8f069SAndrzej Hajda struct exynos_dsi_transfer *xfer) 10197eb8f069SAndrzej Hajda { 10207eb8f069SAndrzej Hajda unsigned long flags; 10217eb8f069SAndrzej Hajda bool start; 10227eb8f069SAndrzej Hajda 10237eb8f069SAndrzej Hajda spin_lock_irqsave(&dsi->transfer_lock, flags); 10247eb8f069SAndrzej Hajda 10257eb8f069SAndrzej Hajda if (!list_empty(&dsi->transfer_list) && 10267eb8f069SAndrzej Hajda xfer == list_first_entry(&dsi->transfer_list, 10277eb8f069SAndrzej Hajda struct exynos_dsi_transfer, list)) { 10287eb8f069SAndrzej Hajda list_del_init(&xfer->list); 10297eb8f069SAndrzej Hajda start = !list_empty(&dsi->transfer_list); 10307eb8f069SAndrzej Hajda spin_unlock_irqrestore(&dsi->transfer_lock, flags); 10317eb8f069SAndrzej Hajda if (start) 10327eb8f069SAndrzej Hajda exynos_dsi_transfer_start(dsi); 10337eb8f069SAndrzej Hajda return; 10347eb8f069SAndrzej Hajda } 10357eb8f069SAndrzej Hajda 10367eb8f069SAndrzej Hajda list_del_init(&xfer->list); 10377eb8f069SAndrzej Hajda 10387eb8f069SAndrzej Hajda spin_unlock_irqrestore(&dsi->transfer_lock, flags); 10397eb8f069SAndrzej Hajda } 10407eb8f069SAndrzej Hajda 10417eb8f069SAndrzej Hajda static int exynos_dsi_transfer(struct exynos_dsi *dsi, 10427eb8f069SAndrzej Hajda struct exynos_dsi_transfer *xfer) 10437eb8f069SAndrzej Hajda { 10447eb8f069SAndrzej Hajda unsigned long flags; 10457eb8f069SAndrzej Hajda bool stopped; 10467eb8f069SAndrzej Hajda 10477eb8f069SAndrzej Hajda xfer->tx_done = 0; 10487eb8f069SAndrzej Hajda xfer->rx_done = 0; 10497eb8f069SAndrzej Hajda xfer->result = -ETIMEDOUT; 10507eb8f069SAndrzej Hajda init_completion(&xfer->completed); 10517eb8f069SAndrzej Hajda 10527eb8f069SAndrzej Hajda spin_lock_irqsave(&dsi->transfer_lock, flags); 10537eb8f069SAndrzej Hajda 10547eb8f069SAndrzej Hajda stopped = list_empty(&dsi->transfer_list); 10557eb8f069SAndrzej Hajda list_add_tail(&xfer->list, &dsi->transfer_list); 10567eb8f069SAndrzej Hajda 10577eb8f069SAndrzej Hajda spin_unlock_irqrestore(&dsi->transfer_lock, flags); 10587eb8f069SAndrzej Hajda 10597eb8f069SAndrzej Hajda if (stopped) 10607eb8f069SAndrzej Hajda exynos_dsi_transfer_start(dsi); 10617eb8f069SAndrzej Hajda 10627eb8f069SAndrzej Hajda wait_for_completion_timeout(&xfer->completed, 10637eb8f069SAndrzej Hajda msecs_to_jiffies(DSI_XFER_TIMEOUT_MS)); 10647eb8f069SAndrzej Hajda if (xfer->result == -ETIMEDOUT) { 10657eb8f069SAndrzej Hajda exynos_dsi_remove_transfer(dsi, xfer); 10667eb8f069SAndrzej Hajda dev_err(dsi->dev, "xfer timed out: %*ph %*ph\n", 2, xfer->data, 10677eb8f069SAndrzej Hajda xfer->tx_len, xfer->tx_payload); 10687eb8f069SAndrzej Hajda return -ETIMEDOUT; 10697eb8f069SAndrzej Hajda } 10707eb8f069SAndrzej Hajda 10717eb8f069SAndrzej Hajda /* Also covers hardware timeout condition */ 10727eb8f069SAndrzej Hajda return xfer->result; 10737eb8f069SAndrzej Hajda } 10747eb8f069SAndrzej Hajda 10757eb8f069SAndrzej Hajda static irqreturn_t exynos_dsi_irq(int irq, void *dev_id) 10767eb8f069SAndrzej Hajda { 10777eb8f069SAndrzej Hajda struct exynos_dsi *dsi = dev_id; 10787eb8f069SAndrzej Hajda u32 status; 10797eb8f069SAndrzej Hajda 10807eb8f069SAndrzej Hajda status = readl(dsi->reg_base + DSIM_INTSRC_REG); 10817eb8f069SAndrzej Hajda if (!status) { 10827eb8f069SAndrzej Hajda static unsigned long int j; 10837eb8f069SAndrzej Hajda if (printk_timed_ratelimit(&j, 500)) 10847eb8f069SAndrzej Hajda dev_warn(dsi->dev, "spurious interrupt\n"); 10857eb8f069SAndrzej Hajda return IRQ_HANDLED; 10867eb8f069SAndrzej Hajda } 10877eb8f069SAndrzej Hajda writel(status, dsi->reg_base + DSIM_INTSRC_REG); 10887eb8f069SAndrzej Hajda 10897eb8f069SAndrzej Hajda if (status & DSIM_INT_SW_RST_RELEASE) { 10907eb8f069SAndrzej Hajda u32 mask = ~(DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY); 10917eb8f069SAndrzej Hajda writel(mask, dsi->reg_base + DSIM_INTMSK_REG); 10927eb8f069SAndrzej Hajda complete(&dsi->completed); 10937eb8f069SAndrzej Hajda return IRQ_HANDLED; 10947eb8f069SAndrzej Hajda } 10957eb8f069SAndrzej Hajda 10967eb8f069SAndrzej Hajda if (!(status & (DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY))) 10977eb8f069SAndrzej Hajda return IRQ_HANDLED; 10987eb8f069SAndrzej Hajda 10997eb8f069SAndrzej Hajda if (exynos_dsi_transfer_finish(dsi)) 11007eb8f069SAndrzej Hajda exynos_dsi_transfer_start(dsi); 11017eb8f069SAndrzej Hajda 11027eb8f069SAndrzej Hajda return IRQ_HANDLED; 11037eb8f069SAndrzej Hajda } 11047eb8f069SAndrzej Hajda 1105e17ddeccSYoungJun Cho static irqreturn_t exynos_dsi_te_irq_handler(int irq, void *dev_id) 1106e17ddeccSYoungJun Cho { 1107e17ddeccSYoungJun Cho struct exynos_dsi *dsi = (struct exynos_dsi *)dev_id; 1108e17ddeccSYoungJun Cho struct drm_encoder *encoder = dsi->encoder; 1109e17ddeccSYoungJun Cho 1110e17ddeccSYoungJun Cho if (dsi->state & DSIM_STATE_ENABLED) 1111e17ddeccSYoungJun Cho exynos_drm_crtc_te_handler(encoder->crtc); 1112e17ddeccSYoungJun Cho 1113e17ddeccSYoungJun Cho return IRQ_HANDLED; 1114e17ddeccSYoungJun Cho } 1115e17ddeccSYoungJun Cho 1116e17ddeccSYoungJun Cho static void exynos_dsi_enable_irq(struct exynos_dsi *dsi) 1117e17ddeccSYoungJun Cho { 1118e17ddeccSYoungJun Cho enable_irq(dsi->irq); 1119e17ddeccSYoungJun Cho 1120e17ddeccSYoungJun Cho if (gpio_is_valid(dsi->te_gpio)) 1121e17ddeccSYoungJun Cho enable_irq(gpio_to_irq(dsi->te_gpio)); 1122e17ddeccSYoungJun Cho } 1123e17ddeccSYoungJun Cho 1124e17ddeccSYoungJun Cho static void exynos_dsi_disable_irq(struct exynos_dsi *dsi) 1125e17ddeccSYoungJun Cho { 1126e17ddeccSYoungJun Cho if (gpio_is_valid(dsi->te_gpio)) 1127e17ddeccSYoungJun Cho disable_irq(gpio_to_irq(dsi->te_gpio)); 1128e17ddeccSYoungJun Cho 1129e17ddeccSYoungJun Cho disable_irq(dsi->irq); 1130e17ddeccSYoungJun Cho } 1131e17ddeccSYoungJun Cho 11327eb8f069SAndrzej Hajda static int exynos_dsi_init(struct exynos_dsi *dsi) 11337eb8f069SAndrzej Hajda { 11347eb8f069SAndrzej Hajda exynos_dsi_reset(dsi); 1135e17ddeccSYoungJun Cho exynos_dsi_enable_irq(dsi); 11369a320415SYoungJun Cho exynos_dsi_enable_clock(dsi); 11377eb8f069SAndrzej Hajda exynos_dsi_wait_for_reset(dsi); 11389a320415SYoungJun Cho exynos_dsi_set_phy_ctrl(dsi); 11397eb8f069SAndrzej Hajda exynos_dsi_init_link(dsi); 11407eb8f069SAndrzej Hajda 11417eb8f069SAndrzej Hajda return 0; 11427eb8f069SAndrzej Hajda } 11437eb8f069SAndrzej Hajda 1144e17ddeccSYoungJun Cho static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi) 1145e17ddeccSYoungJun Cho { 1146e17ddeccSYoungJun Cho int ret; 1147e17ddeccSYoungJun Cho 1148e17ddeccSYoungJun Cho dsi->te_gpio = of_get_named_gpio(dsi->panel_node, "te-gpios", 0); 1149e17ddeccSYoungJun Cho if (!gpio_is_valid(dsi->te_gpio)) { 1150e17ddeccSYoungJun Cho dev_err(dsi->dev, "no te-gpios specified\n"); 1151e17ddeccSYoungJun Cho ret = dsi->te_gpio; 1152e17ddeccSYoungJun Cho goto out; 1153e17ddeccSYoungJun Cho } 1154e17ddeccSYoungJun Cho 1155e17ddeccSYoungJun Cho ret = gpio_request_one(dsi->te_gpio, GPIOF_IN, "te_gpio"); 1156e17ddeccSYoungJun Cho if (ret) { 1157e17ddeccSYoungJun Cho dev_err(dsi->dev, "gpio request failed with %d\n", ret); 1158e17ddeccSYoungJun Cho goto out; 1159e17ddeccSYoungJun Cho } 1160e17ddeccSYoungJun Cho 1161e17ddeccSYoungJun Cho /* 1162e17ddeccSYoungJun Cho * This TE GPIO IRQ should not be set to IRQ_NOAUTOEN, because panel 1163e17ddeccSYoungJun Cho * calls drm_panel_init() first then calls mipi_dsi_attach() in probe(). 1164e17ddeccSYoungJun Cho * It means that te_gpio is invalid when exynos_dsi_enable_irq() is 1165e17ddeccSYoungJun Cho * called by drm_panel_init() before panel is attached. 1166e17ddeccSYoungJun Cho */ 1167e17ddeccSYoungJun Cho ret = request_threaded_irq(gpio_to_irq(dsi->te_gpio), 1168e17ddeccSYoungJun Cho exynos_dsi_te_irq_handler, NULL, 1169e17ddeccSYoungJun Cho IRQF_TRIGGER_RISING, "TE", dsi); 1170e17ddeccSYoungJun Cho if (ret) { 1171e17ddeccSYoungJun Cho dev_err(dsi->dev, "request interrupt failed with %d\n", ret); 1172e17ddeccSYoungJun Cho gpio_free(dsi->te_gpio); 1173e17ddeccSYoungJun Cho goto out; 1174e17ddeccSYoungJun Cho } 1175e17ddeccSYoungJun Cho 1176e17ddeccSYoungJun Cho out: 1177e17ddeccSYoungJun Cho return ret; 1178e17ddeccSYoungJun Cho } 1179e17ddeccSYoungJun Cho 1180e17ddeccSYoungJun Cho static void exynos_dsi_unregister_te_irq(struct exynos_dsi *dsi) 1181e17ddeccSYoungJun Cho { 1182e17ddeccSYoungJun Cho if (gpio_is_valid(dsi->te_gpio)) { 1183e17ddeccSYoungJun Cho free_irq(gpio_to_irq(dsi->te_gpio), dsi); 1184e17ddeccSYoungJun Cho gpio_free(dsi->te_gpio); 1185e17ddeccSYoungJun Cho dsi->te_gpio = -ENOENT; 1186e17ddeccSYoungJun Cho } 1187e17ddeccSYoungJun Cho } 1188e17ddeccSYoungJun Cho 11897eb8f069SAndrzej Hajda static int exynos_dsi_host_attach(struct mipi_dsi_host *host, 11907eb8f069SAndrzej Hajda struct mipi_dsi_device *device) 11917eb8f069SAndrzej Hajda { 11927eb8f069SAndrzej Hajda struct exynos_dsi *dsi = host_to_dsi(host); 11937eb8f069SAndrzej Hajda 11947eb8f069SAndrzej Hajda dsi->lanes = device->lanes; 11957eb8f069SAndrzej Hajda dsi->format = device->format; 11967eb8f069SAndrzej Hajda dsi->mode_flags = device->mode_flags; 11977eb8f069SAndrzej Hajda dsi->panel_node = device->dev.of_node; 11987eb8f069SAndrzej Hajda 11997eb8f069SAndrzej Hajda if (dsi->connector.dev) 12007eb8f069SAndrzej Hajda drm_helper_hpd_irq_event(dsi->connector.dev); 12017eb8f069SAndrzej Hajda 1202e17ddeccSYoungJun Cho /* 1203e17ddeccSYoungJun Cho * This is a temporary solution and should be made by more generic way. 1204e17ddeccSYoungJun Cho * 1205e17ddeccSYoungJun Cho * If attached panel device is for command mode one, dsi should register 1206e17ddeccSYoungJun Cho * TE interrupt handler. 1207e17ddeccSYoungJun Cho */ 1208e17ddeccSYoungJun Cho if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO)) { 1209e17ddeccSYoungJun Cho int ret = exynos_dsi_register_te_irq(dsi); 1210e17ddeccSYoungJun Cho 1211e17ddeccSYoungJun Cho if (ret) 1212e17ddeccSYoungJun Cho return ret; 1213e17ddeccSYoungJun Cho } 1214e17ddeccSYoungJun Cho 12157eb8f069SAndrzej Hajda return 0; 12167eb8f069SAndrzej Hajda } 12177eb8f069SAndrzej Hajda 12187eb8f069SAndrzej Hajda static int exynos_dsi_host_detach(struct mipi_dsi_host *host, 12197eb8f069SAndrzej Hajda struct mipi_dsi_device *device) 12207eb8f069SAndrzej Hajda { 12217eb8f069SAndrzej Hajda struct exynos_dsi *dsi = host_to_dsi(host); 12227eb8f069SAndrzej Hajda 1223e17ddeccSYoungJun Cho exynos_dsi_unregister_te_irq(dsi); 1224e17ddeccSYoungJun Cho 12257eb8f069SAndrzej Hajda dsi->panel_node = NULL; 12267eb8f069SAndrzej Hajda 12277eb8f069SAndrzej Hajda if (dsi->connector.dev) 12287eb8f069SAndrzej Hajda drm_helper_hpd_irq_event(dsi->connector.dev); 12297eb8f069SAndrzej Hajda 12307eb8f069SAndrzej Hajda return 0; 12317eb8f069SAndrzej Hajda } 12327eb8f069SAndrzej Hajda 12337eb8f069SAndrzej Hajda /* distinguish between short and long DSI packet types */ 12347eb8f069SAndrzej Hajda static bool exynos_dsi_is_short_dsi_type(u8 type) 12357eb8f069SAndrzej Hajda { 12367eb8f069SAndrzej Hajda return (type & 0x0f) <= 8; 12377eb8f069SAndrzej Hajda } 12387eb8f069SAndrzej Hajda 12397eb8f069SAndrzej Hajda static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host, 1240ed6ff40eSThierry Reding const struct mipi_dsi_msg *msg) 12417eb8f069SAndrzej Hajda { 12427eb8f069SAndrzej Hajda struct exynos_dsi *dsi = host_to_dsi(host); 12437eb8f069SAndrzej Hajda struct exynos_dsi_transfer xfer; 12447eb8f069SAndrzej Hajda int ret; 12457eb8f069SAndrzej Hajda 12467eb8f069SAndrzej Hajda if (!(dsi->state & DSIM_STATE_INITIALIZED)) { 12477eb8f069SAndrzej Hajda ret = exynos_dsi_init(dsi); 12487eb8f069SAndrzej Hajda if (ret) 12497eb8f069SAndrzej Hajda return ret; 12507eb8f069SAndrzej Hajda dsi->state |= DSIM_STATE_INITIALIZED; 12517eb8f069SAndrzej Hajda } 12527eb8f069SAndrzej Hajda 12537eb8f069SAndrzej Hajda if (msg->tx_len == 0) 12547eb8f069SAndrzej Hajda return -EINVAL; 12557eb8f069SAndrzej Hajda 12567eb8f069SAndrzej Hajda xfer.data_id = msg->type | (msg->channel << 6); 12577eb8f069SAndrzej Hajda 12587eb8f069SAndrzej Hajda if (exynos_dsi_is_short_dsi_type(msg->type)) { 12597eb8f069SAndrzej Hajda const char *tx_buf = msg->tx_buf; 12607eb8f069SAndrzej Hajda 12617eb8f069SAndrzej Hajda if (msg->tx_len > 2) 12627eb8f069SAndrzej Hajda return -EINVAL; 12637eb8f069SAndrzej Hajda xfer.tx_len = 0; 12647eb8f069SAndrzej Hajda xfer.data[0] = tx_buf[0]; 12657eb8f069SAndrzej Hajda xfer.data[1] = (msg->tx_len == 2) ? tx_buf[1] : 0; 12667eb8f069SAndrzej Hajda } else { 12677eb8f069SAndrzej Hajda xfer.tx_len = msg->tx_len; 12687eb8f069SAndrzej Hajda xfer.data[0] = msg->tx_len & 0xff; 12697eb8f069SAndrzej Hajda xfer.data[1] = msg->tx_len >> 8; 12707eb8f069SAndrzej Hajda xfer.tx_payload = msg->tx_buf; 12717eb8f069SAndrzej Hajda } 12727eb8f069SAndrzej Hajda 12737eb8f069SAndrzej Hajda xfer.rx_len = msg->rx_len; 12747eb8f069SAndrzej Hajda xfer.rx_payload = msg->rx_buf; 12757eb8f069SAndrzej Hajda xfer.flags = msg->flags; 12767eb8f069SAndrzej Hajda 12777eb8f069SAndrzej Hajda ret = exynos_dsi_transfer(dsi, &xfer); 12787eb8f069SAndrzej Hajda return (ret < 0) ? ret : xfer.rx_done; 12797eb8f069SAndrzej Hajda } 12807eb8f069SAndrzej Hajda 12817eb8f069SAndrzej Hajda static const struct mipi_dsi_host_ops exynos_dsi_ops = { 12827eb8f069SAndrzej Hajda .attach = exynos_dsi_host_attach, 12837eb8f069SAndrzej Hajda .detach = exynos_dsi_host_detach, 12847eb8f069SAndrzej Hajda .transfer = exynos_dsi_host_transfer, 12857eb8f069SAndrzej Hajda }; 12867eb8f069SAndrzej Hajda 12877eb8f069SAndrzej Hajda static int exynos_dsi_poweron(struct exynos_dsi *dsi) 12887eb8f069SAndrzej Hajda { 12897eb8f069SAndrzej Hajda int ret; 12907eb8f069SAndrzej Hajda 12917eb8f069SAndrzej Hajda ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies); 12927eb8f069SAndrzej Hajda if (ret < 0) { 12937eb8f069SAndrzej Hajda dev_err(dsi->dev, "cannot enable regulators %d\n", ret); 12947eb8f069SAndrzej Hajda return ret; 12957eb8f069SAndrzej Hajda } 12967eb8f069SAndrzej Hajda 12977eb8f069SAndrzej Hajda ret = clk_prepare_enable(dsi->bus_clk); 12987eb8f069SAndrzej Hajda if (ret < 0) { 12997eb8f069SAndrzej Hajda dev_err(dsi->dev, "cannot enable bus clock %d\n", ret); 13007eb8f069SAndrzej Hajda goto err_bus_clk; 13017eb8f069SAndrzej Hajda } 13027eb8f069SAndrzej Hajda 13037eb8f069SAndrzej Hajda ret = clk_prepare_enable(dsi->pll_clk); 13047eb8f069SAndrzej Hajda if (ret < 0) { 13057eb8f069SAndrzej Hajda dev_err(dsi->dev, "cannot enable pll clock %d\n", ret); 13067eb8f069SAndrzej Hajda goto err_pll_clk; 13077eb8f069SAndrzej Hajda } 13087eb8f069SAndrzej Hajda 13097eb8f069SAndrzej Hajda ret = phy_power_on(dsi->phy); 13107eb8f069SAndrzej Hajda if (ret < 0) { 13117eb8f069SAndrzej Hajda dev_err(dsi->dev, "cannot enable phy %d\n", ret); 13127eb8f069SAndrzej Hajda goto err_phy; 13137eb8f069SAndrzej Hajda } 13147eb8f069SAndrzej Hajda 13157eb8f069SAndrzej Hajda return 0; 13167eb8f069SAndrzej Hajda 13177eb8f069SAndrzej Hajda err_phy: 13187eb8f069SAndrzej Hajda clk_disable_unprepare(dsi->pll_clk); 13197eb8f069SAndrzej Hajda err_pll_clk: 13207eb8f069SAndrzej Hajda clk_disable_unprepare(dsi->bus_clk); 13217eb8f069SAndrzej Hajda err_bus_clk: 13227eb8f069SAndrzej Hajda regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies); 13237eb8f069SAndrzej Hajda 13247eb8f069SAndrzej Hajda return ret; 13257eb8f069SAndrzej Hajda } 13267eb8f069SAndrzej Hajda 13277eb8f069SAndrzej Hajda static void exynos_dsi_poweroff(struct exynos_dsi *dsi) 13287eb8f069SAndrzej Hajda { 13297eb8f069SAndrzej Hajda int ret; 13307eb8f069SAndrzej Hajda 13317eb8f069SAndrzej Hajda usleep_range(10000, 20000); 13327eb8f069SAndrzej Hajda 13337eb8f069SAndrzej Hajda if (dsi->state & DSIM_STATE_INITIALIZED) { 13347eb8f069SAndrzej Hajda dsi->state &= ~DSIM_STATE_INITIALIZED; 13357eb8f069SAndrzej Hajda 13367eb8f069SAndrzej Hajda exynos_dsi_disable_clock(dsi); 13377eb8f069SAndrzej Hajda 1338e17ddeccSYoungJun Cho exynos_dsi_disable_irq(dsi); 13397eb8f069SAndrzej Hajda } 13407eb8f069SAndrzej Hajda 13417eb8f069SAndrzej Hajda dsi->state &= ~DSIM_STATE_CMD_LPM; 13427eb8f069SAndrzej Hajda 13437eb8f069SAndrzej Hajda phy_power_off(dsi->phy); 13447eb8f069SAndrzej Hajda 13457eb8f069SAndrzej Hajda clk_disable_unprepare(dsi->pll_clk); 13467eb8f069SAndrzej Hajda clk_disable_unprepare(dsi->bus_clk); 13477eb8f069SAndrzej Hajda 13487eb8f069SAndrzej Hajda ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies); 13497eb8f069SAndrzej Hajda if (ret < 0) 13507eb8f069SAndrzej Hajda dev_err(dsi->dev, "cannot disable regulators %d\n", ret); 13517eb8f069SAndrzej Hajda } 13527eb8f069SAndrzej Hajda 13537eb8f069SAndrzej Hajda static int exynos_dsi_enable(struct exynos_dsi *dsi) 13547eb8f069SAndrzej Hajda { 13557eb8f069SAndrzej Hajda int ret; 13567eb8f069SAndrzej Hajda 13577eb8f069SAndrzej Hajda if (dsi->state & DSIM_STATE_ENABLED) 13587eb8f069SAndrzej Hajda return 0; 13597eb8f069SAndrzej Hajda 13607eb8f069SAndrzej Hajda ret = exynos_dsi_poweron(dsi); 13617eb8f069SAndrzej Hajda if (ret < 0) 13627eb8f069SAndrzej Hajda return ret; 13637eb8f069SAndrzej Hajda 1364cdfb8694SAjay Kumar ret = drm_panel_prepare(dsi->panel); 13657eb8f069SAndrzej Hajda if (ret < 0) { 13667eb8f069SAndrzej Hajda exynos_dsi_poweroff(dsi); 13677eb8f069SAndrzej Hajda return ret; 13687eb8f069SAndrzej Hajda } 13697eb8f069SAndrzej Hajda 13707eb8f069SAndrzej Hajda exynos_dsi_set_display_mode(dsi); 13717eb8f069SAndrzej Hajda exynos_dsi_set_display_enable(dsi, true); 13727eb8f069SAndrzej Hajda 1373cdfb8694SAjay Kumar ret = drm_panel_enable(dsi->panel); 1374cdfb8694SAjay Kumar if (ret < 0) { 1375cdfb8694SAjay Kumar exynos_dsi_set_display_enable(dsi, false); 1376cdfb8694SAjay Kumar drm_panel_unprepare(dsi->panel); 1377cdfb8694SAjay Kumar exynos_dsi_poweroff(dsi); 1378cdfb8694SAjay Kumar return ret; 1379cdfb8694SAjay Kumar } 1380cdfb8694SAjay Kumar 13817eb8f069SAndrzej Hajda dsi->state |= DSIM_STATE_ENABLED; 13827eb8f069SAndrzej Hajda 13837eb8f069SAndrzej Hajda return 0; 13847eb8f069SAndrzej Hajda } 13857eb8f069SAndrzej Hajda 13867eb8f069SAndrzej Hajda static void exynos_dsi_disable(struct exynos_dsi *dsi) 13877eb8f069SAndrzej Hajda { 13887eb8f069SAndrzej Hajda if (!(dsi->state & DSIM_STATE_ENABLED)) 13897eb8f069SAndrzej Hajda return; 13907eb8f069SAndrzej Hajda 13917eb8f069SAndrzej Hajda drm_panel_disable(dsi->panel); 1392cdfb8694SAjay Kumar exynos_dsi_set_display_enable(dsi, false); 1393cdfb8694SAjay Kumar drm_panel_unprepare(dsi->panel); 13947eb8f069SAndrzej Hajda exynos_dsi_poweroff(dsi); 13957eb8f069SAndrzej Hajda 13967eb8f069SAndrzej Hajda dsi->state &= ~DSIM_STATE_ENABLED; 13977eb8f069SAndrzej Hajda } 13987eb8f069SAndrzej Hajda 13997eb8f069SAndrzej Hajda static void exynos_dsi_dpms(struct exynos_drm_display *display, int mode) 14007eb8f069SAndrzej Hajda { 14017eb8f069SAndrzej Hajda struct exynos_dsi *dsi = display->ctx; 14027eb8f069SAndrzej Hajda 14037eb8f069SAndrzej Hajda if (dsi->panel) { 14047eb8f069SAndrzej Hajda switch (mode) { 14057eb8f069SAndrzej Hajda case DRM_MODE_DPMS_ON: 14067eb8f069SAndrzej Hajda exynos_dsi_enable(dsi); 14077eb8f069SAndrzej Hajda break; 14087eb8f069SAndrzej Hajda case DRM_MODE_DPMS_STANDBY: 14097eb8f069SAndrzej Hajda case DRM_MODE_DPMS_SUSPEND: 14107eb8f069SAndrzej Hajda case DRM_MODE_DPMS_OFF: 14117eb8f069SAndrzej Hajda exynos_dsi_disable(dsi); 14127eb8f069SAndrzej Hajda break; 14137eb8f069SAndrzej Hajda default: 14147eb8f069SAndrzej Hajda break; 14157eb8f069SAndrzej Hajda } 14167eb8f069SAndrzej Hajda } 14177eb8f069SAndrzej Hajda } 14187eb8f069SAndrzej Hajda 14197eb8f069SAndrzej Hajda static enum drm_connector_status 14207eb8f069SAndrzej Hajda exynos_dsi_detect(struct drm_connector *connector, bool force) 14217eb8f069SAndrzej Hajda { 14227eb8f069SAndrzej Hajda struct exynos_dsi *dsi = connector_to_dsi(connector); 14237eb8f069SAndrzej Hajda 14247eb8f069SAndrzej Hajda if (!dsi->panel) { 14257eb8f069SAndrzej Hajda dsi->panel = of_drm_find_panel(dsi->panel_node); 14267eb8f069SAndrzej Hajda if (dsi->panel) 14277eb8f069SAndrzej Hajda drm_panel_attach(dsi->panel, &dsi->connector); 14287eb8f069SAndrzej Hajda } else if (!dsi->panel_node) { 14297eb8f069SAndrzej Hajda struct exynos_drm_display *display; 14307eb8f069SAndrzej Hajda 14317eb8f069SAndrzej Hajda display = platform_get_drvdata(to_platform_device(dsi->dev)); 14327eb8f069SAndrzej Hajda exynos_dsi_dpms(display, DRM_MODE_DPMS_OFF); 14337eb8f069SAndrzej Hajda drm_panel_detach(dsi->panel); 14347eb8f069SAndrzej Hajda dsi->panel = NULL; 14357eb8f069SAndrzej Hajda } 14367eb8f069SAndrzej Hajda 14377eb8f069SAndrzej Hajda if (dsi->panel) 14387eb8f069SAndrzej Hajda return connector_status_connected; 14397eb8f069SAndrzej Hajda 14407eb8f069SAndrzej Hajda return connector_status_disconnected; 14417eb8f069SAndrzej Hajda } 14427eb8f069SAndrzej Hajda 14437eb8f069SAndrzej Hajda static void exynos_dsi_connector_destroy(struct drm_connector *connector) 14447eb8f069SAndrzej Hajda { 14450ae46015SAndrzej Hajda drm_connector_unregister(connector); 14460ae46015SAndrzej Hajda drm_connector_cleanup(connector); 14470ae46015SAndrzej Hajda connector->dev = NULL; 14487eb8f069SAndrzej Hajda } 14497eb8f069SAndrzej Hajda 14507eb8f069SAndrzej Hajda static struct drm_connector_funcs exynos_dsi_connector_funcs = { 14517eb8f069SAndrzej Hajda .dpms = drm_helper_connector_dpms, 14527eb8f069SAndrzej Hajda .detect = exynos_dsi_detect, 14537eb8f069SAndrzej Hajda .fill_modes = drm_helper_probe_single_connector_modes, 14547eb8f069SAndrzej Hajda .destroy = exynos_dsi_connector_destroy, 14557eb8f069SAndrzej Hajda }; 14567eb8f069SAndrzej Hajda 14577eb8f069SAndrzej Hajda static int exynos_dsi_get_modes(struct drm_connector *connector) 14587eb8f069SAndrzej Hajda { 14597eb8f069SAndrzej Hajda struct exynos_dsi *dsi = connector_to_dsi(connector); 14607eb8f069SAndrzej Hajda 14617eb8f069SAndrzej Hajda if (dsi->panel) 14627eb8f069SAndrzej Hajda return dsi->panel->funcs->get_modes(dsi->panel); 14637eb8f069SAndrzej Hajda 14647eb8f069SAndrzej Hajda return 0; 14657eb8f069SAndrzej Hajda } 14667eb8f069SAndrzej Hajda 14677eb8f069SAndrzej Hajda static int exynos_dsi_mode_valid(struct drm_connector *connector, 14687eb8f069SAndrzej Hajda struct drm_display_mode *mode) 14697eb8f069SAndrzej Hajda { 14707eb8f069SAndrzej Hajda return MODE_OK; 14717eb8f069SAndrzej Hajda } 14727eb8f069SAndrzej Hajda 14737eb8f069SAndrzej Hajda static struct drm_encoder * 14747eb8f069SAndrzej Hajda exynos_dsi_best_encoder(struct drm_connector *connector) 14757eb8f069SAndrzej Hajda { 14767eb8f069SAndrzej Hajda struct exynos_dsi *dsi = connector_to_dsi(connector); 14777eb8f069SAndrzej Hajda 14787eb8f069SAndrzej Hajda return dsi->encoder; 14797eb8f069SAndrzej Hajda } 14807eb8f069SAndrzej Hajda 14817eb8f069SAndrzej Hajda static struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = { 14827eb8f069SAndrzej Hajda .get_modes = exynos_dsi_get_modes, 14837eb8f069SAndrzej Hajda .mode_valid = exynos_dsi_mode_valid, 14847eb8f069SAndrzej Hajda .best_encoder = exynos_dsi_best_encoder, 14857eb8f069SAndrzej Hajda }; 14867eb8f069SAndrzej Hajda 14877eb8f069SAndrzej Hajda static int exynos_dsi_create_connector(struct exynos_drm_display *display, 14887eb8f069SAndrzej Hajda struct drm_encoder *encoder) 14897eb8f069SAndrzej Hajda { 14907eb8f069SAndrzej Hajda struct exynos_dsi *dsi = display->ctx; 14917eb8f069SAndrzej Hajda struct drm_connector *connector = &dsi->connector; 14927eb8f069SAndrzej Hajda int ret; 14937eb8f069SAndrzej Hajda 14947eb8f069SAndrzej Hajda dsi->encoder = encoder; 14957eb8f069SAndrzej Hajda 14967eb8f069SAndrzej Hajda connector->polled = DRM_CONNECTOR_POLL_HPD; 14977eb8f069SAndrzej Hajda 14987eb8f069SAndrzej Hajda ret = drm_connector_init(encoder->dev, connector, 14997eb8f069SAndrzej Hajda &exynos_dsi_connector_funcs, 15007eb8f069SAndrzej Hajda DRM_MODE_CONNECTOR_DSI); 15017eb8f069SAndrzej Hajda if (ret) { 15027eb8f069SAndrzej Hajda DRM_ERROR("Failed to initialize connector with drm\n"); 15037eb8f069SAndrzej Hajda return ret; 15047eb8f069SAndrzej Hajda } 15057eb8f069SAndrzej Hajda 15067eb8f069SAndrzej Hajda drm_connector_helper_add(connector, &exynos_dsi_connector_helper_funcs); 150734ea3d38SThomas Wood drm_connector_register(connector); 15087eb8f069SAndrzej Hajda drm_mode_connector_attach_encoder(connector, encoder); 15097eb8f069SAndrzej Hajda 15107eb8f069SAndrzej Hajda return 0; 15117eb8f069SAndrzej Hajda } 15127eb8f069SAndrzej Hajda 15137eb8f069SAndrzej Hajda static void exynos_dsi_mode_set(struct exynos_drm_display *display, 15147eb8f069SAndrzej Hajda struct drm_display_mode *mode) 15157eb8f069SAndrzej Hajda { 15167eb8f069SAndrzej Hajda struct exynos_dsi *dsi = display->ctx; 15177eb8f069SAndrzej Hajda struct videomode *vm = &dsi->vm; 15187eb8f069SAndrzej Hajda 15197eb8f069SAndrzej Hajda vm->hactive = mode->hdisplay; 15207eb8f069SAndrzej Hajda vm->vactive = mode->vdisplay; 15217eb8f069SAndrzej Hajda vm->vfront_porch = mode->vsync_start - mode->vdisplay; 15227eb8f069SAndrzej Hajda vm->vback_porch = mode->vtotal - mode->vsync_end; 15237eb8f069SAndrzej Hajda vm->vsync_len = mode->vsync_end - mode->vsync_start; 15247eb8f069SAndrzej Hajda vm->hfront_porch = mode->hsync_start - mode->hdisplay; 15257eb8f069SAndrzej Hajda vm->hback_porch = mode->htotal - mode->hsync_end; 15267eb8f069SAndrzej Hajda vm->hsync_len = mode->hsync_end - mode->hsync_start; 15277eb8f069SAndrzej Hajda } 15287eb8f069SAndrzej Hajda 15297eb8f069SAndrzej Hajda static struct exynos_drm_display_ops exynos_dsi_display_ops = { 15307eb8f069SAndrzej Hajda .create_connector = exynos_dsi_create_connector, 15317eb8f069SAndrzej Hajda .mode_set = exynos_dsi_mode_set, 15327eb8f069SAndrzej Hajda .dpms = exynos_dsi_dpms 15337eb8f069SAndrzej Hajda }; 15347eb8f069SAndrzej Hajda 1535bd024b86SSjoerd Simons MODULE_DEVICE_TABLE(of, exynos_dsi_of_match); 15367eb8f069SAndrzej Hajda 15377eb8f069SAndrzej Hajda /* of_* functions will be removed after merge of of_graph patches */ 15387eb8f069SAndrzej Hajda static struct device_node * 15397eb8f069SAndrzej Hajda of_get_child_by_name_reg(struct device_node *parent, const char *name, u32 reg) 15407eb8f069SAndrzej Hajda { 15417eb8f069SAndrzej Hajda struct device_node *np; 15427eb8f069SAndrzej Hajda 15437eb8f069SAndrzej Hajda for_each_child_of_node(parent, np) { 15447eb8f069SAndrzej Hajda u32 r; 15457eb8f069SAndrzej Hajda 15467eb8f069SAndrzej Hajda if (!np->name || of_node_cmp(np->name, name)) 15477eb8f069SAndrzej Hajda continue; 15487eb8f069SAndrzej Hajda 15497eb8f069SAndrzej Hajda if (of_property_read_u32(np, "reg", &r) < 0) 15507eb8f069SAndrzej Hajda r = 0; 15517eb8f069SAndrzej Hajda 15527eb8f069SAndrzej Hajda if (reg == r) 15537eb8f069SAndrzej Hajda break; 15547eb8f069SAndrzej Hajda } 15557eb8f069SAndrzej Hajda 15567eb8f069SAndrzej Hajda return np; 15577eb8f069SAndrzej Hajda } 15587eb8f069SAndrzej Hajda 15597eb8f069SAndrzej Hajda static struct device_node *of_graph_get_port_by_reg(struct device_node *parent, 15607eb8f069SAndrzej Hajda u32 reg) 15617eb8f069SAndrzej Hajda { 15627eb8f069SAndrzej Hajda struct device_node *ports, *port; 15637eb8f069SAndrzej Hajda 15647eb8f069SAndrzej Hajda ports = of_get_child_by_name(parent, "ports"); 15657eb8f069SAndrzej Hajda if (ports) 15667eb8f069SAndrzej Hajda parent = ports; 15677eb8f069SAndrzej Hajda 15687eb8f069SAndrzej Hajda port = of_get_child_by_name_reg(parent, "port", reg); 15697eb8f069SAndrzej Hajda 15707eb8f069SAndrzej Hajda of_node_put(ports); 15717eb8f069SAndrzej Hajda 15727eb8f069SAndrzej Hajda return port; 15737eb8f069SAndrzej Hajda } 15747eb8f069SAndrzej Hajda 15757eb8f069SAndrzej Hajda static struct device_node * 15767eb8f069SAndrzej Hajda of_graph_get_endpoint_by_reg(struct device_node *port, u32 reg) 15777eb8f069SAndrzej Hajda { 15787eb8f069SAndrzej Hajda return of_get_child_by_name_reg(port, "endpoint", reg); 15797eb8f069SAndrzej Hajda } 15807eb8f069SAndrzej Hajda 15817eb8f069SAndrzej Hajda static int exynos_dsi_of_read_u32(const struct device_node *np, 15827eb8f069SAndrzej Hajda const char *propname, u32 *out_value) 15837eb8f069SAndrzej Hajda { 15847eb8f069SAndrzej Hajda int ret = of_property_read_u32(np, propname, out_value); 15857eb8f069SAndrzej Hajda 15867eb8f069SAndrzej Hajda if (ret < 0) 15877eb8f069SAndrzej Hajda pr_err("%s: failed to get '%s' property\n", np->full_name, 15887eb8f069SAndrzej Hajda propname); 15897eb8f069SAndrzej Hajda 15907eb8f069SAndrzej Hajda return ret; 15917eb8f069SAndrzej Hajda } 15927eb8f069SAndrzej Hajda 15937eb8f069SAndrzej Hajda enum { 15947eb8f069SAndrzej Hajda DSI_PORT_IN, 15957eb8f069SAndrzej Hajda DSI_PORT_OUT 15967eb8f069SAndrzej Hajda }; 15977eb8f069SAndrzej Hajda 15987eb8f069SAndrzej Hajda static int exynos_dsi_parse_dt(struct exynos_dsi *dsi) 15997eb8f069SAndrzej Hajda { 16007eb8f069SAndrzej Hajda struct device *dev = dsi->dev; 16017eb8f069SAndrzej Hajda struct device_node *node = dev->of_node; 16027eb8f069SAndrzej Hajda struct device_node *port, *ep; 16037eb8f069SAndrzej Hajda int ret; 16047eb8f069SAndrzej Hajda 16057eb8f069SAndrzej Hajda ret = exynos_dsi_of_read_u32(node, "samsung,pll-clock-frequency", 16067eb8f069SAndrzej Hajda &dsi->pll_clk_rate); 16077eb8f069SAndrzej Hajda if (ret < 0) 16087eb8f069SAndrzej Hajda return ret; 16097eb8f069SAndrzej Hajda 16107eb8f069SAndrzej Hajda port = of_graph_get_port_by_reg(node, DSI_PORT_OUT); 16117eb8f069SAndrzej Hajda if (!port) { 16127eb8f069SAndrzej Hajda dev_err(dev, "no output port specified\n"); 16137eb8f069SAndrzej Hajda return -EINVAL; 16147eb8f069SAndrzej Hajda } 16157eb8f069SAndrzej Hajda 16167eb8f069SAndrzej Hajda ep = of_graph_get_endpoint_by_reg(port, 0); 16177eb8f069SAndrzej Hajda of_node_put(port); 16187eb8f069SAndrzej Hajda if (!ep) { 16197eb8f069SAndrzej Hajda dev_err(dev, "no endpoint specified in output port\n"); 16207eb8f069SAndrzej Hajda return -EINVAL; 16217eb8f069SAndrzej Hajda } 16227eb8f069SAndrzej Hajda 16237eb8f069SAndrzej Hajda ret = exynos_dsi_of_read_u32(ep, "samsung,burst-clock-frequency", 16247eb8f069SAndrzej Hajda &dsi->burst_clk_rate); 16257eb8f069SAndrzej Hajda if (ret < 0) 16267eb8f069SAndrzej Hajda goto end; 16277eb8f069SAndrzej Hajda 16287eb8f069SAndrzej Hajda ret = exynos_dsi_of_read_u32(ep, "samsung,esc-clock-frequency", 16297eb8f069SAndrzej Hajda &dsi->esc_clk_rate); 16307eb8f069SAndrzej Hajda 16317eb8f069SAndrzej Hajda end: 16327eb8f069SAndrzej Hajda of_node_put(ep); 16337eb8f069SAndrzej Hajda 16347eb8f069SAndrzej Hajda return ret; 16357eb8f069SAndrzej Hajda } 16367eb8f069SAndrzej Hajda 1637f37cd5e8SInki Dae static int exynos_dsi_bind(struct device *dev, struct device *master, 1638f37cd5e8SInki Dae void *data) 1639f37cd5e8SInki Dae { 16402900c69cSAndrzej Hajda struct exynos_drm_display *display = dev_get_drvdata(dev); 16412900c69cSAndrzej Hajda struct exynos_dsi *dsi = display->ctx; 1642f37cd5e8SInki Dae struct drm_device *drm_dev = data; 1643f37cd5e8SInki Dae int ret; 1644f37cd5e8SInki Dae 16452900c69cSAndrzej Hajda ret = exynos_drm_create_enc_conn(drm_dev, display); 1646f37cd5e8SInki Dae if (ret) { 1647f37cd5e8SInki Dae DRM_ERROR("Encoder create [%d] failed with %d\n", 16482900c69cSAndrzej Hajda display->type, ret); 1649f37cd5e8SInki Dae return ret; 1650f37cd5e8SInki Dae } 1651f37cd5e8SInki Dae 1652f37cd5e8SInki Dae return mipi_dsi_host_register(&dsi->dsi_host); 1653f37cd5e8SInki Dae } 1654f37cd5e8SInki Dae 1655f37cd5e8SInki Dae static void exynos_dsi_unbind(struct device *dev, struct device *master, 1656f37cd5e8SInki Dae void *data) 1657f37cd5e8SInki Dae { 16582900c69cSAndrzej Hajda struct exynos_drm_display *display = dev_get_drvdata(dev); 16592900c69cSAndrzej Hajda struct exynos_dsi *dsi = display->ctx; 1660f37cd5e8SInki Dae 16612900c69cSAndrzej Hajda exynos_dsi_dpms(display, DRM_MODE_DPMS_OFF); 1662f37cd5e8SInki Dae 16630ae46015SAndrzej Hajda mipi_dsi_host_unregister(&dsi->dsi_host); 1664f37cd5e8SInki Dae } 1665f37cd5e8SInki Dae 1666f37cd5e8SInki Dae static const struct component_ops exynos_dsi_component_ops = { 1667f37cd5e8SInki Dae .bind = exynos_dsi_bind, 1668f37cd5e8SInki Dae .unbind = exynos_dsi_unbind, 1669f37cd5e8SInki Dae }; 1670f37cd5e8SInki Dae 16717eb8f069SAndrzej Hajda static int exynos_dsi_probe(struct platform_device *pdev) 16727eb8f069SAndrzej Hajda { 16732900c69cSAndrzej Hajda struct device *dev = &pdev->dev; 16747eb8f069SAndrzej Hajda struct resource *res; 16757eb8f069SAndrzej Hajda struct exynos_dsi *dsi; 16767eb8f069SAndrzej Hajda int ret; 16777eb8f069SAndrzej Hajda 16782900c69cSAndrzej Hajda dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); 16792900c69cSAndrzej Hajda if (!dsi) 16802900c69cSAndrzej Hajda return -ENOMEM; 16812900c69cSAndrzej Hajda 16822900c69cSAndrzej Hajda dsi->display.type = EXYNOS_DISPLAY_TYPE_LCD; 16832900c69cSAndrzej Hajda dsi->display.ops = &exynos_dsi_display_ops; 16842900c69cSAndrzej Hajda 16852900c69cSAndrzej Hajda ret = exynos_drm_component_add(dev, EXYNOS_DEVICE_TYPE_CONNECTOR, 16862900c69cSAndrzej Hajda dsi->display.type); 1687df5225bcSInki Dae if (ret) 1688df5225bcSInki Dae return ret; 1689df5225bcSInki Dae 1690e17ddeccSYoungJun Cho /* To be checked as invalid one */ 1691e17ddeccSYoungJun Cho dsi->te_gpio = -ENOENT; 1692e17ddeccSYoungJun Cho 16937eb8f069SAndrzej Hajda init_completion(&dsi->completed); 16947eb8f069SAndrzej Hajda spin_lock_init(&dsi->transfer_lock); 16957eb8f069SAndrzej Hajda INIT_LIST_HEAD(&dsi->transfer_list); 16967eb8f069SAndrzej Hajda 16977eb8f069SAndrzej Hajda dsi->dsi_host.ops = &exynos_dsi_ops; 16987eb8f069SAndrzej Hajda dsi->dsi_host.dev = &pdev->dev; 16997eb8f069SAndrzej Hajda 17007eb8f069SAndrzej Hajda dsi->dev = &pdev->dev; 17019a320415SYoungJun Cho dsi->driver_data = exynos_dsi_get_driver_data(pdev); 17027eb8f069SAndrzej Hajda 17037eb8f069SAndrzej Hajda ret = exynos_dsi_parse_dt(dsi); 17047eb8f069SAndrzej Hajda if (ret) 1705df5225bcSInki Dae goto err_del_component; 17067eb8f069SAndrzej Hajda 17077eb8f069SAndrzej Hajda dsi->supplies[0].supply = "vddcore"; 17087eb8f069SAndrzej Hajda dsi->supplies[1].supply = "vddio"; 17097eb8f069SAndrzej Hajda ret = devm_regulator_bulk_get(&pdev->dev, ARRAY_SIZE(dsi->supplies), 17107eb8f069SAndrzej Hajda dsi->supplies); 17117eb8f069SAndrzej Hajda if (ret) { 17127eb8f069SAndrzej Hajda dev_info(&pdev->dev, "failed to get regulators: %d\n", ret); 17137eb8f069SAndrzej Hajda return -EPROBE_DEFER; 17147eb8f069SAndrzej Hajda } 17157eb8f069SAndrzej Hajda 17167eb8f069SAndrzej Hajda dsi->pll_clk = devm_clk_get(&pdev->dev, "pll_clk"); 17177eb8f069SAndrzej Hajda if (IS_ERR(dsi->pll_clk)) { 17187eb8f069SAndrzej Hajda dev_info(&pdev->dev, "failed to get dsi pll input clock\n"); 1719df5225bcSInki Dae ret = PTR_ERR(dsi->pll_clk); 1720df5225bcSInki Dae goto err_del_component; 17217eb8f069SAndrzej Hajda } 17227eb8f069SAndrzej Hajda 17237eb8f069SAndrzej Hajda dsi->bus_clk = devm_clk_get(&pdev->dev, "bus_clk"); 17247eb8f069SAndrzej Hajda if (IS_ERR(dsi->bus_clk)) { 17257eb8f069SAndrzej Hajda dev_info(&pdev->dev, "failed to get dsi bus clock\n"); 1726df5225bcSInki Dae ret = PTR_ERR(dsi->bus_clk); 1727df5225bcSInki Dae goto err_del_component; 17287eb8f069SAndrzej Hajda } 17297eb8f069SAndrzej Hajda 17307eb8f069SAndrzej Hajda res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 17317eb8f069SAndrzej Hajda dsi->reg_base = devm_ioremap_resource(&pdev->dev, res); 1732293d3f6aSJingoo Han if (IS_ERR(dsi->reg_base)) { 17337eb8f069SAndrzej Hajda dev_err(&pdev->dev, "failed to remap io region\n"); 1734df5225bcSInki Dae ret = PTR_ERR(dsi->reg_base); 1735df5225bcSInki Dae goto err_del_component; 17367eb8f069SAndrzej Hajda } 17377eb8f069SAndrzej Hajda 17387eb8f069SAndrzej Hajda dsi->phy = devm_phy_get(&pdev->dev, "dsim"); 17397eb8f069SAndrzej Hajda if (IS_ERR(dsi->phy)) { 17407eb8f069SAndrzej Hajda dev_info(&pdev->dev, "failed to get dsim phy\n"); 1741df5225bcSInki Dae ret = PTR_ERR(dsi->phy); 1742df5225bcSInki Dae goto err_del_component; 17437eb8f069SAndrzej Hajda } 17447eb8f069SAndrzej Hajda 17457eb8f069SAndrzej Hajda dsi->irq = platform_get_irq(pdev, 0); 17467eb8f069SAndrzej Hajda if (dsi->irq < 0) { 17477eb8f069SAndrzej Hajda dev_err(&pdev->dev, "failed to request dsi irq resource\n"); 1748df5225bcSInki Dae ret = dsi->irq; 1749df5225bcSInki Dae goto err_del_component; 17507eb8f069SAndrzej Hajda } 17517eb8f069SAndrzej Hajda 17527eb8f069SAndrzej Hajda irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN); 17537eb8f069SAndrzej Hajda ret = devm_request_threaded_irq(&pdev->dev, dsi->irq, NULL, 17547eb8f069SAndrzej Hajda exynos_dsi_irq, IRQF_ONESHOT, 17557eb8f069SAndrzej Hajda dev_name(&pdev->dev), dsi); 17567eb8f069SAndrzej Hajda if (ret) { 17577eb8f069SAndrzej Hajda dev_err(&pdev->dev, "failed to request dsi irq\n"); 1758df5225bcSInki Dae goto err_del_component; 17597eb8f069SAndrzej Hajda } 17607eb8f069SAndrzej Hajda 17617eb8f069SAndrzej Hajda exynos_dsi_display.ctx = dsi; 17627eb8f069SAndrzej Hajda 17637eb8f069SAndrzej Hajda platform_set_drvdata(pdev, &exynos_dsi_display); 17647eb8f069SAndrzej Hajda 1765df5225bcSInki Dae ret = component_add(&pdev->dev, &exynos_dsi_component_ops); 1766df5225bcSInki Dae if (ret) 1767df5225bcSInki Dae goto err_del_component; 1768df5225bcSInki Dae 1769df5225bcSInki Dae return ret; 1770df5225bcSInki Dae 1771df5225bcSInki Dae err_del_component: 1772df5225bcSInki Dae exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR); 1773df5225bcSInki Dae return ret; 17747eb8f069SAndrzej Hajda } 17757eb8f069SAndrzej Hajda 17767eb8f069SAndrzej Hajda static int exynos_dsi_remove(struct platform_device *pdev) 17777eb8f069SAndrzej Hajda { 1778df5225bcSInki Dae component_del(&pdev->dev, &exynos_dsi_component_ops); 1779df5225bcSInki Dae exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR); 1780df5225bcSInki Dae 17817eb8f069SAndrzej Hajda return 0; 17827eb8f069SAndrzej Hajda } 17837eb8f069SAndrzej Hajda 17847eb8f069SAndrzej Hajda struct platform_driver dsi_driver = { 17857eb8f069SAndrzej Hajda .probe = exynos_dsi_probe, 17867eb8f069SAndrzej Hajda .remove = exynos_dsi_remove, 17877eb8f069SAndrzej Hajda .driver = { 17887eb8f069SAndrzej Hajda .name = "exynos-dsi", 17897eb8f069SAndrzej Hajda .owner = THIS_MODULE, 17907eb8f069SAndrzej Hajda .of_match_table = exynos_dsi_of_match, 17917eb8f069SAndrzej Hajda }, 17927eb8f069SAndrzej Hajda }; 17937eb8f069SAndrzej Hajda 17947eb8f069SAndrzej Hajda MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>"); 17957eb8f069SAndrzej Hajda MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>"); 17967eb8f069SAndrzej Hajda MODULE_DESCRIPTION("Samsung SoC MIPI DSI Master"); 17977eb8f069SAndrzej Hajda MODULE_LICENSE("GPL v2"); 1798