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