1e2c0a7ffSGuochun Huang // SPDX-License-Identifier: GPL-2.0+ 2e2c0a7ffSGuochun Huang /* 3e2c0a7ffSGuochun Huang * (C) Copyright 2022 Rockchip Electronics Co., Ltd 4e2c0a7ffSGuochun Huang */ 5e2c0a7ffSGuochun Huang 6e2c0a7ffSGuochun Huang #include <common.h> 7e2c0a7ffSGuochun Huang #include <dm.h> 8e2c0a7ffSGuochun Huang #include <errno.h> 9e2c0a7ffSGuochun Huang #include <i2c.h> 10e2c0a7ffSGuochun Huang #include <max96755f.h> 11e2c0a7ffSGuochun Huang #include <video_bridge.h> 12*59487b18SGuochun Huang #include <drm/drm_mipi_dsi.h> 13e2c0a7ffSGuochun Huang #include <dm/of_access.h> 14e2c0a7ffSGuochun Huang #include <linux/media-bus-format.h> 15e2c0a7ffSGuochun Huang 16e2c0a7ffSGuochun Huang #include "rockchip_bridge.h" 17e2c0a7ffSGuochun Huang #include "rockchip_display.h" 18e2c0a7ffSGuochun Huang #include "rockchip_panel.h" 19e2c0a7ffSGuochun Huang 20e2c0a7ffSGuochun Huang static void max96755f_mipi_dsi_rx_config(struct max96755f_priv *priv) 21e2c0a7ffSGuochun Huang { 22e2c0a7ffSGuochun Huang struct drm_display_mode *mode = &priv->mode; 23e2c0a7ffSGuochun Huang u32 hfp, hsa, hbp, hact; 24e2c0a7ffSGuochun Huang u32 vact, vsa, vfp, vbp; 25e2c0a7ffSGuochun Huang 26e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(priv->dev, 0x0331, NUM_LANES, 27e2c0a7ffSGuochun Huang FIELD_PREP(NUM_LANES, priv->num_lanes - 1)); 28e2c0a7ffSGuochun Huang if (!priv->dpi_deskew_en) 29e2c0a7ffSGuochun Huang return; 30e2c0a7ffSGuochun Huang 31e2c0a7ffSGuochun Huang vact = mode->vdisplay; 32e2c0a7ffSGuochun Huang vsa = mode->vsync_end - mode->vsync_start; 33e2c0a7ffSGuochun Huang vfp = mode->vsync_start - mode->vdisplay; 34e2c0a7ffSGuochun Huang vbp = mode->vtotal - mode->vsync_end; 35e2c0a7ffSGuochun Huang hact = mode->hdisplay; 36e2c0a7ffSGuochun Huang hsa = mode->hsync_end - mode->hsync_start; 37e2c0a7ffSGuochun Huang hfp = mode->hsync_start - mode->hdisplay; 38e2c0a7ffSGuochun Huang hbp = mode->htotal - mode->hsync_end; 39e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03A4, 0xc1); 40e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x0385, FIELD_PREP(DPI_HSYNC_WIDTH_L, hsa)); 41e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x0386, FIELD_PREP(DPI_VYSNC_WIDTH_L, vsa)); 42e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x0387, 43e2c0a7ffSGuochun Huang FIELD_PREP(DPI_VSYNC_WIDTH_H, (vsa >> 8)) | 44e2c0a7ffSGuochun Huang FIELD_PREP(DPI_HSYNC_WIDTH_H, (hsa >> 8))); 45e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03a5, FIELD_PREP(DPI_VFP_L, vfp)); 46e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03a6, 47e2c0a7ffSGuochun Huang FIELD_PREP(DPI_VBP_L, vbp) | 48e2c0a7ffSGuochun Huang FIELD_PREP(DPI_VFP_H, (vfp >> 8))); 49e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03a7, FIELD_PREP(DPI_VBP_H, (vbp >> 4))); 50e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03a8, FIELD_PREP(DPI_VACT_L, vact)); 51e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03a9, FIELD_PREP(DPI_VACT_H, (vact >> 8))); 52e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03aa, FIELD_PREP(DPI_HFP_L, hfp)); 53e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03ab, 54e2c0a7ffSGuochun Huang FIELD_PREP(DPI_HBP_L, hbp) | 55e2c0a7ffSGuochun Huang FIELD_PREP(DPI_HFP_H, (hfp >> 7))); 56e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03ac, FIELD_PREP(DPI_HBP_H, (hbp >> 4))); 57e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03ad, FIELD_PREP(DPI_HACT_L, hact)); 58e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03ae, FIELD_PREP(DPI_HACT_H, (hact >> 8))); 59e2c0a7ffSGuochun Huang } 60e2c0a7ffSGuochun Huang 61e2c0a7ffSGuochun Huang static void max96755f_bridge_enable(struct rockchip_bridge *bridge) 62e2c0a7ffSGuochun Huang { 63e2c0a7ffSGuochun Huang struct udevice *dev = bridge->dev; 64e2c0a7ffSGuochun Huang struct max96755f_priv *priv = dev_get_priv(dev->parent); 65e2c0a7ffSGuochun Huang 66e2c0a7ffSGuochun Huang max96755f_mipi_dsi_rx_config(priv); 67e2c0a7ffSGuochun Huang if (priv->split_mode) { 68e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x0010, 69e2c0a7ffSGuochun Huang RESET_ONESHOT | AUTO_LINK | LINK_CFG, 70e2c0a7ffSGuochun Huang FIELD_PREP(RESET_ONESHOT, 1) | 71e2c0a7ffSGuochun Huang FIELD_PREP(AUTO_LINK, 0) | 72e2c0a7ffSGuochun Huang FIELD_PREP(LINK_CFG, SPLITTER_MODE)); 73e2c0a7ffSGuochun Huang mdelay(50); 74e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x0053, 75e2c0a7ffSGuochun Huang TX_SPLIT_MASK_B | TX_SPLIT_MASK_A | TX_STR_SEL, 76e2c0a7ffSGuochun Huang FIELD_PREP(TX_SPLIT_MASK_B, 0) | 77e2c0a7ffSGuochun Huang FIELD_PREP(TX_SPLIT_MASK_A, 1) | 78e2c0a7ffSGuochun Huang FIELD_PREP(TX_STR_SEL, 0)); 79e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x0057, 80e2c0a7ffSGuochun Huang TX_SPLIT_MASK_B | TX_SPLIT_MASK_A | TX_STR_SEL, 81e2c0a7ffSGuochun Huang FIELD_PREP(TX_SPLIT_MASK_B, 1) | 82e2c0a7ffSGuochun Huang FIELD_PREP(TX_SPLIT_MASK_A, 0) | 83e2c0a7ffSGuochun Huang FIELD_PREP(TX_STR_SEL, 1)); 84e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x032a, 85e2c0a7ffSGuochun Huang DV_SWP_AB | DV_CONV | DV_SPL | DV_EN, 86e2c0a7ffSGuochun Huang FIELD_PREP(DV_SWP_AB, priv->dv_swp_ab) | 87e2c0a7ffSGuochun Huang FIELD_PREP(DV_CONV, 1) | 88e2c0a7ffSGuochun Huang FIELD_PREP(DV_SPL, 1) | 89e2c0a7ffSGuochun Huang FIELD_PREP(DV_EN, 1)); 90e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x0311, 91e2c0a7ffSGuochun Huang START_PORTAX | START_PORTAY, 92e2c0a7ffSGuochun Huang FIELD_PREP(START_PORTAX, 1) | 93e2c0a7ffSGuochun Huang FIELD_PREP(START_PORTAY, 1)); 94e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x0002, 95e2c0a7ffSGuochun Huang VID_TX_EN_X | VID_TX_EN_Y, 96e2c0a7ffSGuochun Huang FIELD_PREP(VID_TX_EN_X, 1) | 97e2c0a7ffSGuochun Huang FIELD_PREP(VID_TX_EN_Y, 1)); 98e2c0a7ffSGuochun Huang 99e2c0a7ffSGuochun Huang } else { 100e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x0002, VID_TX_EN_X, 101e2c0a7ffSGuochun Huang FIELD_PREP(VID_TX_EN_X, 1)); 102e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x0311, START_PORTAX, 103e2c0a7ffSGuochun Huang FIELD_PREP(START_PORTAX, 1)); 104e2c0a7ffSGuochun Huang } 105e2c0a7ffSGuochun Huang 106e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x0010, RESET_ONESHOT, 107e2c0a7ffSGuochun Huang FIELD_PREP(RESET_ONESHOT, 1)); 108e2c0a7ffSGuochun Huang mdelay(100); 109e2c0a7ffSGuochun Huang } 110e2c0a7ffSGuochun Huang 111e2c0a7ffSGuochun Huang static void max96755f_bridge_disable(struct rockchip_bridge *bridge) 112e2c0a7ffSGuochun Huang { 113e2c0a7ffSGuochun Huang struct udevice *dev = bridge->dev; 114e2c0a7ffSGuochun Huang struct max96755f_priv *priv = dev_get_priv(dev->parent); 115e2c0a7ffSGuochun Huang 116e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x0002, VID_TX_EN_X, 117e2c0a7ffSGuochun Huang FIELD_PREP(VID_TX_EN_X, 0)); 118e2c0a7ffSGuochun Huang 119e2c0a7ffSGuochun Huang if (priv->split_mode) 120e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x0010, 121e2c0a7ffSGuochun Huang AUTO_LINK | LINK_CFG, 122e2c0a7ffSGuochun Huang FIELD_PREP(AUTO_LINK, 0) | 123e2c0a7ffSGuochun Huang FIELD_PREP(LINK_CFG, LINKA)); 124e2c0a7ffSGuochun Huang } 125e2c0a7ffSGuochun Huang 126e2c0a7ffSGuochun Huang static void max96755f_bridge_mode_set(struct rockchip_bridge *bridge, 127e2c0a7ffSGuochun Huang const struct drm_display_mode *mode) 128e2c0a7ffSGuochun Huang { 129e2c0a7ffSGuochun Huang struct udevice *dev = bridge->dev; 130e2c0a7ffSGuochun Huang struct max96755f_priv *priv = dev_get_priv(dev->parent); 131e2c0a7ffSGuochun Huang 132e2c0a7ffSGuochun Huang memcpy(&priv->mode, mode, sizeof(struct drm_display_mode)); 133e2c0a7ffSGuochun Huang } 134e2c0a7ffSGuochun Huang 135f1efa5adSGuochun Huang static bool max96755f_bridge_detect(struct rockchip_bridge *bridge) 136f1efa5adSGuochun Huang { 137f1efa5adSGuochun Huang struct max96755f_priv *priv = dev_get_priv(bridge->dev->parent); 138f1efa5adSGuochun Huang 139f1efa5adSGuochun Huang if (!dm_gpio_get_value(&priv->lock_gpio)) 140f1efa5adSGuochun Huang return false; 141f1efa5adSGuochun Huang 142f1efa5adSGuochun Huang return true; 143f1efa5adSGuochun Huang } 144f1efa5adSGuochun Huang 145e2c0a7ffSGuochun Huang static const struct rockchip_bridge_funcs max96755f_bridge_funcs = { 146e2c0a7ffSGuochun Huang .enable = max96755f_bridge_enable, 147e2c0a7ffSGuochun Huang .disable = max96755f_bridge_disable, 148e2c0a7ffSGuochun Huang .mode_set = max96755f_bridge_mode_set, 149f1efa5adSGuochun Huang .detect = max96755f_bridge_detect, 150e2c0a7ffSGuochun Huang }; 151e2c0a7ffSGuochun Huang 152*59487b18SGuochun Huang static int max96755f_bridge_bind(struct udevice *dev) 153*59487b18SGuochun Huang { 154*59487b18SGuochun Huang struct mipi_dsi_device *device = dev_get_platdata(dev); 155*59487b18SGuochun Huang 156*59487b18SGuochun Huang device->dev = dev; 157*59487b18SGuochun Huang device->lanes = dev_read_u32_default(dev, "dsi,lanes", 4); 158*59487b18SGuochun Huang device->format = dev_read_u32_default(dev, "dsi,format", 159*59487b18SGuochun Huang MIPI_DSI_FMT_RGB888); 160*59487b18SGuochun Huang device->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE; 161*59487b18SGuochun Huang device->channel = dev_read_u32_default(dev, "reg", 0); 162*59487b18SGuochun Huang 163*59487b18SGuochun Huang return 0; 164*59487b18SGuochun Huang } 165*59487b18SGuochun Huang 166e2c0a7ffSGuochun Huang static int max96755f_bridge_probe(struct udevice *dev) 167e2c0a7ffSGuochun Huang { 168e2c0a7ffSGuochun Huang struct rockchip_bridge *bridge; 169e2c0a7ffSGuochun Huang struct max96755f_priv *priv = dev_get_priv(dev->parent); 170f1efa5adSGuochun Huang int ret; 171e2c0a7ffSGuochun Huang 172e2c0a7ffSGuochun Huang bridge = calloc(1, sizeof(*bridge)); 173e2c0a7ffSGuochun Huang if (!bridge) 174e2c0a7ffSGuochun Huang return -ENOMEM; 175e2c0a7ffSGuochun Huang 176e2c0a7ffSGuochun Huang dev->driver_data = (ulong)bridge; 177e2c0a7ffSGuochun Huang bridge->dev = dev; 178e2c0a7ffSGuochun Huang bridge->funcs = &max96755f_bridge_funcs; 179e2c0a7ffSGuochun Huang 180e2c0a7ffSGuochun Huang priv->num_lanes = dev_read_u32_default(dev, "dsi,lanes", 4); 181e2c0a7ffSGuochun Huang priv->dv_swp_ab = dev_read_bool(dev, "vd-swap-ab"); 182e2c0a7ffSGuochun Huang priv->dpi_deskew_en = dev_read_bool(dev, "dpi-deskew-en"); 183e2c0a7ffSGuochun Huang 184f1efa5adSGuochun Huang ret = gpio_request_by_name(dev, "lock-gpios", 0, &priv->lock_gpio, 185f1efa5adSGuochun Huang GPIOD_IS_IN); 186f1efa5adSGuochun Huang if (ret) { 187f1efa5adSGuochun Huang dev_err(dev, "failed to get lock GPIO: %d\n", ret); 188f1efa5adSGuochun Huang return ret; 189f1efa5adSGuochun Huang } 190f1efa5adSGuochun Huang 191e2c0a7ffSGuochun Huang return 0; 192e2c0a7ffSGuochun Huang } 193e2c0a7ffSGuochun Huang 194e2c0a7ffSGuochun Huang static const struct udevice_id max96755f_bridge_of_match[] = { 195e2c0a7ffSGuochun Huang { .compatible = "maxim,max96755f-bridge", }, 196e2c0a7ffSGuochun Huang { } 197e2c0a7ffSGuochun Huang }; 198e2c0a7ffSGuochun Huang 199e2c0a7ffSGuochun Huang U_BOOT_DRIVER(max96755f_bridge) = { 200e2c0a7ffSGuochun Huang .name = "max96755f_bridge", 201e2c0a7ffSGuochun Huang .id = UCLASS_VIDEO_BRIDGE, 202e2c0a7ffSGuochun Huang .of_match = max96755f_bridge_of_match, 203e2c0a7ffSGuochun Huang .probe = max96755f_bridge_probe, 204*59487b18SGuochun Huang .bind = max96755f_bridge_bind, 205*59487b18SGuochun Huang .platdata_auto_alloc_size = sizeof(struct mipi_dsi_device), 206e2c0a7ffSGuochun Huang }; 207