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> 197eb8f069SAndrzej Hajda #include <linux/irq.h> 207eb8f069SAndrzej Hajda #include <linux/phy/phy.h> 217eb8f069SAndrzej Hajda #include <linux/regulator/consumer.h> 22f37cd5e8SInki Dae #include <linux/component.h> 237eb8f069SAndrzej Hajda 247eb8f069SAndrzej Hajda #include <video/mipi_display.h> 257eb8f069SAndrzej Hajda #include <video/videomode.h> 267eb8f069SAndrzej Hajda 277eb8f069SAndrzej Hajda #include "exynos_drm_drv.h" 287eb8f069SAndrzej Hajda 297eb8f069SAndrzej Hajda /* returns true iff both arguments logically differs */ 307eb8f069SAndrzej Hajda #define NEQV(a, b) (!(a) ^ !(b)) 317eb8f069SAndrzej Hajda 327eb8f069SAndrzej Hajda #define DSIM_STATUS_REG 0x0 /* Status register */ 337eb8f069SAndrzej Hajda #define DSIM_SWRST_REG 0x4 /* Software reset register */ 347eb8f069SAndrzej Hajda #define DSIM_CLKCTRL_REG 0x8 /* Clock control register */ 357eb8f069SAndrzej Hajda #define DSIM_TIMEOUT_REG 0xc /* Time out register */ 367eb8f069SAndrzej Hajda #define DSIM_CONFIG_REG 0x10 /* Configuration register */ 377eb8f069SAndrzej Hajda #define DSIM_ESCMODE_REG 0x14 /* Escape mode register */ 387eb8f069SAndrzej Hajda 397eb8f069SAndrzej Hajda /* Main display image resolution register */ 407eb8f069SAndrzej Hajda #define DSIM_MDRESOL_REG 0x18 417eb8f069SAndrzej Hajda #define DSIM_MVPORCH_REG 0x1c /* Main display Vporch register */ 427eb8f069SAndrzej Hajda #define DSIM_MHPORCH_REG 0x20 /* Main display Hporch register */ 437eb8f069SAndrzej Hajda #define DSIM_MSYNC_REG 0x24 /* Main display sync area register */ 447eb8f069SAndrzej Hajda 457eb8f069SAndrzej Hajda /* Sub display image resolution register */ 467eb8f069SAndrzej Hajda #define DSIM_SDRESOL_REG 0x28 477eb8f069SAndrzej Hajda #define DSIM_INTSRC_REG 0x2c /* Interrupt source register */ 487eb8f069SAndrzej Hajda #define DSIM_INTMSK_REG 0x30 /* Interrupt mask register */ 497eb8f069SAndrzej Hajda #define DSIM_PKTHDR_REG 0x34 /* Packet Header FIFO register */ 507eb8f069SAndrzej Hajda #define DSIM_PAYLOAD_REG 0x38 /* Payload FIFO register */ 517eb8f069SAndrzej Hajda #define DSIM_RXFIFO_REG 0x3c /* Read FIFO register */ 527eb8f069SAndrzej Hajda #define DSIM_FIFOTHLD_REG 0x40 /* FIFO threshold level register */ 537eb8f069SAndrzej Hajda #define DSIM_FIFOCTRL_REG 0x44 /* FIFO status and control register */ 547eb8f069SAndrzej Hajda 557eb8f069SAndrzej Hajda /* FIFO memory AC characteristic register */ 567eb8f069SAndrzej Hajda #define DSIM_PLLCTRL_REG 0x4c /* PLL control register */ 577eb8f069SAndrzej Hajda #define DSIM_PLLTMR_REG 0x50 /* PLL timer register */ 587eb8f069SAndrzej Hajda #define DSIM_PHYACCHR_REG 0x54 /* D-PHY AC characteristic register */ 597eb8f069SAndrzej Hajda #define DSIM_PHYACCHR1_REG 0x58 /* D-PHY AC characteristic register1 */ 607eb8f069SAndrzej Hajda 617eb8f069SAndrzej Hajda /* DSIM_STATUS */ 627eb8f069SAndrzej Hajda #define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0) 637eb8f069SAndrzej Hajda #define DSIM_STOP_STATE_CLK (1 << 8) 647eb8f069SAndrzej Hajda #define DSIM_TX_READY_HS_CLK (1 << 10) 657eb8f069SAndrzej Hajda #define DSIM_PLL_STABLE (1 << 31) 667eb8f069SAndrzej Hajda 677eb8f069SAndrzej Hajda /* DSIM_SWRST */ 687eb8f069SAndrzej Hajda #define DSIM_FUNCRST (1 << 16) 697eb8f069SAndrzej Hajda #define DSIM_SWRST (1 << 0) 707eb8f069SAndrzej Hajda 717eb8f069SAndrzej Hajda /* DSIM_TIMEOUT */ 727eb8f069SAndrzej Hajda #define DSIM_LPDR_TIMEOUT(x) ((x) << 0) 737eb8f069SAndrzej Hajda #define DSIM_BTA_TIMEOUT(x) ((x) << 16) 747eb8f069SAndrzej Hajda 757eb8f069SAndrzej Hajda /* DSIM_CLKCTRL */ 767eb8f069SAndrzej Hajda #define DSIM_ESC_PRESCALER(x) (((x) & 0xffff) << 0) 777eb8f069SAndrzej Hajda #define DSIM_ESC_PRESCALER_MASK (0xffff << 0) 787eb8f069SAndrzej Hajda #define DSIM_LANE_ESC_CLK_EN_CLK (1 << 19) 797eb8f069SAndrzej Hajda #define DSIM_LANE_ESC_CLK_EN_DATA(x) (((x) & 0xf) << 20) 807eb8f069SAndrzej Hajda #define DSIM_LANE_ESC_CLK_EN_DATA_MASK (0xf << 20) 817eb8f069SAndrzej Hajda #define DSIM_BYTE_CLKEN (1 << 24) 827eb8f069SAndrzej Hajda #define DSIM_BYTE_CLK_SRC(x) (((x) & 0x3) << 25) 837eb8f069SAndrzej Hajda #define DSIM_BYTE_CLK_SRC_MASK (0x3 << 25) 847eb8f069SAndrzej Hajda #define DSIM_PLL_BYPASS (1 << 27) 857eb8f069SAndrzej Hajda #define DSIM_ESC_CLKEN (1 << 28) 867eb8f069SAndrzej Hajda #define DSIM_TX_REQUEST_HSCLK (1 << 31) 877eb8f069SAndrzej Hajda 887eb8f069SAndrzej Hajda /* DSIM_CONFIG */ 897eb8f069SAndrzej Hajda #define DSIM_LANE_EN_CLK (1 << 0) 907eb8f069SAndrzej Hajda #define DSIM_LANE_EN(x) (((x) & 0xf) << 1) 917eb8f069SAndrzej Hajda #define DSIM_NUM_OF_DATA_LANE(x) (((x) & 0x3) << 5) 927eb8f069SAndrzej Hajda #define DSIM_SUB_PIX_FORMAT(x) (((x) & 0x7) << 8) 937eb8f069SAndrzej Hajda #define DSIM_MAIN_PIX_FORMAT_MASK (0x7 << 12) 947eb8f069SAndrzej Hajda #define DSIM_MAIN_PIX_FORMAT_RGB888 (0x7 << 12) 957eb8f069SAndrzej Hajda #define DSIM_MAIN_PIX_FORMAT_RGB666 (0x6 << 12) 967eb8f069SAndrzej Hajda #define DSIM_MAIN_PIX_FORMAT_RGB666_P (0x5 << 12) 977eb8f069SAndrzej Hajda #define DSIM_MAIN_PIX_FORMAT_RGB565 (0x4 << 12) 987eb8f069SAndrzej Hajda #define DSIM_SUB_VC (((x) & 0x3) << 16) 997eb8f069SAndrzej Hajda #define DSIM_MAIN_VC (((x) & 0x3) << 18) 1007eb8f069SAndrzej Hajda #define DSIM_HSA_MODE (1 << 20) 1017eb8f069SAndrzej Hajda #define DSIM_HBP_MODE (1 << 21) 1027eb8f069SAndrzej Hajda #define DSIM_HFP_MODE (1 << 22) 1037eb8f069SAndrzej Hajda #define DSIM_HSE_MODE (1 << 23) 1047eb8f069SAndrzej Hajda #define DSIM_AUTO_MODE (1 << 24) 1057eb8f069SAndrzej Hajda #define DSIM_VIDEO_MODE (1 << 25) 1067eb8f069SAndrzej Hajda #define DSIM_BURST_MODE (1 << 26) 1077eb8f069SAndrzej Hajda #define DSIM_SYNC_INFORM (1 << 27) 1087eb8f069SAndrzej Hajda #define DSIM_EOT_DISABLE (1 << 28) 1097eb8f069SAndrzej Hajda #define DSIM_MFLUSH_VS (1 << 29) 1107eb8f069SAndrzej Hajda 1117eb8f069SAndrzej Hajda /* DSIM_ESCMODE */ 1127eb8f069SAndrzej Hajda #define DSIM_TX_TRIGGER_RST (1 << 4) 1137eb8f069SAndrzej Hajda #define DSIM_TX_LPDT_LP (1 << 6) 1147eb8f069SAndrzej Hajda #define DSIM_CMD_LPDT_LP (1 << 7) 1157eb8f069SAndrzej Hajda #define DSIM_FORCE_BTA (1 << 16) 1167eb8f069SAndrzej Hajda #define DSIM_FORCE_STOP_STATE (1 << 20) 1177eb8f069SAndrzej Hajda #define DSIM_STOP_STATE_CNT(x) (((x) & 0x7ff) << 21) 1187eb8f069SAndrzej Hajda #define DSIM_STOP_STATE_CNT_MASK (0x7ff << 21) 1197eb8f069SAndrzej Hajda 1207eb8f069SAndrzej Hajda /* DSIM_MDRESOL */ 1217eb8f069SAndrzej Hajda #define DSIM_MAIN_STAND_BY (1 << 31) 1227eb8f069SAndrzej Hajda #define DSIM_MAIN_VRESOL(x) (((x) & 0x7ff) << 16) 1237eb8f069SAndrzej Hajda #define DSIM_MAIN_HRESOL(x) (((x) & 0X7ff) << 0) 1247eb8f069SAndrzej Hajda 1257eb8f069SAndrzej Hajda /* DSIM_MVPORCH */ 1267eb8f069SAndrzej Hajda #define DSIM_CMD_ALLOW(x) ((x) << 28) 1277eb8f069SAndrzej Hajda #define DSIM_STABLE_VFP(x) ((x) << 16) 1287eb8f069SAndrzej Hajda #define DSIM_MAIN_VBP(x) ((x) << 0) 1297eb8f069SAndrzej Hajda #define DSIM_CMD_ALLOW_MASK (0xf << 28) 1307eb8f069SAndrzej Hajda #define DSIM_STABLE_VFP_MASK (0x7ff << 16) 1317eb8f069SAndrzej Hajda #define DSIM_MAIN_VBP_MASK (0x7ff << 0) 1327eb8f069SAndrzej Hajda 1337eb8f069SAndrzej Hajda /* DSIM_MHPORCH */ 1347eb8f069SAndrzej Hajda #define DSIM_MAIN_HFP(x) ((x) << 16) 1357eb8f069SAndrzej Hajda #define DSIM_MAIN_HBP(x) ((x) << 0) 1367eb8f069SAndrzej Hajda #define DSIM_MAIN_HFP_MASK ((0xffff) << 16) 1377eb8f069SAndrzej Hajda #define DSIM_MAIN_HBP_MASK ((0xffff) << 0) 1387eb8f069SAndrzej Hajda 1397eb8f069SAndrzej Hajda /* DSIM_MSYNC */ 1407eb8f069SAndrzej Hajda #define DSIM_MAIN_VSA(x) ((x) << 22) 1417eb8f069SAndrzej Hajda #define DSIM_MAIN_HSA(x) ((x) << 0) 1427eb8f069SAndrzej Hajda #define DSIM_MAIN_VSA_MASK ((0x3ff) << 22) 1437eb8f069SAndrzej Hajda #define DSIM_MAIN_HSA_MASK ((0xffff) << 0) 1447eb8f069SAndrzej Hajda 1457eb8f069SAndrzej Hajda /* DSIM_SDRESOL */ 1467eb8f069SAndrzej Hajda #define DSIM_SUB_STANDY(x) ((x) << 31) 1477eb8f069SAndrzej Hajda #define DSIM_SUB_VRESOL(x) ((x) << 16) 1487eb8f069SAndrzej Hajda #define DSIM_SUB_HRESOL(x) ((x) << 0) 1497eb8f069SAndrzej Hajda #define DSIM_SUB_STANDY_MASK ((0x1) << 31) 1507eb8f069SAndrzej Hajda #define DSIM_SUB_VRESOL_MASK ((0x7ff) << 16) 1517eb8f069SAndrzej Hajda #define DSIM_SUB_HRESOL_MASK ((0x7ff) << 0) 1527eb8f069SAndrzej Hajda 1537eb8f069SAndrzej Hajda /* DSIM_INTSRC */ 1547eb8f069SAndrzej Hajda #define DSIM_INT_PLL_STABLE (1 << 31) 1557eb8f069SAndrzej Hajda #define DSIM_INT_SW_RST_RELEASE (1 << 30) 1567eb8f069SAndrzej Hajda #define DSIM_INT_SFR_FIFO_EMPTY (1 << 29) 1577eb8f069SAndrzej Hajda #define DSIM_INT_BTA (1 << 25) 1587eb8f069SAndrzej Hajda #define DSIM_INT_FRAME_DONE (1 << 24) 1597eb8f069SAndrzej Hajda #define DSIM_INT_RX_TIMEOUT (1 << 21) 1607eb8f069SAndrzej Hajda #define DSIM_INT_BTA_TIMEOUT (1 << 20) 1617eb8f069SAndrzej Hajda #define DSIM_INT_RX_DONE (1 << 18) 1627eb8f069SAndrzej Hajda #define DSIM_INT_RX_TE (1 << 17) 1637eb8f069SAndrzej Hajda #define DSIM_INT_RX_ACK (1 << 16) 1647eb8f069SAndrzej Hajda #define DSIM_INT_RX_ECC_ERR (1 << 15) 1657eb8f069SAndrzej Hajda #define DSIM_INT_RX_CRC_ERR (1 << 14) 1667eb8f069SAndrzej Hajda 1677eb8f069SAndrzej Hajda /* DSIM_FIFOCTRL */ 1687eb8f069SAndrzej Hajda #define DSIM_RX_DATA_FULL (1 << 25) 1697eb8f069SAndrzej Hajda #define DSIM_RX_DATA_EMPTY (1 << 24) 1707eb8f069SAndrzej Hajda #define DSIM_SFR_HEADER_FULL (1 << 23) 1717eb8f069SAndrzej Hajda #define DSIM_SFR_HEADER_EMPTY (1 << 22) 1727eb8f069SAndrzej Hajda #define DSIM_SFR_PAYLOAD_FULL (1 << 21) 1737eb8f069SAndrzej Hajda #define DSIM_SFR_PAYLOAD_EMPTY (1 << 20) 1747eb8f069SAndrzej Hajda #define DSIM_I80_HEADER_FULL (1 << 19) 1757eb8f069SAndrzej Hajda #define DSIM_I80_HEADER_EMPTY (1 << 18) 1767eb8f069SAndrzej Hajda #define DSIM_I80_PAYLOAD_FULL (1 << 17) 1777eb8f069SAndrzej Hajda #define DSIM_I80_PAYLOAD_EMPTY (1 << 16) 1787eb8f069SAndrzej Hajda #define DSIM_SD_HEADER_FULL (1 << 15) 1797eb8f069SAndrzej Hajda #define DSIM_SD_HEADER_EMPTY (1 << 14) 1807eb8f069SAndrzej Hajda #define DSIM_SD_PAYLOAD_FULL (1 << 13) 1817eb8f069SAndrzej Hajda #define DSIM_SD_PAYLOAD_EMPTY (1 << 12) 1827eb8f069SAndrzej Hajda #define DSIM_MD_HEADER_FULL (1 << 11) 1837eb8f069SAndrzej Hajda #define DSIM_MD_HEADER_EMPTY (1 << 10) 1847eb8f069SAndrzej Hajda #define DSIM_MD_PAYLOAD_FULL (1 << 9) 1857eb8f069SAndrzej Hajda #define DSIM_MD_PAYLOAD_EMPTY (1 << 8) 1867eb8f069SAndrzej Hajda #define DSIM_RX_FIFO (1 << 4) 1877eb8f069SAndrzej Hajda #define DSIM_SFR_FIFO (1 << 3) 1887eb8f069SAndrzej Hajda #define DSIM_I80_FIFO (1 << 2) 1897eb8f069SAndrzej Hajda #define DSIM_SD_FIFO (1 << 1) 1907eb8f069SAndrzej Hajda #define DSIM_MD_FIFO (1 << 0) 1917eb8f069SAndrzej Hajda 1927eb8f069SAndrzej Hajda /* DSIM_PHYACCHR */ 1937eb8f069SAndrzej Hajda #define DSIM_AFC_EN (1 << 14) 1947eb8f069SAndrzej Hajda #define DSIM_AFC_CTL(x) (((x) & 0x7) << 5) 1957eb8f069SAndrzej Hajda 1967eb8f069SAndrzej Hajda /* DSIM_PLLCTRL */ 1977eb8f069SAndrzej Hajda #define DSIM_FREQ_BAND(x) ((x) << 24) 1987eb8f069SAndrzej Hajda #define DSIM_PLL_EN (1 << 23) 1997eb8f069SAndrzej Hajda #define DSIM_PLL_P(x) ((x) << 13) 2007eb8f069SAndrzej Hajda #define DSIM_PLL_M(x) ((x) << 4) 2017eb8f069SAndrzej Hajda #define DSIM_PLL_S(x) ((x) << 1) 2027eb8f069SAndrzej Hajda 2037eb8f069SAndrzej Hajda #define DSI_MAX_BUS_WIDTH 4 2047eb8f069SAndrzej Hajda #define DSI_NUM_VIRTUAL_CHANNELS 4 2057eb8f069SAndrzej Hajda #define DSI_TX_FIFO_SIZE 2048 2067eb8f069SAndrzej Hajda #define DSI_RX_FIFO_SIZE 256 2077eb8f069SAndrzej Hajda #define DSI_XFER_TIMEOUT_MS 100 2087eb8f069SAndrzej Hajda #define DSI_RX_FIFO_EMPTY 0x30800002 2097eb8f069SAndrzej Hajda 2107eb8f069SAndrzej Hajda enum exynos_dsi_transfer_type { 2117eb8f069SAndrzej Hajda EXYNOS_DSI_TX, 2127eb8f069SAndrzej Hajda EXYNOS_DSI_RX, 2137eb8f069SAndrzej Hajda }; 2147eb8f069SAndrzej Hajda 2157eb8f069SAndrzej Hajda struct exynos_dsi_transfer { 2167eb8f069SAndrzej Hajda struct list_head list; 2177eb8f069SAndrzej Hajda struct completion completed; 2187eb8f069SAndrzej Hajda int result; 2197eb8f069SAndrzej Hajda u8 data_id; 2207eb8f069SAndrzej Hajda u8 data[2]; 2217eb8f069SAndrzej Hajda u16 flags; 2227eb8f069SAndrzej Hajda 2237eb8f069SAndrzej Hajda const u8 *tx_payload; 2247eb8f069SAndrzej Hajda u16 tx_len; 2257eb8f069SAndrzej Hajda u16 tx_done; 2267eb8f069SAndrzej Hajda 2277eb8f069SAndrzej Hajda u8 *rx_payload; 2287eb8f069SAndrzej Hajda u16 rx_len; 2297eb8f069SAndrzej Hajda u16 rx_done; 2307eb8f069SAndrzej Hajda }; 2317eb8f069SAndrzej Hajda 2327eb8f069SAndrzej Hajda #define DSIM_STATE_ENABLED BIT(0) 2337eb8f069SAndrzej Hajda #define DSIM_STATE_INITIALIZED BIT(1) 2347eb8f069SAndrzej Hajda #define DSIM_STATE_CMD_LPM BIT(2) 2357eb8f069SAndrzej Hajda 2367eb8f069SAndrzej Hajda struct exynos_dsi { 2377eb8f069SAndrzej Hajda struct mipi_dsi_host dsi_host; 2387eb8f069SAndrzej Hajda struct drm_connector connector; 2397eb8f069SAndrzej Hajda struct drm_encoder *encoder; 2407eb8f069SAndrzej Hajda struct device_node *panel_node; 2417eb8f069SAndrzej Hajda struct drm_panel *panel; 2427eb8f069SAndrzej Hajda struct device *dev; 2437eb8f069SAndrzej Hajda 2447eb8f069SAndrzej Hajda void __iomem *reg_base; 2457eb8f069SAndrzej Hajda struct phy *phy; 2467eb8f069SAndrzej Hajda struct clk *pll_clk; 2477eb8f069SAndrzej Hajda struct clk *bus_clk; 2487eb8f069SAndrzej Hajda struct regulator_bulk_data supplies[2]; 2497eb8f069SAndrzej Hajda int irq; 2507eb8f069SAndrzej Hajda 2517eb8f069SAndrzej Hajda u32 pll_clk_rate; 2527eb8f069SAndrzej Hajda u32 burst_clk_rate; 2537eb8f069SAndrzej Hajda u32 esc_clk_rate; 2547eb8f069SAndrzej Hajda u32 lanes; 2557eb8f069SAndrzej Hajda u32 mode_flags; 2567eb8f069SAndrzej Hajda u32 format; 2577eb8f069SAndrzej Hajda struct videomode vm; 2587eb8f069SAndrzej Hajda 2597eb8f069SAndrzej Hajda int state; 2607eb8f069SAndrzej Hajda struct drm_property *brightness; 2617eb8f069SAndrzej Hajda struct completion completed; 2627eb8f069SAndrzej Hajda 2637eb8f069SAndrzej Hajda spinlock_t transfer_lock; /* protects transfer_list */ 2647eb8f069SAndrzej Hajda struct list_head transfer_list; 2657eb8f069SAndrzej Hajda }; 2667eb8f069SAndrzej Hajda 2677eb8f069SAndrzej Hajda #define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host) 2687eb8f069SAndrzej Hajda #define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector) 2697eb8f069SAndrzej Hajda 2707eb8f069SAndrzej Hajda static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi) 2717eb8f069SAndrzej Hajda { 2727eb8f069SAndrzej Hajda if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300))) 2737eb8f069SAndrzej Hajda return; 2747eb8f069SAndrzej Hajda 2757eb8f069SAndrzej Hajda dev_err(dsi->dev, "timeout waiting for reset\n"); 2767eb8f069SAndrzej Hajda } 2777eb8f069SAndrzej Hajda 2787eb8f069SAndrzej Hajda static void exynos_dsi_reset(struct exynos_dsi *dsi) 2797eb8f069SAndrzej Hajda { 2807eb8f069SAndrzej Hajda reinit_completion(&dsi->completed); 2817eb8f069SAndrzej Hajda writel(DSIM_SWRST, dsi->reg_base + DSIM_SWRST_REG); 2827eb8f069SAndrzej Hajda } 2837eb8f069SAndrzej Hajda 2847eb8f069SAndrzej Hajda #ifndef MHZ 2857eb8f069SAndrzej Hajda #define MHZ (1000*1000) 2867eb8f069SAndrzej Hajda #endif 2877eb8f069SAndrzej Hajda 2887eb8f069SAndrzej Hajda static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi, 2897eb8f069SAndrzej Hajda unsigned long fin, unsigned long fout, u8 *p, u16 *m, u8 *s) 2907eb8f069SAndrzej Hajda { 2917eb8f069SAndrzej Hajda unsigned long best_freq = 0; 2927eb8f069SAndrzej Hajda u32 min_delta = 0xffffffff; 2937eb8f069SAndrzej Hajda u8 p_min, p_max; 2947eb8f069SAndrzej Hajda u8 _p, uninitialized_var(best_p); 2957eb8f069SAndrzej Hajda u16 _m, uninitialized_var(best_m); 2967eb8f069SAndrzej Hajda u8 _s, uninitialized_var(best_s); 2977eb8f069SAndrzej Hajda 2987eb8f069SAndrzej Hajda p_min = DIV_ROUND_UP(fin, (12 * MHZ)); 2997eb8f069SAndrzej Hajda p_max = fin / (6 * MHZ); 3007eb8f069SAndrzej Hajda 3017eb8f069SAndrzej Hajda for (_p = p_min; _p <= p_max; ++_p) { 3027eb8f069SAndrzej Hajda for (_s = 0; _s <= 5; ++_s) { 3037eb8f069SAndrzej Hajda u64 tmp; 3047eb8f069SAndrzej Hajda u32 delta; 3057eb8f069SAndrzej Hajda 3067eb8f069SAndrzej Hajda tmp = (u64)fout * (_p << _s); 3077eb8f069SAndrzej Hajda do_div(tmp, fin); 3087eb8f069SAndrzej Hajda _m = tmp; 3097eb8f069SAndrzej Hajda if (_m < 41 || _m > 125) 3107eb8f069SAndrzej Hajda continue; 3117eb8f069SAndrzej Hajda 3127eb8f069SAndrzej Hajda tmp = (u64)_m * fin; 3137eb8f069SAndrzej Hajda do_div(tmp, _p); 3147eb8f069SAndrzej Hajda if (tmp < 500 * MHZ || tmp > 1000 * MHZ) 3157eb8f069SAndrzej Hajda continue; 3167eb8f069SAndrzej Hajda 3177eb8f069SAndrzej Hajda tmp = (u64)_m * fin; 3187eb8f069SAndrzej Hajda do_div(tmp, _p << _s); 3197eb8f069SAndrzej Hajda 3207eb8f069SAndrzej Hajda delta = abs(fout - tmp); 3217eb8f069SAndrzej Hajda if (delta < min_delta) { 3227eb8f069SAndrzej Hajda best_p = _p; 3237eb8f069SAndrzej Hajda best_m = _m; 3247eb8f069SAndrzej Hajda best_s = _s; 3257eb8f069SAndrzej Hajda min_delta = delta; 3267eb8f069SAndrzej Hajda best_freq = tmp; 3277eb8f069SAndrzej Hajda } 3287eb8f069SAndrzej Hajda } 3297eb8f069SAndrzej Hajda } 3307eb8f069SAndrzej Hajda 3317eb8f069SAndrzej Hajda if (best_freq) { 3327eb8f069SAndrzej Hajda *p = best_p; 3337eb8f069SAndrzej Hajda *m = best_m; 3347eb8f069SAndrzej Hajda *s = best_s; 3357eb8f069SAndrzej Hajda } 3367eb8f069SAndrzej Hajda 3377eb8f069SAndrzej Hajda return best_freq; 3387eb8f069SAndrzej Hajda } 3397eb8f069SAndrzej Hajda 3407eb8f069SAndrzej Hajda static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi, 3417eb8f069SAndrzej Hajda unsigned long freq) 3427eb8f069SAndrzej Hajda { 3437eb8f069SAndrzej Hajda static const unsigned long freq_bands[] = { 3447eb8f069SAndrzej Hajda 100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ, 3457eb8f069SAndrzej Hajda 270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ, 3467eb8f069SAndrzej Hajda 510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ, 3477eb8f069SAndrzej Hajda 770 * MHZ, 870 * MHZ, 950 * MHZ, 3487eb8f069SAndrzej Hajda }; 3497eb8f069SAndrzej Hajda unsigned long fin, fout; 3507eb8f069SAndrzej Hajda int timeout, band; 3517eb8f069SAndrzej Hajda u8 p, s; 3527eb8f069SAndrzej Hajda u16 m; 3537eb8f069SAndrzej Hajda u32 reg; 3547eb8f069SAndrzej Hajda 3557eb8f069SAndrzej Hajda clk_set_rate(dsi->pll_clk, dsi->pll_clk_rate); 3567eb8f069SAndrzej Hajda 3577eb8f069SAndrzej Hajda fin = clk_get_rate(dsi->pll_clk); 3587eb8f069SAndrzej Hajda if (!fin) { 3597eb8f069SAndrzej Hajda dev_err(dsi->dev, "failed to get PLL clock frequency\n"); 3607eb8f069SAndrzej Hajda return 0; 3617eb8f069SAndrzej Hajda } 3627eb8f069SAndrzej Hajda 3637eb8f069SAndrzej Hajda dev_dbg(dsi->dev, "PLL input frequency: %lu\n", fin); 3647eb8f069SAndrzej Hajda 3657eb8f069SAndrzej Hajda fout = exynos_dsi_pll_find_pms(dsi, fin, freq, &p, &m, &s); 3667eb8f069SAndrzej Hajda if (!fout) { 3677eb8f069SAndrzej Hajda dev_err(dsi->dev, 3687eb8f069SAndrzej Hajda "failed to find PLL PMS for requested frequency\n"); 3697eb8f069SAndrzej Hajda return -EFAULT; 3707eb8f069SAndrzej Hajda } 3717eb8f069SAndrzej Hajda 3727eb8f069SAndrzej Hajda for (band = 0; band < ARRAY_SIZE(freq_bands); ++band) 3737eb8f069SAndrzej Hajda if (fout < freq_bands[band]) 3747eb8f069SAndrzej Hajda break; 3757eb8f069SAndrzej Hajda 3767eb8f069SAndrzej Hajda dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d), band %d\n", fout, 3777eb8f069SAndrzej Hajda p, m, s, band); 3787eb8f069SAndrzej Hajda 3797eb8f069SAndrzej Hajda writel(500, dsi->reg_base + DSIM_PLLTMR_REG); 3807eb8f069SAndrzej Hajda 3817eb8f069SAndrzej Hajda reg = DSIM_FREQ_BAND(band) | DSIM_PLL_EN 3827eb8f069SAndrzej Hajda | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s); 3837eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG); 3847eb8f069SAndrzej Hajda 3857eb8f069SAndrzej Hajda timeout = 1000; 3867eb8f069SAndrzej Hajda do { 3877eb8f069SAndrzej Hajda if (timeout-- == 0) { 3887eb8f069SAndrzej Hajda dev_err(dsi->dev, "PLL failed to stabilize\n"); 3897eb8f069SAndrzej Hajda return -EFAULT; 3907eb8f069SAndrzej Hajda } 3917eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_STATUS_REG); 3927eb8f069SAndrzej Hajda } while ((reg & DSIM_PLL_STABLE) == 0); 3937eb8f069SAndrzej Hajda 3947eb8f069SAndrzej Hajda return fout; 3957eb8f069SAndrzej Hajda } 3967eb8f069SAndrzej Hajda 3977eb8f069SAndrzej Hajda static int exynos_dsi_enable_clock(struct exynos_dsi *dsi) 3987eb8f069SAndrzej Hajda { 3997eb8f069SAndrzej Hajda unsigned long hs_clk, byte_clk, esc_clk; 4007eb8f069SAndrzej Hajda unsigned long esc_div; 4017eb8f069SAndrzej Hajda u32 reg; 4027eb8f069SAndrzej Hajda 4037eb8f069SAndrzej Hajda hs_clk = exynos_dsi_set_pll(dsi, dsi->burst_clk_rate); 4047eb8f069SAndrzej Hajda if (!hs_clk) { 4057eb8f069SAndrzej Hajda dev_err(dsi->dev, "failed to configure DSI PLL\n"); 4067eb8f069SAndrzej Hajda return -EFAULT; 4077eb8f069SAndrzej Hajda } 4087eb8f069SAndrzej Hajda 4097eb8f069SAndrzej Hajda byte_clk = hs_clk / 8; 4107eb8f069SAndrzej Hajda esc_div = DIV_ROUND_UP(byte_clk, dsi->esc_clk_rate); 4117eb8f069SAndrzej Hajda esc_clk = byte_clk / esc_div; 4127eb8f069SAndrzej Hajda 4137eb8f069SAndrzej Hajda if (esc_clk > 20 * MHZ) { 4147eb8f069SAndrzej Hajda ++esc_div; 4157eb8f069SAndrzej Hajda esc_clk = byte_clk / esc_div; 4167eb8f069SAndrzej Hajda } 4177eb8f069SAndrzej Hajda 4187eb8f069SAndrzej Hajda dev_dbg(dsi->dev, "hs_clk = %lu, byte_clk = %lu, esc_clk = %lu\n", 4197eb8f069SAndrzej Hajda hs_clk, byte_clk, esc_clk); 4207eb8f069SAndrzej Hajda 4217eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_CLKCTRL_REG); 4227eb8f069SAndrzej Hajda reg &= ~(DSIM_ESC_PRESCALER_MASK | DSIM_LANE_ESC_CLK_EN_CLK 4237eb8f069SAndrzej Hajda | DSIM_LANE_ESC_CLK_EN_DATA_MASK | DSIM_PLL_BYPASS 4247eb8f069SAndrzej Hajda | DSIM_BYTE_CLK_SRC_MASK); 4257eb8f069SAndrzej Hajda reg |= DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN 4267eb8f069SAndrzej Hajda | DSIM_ESC_PRESCALER(esc_div) 4277eb8f069SAndrzej Hajda | DSIM_LANE_ESC_CLK_EN_CLK 4287eb8f069SAndrzej Hajda | DSIM_LANE_ESC_CLK_EN_DATA(BIT(dsi->lanes) - 1) 4297eb8f069SAndrzej Hajda | DSIM_BYTE_CLK_SRC(0) 4307eb8f069SAndrzej Hajda | DSIM_TX_REQUEST_HSCLK; 4317eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_CLKCTRL_REG); 4327eb8f069SAndrzej Hajda 4337eb8f069SAndrzej Hajda return 0; 4347eb8f069SAndrzej Hajda } 4357eb8f069SAndrzej Hajda 4367eb8f069SAndrzej Hajda static void exynos_dsi_disable_clock(struct exynos_dsi *dsi) 4377eb8f069SAndrzej Hajda { 4387eb8f069SAndrzej Hajda u32 reg; 4397eb8f069SAndrzej Hajda 4407eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_CLKCTRL_REG); 4417eb8f069SAndrzej Hajda reg &= ~(DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK 4427eb8f069SAndrzej Hajda | DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN); 4437eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_CLKCTRL_REG); 4447eb8f069SAndrzej Hajda 4457eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_PLLCTRL_REG); 4467eb8f069SAndrzej Hajda reg &= ~DSIM_PLL_EN; 4477eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG); 4487eb8f069SAndrzej Hajda } 4497eb8f069SAndrzej Hajda 4507eb8f069SAndrzej Hajda static int exynos_dsi_init_link(struct exynos_dsi *dsi) 4517eb8f069SAndrzej Hajda { 4527eb8f069SAndrzej Hajda int timeout; 4537eb8f069SAndrzej Hajda u32 reg; 4547eb8f069SAndrzej Hajda u32 lanes_mask; 4557eb8f069SAndrzej Hajda 4567eb8f069SAndrzej Hajda /* Initialize FIFO pointers */ 4577eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_FIFOCTRL_REG); 4587eb8f069SAndrzej Hajda reg &= ~0x1f; 4597eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_FIFOCTRL_REG); 4607eb8f069SAndrzej Hajda 4617eb8f069SAndrzej Hajda usleep_range(9000, 11000); 4627eb8f069SAndrzej Hajda 4637eb8f069SAndrzej Hajda reg |= 0x1f; 4647eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_FIFOCTRL_REG); 4657eb8f069SAndrzej Hajda 4667eb8f069SAndrzej Hajda usleep_range(9000, 11000); 4677eb8f069SAndrzej Hajda 4687eb8f069SAndrzej Hajda /* DSI configuration */ 4697eb8f069SAndrzej Hajda reg = 0; 4707eb8f069SAndrzej Hajda 4717eb8f069SAndrzej Hajda if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { 4727eb8f069SAndrzej Hajda reg |= DSIM_VIDEO_MODE; 4737eb8f069SAndrzej Hajda 4747eb8f069SAndrzej Hajda if (!(dsi->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH)) 4757eb8f069SAndrzej Hajda reg |= DSIM_MFLUSH_VS; 4767eb8f069SAndrzej Hajda if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET)) 4777eb8f069SAndrzej Hajda reg |= DSIM_EOT_DISABLE; 4787eb8f069SAndrzej Hajda if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) 4797eb8f069SAndrzej Hajda reg |= DSIM_SYNC_INFORM; 4807eb8f069SAndrzej Hajda if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) 4817eb8f069SAndrzej Hajda reg |= DSIM_BURST_MODE; 4827eb8f069SAndrzej Hajda if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT) 4837eb8f069SAndrzej Hajda reg |= DSIM_AUTO_MODE; 4847eb8f069SAndrzej Hajda if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSE) 4857eb8f069SAndrzej Hajda reg |= DSIM_HSE_MODE; 4867eb8f069SAndrzej Hajda if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HFP)) 4877eb8f069SAndrzej Hajda reg |= DSIM_HFP_MODE; 4887eb8f069SAndrzej Hajda if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HBP)) 4897eb8f069SAndrzej Hajda reg |= DSIM_HBP_MODE; 4907eb8f069SAndrzej Hajda if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSA)) 4917eb8f069SAndrzej Hajda reg |= DSIM_HSA_MODE; 4927eb8f069SAndrzej Hajda } 4937eb8f069SAndrzej Hajda 4947eb8f069SAndrzej Hajda switch (dsi->format) { 4957eb8f069SAndrzej Hajda case MIPI_DSI_FMT_RGB888: 4967eb8f069SAndrzej Hajda reg |= DSIM_MAIN_PIX_FORMAT_RGB888; 4977eb8f069SAndrzej Hajda break; 4987eb8f069SAndrzej Hajda case MIPI_DSI_FMT_RGB666: 4997eb8f069SAndrzej Hajda reg |= DSIM_MAIN_PIX_FORMAT_RGB666; 5007eb8f069SAndrzej Hajda break; 5017eb8f069SAndrzej Hajda case MIPI_DSI_FMT_RGB666_PACKED: 5027eb8f069SAndrzej Hajda reg |= DSIM_MAIN_PIX_FORMAT_RGB666_P; 5037eb8f069SAndrzej Hajda break; 5047eb8f069SAndrzej Hajda case MIPI_DSI_FMT_RGB565: 5057eb8f069SAndrzej Hajda reg |= DSIM_MAIN_PIX_FORMAT_RGB565; 5067eb8f069SAndrzej Hajda break; 5077eb8f069SAndrzej Hajda default: 5087eb8f069SAndrzej Hajda dev_err(dsi->dev, "invalid pixel format\n"); 5097eb8f069SAndrzej Hajda return -EINVAL; 5107eb8f069SAndrzej Hajda } 5117eb8f069SAndrzej Hajda 5127eb8f069SAndrzej Hajda reg |= DSIM_NUM_OF_DATA_LANE(dsi->lanes - 1); 5137eb8f069SAndrzej Hajda 5147eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_CONFIG_REG); 5157eb8f069SAndrzej Hajda 5167eb8f069SAndrzej Hajda reg |= DSIM_LANE_EN_CLK; 5177eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_CONFIG_REG); 5187eb8f069SAndrzej Hajda 5197eb8f069SAndrzej Hajda lanes_mask = BIT(dsi->lanes) - 1; 5207eb8f069SAndrzej Hajda reg |= DSIM_LANE_EN(lanes_mask); 5217eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_CONFIG_REG); 5227eb8f069SAndrzej Hajda 5237eb8f069SAndrzej Hajda /* Check clock and data lane state are stop state */ 5247eb8f069SAndrzej Hajda timeout = 100; 5257eb8f069SAndrzej Hajda do { 5267eb8f069SAndrzej Hajda if (timeout-- == 0) { 5277eb8f069SAndrzej Hajda dev_err(dsi->dev, "waiting for bus lanes timed out\n"); 5287eb8f069SAndrzej Hajda return -EFAULT; 5297eb8f069SAndrzej Hajda } 5307eb8f069SAndrzej Hajda 5317eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_STATUS_REG); 5327eb8f069SAndrzej Hajda if ((reg & DSIM_STOP_STATE_DAT(lanes_mask)) 5337eb8f069SAndrzej Hajda != DSIM_STOP_STATE_DAT(lanes_mask)) 5347eb8f069SAndrzej Hajda continue; 5357eb8f069SAndrzej Hajda } while (!(reg & (DSIM_STOP_STATE_CLK | DSIM_TX_READY_HS_CLK))); 5367eb8f069SAndrzej Hajda 5377eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_ESCMODE_REG); 5387eb8f069SAndrzej Hajda reg &= ~DSIM_STOP_STATE_CNT_MASK; 5397eb8f069SAndrzej Hajda reg |= DSIM_STOP_STATE_CNT(0xf); 5407eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_ESCMODE_REG); 5417eb8f069SAndrzej Hajda 5427eb8f069SAndrzej Hajda reg = DSIM_BTA_TIMEOUT(0xff) | DSIM_LPDR_TIMEOUT(0xffff); 5437eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_TIMEOUT_REG); 5447eb8f069SAndrzej Hajda 5457eb8f069SAndrzej Hajda return 0; 5467eb8f069SAndrzej Hajda } 5477eb8f069SAndrzej Hajda 5487eb8f069SAndrzej Hajda static void exynos_dsi_set_display_mode(struct exynos_dsi *dsi) 5497eb8f069SAndrzej Hajda { 5507eb8f069SAndrzej Hajda struct videomode *vm = &dsi->vm; 5517eb8f069SAndrzej Hajda u32 reg; 5527eb8f069SAndrzej Hajda 5537eb8f069SAndrzej Hajda if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { 5547eb8f069SAndrzej Hajda reg = DSIM_CMD_ALLOW(0xf) 5557eb8f069SAndrzej Hajda | DSIM_STABLE_VFP(vm->vfront_porch) 5567eb8f069SAndrzej Hajda | DSIM_MAIN_VBP(vm->vback_porch); 5577eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_MVPORCH_REG); 5587eb8f069SAndrzej Hajda 5597eb8f069SAndrzej Hajda reg = DSIM_MAIN_HFP(vm->hfront_porch) 5607eb8f069SAndrzej Hajda | DSIM_MAIN_HBP(vm->hback_porch); 5617eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_MHPORCH_REG); 5627eb8f069SAndrzej Hajda 5637eb8f069SAndrzej Hajda reg = DSIM_MAIN_VSA(vm->vsync_len) 5647eb8f069SAndrzej Hajda | DSIM_MAIN_HSA(vm->hsync_len); 5657eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_MSYNC_REG); 5667eb8f069SAndrzej Hajda } 5677eb8f069SAndrzej Hajda 5687eb8f069SAndrzej Hajda reg = DSIM_MAIN_HRESOL(vm->hactive) | DSIM_MAIN_VRESOL(vm->vactive); 5697eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_MDRESOL_REG); 5707eb8f069SAndrzej Hajda 5717eb8f069SAndrzej Hajda dev_dbg(dsi->dev, "LCD size = %dx%d\n", vm->hactive, vm->vactive); 5727eb8f069SAndrzej Hajda } 5737eb8f069SAndrzej Hajda 5747eb8f069SAndrzej Hajda static void exynos_dsi_set_display_enable(struct exynos_dsi *dsi, bool enable) 5757eb8f069SAndrzej Hajda { 5767eb8f069SAndrzej Hajda u32 reg; 5777eb8f069SAndrzej Hajda 5787eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_MDRESOL_REG); 5797eb8f069SAndrzej Hajda if (enable) 5807eb8f069SAndrzej Hajda reg |= DSIM_MAIN_STAND_BY; 5817eb8f069SAndrzej Hajda else 5827eb8f069SAndrzej Hajda reg &= ~DSIM_MAIN_STAND_BY; 5837eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_MDRESOL_REG); 5847eb8f069SAndrzej Hajda } 5857eb8f069SAndrzej Hajda 5867eb8f069SAndrzej Hajda static int exynos_dsi_wait_for_hdr_fifo(struct exynos_dsi *dsi) 5877eb8f069SAndrzej Hajda { 5887eb8f069SAndrzej Hajda int timeout = 2000; 5897eb8f069SAndrzej Hajda 5907eb8f069SAndrzej Hajda do { 5917eb8f069SAndrzej Hajda u32 reg = readl(dsi->reg_base + DSIM_FIFOCTRL_REG); 5927eb8f069SAndrzej Hajda 5937eb8f069SAndrzej Hajda if (!(reg & DSIM_SFR_HEADER_FULL)) 5947eb8f069SAndrzej Hajda return 0; 5957eb8f069SAndrzej Hajda 5967eb8f069SAndrzej Hajda if (!cond_resched()) 5977eb8f069SAndrzej Hajda usleep_range(950, 1050); 5987eb8f069SAndrzej Hajda } while (--timeout); 5997eb8f069SAndrzej Hajda 6007eb8f069SAndrzej Hajda return -ETIMEDOUT; 6017eb8f069SAndrzej Hajda } 6027eb8f069SAndrzej Hajda 6037eb8f069SAndrzej Hajda static void exynos_dsi_set_cmd_lpm(struct exynos_dsi *dsi, bool lpm) 6047eb8f069SAndrzej Hajda { 6057eb8f069SAndrzej Hajda u32 v = readl(dsi->reg_base + DSIM_ESCMODE_REG); 6067eb8f069SAndrzej Hajda 6077eb8f069SAndrzej Hajda if (lpm) 6087eb8f069SAndrzej Hajda v |= DSIM_CMD_LPDT_LP; 6097eb8f069SAndrzej Hajda else 6107eb8f069SAndrzej Hajda v &= ~DSIM_CMD_LPDT_LP; 6117eb8f069SAndrzej Hajda 6127eb8f069SAndrzej Hajda writel(v, dsi->reg_base + DSIM_ESCMODE_REG); 6137eb8f069SAndrzej Hajda } 6147eb8f069SAndrzej Hajda 6157eb8f069SAndrzej Hajda static void exynos_dsi_force_bta(struct exynos_dsi *dsi) 6167eb8f069SAndrzej Hajda { 6177eb8f069SAndrzej Hajda u32 v = readl(dsi->reg_base + DSIM_ESCMODE_REG); 6187eb8f069SAndrzej Hajda 6197eb8f069SAndrzej Hajda v |= DSIM_FORCE_BTA; 6207eb8f069SAndrzej Hajda writel(v, dsi->reg_base + DSIM_ESCMODE_REG); 6217eb8f069SAndrzej Hajda } 6227eb8f069SAndrzej Hajda 6237eb8f069SAndrzej Hajda static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi, 6247eb8f069SAndrzej Hajda struct exynos_dsi_transfer *xfer) 6257eb8f069SAndrzej Hajda { 6267eb8f069SAndrzej Hajda struct device *dev = dsi->dev; 6277eb8f069SAndrzej Hajda const u8 *payload = xfer->tx_payload + xfer->tx_done; 6287eb8f069SAndrzej Hajda u16 length = xfer->tx_len - xfer->tx_done; 6297eb8f069SAndrzej Hajda bool first = !xfer->tx_done; 6307eb8f069SAndrzej Hajda u32 reg; 6317eb8f069SAndrzej Hajda 6327eb8f069SAndrzej Hajda dev_dbg(dev, "< xfer %p: tx len %u, done %u, rx len %u, done %u\n", 6337eb8f069SAndrzej Hajda xfer, xfer->tx_len, xfer->tx_done, xfer->rx_len, xfer->rx_done); 6347eb8f069SAndrzej Hajda 6357eb8f069SAndrzej Hajda if (length > DSI_TX_FIFO_SIZE) 6367eb8f069SAndrzej Hajda length = DSI_TX_FIFO_SIZE; 6377eb8f069SAndrzej Hajda 6387eb8f069SAndrzej Hajda xfer->tx_done += length; 6397eb8f069SAndrzej Hajda 6407eb8f069SAndrzej Hajda /* Send payload */ 6417eb8f069SAndrzej Hajda while (length >= 4) { 6427eb8f069SAndrzej Hajda reg = (payload[3] << 24) | (payload[2] << 16) 6437eb8f069SAndrzej Hajda | (payload[1] << 8) | payload[0]; 6447eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_PAYLOAD_REG); 6457eb8f069SAndrzej Hajda payload += 4; 6467eb8f069SAndrzej Hajda length -= 4; 6477eb8f069SAndrzej Hajda } 6487eb8f069SAndrzej Hajda 6497eb8f069SAndrzej Hajda reg = 0; 6507eb8f069SAndrzej Hajda switch (length) { 6517eb8f069SAndrzej Hajda case 3: 6527eb8f069SAndrzej Hajda reg |= payload[2] << 16; 6537eb8f069SAndrzej Hajda /* Fall through */ 6547eb8f069SAndrzej Hajda case 2: 6557eb8f069SAndrzej Hajda reg |= payload[1] << 8; 6567eb8f069SAndrzej Hajda /* Fall through */ 6577eb8f069SAndrzej Hajda case 1: 6587eb8f069SAndrzej Hajda reg |= payload[0]; 6597eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_PAYLOAD_REG); 6607eb8f069SAndrzej Hajda break; 6617eb8f069SAndrzej Hajda case 0: 6627eb8f069SAndrzej Hajda /* Do nothing */ 6637eb8f069SAndrzej Hajda break; 6647eb8f069SAndrzej Hajda } 6657eb8f069SAndrzej Hajda 6667eb8f069SAndrzej Hajda /* Send packet header */ 6677eb8f069SAndrzej Hajda if (!first) 6687eb8f069SAndrzej Hajda return; 6697eb8f069SAndrzej Hajda 6707eb8f069SAndrzej Hajda reg = (xfer->data[1] << 16) | (xfer->data[0] << 8) | xfer->data_id; 6717eb8f069SAndrzej Hajda if (exynos_dsi_wait_for_hdr_fifo(dsi)) { 6727eb8f069SAndrzej Hajda dev_err(dev, "waiting for header FIFO timed out\n"); 6737eb8f069SAndrzej Hajda return; 6747eb8f069SAndrzej Hajda } 6757eb8f069SAndrzej Hajda 6767eb8f069SAndrzej Hajda if (NEQV(xfer->flags & MIPI_DSI_MSG_USE_LPM, 6777eb8f069SAndrzej Hajda dsi->state & DSIM_STATE_CMD_LPM)) { 6787eb8f069SAndrzej Hajda exynos_dsi_set_cmd_lpm(dsi, xfer->flags & MIPI_DSI_MSG_USE_LPM); 6797eb8f069SAndrzej Hajda dsi->state ^= DSIM_STATE_CMD_LPM; 6807eb8f069SAndrzej Hajda } 6817eb8f069SAndrzej Hajda 6827eb8f069SAndrzej Hajda writel(reg, dsi->reg_base + DSIM_PKTHDR_REG); 6837eb8f069SAndrzej Hajda 6847eb8f069SAndrzej Hajda if (xfer->flags & MIPI_DSI_MSG_REQ_ACK) 6857eb8f069SAndrzej Hajda exynos_dsi_force_bta(dsi); 6867eb8f069SAndrzej Hajda } 6877eb8f069SAndrzej Hajda 6887eb8f069SAndrzej Hajda static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi, 6897eb8f069SAndrzej Hajda struct exynos_dsi_transfer *xfer) 6907eb8f069SAndrzej Hajda { 6917eb8f069SAndrzej Hajda u8 *payload = xfer->rx_payload + xfer->rx_done; 6927eb8f069SAndrzej Hajda bool first = !xfer->rx_done; 6937eb8f069SAndrzej Hajda struct device *dev = dsi->dev; 6947eb8f069SAndrzej Hajda u16 length; 6957eb8f069SAndrzej Hajda u32 reg; 6967eb8f069SAndrzej Hajda 6977eb8f069SAndrzej Hajda if (first) { 6987eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_RXFIFO_REG); 6997eb8f069SAndrzej Hajda 7007eb8f069SAndrzej Hajda switch (reg & 0x3f) { 7017eb8f069SAndrzej Hajda case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE: 7027eb8f069SAndrzej Hajda case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE: 7037eb8f069SAndrzej Hajda if (xfer->rx_len >= 2) { 7047eb8f069SAndrzej Hajda payload[1] = reg >> 16; 7057eb8f069SAndrzej Hajda ++xfer->rx_done; 7067eb8f069SAndrzej Hajda } 7077eb8f069SAndrzej Hajda /* Fall through */ 7087eb8f069SAndrzej Hajda case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE: 7097eb8f069SAndrzej Hajda case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE: 7107eb8f069SAndrzej Hajda payload[0] = reg >> 8; 7117eb8f069SAndrzej Hajda ++xfer->rx_done; 7127eb8f069SAndrzej Hajda xfer->rx_len = xfer->rx_done; 7137eb8f069SAndrzej Hajda xfer->result = 0; 7147eb8f069SAndrzej Hajda goto clear_fifo; 7157eb8f069SAndrzej Hajda case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT: 7167eb8f069SAndrzej Hajda dev_err(dev, "DSI Error Report: 0x%04x\n", 7177eb8f069SAndrzej Hajda (reg >> 8) & 0xffff); 7187eb8f069SAndrzej Hajda xfer->result = 0; 7197eb8f069SAndrzej Hajda goto clear_fifo; 7207eb8f069SAndrzej Hajda } 7217eb8f069SAndrzej Hajda 7227eb8f069SAndrzej Hajda length = (reg >> 8) & 0xffff; 7237eb8f069SAndrzej Hajda if (length > xfer->rx_len) { 7247eb8f069SAndrzej Hajda dev_err(dev, 7257eb8f069SAndrzej Hajda "response too long (%u > %u bytes), stripping\n", 7267eb8f069SAndrzej Hajda xfer->rx_len, length); 7277eb8f069SAndrzej Hajda length = xfer->rx_len; 7287eb8f069SAndrzej Hajda } else if (length < xfer->rx_len) 7297eb8f069SAndrzej Hajda xfer->rx_len = length; 7307eb8f069SAndrzej Hajda } 7317eb8f069SAndrzej Hajda 7327eb8f069SAndrzej Hajda length = xfer->rx_len - xfer->rx_done; 7337eb8f069SAndrzej Hajda xfer->rx_done += length; 7347eb8f069SAndrzej Hajda 7357eb8f069SAndrzej Hajda /* Receive payload */ 7367eb8f069SAndrzej Hajda while (length >= 4) { 7377eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_RXFIFO_REG); 7387eb8f069SAndrzej Hajda payload[0] = (reg >> 0) & 0xff; 7397eb8f069SAndrzej Hajda payload[1] = (reg >> 8) & 0xff; 7407eb8f069SAndrzej Hajda payload[2] = (reg >> 16) & 0xff; 7417eb8f069SAndrzej Hajda payload[3] = (reg >> 24) & 0xff; 7427eb8f069SAndrzej Hajda payload += 4; 7437eb8f069SAndrzej Hajda length -= 4; 7447eb8f069SAndrzej Hajda } 7457eb8f069SAndrzej Hajda 7467eb8f069SAndrzej Hajda if (length) { 7477eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_RXFIFO_REG); 7487eb8f069SAndrzej Hajda switch (length) { 7497eb8f069SAndrzej Hajda case 3: 7507eb8f069SAndrzej Hajda payload[2] = (reg >> 16) & 0xff; 7517eb8f069SAndrzej Hajda /* Fall through */ 7527eb8f069SAndrzej Hajda case 2: 7537eb8f069SAndrzej Hajda payload[1] = (reg >> 8) & 0xff; 7547eb8f069SAndrzej Hajda /* Fall through */ 7557eb8f069SAndrzej Hajda case 1: 7567eb8f069SAndrzej Hajda payload[0] = reg & 0xff; 7577eb8f069SAndrzej Hajda } 7587eb8f069SAndrzej Hajda } 7597eb8f069SAndrzej Hajda 7607eb8f069SAndrzej Hajda if (xfer->rx_done == xfer->rx_len) 7617eb8f069SAndrzej Hajda xfer->result = 0; 7627eb8f069SAndrzej Hajda 7637eb8f069SAndrzej Hajda clear_fifo: 7647eb8f069SAndrzej Hajda length = DSI_RX_FIFO_SIZE / 4; 7657eb8f069SAndrzej Hajda do { 7667eb8f069SAndrzej Hajda reg = readl(dsi->reg_base + DSIM_RXFIFO_REG); 7677eb8f069SAndrzej Hajda if (reg == DSI_RX_FIFO_EMPTY) 7687eb8f069SAndrzej Hajda break; 7697eb8f069SAndrzej Hajda } while (--length); 7707eb8f069SAndrzej Hajda } 7717eb8f069SAndrzej Hajda 7727eb8f069SAndrzej Hajda static void exynos_dsi_transfer_start(struct exynos_dsi *dsi) 7737eb8f069SAndrzej Hajda { 7747eb8f069SAndrzej Hajda unsigned long flags; 7757eb8f069SAndrzej Hajda struct exynos_dsi_transfer *xfer; 7767eb8f069SAndrzej Hajda bool start = false; 7777eb8f069SAndrzej Hajda 7787eb8f069SAndrzej Hajda again: 7797eb8f069SAndrzej Hajda spin_lock_irqsave(&dsi->transfer_lock, flags); 7807eb8f069SAndrzej Hajda 7817eb8f069SAndrzej Hajda if (list_empty(&dsi->transfer_list)) { 7827eb8f069SAndrzej Hajda spin_unlock_irqrestore(&dsi->transfer_lock, flags); 7837eb8f069SAndrzej Hajda return; 7847eb8f069SAndrzej Hajda } 7857eb8f069SAndrzej Hajda 7867eb8f069SAndrzej Hajda xfer = list_first_entry(&dsi->transfer_list, 7877eb8f069SAndrzej Hajda struct exynos_dsi_transfer, list); 7887eb8f069SAndrzej Hajda 7897eb8f069SAndrzej Hajda spin_unlock_irqrestore(&dsi->transfer_lock, flags); 7907eb8f069SAndrzej Hajda 7917eb8f069SAndrzej Hajda if (xfer->tx_len && xfer->tx_done == xfer->tx_len) 7927eb8f069SAndrzej Hajda /* waiting for RX */ 7937eb8f069SAndrzej Hajda return; 7947eb8f069SAndrzej Hajda 7957eb8f069SAndrzej Hajda exynos_dsi_send_to_fifo(dsi, xfer); 7967eb8f069SAndrzej Hajda 7977eb8f069SAndrzej Hajda if (xfer->tx_len || xfer->rx_len) 7987eb8f069SAndrzej Hajda return; 7997eb8f069SAndrzej Hajda 8007eb8f069SAndrzej Hajda xfer->result = 0; 8017eb8f069SAndrzej Hajda complete(&xfer->completed); 8027eb8f069SAndrzej Hajda 8037eb8f069SAndrzej Hajda spin_lock_irqsave(&dsi->transfer_lock, flags); 8047eb8f069SAndrzej Hajda 8057eb8f069SAndrzej Hajda list_del_init(&xfer->list); 8067eb8f069SAndrzej Hajda start = !list_empty(&dsi->transfer_list); 8077eb8f069SAndrzej Hajda 8087eb8f069SAndrzej Hajda spin_unlock_irqrestore(&dsi->transfer_lock, flags); 8097eb8f069SAndrzej Hajda 8107eb8f069SAndrzej Hajda if (start) 8117eb8f069SAndrzej Hajda goto again; 8127eb8f069SAndrzej Hajda } 8137eb8f069SAndrzej Hajda 8147eb8f069SAndrzej Hajda static bool exynos_dsi_transfer_finish(struct exynos_dsi *dsi) 8157eb8f069SAndrzej Hajda { 8167eb8f069SAndrzej Hajda struct exynos_dsi_transfer *xfer; 8177eb8f069SAndrzej Hajda unsigned long flags; 8187eb8f069SAndrzej Hajda bool start = true; 8197eb8f069SAndrzej Hajda 8207eb8f069SAndrzej Hajda spin_lock_irqsave(&dsi->transfer_lock, flags); 8217eb8f069SAndrzej Hajda 8227eb8f069SAndrzej Hajda if (list_empty(&dsi->transfer_list)) { 8237eb8f069SAndrzej Hajda spin_unlock_irqrestore(&dsi->transfer_lock, flags); 8247eb8f069SAndrzej Hajda return false; 8257eb8f069SAndrzej Hajda } 8267eb8f069SAndrzej Hajda 8277eb8f069SAndrzej Hajda xfer = list_first_entry(&dsi->transfer_list, 8287eb8f069SAndrzej Hajda struct exynos_dsi_transfer, list); 8297eb8f069SAndrzej Hajda 8307eb8f069SAndrzej Hajda spin_unlock_irqrestore(&dsi->transfer_lock, flags); 8317eb8f069SAndrzej Hajda 8327eb8f069SAndrzej Hajda dev_dbg(dsi->dev, 8337eb8f069SAndrzej Hajda "> xfer %p, tx_len %u, tx_done %u, rx_len %u, rx_done %u\n", 8347eb8f069SAndrzej Hajda xfer, xfer->tx_len, xfer->tx_done, xfer->rx_len, xfer->rx_done); 8357eb8f069SAndrzej Hajda 8367eb8f069SAndrzej Hajda if (xfer->tx_done != xfer->tx_len) 8377eb8f069SAndrzej Hajda return true; 8387eb8f069SAndrzej Hajda 8397eb8f069SAndrzej Hajda if (xfer->rx_done != xfer->rx_len) 8407eb8f069SAndrzej Hajda exynos_dsi_read_from_fifo(dsi, xfer); 8417eb8f069SAndrzej Hajda 8427eb8f069SAndrzej Hajda if (xfer->rx_done != xfer->rx_len) 8437eb8f069SAndrzej Hajda return true; 8447eb8f069SAndrzej Hajda 8457eb8f069SAndrzej Hajda spin_lock_irqsave(&dsi->transfer_lock, flags); 8467eb8f069SAndrzej Hajda 8477eb8f069SAndrzej Hajda list_del_init(&xfer->list); 8487eb8f069SAndrzej Hajda start = !list_empty(&dsi->transfer_list); 8497eb8f069SAndrzej Hajda 8507eb8f069SAndrzej Hajda spin_unlock_irqrestore(&dsi->transfer_lock, flags); 8517eb8f069SAndrzej Hajda 8527eb8f069SAndrzej Hajda if (!xfer->rx_len) 8537eb8f069SAndrzej Hajda xfer->result = 0; 8547eb8f069SAndrzej Hajda complete(&xfer->completed); 8557eb8f069SAndrzej Hajda 8567eb8f069SAndrzej Hajda return start; 8577eb8f069SAndrzej Hajda } 8587eb8f069SAndrzej Hajda 8597eb8f069SAndrzej Hajda static void exynos_dsi_remove_transfer(struct exynos_dsi *dsi, 8607eb8f069SAndrzej Hajda struct exynos_dsi_transfer *xfer) 8617eb8f069SAndrzej Hajda { 8627eb8f069SAndrzej Hajda unsigned long flags; 8637eb8f069SAndrzej Hajda bool start; 8647eb8f069SAndrzej Hajda 8657eb8f069SAndrzej Hajda spin_lock_irqsave(&dsi->transfer_lock, flags); 8667eb8f069SAndrzej Hajda 8677eb8f069SAndrzej Hajda if (!list_empty(&dsi->transfer_list) && 8687eb8f069SAndrzej Hajda xfer == list_first_entry(&dsi->transfer_list, 8697eb8f069SAndrzej Hajda struct exynos_dsi_transfer, list)) { 8707eb8f069SAndrzej Hajda list_del_init(&xfer->list); 8717eb8f069SAndrzej Hajda start = !list_empty(&dsi->transfer_list); 8727eb8f069SAndrzej Hajda spin_unlock_irqrestore(&dsi->transfer_lock, flags); 8737eb8f069SAndrzej Hajda if (start) 8747eb8f069SAndrzej Hajda exynos_dsi_transfer_start(dsi); 8757eb8f069SAndrzej Hajda return; 8767eb8f069SAndrzej Hajda } 8777eb8f069SAndrzej Hajda 8787eb8f069SAndrzej Hajda list_del_init(&xfer->list); 8797eb8f069SAndrzej Hajda 8807eb8f069SAndrzej Hajda spin_unlock_irqrestore(&dsi->transfer_lock, flags); 8817eb8f069SAndrzej Hajda } 8827eb8f069SAndrzej Hajda 8837eb8f069SAndrzej Hajda static int exynos_dsi_transfer(struct exynos_dsi *dsi, 8847eb8f069SAndrzej Hajda struct exynos_dsi_transfer *xfer) 8857eb8f069SAndrzej Hajda { 8867eb8f069SAndrzej Hajda unsigned long flags; 8877eb8f069SAndrzej Hajda bool stopped; 8887eb8f069SAndrzej Hajda 8897eb8f069SAndrzej Hajda xfer->tx_done = 0; 8907eb8f069SAndrzej Hajda xfer->rx_done = 0; 8917eb8f069SAndrzej Hajda xfer->result = -ETIMEDOUT; 8927eb8f069SAndrzej Hajda init_completion(&xfer->completed); 8937eb8f069SAndrzej Hajda 8947eb8f069SAndrzej Hajda spin_lock_irqsave(&dsi->transfer_lock, flags); 8957eb8f069SAndrzej Hajda 8967eb8f069SAndrzej Hajda stopped = list_empty(&dsi->transfer_list); 8977eb8f069SAndrzej Hajda list_add_tail(&xfer->list, &dsi->transfer_list); 8987eb8f069SAndrzej Hajda 8997eb8f069SAndrzej Hajda spin_unlock_irqrestore(&dsi->transfer_lock, flags); 9007eb8f069SAndrzej Hajda 9017eb8f069SAndrzej Hajda if (stopped) 9027eb8f069SAndrzej Hajda exynos_dsi_transfer_start(dsi); 9037eb8f069SAndrzej Hajda 9047eb8f069SAndrzej Hajda wait_for_completion_timeout(&xfer->completed, 9057eb8f069SAndrzej Hajda msecs_to_jiffies(DSI_XFER_TIMEOUT_MS)); 9067eb8f069SAndrzej Hajda if (xfer->result == -ETIMEDOUT) { 9077eb8f069SAndrzej Hajda exynos_dsi_remove_transfer(dsi, xfer); 9087eb8f069SAndrzej Hajda dev_err(dsi->dev, "xfer timed out: %*ph %*ph\n", 2, xfer->data, 9097eb8f069SAndrzej Hajda xfer->tx_len, xfer->tx_payload); 9107eb8f069SAndrzej Hajda return -ETIMEDOUT; 9117eb8f069SAndrzej Hajda } 9127eb8f069SAndrzej Hajda 9137eb8f069SAndrzej Hajda /* Also covers hardware timeout condition */ 9147eb8f069SAndrzej Hajda return xfer->result; 9157eb8f069SAndrzej Hajda } 9167eb8f069SAndrzej Hajda 9177eb8f069SAndrzej Hajda static irqreturn_t exynos_dsi_irq(int irq, void *dev_id) 9187eb8f069SAndrzej Hajda { 9197eb8f069SAndrzej Hajda struct exynos_dsi *dsi = dev_id; 9207eb8f069SAndrzej Hajda u32 status; 9217eb8f069SAndrzej Hajda 9227eb8f069SAndrzej Hajda status = readl(dsi->reg_base + DSIM_INTSRC_REG); 9237eb8f069SAndrzej Hajda if (!status) { 9247eb8f069SAndrzej Hajda static unsigned long int j; 9257eb8f069SAndrzej Hajda if (printk_timed_ratelimit(&j, 500)) 9267eb8f069SAndrzej Hajda dev_warn(dsi->dev, "spurious interrupt\n"); 9277eb8f069SAndrzej Hajda return IRQ_HANDLED; 9287eb8f069SAndrzej Hajda } 9297eb8f069SAndrzej Hajda writel(status, dsi->reg_base + DSIM_INTSRC_REG); 9307eb8f069SAndrzej Hajda 9317eb8f069SAndrzej Hajda if (status & DSIM_INT_SW_RST_RELEASE) { 9327eb8f069SAndrzej Hajda u32 mask = ~(DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY); 9337eb8f069SAndrzej Hajda writel(mask, dsi->reg_base + DSIM_INTMSK_REG); 9347eb8f069SAndrzej Hajda complete(&dsi->completed); 9357eb8f069SAndrzej Hajda return IRQ_HANDLED; 9367eb8f069SAndrzej Hajda } 9377eb8f069SAndrzej Hajda 9387eb8f069SAndrzej Hajda if (!(status & (DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY))) 9397eb8f069SAndrzej Hajda return IRQ_HANDLED; 9407eb8f069SAndrzej Hajda 9417eb8f069SAndrzej Hajda if (exynos_dsi_transfer_finish(dsi)) 9427eb8f069SAndrzej Hajda exynos_dsi_transfer_start(dsi); 9437eb8f069SAndrzej Hajda 9447eb8f069SAndrzej Hajda return IRQ_HANDLED; 9457eb8f069SAndrzej Hajda } 9467eb8f069SAndrzej Hajda 9477eb8f069SAndrzej Hajda static int exynos_dsi_init(struct exynos_dsi *dsi) 9487eb8f069SAndrzej Hajda { 9497eb8f069SAndrzej Hajda exynos_dsi_enable_clock(dsi); 9507eb8f069SAndrzej Hajda exynos_dsi_reset(dsi); 9517eb8f069SAndrzej Hajda enable_irq(dsi->irq); 9527eb8f069SAndrzej Hajda exynos_dsi_wait_for_reset(dsi); 9537eb8f069SAndrzej Hajda exynos_dsi_init_link(dsi); 9547eb8f069SAndrzej Hajda 9557eb8f069SAndrzej Hajda return 0; 9567eb8f069SAndrzej Hajda } 9577eb8f069SAndrzej Hajda 9587eb8f069SAndrzej Hajda static int exynos_dsi_host_attach(struct mipi_dsi_host *host, 9597eb8f069SAndrzej Hajda struct mipi_dsi_device *device) 9607eb8f069SAndrzej Hajda { 9617eb8f069SAndrzej Hajda struct exynos_dsi *dsi = host_to_dsi(host); 9627eb8f069SAndrzej Hajda 9637eb8f069SAndrzej Hajda dsi->lanes = device->lanes; 9647eb8f069SAndrzej Hajda dsi->format = device->format; 9657eb8f069SAndrzej Hajda dsi->mode_flags = device->mode_flags; 9667eb8f069SAndrzej Hajda dsi->panel_node = device->dev.of_node; 9677eb8f069SAndrzej Hajda 9687eb8f069SAndrzej Hajda if (dsi->connector.dev) 9697eb8f069SAndrzej Hajda drm_helper_hpd_irq_event(dsi->connector.dev); 9707eb8f069SAndrzej Hajda 9717eb8f069SAndrzej Hajda return 0; 9727eb8f069SAndrzej Hajda } 9737eb8f069SAndrzej Hajda 9747eb8f069SAndrzej Hajda static int exynos_dsi_host_detach(struct mipi_dsi_host *host, 9757eb8f069SAndrzej Hajda struct mipi_dsi_device *device) 9767eb8f069SAndrzej Hajda { 9777eb8f069SAndrzej Hajda struct exynos_dsi *dsi = host_to_dsi(host); 9787eb8f069SAndrzej Hajda 9797eb8f069SAndrzej Hajda dsi->panel_node = NULL; 9807eb8f069SAndrzej Hajda 9817eb8f069SAndrzej Hajda if (dsi->connector.dev) 9827eb8f069SAndrzej Hajda drm_helper_hpd_irq_event(dsi->connector.dev); 9837eb8f069SAndrzej Hajda 9847eb8f069SAndrzej Hajda return 0; 9857eb8f069SAndrzej Hajda } 9867eb8f069SAndrzej Hajda 9877eb8f069SAndrzej Hajda /* distinguish between short and long DSI packet types */ 9887eb8f069SAndrzej Hajda static bool exynos_dsi_is_short_dsi_type(u8 type) 9897eb8f069SAndrzej Hajda { 9907eb8f069SAndrzej Hajda return (type & 0x0f) <= 8; 9917eb8f069SAndrzej Hajda } 9927eb8f069SAndrzej Hajda 9937eb8f069SAndrzej Hajda static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host, 9947eb8f069SAndrzej Hajda struct mipi_dsi_msg *msg) 9957eb8f069SAndrzej Hajda { 9967eb8f069SAndrzej Hajda struct exynos_dsi *dsi = host_to_dsi(host); 9977eb8f069SAndrzej Hajda struct exynos_dsi_transfer xfer; 9987eb8f069SAndrzej Hajda int ret; 9997eb8f069SAndrzej Hajda 10007eb8f069SAndrzej Hajda if (!(dsi->state & DSIM_STATE_INITIALIZED)) { 10017eb8f069SAndrzej Hajda ret = exynos_dsi_init(dsi); 10027eb8f069SAndrzej Hajda if (ret) 10037eb8f069SAndrzej Hajda return ret; 10047eb8f069SAndrzej Hajda dsi->state |= DSIM_STATE_INITIALIZED; 10057eb8f069SAndrzej Hajda } 10067eb8f069SAndrzej Hajda 10077eb8f069SAndrzej Hajda if (msg->tx_len == 0) 10087eb8f069SAndrzej Hajda return -EINVAL; 10097eb8f069SAndrzej Hajda 10107eb8f069SAndrzej Hajda xfer.data_id = msg->type | (msg->channel << 6); 10117eb8f069SAndrzej Hajda 10127eb8f069SAndrzej Hajda if (exynos_dsi_is_short_dsi_type(msg->type)) { 10137eb8f069SAndrzej Hajda const char *tx_buf = msg->tx_buf; 10147eb8f069SAndrzej Hajda 10157eb8f069SAndrzej Hajda if (msg->tx_len > 2) 10167eb8f069SAndrzej Hajda return -EINVAL; 10177eb8f069SAndrzej Hajda xfer.tx_len = 0; 10187eb8f069SAndrzej Hajda xfer.data[0] = tx_buf[0]; 10197eb8f069SAndrzej Hajda xfer.data[1] = (msg->tx_len == 2) ? tx_buf[1] : 0; 10207eb8f069SAndrzej Hajda } else { 10217eb8f069SAndrzej Hajda xfer.tx_len = msg->tx_len; 10227eb8f069SAndrzej Hajda xfer.data[0] = msg->tx_len & 0xff; 10237eb8f069SAndrzej Hajda xfer.data[1] = msg->tx_len >> 8; 10247eb8f069SAndrzej Hajda xfer.tx_payload = msg->tx_buf; 10257eb8f069SAndrzej Hajda } 10267eb8f069SAndrzej Hajda 10277eb8f069SAndrzej Hajda xfer.rx_len = msg->rx_len; 10287eb8f069SAndrzej Hajda xfer.rx_payload = msg->rx_buf; 10297eb8f069SAndrzej Hajda xfer.flags = msg->flags; 10307eb8f069SAndrzej Hajda 10317eb8f069SAndrzej Hajda ret = exynos_dsi_transfer(dsi, &xfer); 10327eb8f069SAndrzej Hajda return (ret < 0) ? ret : xfer.rx_done; 10337eb8f069SAndrzej Hajda } 10347eb8f069SAndrzej Hajda 10357eb8f069SAndrzej Hajda static const struct mipi_dsi_host_ops exynos_dsi_ops = { 10367eb8f069SAndrzej Hajda .attach = exynos_dsi_host_attach, 10377eb8f069SAndrzej Hajda .detach = exynos_dsi_host_detach, 10387eb8f069SAndrzej Hajda .transfer = exynos_dsi_host_transfer, 10397eb8f069SAndrzej Hajda }; 10407eb8f069SAndrzej Hajda 10417eb8f069SAndrzej Hajda static int exynos_dsi_poweron(struct exynos_dsi *dsi) 10427eb8f069SAndrzej Hajda { 10437eb8f069SAndrzej Hajda int ret; 10447eb8f069SAndrzej Hajda 10457eb8f069SAndrzej Hajda ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies); 10467eb8f069SAndrzej Hajda if (ret < 0) { 10477eb8f069SAndrzej Hajda dev_err(dsi->dev, "cannot enable regulators %d\n", ret); 10487eb8f069SAndrzej Hajda return ret; 10497eb8f069SAndrzej Hajda } 10507eb8f069SAndrzej Hajda 10517eb8f069SAndrzej Hajda ret = clk_prepare_enable(dsi->bus_clk); 10527eb8f069SAndrzej Hajda if (ret < 0) { 10537eb8f069SAndrzej Hajda dev_err(dsi->dev, "cannot enable bus clock %d\n", ret); 10547eb8f069SAndrzej Hajda goto err_bus_clk; 10557eb8f069SAndrzej Hajda } 10567eb8f069SAndrzej Hajda 10577eb8f069SAndrzej Hajda ret = clk_prepare_enable(dsi->pll_clk); 10587eb8f069SAndrzej Hajda if (ret < 0) { 10597eb8f069SAndrzej Hajda dev_err(dsi->dev, "cannot enable pll clock %d\n", ret); 10607eb8f069SAndrzej Hajda goto err_pll_clk; 10617eb8f069SAndrzej Hajda } 10627eb8f069SAndrzej Hajda 10637eb8f069SAndrzej Hajda ret = phy_power_on(dsi->phy); 10647eb8f069SAndrzej Hajda if (ret < 0) { 10657eb8f069SAndrzej Hajda dev_err(dsi->dev, "cannot enable phy %d\n", ret); 10667eb8f069SAndrzej Hajda goto err_phy; 10677eb8f069SAndrzej Hajda } 10687eb8f069SAndrzej Hajda 10697eb8f069SAndrzej Hajda return 0; 10707eb8f069SAndrzej Hajda 10717eb8f069SAndrzej Hajda err_phy: 10727eb8f069SAndrzej Hajda clk_disable_unprepare(dsi->pll_clk); 10737eb8f069SAndrzej Hajda err_pll_clk: 10747eb8f069SAndrzej Hajda clk_disable_unprepare(dsi->bus_clk); 10757eb8f069SAndrzej Hajda err_bus_clk: 10767eb8f069SAndrzej Hajda regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies); 10777eb8f069SAndrzej Hajda 10787eb8f069SAndrzej Hajda return ret; 10797eb8f069SAndrzej Hajda } 10807eb8f069SAndrzej Hajda 10817eb8f069SAndrzej Hajda static void exynos_dsi_poweroff(struct exynos_dsi *dsi) 10827eb8f069SAndrzej Hajda { 10837eb8f069SAndrzej Hajda int ret; 10847eb8f069SAndrzej Hajda 10857eb8f069SAndrzej Hajda usleep_range(10000, 20000); 10867eb8f069SAndrzej Hajda 10877eb8f069SAndrzej Hajda if (dsi->state & DSIM_STATE_INITIALIZED) { 10887eb8f069SAndrzej Hajda dsi->state &= ~DSIM_STATE_INITIALIZED; 10897eb8f069SAndrzej Hajda 10907eb8f069SAndrzej Hajda exynos_dsi_disable_clock(dsi); 10917eb8f069SAndrzej Hajda 10927eb8f069SAndrzej Hajda disable_irq(dsi->irq); 10937eb8f069SAndrzej Hajda } 10947eb8f069SAndrzej Hajda 10957eb8f069SAndrzej Hajda dsi->state &= ~DSIM_STATE_CMD_LPM; 10967eb8f069SAndrzej Hajda 10977eb8f069SAndrzej Hajda phy_power_off(dsi->phy); 10987eb8f069SAndrzej Hajda 10997eb8f069SAndrzej Hajda clk_disable_unprepare(dsi->pll_clk); 11007eb8f069SAndrzej Hajda clk_disable_unprepare(dsi->bus_clk); 11017eb8f069SAndrzej Hajda 11027eb8f069SAndrzej Hajda ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies); 11037eb8f069SAndrzej Hajda if (ret < 0) 11047eb8f069SAndrzej Hajda dev_err(dsi->dev, "cannot disable regulators %d\n", ret); 11057eb8f069SAndrzej Hajda } 11067eb8f069SAndrzej Hajda 11077eb8f069SAndrzej Hajda static int exynos_dsi_enable(struct exynos_dsi *dsi) 11087eb8f069SAndrzej Hajda { 11097eb8f069SAndrzej Hajda int ret; 11107eb8f069SAndrzej Hajda 11117eb8f069SAndrzej Hajda if (dsi->state & DSIM_STATE_ENABLED) 11127eb8f069SAndrzej Hajda return 0; 11137eb8f069SAndrzej Hajda 11147eb8f069SAndrzej Hajda ret = exynos_dsi_poweron(dsi); 11157eb8f069SAndrzej Hajda if (ret < 0) 11167eb8f069SAndrzej Hajda return ret; 11177eb8f069SAndrzej Hajda 11187eb8f069SAndrzej Hajda ret = drm_panel_enable(dsi->panel); 11197eb8f069SAndrzej Hajda if (ret < 0) { 11207eb8f069SAndrzej Hajda exynos_dsi_poweroff(dsi); 11217eb8f069SAndrzej Hajda return ret; 11227eb8f069SAndrzej Hajda } 11237eb8f069SAndrzej Hajda 11247eb8f069SAndrzej Hajda exynos_dsi_set_display_mode(dsi); 11257eb8f069SAndrzej Hajda exynos_dsi_set_display_enable(dsi, true); 11267eb8f069SAndrzej Hajda 11277eb8f069SAndrzej Hajda dsi->state |= DSIM_STATE_ENABLED; 11287eb8f069SAndrzej Hajda 11297eb8f069SAndrzej Hajda return 0; 11307eb8f069SAndrzej Hajda } 11317eb8f069SAndrzej Hajda 11327eb8f069SAndrzej Hajda static void exynos_dsi_disable(struct exynos_dsi *dsi) 11337eb8f069SAndrzej Hajda { 11347eb8f069SAndrzej Hajda if (!(dsi->state & DSIM_STATE_ENABLED)) 11357eb8f069SAndrzej Hajda return; 11367eb8f069SAndrzej Hajda 11377eb8f069SAndrzej Hajda exynos_dsi_set_display_enable(dsi, false); 11387eb8f069SAndrzej Hajda drm_panel_disable(dsi->panel); 11397eb8f069SAndrzej Hajda exynos_dsi_poweroff(dsi); 11407eb8f069SAndrzej Hajda 11417eb8f069SAndrzej Hajda dsi->state &= ~DSIM_STATE_ENABLED; 11427eb8f069SAndrzej Hajda } 11437eb8f069SAndrzej Hajda 11447eb8f069SAndrzej Hajda static void exynos_dsi_dpms(struct exynos_drm_display *display, int mode) 11457eb8f069SAndrzej Hajda { 11467eb8f069SAndrzej Hajda struct exynos_dsi *dsi = display->ctx; 11477eb8f069SAndrzej Hajda 11487eb8f069SAndrzej Hajda if (dsi->panel) { 11497eb8f069SAndrzej Hajda switch (mode) { 11507eb8f069SAndrzej Hajda case DRM_MODE_DPMS_ON: 11517eb8f069SAndrzej Hajda exynos_dsi_enable(dsi); 11527eb8f069SAndrzej Hajda break; 11537eb8f069SAndrzej Hajda case DRM_MODE_DPMS_STANDBY: 11547eb8f069SAndrzej Hajda case DRM_MODE_DPMS_SUSPEND: 11557eb8f069SAndrzej Hajda case DRM_MODE_DPMS_OFF: 11567eb8f069SAndrzej Hajda exynos_dsi_disable(dsi); 11577eb8f069SAndrzej Hajda break; 11587eb8f069SAndrzej Hajda default: 11597eb8f069SAndrzej Hajda break; 11607eb8f069SAndrzej Hajda } 11617eb8f069SAndrzej Hajda } 11627eb8f069SAndrzej Hajda } 11637eb8f069SAndrzej Hajda 11647eb8f069SAndrzej Hajda static enum drm_connector_status 11657eb8f069SAndrzej Hajda exynos_dsi_detect(struct drm_connector *connector, bool force) 11667eb8f069SAndrzej Hajda { 11677eb8f069SAndrzej Hajda struct exynos_dsi *dsi = connector_to_dsi(connector); 11687eb8f069SAndrzej Hajda 11697eb8f069SAndrzej Hajda if (!dsi->panel) { 11707eb8f069SAndrzej Hajda dsi->panel = of_drm_find_panel(dsi->panel_node); 11717eb8f069SAndrzej Hajda if (dsi->panel) 11727eb8f069SAndrzej Hajda drm_panel_attach(dsi->panel, &dsi->connector); 11737eb8f069SAndrzej Hajda } else if (!dsi->panel_node) { 11747eb8f069SAndrzej Hajda struct exynos_drm_display *display; 11757eb8f069SAndrzej Hajda 11767eb8f069SAndrzej Hajda display = platform_get_drvdata(to_platform_device(dsi->dev)); 11777eb8f069SAndrzej Hajda exynos_dsi_dpms(display, DRM_MODE_DPMS_OFF); 11787eb8f069SAndrzej Hajda drm_panel_detach(dsi->panel); 11797eb8f069SAndrzej Hajda dsi->panel = NULL; 11807eb8f069SAndrzej Hajda } 11817eb8f069SAndrzej Hajda 11827eb8f069SAndrzej Hajda if (dsi->panel) 11837eb8f069SAndrzej Hajda return connector_status_connected; 11847eb8f069SAndrzej Hajda 11857eb8f069SAndrzej Hajda return connector_status_disconnected; 11867eb8f069SAndrzej Hajda } 11877eb8f069SAndrzej Hajda 11887eb8f069SAndrzej Hajda static void exynos_dsi_connector_destroy(struct drm_connector *connector) 11897eb8f069SAndrzej Hajda { 11907eb8f069SAndrzej Hajda } 11917eb8f069SAndrzej Hajda 11927eb8f069SAndrzej Hajda static struct drm_connector_funcs exynos_dsi_connector_funcs = { 11937eb8f069SAndrzej Hajda .dpms = drm_helper_connector_dpms, 11947eb8f069SAndrzej Hajda .detect = exynos_dsi_detect, 11957eb8f069SAndrzej Hajda .fill_modes = drm_helper_probe_single_connector_modes, 11967eb8f069SAndrzej Hajda .destroy = exynos_dsi_connector_destroy, 11977eb8f069SAndrzej Hajda }; 11987eb8f069SAndrzej Hajda 11997eb8f069SAndrzej Hajda static int exynos_dsi_get_modes(struct drm_connector *connector) 12007eb8f069SAndrzej Hajda { 12017eb8f069SAndrzej Hajda struct exynos_dsi *dsi = connector_to_dsi(connector); 12027eb8f069SAndrzej Hajda 12037eb8f069SAndrzej Hajda if (dsi->panel) 12047eb8f069SAndrzej Hajda return dsi->panel->funcs->get_modes(dsi->panel); 12057eb8f069SAndrzej Hajda 12067eb8f069SAndrzej Hajda return 0; 12077eb8f069SAndrzej Hajda } 12087eb8f069SAndrzej Hajda 12097eb8f069SAndrzej Hajda static int exynos_dsi_mode_valid(struct drm_connector *connector, 12107eb8f069SAndrzej Hajda struct drm_display_mode *mode) 12117eb8f069SAndrzej Hajda { 12127eb8f069SAndrzej Hajda return MODE_OK; 12137eb8f069SAndrzej Hajda } 12147eb8f069SAndrzej Hajda 12157eb8f069SAndrzej Hajda static struct drm_encoder * 12167eb8f069SAndrzej Hajda exynos_dsi_best_encoder(struct drm_connector *connector) 12177eb8f069SAndrzej Hajda { 12187eb8f069SAndrzej Hajda struct exynos_dsi *dsi = connector_to_dsi(connector); 12197eb8f069SAndrzej Hajda 12207eb8f069SAndrzej Hajda return dsi->encoder; 12217eb8f069SAndrzej Hajda } 12227eb8f069SAndrzej Hajda 12237eb8f069SAndrzej Hajda static struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = { 12247eb8f069SAndrzej Hajda .get_modes = exynos_dsi_get_modes, 12257eb8f069SAndrzej Hajda .mode_valid = exynos_dsi_mode_valid, 12267eb8f069SAndrzej Hajda .best_encoder = exynos_dsi_best_encoder, 12277eb8f069SAndrzej Hajda }; 12287eb8f069SAndrzej Hajda 12297eb8f069SAndrzej Hajda static int exynos_dsi_create_connector(struct exynos_drm_display *display, 12307eb8f069SAndrzej Hajda struct drm_encoder *encoder) 12317eb8f069SAndrzej Hajda { 12327eb8f069SAndrzej Hajda struct exynos_dsi *dsi = display->ctx; 12337eb8f069SAndrzej Hajda struct drm_connector *connector = &dsi->connector; 12347eb8f069SAndrzej Hajda int ret; 12357eb8f069SAndrzej Hajda 12367eb8f069SAndrzej Hajda dsi->encoder = encoder; 12377eb8f069SAndrzej Hajda 12387eb8f069SAndrzej Hajda connector->polled = DRM_CONNECTOR_POLL_HPD; 12397eb8f069SAndrzej Hajda 12407eb8f069SAndrzej Hajda ret = drm_connector_init(encoder->dev, connector, 12417eb8f069SAndrzej Hajda &exynos_dsi_connector_funcs, 12427eb8f069SAndrzej Hajda DRM_MODE_CONNECTOR_DSI); 12437eb8f069SAndrzej Hajda if (ret) { 12447eb8f069SAndrzej Hajda DRM_ERROR("Failed to initialize connector with drm\n"); 12457eb8f069SAndrzej Hajda return ret; 12467eb8f069SAndrzej Hajda } 12477eb8f069SAndrzej Hajda 12487eb8f069SAndrzej Hajda drm_connector_helper_add(connector, &exynos_dsi_connector_helper_funcs); 12497eb8f069SAndrzej Hajda drm_sysfs_connector_add(connector); 12507eb8f069SAndrzej Hajda drm_mode_connector_attach_encoder(connector, encoder); 12517eb8f069SAndrzej Hajda 12527eb8f069SAndrzej Hajda return 0; 12537eb8f069SAndrzej Hajda } 12547eb8f069SAndrzej Hajda 12557eb8f069SAndrzej Hajda static void exynos_dsi_mode_set(struct exynos_drm_display *display, 12567eb8f069SAndrzej Hajda struct drm_display_mode *mode) 12577eb8f069SAndrzej Hajda { 12587eb8f069SAndrzej Hajda struct exynos_dsi *dsi = display->ctx; 12597eb8f069SAndrzej Hajda struct videomode *vm = &dsi->vm; 12607eb8f069SAndrzej Hajda 12617eb8f069SAndrzej Hajda vm->hactive = mode->hdisplay; 12627eb8f069SAndrzej Hajda vm->vactive = mode->vdisplay; 12637eb8f069SAndrzej Hajda vm->vfront_porch = mode->vsync_start - mode->vdisplay; 12647eb8f069SAndrzej Hajda vm->vback_porch = mode->vtotal - mode->vsync_end; 12657eb8f069SAndrzej Hajda vm->vsync_len = mode->vsync_end - mode->vsync_start; 12667eb8f069SAndrzej Hajda vm->hfront_porch = mode->hsync_start - mode->hdisplay; 12677eb8f069SAndrzej Hajda vm->hback_porch = mode->htotal - mode->hsync_end; 12687eb8f069SAndrzej Hajda vm->hsync_len = mode->hsync_end - mode->hsync_start; 12697eb8f069SAndrzej Hajda } 12707eb8f069SAndrzej Hajda 12717eb8f069SAndrzej Hajda static struct exynos_drm_display_ops exynos_dsi_display_ops = { 12727eb8f069SAndrzej Hajda .create_connector = exynos_dsi_create_connector, 12737eb8f069SAndrzej Hajda .mode_set = exynos_dsi_mode_set, 12747eb8f069SAndrzej Hajda .dpms = exynos_dsi_dpms 12757eb8f069SAndrzej Hajda }; 12767eb8f069SAndrzej Hajda 12777eb8f069SAndrzej Hajda static struct exynos_drm_display exynos_dsi_display = { 12787eb8f069SAndrzej Hajda .type = EXYNOS_DISPLAY_TYPE_LCD, 12797eb8f069SAndrzej Hajda .ops = &exynos_dsi_display_ops, 12807eb8f069SAndrzej Hajda }; 12817eb8f069SAndrzej Hajda 12827eb8f069SAndrzej Hajda /* of_* functions will be removed after merge of of_graph patches */ 12837eb8f069SAndrzej Hajda static struct device_node * 12847eb8f069SAndrzej Hajda of_get_child_by_name_reg(struct device_node *parent, const char *name, u32 reg) 12857eb8f069SAndrzej Hajda { 12867eb8f069SAndrzej Hajda struct device_node *np; 12877eb8f069SAndrzej Hajda 12887eb8f069SAndrzej Hajda for_each_child_of_node(parent, np) { 12897eb8f069SAndrzej Hajda u32 r; 12907eb8f069SAndrzej Hajda 12917eb8f069SAndrzej Hajda if (!np->name || of_node_cmp(np->name, name)) 12927eb8f069SAndrzej Hajda continue; 12937eb8f069SAndrzej Hajda 12947eb8f069SAndrzej Hajda if (of_property_read_u32(np, "reg", &r) < 0) 12957eb8f069SAndrzej Hajda r = 0; 12967eb8f069SAndrzej Hajda 12977eb8f069SAndrzej Hajda if (reg == r) 12987eb8f069SAndrzej Hajda break; 12997eb8f069SAndrzej Hajda } 13007eb8f069SAndrzej Hajda 13017eb8f069SAndrzej Hajda return np; 13027eb8f069SAndrzej Hajda } 13037eb8f069SAndrzej Hajda 13047eb8f069SAndrzej Hajda static struct device_node *of_graph_get_port_by_reg(struct device_node *parent, 13057eb8f069SAndrzej Hajda u32 reg) 13067eb8f069SAndrzej Hajda { 13077eb8f069SAndrzej Hajda struct device_node *ports, *port; 13087eb8f069SAndrzej Hajda 13097eb8f069SAndrzej Hajda ports = of_get_child_by_name(parent, "ports"); 13107eb8f069SAndrzej Hajda if (ports) 13117eb8f069SAndrzej Hajda parent = ports; 13127eb8f069SAndrzej Hajda 13137eb8f069SAndrzej Hajda port = of_get_child_by_name_reg(parent, "port", reg); 13147eb8f069SAndrzej Hajda 13157eb8f069SAndrzej Hajda of_node_put(ports); 13167eb8f069SAndrzej Hajda 13177eb8f069SAndrzej Hajda return port; 13187eb8f069SAndrzej Hajda } 13197eb8f069SAndrzej Hajda 13207eb8f069SAndrzej Hajda static struct device_node * 13217eb8f069SAndrzej Hajda of_graph_get_endpoint_by_reg(struct device_node *port, u32 reg) 13227eb8f069SAndrzej Hajda { 13237eb8f069SAndrzej Hajda return of_get_child_by_name_reg(port, "endpoint", reg); 13247eb8f069SAndrzej Hajda } 13257eb8f069SAndrzej Hajda 13267eb8f069SAndrzej Hajda static int exynos_dsi_of_read_u32(const struct device_node *np, 13277eb8f069SAndrzej Hajda const char *propname, u32 *out_value) 13287eb8f069SAndrzej Hajda { 13297eb8f069SAndrzej Hajda int ret = of_property_read_u32(np, propname, out_value); 13307eb8f069SAndrzej Hajda 13317eb8f069SAndrzej Hajda if (ret < 0) 13327eb8f069SAndrzej Hajda pr_err("%s: failed to get '%s' property\n", np->full_name, 13337eb8f069SAndrzej Hajda propname); 13347eb8f069SAndrzej Hajda 13357eb8f069SAndrzej Hajda return ret; 13367eb8f069SAndrzej Hajda } 13377eb8f069SAndrzej Hajda 13387eb8f069SAndrzej Hajda enum { 13397eb8f069SAndrzej Hajda DSI_PORT_IN, 13407eb8f069SAndrzej Hajda DSI_PORT_OUT 13417eb8f069SAndrzej Hajda }; 13427eb8f069SAndrzej Hajda 13437eb8f069SAndrzej Hajda static int exynos_dsi_parse_dt(struct exynos_dsi *dsi) 13447eb8f069SAndrzej Hajda { 13457eb8f069SAndrzej Hajda struct device *dev = dsi->dev; 13467eb8f069SAndrzej Hajda struct device_node *node = dev->of_node; 13477eb8f069SAndrzej Hajda struct device_node *port, *ep; 13487eb8f069SAndrzej Hajda int ret; 13497eb8f069SAndrzej Hajda 13507eb8f069SAndrzej Hajda ret = exynos_dsi_of_read_u32(node, "samsung,pll-clock-frequency", 13517eb8f069SAndrzej Hajda &dsi->pll_clk_rate); 13527eb8f069SAndrzej Hajda if (ret < 0) 13537eb8f069SAndrzej Hajda return ret; 13547eb8f069SAndrzej Hajda 13557eb8f069SAndrzej Hajda port = of_graph_get_port_by_reg(node, DSI_PORT_OUT); 13567eb8f069SAndrzej Hajda if (!port) { 13577eb8f069SAndrzej Hajda dev_err(dev, "no output port specified\n"); 13587eb8f069SAndrzej Hajda return -EINVAL; 13597eb8f069SAndrzej Hajda } 13607eb8f069SAndrzej Hajda 13617eb8f069SAndrzej Hajda ep = of_graph_get_endpoint_by_reg(port, 0); 13627eb8f069SAndrzej Hajda of_node_put(port); 13637eb8f069SAndrzej Hajda if (!ep) { 13647eb8f069SAndrzej Hajda dev_err(dev, "no endpoint specified in output port\n"); 13657eb8f069SAndrzej Hajda return -EINVAL; 13667eb8f069SAndrzej Hajda } 13677eb8f069SAndrzej Hajda 13687eb8f069SAndrzej Hajda ret = exynos_dsi_of_read_u32(ep, "samsung,burst-clock-frequency", 13697eb8f069SAndrzej Hajda &dsi->burst_clk_rate); 13707eb8f069SAndrzej Hajda if (ret < 0) 13717eb8f069SAndrzej Hajda goto end; 13727eb8f069SAndrzej Hajda 13737eb8f069SAndrzej Hajda ret = exynos_dsi_of_read_u32(ep, "samsung,esc-clock-frequency", 13747eb8f069SAndrzej Hajda &dsi->esc_clk_rate); 13757eb8f069SAndrzej Hajda 13767eb8f069SAndrzej Hajda end: 13777eb8f069SAndrzej Hajda of_node_put(ep); 13787eb8f069SAndrzej Hajda 13797eb8f069SAndrzej Hajda return ret; 13807eb8f069SAndrzej Hajda } 13817eb8f069SAndrzej Hajda 1382f37cd5e8SInki Dae static int exynos_dsi_bind(struct device *dev, struct device *master, 1383f37cd5e8SInki Dae void *data) 1384f37cd5e8SInki Dae { 1385f37cd5e8SInki Dae struct drm_device *drm_dev = data; 1386f37cd5e8SInki Dae struct exynos_dsi *dsi; 1387f37cd5e8SInki Dae int ret; 1388f37cd5e8SInki Dae 1389f37cd5e8SInki Dae ret = exynos_drm_create_enc_conn(drm_dev, &exynos_dsi_display); 1390f37cd5e8SInki Dae if (ret) { 1391f37cd5e8SInki Dae DRM_ERROR("Encoder create [%d] failed with %d\n", 1392f37cd5e8SInki Dae exynos_dsi_display.type, ret); 1393f37cd5e8SInki Dae return ret; 1394f37cd5e8SInki Dae } 1395f37cd5e8SInki Dae 1396f37cd5e8SInki Dae dsi = exynos_dsi_display.ctx; 1397f37cd5e8SInki Dae 1398f37cd5e8SInki Dae return mipi_dsi_host_register(&dsi->dsi_host); 1399f37cd5e8SInki Dae } 1400f37cd5e8SInki Dae 1401f37cd5e8SInki Dae static void exynos_dsi_unbind(struct device *dev, struct device *master, 1402f37cd5e8SInki Dae void *data) 1403f37cd5e8SInki Dae { 1404f37cd5e8SInki Dae struct exynos_dsi *dsi = exynos_dsi_display.ctx; 1405f37cd5e8SInki Dae struct drm_encoder *encoder = dsi->encoder; 1406f37cd5e8SInki Dae 1407f37cd5e8SInki Dae exynos_dsi_dpms(&exynos_dsi_display, DRM_MODE_DPMS_OFF); 1408f37cd5e8SInki Dae 1409f37cd5e8SInki Dae mipi_dsi_host_unregister(&dsi->dsi_host); 1410f37cd5e8SInki Dae 1411f37cd5e8SInki Dae encoder->funcs->destroy(encoder); 1412f37cd5e8SInki Dae drm_connector_cleanup(&dsi->connector); 1413f37cd5e8SInki Dae } 1414f37cd5e8SInki Dae 1415f37cd5e8SInki Dae static const struct component_ops exynos_dsi_component_ops = { 1416f37cd5e8SInki Dae .bind = exynos_dsi_bind, 1417f37cd5e8SInki Dae .unbind = exynos_dsi_unbind, 1418f37cd5e8SInki Dae }; 1419f37cd5e8SInki Dae 14207eb8f069SAndrzej Hajda static int exynos_dsi_probe(struct platform_device *pdev) 14217eb8f069SAndrzej Hajda { 14227eb8f069SAndrzej Hajda struct resource *res; 14237eb8f069SAndrzej Hajda struct exynos_dsi *dsi; 14247eb8f069SAndrzej Hajda int ret; 14257eb8f069SAndrzej Hajda 1426df5225bcSInki Dae ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR, 1427df5225bcSInki Dae exynos_dsi_display.type); 1428df5225bcSInki Dae if (ret) 1429df5225bcSInki Dae return ret; 1430df5225bcSInki Dae 14317eb8f069SAndrzej Hajda dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL); 14327eb8f069SAndrzej Hajda if (!dsi) { 14337eb8f069SAndrzej Hajda dev_err(&pdev->dev, "failed to allocate dsi object.\n"); 1434df5225bcSInki Dae ret = -ENOMEM; 1435df5225bcSInki Dae goto err_del_component; 14367eb8f069SAndrzej Hajda } 14377eb8f069SAndrzej Hajda 14387eb8f069SAndrzej Hajda init_completion(&dsi->completed); 14397eb8f069SAndrzej Hajda spin_lock_init(&dsi->transfer_lock); 14407eb8f069SAndrzej Hajda INIT_LIST_HEAD(&dsi->transfer_list); 14417eb8f069SAndrzej Hajda 14427eb8f069SAndrzej Hajda dsi->dsi_host.ops = &exynos_dsi_ops; 14437eb8f069SAndrzej Hajda dsi->dsi_host.dev = &pdev->dev; 14447eb8f069SAndrzej Hajda 14457eb8f069SAndrzej Hajda dsi->dev = &pdev->dev; 14467eb8f069SAndrzej Hajda 14477eb8f069SAndrzej Hajda ret = exynos_dsi_parse_dt(dsi); 14487eb8f069SAndrzej Hajda if (ret) 1449df5225bcSInki Dae goto err_del_component; 14507eb8f069SAndrzej Hajda 14517eb8f069SAndrzej Hajda dsi->supplies[0].supply = "vddcore"; 14527eb8f069SAndrzej Hajda dsi->supplies[1].supply = "vddio"; 14537eb8f069SAndrzej Hajda ret = devm_regulator_bulk_get(&pdev->dev, ARRAY_SIZE(dsi->supplies), 14547eb8f069SAndrzej Hajda dsi->supplies); 14557eb8f069SAndrzej Hajda if (ret) { 14567eb8f069SAndrzej Hajda dev_info(&pdev->dev, "failed to get regulators: %d\n", ret); 14577eb8f069SAndrzej Hajda return -EPROBE_DEFER; 14587eb8f069SAndrzej Hajda } 14597eb8f069SAndrzej Hajda 14607eb8f069SAndrzej Hajda dsi->pll_clk = devm_clk_get(&pdev->dev, "pll_clk"); 14617eb8f069SAndrzej Hajda if (IS_ERR(dsi->pll_clk)) { 14627eb8f069SAndrzej Hajda dev_info(&pdev->dev, "failed to get dsi pll input clock\n"); 1463df5225bcSInki Dae ret = PTR_ERR(dsi->pll_clk); 1464df5225bcSInki Dae goto err_del_component; 14657eb8f069SAndrzej Hajda } 14667eb8f069SAndrzej Hajda 14677eb8f069SAndrzej Hajda dsi->bus_clk = devm_clk_get(&pdev->dev, "bus_clk"); 14687eb8f069SAndrzej Hajda if (IS_ERR(dsi->bus_clk)) { 14697eb8f069SAndrzej Hajda dev_info(&pdev->dev, "failed to get dsi bus clock\n"); 1470df5225bcSInki Dae ret = PTR_ERR(dsi->bus_clk); 1471df5225bcSInki Dae goto err_del_component; 14727eb8f069SAndrzej Hajda } 14737eb8f069SAndrzej Hajda 14747eb8f069SAndrzej Hajda res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 14757eb8f069SAndrzej Hajda dsi->reg_base = devm_ioremap_resource(&pdev->dev, res); 1476293d3f6aSJingoo Han if (IS_ERR(dsi->reg_base)) { 14777eb8f069SAndrzej Hajda dev_err(&pdev->dev, "failed to remap io region\n"); 1478df5225bcSInki Dae ret = PTR_ERR(dsi->reg_base); 1479df5225bcSInki Dae goto err_del_component; 14807eb8f069SAndrzej Hajda } 14817eb8f069SAndrzej Hajda 14827eb8f069SAndrzej Hajda dsi->phy = devm_phy_get(&pdev->dev, "dsim"); 14837eb8f069SAndrzej Hajda if (IS_ERR(dsi->phy)) { 14847eb8f069SAndrzej Hajda dev_info(&pdev->dev, "failed to get dsim phy\n"); 1485df5225bcSInki Dae ret = PTR_ERR(dsi->phy); 1486df5225bcSInki Dae goto err_del_component; 14877eb8f069SAndrzej Hajda } 14887eb8f069SAndrzej Hajda 14897eb8f069SAndrzej Hajda dsi->irq = platform_get_irq(pdev, 0); 14907eb8f069SAndrzej Hajda if (dsi->irq < 0) { 14917eb8f069SAndrzej Hajda dev_err(&pdev->dev, "failed to request dsi irq resource\n"); 1492df5225bcSInki Dae ret = dsi->irq; 1493df5225bcSInki Dae goto err_del_component; 14947eb8f069SAndrzej Hajda } 14957eb8f069SAndrzej Hajda 14967eb8f069SAndrzej Hajda irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN); 14977eb8f069SAndrzej Hajda ret = devm_request_threaded_irq(&pdev->dev, dsi->irq, NULL, 14987eb8f069SAndrzej Hajda exynos_dsi_irq, IRQF_ONESHOT, 14997eb8f069SAndrzej Hajda dev_name(&pdev->dev), dsi); 15007eb8f069SAndrzej Hajda if (ret) { 15017eb8f069SAndrzej Hajda dev_err(&pdev->dev, "failed to request dsi irq\n"); 1502df5225bcSInki Dae goto err_del_component; 15037eb8f069SAndrzej Hajda } 15047eb8f069SAndrzej Hajda 15057eb8f069SAndrzej Hajda exynos_dsi_display.ctx = dsi; 15067eb8f069SAndrzej Hajda 15077eb8f069SAndrzej Hajda platform_set_drvdata(pdev, &exynos_dsi_display); 15087eb8f069SAndrzej Hajda 1509df5225bcSInki Dae ret = component_add(&pdev->dev, &exynos_dsi_component_ops); 1510df5225bcSInki Dae if (ret) 1511df5225bcSInki Dae goto err_del_component; 1512df5225bcSInki Dae 1513df5225bcSInki Dae return ret; 1514df5225bcSInki Dae 1515df5225bcSInki Dae err_del_component: 1516df5225bcSInki Dae exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR); 1517df5225bcSInki Dae return ret; 15187eb8f069SAndrzej Hajda } 15197eb8f069SAndrzej Hajda 15207eb8f069SAndrzej Hajda static int exynos_dsi_remove(struct platform_device *pdev) 15217eb8f069SAndrzej Hajda { 1522df5225bcSInki Dae component_del(&pdev->dev, &exynos_dsi_component_ops); 1523df5225bcSInki Dae exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR); 1524df5225bcSInki Dae 15257eb8f069SAndrzej Hajda return 0; 15267eb8f069SAndrzej Hajda } 15277eb8f069SAndrzej Hajda 15287eb8f069SAndrzej Hajda static struct of_device_id exynos_dsi_of_match[] = { 15297eb8f069SAndrzej Hajda { .compatible = "samsung,exynos4210-mipi-dsi" }, 15307eb8f069SAndrzej Hajda { } 15317eb8f069SAndrzej Hajda }; 15327eb8f069SAndrzej Hajda 15337eb8f069SAndrzej Hajda struct platform_driver dsi_driver = { 15347eb8f069SAndrzej Hajda .probe = exynos_dsi_probe, 15357eb8f069SAndrzej Hajda .remove = exynos_dsi_remove, 15367eb8f069SAndrzej Hajda .driver = { 15377eb8f069SAndrzej Hajda .name = "exynos-dsi", 15387eb8f069SAndrzej Hajda .owner = THIS_MODULE, 15397eb8f069SAndrzej Hajda .of_match_table = exynos_dsi_of_match, 15407eb8f069SAndrzej Hajda }, 15417eb8f069SAndrzej Hajda }; 15427eb8f069SAndrzej Hajda 15437eb8f069SAndrzej Hajda MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>"); 15447eb8f069SAndrzej Hajda MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>"); 15457eb8f069SAndrzej Hajda MODULE_DESCRIPTION("Samsung SoC MIPI DSI Master"); 15467eb8f069SAndrzej Hajda MODULE_LICENSE("GPL v2"); 1547