A patch series for moving MIPI-DSI driver for Exynos DRM to drm/bridge directory so that I.MX SoC family can also share the same device driver. Samsung MIPI DSIM device is a common IP that can be used by Exynos and I.MX8M Mini/Nano/Plus SoC. Regarding this, this patch series has added several things below to existing MIPI DSI driver, - Add exynos_dsi_type enum type to provide controller data from different platforms. - Add two pipeline detection ways support - existing Exynos DSI child node and I.MX family of-graph port or ports. - Consider component and bridged based DRM drivers. - Add device tree binding support of I.MX family.
Unified · Split
| @@ -7,6 +7,8 @@ Required properties: · 6 unmodified lines | ||
| 7 | 7 | "samsung,exynos5410-mipi-dsi" /* for Exynos5410/5420/5440 SoCs */ |
| 8 | 8 | "samsung,exynos5422-mipi-dsi" /* for Exynos5422/5800 SoCs */ |
| 9 | 9 | "samsung,exynos5433-mipi-dsi" /* for Exynos5433 SoCs */ |
| 10 | + "fsl,imx8mm-mipi-dsim" /* for i.MX8M Mini/Nano SoCs */ | |
| 11 | + "fsl,imx8mp-mipi-dsim" /* for i.MX8M Plus SoCs */ | |
| 10 | 12 | - reg: physical base address and length of the registers set for the device |
| 11 | 13 | - interrupts: should contain DSI interrupt |
| 12 | 14 | - clocks: list of clock specifiers, must contain an entry for each required |
| @@ -6624,6 +6624,15 @@ T: git git://anongit.freedesktop.org/drm/drm-misc · 6623 unmodified lines | ||
| 6624 | 6624 | F: Documentation/devicetree/bindings/display/panel/samsung,lms397kf04.yaml |
| 6625 | 6625 | F: drivers/gpu/drm/panel/panel-samsung-db7430.c |
| 6626 | 6626 | |
| 6627 | +DRM DRIVER FOR SAMSUNG MIPI DSIM BRIDGE | |
| 6628 | +M: Inki Dae <inki.dae@samsung.com> | |
| 6629 | +M: Jagan Teki <jagan@amarulasolutions.com> | |
| 6630 | +M: Marek Szyprowski <m.szyprowski@samsung.com> | |
| 6631 | +S: Maintained | |
| 6632 | +T: git git://anongit.freedesktop.org/drm/drm-misc | |
| 6633 | +F: drivers/gpu/drm/bridge/samsung-dsim.c | |
| 6634 | +F: include/drm/bridge/samsung-dsim.h | |
| 6635 | + | |
| 6627 | 6636 | DRM DRIVER FOR SAMSUNG S6D27A1 PANELS |
| 6628 | 6637 | M: Markuss Broks <markuss.broks@gmail.com> |
| 6629 | 6638 | S: Maintained |
| @@ -220,6 +220,18 @@ config DRM_PARADE_PS8640 · 219 unmodified lines | ||
| 220 | 220 | The PS8640 is a high-performance and low-power |
| 221 | 221 | MIPI DSI to eDP converter |
| 222 | 222 | |
| 223 | +config DRM_SAMSUNG_DSIM | |
| 224 | + tristate "Samsung MIPI DSIM bridge driver" | |
| 225 | + depends on COMMON_CLK | |
| 226 | + depends on OF && HAS_IOMEM | |
| 227 | + select DRM_KMS_HELPER | |
| 228 | + select DRM_MIPI_DSI | |
| 229 | + select DRM_PANEL_BRIDGE | |
| 230 | + help | |
| 231 | + The Samsung MIPI DSIM bridge controller driver. | |
| 232 | + This MIPI DSIM bridge can be found it on Exynos SoCs and | |
| 233 | + NXP's i.MX8M Mini/Nano. | |
| 234 | + | |
| 223 | 235 | config DRM_SIL_SII8620 |
| 224 | 236 | tristate "Silicon Image SII8620 HDMI/MHL bridge" |
| 225 | 237 | depends on OF |
| @@ -14,6 +14,7 @@ obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v · 13 unmodified lines | ||
| 14 | 14 | obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o |
| 15 | 15 | obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o |
| 16 | 16 | obj-$(CONFIG_DRM_PARADE_PS8640) += parade-ps8640.o |
| 17 | +obj-$(CONFIG_DRM_SAMSUNG_DSIM) += samsung-dsim.o | |
| 17 | 18 | obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o |
| 18 | 19 | obj-$(CONFIG_DRM_SII902X) += sii902x.o |
| 19 | 20 | obj-$(CONFIG_DRM_SII9234) += sii9234.o |
| @@ -0,0 +1,1967 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * Samsung MIPI DSIM bridge driver. | |
| 4 | + * | |
| 5 | + * Copyright (C) 2021 Amarula Solutions(India) | |
| 6 | + * Copyright (c) 2014 Samsung Electronics Co., Ltd | |
| 7 | + * Author: Jagan Teki <jagan@amarulasolutions.com> | |
| 8 | + * | |
| 9 | + * Based on exynos_drm_dsi from | |
| 10 | + * Tomasz Figa <t.figa@samsung.com> | |
| 11 | + */ | |
| 12 | + | |
| 13 | +#include <asm/unaligned.h> | |
| 14 | + | |
| 15 | +#include <linux/clk.h> | |
| 16 | +#include <linux/delay.h> | |
| 17 | +#include <linux/irq.h> | |
| 18 | +#include <linux/media-bus-format.h> | |
| 19 | +#include <linux/of_device.h> | |
| 20 | +#include <linux/phy/phy.h> | |
| 21 | + | |
| 22 | +#include <video/mipi_display.h> | |
| 23 | + | |
| 24 | +#include <drm/bridge/samsung-dsim.h> | |
| 25 | +#include <drm/drm_panel.h> | |
| 26 | +#include <drm/drm_print.h> | |
| 27 | + | |
| 28 | +/* returns true iff both arguments logically differs */ | |
| 29 | +#define NEQV(a, b) (!(a) ^ !(b)) | |
| 30 | + | |
| 31 | +/* DSIM_STATUS */ | |
| 32 | +#define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0) | |
| 33 | +#define DSIM_STOP_STATE_CLK BIT(8) | |
| 34 | +#define DSIM_TX_READY_HS_CLK BIT(10) | |
| 35 | +#define DSIM_PLL_STABLE BIT(31) | |
| 36 | + | |
| 37 | +/* DSIM_SWRST */ | |
| 38 | +#define DSIM_FUNCRST BIT(16) | |
| 39 | +#define DSIM_SWRST BIT(0) | |
| 40 | + | |
| 41 | +/* DSIM_TIMEOUT */ | |
| 42 | +#define DSIM_LPDR_TIMEOUT(x) ((x) << 0) | |
| 43 | +#define DSIM_BTA_TIMEOUT(x) ((x) << 16) | |
| 44 | + | |
| 45 | +/* DSIM_CLKCTRL */ | |
| 46 | +#define DSIM_ESC_PRESCALER(x) (((x) & 0xffff) << 0) | |
| 47 | +#define DSIM_ESC_PRESCALER_MASK (0xffff << 0) | |
| 48 | +#define DSIM_LANE_ESC_CLK_EN_CLK BIT(19) | |
| 49 | +#define DSIM_LANE_ESC_CLK_EN_DATA(x) (((x) & 0xf) << 20) | |
| 50 | +#define DSIM_LANE_ESC_CLK_EN_DATA_MASK (0xf << 20) | |
| 51 | +#define DSIM_BYTE_CLKEN BIT(24) | |
| 52 | +#define DSIM_BYTE_CLK_SRC(x) (((x) & 0x3) << 25) | |
| 53 | +#define DSIM_BYTE_CLK_SRC_MASK (0x3 << 25) | |
| 54 | +#define DSIM_PLL_BYPASS BIT(27) | |
| 55 | +#define DSIM_ESC_CLKEN BIT(28) | |
| 56 | +#define DSIM_TX_REQUEST_HSCLK BIT(31) | |
| 57 | + | |
| 58 | +/* DSIM_CONFIG */ | |
| 59 | +#define DSIM_LANE_EN_CLK BIT(0) | |
| 60 | +#define DSIM_LANE_EN(x) (((x) & 0xf) << 1) | |
| 61 | +#define DSIM_NUM_OF_DATA_LANE(x) (((x) & 0x3) << 5) | |
| 62 | +#define DSIM_SUB_PIX_FORMAT(x) (((x) & 0x7) << 8) | |
| 63 | +#define DSIM_MAIN_PIX_FORMAT_MASK (0x7 << 12) | |
| 64 | +#define DSIM_MAIN_PIX_FORMAT_RGB888 (0x7 << 12) | |
| 65 | +#define DSIM_MAIN_PIX_FORMAT_RGB666 (0x6 << 12) | |
| 66 | +#define DSIM_MAIN_PIX_FORMAT_RGB666_P (0x5 << 12) | |
| 67 | +#define DSIM_MAIN_PIX_FORMAT_RGB565 (0x4 << 12) | |
| 68 | +#define DSIM_SUB_VC (((x) & 0x3) << 16) | |
| 69 | +#define DSIM_MAIN_VC (((x) & 0x3) << 18) | |
| 70 | +#define DSIM_HSA_DISABLE_MODE BIT(20) | |
| 71 | +#define DSIM_HBP_DISABLE_MODE BIT(21) | |
| 72 | +#define DSIM_HFP_DISABLE_MODE BIT(22) | |
| 73 | +/* | |
| 74 | + * The i.MX 8M Mini Applications Processor Reference Manual, | |
| 75 | + * Rev. 3, 11/2020 Page 4091 | |
| 76 | + * The i.MX 8M Nano Applications Processor Reference Manual, | |
| 77 | + * Rev. 2, 07/2022 Page 3058 | |
| 78 | + * The i.MX 8M Plus Applications Processor Reference Manual, | |
| 79 | + * Rev. 1, 06/2021 Page 5436 | |
| 80 | + * all claims this bit is 'HseDisableMode' with the definition | |
| 81 | + * 0 = Disables transfer | |
| 82 | + * 1 = Enables transfer | |
| 83 | + * | |
| 84 | + * This clearly states that HSE is not a disabled bit. | |
| 85 | + * | |
| 86 | + * The naming convention follows as per the manual and the | |
| 87 | + * driver logic is based on the MIPI_DSI_MODE_VIDEO_HSE flag. | |
| 88 | + */ | |
| 89 | +#define DSIM_HSE_DISABLE_MODE BIT(23) | |
| 90 | +#define DSIM_AUTO_MODE BIT(24) | |
| 91 | +#define DSIM_VIDEO_MODE BIT(25) | |
| 92 | +#define DSIM_BURST_MODE BIT(26) | |
| 93 | +#define DSIM_SYNC_INFORM BIT(27) | |
| 94 | +#define DSIM_EOT_DISABLE BIT(28) | |
| 95 | +#define DSIM_MFLUSH_VS BIT(29) | |
| 96 | +/* This flag is valid only for exynos3250/3472/5260/5430 */ | |
| 97 | +#define DSIM_CLKLANE_STOP BIT(30) | |
| 98 | + | |
| 99 | +/* DSIM_ESCMODE */ | |
| 100 | +#define DSIM_TX_TRIGGER_RST BIT(4) | |
| 101 | +#define DSIM_TX_LPDT_LP BIT(6) | |
| 102 | +#define DSIM_CMD_LPDT_LP BIT(7) | |
| 103 | +#define DSIM_FORCE_BTA BIT(16) | |
| 104 | +#define DSIM_FORCE_STOP_STATE BIT(20) | |
| 105 | +#define DSIM_STOP_STATE_CNT(x) (((x) & 0x7ff) << 21) | |
| 106 | +#define DSIM_STOP_STATE_CNT_MASK (0x7ff << 21) | |
| 107 | + | |
| 108 | +/* DSIM_MDRESOL */ | |
| 109 | +#define DSIM_MAIN_STAND_BY BIT(31) | |
| 110 | +#define DSIM_MAIN_VRESOL(x, num_bits) (((x) & ((1 << (num_bits)) - 1)) << 16) | |
| 111 | +#define DSIM_MAIN_HRESOL(x, num_bits) (((x) & ((1 << (num_bits)) - 1)) << 0) | |
| 112 | + | |
| 113 | +/* DSIM_MVPORCH */ | |
| 114 | +#define DSIM_CMD_ALLOW(x) ((x) << 28) | |
| 115 | +#define DSIM_STABLE_VFP(x) ((x) << 16) | |
| 116 | +#define DSIM_MAIN_VBP(x) ((x) << 0) | |
| 117 | +#define DSIM_CMD_ALLOW_MASK (0xf << 28) | |
| 118 | +#define DSIM_STABLE_VFP_MASK (0x7ff << 16) | |
| 119 | +#define DSIM_MAIN_VBP_MASK (0x7ff << 0) | |
| 120 | + | |
| 121 | +/* DSIM_MHPORCH */ | |
| 122 | +#define DSIM_MAIN_HFP(x) ((x) << 16) | |
| 123 | +#define DSIM_MAIN_HBP(x) ((x) << 0) | |
| 124 | +#define DSIM_MAIN_HFP_MASK ((0xffff) << 16) | |
| 125 | +#define DSIM_MAIN_HBP_MASK ((0xffff) << 0) | |
| 126 | + | |
| 127 | +/* DSIM_MSYNC */ | |
| 128 | +#define DSIM_MAIN_VSA(x) ((x) << 22) | |
| 129 | +#define DSIM_MAIN_HSA(x) ((x) << 0) | |
| 130 | +#define DSIM_MAIN_VSA_MASK ((0x3ff) << 22) | |
| 131 | +#define DSIM_MAIN_HSA_MASK ((0xffff) << 0) | |
| 132 | + | |
| 133 | +/* DSIM_SDRESOL */ | |
| 134 | +#define DSIM_SUB_STANDY(x) ((x) << 31) | |
| 135 | +#define DSIM_SUB_VRESOL(x) ((x) << 16) | |
| 136 | +#define DSIM_SUB_HRESOL(x) ((x) << 0) | |
| 137 | +#define DSIM_SUB_STANDY_MASK ((0x1) << 31) | |
| 138 | +#define DSIM_SUB_VRESOL_MASK ((0x7ff) << 16) | |
| 139 | +#define DSIM_SUB_HRESOL_MASK ((0x7ff) << 0) | |
| 140 | + | |
| 141 | +/* DSIM_INTSRC */ | |
| 142 | +#define DSIM_INT_PLL_STABLE BIT(31) | |
| 143 | +#define DSIM_INT_SW_RST_RELEASE BIT(30) | |
| 144 | +#define DSIM_INT_SFR_FIFO_EMPTY BIT(29) | |
| 145 | +#define DSIM_INT_SFR_HDR_FIFO_EMPTY BIT(28) | |
| 146 | +#define DSIM_INT_BTA BIT(25) | |
| 147 | +#define DSIM_INT_FRAME_DONE BIT(24) | |
| 148 | +#define DSIM_INT_RX_TIMEOUT BIT(21) | |
| 149 | +#define DSIM_INT_BTA_TIMEOUT BIT(20) | |
| 150 | +#define DSIM_INT_RX_DONE BIT(18) | |
| 151 | +#define DSIM_INT_RX_TE BIT(17) | |
| 152 | +#define DSIM_INT_RX_ACK BIT(16) | |
| 153 | +#define DSIM_INT_RX_ECC_ERR BIT(15) | |
| 154 | +#define DSIM_INT_RX_CRC_ERR BIT(14) | |
| 155 | + | |
| 156 | +/* DSIM_FIFOCTRL */ | |
| 157 | +#define DSIM_RX_DATA_FULL BIT(25) | |
| 158 | +#define DSIM_RX_DATA_EMPTY BIT(24) | |
| 159 | +#define DSIM_SFR_HEADER_FULL BIT(23) | |
| 160 | +#define DSIM_SFR_HEADER_EMPTY BIT(22) | |
| 161 | +#define DSIM_SFR_PAYLOAD_FULL BIT(21) | |
| 162 | +#define DSIM_SFR_PAYLOAD_EMPTY BIT(20) | |
| 163 | +#define DSIM_I80_HEADER_FULL BIT(19) | |
| 164 | +#define DSIM_I80_HEADER_EMPTY BIT(18) | |
| 165 | +#define DSIM_I80_PAYLOAD_FULL BIT(17) | |
| 166 | +#define DSIM_I80_PAYLOAD_EMPTY BIT(16) | |
| 167 | +#define DSIM_SD_HEADER_FULL BIT(15) | |
| 168 | +#define DSIM_SD_HEADER_EMPTY BIT(14) | |
| 169 | +#define DSIM_SD_PAYLOAD_FULL BIT(13) | |
| 170 | +#define DSIM_SD_PAYLOAD_EMPTY BIT(12) | |
| 171 | +#define DSIM_MD_HEADER_FULL BIT(11) | |
| 172 | +#define DSIM_MD_HEADER_EMPTY BIT(10) | |
| 173 | +#define DSIM_MD_PAYLOAD_FULL BIT(9) | |
| 174 | +#define DSIM_MD_PAYLOAD_EMPTY BIT(8) | |
| 175 | +#define DSIM_RX_FIFO BIT(4) | |
| 176 | +#define DSIM_SFR_FIFO BIT(3) | |
| 177 | +#define DSIM_I80_FIFO BIT(2) | |
| 178 | +#define DSIM_SD_FIFO BIT(1) | |
| 179 | +#define DSIM_MD_FIFO BIT(0) | |
| 180 | + | |
| 181 | +/* DSIM_PHYACCHR */ | |
| 182 | +#define DSIM_AFC_EN BIT(14) | |
| 183 | +#define DSIM_AFC_CTL(x) (((x) & 0x7) << 5) | |
| 184 | + | |
| 185 | +/* DSIM_PLLCTRL */ | |
| 186 | +#define DSIM_FREQ_BAND(x) ((x) << 24) | |
| 187 | +#define DSIM_PLL_EN BIT(23) | |
| 188 | +#define DSIM_PLL_P(x, offset) ((x) << (offset)) | |
| 189 | +#define DSIM_PLL_M(x) ((x) << 4) | |
| 190 | +#define DSIM_PLL_S(x) ((x) << 1) | |
| 191 | + | |
| 192 | +/* DSIM_PHYCTRL */ | |
| 193 | +#define DSIM_PHYCTRL_ULPS_EXIT(x) (((x) & 0x1ff) << 0) | |
| 194 | +#define DSIM_PHYCTRL_B_DPHYCTL_VREG_LP BIT(30) | |
| 195 | +#define DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP BIT(14) | |
| 196 | + | |
| 197 | +/* DSIM_PHYTIMING */ | |
| 198 | +#define DSIM_PHYTIMING_LPX(x) ((x) << 8) | |
| 199 | +#define DSIM_PHYTIMING_HS_EXIT(x) ((x) << 0) | |
| 200 | + | |
| 201 | +/* DSIM_PHYTIMING1 */ | |
| 202 | +#define DSIM_PHYTIMING1_CLK_PREPARE(x) ((x) << 24) | |
| 203 | +#define DSIM_PHYTIMING1_CLK_ZERO(x) ((x) << 16) | |
| 204 | +#define DSIM_PHYTIMING1_CLK_POST(x) ((x) << 8) | |
| 205 | +#define DSIM_PHYTIMING1_CLK_TRAIL(x) ((x) << 0) | |
| 206 | + | |
| 207 | +/* DSIM_PHYTIMING2 */ | |
| 208 | +#define DSIM_PHYTIMING2_HS_PREPARE(x) ((x) << 16) | |
| 209 | +#define DSIM_PHYTIMING2_HS_ZERO(x) ((x) << 8) | |
| 210 | +#define DSIM_PHYTIMING2_HS_TRAIL(x) ((x) << 0) | |
| 211 | + | |
| 212 | +#define DSI_MAX_BUS_WIDTH 4 | |
| 213 | +#define DSI_NUM_VIRTUAL_CHANNELS 4 | |
| 214 | +#define DSI_TX_FIFO_SIZE 2048 | |
| 215 | +#define DSI_RX_FIFO_SIZE 256 | |
| 216 | +#define DSI_XFER_TIMEOUT_MS 100 | |
| 217 | +#define DSI_RX_FIFO_EMPTY 0x30800002 | |
| 218 | + | |
| 219 | +#define OLD_SCLK_MIPI_CLK_NAME "pll_clk" | |
| 220 | + | |
| 221 | +static const char *const clk_names[5] = { | |
| 222 | + "bus_clk", | |
| 223 | + "sclk_mipi", | |
| 224 | + "phyclk_mipidphy0_bitclkdiv8", | |
| 225 | + "phyclk_mipidphy0_rxclkesc0", | |
| 226 | + "sclk_rgb_vclk_to_dsim0" | |
| 227 | +}; | |
| 228 | + | |
| 229 | +enum samsung_dsim_transfer_type { | |
| 230 | + EXYNOS_DSI_TX, | |
| 231 | + EXYNOS_DSI_RX, | |
| 232 | +}; | |
| 233 | + | |
| 234 | +enum reg_idx { | |
| 235 | + DSIM_STATUS_REG, /* Status register */ | |
| 236 | + DSIM_SWRST_REG, /* Software reset register */ | |
| 237 | + DSIM_CLKCTRL_REG, /* Clock control register */ | |
| 238 | + DSIM_TIMEOUT_REG, /* Time out register */ | |
| 239 | + DSIM_CONFIG_REG, /* Configuration register */ | |
| 240 | + DSIM_ESCMODE_REG, /* Escape mode register */ | |
| 241 | + DSIM_MDRESOL_REG, | |
| 242 | + DSIM_MVPORCH_REG, /* Main display Vporch register */ | |
| 243 | + DSIM_MHPORCH_REG, /* Main display Hporch register */ | |
| 244 | + DSIM_MSYNC_REG, /* Main display sync area register */ | |
| 245 | + DSIM_INTSRC_REG, /* Interrupt source register */ | |
| 246 | + DSIM_INTMSK_REG, /* Interrupt mask register */ | |
| 247 | + DSIM_PKTHDR_REG, /* Packet Header FIFO register */ | |
| 248 | + DSIM_PAYLOAD_REG, /* Payload FIFO register */ | |
| 249 | + DSIM_RXFIFO_REG, /* Read FIFO register */ | |
| 250 | + DSIM_FIFOCTRL_REG, /* FIFO status and control register */ | |
| 251 | + DSIM_PLLCTRL_REG, /* PLL control register */ | |
| 252 | + DSIM_PHYCTRL_REG, | |
| 253 | + DSIM_PHYTIMING_REG, | |
| 254 | + DSIM_PHYTIMING1_REG, | |
| 255 | + DSIM_PHYTIMING2_REG, | |
| 256 | + NUM_REGS | |
| 257 | +}; | |
| 258 | + | |
| 259 | +static const unsigned int exynos_reg_ofs[] = { | |
| 260 | + [DSIM_STATUS_REG] = 0x00, | |
| 261 | + [DSIM_SWRST_REG] = 0x04, | |
| 262 | + [DSIM_CLKCTRL_REG] = 0x08, | |
| 263 | + [DSIM_TIMEOUT_REG] = 0x0c, | |
| 264 | + [DSIM_CONFIG_REG] = 0x10, | |
| 265 | + [DSIM_ESCMODE_REG] = 0x14, | |
| 266 | + [DSIM_MDRESOL_REG] = 0x18, | |
| 267 | + [DSIM_MVPORCH_REG] = 0x1c, | |
| 268 | + [DSIM_MHPORCH_REG] = 0x20, | |
| 269 | + [DSIM_MSYNC_REG] = 0x24, | |
| 270 | + [DSIM_INTSRC_REG] = 0x2c, | |
| 271 | + [DSIM_INTMSK_REG] = 0x30, | |
| 272 | + [DSIM_PKTHDR_REG] = 0x34, | |
| 273 | + [DSIM_PAYLOAD_REG] = 0x38, | |
| 274 | + [DSIM_RXFIFO_REG] = 0x3c, | |
| 275 | + [DSIM_FIFOCTRL_REG] = 0x44, | |
| 276 | + [DSIM_PLLCTRL_REG] = 0x4c, | |
| 277 | + [DSIM_PHYCTRL_REG] = 0x5c, | |
| 278 | + [DSIM_PHYTIMING_REG] = 0x64, | |
| 279 | + [DSIM_PHYTIMING1_REG] = 0x68, | |
| 280 | + [DSIM_PHYTIMING2_REG] = 0x6c, | |
| 281 | +}; | |
| 282 | + | |
| 283 | +static const unsigned int exynos5433_reg_ofs[] = { | |
| 284 | + [DSIM_STATUS_REG] = 0x04, | |
| 285 | + [DSIM_SWRST_REG] = 0x0C, | |
| 286 | + [DSIM_CLKCTRL_REG] = 0x10, | |
| 287 | + [DSIM_TIMEOUT_REG] = 0x14, | |
| 288 | + [DSIM_CONFIG_REG] = 0x18, | |
| 289 | + [DSIM_ESCMODE_REG] = 0x1C, | |
| 290 | + [DSIM_MDRESOL_REG] = 0x20, | |
| 291 | + [DSIM_MVPORCH_REG] = 0x24, | |
| 292 | + [DSIM_MHPORCH_REG] = 0x28, | |
| 293 | + [DSIM_MSYNC_REG] = 0x2C, | |
| 294 | + [DSIM_INTSRC_REG] = 0x34, | |
| 295 | + [DSIM_INTMSK_REG] = 0x38, | |
| 296 | + [DSIM_PKTHDR_REG] = 0x3C, | |
| 297 | + [DSIM_PAYLOAD_REG] = 0x40, | |
| 298 | + [DSIM_RXFIFO_REG] = 0x44, | |
| 299 | + [DSIM_FIFOCTRL_REG] = 0x4C, | |
| 300 | + [DSIM_PLLCTRL_REG] = 0x94, | |
| 301 | + [DSIM_PHYCTRL_REG] = 0xA4, | |
| 302 | + [DSIM_PHYTIMING_REG] = 0xB4, | |
| 303 | + [DSIM_PHYTIMING1_REG] = 0xB8, | |
| 304 | + [DSIM_PHYTIMING2_REG] = 0xBC, | |
| 305 | +}; | |
| 306 | + | |
| 307 | +enum reg_value_idx { | |
| 308 | + RESET_TYPE, | |
| 309 | + PLL_TIMER, | |
| 310 | + STOP_STATE_CNT, | |
| 311 | + PHYCTRL_ULPS_EXIT, | |
| 312 | + PHYCTRL_VREG_LP, | |
| 313 | + PHYCTRL_SLEW_UP, | |
| 314 | + PHYTIMING_LPX, | |
| 315 | + PHYTIMING_HS_EXIT, | |
| 316 | + PHYTIMING_CLK_PREPARE, | |
| 317 | + PHYTIMING_CLK_ZERO, | |
| 318 | + PHYTIMING_CLK_POST, | |
| 319 | + PHYTIMING_CLK_TRAIL, | |
| 320 | + PHYTIMING_HS_PREPARE, | |
| 321 | + PHYTIMING_HS_ZERO, | |
| 322 | + PHYTIMING_HS_TRAIL | |
| 323 | +}; | |
| 324 | + | |
| 325 | +static const unsigned int reg_values[] = { | |
| 326 | + [RESET_TYPE] = DSIM_SWRST, | |
| 327 | + [PLL_TIMER] = 500, | |
| 328 | + [STOP_STATE_CNT] = 0xf, | |
| 329 | + [PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0x0af), | |
| 330 | + [PHYCTRL_VREG_LP] = 0, | |
| 331 | + [PHYCTRL_SLEW_UP] = 0, | |
| 332 | + [PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x06), | |
| 333 | + [PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0b), | |
| 334 | + [PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x07), | |
| 335 | + [PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x27), | |
| 336 | + [PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0d), | |
| 337 | + [PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x08), | |
| 338 | + [PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x09), | |
| 339 | + [PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x0d), | |
| 340 | + [PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0b), | |
| 341 | +}; | |
| 342 | + | |
| 343 | +static const unsigned int exynos5422_reg_values[] = { | |
| 344 | + [RESET_TYPE] = DSIM_SWRST, | |
| 345 | + [PLL_TIMER] = 500, | |
| 346 | + [STOP_STATE_CNT] = 0xf, | |
| 347 | + [PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0xaf), | |
| 348 | + [PHYCTRL_VREG_LP] = 0, | |
| 349 | + [PHYCTRL_SLEW_UP] = 0, | |
| 350 | + [PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x08), | |
| 351 | + [PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0d), | |
| 352 | + [PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x09), | |
| 353 | + [PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x30), | |
| 354 | + [PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0e), | |
| 355 | + [PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x0a), | |
| 356 | + [PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x0c), | |
| 357 | + [PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x11), | |
| 358 | + [PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0d), | |
| 359 | +}; | |
| 360 | + | |
| 361 | +static const unsigned int exynos5433_reg_values[] = { | |
| 362 | + [RESET_TYPE] = DSIM_FUNCRST, | |
| 363 | + [PLL_TIMER] = 22200, | |
| 364 | + [STOP_STATE_CNT] = 0xa, | |
| 365 | + [PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0x190), | |
| 366 | + [PHYCTRL_VREG_LP] = DSIM_PHYCTRL_B_DPHYCTL_VREG_LP, | |
| 367 | + [PHYCTRL_SLEW_UP] = DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP, | |
| 368 | + [PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x07), | |
| 369 | + [PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0c), | |
| 370 | + [PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x09), | |
| 371 | + [PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x2d), | |
| 372 | + [PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0e), | |
| 373 | + [PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x09), | |
| 374 | + [PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x0b), | |
| 375 | + [PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x10), | |
| 376 | + [PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0c), | |
| 377 | +}; | |
| 378 | + | |
| 379 | +static const unsigned int imx8mm_dsim_reg_values[] = { | |
| 380 | + [RESET_TYPE] = DSIM_SWRST, | |
| 381 | + [PLL_TIMER] = 500, | |
| 382 | + [STOP_STATE_CNT] = 0xf, | |
| 383 | + [PHYCTRL_ULPS_EXIT] = 0, | |
| 384 | + [PHYCTRL_VREG_LP] = 0, | |
| 385 | + [PHYCTRL_SLEW_UP] = 0, | |
| 386 | + [PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x06), | |
| 387 | + [PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0b), | |
| 388 | + [PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x07), | |
| 389 | + [PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x26), | |
| 390 | + [PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0d), | |
| 391 | + [PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x08), | |
| 392 | + [PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x08), | |
| 393 | + [PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x0d), | |
| 394 | + [PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0b), | |
| 395 | +}; | |
| 396 | + | |
| 397 | +static const struct samsung_dsim_driver_data exynos3_dsi_driver_data = { | |
| 398 | + .reg_ofs = exynos_reg_ofs, | |
| 399 | + .plltmr_reg = 0x50, | |
| 400 | + .has_freqband = 1, | |
| 401 | + .has_clklane_stop = 1, | |
| 402 | + .num_clks = 2, | |
| 403 | + .max_freq = 1000, | |
| 404 | + .wait_for_reset = 1, | |
| 405 | + .num_bits_resol = 11, | |
| 406 | + .pll_p_offset = 13, | |
| 407 | + .reg_values = reg_values, | |
| 408 | +}; | |
| 409 | + | |
| 410 | +static const struct samsung_dsim_driver_data exynos4_dsi_driver_data = { | |
| 411 | + .reg_ofs = exynos_reg_ofs, | |
| 412 | + .plltmr_reg = 0x50, | |
| 413 | + .has_freqband = 1, | |
| 414 | + .has_clklane_stop = 1, | |
| 415 | + .num_clks = 2, | |
| 416 | + .max_freq = 1000, | |
| 417 | + .wait_for_reset = 1, | |
| 418 | + .num_bits_resol = 11, | |
| 419 | + .pll_p_offset = 13, | |
| 420 | + .reg_values = reg_values, | |
| 421 | +}; | |
| 422 | + | |
| 423 | +static const struct samsung_dsim_driver_data exynos5_dsi_driver_data = { | |
| 424 | + .reg_ofs = exynos_reg_ofs, | |
| 425 | + .plltmr_reg = 0x58, | |
| 426 | + .num_clks = 2, | |
| 427 | + .max_freq = 1000, | |
| 428 | + .wait_for_reset = 1, | |
| 429 | + .num_bits_resol = 11, | |
| 430 | + .pll_p_offset = 13, | |
| 431 | + .reg_values = reg_values, | |
| 432 | +}; | |
| 433 | + | |
| 434 | +static const struct samsung_dsim_driver_data exynos5433_dsi_driver_data = { | |
| 435 | + .reg_ofs = exynos5433_reg_ofs, | |
| 436 | + .plltmr_reg = 0xa0, | |
| 437 | + .has_clklane_stop = 1, | |
| 438 | + .num_clks = 5, | |
| 439 | + .max_freq = 1500, | |
| 440 | + .wait_for_reset = 0, | |
| 441 | + .num_bits_resol = 12, | |
| 442 | + .pll_p_offset = 13, | |
| 443 | + .reg_values = exynos5433_reg_values, | |
| 444 | +}; | |
| 445 | + | |
| 446 | +static const struct samsung_dsim_driver_data exynos5422_dsi_driver_data = { | |
| 447 | + .reg_ofs = exynos5433_reg_ofs, | |
| 448 | + .plltmr_reg = 0xa0, | |
| 449 | + .has_clklane_stop = 1, | |
| 450 | + .num_clks = 2, | |
| 451 | + .max_freq = 1500, | |
| 452 | + .wait_for_reset = 1, | |
| 453 | + .num_bits_resol = 12, | |
| 454 | + .pll_p_offset = 13, | |
| 455 | + .reg_values = exynos5422_reg_values, | |
| 456 | +}; | |
| 457 | + | |
| 458 | +static const struct samsung_dsim_driver_data imx8mm_dsi_driver_data = { | |
| 459 | + .reg_ofs = exynos5433_reg_ofs, | |
| 460 | + .plltmr_reg = 0xa0, | |
| 461 | + .has_clklane_stop = 1, | |
| 462 | + .num_clks = 2, | |
| 463 | + .max_freq = 2100, | |
| 464 | + .wait_for_reset = 0, | |
| 465 | + .num_bits_resol = 12, | |
| 466 | + /* | |
| 467 | + * Unlike Exynos, PLL_P(PMS_P) offset 14 is used in i.MX8M Mini/Nano/Plus | |
| 468 | + * downstream driver - drivers/gpu/drm/bridge/sec-dsim.c | |
| 469 | + */ | |
| 470 | + .pll_p_offset = 14, | |
| 471 | + .reg_values = imx8mm_dsim_reg_values, | |
| 472 | +}; | |
| 473 | + | |
| 474 | +static const struct samsung_dsim_driver_data * | |
| 475 | +samsung_dsim_types[DSIM_TYPE_COUNT] = { | |
| 476 | + [DSIM_TYPE_EXYNOS3250] = &exynos3_dsi_driver_data, | |
| 477 | + [DSIM_TYPE_EXYNOS4210] = &exynos4_dsi_driver_data, | |
| 478 | + [DSIM_TYPE_EXYNOS5410] = &exynos5_dsi_driver_data, | |
| 479 | + [DSIM_TYPE_EXYNOS5422] = &exynos5422_dsi_driver_data, | |
| 480 | + [DSIM_TYPE_EXYNOS5433] = &exynos5433_dsi_driver_data, | |
| 481 | + [DSIM_TYPE_IMX8MM] = &imx8mm_dsi_driver_data, | |
| 482 | + [DSIM_TYPE_IMX8MP] = &imx8mm_dsi_driver_data, | |
| 483 | +}; | |
| 484 | + | |
| 485 | +static inline struct samsung_dsim *host_to_dsi(struct mipi_dsi_host *h) | |
| 486 | +{ | |
| 487 | + return container_of(h, struct samsung_dsim, dsi_host); | |
| 488 | +} | |
| 489 | + | |
| 490 | +static inline struct samsung_dsim *bridge_to_dsi(struct drm_bridge *b) | |
| 491 | +{ | |
| 492 | + return container_of(b, struct samsung_dsim, bridge); | |
| 493 | +} | |
| 494 | + | |
| 495 | +static inline void samsung_dsim_write(struct samsung_dsim *dsi, | |
| 496 | + enum reg_idx idx, u32 val) | |
| 497 | +{ | |
| 498 | + writel(val, dsi->reg_base + dsi->driver_data->reg_ofs[idx]); | |
| 499 | +} | |
| 500 | + | |
| 501 | +static inline u32 samsung_dsim_read(struct samsung_dsim *dsi, enum reg_idx idx) | |
| 502 | +{ | |
| 503 | + return readl(dsi->reg_base + dsi->driver_data->reg_ofs[idx]); | |
| 504 | +} | |
| 505 | + | |
| 506 | +static void samsung_dsim_wait_for_reset(struct samsung_dsim *dsi) | |
| 507 | +{ | |
| 508 | + if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300))) | |
| 509 | + return; | |
| 510 | + | |
| 511 | + dev_err(dsi->dev, "timeout waiting for reset\n"); | |
| 512 | +} | |
| 513 | + | |
| 514 | +static void samsung_dsim_reset(struct samsung_dsim *dsi) | |
| 515 | +{ | |
| 516 | + u32 reset_val = dsi->driver_data->reg_values[RESET_TYPE]; | |
| 517 | + | |
| 518 | + reinit_completion(&dsi->completed); | |
| 519 | + samsung_dsim_write(dsi, DSIM_SWRST_REG, reset_val); | |
| 520 | +} | |
| 521 | + | |
| 522 | +#ifndef MHZ | |
| 523 | +#define MHZ (1000 * 1000) | |
| 524 | +#endif | |
| 525 | + | |
| 526 | +static unsigned long samsung_dsim_pll_find_pms(struct samsung_dsim *dsi, | |
| 527 | + unsigned long fin, | |
| 528 | + unsigned long fout, | |
| 529 | + u8 *p, u16 *m, u8 *s) | |
| 530 | +{ | |
| 531 | + const struct samsung_dsim_driver_data *driver_data = dsi->driver_data; | |
| 532 | + unsigned long best_freq = 0; | |
| 533 | + u32 min_delta = 0xffffffff; | |
| 534 | + u8 p_min, p_max; | |
| 535 | + u8 _p, best_p; | |
| 536 | + u16 _m, best_m; | |
| 537 | + u8 _s, best_s; | |
| 538 | + | |
| 539 | + p_min = DIV_ROUND_UP(fin, (12 * MHZ)); | |
| 540 | + p_max = fin / (6 * MHZ); | |
| 541 | + | |
| 542 | + for (_p = p_min; _p <= p_max; ++_p) { | |
| 543 | + for (_s = 0; _s <= 5; ++_s) { | |
| 544 | + u64 tmp; | |
| 545 | + u32 delta; | |
| 546 | + | |
| 547 | + tmp = (u64)fout * (_p << _s); | |
| 548 | + do_div(tmp, fin); | |
| 549 | + _m = tmp; | |
| 550 | + if (_m < 41 || _m > 125) | |
| 551 | + continue; | |
| 552 | + | |
| 553 | + tmp = (u64)_m * fin; | |
| 554 | + do_div(tmp, _p); | |
| 555 | + if (tmp < 500 * MHZ || | |
| 556 | + tmp > driver_data->max_freq * MHZ) | |
| 557 | + continue; | |
| 558 | + | |
| 559 | + tmp = (u64)_m * fin; | |
| 560 | + do_div(tmp, _p << _s); | |
| 561 | + | |
| 562 | + delta = abs(fout - tmp); | |
| 563 | + if (delta < min_delta) { | |
| 564 | + best_p = _p; | |
| 565 | + best_m = _m; | |
| 566 | + best_s = _s; | |
| 567 | + min_delta = delta; | |
| 568 | + best_freq = tmp; | |
| 569 | + } | |
| 570 | + } | |
| 571 | + } | |
| 572 | + | |
| 573 | + if (best_freq) { | |
| 574 | + *p = best_p; | |
| 575 | + *m = best_m; | |
| 576 | + *s = best_s; | |
| 577 | + } | |
| 578 | + | |
| 579 | + return best_freq; | |
| 580 | +} | |
| 581 | + | |
| 582 | +static unsigned long samsung_dsim_set_pll(struct samsung_dsim *dsi, | |
| 583 | + unsigned long freq) | |
| 584 | +{ | |
| 585 | + const struct samsung_dsim_driver_data *driver_data = dsi->driver_data; | |
| 586 | + unsigned long fin, fout; | |
| 587 | + int timeout; | |
| 588 | + u8 p, s; | |
| 589 | + u16 m; | |
| 590 | + u32 reg; | |
| 591 | + | |
| 592 | + fin = dsi->pll_clk_rate; | |
| 593 | + fout = samsung_dsim_pll_find_pms(dsi, fin, freq, &p, &m, &s); | |
| 594 | + if (!fout) { | |
| 595 | + dev_err(dsi->dev, | |
| 596 | + "failed to find PLL PMS for requested frequency\n"); | |
| 597 | + return 0; | |
| 598 | + } | |
| 599 | + dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s); | |
| 600 | + | |
| 601 | + writel(driver_data->reg_values[PLL_TIMER], | |
| 602 | + dsi->reg_base + driver_data->plltmr_reg); | |
| 603 | + | |
| 604 | + reg = DSIM_PLL_EN | DSIM_PLL_P(p, driver_data->pll_p_offset) | | |
| 605 | + DSIM_PLL_M(m) | DSIM_PLL_S(s); | |
| 606 | + | |
| 607 | + if (driver_data->has_freqband) { | |
| 608 | + static const unsigned long freq_bands[] = { | |
| 609 | + 100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ, | |
| 610 | + 270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ, | |
| 611 | + 510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ, | |
| 612 | + 770 * MHZ, 870 * MHZ, 950 * MHZ, | |
| 613 | + }; | |
| 614 | + int band; | |
| 615 | + | |
| 616 | + for (band = 0; band < ARRAY_SIZE(freq_bands); ++band) | |
| 617 | + if (fout < freq_bands[band]) | |
| 618 | + break; | |
| 619 | + | |
| 620 | + dev_dbg(dsi->dev, "band %d\n", band); | |
| 621 | + | |
| 622 | + reg |= DSIM_FREQ_BAND(band); | |
| 623 | + } | |
| 624 | + | |
| 625 | + samsung_dsim_write(dsi, DSIM_PLLCTRL_REG, reg); | |
| 626 | + | |
| 627 | + timeout = 1000; | |
| 628 | + do { | |
| 629 | + if (timeout-- == 0) { | |
| 630 | + dev_err(dsi->dev, "PLL failed to stabilize\n"); | |
| 631 | + return 0; | |
| 632 | + } | |
| 633 | + reg = samsung_dsim_read(dsi, DSIM_STATUS_REG); | |
| 634 | + } while ((reg & DSIM_PLL_STABLE) == 0); | |
| 635 | + | |
| 636 | + return fout; | |
| 637 | +} | |
| 638 | + | |
| 639 | +static int samsung_dsim_enable_clock(struct samsung_dsim *dsi) | |
| 640 | +{ | |
| 641 | + unsigned long hs_clk, byte_clk, esc_clk; | |
| 642 | + unsigned long esc_div; | |
| 643 | + u32 reg; | |
| 644 | + | |
| 645 | + hs_clk = samsung_dsim_set_pll(dsi, dsi->burst_clk_rate); | |
| 646 | + if (!hs_clk) { | |
| 647 | + dev_err(dsi->dev, "failed to configure DSI PLL\n"); | |
| 648 | + return -EFAULT; | |
| 649 | + } | |
| 650 | + | |
| 651 | + byte_clk = hs_clk / 8; | |
| 652 | + esc_div = DIV_ROUND_UP(byte_clk, dsi->esc_clk_rate); | |
| 653 | + esc_clk = byte_clk / esc_div; | |
| 654 | + | |
| 655 | + if (esc_clk > 20 * MHZ) { | |
| 656 | + ++esc_div; | |
| 657 | + esc_clk = byte_clk / esc_div; | |
| 658 | + } | |
| 659 | + | |
| 660 | + dev_dbg(dsi->dev, "hs_clk = %lu, byte_clk = %lu, esc_clk = %lu\n", | |
| 661 | + hs_clk, byte_clk, esc_clk); | |
| 662 | + | |
| 663 | + reg = samsung_dsim_read(dsi, DSIM_CLKCTRL_REG); | |
| 664 | + reg &= ~(DSIM_ESC_PRESCALER_MASK | DSIM_LANE_ESC_CLK_EN_CLK | |
| 665 | + | DSIM_LANE_ESC_CLK_EN_DATA_MASK | DSIM_PLL_BYPASS | |
| 666 | + | DSIM_BYTE_CLK_SRC_MASK); | |
| 667 | + reg |= DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN | |
| 668 | + | DSIM_ESC_PRESCALER(esc_div) | |
| 669 | + | DSIM_LANE_ESC_CLK_EN_CLK | |
| 670 | + | DSIM_LANE_ESC_CLK_EN_DATA(BIT(dsi->lanes) - 1) | |
| 671 | + | DSIM_BYTE_CLK_SRC(0) | |
| 672 | + | DSIM_TX_REQUEST_HSCLK; | |
| 673 | + samsung_dsim_write(dsi, DSIM_CLKCTRL_REG, reg); | |
| 674 | + | |
| 675 | + return 0; | |
| 676 | +} | |
| 677 | + | |
| 678 | +static void samsung_dsim_set_phy_ctrl(struct samsung_dsim *dsi) | |
| 679 | +{ | |
| 680 | + const struct samsung_dsim_driver_data *driver_data = dsi->driver_data; | |
| 681 | + const unsigned int *reg_values = driver_data->reg_values; | |
| 682 | + u32 reg; | |
| 683 | + | |
| 684 | + if (driver_data->has_freqband) | |
| 685 | + return; | |
| 686 | + | |
| 687 | + /* B D-PHY: D-PHY Master & Slave Analog Block control */ | |
| 688 | + reg = reg_values[PHYCTRL_ULPS_EXIT] | reg_values[PHYCTRL_VREG_LP] | | |
| 689 | + reg_values[PHYCTRL_SLEW_UP]; | |
| 690 | + samsung_dsim_write(dsi, DSIM_PHYCTRL_REG, reg); | |
| 691 | + | |
| 692 | + /* | |
| 693 | + * T LPX: Transmitted length of any Low-Power state period | |
| 694 | + * T HS-EXIT: Time that the transmitter drives LP-11 following a HS | |
| 695 | + * burst | |
| 696 | + */ | |
| 697 | + reg = reg_values[PHYTIMING_LPX] | reg_values[PHYTIMING_HS_EXIT]; | |
| 698 | + samsung_dsim_write(dsi, DSIM_PHYTIMING_REG, reg); | |
| 699 | + | |
| 700 | + /* | |
| 701 | + * T CLK-PREPARE: Time that the transmitter drives the Clock Lane LP-00 | |
| 702 | + * Line state immediately before the HS-0 Line state starting the | |
| 703 | + * HS transmission | |
| 704 | + * T CLK-ZERO: Time that the transmitter drives the HS-0 state prior to | |
| 705 | + * transmitting the Clock. | |
| 706 | + * T CLK_POST: Time that the transmitter continues to send HS clock | |
| 707 | + * after the last associated Data Lane has transitioned to LP Mode | |
| 708 | + * Interval is defined as the period from the end of T HS-TRAIL to | |
| 709 | + * the beginning of T CLK-TRAIL | |
| 710 | + * T CLK-TRAIL: Time that the transmitter drives the HS-0 state after | |
| 711 | + * the last payload clock bit of a HS transmission burst | |
| 712 | + */ | |
| 713 | + reg = reg_values[PHYTIMING_CLK_PREPARE] | | |
| 714 | + reg_values[PHYTIMING_CLK_ZERO] | | |
| 715 | + reg_values[PHYTIMING_CLK_POST] | | |
| 716 | + reg_values[PHYTIMING_CLK_TRAIL]; | |
| 717 | + | |
| 718 | + samsung_dsim_write(dsi, DSIM_PHYTIMING1_REG, reg); | |
| 719 | + | |
| 720 | + /* | |
| 721 | + * T HS-PREPARE: Time that the transmitter drives the Data Lane LP-00 | |
| 722 | + * Line state immediately before the HS-0 Line state starting the | |
| 723 | + * HS transmission | |
| 724 | + * T HS-ZERO: Time that the transmitter drives the HS-0 state prior to | |
| 725 | + * transmitting the Sync sequence. | |
| 726 | + * T HS-TRAIL: Time that the transmitter drives the flipped differential | |
| 727 | + * state after last payload data bit of a HS transmission burst | |
| 728 | + */ | |
| 729 | + reg = reg_values[PHYTIMING_HS_PREPARE] | reg_values[PHYTIMING_HS_ZERO] | | |
| 730 | + reg_values[PHYTIMING_HS_TRAIL]; | |
| 731 | + samsung_dsim_write(dsi, DSIM_PHYTIMING2_REG, reg); | |
| 732 | +} | |
| 733 | + | |
| 734 | +static void samsung_dsim_disable_clock(struct samsung_dsim *dsi) | |
| 735 | +{ | |
| 736 | + u32 reg; | |
| 737 | + | |
| 738 | + reg = samsung_dsim_read(dsi, DSIM_CLKCTRL_REG); | |
| 739 | + reg &= ~(DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK | |
| 740 | + | DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN); | |
| 741 | + samsung_dsim_write(dsi, DSIM_CLKCTRL_REG, reg); | |
| 742 | + | |
| 743 | + reg = samsung_dsim_read(dsi, DSIM_PLLCTRL_REG); | |
| 744 | + reg &= ~DSIM_PLL_EN; | |
| 745 | + samsung_dsim_write(dsi, DSIM_PLLCTRL_REG, reg); | |
| 746 | +} | |
| 747 | + | |
| 748 | +static void samsung_dsim_enable_lane(struct samsung_dsim *dsi, u32 lane) | |
| 749 | +{ | |
| 750 | + u32 reg = samsung_dsim_read(dsi, DSIM_CONFIG_REG); | |
| 751 | + | |
| 752 | + reg |= (DSIM_NUM_OF_DATA_LANE(dsi->lanes - 1) | DSIM_LANE_EN_CLK | | |
| 753 | + DSIM_LANE_EN(lane)); | |
| 754 | + samsung_dsim_write(dsi, DSIM_CONFIG_REG, reg); | |
| 755 | +} | |
| 756 | + | |
| 757 | +static int samsung_dsim_init_link(struct samsung_dsim *dsi) | |
| 758 | +{ | |
| 759 | + const struct samsung_dsim_driver_data *driver_data = dsi->driver_data; | |
| 760 | + int timeout; | |
| 761 | + u32 reg; | |
| 762 | + u32 lanes_mask; | |
| 763 | + | |
| 764 | + /* Initialize FIFO pointers */ | |
| 765 | + reg = samsung_dsim_read(dsi, DSIM_FIFOCTRL_REG); | |
| 766 | + reg &= ~0x1f; | |
| 767 | + samsung_dsim_write(dsi, DSIM_FIFOCTRL_REG, reg); | |
| 768 | + | |
| 769 | + usleep_range(9000, 11000); | |
| 770 | + | |
| 771 | + reg |= 0x1f; | |
| 772 | + samsung_dsim_write(dsi, DSIM_FIFOCTRL_REG, reg); | |
| 773 | + usleep_range(9000, 11000); | |
| 774 | + | |
| 775 | + /* DSI configuration */ | |
| 776 | + reg = 0; | |
| 777 | + | |
| 778 | + /* | |
| 779 | + * The first bit of mode_flags specifies display configuration. | |
| 780 | + * If this bit is set[= MIPI_DSI_MODE_VIDEO], dsi will support video | |
| 781 | + * mode, otherwise it will support command mode. | |
| 782 | + */ | |
| 783 | + if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { | |
| 784 | + reg |= DSIM_VIDEO_MODE; | |
| 785 | + | |
| 786 | + /* | |
| 787 | + * The user manual describes that following bits are ignored in | |
| 788 | + * command mode. | |
| 789 | + */ | |
| 790 | + if (!(dsi->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH)) | |
| 791 | + reg |= DSIM_MFLUSH_VS; | |
| 792 | + if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) | |
| 793 | + reg |= DSIM_SYNC_INFORM; | |
| 794 | + if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) | |
| 795 | + reg |= DSIM_BURST_MODE; | |
| 796 | + if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT) | |
| 797 | + reg |= DSIM_AUTO_MODE; | |
| 798 | + if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSE) | |
| 799 | + reg |= DSIM_HSE_DISABLE_MODE; | |
| 800 | + if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_NO_HFP) | |
| 801 | + reg |= DSIM_HFP_DISABLE_MODE; | |
| 802 | + if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_NO_HBP) | |
| 803 | + reg |= DSIM_HBP_DISABLE_MODE; | |
| 804 | + if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_NO_HSA) | |
| 805 | + reg |= DSIM_HSA_DISABLE_MODE; | |
| 806 | + } | |
| 807 | + | |
| 808 | + if (dsi->mode_flags & MIPI_DSI_MODE_NO_EOT_PACKET) | |
| 809 | + reg |= DSIM_EOT_DISABLE; | |
| 810 | + | |
| 811 | + switch (dsi->format) { | |
| 812 | + case MIPI_DSI_FMT_RGB888: | |
| 813 | + reg |= DSIM_MAIN_PIX_FORMAT_RGB888; | |
| 814 | + break; | |
| 815 | + case MIPI_DSI_FMT_RGB666: | |
| 816 | + reg |= DSIM_MAIN_PIX_FORMAT_RGB666; | |
| 817 | + break; | |
| 818 | + case MIPI_DSI_FMT_RGB666_PACKED: | |
| 819 | + reg |= DSIM_MAIN_PIX_FORMAT_RGB666_P; | |
| 820 | + break; | |
| 821 | + case MIPI_DSI_FMT_RGB565: | |
| 822 | + reg |= DSIM_MAIN_PIX_FORMAT_RGB565; | |
| 823 | + break; | |
| 824 | + default: | |
| 825 | + dev_err(dsi->dev, "invalid pixel format\n"); | |
| 826 | + return -EINVAL; | |
| 827 | + } | |
| 828 | + | |
| 829 | + /* | |
| 830 | + * Use non-continuous clock mode if the periparal wants and | |
| 831 | + * host controller supports | |
| 832 | + * | |
| 833 | + * In non-continous clock mode, host controller will turn off | |
| 834 | + * the HS clock between high-speed transmissions to reduce | |
| 835 | + * power consumption. | |
| 836 | + */ | |
| 837 | + if (driver_data->has_clklane_stop && | |
| 838 | + dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) | |
| 839 | + reg |= DSIM_CLKLANE_STOP; | |
| 840 | + samsung_dsim_write(dsi, DSIM_CONFIG_REG, reg); | |
| 841 | + | |
| 842 | + lanes_mask = BIT(dsi->lanes) - 1; | |
| 843 | + samsung_dsim_enable_lane(dsi, lanes_mask); | |
| 844 | + | |
| 845 | + /* Check clock and data lane state are stop state */ | |
| 846 | + timeout = 100; | |
| 847 | + do { | |
| 848 | + if (timeout-- == 0) { | |
| 849 | + dev_err(dsi->dev, "waiting for bus lanes timed out\n"); | |
| 850 | + return -EFAULT; | |
| 851 | + } | |
| 852 | + | |
| 853 | + reg = samsung_dsim_read(dsi, DSIM_STATUS_REG); | |
| 854 | + if ((reg & DSIM_STOP_STATE_DAT(lanes_mask)) | |
| 855 | + != DSIM_STOP_STATE_DAT(lanes_mask)) | |
| 856 | + continue; | |
| 857 | + } while (!(reg & (DSIM_STOP_STATE_CLK | DSIM_TX_READY_HS_CLK))); | |
| 858 | + | |
| 859 | + reg = samsung_dsim_read(dsi, DSIM_ESCMODE_REG); | |
| 860 | + reg &= ~DSIM_STOP_STATE_CNT_MASK; | |
| 861 | + reg |= DSIM_STOP_STATE_CNT(driver_data->reg_values[STOP_STATE_CNT]); | |
| 862 | + samsung_dsim_write(dsi, DSIM_ESCMODE_REG, reg); | |
| 863 | + | |
| 864 | + reg = DSIM_BTA_TIMEOUT(0xff) | DSIM_LPDR_TIMEOUT(0xffff); | |
| 865 | + samsung_dsim_write(dsi, DSIM_TIMEOUT_REG, reg); | |
| 866 | + | |
| 867 | + return 0; | |
| 868 | +} | |
| 869 | + | |
| 870 | +static void samsung_dsim_set_display_mode(struct samsung_dsim *dsi) | |
| 871 | +{ | |
| 872 | + struct drm_display_mode *m = &dsi->mode; | |
| 873 | + unsigned int num_bits_resol = dsi->driver_data->num_bits_resol; | |
| 874 | + u32 reg; | |
| 875 | + | |
| 876 | + if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { | |
| 877 | + reg = DSIM_CMD_ALLOW(0xf) | |
| 878 | + | DSIM_STABLE_VFP(m->vsync_start - m->vdisplay) | |
| 879 | + | DSIM_MAIN_VBP(m->vtotal - m->vsync_end); | |
| 880 | + samsung_dsim_write(dsi, DSIM_MVPORCH_REG, reg); | |
| 881 | + | |
| 882 | + reg = DSIM_MAIN_HFP(m->hsync_start - m->hdisplay) | |
| 883 | + | DSIM_MAIN_HBP(m->htotal - m->hsync_end); | |
| 884 | + samsung_dsim_write(dsi, DSIM_MHPORCH_REG, reg); | |
| 885 | + | |
| 886 | + reg = DSIM_MAIN_VSA(m->vsync_end - m->vsync_start) | |
| 887 | + | DSIM_MAIN_HSA(m->hsync_end - m->hsync_start); | |
| 888 | + samsung_dsim_write(dsi, DSIM_MSYNC_REG, reg); | |
| 889 | + } | |
| 890 | + reg = DSIM_MAIN_HRESOL(m->hdisplay, num_bits_resol) | | |
| 891 | + DSIM_MAIN_VRESOL(m->vdisplay, num_bits_resol); | |
| 892 | + | |
| 893 | + samsung_dsim_write(dsi, DSIM_MDRESOL_REG, reg); | |
| 894 | + | |
| 895 | + dev_dbg(dsi->dev, "LCD size = %dx%d\n", m->hdisplay, m->vdisplay); | |
| 896 | +} | |
| 897 | + | |
| 898 | +static void samsung_dsim_set_display_enable(struct samsung_dsim *dsi, bool enable) | |
| 899 | +{ | |
| 900 | + u32 reg; | |
| 901 | + | |
| 902 | + reg = samsung_dsim_read(dsi, DSIM_MDRESOL_REG); | |
| 903 | + if (enable) | |
| 904 | + reg |= DSIM_MAIN_STAND_BY; | |
| 905 | + else | |
| 906 | + reg &= ~DSIM_MAIN_STAND_BY; | |
| 907 | + samsung_dsim_write(dsi, DSIM_MDRESOL_REG, reg); | |
| 908 | +} | |
| 909 | + | |
| 910 | +static int samsung_dsim_wait_for_hdr_fifo(struct samsung_dsim *dsi) | |
| 911 | +{ | |
| 912 | + int timeout = 2000; | |
| 913 | + | |
| 914 | + do { | |
| 915 | + u32 reg = samsung_dsim_read(dsi, DSIM_FIFOCTRL_REG); | |
| 916 | + | |
| 917 | + if (!(reg & DSIM_SFR_HEADER_FULL)) | |
| 918 | + return 0; | |
| 919 | + | |
| 920 | + if (!cond_resched()) | |
| 921 | + usleep_range(950, 1050); | |
| 922 | + } while (--timeout); | |
| 923 | + | |
| 924 | + return -ETIMEDOUT; | |
| 925 | +} | |
| 926 | + | |
| 927 | +static void samsung_dsim_set_cmd_lpm(struct samsung_dsim *dsi, bool lpm) | |
| 928 | +{ | |
| 929 | + u32 v = samsung_dsim_read(dsi, DSIM_ESCMODE_REG); | |
| 930 | + | |
| 931 | + if (lpm) | |
| 932 | + v |= DSIM_CMD_LPDT_LP; | |
| 933 | + else | |
| 934 | + v &= ~DSIM_CMD_LPDT_LP; | |
| 935 | + | |
| 936 | + samsung_dsim_write(dsi, DSIM_ESCMODE_REG, v); | |
| 937 | +} | |
| 938 | + | |
| 939 | +static void samsung_dsim_force_bta(struct samsung_dsim *dsi) | |
| 940 | +{ | |
| 941 | + u32 v = samsung_dsim_read(dsi, DSIM_ESCMODE_REG); | |
| 942 | + | |
| 943 | + v |= DSIM_FORCE_BTA; | |
| 944 | + samsung_dsim_write(dsi, DSIM_ESCMODE_REG, v); | |
| 945 | +} | |
| 946 | + | |
| 947 | +static void samsung_dsim_send_to_fifo(struct samsung_dsim *dsi, | |
| 948 | + struct samsung_dsim_transfer *xfer) | |
| 949 | +{ | |
| 950 | + struct device *dev = dsi->dev; | |
| 951 | + struct mipi_dsi_packet *pkt = &xfer->packet; | |
| 952 | + const u8 *payload = pkt->payload + xfer->tx_done; | |
| 953 | + u16 length = pkt->payload_length - xfer->tx_done; | |
| 954 | + bool first = !xfer->tx_done; | |
| 955 | + u32 reg; | |
| 956 | + | |
| 957 | + dev_dbg(dev, "< xfer %pK: tx len %u, done %u, rx len %u, done %u\n", | |
| 958 | + xfer, length, xfer->tx_done, xfer->rx_len, xfer->rx_done); | |
| 959 | + | |
| 960 | + if (length > DSI_TX_FIFO_SIZE) | |
| 961 | + length = DSI_TX_FIFO_SIZE; | |
| 962 | + | |
| 963 | + xfer->tx_done += length; | |
| 964 | + | |
| 965 | + /* Send payload */ | |
| 966 | + while (length >= 4) { | |
| 967 | + reg = get_unaligned_le32(payload); | |
| 968 | + samsung_dsim_write(dsi, DSIM_PAYLOAD_REG, reg); | |
| 969 | + payload += 4; | |
| 970 | + length -= 4; | |
| 971 | + } | |
| 972 | + | |
| 973 | + reg = 0; | |
| 974 | + switch (length) { | |
| 975 | + case 3: | |
| 976 | + reg |= payload[2] << 16; | |
| 977 | + fallthrough; | |
| 978 | + case 2: | |
| 979 | + reg |= payload[1] << 8; | |
| 980 | + fallthrough; | |
| 981 | + case 1: | |
| 982 | + reg |= payload[0]; | |
| 983 | + samsung_dsim_write(dsi, DSIM_PAYLOAD_REG, reg); | |
| 984 | + break; | |
| 985 | + } | |
| 986 | + | |
| 987 | + /* Send packet header */ | |
| 988 | + if (!first) | |
| 989 | + return; | |
| 990 | + | |
| 991 | + reg = get_unaligned_le32(pkt->header); | |
| 992 | + if (samsung_dsim_wait_for_hdr_fifo(dsi)) { | |
| 993 | + dev_err(dev, "waiting for header FIFO timed out\n"); | |
| 994 | + return; | |
| 995 | + } | |
| 996 | + | |
| 997 | + if (NEQV(xfer->flags & MIPI_DSI_MSG_USE_LPM, | |
| 998 | + dsi->state & DSIM_STATE_CMD_LPM)) { | |
| 999 | + samsung_dsim_set_cmd_lpm(dsi, xfer->flags & MIPI_DSI_MSG_USE_LPM); | |
| 1000 | + dsi->state ^= DSIM_STATE_CMD_LPM; | |
| 1001 | + } | |
| 1002 | + | |
| 1003 | + samsung_dsim_write(dsi, DSIM_PKTHDR_REG, reg); | |
| 1004 | + | |
| 1005 | + if (xfer->flags & MIPI_DSI_MSG_REQ_ACK) | |
| 1006 | + samsung_dsim_force_bta(dsi); | |
| 1007 | +} | |
| 1008 | + | |
| 1009 | +static void samsung_dsim_read_from_fifo(struct samsung_dsim *dsi, | |
| 1010 | + struct samsung_dsim_transfer *xfer) | |
| 1011 | +{ | |
| 1012 | + u8 *payload = xfer->rx_payload + xfer->rx_done; | |
| 1013 | + bool first = !xfer->rx_done; | |
| 1014 | + struct device *dev = dsi->dev; | |
| 1015 | + u16 length; | |
| 1016 | + u32 reg; | |
| 1017 | + | |
| 1018 | + if (first) { | |
| 1019 | + reg = samsung_dsim_read(dsi, DSIM_RXFIFO_REG); | |
| 1020 | + | |
| 1021 | + switch (reg & 0x3f) { | |
| 1022 | + case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE: | |
| 1023 | + case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE: | |
| 1024 | + if (xfer->rx_len >= 2) { | |
| 1025 | + payload[1] = reg >> 16; | |
| 1026 | + ++xfer->rx_done; | |
| 1027 | + } | |
| 1028 | + fallthrough; | |
| 1029 | + case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE: | |
| 1030 | + case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE: | |
| 1031 | + payload[0] = reg >> 8; | |
| 1032 | + ++xfer->rx_done; | |
| 1033 | + xfer->rx_len = xfer->rx_done; | |
| 1034 | + xfer->result = 0; | |
| 1035 | + goto clear_fifo; | |
| 1036 | + case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT: | |
| 1037 | + dev_err(dev, "DSI Error Report: 0x%04x\n", (reg >> 8) & 0xffff); | |
| 1038 | + xfer->result = 0; | |
| 1039 | + goto clear_fifo; | |
| 1040 | + } | |
| 1041 | + | |
| 1042 | + length = (reg >> 8) & 0xffff; | |
| 1043 | + if (length > xfer->rx_len) { | |
| 1044 | + dev_err(dev, | |
| 1045 | + "response too long (%u > %u bytes), stripping\n", | |
| 1046 | + xfer->rx_len, length); | |
| 1047 | + length = xfer->rx_len; | |
| 1048 | + } else if (length < xfer->rx_len) { | |
| 1049 | + xfer->rx_len = length; | |
| 1050 | + } | |
| 1051 | + } | |
| 1052 | + | |
| 1053 | + length = xfer->rx_len - xfer->rx_done; | |
| 1054 | + xfer->rx_done += length; | |
| 1055 | + | |
| 1056 | + /* Receive payload */ | |
| 1057 | + while (length >= 4) { | |
| 1058 | + reg = samsung_dsim_read(dsi, DSIM_RXFIFO_REG); | |
| 1059 | + payload[0] = (reg >> 0) & 0xff; | |
| 1060 | + payload[1] = (reg >> 8) & 0xff; | |
| 1061 | + payload[2] = (reg >> 16) & 0xff; | |
| 1062 | + payload[3] = (reg >> 24) & 0xff; | |
| 1063 | + payload += 4; | |
| 1064 | + length -= 4; | |
| 1065 | + } | |
| 1066 | + | |
| 1067 | + if (length) { | |
| 1068 | + reg = samsung_dsim_read(dsi, DSIM_RXFIFO_REG); | |
| 1069 | + switch (length) { | |
| 1070 | + case 3: | |
| 1071 | + payload[2] = (reg >> 16) & 0xff; | |
| 1072 | + fallthrough; | |
| 1073 | + case 2: | |
| 1074 | + payload[1] = (reg >> 8) & 0xff; | |
| 1075 | + fallthrough; | |
| 1076 | + case 1: | |
| 1077 | + payload[0] = reg & 0xff; | |
| 1078 | + } | |
| 1079 | + } | |
| 1080 | + | |
| 1081 | + if (xfer->rx_done == xfer->rx_len) | |
| 1082 | + xfer->result = 0; | |
| 1083 | + | |
| 1084 | +clear_fifo: | |
| 1085 | + length = DSI_RX_FIFO_SIZE / 4; | |
| 1086 | + do { | |
| 1087 | + reg = samsung_dsim_read(dsi, DSIM_RXFIFO_REG); | |
| 1088 | + if (reg == DSI_RX_FIFO_EMPTY) | |
| 1089 | + break; | |
| 1090 | + } while (--length); | |
| 1091 | +} | |
| 1092 | + | |
| 1093 | +static void samsung_dsim_transfer_start(struct samsung_dsim *dsi) | |
| 1094 | +{ | |
| 1095 | + unsigned long flags; | |
| 1096 | + struct samsung_dsim_transfer *xfer; | |
| 1097 | + bool start = false; | |
| 1098 | + | |
| 1099 | +again: | |
| 1100 | + spin_lock_irqsave(&dsi->transfer_lock, flags); | |
| 1101 | + | |
| 1102 | + if (list_empty(&dsi->transfer_list)) { | |
| 1103 | + spin_unlock_irqrestore(&dsi->transfer_lock, flags); | |
| 1104 | + return; | |
| 1105 | + } | |
| 1106 | + | |
| 1107 | + xfer = list_first_entry(&dsi->transfer_list, | |
| 1108 | + struct samsung_dsim_transfer, list); | |
| 1109 | + | |
| 1110 | + spin_unlock_irqrestore(&dsi->transfer_lock, flags); | |
| 1111 | + | |
| 1112 | + if (xfer->packet.payload_length && | |
| 1113 | + xfer->tx_done == xfer->packet.payload_length) | |
| 1114 | + /* waiting for RX */ | |
| 1115 | + return; | |
| 1116 | + | |
| 1117 | + samsung_dsim_send_to_fifo(dsi, xfer); | |
| 1118 | + | |
| 1119 | + if (xfer->packet.payload_length || xfer->rx_len) | |
| 1120 | + return; | |
| 1121 | + | |
| 1122 | + xfer->result = 0; | |
| 1123 | + complete(&xfer->completed); | |
| 1124 | + | |
| 1125 | + spin_lock_irqsave(&dsi->transfer_lock, flags); | |
| 1126 | + | |
| 1127 | + list_del_init(&xfer->list); | |
| 1128 | + start = !list_empty(&dsi->transfer_list); | |
| 1129 | + | |
| 1130 | + spin_unlock_irqrestore(&dsi->transfer_lock, flags); | |
| 1131 | + | |
| 1132 | + if (start) | |
| 1133 | + goto again; | |
| 1134 | +} | |
| 1135 | + | |
| 1136 | +static bool samsung_dsim_transfer_finish(struct samsung_dsim *dsi) | |
| 1137 | +{ | |
| 1138 | + struct samsung_dsim_transfer *xfer; | |
| 1139 | + unsigned long flags; | |
| 1140 | + bool start = true; | |
| 1141 | + | |
| 1142 | + spin_lock_irqsave(&dsi->transfer_lock, flags); | |
| 1143 | + | |
| 1144 | + if (list_empty(&dsi->transfer_list)) { | |
| 1145 | + spin_unlock_irqrestore(&dsi->transfer_lock, flags); | |
| 1146 | + return false; | |
| 1147 | + } | |
| 1148 | + | |
| 1149 | + xfer = list_first_entry(&dsi->transfer_list, | |
| 1150 | + struct samsung_dsim_transfer, list); | |
| 1151 | + | |
| 1152 | + spin_unlock_irqrestore(&dsi->transfer_lock, flags); | |
| 1153 | + | |
| 1154 | + dev_dbg(dsi->dev, | |
| 1155 | + "> xfer %pK, tx_len %zu, tx_done %u, rx_len %u, rx_done %u\n", | |
| 1156 | + xfer, xfer->packet.payload_length, xfer->tx_done, xfer->rx_len, | |
| 1157 | + xfer->rx_done); | |
| 1158 | + | |
| 1159 | + if (xfer->tx_done != xfer->packet.payload_length) | |
| 1160 | + return true; | |
| 1161 | + | |
| 1162 | + if (xfer->rx_done != xfer->rx_len) | |
| 1163 | + samsung_dsim_read_from_fifo(dsi, xfer); | |
| 1164 | + | |
| 1165 | + if (xfer->rx_done != xfer->rx_len) | |
| 1166 | + return true; | |
| 1167 | + | |
| 1168 | + spin_lock_irqsave(&dsi->transfer_lock, flags); | |
| 1169 | + | |
| 1170 | + list_del_init(&xfer->list); | |
| 1171 | + start = !list_empty(&dsi->transfer_list); | |
| 1172 | + | |
| 1173 | + spin_unlock_irqrestore(&dsi->transfer_lock, flags); | |
| 1174 | + | |
| 1175 | + if (!xfer->rx_len) | |
| 1176 | + xfer->result = 0; | |
| 1177 | + complete(&xfer->completed); | |
| 1178 | + | |
| 1179 | + return start; | |
| 1180 | +} | |
| 1181 | + | |
| 1182 | +static void samsung_dsim_remove_transfer(struct samsung_dsim *dsi, | |
| 1183 | + struct samsung_dsim_transfer *xfer) | |
| 1184 | +{ | |
| 1185 | + unsigned long flags; | |
| 1186 | + bool start; | |
| 1187 | + | |
| 1188 | + spin_lock_irqsave(&dsi->transfer_lock, flags); | |
| 1189 | + | |
| 1190 | + if (!list_empty(&dsi->transfer_list) && | |
| 1191 | + xfer == list_first_entry(&dsi->transfer_list, | |
| 1192 | + struct samsung_dsim_transfer, list)) { | |
| 1193 | + list_del_init(&xfer->list); | |
| 1194 | + start = !list_empty(&dsi->transfer_list); | |
| 1195 | + spin_unlock_irqrestore(&dsi->transfer_lock, flags); | |
| 1196 | + if (start) | |
| 1197 | + samsung_dsim_transfer_start(dsi); | |
| 1198 | + return; | |
| 1199 | + } | |
| 1200 | + | |
| 1201 | + list_del_init(&xfer->list); | |
| 1202 | + | |
| 1203 | + spin_unlock_irqrestore(&dsi->transfer_lock, flags); | |
| 1204 | +} | |
| 1205 | + | |
| 1206 | +static int samsung_dsim_transfer(struct samsung_dsim *dsi, | |
| 1207 | + struct samsung_dsim_transfer *xfer) | |
| 1208 | +{ | |
| 1209 | + unsigned long flags; | |
| 1210 | + bool stopped; | |
| 1211 | + | |
| 1212 | + xfer->tx_done = 0; | |
| 1213 | + xfer->rx_done = 0; | |
| 1214 | + xfer->result = -ETIMEDOUT; | |
| 1215 | + init_completion(&xfer->completed); | |
| 1216 | + | |
| 1217 | + spin_lock_irqsave(&dsi->transfer_lock, flags); | |
| 1218 | + | |
| 1219 | + stopped = list_empty(&dsi->transfer_list); | |
| 1220 | + list_add_tail(&xfer->list, &dsi->transfer_list); | |
| 1221 | + | |
| 1222 | + spin_unlock_irqrestore(&dsi->transfer_lock, flags); | |
| 1223 | + | |
| 1224 | + if (stopped) | |
| 1225 | + samsung_dsim_transfer_start(dsi); | |
| 1226 | + | |
| 1227 | + wait_for_completion_timeout(&xfer->completed, | |
| 1228 | + msecs_to_jiffies(DSI_XFER_TIMEOUT_MS)); | |
| 1229 | + if (xfer->result == -ETIMEDOUT) { | |
| 1230 | + struct mipi_dsi_packet *pkt = &xfer->packet; | |
| 1231 | + | |
| 1232 | + samsung_dsim_remove_transfer(dsi, xfer); | |
| 1233 | + dev_err(dsi->dev, "xfer timed out: %*ph %*ph\n", 4, pkt->header, | |
| 1234 | + (int)pkt->payload_length, pkt->payload); | |
| 1235 | + return -ETIMEDOUT; | |
| 1236 | + } | |
| 1237 | + | |
| 1238 | + /* Also covers hardware timeout condition */ | |
| 1239 | + return xfer->result; | |
| 1240 | +} | |
| 1241 | + | |
| 1242 | +static irqreturn_t samsung_dsim_irq(int irq, void *dev_id) | |
| 1243 | +{ | |
| 1244 | + struct samsung_dsim *dsi = dev_id; | |
| 1245 | + u32 status; | |
| 1246 | + | |
| 1247 | + status = samsung_dsim_read(dsi, DSIM_INTSRC_REG); | |
| 1248 | + if (!status) { | |
| 1249 | + static unsigned long j; | |
| 1250 | + | |
| 1251 | + if (printk_timed_ratelimit(&j, 500)) | |
| 1252 | + dev_warn(dsi->dev, "spurious interrupt\n"); | |
| 1253 | + return IRQ_HANDLED; | |
| 1254 | + } | |
| 1255 | + samsung_dsim_write(dsi, DSIM_INTSRC_REG, status); | |
| 1256 | + | |
| 1257 | + if (status & DSIM_INT_SW_RST_RELEASE) { | |
| 1258 | + unsigned long mask = ~(DSIM_INT_RX_DONE | | |
| 1259 | + DSIM_INT_SFR_FIFO_EMPTY | | |
| 1260 | + DSIM_INT_SFR_HDR_FIFO_EMPTY | | |
| 1261 | + DSIM_INT_RX_ECC_ERR | | |
| 1262 | + DSIM_INT_SW_RST_RELEASE); | |
| 1263 | + samsung_dsim_write(dsi, DSIM_INTMSK_REG, mask); | |
| 1264 | + complete(&dsi->completed); | |
| 1265 | + return IRQ_HANDLED; | |
| 1266 | + } | |
| 1267 | + | |
| 1268 | + if (!(status & (DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY | | |
| 1269 | + DSIM_INT_PLL_STABLE))) | |
| 1270 | + return IRQ_HANDLED; | |
| 1271 | + | |
| 1272 | + if (samsung_dsim_transfer_finish(dsi)) | |
| 1273 | + samsung_dsim_transfer_start(dsi); | |
| 1274 | + | |
| 1275 | + return IRQ_HANDLED; | |
| 1276 | +} | |
| 1277 | + | |
| 1278 | +static void samsung_dsim_enable_irq(struct samsung_dsim *dsi) | |
| 1279 | +{ | |
| 1280 | + enable_irq(dsi->irq); | |
| 1281 | + | |
| 1282 | + if (dsi->te_gpio) | |
| 1283 | + enable_irq(gpiod_to_irq(dsi->te_gpio)); | |
| 1284 | +} | |
| 1285 | + | |
| 1286 | +static void samsung_dsim_disable_irq(struct samsung_dsim *dsi) | |
| 1287 | +{ | |
| 1288 | + if (dsi->te_gpio) | |
| 1289 | + disable_irq(gpiod_to_irq(dsi->te_gpio)); | |
| 1290 | + | |
| 1291 | + disable_irq(dsi->irq); | |
| 1292 | +} | |
| 1293 | + | |
| 1294 | +static int samsung_dsim_init(struct samsung_dsim *dsi) | |
| 1295 | +{ | |
| 1296 | + const struct samsung_dsim_driver_data *driver_data = dsi->driver_data; | |
| 1297 | + | |
| 1298 | + if (dsi->state & DSIM_STATE_INITIALIZED) | |
| 1299 | + return 0; | |
| 1300 | + | |
| 1301 | + samsung_dsim_reset(dsi); | |
| 1302 | + samsung_dsim_enable_irq(dsi); | |
| 1303 | + | |
| 1304 | + if (driver_data->reg_values[RESET_TYPE] == DSIM_FUNCRST) | |
| 1305 | + samsung_dsim_enable_lane(dsi, BIT(dsi->lanes) - 1); | |
| 1306 | + | |
| 1307 | + samsung_dsim_enable_clock(dsi); | |
| 1308 | + if (driver_data->wait_for_reset) | |
| 1309 | + samsung_dsim_wait_for_reset(dsi); | |
| 1310 | + samsung_dsim_set_phy_ctrl(dsi); | |
| 1311 | + samsung_dsim_init_link(dsi); | |
| 1312 | + | |
| 1313 | + dsi->state |= DSIM_STATE_INITIALIZED; | |
| 1314 | + | |
| 1315 | + return 0; | |
| 1316 | +} | |
| 1317 | + | |
| 1318 | +static void samsung_dsim_atomic_pre_enable(struct drm_bridge *bridge, | |
| 1319 | + struct drm_bridge_state *old_bridge_state) | |
| 1320 | +{ | |
| 1321 | + struct samsung_dsim *dsi = bridge_to_dsi(bridge); | |
| 1322 | + int ret; | |
| 1323 | + | |
| 1324 | + if (dsi->state & DSIM_STATE_ENABLED) | |
| 1325 | + return; | |
| 1326 | + | |
| 1327 | + ret = pm_runtime_resume_and_get(dsi->dev); | |
| 1328 | + if (ret < 0) { | |
| 1329 | + dev_err(dsi->dev, "failed to enable DSI device.\n"); | |
| 1330 | + return; | |
| 1331 | + } | |
| 1332 | + | |
| 1333 | + dsi->state |= DSIM_STATE_ENABLED; | |
| 1334 | + | |
| 1335 | + /* | |
| 1336 | + * For Exynos-DSIM the downstream bridge, or panel are expecting | |
| 1337 | + * the host initialization during DSI transfer. | |
| 1338 | + */ | |
| 1339 | + if (!samsung_dsim_hw_is_exynos(dsi->plat_data->hw_type)) { | |
| 1340 | + ret = samsung_dsim_init(dsi); | |
| 1341 | + if (ret) | |
| 1342 | + return; | |
| 1343 | + } | |
| 1344 | +} | |
| 1345 | + | |
| 1346 | +static void samsung_dsim_atomic_enable(struct drm_bridge *bridge, | |
| 1347 | + struct drm_bridge_state *old_bridge_state) | |
| 1348 | +{ | |
| 1349 | + struct samsung_dsim *dsi = bridge_to_dsi(bridge); | |
| 1350 | + | |
| 1351 | + samsung_dsim_set_display_mode(dsi); | |
| 1352 | + samsung_dsim_set_display_enable(dsi, true); | |
| 1353 | + | |
| 1354 | + dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE; | |
| 1355 | +} | |
| 1356 | + | |
| 1357 | +static void samsung_dsim_atomic_disable(struct drm_bridge *bridge, | |
| 1358 | + struct drm_bridge_state *old_bridge_state) | |
| 1359 | +{ | |
| 1360 | + struct samsung_dsim *dsi = bridge_to_dsi(bridge); | |
| 1361 | + | |
| 1362 | + if (!(dsi->state & DSIM_STATE_ENABLED)) | |
| 1363 | + return; | |
| 1364 | + | |
| 1365 | + dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE; | |
| 1366 | +} | |
| 1367 | + | |
| 1368 | +static void samsung_dsim_atomic_post_disable(struct drm_bridge *bridge, | |
| 1369 | + struct drm_bridge_state *old_bridge_state) | |
| 1370 | +{ | |
| 1371 | + struct samsung_dsim *dsi = bridge_to_dsi(bridge); | |
| 1372 | + | |
| 1373 | + samsung_dsim_set_display_enable(dsi, false); | |
| 1374 | + | |
| 1375 | + dsi->state &= ~DSIM_STATE_ENABLED; | |
| 1376 | + pm_runtime_put_sync(dsi->dev); | |
| 1377 | +} | |
| 1378 | + | |
| 1379 | +/* | |
| 1380 | + * This pixel output formats list referenced from, | |
| 1381 | + * AN13573 i.MX 8/RT MIPI DSI/CSI-2, Rev. 0, 21 March 2022 | |
| 1382 | + * 3.7.4 Pixel formats | |
| 1383 | + * Table 14. DSI pixel packing formats | |
| 1384 | + */ | |
| 1385 | +static const u32 samsung_dsim_pixel_output_fmts[] = { | |
| 1386 | + MEDIA_BUS_FMT_YUYV10_1X20, | |
| 1387 | + MEDIA_BUS_FMT_YUYV12_1X24, | |
| 1388 | + MEDIA_BUS_FMT_UYVY8_1X16, | |
| 1389 | + MEDIA_BUS_FMT_RGB101010_1X30, | |
| 1390 | + MEDIA_BUS_FMT_RGB121212_1X36, | |
| 1391 | + MEDIA_BUS_FMT_RGB565_1X16, | |
| 1392 | + MEDIA_BUS_FMT_RGB666_1X18, | |
| 1393 | + MEDIA_BUS_FMT_RGB888_1X24, | |
| 1394 | +}; | |
| 1395 | + | |
| 1396 | +static bool samsung_dsim_pixel_output_fmt_supported(u32 fmt) | |
| 1397 | +{ | |
| 1398 | + int i; | |
| 1399 | + | |
| 1400 | + if (fmt == MEDIA_BUS_FMT_FIXED) | |
| 1401 | + return false; | |
| 1402 | + | |
| 1403 | + for (i = 0; i < ARRAY_SIZE(samsung_dsim_pixel_output_fmts); i++) { | |
| 1404 | + if (samsung_dsim_pixel_output_fmts[i] == fmt) | |
| 1405 | + return true; | |
| 1406 | + } | |
| 1407 | + | |
| 1408 | + return false; | |
| 1409 | +} | |
| 1410 | + | |
| 1411 | +static u32 * | |
| 1412 | +samsung_dsim_atomic_get_input_bus_fmts(struct drm_bridge *bridge, | |
| 1413 | + struct drm_bridge_state *bridge_state, | |
| 1414 | + struct drm_crtc_state *crtc_state, | |
| 1415 | + struct drm_connector_state *conn_state, | |
| 1416 | + u32 output_fmt, | |
| 1417 | + unsigned int *num_input_fmts) | |
| 1418 | +{ | |
| 1419 | + u32 *input_fmts; | |
| 1420 | + | |
| 1421 | + input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL); | |
| 1422 | + if (!input_fmts) | |
| 1423 | + return NULL; | |
| 1424 | + | |
| 1425 | + if (!samsung_dsim_pixel_output_fmt_supported(output_fmt)) | |
| 1426 | + /* | |
| 1427 | + * Some bridge/display drivers are still not able to pass the | |
| 1428 | + * correct format, so handle those pipelines by falling back | |
| 1429 | + * to the default format till the supported formats finalized. | |
| 1430 | + */ | |
| 1431 | + output_fmt = MEDIA_BUS_FMT_RGB888_1X24; | |
| 1432 | + | |
| 1433 | + input_fmts[0] = output_fmt; | |
| 1434 | + *num_input_fmts = 1; | |
| 1435 | + | |
| 1436 | + return input_fmts; | |
| 1437 | +} | |
| 1438 | + | |
| 1439 | +static int samsung_dsim_atomic_check(struct drm_bridge *bridge, | |
| 1440 | + struct drm_bridge_state *bridge_state, | |
| 1441 | + struct drm_crtc_state *crtc_state, | |
| 1442 | + struct drm_connector_state *conn_state) | |
| 1443 | +{ | |
| 1444 | + struct samsung_dsim *dsi = bridge_to_dsi(bridge); | |
| 1445 | + struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; | |
| 1446 | + | |
| 1447 | + /* | |
| 1448 | + * The i.MX8M Mini/Nano glue logic between LCDIF and DSIM | |
| 1449 | + * inverts HS/VS/DE sync signals polarity, therefore, while | |
| 1450 | + * i.MX 8M Mini Applications Processor Reference Manual Rev. 3, 11/2020 | |
| 1451 | + * 13.6.3.5.2 RGB interface | |
| 1452 | + * i.MX 8M Nano Applications Processor Reference Manual Rev. 2, 07/2022 | |
| 1453 | + * 13.6.2.7.2 RGB interface | |
| 1454 | + * both claim "Vsync, Hsync, and VDEN are active high signals.", the | |
| 1455 | + * LCDIF must generate inverted HS/VS/DE signals, i.e. active LOW. | |
| 1456 | + * | |
| 1457 | + * The i.MX8M Plus glue logic between LCDIFv3 and DSIM does not | |
| 1458 | + * implement the same behavior, therefore LCDIFv3 must generate | |
| 1459 | + * HS/VS/DE signals active HIGH. | |
| 1460 | + */ | |
| 1461 | + if (dsi->plat_data->hw_type == DSIM_TYPE_IMX8MM) { | |
| 1462 | + adjusted_mode->flags |= (DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC); | |
| 1463 | + adjusted_mode->flags &= ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC); | |
| 1464 | + } else if (dsi->plat_data->hw_type == DSIM_TYPE_IMX8MP) { | |
| 1465 | + adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC); | |
| 1466 | + adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC); | |
| 1467 | + } | |
| 1468 | + | |
| 1469 | + return 0; | |
| 1470 | +} | |
| 1471 | + | |
| 1472 | +static void samsung_dsim_mode_set(struct drm_bridge *bridge, | |
| 1473 | + const struct drm_display_mode *mode, | |
| 1474 | + const struct drm_display_mode *adjusted_mode) | |
| 1475 | +{ | |
| 1476 | + struct samsung_dsim *dsi = bridge_to_dsi(bridge); | |
| 1477 | + | |
| 1478 | + drm_mode_copy(&dsi->mode, adjusted_mode); | |
| 1479 | +} | |
| 1480 | + | |
| 1481 | +static int samsung_dsim_attach(struct drm_bridge *bridge, | |
| 1482 | + enum drm_bridge_attach_flags flags) | |
| 1483 | +{ | |
| 1484 | + struct samsung_dsim *dsi = bridge_to_dsi(bridge); | |
| 1485 | + | |
| 1486 | + return drm_bridge_attach(bridge->encoder, dsi->out_bridge, bridge, | |
| 1487 | + flags); | |
| 1488 | +} | |
| 1489 | + | |
| 1490 | +static const struct drm_bridge_funcs samsung_dsim_bridge_funcs = { | |
| 1491 | + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, | |
| 1492 | + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, | |
| 1493 | + .atomic_reset = drm_atomic_helper_bridge_reset, | |
| 1494 | + .atomic_get_input_bus_fmts = samsung_dsim_atomic_get_input_bus_fmts, | |
| 1495 | + .atomic_check = samsung_dsim_atomic_check, | |
| 1496 | + .atomic_pre_enable = samsung_dsim_atomic_pre_enable, | |
| 1497 | + .atomic_enable = samsung_dsim_atomic_enable, | |
| 1498 | + .atomic_disable = samsung_dsim_atomic_disable, | |
| 1499 | + .atomic_post_disable = samsung_dsim_atomic_post_disable, | |
| 1500 | + .mode_set = samsung_dsim_mode_set, | |
| 1501 | + .attach = samsung_dsim_attach, | |
| 1502 | +}; | |
| 1503 | + | |
| 1504 | +static irqreturn_t samsung_dsim_te_irq_handler(int irq, void *dev_id) | |
| 1505 | +{ | |
| 1506 | + struct samsung_dsim *dsi = (struct samsung_dsim *)dev_id; | |
| 1507 | + const struct samsung_dsim_plat_data *pdata = dsi->plat_data; | |
| 1508 | + | |
| 1509 | + if (pdata->host_ops && pdata->host_ops->te_irq_handler) | |
| 1510 | + return pdata->host_ops->te_irq_handler(dsi); | |
| 1511 | + | |
| 1512 | + return IRQ_HANDLED; | |
| 1513 | +} | |
| 1514 | + | |
| 1515 | +static int samsung_dsim_register_te_irq(struct samsung_dsim *dsi, struct device *dev) | |
| 1516 | +{ | |
| 1517 | + int te_gpio_irq; | |
| 1518 | + int ret; | |
| 1519 | + | |
| 1520 | + dsi->te_gpio = devm_gpiod_get_optional(dev, "te", GPIOD_IN); | |
| 1521 | + if (!dsi->te_gpio) | |
| 1522 | + return 0; | |
| 1523 | + else if (IS_ERR(dsi->te_gpio)) | |
| 1524 | + return dev_err_probe(dev, PTR_ERR(dsi->te_gpio), "failed to get te GPIO\n"); | |
| 1525 | + | |
| 1526 | + te_gpio_irq = gpiod_to_irq(dsi->te_gpio); | |
| 1527 | + | |
| 1528 | + ret = request_threaded_irq(te_gpio_irq, samsung_dsim_te_irq_handler, NULL, | |
| 1529 | + IRQF_TRIGGER_RISING | IRQF_NO_AUTOEN, "TE", dsi); | |
| 1530 | + if (ret) { | |
| 1531 | + dev_err(dsi->dev, "request interrupt failed with %d\n", ret); | |
| 1532 | + gpiod_put(dsi->te_gpio); | |
| 1533 | + return ret; | |
| 1534 | + } | |
| 1535 | + | |
| 1536 | + return 0; | |
| 1537 | +} | |
| 1538 | + | |
| 1539 | +static int samsung_dsim_host_attach(struct mipi_dsi_host *host, | |
| 1540 | + struct mipi_dsi_device *device) | |
| 1541 | +{ | |
| 1542 | + struct samsung_dsim *dsi = host_to_dsi(host); | |
| 1543 | + const struct samsung_dsim_plat_data *pdata = dsi->plat_data; | |
| 1544 | + struct device *dev = dsi->dev; | |
| 1545 | + struct device_node *np = dev->of_node; | |
| 1546 | + struct device_node *remote; | |
| 1547 | + struct drm_panel *panel; | |
| 1548 | + int ret; | |
| 1549 | + | |
| 1550 | + /* | |
| 1551 | + * Devices can also be child nodes when we also control that device | |
| 1552 | + * through the upstream device (ie, MIPI-DCS for a MIPI-DSI device). | |
| 1553 | + * | |
| 1554 | + * Lookup for a child node of the given parent that isn't either port | |
| 1555 | + * or ports. | |
| 1556 | + */ | |
| 1557 | + for_each_available_child_of_node(np, remote) { | |
| 1558 | + if (of_node_name_eq(remote, "port") || | |
| 1559 | + of_node_name_eq(remote, "ports")) | |
| 1560 | + continue; | |
| 1561 | + | |
| 1562 | + goto of_find_panel_or_bridge; | |
| 1563 | + } | |
| 1564 | + | |
| 1565 | + /* | |
| 1566 | + * of_graph_get_remote_node() produces a noisy error message if port | |
| 1567 | + * node isn't found and the absence of the port is a legit case here, | |
| 1568 | + * so at first we silently check whether graph presents in the | |
| 1569 | + * device-tree node. | |
| 1570 | + */ | |
| 1571 | + if (!of_graph_is_present(np)) | |
| 1572 | + return -ENODEV; | |
| 1573 | + | |
| 1574 | + remote = of_graph_get_remote_node(np, 1, 0); | |
| 1575 | + | |
| 1576 | +of_find_panel_or_bridge: | |
| 1577 | + if (!remote) | |
| 1578 | + return -ENODEV; | |
| 1579 | + | |
| 1580 | + panel = of_drm_find_panel(remote); | |
| 1581 | + if (!IS_ERR(panel)) { | |
| 1582 | + dsi->out_bridge = devm_drm_panel_bridge_add(dev, panel); | |
| 1583 | + } else { | |
| 1584 | + dsi->out_bridge = of_drm_find_bridge(remote); | |
| 1585 | + if (!dsi->out_bridge) | |
| 1586 | + dsi->out_bridge = ERR_PTR(-EINVAL); | |
| 1587 | + } | |
| 1588 | + | |
| 1589 | + of_node_put(remote); | |
| 1590 | + | |
| 1591 | + if (IS_ERR(dsi->out_bridge)) { | |
| 1592 | + ret = PTR_ERR(dsi->out_bridge); | |
| 1593 | + DRM_DEV_ERROR(dev, "failed to find the bridge: %d\n", ret); | |
| 1594 | + return ret; | |
| 1595 | + } | |
| 1596 | + | |
| 1597 | + DRM_DEV_INFO(dev, "Attached %s device\n", device->name); | |
| 1598 | + | |
| 1599 | + drm_bridge_add(&dsi->bridge); | |
| 1600 | + | |
| 1601 | + /* | |
| 1602 | + * This is a temporary solution and should be made by more generic way. | |
| 1603 | + * | |
| 1604 | + * If attached panel device is for command mode one, dsi should register | |
| 1605 | + * TE interrupt handler. | |
| 1606 | + */ | |
| 1607 | + if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO)) { | |
| 1608 | + ret = samsung_dsim_register_te_irq(dsi, &device->dev); | |
| 1609 | + if (ret) | |
| 1610 | + return ret; | |
| 1611 | + } | |
| 1612 | + | |
| 1613 | + if (pdata->host_ops && pdata->host_ops->attach) { | |
| 1614 | + ret = pdata->host_ops->attach(dsi, device); | |
| 1615 | + if (ret) | |
| 1616 | + return ret; | |
| 1617 | + } | |
| 1618 | + | |
| 1619 | + dsi->lanes = device->lanes; | |
| 1620 | + dsi->format = device->format; | |
| 1621 | + dsi->mode_flags = device->mode_flags; | |
| 1622 | + | |
| 1623 | + return 0; | |
| 1624 | +} | |
| 1625 | + | |
| 1626 | +static void samsung_dsim_unregister_te_irq(struct samsung_dsim *dsi) | |
| 1627 | +{ | |
| 1628 | + if (dsi->te_gpio) { | |
| 1629 | + free_irq(gpiod_to_irq(dsi->te_gpio), dsi); | |
| 1630 | + gpiod_put(dsi->te_gpio); | |
| 1631 | + } | |
| 1632 | +} | |
| 1633 | + | |
| 1634 | +static int samsung_dsim_host_detach(struct mipi_dsi_host *host, | |
| 1635 | + struct mipi_dsi_device *device) | |
| 1636 | +{ | |
| 1637 | + struct samsung_dsim *dsi = host_to_dsi(host); | |
| 1638 | + const struct samsung_dsim_plat_data *pdata = dsi->plat_data; | |
| 1639 | + | |
| 1640 | + dsi->out_bridge = NULL; | |
| 1641 | + | |
| 1642 | + if (pdata->host_ops && pdata->host_ops->detach) | |
| 1643 | + pdata->host_ops->detach(dsi, device); | |
| 1644 | + | |
| 1645 | + samsung_dsim_unregister_te_irq(dsi); | |
| 1646 | + | |
| 1647 | + drm_bridge_remove(&dsi->bridge); | |
| 1648 | + | |
| 1649 | + return 0; | |
| 1650 | +} | |
| 1651 | + | |
| 1652 | +static ssize_t samsung_dsim_host_transfer(struct mipi_dsi_host *host, | |
| 1653 | + const struct mipi_dsi_msg *msg) | |
| 1654 | +{ | |
| 1655 | + struct samsung_dsim *dsi = host_to_dsi(host); | |
| 1656 | + struct samsung_dsim_transfer xfer; | |
| 1657 | + int ret; | |
| 1658 | + | |
| 1659 | + if (!(dsi->state & DSIM_STATE_ENABLED)) | |
| 1660 | + return -EINVAL; | |
| 1661 | + | |
| 1662 | + ret = samsung_dsim_init(dsi); | |
| 1663 | + if (ret) | |
| 1664 | + return ret; | |
| 1665 | + | |
| 1666 | + ret = mipi_dsi_create_packet(&xfer.packet, msg); | |
| 1667 | + if (ret < 0) | |
| 1668 | + return ret; | |
| 1669 | + | |
| 1670 | + xfer.rx_len = msg->rx_len; | |
| 1671 | + xfer.rx_payload = msg->rx_buf; | |
| 1672 | + xfer.flags = msg->flags; | |
| 1673 | + | |
| 1674 | + ret = samsung_dsim_transfer(dsi, &xfer); | |
| 1675 | + return (ret < 0) ? ret : xfer.rx_done; | |
| 1676 | +} | |
| 1677 | + | |
| 1678 | +static const struct mipi_dsi_host_ops samsung_dsim_ops = { | |
| 1679 | + .attach = samsung_dsim_host_attach, | |
| 1680 | + .detach = samsung_dsim_host_detach, | |
| 1681 | + .transfer = samsung_dsim_host_transfer, | |
| 1682 | +}; | |
| 1683 | + | |
| 1684 | +static int samsung_dsim_of_read_u32(const struct device_node *np, | |
| 1685 | + const char *propname, u32 *out_value) | |
| 1686 | +{ | |
| 1687 | + int ret = of_property_read_u32(np, propname, out_value); | |
| 1688 | + | |
| 1689 | + if (ret < 0) | |
| 1690 | + pr_err("%pOF: failed to get '%s' property\n", np, propname); | |
| 1691 | + | |
| 1692 | + return ret; | |
| 1693 | +} | |
| 1694 | + | |
| 1695 | +static int samsung_dsim_parse_dt(struct samsung_dsim *dsi) | |
| 1696 | +{ | |
| 1697 | + struct device *dev = dsi->dev; | |
| 1698 | + struct device_node *node = dev->of_node; | |
| 1699 | + int ret; | |
| 1700 | + | |
| 1701 | + ret = samsung_dsim_of_read_u32(node, "samsung,pll-clock-frequency", | |
| 1702 | + &dsi->pll_clk_rate); | |
| 1703 | + if (ret < 0) | |
| 1704 | + return ret; | |
| 1705 | + | |
| 1706 | + ret = samsung_dsim_of_read_u32(node, "samsung,burst-clock-frequency", | |
| 1707 | + &dsi->burst_clk_rate); | |
| 1708 | + if (ret < 0) | |
| 1709 | + return ret; | |
| 1710 | + | |
| 1711 | + ret = samsung_dsim_of_read_u32(node, "samsung,esc-clock-frequency", | |
| 1712 | + &dsi->esc_clk_rate); | |
| 1713 | + if (ret < 0) | |
| 1714 | + return ret; | |
| 1715 | + | |
| 1716 | + return 0; | |
| 1717 | +} | |
| 1718 | + | |
| 1719 | +static int generic_dsim_register_host(struct samsung_dsim *dsi) | |
| 1720 | +{ | |
| 1721 | + return mipi_dsi_host_register(&dsi->dsi_host); | |
| 1722 | +} | |
| 1723 | + | |
| 1724 | +static void generic_dsim_unregister_host(struct samsung_dsim *dsi) | |
| 1725 | +{ | |
| 1726 | + mipi_dsi_host_unregister(&dsi->dsi_host); | |
| 1727 | +} | |
| 1728 | + | |
| 1729 | +static const struct samsung_dsim_host_ops generic_dsim_host_ops = { | |
| 1730 | + .register_host = generic_dsim_register_host, | |
| 1731 | + .unregister_host = generic_dsim_unregister_host, | |
| 1732 | +}; | |
| 1733 | + | |
| 1734 | +static const struct drm_bridge_timings samsung_dsim_bridge_timings_de_high = { | |
| 1735 | + .input_bus_flags = DRM_BUS_FLAG_DE_HIGH, | |
| 1736 | +}; | |
| 1737 | + | |
| 1738 | +static const struct drm_bridge_timings samsung_dsim_bridge_timings_de_low = { | |
| 1739 | + .input_bus_flags = DRM_BUS_FLAG_DE_LOW, | |
| 1740 | +}; | |
| 1741 | + | |
| 1742 | +int samsung_dsim_probe(struct platform_device *pdev) | |
| 1743 | +{ | |
| 1744 | + struct device *dev = &pdev->dev; | |
| 1745 | + struct samsung_dsim *dsi; | |
| 1746 | + int ret, i; | |
| 1747 | + | |
| 1748 | + dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); | |
| 1749 | + if (!dsi) | |
| 1750 | + return -ENOMEM; | |
| 1751 | + | |
| 1752 | + init_completion(&dsi->completed); | |
| 1753 | + spin_lock_init(&dsi->transfer_lock); | |
| 1754 | + INIT_LIST_HEAD(&dsi->transfer_list); | |
| 1755 | + | |
| 1756 | + dsi->dsi_host.ops = &samsung_dsim_ops; | |
| 1757 | + dsi->dsi_host.dev = dev; | |
| 1758 | + | |
| 1759 | + dsi->dev = dev; | |
| 1760 | + dsi->plat_data = of_device_get_match_data(dev); | |
| 1761 | + dsi->driver_data = samsung_dsim_types[dsi->plat_data->hw_type]; | |
| 1762 | + | |
| 1763 | + dsi->supplies[0].supply = "vddcore"; | |
| 1764 | + dsi->supplies[1].supply = "vddio"; | |
| 1765 | + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(dsi->supplies), | |
| 1766 | + dsi->supplies); | |
| 1767 | + if (ret) | |
| 1768 | + return dev_err_probe(dev, ret, "failed to get regulators\n"); | |
| 1769 | + | |
| 1770 | + dsi->clks = devm_kcalloc(dev, dsi->driver_data->num_clks, | |
| 1771 | + sizeof(*dsi->clks), GFP_KERNEL); | |
| 1772 | + if (!dsi->clks) | |
| 1773 | + return -ENOMEM; | |
| 1774 | + | |
| 1775 | + for (i = 0; i < dsi->driver_data->num_clks; i++) { | |
| 1776 | + dsi->clks[i] = devm_clk_get(dev, clk_names[i]); | |
| 1777 | + if (IS_ERR(dsi->clks[i])) { | |
| 1778 | + if (strcmp(clk_names[i], "sclk_mipi") == 0) { | |
| 1779 | + dsi->clks[i] = devm_clk_get(dev, OLD_SCLK_MIPI_CLK_NAME); | |
| 1780 | + if (!IS_ERR(dsi->clks[i])) | |
| 1781 | + continue; | |
| 1782 | + } | |
| 1783 | + | |
| 1784 | + dev_info(dev, "failed to get the clock: %s\n", clk_names[i]); | |
| 1785 | + return PTR_ERR(dsi->clks[i]); | |
| 1786 | + } | |
| 1787 | + } | |
| 1788 | + | |
| 1789 | + dsi->reg_base = devm_platform_ioremap_resource(pdev, 0); | |
| 1790 | + if (IS_ERR(dsi->reg_base)) | |
| 1791 | + return PTR_ERR(dsi->reg_base); | |
| 1792 | + | |
| 1793 | + dsi->phy = devm_phy_optional_get(dev, "dsim"); | |
| 1794 | + if (IS_ERR(dsi->phy)) { | |
| 1795 | + dev_info(dev, "failed to get dsim phy\n"); | |
| 1796 | + return PTR_ERR(dsi->phy); | |
| 1797 | + } | |
| 1798 | + | |
| 1799 | + dsi->irq = platform_get_irq(pdev, 0); | |
| 1800 | + if (dsi->irq < 0) | |
| 1801 | + return dsi->irq; | |
| 1802 | + | |
| 1803 | + ret = devm_request_threaded_irq(dev, dsi->irq, NULL, | |
| 1804 | + samsung_dsim_irq, | |
| 1805 | + IRQF_ONESHOT | IRQF_NO_AUTOEN, | |
| 1806 | + dev_name(dev), dsi); | |
| 1807 | + if (ret) { | |
| 1808 | + dev_err(dev, "failed to request dsi irq\n"); | |
| 1809 | + return ret; | |
| 1810 | + } | |
| 1811 | + | |
| 1812 | + ret = samsung_dsim_parse_dt(dsi); | |
| 1813 | + if (ret) | |
| 1814 | + return ret; | |
| 1815 | + | |
| 1816 | + platform_set_drvdata(pdev, dsi); | |
| 1817 | + | |
| 1818 | + pm_runtime_enable(dev); | |
| 1819 | + | |
| 1820 | + dsi->bridge.funcs = &samsung_dsim_bridge_funcs; | |
| 1821 | + dsi->bridge.of_node = dev->of_node; | |
| 1822 | + dsi->bridge.type = DRM_MODE_CONNECTOR_DSI; | |
| 1823 | + | |
| 1824 | + /* DE_LOW: i.MX8M Mini/Nano LCDIF-DSIM glue logic inverts HS/VS/DE */ | |
| 1825 | + if (dsi->plat_data->hw_type == DSIM_TYPE_IMX8MM) | |
| 1826 | + dsi->bridge.timings = &samsung_dsim_bridge_timings_de_low; | |
| 1827 | + else | |
| 1828 | + dsi->bridge.timings = &samsung_dsim_bridge_timings_de_high; | |
| 1829 | + | |
| 1830 | + if (dsi->plat_data->host_ops && dsi->plat_data->host_ops->register_host) | |
| 1831 | + ret = dsi->plat_data->host_ops->register_host(dsi); | |
| 1832 | + | |
| 1833 | + if (ret) | |
| 1834 | + goto err_disable_runtime; | |
| 1835 | + | |
| 1836 | + return 0; | |
| 1837 | + | |
| 1838 | +err_disable_runtime: | |
| 1839 | + pm_runtime_disable(dev); | |
| 1840 | + | |
| 1841 | + return ret; | |
| 1842 | +} | |
| 1843 | +EXPORT_SYMBOL_GPL(samsung_dsim_probe); | |
| 1844 | + | |
| 1845 | +int samsung_dsim_remove(struct platform_device *pdev) | |
| 1846 | +{ | |
| 1847 | + struct samsung_dsim *dsi = platform_get_drvdata(pdev); | |
| 1848 | + | |
| 1849 | + pm_runtime_disable(&pdev->dev); | |
| 1850 | + | |
| 1851 | + if (dsi->plat_data->host_ops && dsi->plat_data->host_ops->unregister_host) | |
| 1852 | + dsi->plat_data->host_ops->unregister_host(dsi); | |
| 1853 | + | |
| 1854 | + return 0; | |
| 1855 | +} | |
| 1856 | +EXPORT_SYMBOL_GPL(samsung_dsim_remove); | |
| 1857 | + | |
| 1858 | +static int __maybe_unused samsung_dsim_suspend(struct device *dev) | |
| 1859 | +{ | |
| 1860 | + struct samsung_dsim *dsi = dev_get_drvdata(dev); | |
| 1861 | + const struct samsung_dsim_driver_data *driver_data = dsi->driver_data; | |
| 1862 | + int ret, i; | |
| 1863 | + | |
| 1864 | + usleep_range(10000, 20000); | |
| 1865 | + | |
| 1866 | + if (dsi->state & DSIM_STATE_INITIALIZED) { | |
| 1867 | + dsi->state &= ~DSIM_STATE_INITIALIZED; | |
| 1868 | + | |
| 1869 | + samsung_dsim_disable_clock(dsi); | |
| 1870 | + | |
| 1871 | + samsung_dsim_disable_irq(dsi); | |
| 1872 | + } | |
| 1873 | + | |
| 1874 | + dsi->state &= ~DSIM_STATE_CMD_LPM; | |
| 1875 | + | |
| 1876 | + phy_power_off(dsi->phy); | |
| 1877 | + | |
| 1878 | + for (i = driver_data->num_clks - 1; i > -1; i--) | |
| 1879 | + clk_disable_unprepare(dsi->clks[i]); | |
| 1880 | + | |
| 1881 | + ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies); | |
| 1882 | + if (ret < 0) | |
| 1883 | + dev_err(dsi->dev, "cannot disable regulators %d\n", ret); | |
| 1884 | + | |
| 1885 | + return 0; | |
| 1886 | +} | |
| 1887 | + | |
| 1888 | +static int __maybe_unused samsung_dsim_resume(struct device *dev) | |
| 1889 | +{ | |
| 1890 | + struct samsung_dsim *dsi = dev_get_drvdata(dev); | |
| 1891 | + const struct samsung_dsim_driver_data *driver_data = dsi->driver_data; | |
| 1892 | + int ret, i; | |
| 1893 | + | |
| 1894 | + ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies); | |
| 1895 | + if (ret < 0) { | |
| 1896 | + dev_err(dsi->dev, "cannot enable regulators %d\n", ret); | |
| 1897 | + return ret; | |
| 1898 | + } | |
| 1899 | + | |
| 1900 | + for (i = 0; i < driver_data->num_clks; i++) { | |
| 1901 | + ret = clk_prepare_enable(dsi->clks[i]); | |
| 1902 | + if (ret < 0) | |
| 1903 | + goto err_clk; | |
| 1904 | + } | |
| 1905 | + | |
| 1906 | + ret = phy_power_on(dsi->phy); | |
| 1907 | + if (ret < 0) { | |
| 1908 | + dev_err(dsi->dev, "cannot enable phy %d\n", ret); | |
| 1909 | + goto err_clk; | |
| 1910 | + } | |
| 1911 | + | |
| 1912 | + return 0; | |
| 1913 | + | |
| 1914 | +err_clk: | |
| 1915 | + while (--i > -1) | |
| 1916 | + clk_disable_unprepare(dsi->clks[i]); | |
| 1917 | + regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies); | |
| 1918 | + | |
| 1919 | + return ret; | |
| 1920 | +} | |
| 1921 | + | |
| 1922 | +const struct dev_pm_ops samsung_dsim_pm_ops = { | |
| 1923 | + SET_RUNTIME_PM_OPS(samsung_dsim_suspend, samsung_dsim_resume, NULL) | |
| 1924 | + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, | |
| 1925 | + pm_runtime_force_resume) | |
| 1926 | +}; | |
| 1927 | +EXPORT_SYMBOL_GPL(samsung_dsim_pm_ops); | |
| 1928 | + | |
| 1929 | +static const struct samsung_dsim_plat_data samsung_dsim_imx8mm_pdata = { | |
| 1930 | + .hw_type = DSIM_TYPE_IMX8MM, | |
| 1931 | + .host_ops = &generic_dsim_host_ops, | |
| 1932 | +}; | |
| 1933 | + | |
| 1934 | +static const struct samsung_dsim_plat_data samsung_dsim_imx8mp_pdata = { | |
| 1935 | + .hw_type = DSIM_TYPE_IMX8MP, | |
| 1936 | + .host_ops = &generic_dsim_host_ops, | |
| 1937 | +}; | |
| 1938 | + | |
| 1939 | +static const struct of_device_id samsung_dsim_of_match[] = { | |
| 1940 | + { | |
| 1941 | + .compatible = "fsl,imx8mm-mipi-dsim", | |
| 1942 | + .data = &samsung_dsim_imx8mm_pdata, | |
| 1943 | + }, | |
| 1944 | + { | |
| 1945 | + .compatible = "fsl,imx8mp-mipi-dsim", | |
| 1946 | + .data = &samsung_dsim_imx8mp_pdata, | |
| 1947 | + }, | |
| 1948 | + { /* sentinel. */ } | |
| 1949 | +}; | |
| 1950 | +MODULE_DEVICE_TABLE(of, samsung_dsim_of_match); | |
| 1951 | + | |
| 1952 | +static struct platform_driver samsung_dsim_driver = { | |
| 1953 | + .probe = samsung_dsim_probe, | |
| 1954 | + .remove = samsung_dsim_remove, | |
| 1955 | + .driver = { | |
| 1956 | + .name = "samsung-dsim", | |
| 1957 | + .owner = THIS_MODULE, | |
| 1958 | + .pm = &samsung_dsim_pm_ops, | |
| 1959 | + .of_match_table = samsung_dsim_of_match, | |
| 1960 | + }, | |
| 1961 | +}; | |
| 1962 | + | |
| 1963 | +module_platform_driver(samsung_dsim_driver); | |
| 1964 | + | |
| 1965 | +MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>"); | |
| 1966 | +MODULE_DESCRIPTION("Samsung MIPI DSIM controller bridge"); | |
| 1967 | +MODULE_LICENSE("GPL"); | |
| @@ -59,6 +59,7 @@ config DRM_EXYNOS_DSI · 58 unmodified lines | ||
| 59 | 59 | depends on DRM_EXYNOS_FIMD || DRM_EXYNOS5433_DECON || DRM_EXYNOS7_DECON |
| 60 | 60 | select DRM_MIPI_DSI |
| 61 | 61 | select DRM_PANEL |
| 62 | + select DRM_SAMSUNG_DSIM | |
| 62 | 63 | default n |
| 63 | 64 | help |
| 64 | 65 | This enables support for Exynos MIPI-DSI device. |
| @@ -1,1521 +1,56 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
| 2 | 2 | /* |
| 3 | − * Samsung SoC MIPI DSI Master driver. | |
| 3 | + * Samsung MIPI DSIM glue for Exynos SoCs. | |
| 4 | 4 | * |
| 5 | 5 | * Copyright (c) 2014 Samsung Electronics Co., Ltd |
| 6 | 6 | * |
| 7 | 7 | * Contacts: Tomasz Figa <t.figa@samsung.com> |
| 8 | −*/ | |
| 8 | + */ | |
| 9 | 9 | |
| 10 | −#include <linux/clk.h> | |
| 11 | −#include <linux/delay.h> | |
| 12 | 10 | #include <linux/component.h> |
| 13 | −#include <linux/gpio/consumer.h> | |
| 14 | −#include <linux/irq.h> | |
| 15 | 11 | #include <linux/of_device.h> |
| 16 | −#include <linux/of_graph.h> | |
| 17 | −#include <linux/phy/phy.h> | |
| 18 | −#include <linux/regulator/consumer.h> | |
| 19 | − | |
| 20 | −#include <asm/unaligned.h> | |
| 21 | 12 | |
| 22 | −#include <video/mipi_display.h> | |
| 23 | −#include <video/videomode.h> | |
| 24 | − | |
| 25 | −#include <drm/drm_atomic_helper.h> | |
| 26 | −#include <drm/drm_bridge.h> | |
| 27 | −#include <drm/drm_mipi_dsi.h> | |
| 28 | −#include <drm/drm_panel.h> | |
| 29 | −#include <drm/drm_print.h> | |
| 13 | +#include <drm/bridge/samsung-dsim.h> | |
| 30 | 14 | #include <drm/drm_probe_helper.h> |
| 31 | 15 | #include <drm/drm_simple_kms_helper.h> |
| 32 | 16 | |
| 33 | 17 | #include "exynos_drm_crtc.h" |
| 34 | 18 | #include "exynos_drm_drv.h" |
| 35 | 19 | |
| 36 | −/* returns true iff both arguments logically differs */ | |
| 37 | −#define NEQV(a, b) (!(a) ^ !(b)) | |
| 38 | − | |
| 39 | −/* DSIM_STATUS */ | |
| 40 | −#define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0) | |
| 41 | −#define DSIM_STOP_STATE_CLK (1 << 8) | |
| 42 | −#define DSIM_TX_READY_HS_CLK (1 << 10) | |
| 43 | −#define DSIM_PLL_STABLE (1 << 31) | |
| 44 | − | |
| 45 | −/* DSIM_SWRST */ | |
| 46 | −#define DSIM_FUNCRST (1 << 16) | |
| 47 | −#define DSIM_SWRST (1 << 0) | |
| 48 | − | |
| 49 | −/* DSIM_TIMEOUT */ | |
| 50 | −#define DSIM_LPDR_TIMEOUT(x) ((x) << 0) | |
| 51 | −#define DSIM_BTA_TIMEOUT(x) ((x) << 16) | |
| 52 | − | |
| 53 | −/* DSIM_CLKCTRL */ | |
| 54 | −#define DSIM_ESC_PRESCALER(x) (((x) & 0xffff) << 0) | |
| 55 | −#define DSIM_ESC_PRESCALER_MASK (0xffff << 0) | |
| 56 | −#define DSIM_LANE_ESC_CLK_EN_CLK (1 << 19) | |
| 57 | −#define DSIM_LANE_ESC_CLK_EN_DATA(x) (((x) & 0xf) << 20) | |
| 58 | −#define DSIM_LANE_ESC_CLK_EN_DATA_MASK (0xf << 20) | |
| 59 | −#define DSIM_BYTE_CLKEN (1 << 24) | |
| 60 | −#define DSIM_BYTE_CLK_SRC(x) (((x) & 0x3) << 25) | |
| 61 | −#define DSIM_BYTE_CLK_SRC_MASK (0x3 << 25) | |
| 62 | −#define DSIM_PLL_BYPASS (1 << 27) | |
| 63 | −#define DSIM_ESC_CLKEN (1 << 28) | |
| 64 | −#define DSIM_TX_REQUEST_HSCLK (1 << 31) | |
| 65 | − | |
| 66 | −/* DSIM_CONFIG */ | |
| 67 | −#define DSIM_LANE_EN_CLK (1 << 0) | |
| 68 | −#define DSIM_LANE_EN(x) (((x) & 0xf) << 1) | |
| 69 | −#define DSIM_NUM_OF_DATA_LANE(x) (((x) & 0x3) << 5) | |
| 70 | −#define DSIM_SUB_PIX_FORMAT(x) (((x) & 0x7) << 8) | |
| 71 | −#define DSIM_MAIN_PIX_FORMAT_MASK (0x7 << 12) | |
| 72 | −#define DSIM_MAIN_PIX_FORMAT_RGB888 (0x7 << 12) | |
| 73 | −#define DSIM_MAIN_PIX_FORMAT_RGB666 (0x6 << 12) | |
| 74 | −#define DSIM_MAIN_PIX_FORMAT_RGB666_P (0x5 << 12) | |
| 75 | −#define DSIM_MAIN_PIX_FORMAT_RGB565 (0x4 << 12) | |
| 76 | −#define DSIM_SUB_VC (((x) & 0x3) << 16) | |
| 77 | −#define DSIM_MAIN_VC (((x) & 0x3) << 18) | |
| 78 | −#define DSIM_HSA_DISABLE_MODE (1 << 20) | |
| 79 | −#define DSIM_HBP_DISABLE_MODE (1 << 21) | |
| 80 | −#define DSIM_HFP_DISABLE_MODE (1 << 22) | |
| 81 | −/* | |
| 82 | − * The i.MX 8M Mini Applications Processor Reference Manual, | |
| 83 | − * Rev. 3, 11/2020 Page 4091 | |
| 84 | − * The i.MX 8M Nano Applications Processor Reference Manual, | |
| 85 | − * Rev. 2, 07/2022 Page 3058 | |
| 86 | − * The i.MX 8M Plus Applications Processor Reference Manual, | |
| 87 | − * Rev. 1, 06/2021 Page 5436 | |
| 88 | − * named this bit as 'HseDisableMode' but the bit definition | |
| 89 | − * is quite opposite like | |
| 90 | − * 0 = Disables transfer | |
| 91 | − * 1 = Enables transfer | |
| 92 | − * which clearly states that HSE is not a disable bit. | |
| 93 | − * | |
| 94 | − * This bit is named as per the manual even though it is not | |
| 95 | − * a disable bit however the driver logic for handling HSE | |
| 96 | − * is based on the MIPI_DSI_MODE_VIDEO_HSE flag itself. | |
| 97 | − */ | |
| 98 | −#define DSIM_HSE_DISABLE_MODE (1 << 23) | |
| 99 | −#define DSIM_AUTO_MODE (1 << 24) | |
| 100 | −#define DSIM_VIDEO_MODE (1 << 25) | |
| 101 | −#define DSIM_BURST_MODE (1 << 26) | |
| 102 | −#define DSIM_SYNC_INFORM (1 << 27) | |
| 103 | −#define DSIM_EOT_DISABLE (1 << 28) | |
| 104 | −#define DSIM_MFLUSH_VS (1 << 29) | |
| 105 | −/* This flag is valid only for exynos3250/3472/5260/5430 */ | |
| 106 | −#define DSIM_CLKLANE_STOP (1 << 30) | |
| 107 | − | |
| 108 | −/* DSIM_ESCMODE */ | |
| 109 | −#define DSIM_TX_TRIGGER_RST (1 << 4) | |
| 110 | −#define DSIM_TX_LPDT_LP (1 << 6) | |
| 111 | −#define DSIM_CMD_LPDT_LP (1 << 7) | |
| 112 | −#define DSIM_FORCE_BTA (1 << 16) | |
| 113 | −#define DSIM_FORCE_STOP_STATE (1 << 20) | |
| 114 | −#define DSIM_STOP_STATE_CNT(x) (((x) & 0x7ff) << 21) | |
| 115 | −#define DSIM_STOP_STATE_CNT_MASK (0x7ff << 21) | |
| 116 | − | |
| 117 | −/* DSIM_MDRESOL */ | |
| 118 | −#define DSIM_MAIN_STAND_BY (1 << 31) | |
| 119 | −#define DSIM_MAIN_VRESOL(x, num_bits) (((x) & ((1 << (num_bits)) - 1)) << 16) | |
| 120 | −#define DSIM_MAIN_HRESOL(x, num_bits) (((x) & ((1 << (num_bits)) - 1)) << 0) | |
| 121 | − | |
| 122 | −/* DSIM_MVPORCH */ | |
| 123 | −#define DSIM_CMD_ALLOW(x) ((x) << 28) | |
| 124 | −#define DSIM_STABLE_VFP(x) ((x) << 16) | |
| 125 | −#define DSIM_MAIN_VBP(x) ((x) << 0) | |
| 126 | −#define DSIM_CMD_ALLOW_MASK (0xf << 28) | |
| 127 | −#define DSIM_STABLE_VFP_MASK (0x7ff << 16) | |
| 128 | −#define DSIM_MAIN_VBP_MASK (0x7ff << 0) | |
| 129 | − | |
| 130 | −/* DSIM_MHPORCH */ | |
| 131 | −#define DSIM_MAIN_HFP(x) ((x) << 16) | |
| 132 | −#define DSIM_MAIN_HBP(x) ((x) << 0) | |
| 133 | −#define DSIM_MAIN_HFP_MASK ((0xffff) << 16) | |
| 134 | −#define DSIM_MAIN_HBP_MASK ((0xffff) << 0) | |
| 135 | − | |
| 136 | −/* DSIM_MSYNC */ | |
| 137 | −#define DSIM_MAIN_VSA(x) ((x) << 22) | |
| 138 | −#define DSIM_MAIN_HSA(x) ((x) << 0) | |
| 139 | −#define DSIM_MAIN_VSA_MASK ((0x3ff) << 22) | |
| 140 | −#define DSIM_MAIN_HSA_MASK ((0xffff) << 0) | |
| 141 | − | |
| 142 | −/* DSIM_SDRESOL */ | |
| 143 | −#define DSIM_SUB_STANDY(x) ((x) << 31) | |
| 144 | −#define DSIM_SUB_VRESOL(x) ((x) << 16) | |
| 145 | −#define DSIM_SUB_HRESOL(x) ((x) << 0) | |
| 146 | −#define DSIM_SUB_STANDY_MASK ((0x1) << 31) | |
| 147 | −#define DSIM_SUB_VRESOL_MASK ((0x7ff) << 16) | |
| 148 | −#define DSIM_SUB_HRESOL_MASK ((0x7ff) << 0) | |
| 149 | − | |
| 150 | −/* DSIM_INTSRC */ | |
| 151 | −#define DSIM_INT_PLL_STABLE (1 << 31) | |
| 152 | −#define DSIM_INT_SW_RST_RELEASE (1 << 30) | |
| 153 | −#define DSIM_INT_SFR_FIFO_EMPTY (1 << 29) | |
| 154 | −#define DSIM_INT_SFR_HDR_FIFO_EMPTY (1 << 28) | |
| 155 | −#define DSIM_INT_BTA (1 << 25) | |
| 156 | −#define DSIM_INT_FRAME_DONE (1 << 24) | |
| 157 | −#define DSIM_INT_RX_TIMEOUT (1 << 21) | |
| 158 | −#define DSIM_INT_BTA_TIMEOUT (1 << 20) | |
| 159 | −#define DSIM_INT_RX_DONE (1 << 18) | |
| 160 | −#define DSIM_INT_RX_TE (1 << 17) | |
| 161 | −#define DSIM_INT_RX_ACK (1 << 16) | |
| 162 | −#define DSIM_INT_RX_ECC_ERR (1 << 15) | |
| 163 | −#define DSIM_INT_RX_CRC_ERR (1 << 14) | |
| 164 | − | |
| 165 | −/* DSIM_FIFOCTRL */ | |
| 166 | −#define DSIM_RX_DATA_FULL (1 << 25) | |
| 167 | −#define DSIM_RX_DATA_EMPTY (1 << 24) | |
| 168 | −#define DSIM_SFR_HEADER_FULL (1 << 23) | |
| 169 | −#define DSIM_SFR_HEADER_EMPTY (1 << 22) | |
| 170 | −#define DSIM_SFR_PAYLOAD_FULL (1 << 21) | |
| 171 | −#define DSIM_SFR_PAYLOAD_EMPTY (1 << 20) | |
| 172 | −#define DSIM_I80_HEADER_FULL (1 << 19) | |
| 173 | −#define DSIM_I80_HEADER_EMPTY (1 << 18) | |
| 174 | −#define DSIM_I80_PAYLOAD_FULL (1 << 17) | |
| 175 | −#define DSIM_I80_PAYLOAD_EMPTY (1 << 16) | |
| 176 | −#define DSIM_SD_HEADER_FULL (1 << 15) | |
| 177 | −#define DSIM_SD_HEADER_EMPTY (1 << 14) | |
| 178 | −#define DSIM_SD_PAYLOAD_FULL (1 << 13) | |
| 179 | −#define DSIM_SD_PAYLOAD_EMPTY (1 << 12) | |
| 180 | −#define DSIM_MD_HEADER_FULL (1 << 11) | |
| 181 | −#define DSIM_MD_HEADER_EMPTY (1 << 10) | |
| 182 | −#define DSIM_MD_PAYLOAD_FULL (1 << 9) | |
| 183 | −#define DSIM_MD_PAYLOAD_EMPTY (1 << 8) | |
| 184 | −#define DSIM_RX_FIFO (1 << 4) | |
| 185 | −#define DSIM_SFR_FIFO (1 << 3) | |
| 186 | −#define DSIM_I80_FIFO (1 << 2) | |
| 187 | −#define DSIM_SD_FIFO (1 << 1) | |
| 188 | −#define DSIM_MD_FIFO (1 << 0) | |
| 189 | − | |
| 190 | −/* DSIM_PHYACCHR */ | |
| 191 | −#define DSIM_AFC_EN (1 << 14) | |
| 192 | −#define DSIM_AFC_CTL(x) (((x) & 0x7) << 5) | |
| 193 | − | |
| 194 | −/* DSIM_PLLCTRL */ | |
| 195 | −#define DSIM_FREQ_BAND(x) ((x) << 24) | |
| 196 | −#define DSIM_PLL_EN (1 << 23) | |
| 197 | −#define DSIM_PLL_P(x) ((x) << 13) | |
| 198 | −#define DSIM_PLL_M(x) ((x) << 4) | |
| 199 | −#define DSIM_PLL_S(x) ((x) << 1) | |
| 200 | − | |
| 201 | −/* DSIM_PHYCTRL */ | |
| 202 | −#define DSIM_PHYCTRL_ULPS_EXIT(x) (((x) & 0x1ff) << 0) | |
| 203 | −#define DSIM_PHYCTRL_B_DPHYCTL_VREG_LP (1 << 30) | |
| 204 | −#define DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP (1 << 14) | |
| 205 | − | |
| 206 | −/* DSIM_PHYTIMING */ | |
| 207 | −#define DSIM_PHYTIMING_LPX(x) ((x) << 8) | |
| 208 | −#define DSIM_PHYTIMING_HS_EXIT(x) ((x) << 0) | |
| 209 | − | |
| 210 | −/* DSIM_PHYTIMING1 */ | |
| 211 | −#define DSIM_PHYTIMING1_CLK_PREPARE(x) ((x) << 24) | |
| 212 | −#define DSIM_PHYTIMING1_CLK_ZERO(x) ((x) << 16) | |
| 213 | −#define DSIM_PHYTIMING1_CLK_POST(x) ((x) << 8) | |
| 214 | −#define DSIM_PHYTIMING1_CLK_TRAIL(x) ((x) << 0) | |
| 215 | − | |
| 216 | −/* DSIM_PHYTIMING2 */ | |
| 217 | −#define DSIM_PHYTIMING2_HS_PREPARE(x) ((x) << 16) | |
| 218 | −#define DSIM_PHYTIMING2_HS_ZERO(x) ((x) << 8) | |
| 219 | −#define DSIM_PHYTIMING2_HS_TRAIL(x) ((x) << 0) | |
| 220 | − | |
| 221 | −#define DSI_MAX_BUS_WIDTH 4 | |
| 222 | −#define DSI_NUM_VIRTUAL_CHANNELS 4 | |
| 223 | −#define DSI_TX_FIFO_SIZE 2048 | |
| 224 | −#define DSI_RX_FIFO_SIZE 256 | |
| 225 | −#define DSI_XFER_TIMEOUT_MS 100 | |
| 226 | −#define DSI_RX_FIFO_EMPTY 0x30800002 | |
| 227 | − | |
| 228 | −#define OLD_SCLK_MIPI_CLK_NAME "pll_clk" | |
| 229 | − | |
| 230 | −static const char *const clk_names[5] = { "bus_clk", "sclk_mipi", | |
| 231 | − "phyclk_mipidphy0_bitclkdiv8", "phyclk_mipidphy0_rxclkesc0", | |
| 232 | − "sclk_rgb_vclk_to_dsim0" }; | |
| 233 | − | |
| 234 | −enum exynos_dsi_transfer_type { | |
| 235 | − EXYNOS_DSI_TX, | |
| 236 | − EXYNOS_DSI_RX, | |
| 237 | −}; | |
| 238 | − | |
| 239 | −struct exynos_dsi_transfer { | |
| 240 | − struct list_head list; | |
| 241 | − struct completion completed; | |
| 242 | − int result; | |
| 243 | − struct mipi_dsi_packet packet; | |
| 244 | − u16 flags; | |
| 245 | − u16 tx_done; | |
| 246 | − | |
| 247 | − u8 *rx_payload; | |
| 248 | − u16 rx_len; | |
| 249 | − u16 rx_done; | |
| 250 | −}; | |
| 251 | − | |
| 252 | −#define DSIM_STATE_ENABLED BIT(0) | |
| 253 | −#define DSIM_STATE_INITIALIZED BIT(1) | |
| 254 | −#define DSIM_STATE_CMD_LPM BIT(2) | |
| 255 | −#define DSIM_STATE_VIDOUT_AVAILABLE BIT(3) | |
| 256 | − | |
| 257 | −struct exynos_dsi_driver_data { | |
| 258 | − const unsigned int *reg_ofs; | |
| 259 | − unsigned int plltmr_reg; | |
| 260 | − unsigned int has_freqband:1; | |
| 261 | − unsigned int has_clklane_stop:1; | |
| 262 | − unsigned int num_clks; | |
| 263 | − unsigned int max_freq; | |
| 264 | − unsigned int wait_for_reset; | |
| 265 | − unsigned int num_bits_resol; | |
| 266 | − const unsigned int *reg_values; | |
| 267 | −}; | |
| 268 | − | |
| 269 | 20 | struct exynos_dsi { |
| 270 | 21 | struct drm_encoder encoder; |
| 271 | − struct mipi_dsi_host dsi_host; | |
| 272 | − struct drm_bridge bridge; | |
| 273 | − struct drm_bridge *out_bridge; | |
| 274 | − struct device *dev; | |
| 275 | − struct drm_display_mode mode; | |
| 276 | − | |
| 277 | − void __iomem *reg_base; | |
| 278 | − struct phy *phy; | |
| 279 | − struct clk **clks; | |
| 280 | − struct regulator_bulk_data supplies[2]; | |
| 281 | − int irq; | |
| 282 | − struct gpio_desc *te_gpio; | |
| 283 | − | |
| 284 | − u32 pll_clk_rate; | |
| 285 | − u32 burst_clk_rate; | |
| 286 | − u32 esc_clk_rate; | |
| 287 | − u32 lanes; | |
| 288 | − u32 mode_flags; | |
| 289 | − u32 format; | |
| 290 | − | |
| 291 | − int state; | |
| 292 | − struct drm_property *brightness; | |
| 293 | − struct completion completed; | |
| 294 | − | |
| 295 | − spinlock_t transfer_lock; /* protects transfer_list */ | |
| 296 | − struct list_head transfer_list; | |
| 297 | − | |
| 298 | − const struct exynos_dsi_driver_data *driver_data; | |
| 299 | −}; | |
| 300 | − | |
| 301 | −#define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host) | |
| 302 | − | |
| 303 | −static inline struct exynos_dsi *bridge_to_dsi(struct drm_bridge *b) | |
| 304 | −{ | |
| 305 | − return container_of(b, struct exynos_dsi, bridge); | |
| 306 | −} | |
| 307 | − | |
| 308 | −enum reg_idx { | |
| 309 | − DSIM_STATUS_REG, /* Status register */ | |
| 310 | − DSIM_SWRST_REG, /* Software reset register */ | |
| 311 | − DSIM_CLKCTRL_REG, /* Clock control register */ | |
| 312 | − DSIM_TIMEOUT_REG, /* Time out register */ | |
| 313 | − DSIM_CONFIG_REG, /* Configuration register */ | |
| 314 | − DSIM_ESCMODE_REG, /* Escape mode register */ | |
| 315 | − DSIM_MDRESOL_REG, | |
| 316 | − DSIM_MVPORCH_REG, /* Main display Vporch register */ | |
| 317 | − DSIM_MHPORCH_REG, /* Main display Hporch register */ | |
| 318 | − DSIM_MSYNC_REG, /* Main display sync area register */ | |
| 319 | − DSIM_INTSRC_REG, /* Interrupt source register */ | |
| 320 | − DSIM_INTMSK_REG, /* Interrupt mask register */ | |
| 321 | − DSIM_PKTHDR_REG, /* Packet Header FIFO register */ | |
| 322 | − DSIM_PAYLOAD_REG, /* Payload FIFO register */ | |
| 323 | − DSIM_RXFIFO_REG, /* Read FIFO register */ | |
| 324 | − DSIM_FIFOCTRL_REG, /* FIFO status and control register */ | |
| 325 | − DSIM_PLLCTRL_REG, /* PLL control register */ | |
| 326 | − DSIM_PHYCTRL_REG, | |
| 327 | − DSIM_PHYTIMING_REG, | |
| 328 | − DSIM_PHYTIMING1_REG, | |
| 329 | − DSIM_PHYTIMING2_REG, | |
| 330 | − NUM_REGS | |
| 331 | −}; | |
| 332 | − | |
| 333 | −static inline void exynos_dsi_write(struct exynos_dsi *dsi, enum reg_idx idx, | |
| 334 | − u32 val) | |
| 335 | −{ | |
| 336 | − | |
| 337 | − writel(val, dsi->reg_base + dsi->driver_data->reg_ofs[idx]); | |
| 338 | −} | |
| 339 | − | |
| 340 | −static inline u32 exynos_dsi_read(struct exynos_dsi *dsi, enum reg_idx idx) | |
| 341 | −{ | |
| 342 | − return readl(dsi->reg_base + dsi->driver_data->reg_ofs[idx]); | |
| 343 | −} | |
| 344 | − | |
| 345 | −static const unsigned int exynos_reg_ofs[] = { | |
| 346 | − [DSIM_STATUS_REG] = 0x00, | |
| 347 | − [DSIM_SWRST_REG] = 0x04, | |
| 348 | − [DSIM_CLKCTRL_REG] = 0x08, | |
| 349 | − [DSIM_TIMEOUT_REG] = 0x0c, | |
| 350 | − [DSIM_CONFIG_REG] = 0x10, | |
| 351 | − [DSIM_ESCMODE_REG] = 0x14, | |
| 352 | − [DSIM_MDRESOL_REG] = 0x18, | |
| 353 | − [DSIM_MVPORCH_REG] = 0x1c, | |
| 354 | − [DSIM_MHPORCH_REG] = 0x20, | |
| 355 | − [DSIM_MSYNC_REG] = 0x24, | |
| 356 | − [DSIM_INTSRC_REG] = 0x2c, | |
| 357 | − [DSIM_INTMSK_REG] = 0x30, | |
| 358 | − [DSIM_PKTHDR_REG] = 0x34, | |
| 359 | − [DSIM_PAYLOAD_REG] = 0x38, | |
| 360 | − [DSIM_RXFIFO_REG] = 0x3c, | |
| 361 | − [DSIM_FIFOCTRL_REG] = 0x44, | |
| 362 | − [DSIM_PLLCTRL_REG] = 0x4c, | |
| 363 | − [DSIM_PHYCTRL_REG] = 0x5c, | |
| 364 | − [DSIM_PHYTIMING_REG] = 0x64, | |
| 365 | − [DSIM_PHYTIMING1_REG] = 0x68, | |
| 366 | − [DSIM_PHYTIMING2_REG] = 0x6c, | |
| 367 | −}; | |
| 368 | − | |
| 369 | −static const unsigned int exynos5433_reg_ofs[] = { | |
| 370 | − [DSIM_STATUS_REG] = 0x04, | |
| 371 | − [DSIM_SWRST_REG] = 0x0C, | |
| 372 | − [DSIM_CLKCTRL_REG] = 0x10, | |
| 373 | − [DSIM_TIMEOUT_REG] = 0x14, | |
| 374 | − [DSIM_CONFIG_REG] = 0x18, | |
| 375 | − [DSIM_ESCMODE_REG] = 0x1C, | |
| 376 | − [DSIM_MDRESOL_REG] = 0x20, | |
| 377 | − [DSIM_MVPORCH_REG] = 0x24, | |
| 378 | − [DSIM_MHPORCH_REG] = 0x28, | |
| 379 | − [DSIM_MSYNC_REG] = 0x2C, | |
| 380 | − [DSIM_INTSRC_REG] = 0x34, | |
| 381 | − [DSIM_INTMSK_REG] = 0x38, | |
| 382 | − [DSIM_PKTHDR_REG] = 0x3C, | |
| 383 | − [DSIM_PAYLOAD_REG] = 0x40, | |
| 384 | − [DSIM_RXFIFO_REG] = 0x44, | |
| 385 | − [DSIM_FIFOCTRL_REG] = 0x4C, | |
| 386 | − [DSIM_PLLCTRL_REG] = 0x94, | |
| 387 | − [DSIM_PHYCTRL_REG] = 0xA4, | |
| 388 | − [DSIM_PHYTIMING_REG] = 0xB4, | |
| 389 | − [DSIM_PHYTIMING1_REG] = 0xB8, | |
| 390 | − [DSIM_PHYTIMING2_REG] = 0xBC, | |
| 391 | −}; | |
| 392 | − | |
| 393 | −enum reg_value_idx { | |
| 394 | − RESET_TYPE, | |
| 395 | − PLL_TIMER, | |
| 396 | − STOP_STATE_CNT, | |
| 397 | − PHYCTRL_ULPS_EXIT, | |
| 398 | − PHYCTRL_VREG_LP, | |
| 399 | − PHYCTRL_SLEW_UP, | |
| 400 | − PHYTIMING_LPX, | |
| 401 | − PHYTIMING_HS_EXIT, | |
| 402 | − PHYTIMING_CLK_PREPARE, | |
| 403 | − PHYTIMING_CLK_ZERO, | |
| 404 | − PHYTIMING_CLK_POST, | |
| 405 | − PHYTIMING_CLK_TRAIL, | |
| 406 | − PHYTIMING_HS_PREPARE, | |
| 407 | − PHYTIMING_HS_ZERO, | |
| 408 | − PHYTIMING_HS_TRAIL | |
| 409 | −}; | |
| 410 | − | |
| 411 | −static const unsigned int reg_values[] = { | |
| 412 | − [RESET_TYPE] = DSIM_SWRST, | |
| 413 | − [PLL_TIMER] = 500, | |
| 414 | − [STOP_STATE_CNT] = 0xf, | |
| 415 | − [PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0x0af), | |
| 416 | − [PHYCTRL_VREG_LP] = 0, | |
| 417 | − [PHYCTRL_SLEW_UP] = 0, | |
| 418 | − [PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x06), | |
| 419 | − [PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0b), | |
| 420 | − [PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x07), | |
| 421 | − [PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x27), | |
| 422 | − [PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0d), | |
| 423 | − [PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x08), | |
| 424 | − [PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x09), | |
| 425 | − [PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x0d), | |
| 426 | − [PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0b), | |
| 427 | −}; | |
| 428 | − | |
| 429 | −static const unsigned int exynos5422_reg_values[] = { | |
| 430 | − [RESET_TYPE] = DSIM_SWRST, | |
| 431 | − [PLL_TIMER] = 500, | |
| 432 | − [STOP_STATE_CNT] = 0xf, | |
| 433 | − [PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0xaf), | |
| 434 | − [PHYCTRL_VREG_LP] = 0, | |
| 435 | − [PHYCTRL_SLEW_UP] = 0, | |
| 436 | − [PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x08), | |
| 437 | − [PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0d), | |
| 438 | − [PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x09), | |
| 439 | − [PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x30), | |
| 440 | − [PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0e), | |
| 441 | − [PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x0a), | |
| 442 | − [PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x0c), | |
| 443 | − [PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x11), | |
| 444 | − [PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0d), | |
| 445 | −}; | |
| 446 | − | |
| 447 | −static const unsigned int exynos5433_reg_values[] = { | |
| 448 | − [RESET_TYPE] = DSIM_FUNCRST, | |
| 449 | − [PLL_TIMER] = 22200, | |
| 450 | − [STOP_STATE_CNT] = 0xa, | |
| 451 | − [PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0x190), | |
| 452 | − [PHYCTRL_VREG_LP] = DSIM_PHYCTRL_B_DPHYCTL_VREG_LP, | |
| 453 | − [PHYCTRL_SLEW_UP] = DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP, | |
| 454 | − [PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x07), | |
| 455 | − [PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0c), | |
| 456 | − [PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x09), | |
| 457 | − [PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x2d), | |
| 458 | − [PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0e), | |
| 459 | − [PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x09), | |
| 460 | − [PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x0b), | |
| 461 | − [PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x10), | |
| 462 | − [PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0c), | |
| 463 | −}; | |
| 464 | − | |
| 465 | −static const struct exynos_dsi_driver_data exynos3_dsi_driver_data = { | |
| 466 | − .reg_ofs = exynos_reg_ofs, | |
| 467 | − .plltmr_reg = 0x50, | |
| 468 | − .has_freqband = 1, | |
| 469 | − .has_clklane_stop = 1, | |
| 470 | − .num_clks = 2, | |
| 471 | − .max_freq = 1000, | |
| 472 | − .wait_for_reset = 1, | |
| 473 | − .num_bits_resol = 11, | |
| 474 | − .reg_values = reg_values, | |
| 475 | 22 | }; |
| 476 | 23 | |
| 477 | −static const struct exynos_dsi_driver_data exynos4_dsi_driver_data = { | |
| 478 | − .reg_ofs = exynos_reg_ofs, | |
| 479 | − .plltmr_reg = 0x50, | |
| 480 | − .has_freqband = 1, | |
| 481 | − .has_clklane_stop = 1, | |
| 482 | − .num_clks = 2, | |
| 483 | − .max_freq = 1000, | |
| 484 | − .wait_for_reset = 1, | |
| 485 | − .num_bits_resol = 11, | |
| 486 | − .reg_values = reg_values, | |
| 487 | −}; | |
| 488 | − | |
| 489 | −static const struct exynos_dsi_driver_data exynos5_dsi_driver_data = { | |
| 490 | − .reg_ofs = exynos_reg_ofs, | |
| 491 | − .plltmr_reg = 0x58, | |
| 492 | − .num_clks = 2, | |
| 493 | − .max_freq = 1000, | |
| 494 | − .wait_for_reset = 1, | |
| 495 | − .num_bits_resol = 11, | |
| 496 | − .reg_values = reg_values, | |
| 497 | −}; | |
| 498 | − | |
| 499 | −static const struct exynos_dsi_driver_data exynos5433_dsi_driver_data = { | |
| 500 | − .reg_ofs = exynos5433_reg_ofs, | |
| 501 | − .plltmr_reg = 0xa0, | |
| 502 | − .has_clklane_stop = 1, | |
| 503 | − .num_clks = 5, | |
| 504 | − .max_freq = 1500, | |
| 505 | − .wait_for_reset = 0, | |
| 506 | − .num_bits_resol = 12, | |
| 507 | − .reg_values = exynos5433_reg_values, | |
| 508 | −}; | |
| 509 | − | |
| 510 | −static const struct exynos_dsi_driver_data exynos5422_dsi_driver_data = { | |
| 511 | − .reg_ofs = exynos5433_reg_ofs, | |
| 512 | − .plltmr_reg = 0xa0, | |
| 513 | − .has_clklane_stop = 1, | |
| 514 | − .num_clks = 2, | |
| 515 | − .max_freq = 1500, | |
| 516 | − .wait_for_reset = 1, | |
| 517 | − .num_bits_resol = 12, | |
| 518 | − .reg_values = exynos5422_reg_values, | |
| 519 | −}; | |
| 520 | − | |
| 521 | −static const struct of_device_id exynos_dsi_of_match[] = { | |
| 522 | − { .compatible = "samsung,exynos3250-mipi-dsi", | |
| 523 | − .data = &exynos3_dsi_driver_data }, | |
| 524 | − { .compatible = "samsung,exynos4210-mipi-dsi", | |
| 525 | − .data = &exynos4_dsi_driver_data }, | |
| 526 | − { .compatible = "samsung,exynos5410-mipi-dsi", | |
| 527 | − .data = &exynos5_dsi_driver_data }, | |
| 528 | − { .compatible = "samsung,exynos5422-mipi-dsi", | |
| 529 | − .data = &exynos5422_dsi_driver_data }, | |
| 530 | − { .compatible = "samsung,exynos5433-mipi-dsi", | |
| 531 | − .data = &exynos5433_dsi_driver_data }, | |
| 532 | − { } | |
| 533 | −}; | |
| 534 | − | |
| 535 | −static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi) | |
| 536 | −{ | |
| 537 | − if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300))) | |
| 538 | − return; | |
| 539 | − | |
| 540 | − dev_err(dsi->dev, "timeout waiting for reset\n"); | |
| 541 | −} | |
| 542 | − | |
| 543 | −static void exynos_dsi_reset(struct exynos_dsi *dsi) | |
| 544 | −{ | |
| 545 | − u32 reset_val = dsi->driver_data->reg_values[RESET_TYPE]; | |
| 546 | − | |
| 547 | − reinit_completion(&dsi->completed); | |
| 548 | − exynos_dsi_write(dsi, DSIM_SWRST_REG, reset_val); | |
| 549 | −} | |
| 550 | − | |
| 551 | −#ifndef MHZ | |
| 552 | −#define MHZ (1000*1000) | |
| 553 | −#endif | |
| 554 | − | |
| 555 | −static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi, | |
| 556 | − unsigned long fin, unsigned long fout, u8 *p, u16 *m, u8 *s) | |
| 557 | −{ | |
| 558 | − const struct exynos_dsi_driver_data *driver_data = dsi->driver_data; | |
| 559 | − unsigned long best_freq = 0; | |
| 560 | − u32 min_delta = 0xffffffff; | |
| 561 | − u8 p_min, p_max; | |
| 562 | − u8 _p, best_p; | |
| 563 | − u16 _m, best_m; | |
| 564 | − u8 _s, best_s; | |
| 565 | − | |
| 566 | − p_min = DIV_ROUND_UP(fin, (12 * MHZ)); | |
| 567 | − p_max = fin / (6 * MHZ); | |
| 568 | − | |
| 569 | − for (_p = p_min; _p <= p_max; ++_p) { | |
| 570 | − for (_s = 0; _s <= 5; ++_s) { | |
| 571 | − u64 tmp; | |
| 572 | − u32 delta; | |
| 573 | − | |
| 574 | − tmp = (u64)fout * (_p << _s); | |
| 575 | − do_div(tmp, fin); | |
| 576 | − _m = tmp; | |
| 577 | − if (_m < 41 || _m > 125) | |
| 578 | − continue; | |
| 579 | − | |
| 580 | − tmp = (u64)_m * fin; | |
| 581 | − do_div(tmp, _p); | |
| 582 | − if (tmp < 500 * MHZ || | |
| 583 | − tmp > driver_data->max_freq * MHZ) | |
| 584 | − continue; | |
| 585 | − | |
| 586 | − tmp = (u64)_m * fin; | |
| 587 | − do_div(tmp, _p << _s); | |
| 588 | − | |
| 589 | − delta = abs(fout - tmp); | |
| 590 | − if (delta < min_delta) { | |
| 591 | − best_p = _p; | |
| 592 | − best_m = _m; | |
| 593 | − best_s = _s; | |
| 594 | − min_delta = delta; | |
| 595 | − best_freq = tmp; | |
| 596 | − } | |
| 597 | − } | |
| 598 | − } | |
| 599 | − | |
| 600 | − if (best_freq) { | |
| 601 | − *p = best_p; | |
| 602 | − *m = best_m; | |
| 603 | − *s = best_s; | |
| 604 | − } | |
| 605 | − | |
| 606 | − return best_freq; | |
| 607 | −} | |
| 608 | − | |
| 609 | −static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi, | |
| 610 | − unsigned long freq) | |
| 611 | −{ | |
| 612 | − const struct exynos_dsi_driver_data *driver_data = dsi->driver_data; | |
| 613 | − unsigned long fin, fout; | |
| 614 | − int timeout; | |
| 615 | − u8 p, s; | |
| 616 | − u16 m; | |
| 617 | − u32 reg; | |
| 618 | − | |
| 619 | − fin = dsi->pll_clk_rate; | |
| 620 | − fout = exynos_dsi_pll_find_pms(dsi, fin, freq, &p, &m, &s); | |
| 621 | − if (!fout) { | |
| 622 | − dev_err(dsi->dev, | |
| 623 | − "failed to find PLL PMS for requested frequency\n"); | |
| 624 | − return 0; | |
| 625 | − } | |
| 626 | − dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s); | |
| 627 | − | |
| 628 | − writel(driver_data->reg_values[PLL_TIMER], | |
| 629 | − dsi->reg_base + driver_data->plltmr_reg); | |
| 630 | − | |
| 631 | − reg = DSIM_PLL_EN | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s); | |
| 632 | − | |
| 633 | − if (driver_data->has_freqband) { | |
| 634 | − static const unsigned long freq_bands[] = { | |
| 635 | − 100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ, | |
| 636 | − 270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ, | |
| 637 | − 510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ, | |
| 638 | − 770 * MHZ, 870 * MHZ, 950 * MHZ, | |
| 639 | − }; | |
| 640 | − int band; | |
| 641 | − | |
| 642 | − for (band = 0; band < ARRAY_SIZE(freq_bands); ++band) | |
| 643 | − if (fout < freq_bands[band]) | |
| 644 | − break; | |
| 645 | − | |
| 646 | − dev_dbg(dsi->dev, "band %d\n", band); | |
| 647 | − | |
| 648 | − reg |= DSIM_FREQ_BAND(band); | |
| 649 | − } | |
| 650 | − | |
| 651 | − exynos_dsi_write(dsi, DSIM_PLLCTRL_REG, reg); | |
| 652 | − | |
| 653 | − timeout = 1000; | |
| 654 | − do { | |
| 655 | − if (timeout-- == 0) { | |
| 656 | − dev_err(dsi->dev, "PLL failed to stabilize\n"); | |
| 657 | − return 0; | |
| 658 | − } | |
| 659 | − reg = exynos_dsi_read(dsi, DSIM_STATUS_REG); | |
| 660 | − } while ((reg & DSIM_PLL_STABLE) == 0); | |
| 661 | − | |
| 662 | − return fout; | |
| 663 | −} | |
| 664 | − | |
| 665 | −static int exynos_dsi_enable_clock(struct exynos_dsi *dsi) | |
| 666 | −{ | |
| 667 | − unsigned long hs_clk, byte_clk, esc_clk; | |
| 668 | − unsigned long esc_div; | |
| 669 | − u32 reg; | |
| 670 | − | |
| 671 | − hs_clk = exynos_dsi_set_pll(dsi, dsi->burst_clk_rate); | |
| 672 | − if (!hs_clk) { | |
| 673 | − dev_err(dsi->dev, "failed to configure DSI PLL\n"); | |
| 674 | − return -EFAULT; | |
| 675 | − } | |
| 676 | − | |
| 677 | − byte_clk = hs_clk / 8; | |
| 678 | − esc_div = DIV_ROUND_UP(byte_clk, dsi->esc_clk_rate); | |
| 679 | − esc_clk = byte_clk / esc_div; | |
| 680 | − | |
| 681 | − if (esc_clk > 20 * MHZ) { | |
| 682 | − ++esc_div; | |
| 683 | − esc_clk = byte_clk / esc_div; | |
| 684 | − } | |
| 685 | − | |
| 686 | − dev_dbg(dsi->dev, "hs_clk = %lu, byte_clk = %lu, esc_clk = %lu\n", | |
| 687 | − hs_clk, byte_clk, esc_clk); | |
| 688 | − | |
| 689 | − reg = exynos_dsi_read(dsi, DSIM_CLKCTRL_REG); | |
| 690 | − reg &= ~(DSIM_ESC_PRESCALER_MASK | DSIM_LANE_ESC_CLK_EN_CLK | |
| 691 | − | DSIM_LANE_ESC_CLK_EN_DATA_MASK | DSIM_PLL_BYPASS | |
| 692 | − | DSIM_BYTE_CLK_SRC_MASK); | |
| 693 | − reg |= DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN | |
| 694 | − | DSIM_ESC_PRESCALER(esc_div) | |
| 695 | − | DSIM_LANE_ESC_CLK_EN_CLK | |
| 696 | − | DSIM_LANE_ESC_CLK_EN_DATA(BIT(dsi->lanes) - 1) | |
| 697 | − | DSIM_BYTE_CLK_SRC(0) | |
| 698 | − | DSIM_TX_REQUEST_HSCLK; | |
| 699 | − exynos_dsi_write(dsi, DSIM_CLKCTRL_REG, reg); | |
| 700 | − | |
| 701 | − return 0; | |
| 702 | −} | |
| 703 | − | |
| 704 | −static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi) | |
| 705 | −{ | |
| 706 | − const struct exynos_dsi_driver_data *driver_data = dsi->driver_data; | |
| 707 | − const unsigned int *reg_values = driver_data->reg_values; | |
| 708 | − u32 reg; | |
| 709 | − | |
| 710 | − if (driver_data->has_freqband) | |
| 711 | − return; | |
| 712 | − | |
| 713 | − /* B D-PHY: D-PHY Master & Slave Analog Block control */ | |
| 714 | − reg = reg_values[PHYCTRL_ULPS_EXIT] | reg_values[PHYCTRL_VREG_LP] | | |
| 715 | − reg_values[PHYCTRL_SLEW_UP]; | |
| 716 | − exynos_dsi_write(dsi, DSIM_PHYCTRL_REG, reg); | |
| 717 | − | |
| 718 | − /* | |
| 719 | − * T LPX: Transmitted length of any Low-Power state period | |
| 720 | − * T HS-EXIT: Time that the transmitter drives LP-11 following a HS | |
| 721 | − * burst | |
| 722 | − */ | |
| 723 | − reg = reg_values[PHYTIMING_LPX] | reg_values[PHYTIMING_HS_EXIT]; | |
| 724 | − exynos_dsi_write(dsi, DSIM_PHYTIMING_REG, reg); | |
| 725 | − | |
| 726 | − /* | |
| 727 | − * T CLK-PREPARE: Time that the transmitter drives the Clock Lane LP-00 | |
| 728 | − * Line state immediately before the HS-0 Line state starting the | |
| 729 | − * HS transmission | |
| 730 | − * T CLK-ZERO: Time that the transmitter drives the HS-0 state prior to | |
| 731 | − * transmitting the Clock. | |
| 732 | − * T CLK_POST: Time that the transmitter continues to send HS clock | |
| 733 | − * after the last associated Data Lane has transitioned to LP Mode | |
| 734 | − * Interval is defined as the period from the end of T HS-TRAIL to | |
| 735 | − * the beginning of T CLK-TRAIL | |
| 736 | − * T CLK-TRAIL: Time that the transmitter drives the HS-0 state after | |
| 737 | − * the last payload clock bit of a HS transmission burst | |
| 738 | − */ | |
| 739 | − reg = reg_values[PHYTIMING_CLK_PREPARE] | | |
| 740 | − reg_values[PHYTIMING_CLK_ZERO] | | |
| 741 | − reg_values[PHYTIMING_CLK_POST] | | |
| 742 | − reg_values[PHYTIMING_CLK_TRAIL]; | |
| 743 | − | |
| 744 | − exynos_dsi_write(dsi, DSIM_PHYTIMING1_REG, reg); | |
| 745 | − | |
| 746 | − /* | |
| 747 | − * T HS-PREPARE: Time that the transmitter drives the Data Lane LP-00 | |
| 748 | − * Line state immediately before the HS-0 Line state starting the | |
| 749 | − * HS transmission | |
| 750 | − * T HS-ZERO: Time that the transmitter drives the HS-0 state prior to | |
| 751 | − * transmitting the Sync sequence. | |
| 752 | − * T HS-TRAIL: Time that the transmitter drives the flipped differential | |
| 753 | − * state after last payload data bit of a HS transmission burst | |
| 754 | − */ | |
| 755 | − reg = reg_values[PHYTIMING_HS_PREPARE] | reg_values[PHYTIMING_HS_ZERO] | | |
| 756 | − reg_values[PHYTIMING_HS_TRAIL]; | |
| 757 | − exynos_dsi_write(dsi, DSIM_PHYTIMING2_REG, reg); | |
| 758 | −} | |
| 759 | − | |
| 760 | −static void exynos_dsi_disable_clock(struct exynos_dsi *dsi) | |
| 761 | −{ | |
| 762 | − u32 reg; | |
| 763 | − | |
| 764 | − reg = exynos_dsi_read(dsi, DSIM_CLKCTRL_REG); | |
| 765 | − reg &= ~(DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK | |
| 766 | − | DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN); | |
| 767 | − exynos_dsi_write(dsi, DSIM_CLKCTRL_REG, reg); | |
| 768 | − | |
| 769 | − reg = exynos_dsi_read(dsi, DSIM_PLLCTRL_REG); | |
| 770 | − reg &= ~DSIM_PLL_EN; | |
| 771 | − exynos_dsi_write(dsi, DSIM_PLLCTRL_REG, reg); | |
| 772 | −} | |
| 773 | − | |
| 774 | −static void exynos_dsi_enable_lane(struct exynos_dsi *dsi, u32 lane) | |
| 775 | −{ | |
| 776 | − u32 reg = exynos_dsi_read(dsi, DSIM_CONFIG_REG); | |
| 777 | − reg |= (DSIM_NUM_OF_DATA_LANE(dsi->lanes - 1) | DSIM_LANE_EN_CLK | | |
| 778 | − DSIM_LANE_EN(lane)); | |
| 779 | − exynos_dsi_write(dsi, DSIM_CONFIG_REG, reg); | |
| 780 | −} | |
| 781 | − | |
| 782 | −static int exynos_dsi_init_link(struct exynos_dsi *dsi) | |
| 783 | −{ | |
| 784 | − const struct exynos_dsi_driver_data *driver_data = dsi->driver_data; | |
| 785 | − int timeout; | |
| 786 | − u32 reg; | |
| 787 | − u32 lanes_mask; | |
| 788 | − | |
| 789 | − /* Initialize FIFO pointers */ | |
| 790 | − reg = exynos_dsi_read(dsi, DSIM_FIFOCTRL_REG); | |
| 791 | − reg &= ~0x1f; | |
| 792 | − exynos_dsi_write(dsi, DSIM_FIFOCTRL_REG, reg); | |
| 793 | − | |
| 794 | − usleep_range(9000, 11000); | |
| 795 | − | |
| 796 | − reg |= 0x1f; | |
| 797 | − exynos_dsi_write(dsi, DSIM_FIFOCTRL_REG, reg); | |
| 798 | − usleep_range(9000, 11000); | |
| 799 | − | |
| 800 | − /* DSI configuration */ | |
| 801 | − reg = 0; | |
| 802 | − | |
| 803 | − /* | |
| 804 | − * The first bit of mode_flags specifies display configuration. | |
| 805 | − * If this bit is set[= MIPI_DSI_MODE_VIDEO], dsi will support video | |
| 806 | − * mode, otherwise it will support command mode. | |
| 807 | − */ | |
| 808 | − if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { | |
| 809 | − reg |= DSIM_VIDEO_MODE; | |
| 810 | − | |
| 811 | − /* | |
| 812 | − * The user manual describes that following bits are ignored in | |
| 813 | − * command mode. | |
| 814 | − */ | |
| 815 | − if (!(dsi->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH)) | |
| 816 | − reg |= DSIM_MFLUSH_VS; | |
| 817 | − if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) | |
| 818 | − reg |= DSIM_SYNC_INFORM; | |
| 819 | − if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) | |
| 820 | − reg |= DSIM_BURST_MODE; | |
| 821 | − if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT) | |
| 822 | − reg |= DSIM_AUTO_MODE; | |
| 823 | − if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSE) | |
| 824 | − reg |= DSIM_HSE_DISABLE_MODE; | |
| 825 | − if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_NO_HFP) | |
| 826 | − reg |= DSIM_HFP_DISABLE_MODE; | |
| 827 | − if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_NO_HBP) | |
| 828 | − reg |= DSIM_HBP_DISABLE_MODE; | |
| 829 | − if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_NO_HSA) | |
| 830 | − reg |= DSIM_HSA_DISABLE_MODE; | |
| 831 | − } | |
| 832 | − | |
| 833 | − if (dsi->mode_flags & MIPI_DSI_MODE_NO_EOT_PACKET) | |
| 834 | − reg |= DSIM_EOT_DISABLE; | |
| 835 | − | |
| 836 | − switch (dsi->format) { | |
| 837 | − case MIPI_DSI_FMT_RGB888: | |
| 838 | − reg |= DSIM_MAIN_PIX_FORMAT_RGB888; | |
| 839 | − break; | |
| 840 | − case MIPI_DSI_FMT_RGB666: | |
| 841 | − reg |= DSIM_MAIN_PIX_FORMAT_RGB666; | |
| 842 | − break; | |
| 843 | − case MIPI_DSI_FMT_RGB666_PACKED: | |
| 844 | − reg |= DSIM_MAIN_PIX_FORMAT_RGB666_P; | |
| 845 | − break; | |
| 846 | − case MIPI_DSI_FMT_RGB565: | |
| 847 | − reg |= DSIM_MAIN_PIX_FORMAT_RGB565; | |
| 848 | − break; | |
| 849 | − default: | |
| 850 | − dev_err(dsi->dev, "invalid pixel format\n"); | |
| 851 | − return -EINVAL; | |
| 852 | − } | |
| 853 | − | |
| 854 | − /* | |
| 855 | − * Use non-continuous clock mode if the periparal wants and | |
| 856 | − * host controller supports | |
| 857 | − * | |
| 858 | − * In non-continous clock mode, host controller will turn off | |
| 859 | − * the HS clock between high-speed transmissions to reduce | |
| 860 | − * power consumption. | |
| 861 | − */ | |
| 862 | − if (driver_data->has_clklane_stop && | |
| 863 | − dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) { | |
| 864 | − reg |= DSIM_CLKLANE_STOP; | |
| 865 | − } | |
| 866 | − exynos_dsi_write(dsi, DSIM_CONFIG_REG, reg); | |
| 867 | − | |
| 868 | − lanes_mask = BIT(dsi->lanes) - 1; | |
| 869 | − exynos_dsi_enable_lane(dsi, lanes_mask); | |
| 870 | − | |
| 871 | − /* Check clock and data lane state are stop state */ | |
| 872 | − timeout = 100; | |
| 873 | − do { | |
| 874 | − if (timeout-- == 0) { | |
| 875 | − dev_err(dsi->dev, "waiting for bus lanes timed out\n"); | |
| 876 | − return -EFAULT; | |
| 877 | − } | |
| 878 | − | |
| 879 | − reg = exynos_dsi_read(dsi, DSIM_STATUS_REG); | |
| 880 | − if ((reg & DSIM_STOP_STATE_DAT(lanes_mask)) | |
| 881 | − != DSIM_STOP_STATE_DAT(lanes_mask)) | |
| 882 | − continue; | |
| 883 | − } while (!(reg & (DSIM_STOP_STATE_CLK | DSIM_TX_READY_HS_CLK))); | |
| 884 | − | |
| 885 | − reg = exynos_dsi_read(dsi, DSIM_ESCMODE_REG); | |
| 886 | − reg &= ~DSIM_STOP_STATE_CNT_MASK; | |
| 887 | − reg |= DSIM_STOP_STATE_CNT(driver_data->reg_values[STOP_STATE_CNT]); | |
| 888 | − exynos_dsi_write(dsi, DSIM_ESCMODE_REG, reg); | |
| 889 | − | |
| 890 | − reg = DSIM_BTA_TIMEOUT(0xff) | DSIM_LPDR_TIMEOUT(0xffff); | |
| 891 | − exynos_dsi_write(dsi, DSIM_TIMEOUT_REG, reg); | |
| 892 | − | |
| 893 | − return 0; | |
| 894 | −} | |
| 895 | − | |
| 896 | −static void exynos_dsi_set_display_mode(struct exynos_dsi *dsi) | |
| 897 | −{ | |
| 898 | − struct drm_display_mode *m = &dsi->mode; | |
| 899 | − unsigned int num_bits_resol = dsi->driver_data->num_bits_resol; | |
| 900 | − u32 reg; | |
| 901 | − | |
| 902 | − if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { | |
| 903 | − reg = DSIM_CMD_ALLOW(0xf) | |
| 904 | − | DSIM_STABLE_VFP(m->vsync_start - m->vdisplay) | |
| 905 | − | DSIM_MAIN_VBP(m->vtotal - m->vsync_end); | |
| 906 | − exynos_dsi_write(dsi, DSIM_MVPORCH_REG, reg); | |
| 907 | − | |
| 908 | − reg = DSIM_MAIN_HFP(m->hsync_start - m->hdisplay) | |
| 909 | − | DSIM_MAIN_HBP(m->htotal - m->hsync_end); | |
| 910 | − exynos_dsi_write(dsi, DSIM_MHPORCH_REG, reg); | |
| 911 | − | |
| 912 | − reg = DSIM_MAIN_VSA(m->vsync_end - m->vsync_start) | |
| 913 | − | DSIM_MAIN_HSA(m->hsync_end - m->hsync_start); | |
| 914 | − exynos_dsi_write(dsi, DSIM_MSYNC_REG, reg); | |
| 915 | − } | |
| 916 | − reg = DSIM_MAIN_HRESOL(m->hdisplay, num_bits_resol) | | |
| 917 | − DSIM_MAIN_VRESOL(m->vdisplay, num_bits_resol); | |
| 918 | − | |
| 919 | − exynos_dsi_write(dsi, DSIM_MDRESOL_REG, reg); | |
| 920 | − | |
| 921 | − dev_dbg(dsi->dev, "LCD size = %dx%d\n", m->hdisplay, m->vdisplay); | |
| 922 | −} | |
| 923 | − | |
| 924 | −static void exynos_dsi_set_display_enable(struct exynos_dsi *dsi, bool enable) | |
| 925 | −{ | |
| 926 | − u32 reg; | |
| 927 | − | |
| 928 | − reg = exynos_dsi_read(dsi, DSIM_MDRESOL_REG); | |
| 929 | − if (enable) | |
| 930 | − reg |= DSIM_MAIN_STAND_BY; | |
| 931 | − else | |
| 932 | − reg &= ~DSIM_MAIN_STAND_BY; | |
| 933 | − exynos_dsi_write(dsi, DSIM_MDRESOL_REG, reg); | |
| 934 | −} | |
| 935 | − | |
| 936 | −static int exynos_dsi_wait_for_hdr_fifo(struct exynos_dsi *dsi) | |
| 937 | −{ | |
| 938 | − int timeout = 2000; | |
| 939 | − | |
| 940 | − do { | |
| 941 | − u32 reg = exynos_dsi_read(dsi, DSIM_FIFOCTRL_REG); | |
| 942 | − | |
| 943 | − if (!(reg & DSIM_SFR_HEADER_FULL)) | |
| 944 | − return 0; | |
| 945 | − | |
| 946 | − if (!cond_resched()) | |
| 947 | − usleep_range(950, 1050); | |
| 948 | − } while (--timeout); | |
| 949 | − | |
| 950 | − return -ETIMEDOUT; | |
| 951 | −} | |
| 952 | − | |
| 953 | −static void exynos_dsi_set_cmd_lpm(struct exynos_dsi *dsi, bool lpm) | |
| 954 | −{ | |
| 955 | − u32 v = exynos_dsi_read(dsi, DSIM_ESCMODE_REG); | |
| 956 | − | |
| 957 | − if (lpm) | |
| 958 | − v |= DSIM_CMD_LPDT_LP; | |
| 959 | − else | |
| 960 | − v &= ~DSIM_CMD_LPDT_LP; | |
| 961 | − | |
| 962 | − exynos_dsi_write(dsi, DSIM_ESCMODE_REG, v); | |
| 963 | −} | |
| 964 | − | |
| 965 | −static void exynos_dsi_force_bta(struct exynos_dsi *dsi) | |
| 966 | −{ | |
| 967 | − u32 v = exynos_dsi_read(dsi, DSIM_ESCMODE_REG); | |
| 968 | − v |= DSIM_FORCE_BTA; | |
| 969 | − exynos_dsi_write(dsi, DSIM_ESCMODE_REG, v); | |
| 970 | −} | |
| 971 | − | |
| 972 | −static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi, | |
| 973 | − struct exynos_dsi_transfer *xfer) | |
| 974 | −{ | |
| 975 | − struct device *dev = dsi->dev; | |
| 976 | − struct mipi_dsi_packet *pkt = &xfer->packet; | |
| 977 | − const u8 *payload = pkt->payload + xfer->tx_done; | |
| 978 | − u16 length = pkt->payload_length - xfer->tx_done; | |
| 979 | − bool first = !xfer->tx_done; | |
| 980 | − u32 reg; | |
| 981 | − | |
| 982 | − dev_dbg(dev, "< xfer %pK: tx len %u, done %u, rx len %u, done %u\n", | |
| 983 | − xfer, length, xfer->tx_done, xfer->rx_len, xfer->rx_done); | |
| 984 | − | |
| 985 | − if (length > DSI_TX_FIFO_SIZE) | |
| 986 | − length = DSI_TX_FIFO_SIZE; | |
| 987 | − | |
| 988 | − xfer->tx_done += length; | |
| 989 | − | |
| 990 | − /* Send payload */ | |
| 991 | − while (length >= 4) { | |
| 992 | − reg = get_unaligned_le32(payload); | |
| 993 | − exynos_dsi_write(dsi, DSIM_PAYLOAD_REG, reg); | |
| 994 | − payload += 4; | |
| 995 | − length -= 4; | |
| 996 | − } | |
| 997 | − | |
| 998 | − reg = 0; | |
| 999 | − switch (length) { | |
| 1000 | − case 3: | |
| 1001 | − reg |= payload[2] << 16; | |
| 1002 | − fallthrough; | |
| 1003 | − case 2: | |
| 1004 | − reg |= payload[1] << 8; | |
| 1005 | − fallthrough; | |
| 1006 | − case 1: | |
| 1007 | − reg |= payload[0]; | |
| 1008 | − exynos_dsi_write(dsi, DSIM_PAYLOAD_REG, reg); | |
| 1009 | − break; | |
| 1010 | − } | |
| 1011 | − | |
| 1012 | − /* Send packet header */ | |
| 1013 | − if (!first) | |
| 1014 | − return; | |
| 1015 | − | |
| 1016 | − reg = get_unaligned_le32(pkt->header); | |
| 1017 | − if (exynos_dsi_wait_for_hdr_fifo(dsi)) { | |
| 1018 | − dev_err(dev, "waiting for header FIFO timed out\n"); | |
| 1019 | − return; | |
| 1020 | − } | |
| 1021 | − | |
| 1022 | − if (NEQV(xfer->flags & MIPI_DSI_MSG_USE_LPM, | |
| 1023 | − dsi->state & DSIM_STATE_CMD_LPM)) { | |
| 1024 | − exynos_dsi_set_cmd_lpm(dsi, xfer->flags & MIPI_DSI_MSG_USE_LPM); | |
| 1025 | − dsi->state ^= DSIM_STATE_CMD_LPM; | |
| 1026 | − } | |
| 1027 | − | |
| 1028 | − exynos_dsi_write(dsi, DSIM_PKTHDR_REG, reg); | |
| 1029 | − | |
| 1030 | − if (xfer->flags & MIPI_DSI_MSG_REQ_ACK) | |
| 1031 | − exynos_dsi_force_bta(dsi); | |
| 1032 | −} | |
| 1033 | − | |
| 1034 | −static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi, | |
| 1035 | − struct exynos_dsi_transfer *xfer) | |
| 1036 | −{ | |
| 1037 | − u8 *payload = xfer->rx_payload + xfer->rx_done; | |
| 1038 | − bool first = !xfer->rx_done; | |
| 1039 | − struct device *dev = dsi->dev; | |
| 1040 | − u16 length; | |
| 1041 | − u32 reg; | |
| 1042 | − | |
| 1043 | − if (first) { | |
| 1044 | − reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG); | |
| 1045 | − | |
| 1046 | − switch (reg & 0x3f) { | |
| 1047 | − case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE: | |
| 1048 | − case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE: | |
| 1049 | − if (xfer->rx_len >= 2) { | |
| 1050 | − payload[1] = reg >> 16; | |
| 1051 | − ++xfer->rx_done; | |
| 1052 | − } | |
| 1053 | − fallthrough; | |
| 1054 | − case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE: | |
| 1055 | − case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE: | |
| 1056 | − payload[0] = reg >> 8; | |
| 1057 | − ++xfer->rx_done; | |
| 1058 | − xfer->rx_len = xfer->rx_done; | |
| 1059 | − xfer->result = 0; | |
| 1060 | − goto clear_fifo; | |
| 1061 | − case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT: | |
| 1062 | − dev_err(dev, "DSI Error Report: 0x%04x\n", | |
| 1063 | − (reg >> 8) & 0xffff); | |
| 1064 | − xfer->result = 0; | |
| 1065 | − goto clear_fifo; | |
| 1066 | − } | |
| 1067 | − | |
| 1068 | − length = (reg >> 8) & 0xffff; | |
| 1069 | − if (length > xfer->rx_len) { | |
| 1070 | − dev_err(dev, | |
| 1071 | − "response too long (%u > %u bytes), stripping\n", | |
| 1072 | − xfer->rx_len, length); | |
| 1073 | − length = xfer->rx_len; | |
| 1074 | − } else if (length < xfer->rx_len) | |
| 1075 | − xfer->rx_len = length; | |
| 1076 | − } | |
| 1077 | − | |
| 1078 | − length = xfer->rx_len - xfer->rx_done; | |
| 1079 | − xfer->rx_done += length; | |
| 1080 | − | |
| 1081 | − /* Receive payload */ | |
| 1082 | − while (length >= 4) { | |
| 1083 | − reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG); | |
| 1084 | − payload[0] = (reg >> 0) & 0xff; | |
| 1085 | − payload[1] = (reg >> 8) & 0xff; | |
| 1086 | − payload[2] = (reg >> 16) & 0xff; | |
| 1087 | − payload[3] = (reg >> 24) & 0xff; | |
| 1088 | − payload += 4; | |
| 1089 | − length -= 4; | |
| 1090 | − } | |
| 1091 | − | |
| 1092 | − if (length) { | |
| 1093 | − reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG); | |
| 1094 | − switch (length) { | |
| 1095 | − case 3: | |
| 1096 | − payload[2] = (reg >> 16) & 0xff; | |
| 1097 | − fallthrough; | |
| 1098 | − case 2: | |
| 1099 | − payload[1] = (reg >> 8) & 0xff; | |
| 1100 | − fallthrough; | |
| 1101 | − case 1: | |
| 1102 | − payload[0] = reg & 0xff; | |
| 1103 | − } | |
| 1104 | − } | |
| 1105 | − | |
| 1106 | − if (xfer->rx_done == xfer->rx_len) | |
| 1107 | − xfer->result = 0; | |
| 1108 | − | |
| 1109 | −clear_fifo: | |
| 1110 | − length = DSI_RX_FIFO_SIZE / 4; | |
| 1111 | − do { | |
| 1112 | − reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG); | |
| 1113 | − if (reg == DSI_RX_FIFO_EMPTY) | |
| 1114 | − break; | |
| 1115 | − } while (--length); | |
| 1116 | −} | |
| 1117 | − | |
| 1118 | −static void exynos_dsi_transfer_start(struct exynos_dsi *dsi) | |
| 1119 | −{ | |
| 1120 | − unsigned long flags; | |
| 1121 | − struct exynos_dsi_transfer *xfer; | |
| 1122 | − bool start = false; | |
| 1123 | − | |
| 1124 | −again: | |
| 1125 | − spin_lock_irqsave(&dsi->transfer_lock, flags); | |
| 1126 | − | |
| 1127 | − if (list_empty(&dsi->transfer_list)) { | |
| 1128 | − spin_unlock_irqrestore(&dsi->transfer_lock, flags); | |
| 1129 | − return; | |
| 1130 | − } | |
| 1131 | − | |
| 1132 | − xfer = list_first_entry(&dsi->transfer_list, | |
| 1133 | − struct exynos_dsi_transfer, list); | |
| 1134 | − | |
| 1135 | − spin_unlock_irqrestore(&dsi->transfer_lock, flags); | |
| 1136 | − | |
| 1137 | − if (xfer->packet.payload_length && | |
| 1138 | − xfer->tx_done == xfer->packet.payload_length) | |
| 1139 | − /* waiting for RX */ | |
| 1140 | − return; | |
| 1141 | − | |
| 1142 | − exynos_dsi_send_to_fifo(dsi, xfer); | |
| 1143 | − | |
| 1144 | − if (xfer->packet.payload_length || xfer->rx_len) | |
| 1145 | − return; | |
| 1146 | − | |
| 1147 | − xfer->result = 0; | |
| 1148 | − complete(&xfer->completed); | |
| 1149 | − | |
| 1150 | − spin_lock_irqsave(&dsi->transfer_lock, flags); | |
| 1151 | − | |
| 1152 | − list_del_init(&xfer->list); | |
| 1153 | − start = !list_empty(&dsi->transfer_list); | |
| 1154 | − | |
| 1155 | − spin_unlock_irqrestore(&dsi->transfer_lock, flags); | |
| 1156 | − | |
| 1157 | − if (start) | |
| 1158 | − goto again; | |
| 1159 | −} | |
| 1160 | − | |
| 1161 | −static bool exynos_dsi_transfer_finish(struct exynos_dsi *dsi) | |
| 1162 | −{ | |
| 1163 | − struct exynos_dsi_transfer *xfer; | |
| 1164 | − unsigned long flags; | |
| 1165 | − bool start = true; | |
| 1166 | − | |
| 1167 | − spin_lock_irqsave(&dsi->transfer_lock, flags); | |
| 1168 | − | |
| 1169 | − if (list_empty(&dsi->transfer_list)) { | |
| 1170 | − spin_unlock_irqrestore(&dsi->transfer_lock, flags); | |
| 1171 | − return false; | |
| 1172 | − } | |
| 1173 | − | |
| 1174 | − xfer = list_first_entry(&dsi->transfer_list, | |
| 1175 | − struct exynos_dsi_transfer, list); | |
| 1176 | − | |
| 1177 | − spin_unlock_irqrestore(&dsi->transfer_lock, flags); | |
| 1178 | − | |
| 1179 | − dev_dbg(dsi->dev, | |
| 1180 | − "> xfer %pK, tx_len %zu, tx_done %u, rx_len %u, rx_done %u\n", | |
| 1181 | − xfer, xfer->packet.payload_length, xfer->tx_done, xfer->rx_len, | |
| 1182 | − xfer->rx_done); | |
| 1183 | − | |
| 1184 | − if (xfer->tx_done != xfer->packet.payload_length) | |
| 1185 | − return true; | |
| 1186 | − | |
| 1187 | − if (xfer->rx_done != xfer->rx_len) | |
| 1188 | − exynos_dsi_read_from_fifo(dsi, xfer); | |
| 1189 | − | |
| 1190 | − if (xfer->rx_done != xfer->rx_len) | |
| 1191 | − return true; | |
| 1192 | − | |
| 1193 | − spin_lock_irqsave(&dsi->transfer_lock, flags); | |
| 1194 | − | |
| 1195 | − list_del_init(&xfer->list); | |
| 1196 | − start = !list_empty(&dsi->transfer_list); | |
| 1197 | − | |
| 1198 | − spin_unlock_irqrestore(&dsi->transfer_lock, flags); | |
| 1199 | − | |
| 1200 | − if (!xfer->rx_len) | |
| 1201 | − xfer->result = 0; | |
| 1202 | − complete(&xfer->completed); | |
| 1203 | − | |
| 1204 | − return start; | |
| 1205 | −} | |
| 1206 | − | |
| 1207 | −static void exynos_dsi_remove_transfer(struct exynos_dsi *dsi, | |
| 1208 | − struct exynos_dsi_transfer *xfer) | |
| 24 | +static irqreturn_t exynos_dsi_te_irq_handler(struct samsung_dsim *dsim) | |
| 1209 | 25 | { |
| 1210 | − unsigned long flags; | |
| 1211 | − bool start; | |
| 1212 | − | |
| 1213 | − spin_lock_irqsave(&dsi->transfer_lock, flags); | |
| 1214 | − | |
| 1215 | − if (!list_empty(&dsi->transfer_list) && | |
| 1216 | − xfer == list_first_entry(&dsi->transfer_list, | |
| 1217 | − struct exynos_dsi_transfer, list)) { | |
| 1218 | − list_del_init(&xfer->list); | |
| 1219 | − start = !list_empty(&dsi->transfer_list); | |
| 1220 | − spin_unlock_irqrestore(&dsi->transfer_lock, flags); | |
| 1221 | − if (start) | |
| 1222 | − exynos_dsi_transfer_start(dsi); | |
| 1223 | − return; | |
| 1224 | − } | |
| 1225 | − | |
| 1226 | − list_del_init(&xfer->list); | |
| 1227 | − | |
| 1228 | − spin_unlock_irqrestore(&dsi->transfer_lock, flags); | |
| 1229 | −} | |
| 1230 | − | |
| 1231 | −static int exynos_dsi_transfer(struct exynos_dsi *dsi, | |
| 1232 | − struct exynos_dsi_transfer *xfer) | |
| 1233 | −{ | |
| 1234 | − unsigned long flags; | |
| 1235 | − bool stopped; | |
| 1236 | − | |
| 1237 | − xfer->tx_done = 0; | |
| 1238 | − xfer->rx_done = 0; | |
| 1239 | − xfer->result = -ETIMEDOUT; | |
| 1240 | − init_completion(&xfer->completed); | |
| 1241 | − | |
| 1242 | − spin_lock_irqsave(&dsi->transfer_lock, flags); | |
| 1243 | − | |
| 1244 | − stopped = list_empty(&dsi->transfer_list); | |
| 1245 | − list_add_tail(&xfer->list, &dsi->transfer_list); | |
| 1246 | − | |
| 1247 | − spin_unlock_irqrestore(&dsi->transfer_lock, flags); | |
| 1248 | − | |
| 1249 | − if (stopped) | |
| 1250 | − exynos_dsi_transfer_start(dsi); | |
| 1251 | − | |
| 1252 | − wait_for_completion_timeout(&xfer->completed, | |
| 1253 | − msecs_to_jiffies(DSI_XFER_TIMEOUT_MS)); | |
| 1254 | − if (xfer->result == -ETIMEDOUT) { | |
| 1255 | − struct mipi_dsi_packet *pkt = &xfer->packet; | |
| 1256 | − exynos_dsi_remove_transfer(dsi, xfer); | |
| 1257 | − dev_err(dsi->dev, "xfer timed out: %*ph %*ph\n", 4, pkt->header, | |
| 1258 | − (int)pkt->payload_length, pkt->payload); | |
| 1259 | − return -ETIMEDOUT; | |
| 1260 | − } | |
| 1261 | − | |
| 1262 | − /* Also covers hardware timeout condition */ | |
| 1263 | − return xfer->result; | |
| 1264 | −} | |
| 1265 | − | |
| 1266 | −static irqreturn_t exynos_dsi_irq(int irq, void *dev_id) | |
| 1267 | −{ | |
| 1268 | − struct exynos_dsi *dsi = dev_id; | |
| 1269 | − u32 status; | |
| 1270 | − | |
| 1271 | − status = exynos_dsi_read(dsi, DSIM_INTSRC_REG); | |
| 1272 | − if (!status) { | |
| 1273 | − static unsigned long int j; | |
| 1274 | − if (printk_timed_ratelimit(&j, 500)) | |
| 1275 | − dev_warn(dsi->dev, "spurious interrupt\n"); | |
| 1276 | − return IRQ_HANDLED; | |
| 1277 | − } | |
| 1278 | − exynos_dsi_write(dsi, DSIM_INTSRC_REG, status); | |
| 1279 | − | |
| 1280 | − if (status & DSIM_INT_SW_RST_RELEASE) { | |
| 1281 | − u32 mask = ~(DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY | | |
| 1282 | − DSIM_INT_SFR_HDR_FIFO_EMPTY | DSIM_INT_RX_ECC_ERR | | |
| 1283 | − DSIM_INT_SW_RST_RELEASE); | |
| 1284 | − exynos_dsi_write(dsi, DSIM_INTMSK_REG, mask); | |
| 1285 | − complete(&dsi->completed); | |
| 1286 | − return IRQ_HANDLED; | |
| 1287 | − } | |
| 1288 | − | |
| 1289 | − if (!(status & (DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY | | |
| 1290 | − DSIM_INT_PLL_STABLE))) | |
| 1291 | − return IRQ_HANDLED; | |
| 1292 | − | |
| 1293 | − if (exynos_dsi_transfer_finish(dsi)) | |
| 1294 | − exynos_dsi_transfer_start(dsi); | |
| 1295 | − | |
| 1296 | − return IRQ_HANDLED; | |
| 1297 | −} | |
| 1298 | − | |
| 1299 | −static irqreturn_t exynos_dsi_te_irq_handler(int irq, void *dev_id) | |
| 1300 | −{ | |
| 1301 | − struct exynos_dsi *dsi = (struct exynos_dsi *)dev_id; | |
| 26 | + struct exynos_dsi *dsi = dsim->priv; | |
| 1302 | 27 | struct drm_encoder *encoder = &dsi->encoder; |
| 1303 | 28 | |
| 1304 | − if (dsi->state & DSIM_STATE_VIDOUT_AVAILABLE) | |
| 29 | + if (dsim->state & DSIM_STATE_VIDOUT_AVAILABLE) | |
| 1305 | 30 | exynos_drm_crtc_te_handler(encoder->crtc); |
| 1306 | 31 | |
| 1307 | 32 | return IRQ_HANDLED; |
| 1308 | 33 | } |
| 1309 | 34 | |
| 1310 | −static void exynos_dsi_enable_irq(struct exynos_dsi *dsi) | |
| 1311 | −{ | |
| 1312 | − enable_irq(dsi->irq); | |
| 1313 | − | |
| 1314 | − if (dsi->te_gpio) | |
| 1315 | − enable_irq(gpiod_to_irq(dsi->te_gpio)); | |
| 1316 | −} | |
| 1317 | − | |
| 1318 | −static void exynos_dsi_disable_irq(struct exynos_dsi *dsi) | |
| 1319 | −{ | |
| 1320 | − if (dsi->te_gpio) | |
| 1321 | − disable_irq(gpiod_to_irq(dsi->te_gpio)); | |
| 1322 | − | |
| 1323 | − disable_irq(dsi->irq); | |
| 1324 | −} | |
| 1325 | − | |
| 1326 | −static int exynos_dsi_init(struct exynos_dsi *dsi) | |
| 1327 | −{ | |
| 1328 | − const struct exynos_dsi_driver_data *driver_data = dsi->driver_data; | |
| 1329 | − | |
| 1330 | − exynos_dsi_reset(dsi); | |
| 1331 | − exynos_dsi_enable_irq(dsi); | |
| 1332 | − | |
| 1333 | − if (driver_data->reg_values[RESET_TYPE] == DSIM_FUNCRST) | |
| 1334 | − exynos_dsi_enable_lane(dsi, BIT(dsi->lanes) - 1); | |
| 1335 | − | |
| 1336 | − exynos_dsi_enable_clock(dsi); | |
| 1337 | − if (driver_data->wait_for_reset) | |
| 1338 | − exynos_dsi_wait_for_reset(dsi); | |
| 1339 | − exynos_dsi_set_phy_ctrl(dsi); | |
| 1340 | − exynos_dsi_init_link(dsi); | |
| 1341 | − | |
| 1342 | − return 0; | |
| 1343 | −} | |
| 1344 | − | |
| 1345 | −static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi, | |
| 1346 | − struct device *panel) | |
| 1347 | −{ | |
| 1348 | − int ret; | |
| 1349 | − int te_gpio_irq; | |
| 1350 | − | |
| 1351 | − dsi->te_gpio = gpiod_get_optional(panel, "te", GPIOD_IN); | |
| 1352 | − if (!dsi->te_gpio) { | |
| 1353 | − return 0; | |
| 1354 | − } else if (IS_ERR(dsi->te_gpio)) { | |
| 1355 | − dev_err(dsi->dev, "gpio request failed with %ld\n", | |
| 1356 | − PTR_ERR(dsi->te_gpio)); | |
| 1357 | − return PTR_ERR(dsi->te_gpio); | |
| 1358 | − } | |
| 1359 | − | |
| 1360 | − te_gpio_irq = gpiod_to_irq(dsi->te_gpio); | |
| 1361 | − | |
| 1362 | − ret = request_threaded_irq(te_gpio_irq, exynos_dsi_te_irq_handler, NULL, | |
| 1363 | − IRQF_TRIGGER_RISING | IRQF_NO_AUTOEN, "TE", dsi); | |
| 1364 | − if (ret) { | |
| 1365 | − dev_err(dsi->dev, "request interrupt failed with %d\n", ret); | |
| 1366 | − gpiod_put(dsi->te_gpio); | |
| 1367 | − return ret; | |
| 1368 | − } | |
| 1369 | − | |
| 1370 | − return 0; | |
| 1371 | −} | |
| 1372 | − | |
| 1373 | −static void exynos_dsi_unregister_te_irq(struct exynos_dsi *dsi) | |
| 1374 | −{ | |
| 1375 | − if (dsi->te_gpio) { | |
| 1376 | − free_irq(gpiod_to_irq(dsi->te_gpio), dsi); | |
| 1377 | − gpiod_put(dsi->te_gpio); | |
| 1378 | − } | |
| 1379 | −} | |
| 1380 | − | |
| 1381 | −static void exynos_dsi_atomic_pre_enable(struct drm_bridge *bridge, | |
| 1382 | − struct drm_bridge_state *old_bridge_state) | |
| 1383 | −{ | |
| 1384 | − struct exynos_dsi *dsi = bridge_to_dsi(bridge); | |
| 1385 | − int ret; | |
| 1386 | − | |
| 1387 | − if (dsi->state & DSIM_STATE_ENABLED) | |
| 1388 | − return; | |
| 1389 | − | |
| 1390 | − ret = pm_runtime_resume_and_get(dsi->dev); | |
| 1391 | − if (ret < 0) { | |
| 1392 | − dev_err(dsi->dev, "failed to enable DSI device.\n"); | |
| 1393 | − return; | |
| 1394 | − } | |
| 1395 | − | |
| 1396 | − dsi->state |= DSIM_STATE_ENABLED; | |
| 1397 | −} | |
| 1398 | − | |
| 1399 | −static void exynos_dsi_atomic_enable(struct drm_bridge *bridge, | |
| 1400 | − struct drm_bridge_state *old_bridge_state) | |
| 1401 | −{ | |
| 1402 | − struct exynos_dsi *dsi = bridge_to_dsi(bridge); | |
| 1403 | − | |
| 1404 | − exynos_dsi_set_display_mode(dsi); | |
| 1405 | − exynos_dsi_set_display_enable(dsi, true); | |
| 1406 | − | |
| 1407 | − dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE; | |
| 1408 | − | |
| 1409 | − return; | |
| 1410 | −} | |
| 1411 | − | |
| 1412 | −static void exynos_dsi_atomic_disable(struct drm_bridge *bridge, | |
| 1413 | − struct drm_bridge_state *old_bridge_state) | |
| 1414 | −{ | |
| 1415 | − struct exynos_dsi *dsi = bridge_to_dsi(bridge); | |
| 1416 | − | |
| 1417 | − if (!(dsi->state & DSIM_STATE_ENABLED)) | |
| 1418 | − return; | |
| 1419 | − | |
| 1420 | − dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE; | |
| 1421 | −} | |
| 1422 | − | |
| 1423 | −static void exynos_dsi_atomic_post_disable(struct drm_bridge *bridge, | |
| 1424 | − struct drm_bridge_state *old_bridge_state) | |
| 1425 | −{ | |
| 1426 | − struct exynos_dsi *dsi = bridge_to_dsi(bridge); | |
| 1427 | − | |
| 1428 | − exynos_dsi_set_display_enable(dsi, false); | |
| 1429 | − | |
| 1430 | − dsi->state &= ~DSIM_STATE_ENABLED; | |
| 1431 | − pm_runtime_put_sync(dsi->dev); | |
| 1432 | −} | |
| 1433 | − | |
| 1434 | −static void exynos_dsi_mode_set(struct drm_bridge *bridge, | |
| 1435 | − const struct drm_display_mode *mode, | |
| 1436 | − const struct drm_display_mode *adjusted_mode) | |
| 1437 | −{ | |
| 1438 | − struct exynos_dsi *dsi = bridge_to_dsi(bridge); | |
| 1439 | − | |
| 1440 | − drm_mode_copy(&dsi->mode, adjusted_mode); | |
| 1441 | −} | |
| 1442 | − | |
| 1443 | −static int exynos_dsi_attach(struct drm_bridge *bridge, | |
| 1444 | − enum drm_bridge_attach_flags flags) | |
| 1445 | −{ | |
| 1446 | − struct exynos_dsi *dsi = bridge_to_dsi(bridge); | |
| 1447 | − | |
| 1448 | − return drm_bridge_attach(bridge->encoder, dsi->out_bridge, bridge, | |
| 1449 | − flags); | |
| 1450 | −} | |
| 1451 | − | |
| 1452 | −static const struct drm_bridge_funcs exynos_dsi_bridge_funcs = { | |
| 1453 | − .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, | |
| 1454 | − .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, | |
| 1455 | − .atomic_reset = drm_atomic_helper_bridge_reset, | |
| 1456 | − .atomic_pre_enable = exynos_dsi_atomic_pre_enable, | |
| 1457 | − .atomic_enable = exynos_dsi_atomic_enable, | |
| 1458 | − .atomic_disable = exynos_dsi_atomic_disable, | |
| 1459 | − .atomic_post_disable = exynos_dsi_atomic_post_disable, | |
| 1460 | − .mode_set = exynos_dsi_mode_set, | |
| 1461 | − .attach = exynos_dsi_attach, | |
| 1462 | −}; | |
| 1463 | − | |
| 1464 | −MODULE_DEVICE_TABLE(of, exynos_dsi_of_match); | |
| 1465 | − | |
| 1466 | −static int exynos_dsi_host_attach(struct mipi_dsi_host *host, | |
| 35 | +static int exynos_dsi_host_attach(struct samsung_dsim *dsim, | |
| 1467 | 36 | struct mipi_dsi_device *device) |
| 1468 | 37 | { |
| 1469 | − struct exynos_dsi *dsi = host_to_dsi(host); | |
| 1470 | − struct device *dev = dsi->dev; | |
| 38 | + struct exynos_dsi *dsi = dsim->priv; | |
| 1471 | 39 | struct drm_encoder *encoder = &dsi->encoder; |
| 1472 | 40 | struct drm_device *drm = encoder->dev; |
| 1473 | − struct drm_panel *panel; | |
| 1474 | − int ret; | |
| 1475 | − | |
| 1476 | − panel = of_drm_find_panel(device->dev.of_node); | |
| 1477 | − if (!IS_ERR(panel)) { | |
| 1478 | − dsi->out_bridge = devm_drm_panel_bridge_add(dev, panel); | |
| 1479 | − } else { | |
| 1480 | − dsi->out_bridge = of_drm_find_bridge(device->dev.of_node); | |
| 1481 | − if (!dsi->out_bridge) | |
| 1482 | − dsi->out_bridge = ERR_PTR(-EINVAL); | |
| 1483 | − } | |
| 1484 | − | |
| 1485 | − if (IS_ERR(dsi->out_bridge)) { | |
| 1486 | − ret = PTR_ERR(dsi->out_bridge); | |
| 1487 | − DRM_DEV_ERROR(dev, "failed to find the bridge: %d\n", ret); | |
| 1488 | − return ret; | |
| 1489 | − } | |
| 1490 | − | |
| 1491 | − DRM_DEV_INFO(dev, "Attached %s device\n", device->name); | |
| 1492 | 41 | |
| 1493 | − drm_bridge_add(&dsi->bridge); | |
| 1494 | − | |
| 1495 | − drm_bridge_attach(encoder, &dsi->bridge, | |
| 42 | + drm_bridge_attach(encoder, &dsim->bridge, | |
| 1496 | 43 | list_first_entry_or_null(&encoder->bridge_chain, |
| 1497 | 44 | struct drm_bridge, |
| 1498 | 45 | chain_node), 0); |
| 1499 | 46 | |
| 1500 | − /* | |
| 1501 | − * This is a temporary solution and should be made by more generic way. | |
| 1502 | − * | |
| 1503 | − * If attached panel device is for command mode one, dsi should register | |
| 1504 | − * TE interrupt handler. | |
| 1505 | − */ | |
| 1506 | − if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO)) { | |
| 1507 | − ret = exynos_dsi_register_te_irq(dsi, &device->dev); | |
| 1508 | − if (ret) | |
| 1509 | − return ret; | |
| 1510 | − } | |
| 1511 | − | |
| 1512 | 47 | mutex_lock(&drm->mode_config.mutex); |
| 1513 | 48 | |
| 1514 | − dsi->lanes = device->lanes; | |
| 1515 | − dsi->format = device->format; | |
| 1516 | − dsi->mode_flags = device->mode_flags; | |
| 49 | + dsim->lanes = device->lanes; | |
| 50 | + dsim->format = device->format; | |
| 51 | + dsim->mode_flags = device->mode_flags; | |
| 1517 | 52 | exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD)->i80_mode = |
| 1518 | − !(dsi->mode_flags & MIPI_DSI_MODE_VIDEO); | |
| 53 | + !(dsim->mode_flags & MIPI_DSI_MODE_VIDEO); | |
| 1519 | 54 | |
| 1520 | 55 | mutex_unlock(&drm->mode_config.mutex); |
| 1521 | 56 | |
| @@ -1525,100 +60,20 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host, · 3 unmodified lines | ||
| 1525 | 60 | return 0; |
| 1526 | 61 | } |
| 1527 | 62 | |
| 1528 | −static int exynos_dsi_host_detach(struct mipi_dsi_host *host, | |
| 1529 | − struct mipi_dsi_device *device) | |
| 63 | +static void exynos_dsi_host_detach(struct samsung_dsim *dsim, | |
| 64 | + struct mipi_dsi_device *device) | |
| 1530 | 65 | { |
| 1531 | − struct exynos_dsi *dsi = host_to_dsi(host); | |
| 66 | + struct exynos_dsi *dsi = dsim->priv; | |
| 1532 | 67 | struct drm_device *drm = dsi->encoder.dev; |
| 1533 | 68 | |
| 1534 | − if (dsi->out_bridge->funcs->detach) | |
| 1535 | − dsi->out_bridge->funcs->detach(dsi->out_bridge); | |
| 1536 | − dsi->out_bridge = NULL; | |
| 1537 | − | |
| 1538 | 69 | if (drm->mode_config.poll_enabled) |
| 1539 | 70 | drm_kms_helper_hotplug_event(drm); |
| 1540 | − | |
| 1541 | − exynos_dsi_unregister_te_irq(dsi); | |
| 1542 | − | |
| 1543 | − drm_bridge_remove(&dsi->bridge); | |
| 1544 | − | |
| 1545 | − return 0; | |
| 1546 | 71 | } |
| 1547 | 72 | |
| 1548 | −static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host, | |
| 1549 | − const struct mipi_dsi_msg *msg) | |
| 73 | +static int exynos_dsi_bind(struct device *dev, struct device *master, void *data) | |
| 1550 | 74 | { |
| 1551 | − struct exynos_dsi *dsi = host_to_dsi(host); | |
| 1552 | − struct exynos_dsi_transfer xfer; | |
| 1553 | − int ret; | |
| 1554 | − | |
| 1555 | − if (!(dsi->state & DSIM_STATE_ENABLED)) | |
| 1556 | − return -EINVAL; | |
| 1557 | − | |
| 1558 | − if (!(dsi->state & DSIM_STATE_INITIALIZED)) { | |
| 1559 | − ret = exynos_dsi_init(dsi); | |
| 1560 | − if (ret) | |
| 1561 | − return ret; | |
| 1562 | − dsi->state |= DSIM_STATE_INITIALIZED; | |
| 1563 | − } | |
| 1564 | − | |
| 1565 | − ret = mipi_dsi_create_packet(&xfer.packet, msg); | |
| 1566 | − if (ret < 0) | |
| 1567 | − return ret; | |
| 1568 | − | |
| 1569 | − xfer.rx_len = msg->rx_len; | |
| 1570 | − xfer.rx_payload = msg->rx_buf; | |
| 1571 | − xfer.flags = msg->flags; | |
| 1572 | − | |
| 1573 | − ret = exynos_dsi_transfer(dsi, &xfer); | |
| 1574 | − return (ret < 0) ? ret : xfer.rx_done; | |
| 1575 | −} | |
| 1576 | − | |
| 1577 | −static const struct mipi_dsi_host_ops exynos_dsi_ops = { | |
| 1578 | − .attach = exynos_dsi_host_attach, | |
| 1579 | − .detach = exynos_dsi_host_detach, | |
| 1580 | − .transfer = exynos_dsi_host_transfer, | |
| 1581 | −}; | |
| 1582 | − | |
| 1583 | −static int exynos_dsi_of_read_u32(const struct device_node *np, | |
| 1584 | − const char *propname, u32 *out_value) | |
| 1585 | −{ | |
| 1586 | − int ret = of_property_read_u32(np, propname, out_value); | |
| 1587 | − | |
| 1588 | − if (ret < 0) | |
| 1589 | − pr_err("%pOF: failed to get '%s' property\n", np, propname); | |
| 1590 | − | |
| 1591 | − return ret; | |
| 1592 | −} | |
| 1593 | − | |
| 1594 | −static int exynos_dsi_parse_dt(struct exynos_dsi *dsi) | |
| 1595 | −{ | |
| 1596 | − struct device *dev = dsi->dev; | |
| 1597 | − struct device_node *node = dev->of_node; | |
| 1598 | − int ret; | |
| 1599 | − | |
| 1600 | − ret = exynos_dsi_of_read_u32(node, "samsung,pll-clock-frequency", | |
| 1601 | − &dsi->pll_clk_rate); | |
| 1602 | − if (ret < 0) | |
| 1603 | − return ret; | |
| 1604 | − | |
| 1605 | − ret = exynos_dsi_of_read_u32(node, "samsung,burst-clock-frequency", | |
| 1606 | − &dsi->burst_clk_rate); | |
| 1607 | − if (ret < 0) | |
| 1608 | − return ret; | |
| 1609 | − | |
| 1610 | − ret = exynos_dsi_of_read_u32(node, "samsung,esc-clock-frequency", | |
| 1611 | − &dsi->esc_clk_rate); | |
| 1612 | − if (ret < 0) | |
| 1613 | − return ret; | |
| 1614 | − | |
| 1615 | − return 0; | |
| 1616 | −} | |
| 1617 | − | |
| 1618 | −static int exynos_dsi_bind(struct device *dev, struct device *master, | |
| 1619 | − void *data) | |
| 1620 | −{ | |
| 1621 | − struct exynos_dsi *dsi = dev_get_drvdata(dev); | |
| 75 | + struct samsung_dsim *dsim = dev_get_drvdata(dev); | |
| 76 | + struct exynos_dsi *dsi = dsim->priv; | |
| 1622 | 77 | struct drm_encoder *encoder = &dsi->encoder; |
| 1623 | 78 | struct drm_device *drm_dev = data; |
| 1624 | 79 | int ret; |
| @@ -1629,17 +84,16 @@ static int exynos_dsi_bind(struct device *dev, struct device *master, · 4 unmodified lines | ||
| 1629 | 84 | if (ret < 0) |
| 1630 | 85 | return ret; |
| 1631 | 86 | |
| 1632 | − return mipi_dsi_host_register(&dsi->dsi_host); | |
| 87 | + return mipi_dsi_host_register(&dsim->dsi_host); | |
| 1633 | 88 | } |
| 1634 | 89 | |
| 1635 | −static void exynos_dsi_unbind(struct device *dev, struct device *master, | |
| 1636 | − void *data) | |
| 90 | +static void exynos_dsi_unbind(struct device *dev, struct device *master, void *data) | |
| 1637 | 91 | { |
| 1638 | − struct exynos_dsi *dsi = dev_get_drvdata(dev); | |
| 92 | + struct samsung_dsim *dsim = dev_get_drvdata(dev); | |
| 1639 | 93 | |
| 1640 | − exynos_dsi_atomic_disable(&dsi->bridge, NULL); | |
| 94 | + dsim->bridge.funcs->atomic_disable(&dsim->bridge, NULL); | |
| 1641 | 95 | |
| 1642 | − mipi_dsi_host_unregister(&dsi->dsi_host); | |
| 96 | + mipi_dsi_host_unregister(&dsim->dsi_host); | |
| 1643 | 97 | } |
| 1644 | 98 | |
| 1645 | 99 | static const struct component_ops exynos_dsi_component_ops = { |
| @@ -1647,189 +101,90 @@ static const struct component_ops exynos_dsi_component_ops = { · 1 unmodified line | ||
| 1647 | 101 | .unbind = exynos_dsi_unbind, |
| 1648 | 102 | }; |
| 1649 | 103 | |
| 1650 | −static int exynos_dsi_probe(struct platform_device *pdev) | |
| 104 | +static int exynos_dsi_register_host(struct samsung_dsim *dsim) | |
| 1651 | 105 | { |
| 1652 | − struct device *dev = &pdev->dev; | |
| 1653 | 106 | struct exynos_dsi *dsi; |
| 1654 | − int ret, i; | |
| 1655 | 107 | |
| 1656 | − dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); | |
| 108 | + dsi = devm_kzalloc(dsim->dev, sizeof(*dsi), GFP_KERNEL); | |
| 1657 | 109 | if (!dsi) |
| 1658 | 110 | return -ENOMEM; |
| 1659 | 111 | |
| 1660 | − init_completion(&dsi->completed); | |
| 1661 | − spin_lock_init(&dsi->transfer_lock); | |
| 1662 | − INIT_LIST_HEAD(&dsi->transfer_list); | |
| 1663 | − | |
| 1664 | − dsi->dsi_host.ops = &exynos_dsi_ops; | |
| 1665 | − dsi->dsi_host.dev = dev; | |
| 112 | + dsim->priv = dsi; | |
| 113 | + dsim->bridge.pre_enable_prev_first = true; | |
| 1666 | 114 | |
| 1667 | − dsi->dev = dev; | |
| 1668 | − dsi->driver_data = of_device_get_match_data(dev); | |
| 1669 | − | |
| 1670 | − dsi->supplies[0].supply = "vddcore"; | |
| 1671 | − dsi->supplies[1].supply = "vddio"; | |
| 1672 | − ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(dsi->supplies), | |
| 1673 | − dsi->supplies); | |
| 1674 | − if (ret) | |
| 1675 | − return dev_err_probe(dev, ret, "failed to get regulators\n"); | |
| 1676 | − | |
| 1677 | − dsi->clks = devm_kcalloc(dev, | |
| 1678 | − dsi->driver_data->num_clks, sizeof(*dsi->clks), | |
| 1679 | − GFP_KERNEL); | |
| 1680 | − if (!dsi->clks) | |
| 1681 | − return -ENOMEM; | |
| 1682 | − | |
| 1683 | − for (i = 0; i < dsi->driver_data->num_clks; i++) { | |
| 1684 | − dsi->clks[i] = devm_clk_get(dev, clk_names[i]); | |
| 1685 | − if (IS_ERR(dsi->clks[i])) { | |
| 1686 | − if (strcmp(clk_names[i], "sclk_mipi") == 0) { | |
| 1687 | − dsi->clks[i] = devm_clk_get(dev, | |
| 1688 | − OLD_SCLK_MIPI_CLK_NAME); | |
| 1689 | − if (!IS_ERR(dsi->clks[i])) | |
| 1690 | − continue; | |
| 1691 | − } | |
| 1692 | − | |
| 1693 | − dev_info(dev, "failed to get the clock: %s\n", | |
| 1694 | − clk_names[i]); | |
| 1695 | − return PTR_ERR(dsi->clks[i]); | |
| 1696 | − } | |
| 1697 | − } | |
| 1698 | − | |
| 1699 | − dsi->reg_base = devm_platform_ioremap_resource(pdev, 0); | |
| 1700 | − if (IS_ERR(dsi->reg_base)) | |
| 1701 | − return PTR_ERR(dsi->reg_base); | |
| 1702 | − | |
| 1703 | − dsi->phy = devm_phy_get(dev, "dsim"); | |
| 1704 | − if (IS_ERR(dsi->phy)) { | |
| 1705 | − dev_info(dev, "failed to get dsim phy\n"); | |
| 1706 | − return PTR_ERR(dsi->phy); | |
| 1707 | − } | |
| 1708 | − | |
| 1709 | − dsi->irq = platform_get_irq(pdev, 0); | |
| 1710 | − if (dsi->irq < 0) | |
| 1711 | − return dsi->irq; | |
| 1712 | − | |
| 1713 | − ret = devm_request_threaded_irq(dev, dsi->irq, NULL, | |
| 1714 | − exynos_dsi_irq, | |
| 1715 | − IRQF_ONESHOT | IRQF_NO_AUTOEN, | |
| 1716 | − dev_name(dev), dsi); | |
| 1717 | − if (ret) { | |
| 1718 | − dev_err(dev, "failed to request dsi irq\n"); | |
| 1719 | − return ret; | |
| 1720 | − } | |
| 1721 | − | |
| 1722 | − ret = exynos_dsi_parse_dt(dsi); | |
| 1723 | − if (ret) | |
| 1724 | − return ret; | |
| 1725 | − | |
| 1726 | − platform_set_drvdata(pdev, dsi); | |
| 1727 | − | |
| 1728 | − pm_runtime_enable(dev); | |
| 1729 | − | |
| 1730 | − dsi->bridge.funcs = &exynos_dsi_bridge_funcs; | |
| 1731 | − dsi->bridge.of_node = dev->of_node; | |
| 1732 | − dsi->bridge.type = DRM_MODE_CONNECTOR_DSI; | |
| 1733 | − dsi->bridge.pre_enable_prev_first = true; | |
| 1734 | − | |
| 1735 | − ret = component_add(dev, &exynos_dsi_component_ops); | |
| 1736 | − if (ret) | |
| 1737 | − goto err_disable_runtime; | |
| 1738 | − | |
| 1739 | − return 0; | |
| 1740 | − | |
| 1741 | −err_disable_runtime: | |
| 1742 | − pm_runtime_disable(dev); | |
| 1743 | − | |
| 1744 | − return ret; | |
| 115 | + return component_add(dsim->dev, &exynos_dsi_component_ops); | |
| 1745 | 116 | } |
| 1746 | 117 | |
| 1747 | −static int exynos_dsi_remove(struct platform_device *pdev) | |
| 118 | +static void exynos_dsi_unregister_host(struct samsung_dsim *dsim) | |
| 1748 | 119 | { |
| 1749 | − pm_runtime_disable(&pdev->dev); | |
| 1750 | − | |
| 1751 | − component_del(&pdev->dev, &exynos_dsi_component_ops); | |
| 1752 | − | |
| 1753 | − return 0; | |
| 1754 | −} | |
| 1755 | − | |
| 1756 | −static int __maybe_unused exynos_dsi_suspend(struct device *dev) | |
| 1757 | −{ | |
| 1758 | − struct exynos_dsi *dsi = dev_get_drvdata(dev); | |
| 1759 | − const struct exynos_dsi_driver_data *driver_data = dsi->driver_data; | |
| 1760 | − int ret, i; | |
| 1761 | − | |
| 1762 | − usleep_range(10000, 20000); | |
| 1763 | − | |
| 1764 | − if (dsi->state & DSIM_STATE_INITIALIZED) { | |
| 1765 | − dsi->state &= ~DSIM_STATE_INITIALIZED; | |
| 1766 | − | |
| 1767 | − exynos_dsi_disable_clock(dsi); | |
| 1768 | − | |
| 1769 | − exynos_dsi_disable_irq(dsi); | |
| 1770 | − } | |
| 1771 | − | |
| 1772 | − dsi->state &= ~DSIM_STATE_CMD_LPM; | |
| 1773 | − | |
| 1774 | − phy_power_off(dsi->phy); | |
| 1775 | − | |
| 1776 | − for (i = driver_data->num_clks - 1; i > -1; i--) | |
| 1777 | − clk_disable_unprepare(dsi->clks[i]); | |
| 1778 | − | |
| 1779 | − ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies); | |
| 1780 | − if (ret < 0) | |
| 1781 | − dev_err(dsi->dev, "cannot disable regulators %d\n", ret); | |
| 1782 | − | |
| 1783 | − return 0; | |
| 120 | + component_del(dsim->dev, &exynos_dsi_component_ops); | |
| 1784 | 121 | } |
| 1785 | 122 | |
| 1786 | −static int __maybe_unused exynos_dsi_resume(struct device *dev) | |
| 1787 | −{ | |
| 1788 | − struct exynos_dsi *dsi = dev_get_drvdata(dev); | |
| 1789 | − const struct exynos_dsi_driver_data *driver_data = dsi->driver_data; | |
| 1790 | − int ret, i; | |
| 1791 | − | |
| 1792 | − ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies); | |
| 1793 | − if (ret < 0) { | |
| 1794 | − dev_err(dsi->dev, "cannot enable regulators %d\n", ret); | |
| 1795 | − return ret; | |
| 1796 | − } | |
| 123 | +static const struct samsung_dsim_host_ops exynos_dsi_exynos_host_ops = { | |
| 124 | + .register_host = exynos_dsi_register_host, | |
| 125 | + .unregister_host = exynos_dsi_unregister_host, | |
| 126 | + .attach = exynos_dsi_host_attach, | |
| 127 | + .detach = exynos_dsi_host_detach, | |
| 128 | + .te_irq_handler = exynos_dsi_te_irq_handler, | |
| 129 | +}; | |
| 1797 | 130 | |
| 1798 | − for (i = 0; i < driver_data->num_clks; i++) { | |
| 1799 | − ret = clk_prepare_enable(dsi->clks[i]); | |
| 1800 | − if (ret < 0) | |
| 1801 | − goto err_clk; | |
| 1802 | − } | |
| 131 | +static const struct samsung_dsim_plat_data exynos3250_dsi_pdata = { | |
| 132 | + .hw_type = DSIM_TYPE_EXYNOS3250, | |
| 133 | + .host_ops = &exynos_dsi_exynos_host_ops, | |
| 134 | +}; | |
| 1803 | 135 | |
| 1804 | − ret = phy_power_on(dsi->phy); | |
| 1805 | − if (ret < 0) { | |
| 1806 | − dev_err(dsi->dev, "cannot enable phy %d\n", ret); | |
| 1807 | − goto err_clk; | |
| 1808 | − } | |
| 136 | +static const struct samsung_dsim_plat_data exynos4210_dsi_pdata = { | |
| 137 | + .hw_type = DSIM_TYPE_EXYNOS4210, | |
| 138 | + .host_ops = &exynos_dsi_exynos_host_ops, | |
| 139 | +}; | |
| 1809 | 140 | |
| 1810 | − return 0; | |
| 141 | +static const struct samsung_dsim_plat_data exynos5410_dsi_pdata = { | |
| 142 | + .hw_type = DSIM_TYPE_EXYNOS5410, | |
| 143 | + .host_ops = &exynos_dsi_exynos_host_ops, | |
| 144 | +}; | |
| 1811 | 145 | |
| 1812 | −err_clk: | |
| 1813 | − while (--i > -1) | |
| 1814 | − clk_disable_unprepare(dsi->clks[i]); | |
| 1815 | − regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies); | |
| 146 | +static const struct samsung_dsim_plat_data exynos5422_dsi_pdata = { | |
| 147 | + .hw_type = DSIM_TYPE_EXYNOS5422, | |
| 148 | + .host_ops = &exynos_dsi_exynos_host_ops, | |
| 149 | +}; | |
| 1816 | 150 | |
| 1817 | − return ret; | |
| 1818 | −} | |
| 151 | +static const struct samsung_dsim_plat_data exynos5433_dsi_pdata = { | |
| 152 | + .hw_type = DSIM_TYPE_EXYNOS5433, | |
| 153 | + .host_ops = &exynos_dsi_exynos_host_ops, | |
| 154 | +}; | |
| 1819 | 155 | |
| 1820 | −static const struct dev_pm_ops exynos_dsi_pm_ops = { | |
| 1821 | − SET_RUNTIME_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume, NULL) | |
| 1822 | − SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, | |
| 1823 | − pm_runtime_force_resume) | |
| 156 | +static const struct of_device_id exynos_dsi_of_match[] = { | |
| 157 | + { | |
| 158 | + .compatible = "samsung,exynos3250-mipi-dsi", | |
| 159 | + .data = &exynos3250_dsi_pdata, | |
| 160 | + }, | |
| 161 | + { | |
| 162 | + .compatible = "samsung,exynos4210-mipi-dsi", | |
| 163 | + .data = &exynos4210_dsi_pdata, | |
| 164 | + }, | |
| 165 | + { | |
| 166 | + .compatible = "samsung,exynos5410-mipi-dsi", | |
| 167 | + .data = &exynos5410_dsi_pdata, | |
| 168 | + }, | |
| 169 | + { | |
| 170 | + .compatible = "samsung,exynos5422-mipi-dsi", | |
| 171 | + .data = &exynos5422_dsi_pdata, | |
| 172 | + }, | |
| 173 | + { | |
| 174 | + .compatible = "samsung,exynos5433-mipi-dsi", | |
| 175 | + .data = &exynos5433_dsi_pdata, | |
| 176 | + }, | |
| 177 | + { /* sentinel. */ } | |
| 1824 | 178 | }; |
| 179 | +MODULE_DEVICE_TABLE(of, exynos_dsi_of_match); | |
| 1825 | 180 | |
| 1826 | 181 | struct platform_driver dsi_driver = { |
| 1827 | − .probe = exynos_dsi_probe, | |
| 1828 | − .remove = exynos_dsi_remove, | |
| 182 | + .probe = samsung_dsim_probe, | |
| 183 | + .remove = samsung_dsim_remove, | |
| 1829 | 184 | .driver = { |
| 1830 | 185 | .name = "exynos-dsi", |
| 1831 | 186 | .owner = THIS_MODULE, |
| 1832 | − .pm = &exynos_dsi_pm_ops, | |
| 187 | + .pm = &samsung_dsim_pm_ops, | |
| 1833 | 188 | .of_match_table = exynos_dsi_of_match, |
| 1834 | 189 | }, |
| 1835 | 190 | }; |
| @@ -0,0 +1,115 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0+ */ | |
| 2 | +/* | |
| 3 | + * Copyright (C) 2022 Amarula Solutions(India) | |
| 4 | + * Author: Jagan Teki <jagan@amarulasolutions.com> | |
| 5 | + */ | |
| 6 | + | |
| 7 | +#ifndef __SAMSUNG_DSIM__ | |
| 8 | +#define __SAMSUNG_DSIM__ | |
| 9 | + | |
| 10 | +#include <linux/gpio/consumer.h> | |
| 11 | +#include <linux/regulator/consumer.h> | |
| 12 | + | |
| 13 | +#include <drm/drm_atomic_helper.h> | |
| 14 | +#include <drm/drm_of.h> | |
| 15 | +#include <drm/drm_mipi_dsi.h> | |
| 16 | + | |
| 17 | +struct samsung_dsim; | |
| 18 | + | |
| 19 | +#define DSIM_STATE_ENABLED BIT(0) | |
| 20 | +#define DSIM_STATE_INITIALIZED BIT(1) | |
| 21 | +#define DSIM_STATE_CMD_LPM BIT(2) | |
| 22 | +#define DSIM_STATE_VIDOUT_AVAILABLE BIT(3) | |
| 23 | + | |
| 24 | +enum samsung_dsim_type { | |
| 25 | + DSIM_TYPE_EXYNOS3250, | |
| 26 | + DSIM_TYPE_EXYNOS4210, | |
| 27 | + DSIM_TYPE_EXYNOS5410, | |
| 28 | + DSIM_TYPE_EXYNOS5422, | |
| 29 | + DSIM_TYPE_EXYNOS5433, | |
| 30 | + DSIM_TYPE_IMX8MM, | |
| 31 | + DSIM_TYPE_IMX8MP, | |
| 32 | + DSIM_TYPE_COUNT, | |
| 33 | +}; | |
| 34 | + | |
| 35 | +#define samsung_dsim_hw_is_exynos(hw) \ | |
| 36 | + ((hw) >= DSIM_TYPE_EXYNOS3250 && (hw) <= DSIM_TYPE_EXYNOS5433) | |
| 37 | + | |
| 38 | +struct samsung_dsim_transfer { | |
| 39 | + struct list_head list; | |
| 40 | + struct completion completed; | |
| 41 | + int result; | |
| 42 | + struct mipi_dsi_packet packet; | |
| 43 | + u16 flags; | |
| 44 | + u16 tx_done; | |
| 45 | + | |
| 46 | + u8 *rx_payload; | |
| 47 | + u16 rx_len; | |
| 48 | + u16 rx_done; | |
| 49 | +}; | |
| 50 | + | |
| 51 | +struct samsung_dsim_driver_data { | |
| 52 | + const unsigned int *reg_ofs; | |
| 53 | + unsigned int plltmr_reg; | |
| 54 | + unsigned int has_freqband:1; | |
| 55 | + unsigned int has_clklane_stop:1; | |
| 56 | + unsigned int num_clks; | |
| 57 | + unsigned int max_freq; | |
| 58 | + unsigned int wait_for_reset; | |
| 59 | + unsigned int num_bits_resol; | |
| 60 | + unsigned int pll_p_offset; | |
| 61 | + const unsigned int *reg_values; | |
| 62 | +}; | |
| 63 | + | |
| 64 | +struct samsung_dsim_host_ops { | |
| 65 | + int (*register_host)(struct samsung_dsim *dsim); | |
| 66 | + void (*unregister_host)(struct samsung_dsim *dsim); | |
| 67 | + int (*attach)(struct samsung_dsim *dsim, struct mipi_dsi_device *device); | |
| 68 | + void (*detach)(struct samsung_dsim *dsim, struct mipi_dsi_device *device); | |
| 69 | + irqreturn_t (*te_irq_handler)(struct samsung_dsim *dsim); | |
| 70 | +}; | |
| 71 | + | |
| 72 | +struct samsung_dsim_plat_data { | |
| 73 | + enum samsung_dsim_type hw_type; | |
| 74 | + const struct samsung_dsim_host_ops *host_ops; | |
| 75 | +}; | |
| 76 | + | |
| 77 | +struct samsung_dsim { | |
| 78 | + struct mipi_dsi_host dsi_host; | |
| 79 | + struct drm_bridge bridge; | |
| 80 | + struct drm_bridge *out_bridge; | |
| 81 | + struct device *dev; | |
| 82 | + struct drm_display_mode mode; | |
| 83 | + | |
| 84 | + void __iomem *reg_base; | |
| 85 | + struct phy *phy; | |
| 86 | + struct clk **clks; | |
| 87 | + struct regulator_bulk_data supplies[2]; | |
| 88 | + int irq; | |
| 89 | + struct gpio_desc *te_gpio; | |
| 90 | + | |
| 91 | + u32 pll_clk_rate; | |
| 92 | + u32 burst_clk_rate; | |
| 93 | + u32 esc_clk_rate; | |
| 94 | + u32 lanes; | |
| 95 | + u32 mode_flags; | |
| 96 | + u32 format; | |
| 97 | + | |
| 98 | + int state; | |
| 99 | + struct drm_property *brightness; | |
| 100 | + struct completion completed; | |
| 101 | + | |
| 102 | + spinlock_t transfer_lock; /* protects transfer_list */ | |
| 103 | + struct list_head transfer_list; | |
| 104 | + | |
| 105 | + const struct samsung_dsim_driver_data *driver_data; | |
| 106 | + const struct samsung_dsim_plat_data *plat_data; | |
| 107 | + | |
| 108 | + void *priv; | |
| 109 | +}; | |
| 110 | + | |
| 111 | +extern int samsung_dsim_probe(struct platform_device *pdev); | |
| 112 | +extern int samsung_dsim_remove(struct platform_device *pdev); | |
| 113 | +extern const struct dev_pm_ops samsung_dsim_pm_ops; | |
| 114 | + | |
| 115 | +#endif /* __SAMSUNG_DSIM__ */ | |