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