1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright 2016 Rockchip Inc. 4 */ 5 6 #include <common.h> 7 #include <display.h> 8 #include <dm.h> 9 #include <edid.h> 10 #include <panel.h> 11 #include <regmap.h> 12 #include <syscon.h> 13 #include <asm/gpio.h> 14 #include <asm/io.h> 15 #include <asm/arch/clock.h> 16 #include <asm/arch/lvds_rk3288.h> 17 #include <asm/arch/grf_rk3288.h> 18 #include <dt-bindings/clock/rk3288-cru.h> 19 #include <dt-bindings/video/rk3288.h> 20 21 DECLARE_GLOBAL_DATA_PTR; 22 23 /** 24 * struct rk_lvds_priv - private rockchip lvds display driver info 25 * 26 * @reg: LVDS register address 27 * @grf: GRF register 28 * @panel: Panel device that is used in driver 29 * 30 * @output: Output mode, decided single or double channel, 31 * LVDS or LVTLL 32 * @format: Data format that RGB data will packing as 33 */ 34 struct rk_lvds_priv { 35 void __iomem *regs; 36 struct rk3288_grf *grf; 37 struct udevice *panel; 38 39 int output; 40 int format; 41 }; 42 43 static inline void lvds_writel(struct rk_lvds_priv *lvds, u32 offset, u32 val) 44 { 45 writel(val, lvds->regs + offset); 46 47 writel(val, lvds->regs + offset + 0x100); 48 } 49 50 int rk_lvds_enable(struct udevice *dev, int panel_bpp, 51 const struct display_timing *edid) 52 { 53 struct rk_lvds_priv *priv = dev_get_priv(dev); 54 struct display_plat *uc_plat = dev_get_uclass_platdata(dev); 55 int ret = 0; 56 unsigned int val = 0; 57 58 ret = panel_enable_backlight(priv->panel); 59 if (ret) { 60 debug("%s: backlight error: %d\n", __func__, ret); 61 return ret; 62 } 63 64 /* Select the video source */ 65 if (uc_plat->source_id) 66 val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT | 67 (RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16); 68 else 69 val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16; 70 rk_setreg(&priv->grf->soc_con6, val); 71 72 /* Select data transfer format */ 73 val = priv->format; 74 if (priv->output == LVDS_OUTPUT_DUAL) 75 val |= LVDS_DUAL | LVDS_CH0_EN | LVDS_CH1_EN; 76 else if (priv->output == LVDS_OUTPUT_SINGLE) 77 val |= LVDS_CH0_EN; 78 else if (priv->output == LVDS_OUTPUT_RGB) 79 val |= LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN; 80 val |= (0xffff << 16); 81 rk_setreg(&priv->grf->soc_con7, val); 82 83 /* Enable LVDS PHY */ 84 if (priv->output == LVDS_OUTPUT_RGB) { 85 lvds_writel(priv, RK3288_LVDS_CH0_REG0, 86 RK3288_LVDS_CH0_REG0_TTL_EN | 87 RK3288_LVDS_CH0_REG0_LANECK_EN | 88 RK3288_LVDS_CH0_REG0_LANE4_EN | 89 RK3288_LVDS_CH0_REG0_LANE3_EN | 90 RK3288_LVDS_CH0_REG0_LANE2_EN | 91 RK3288_LVDS_CH0_REG0_LANE1_EN | 92 RK3288_LVDS_CH0_REG0_LANE0_EN); 93 lvds_writel(priv, RK3288_LVDS_CH0_REG2, 94 RK3288_LVDS_PLL_FBDIV_REG2(0x46)); 95 96 lvds_writel(priv, RK3288_LVDS_CH0_REG3, 97 RK3288_LVDS_PLL_FBDIV_REG3(0x46)); 98 lvds_writel(priv, RK3288_LVDS_CH0_REG4, 99 RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE | 100 RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE | 101 RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE | 102 RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE | 103 RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE | 104 RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE); 105 lvds_writel(priv, RK3288_LVDS_CH0_REG5, 106 RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA | 107 RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA | 108 RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA | 109 RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA | 110 RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA | 111 RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA); 112 lvds_writel(priv, RK3288_LVDS_CH0_REGD, 113 RK3288_LVDS_PLL_PREDIV_REGD(0x0a)); 114 lvds_writel(priv, RK3288_LVDS_CH0_REG20, 115 RK3288_LVDS_CH0_REG20_LSB); 116 } else { 117 lvds_writel(priv, RK3288_LVDS_CH0_REG0, 118 RK3288_LVDS_CH0_REG0_LVDS_EN | 119 RK3288_LVDS_CH0_REG0_LANECK_EN | 120 RK3288_LVDS_CH0_REG0_LANE4_EN | 121 RK3288_LVDS_CH0_REG0_LANE3_EN | 122 RK3288_LVDS_CH0_REG0_LANE2_EN | 123 RK3288_LVDS_CH0_REG0_LANE1_EN | 124 RK3288_LVDS_CH0_REG0_LANE0_EN); 125 lvds_writel(priv, RK3288_LVDS_CH0_REG1, 126 RK3288_LVDS_CH0_REG1_LANECK_BIAS | 127 RK3288_LVDS_CH0_REG1_LANE4_BIAS | 128 RK3288_LVDS_CH0_REG1_LANE3_BIAS | 129 RK3288_LVDS_CH0_REG1_LANE2_BIAS | 130 RK3288_LVDS_CH0_REG1_LANE1_BIAS | 131 RK3288_LVDS_CH0_REG1_LANE0_BIAS); 132 lvds_writel(priv, RK3288_LVDS_CH0_REG2, 133 RK3288_LVDS_CH0_REG2_RESERVE_ON | 134 RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE | 135 RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE | 136 RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE | 137 RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE | 138 RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE | 139 RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE | 140 RK3288_LVDS_PLL_FBDIV_REG2(0x46)); 141 lvds_writel(priv, RK3288_LVDS_CH0_REG3, 142 RK3288_LVDS_PLL_FBDIV_REG3(0x46)); 143 lvds_writel(priv, RK3288_LVDS_CH0_REG4, 0x00); 144 lvds_writel(priv, RK3288_LVDS_CH0_REG5, 0x00); 145 lvds_writel(priv, RK3288_LVDS_CH0_REGD, 146 RK3288_LVDS_PLL_PREDIV_REGD(0x0a)); 147 lvds_writel(priv, RK3288_LVDS_CH0_REG20, 148 RK3288_LVDS_CH0_REG20_LSB); 149 } 150 151 /* Power on */ 152 writel(RK3288_LVDS_CFG_REGC_PLL_ENABLE, 153 priv->regs + RK3288_LVDS_CFG_REGC); 154 155 writel(RK3288_LVDS_CFG_REG21_TX_ENABLE, 156 priv->regs + RK3288_LVDS_CFG_REG21); 157 158 return 0; 159 } 160 161 int rk_lvds_read_timing(struct udevice *dev, struct display_timing *timing) 162 { 163 if (fdtdec_decode_display_timing 164 (gd->fdt_blob, dev_of_offset(dev), 0, timing)) { 165 debug("%s: Failed to decode display timing\n", __func__); 166 return -EINVAL; 167 } 168 169 return 0; 170 } 171 172 static int rk_lvds_ofdata_to_platdata(struct udevice *dev) 173 { 174 struct rk_lvds_priv *priv = dev_get_priv(dev); 175 const void *blob = gd->fdt_blob; 176 int node = dev_of_offset(dev); 177 int ret; 178 priv->regs = (void *)devfdt_get_addr(dev); 179 priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); 180 181 ret = fdtdec_get_int(blob, node, "rockchip,output", -1); 182 if (ret != -1) { 183 priv->output = ret; 184 debug("LVDS output : %d\n", ret); 185 } else { 186 /* default set it as output rgb */ 187 priv->output = LVDS_OUTPUT_RGB; 188 } 189 190 ret = fdtdec_get_int(blob, node, "rockchip,data-mapping", -1); 191 if (ret != -1) { 192 priv->format = ret; 193 debug("LVDS data-mapping : %d\n", ret); 194 } else { 195 /* default set it as format jeida */ 196 priv->format = LVDS_FORMAT_JEIDA; 197 } 198 199 ret = fdtdec_get_int(blob, node, "rockchip,data-width", -1); 200 if (ret != -1) { 201 debug("LVDS data-width : %d\n", ret); 202 if (ret == 24) { 203 priv->format |= LVDS_24BIT; 204 } else if (ret == 18) { 205 priv->format |= LVDS_18BIT; 206 } else { 207 debug("rockchip-lvds unsupport data-width[%d]\n", ret); 208 ret = -EINVAL; 209 return ret; 210 } 211 } else { 212 priv->format |= LVDS_24BIT; 213 } 214 215 return 0; 216 } 217 218 int rk_lvds_probe(struct udevice *dev) 219 { 220 struct rk_lvds_priv *priv = dev_get_priv(dev); 221 int ret; 222 223 ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, "rockchip,panel", 224 &priv->panel); 225 if (ret) { 226 debug("%s: Cannot find panel for '%s' (ret=%d)\n", __func__, 227 dev->name, ret); 228 return ret; 229 } 230 231 return 0; 232 } 233 234 static const struct dm_display_ops lvds_rockchip_ops = { 235 .read_timing = rk_lvds_read_timing, 236 .enable = rk_lvds_enable, 237 }; 238 239 static const struct udevice_id rockchip_lvds_ids[] = { 240 {.compatible = "rockchip,rk3288-lvds"}, 241 {} 242 }; 243 244 U_BOOT_DRIVER(lvds_rockchip) = { 245 .name = "lvds_rockchip", 246 .id = UCLASS_DISPLAY, 247 .of_match = rockchip_lvds_ids, 248 .ops = &lvds_rockchip_ops, 249 .ofdata_to_platdata = rk_lvds_ofdata_to_platdata, 250 .probe = rk_lvds_probe, 251 .priv_auto_alloc_size = sizeof(struct rk_lvds_priv), 252 }; 253