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