1 /* 2 * (C) Copyright 2008-2017 Fuzhou Rockchip Electronics Co., Ltd 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <config.h> 8 #include <common.h> 9 #include <errno.h> 10 #include <dm/device.h> 11 #include <dm/read.h> 12 #include <dm/ofnode.h> 13 #include <syscon.h> 14 #include <regmap.h> 15 #include <dm/device.h> 16 #include <dm/read.h> 17 #include <linux/media-bus-format.h> 18 19 #include "rockchip_display.h" 20 #include "rockchip_connector.h" 21 #include "rockchip_phy.h" 22 #include "rockchip_panel.h" 23 #include "rockchip_lvds.h" 24 25 #define PX30_GRF_PD_VO_CON1 0x0438 26 #define PX30_LVDS_SELECT(x) HIWORD_UPDATE(x, 14, 13) 27 #define PX30_LVDS_MODE_EN(x) HIWORD_UPDATE(x, 12, 12) 28 #define PX30_LVDS_MSBSEL(x) HIWORD_UPDATE(x, 11, 11) 29 #define PX30_LVDS_P2S_EN(x) HIWORD_UPDATE(x, 10, 10) 30 #define PX30_LVDS_VOP_SEL(x) HIWORD_UPDATE(x, 1, 1) 31 32 #define RK3126_GRF_LVDS_CON0 0x0150 33 #define RK3126_LVDS_P2S_EN(x) HIWORD_UPDATE(x, 9, 9) 34 #define RK3126_LVDS_MODE_EN(x) HIWORD_UPDATE(x, 6, 6) 35 #define RK3126_LVDS_MSBSEL(x) HIWORD_UPDATE(x, 3, 3) 36 #define RK3126_LVDS_SELECT(x) HIWORD_UPDATE(x, 2, 1) 37 38 #define RK3288_GRF_SOC_CON6 0x025c 39 #define RK3288_LVDS_LCDC_SEL(x) HIWORD_UPDATE(x, 3, 3) 40 #define RK3288_GRF_SOC_CON7 0x0260 41 #define RK3288_LVDS_PWRDWN(x) HIWORD_UPDATE(x, 15, 15) 42 #define RK3288_LVDS_CON_ENABLE_2(x) HIWORD_UPDATE(x, 12, 12) 43 #define RK3288_LVDS_CON_ENABLE_1(x) HIWORD_UPDATE(x, 11, 11) 44 #define RK3288_LVDS_CON_DEN_POL(x) HIWORD_UPDATE(x, 10, 10) 45 #define RK3288_LVDS_CON_HS_POL(x) HIWORD_UPDATE(x, 9, 9) 46 #define RK3288_LVDS_CON_CLKINV(x) HIWORD_UPDATE(x, 8, 8) 47 #define RK3288_LVDS_CON_STARTPHASE(x) HIWORD_UPDATE(x, 7, 7) 48 #define RK3288_LVDS_CON_TTL_EN(x) HIWORD_UPDATE(x, 6, 6) 49 #define RK3288_LVDS_CON_STARTSEL(x) HIWORD_UPDATE(x, 5, 5) 50 #define RK3288_LVDS_CON_CHASEL(x) HIWORD_UPDATE(x, 4, 4) 51 #define RK3288_LVDS_CON_MSBSEL(x) HIWORD_UPDATE(x, 3, 3) 52 #define RK3288_LVDS_CON_SELECT(x) HIWORD_UPDATE(x, 2, 0) 53 54 #define RK3368_GRF_SOC_CON7 0x041c 55 #define RK3368_LVDS_SELECT(x) HIWORD_UPDATE(x, 14, 13) 56 #define RK3368_LVDS_MODE_EN(x) HIWORD_UPDATE(x, 12, 12) 57 #define RK3368_LVDS_MSBSEL(x) HIWORD_UPDATE(x, 11, 11) 58 #define RK3368_LVDS_P2S_EN(x) HIWORD_UPDATE(x, 6, 6) 59 60 enum lvds_format { 61 LVDS_8BIT_MODE_FORMAT_1, 62 LVDS_8BIT_MODE_FORMAT_2, 63 LVDS_8BIT_MODE_FORMAT_3, 64 LVDS_6BIT_MODE, 65 }; 66 67 struct rockchip_lvds; 68 69 struct rockchip_lvds_funcs { 70 void (*enable)(struct rockchip_lvds *lvds, int pipe); 71 void (*disable)(struct rockchip_lvds *lvds); 72 }; 73 74 struct rockchip_lvds { 75 struct udevice *dev; 76 struct regmap *grf; 77 struct rockchip_phy *phy; 78 const struct drm_display_mode *mode; 79 const struct rockchip_lvds_funcs *funcs; 80 enum lvds_format format; 81 bool data_swap; 82 bool dual_channel; 83 }; 84 85 static inline struct rockchip_lvds *state_to_lvds(struct display_state *state) 86 { 87 struct connector_state *conn_state = &state->conn_state; 88 89 return dev_get_priv(conn_state->dev); 90 } 91 92 static int rockchip_lvds_connector_init(struct display_state *state) 93 { 94 struct rockchip_lvds *lvds = state_to_lvds(state); 95 struct connector_state *conn_state = &state->conn_state; 96 struct rockchip_panel *panel = state_get_panel(state); 97 98 lvds->mode = &conn_state->mode; 99 lvds->phy = conn_state->phy; 100 101 switch (panel->bus_format) { 102 case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: /* jeida-18 */ 103 lvds->format = LVDS_6BIT_MODE; 104 break; 105 case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: /* jeida-24 */ 106 lvds->format = LVDS_8BIT_MODE_FORMAT_2; 107 break; 108 case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: /* vesa-24 */ 109 default: 110 lvds->format = LVDS_8BIT_MODE_FORMAT_1; 111 break; 112 } 113 114 conn_state->type = DRM_MODE_CONNECTOR_LVDS; 115 conn_state->output_mode = ROCKCHIP_OUT_MODE_P888; 116 conn_state->color_space = V4L2_COLORSPACE_DEFAULT; 117 118 return 0; 119 } 120 121 static int rockchip_lvds_connector_enable(struct display_state *state) 122 { 123 struct rockchip_lvds *lvds = state_to_lvds(state); 124 struct crtc_state *crtc_state = &state->crtc_state; 125 int pipe = crtc_state->crtc_id; 126 int ret; 127 128 if (lvds->funcs->enable) 129 lvds->funcs->enable(lvds, pipe); 130 131 ret = rockchip_phy_set_mode(lvds->phy, PHY_MODE_VIDEO_LVDS); 132 if (ret) { 133 dev_err(lvds->dev, "failed to set phy mode: %d\n", ret); 134 return ret; 135 } 136 137 rockchip_phy_power_on(lvds->phy); 138 139 return 0; 140 } 141 142 static int rockchip_lvds_connector_disable(struct display_state *state) 143 { 144 struct rockchip_lvds *lvds = state_to_lvds(state); 145 146 rockchip_phy_power_off(lvds->phy); 147 148 if (lvds->funcs->disable) 149 lvds->funcs->disable(lvds); 150 151 return 0; 152 } 153 154 static const struct rockchip_connector_funcs rockchip_lvds_connector_funcs = { 155 .init = rockchip_lvds_connector_init, 156 .enable = rockchip_lvds_connector_enable, 157 .disable = rockchip_lvds_connector_disable, 158 }; 159 160 static int rockchip_lvds_probe(struct udevice *dev) 161 { 162 struct rockchip_lvds *lvds = dev_get_priv(dev); 163 const struct rockchip_connector *connector = 164 (const struct rockchip_connector *)dev_get_driver_data(dev); 165 166 lvds->dev = dev; 167 lvds->funcs = connector->data; 168 lvds->grf = syscon_get_regmap(dev_get_parent(dev)); 169 lvds->dual_channel = dev_read_bool(dev, "dual-channel"); 170 lvds->data_swap = dev_read_bool(dev, "rockchip,data-swap"); 171 172 return 0; 173 } 174 175 static void px30_lvds_enable(struct rockchip_lvds *lvds, int pipe) 176 { 177 regmap_write(lvds->grf, PX30_GRF_PD_VO_CON1, 178 PX30_LVDS_SELECT(lvds->format) | 179 PX30_LVDS_MODE_EN(1) | PX30_LVDS_MSBSEL(1) | 180 PX30_LVDS_P2S_EN(1) | PX30_LVDS_VOP_SEL(pipe)); 181 } 182 183 static void px30_lvds_disable(struct rockchip_lvds *lvds) 184 { 185 regmap_write(lvds->grf, PX30_GRF_PD_VO_CON1, 186 PX30_LVDS_MODE_EN(0) | PX30_LVDS_P2S_EN(0)); 187 } 188 189 static const struct rockchip_lvds_funcs px30_lvds_funcs = { 190 .enable = px30_lvds_enable, 191 .disable = px30_lvds_disable, 192 }; 193 194 static const struct rockchip_connector px30_lvds_driver_data = { 195 .funcs = &rockchip_lvds_connector_funcs, 196 .data = &px30_lvds_funcs, 197 }; 198 199 static void rk3126_lvds_enable(struct rockchip_lvds *lvds, int pipe) 200 { 201 regmap_write(lvds->grf, RK3126_GRF_LVDS_CON0, 202 RK3126_LVDS_P2S_EN(1) | RK3126_LVDS_MODE_EN(1) | 203 RK3126_LVDS_MSBSEL(1) | RK3126_LVDS_SELECT(lvds->format)); 204 } 205 206 static void rk3126_lvds_disable(struct rockchip_lvds *lvds) 207 { 208 regmap_write(lvds->grf, RK3126_GRF_LVDS_CON0, 209 RK3126_LVDS_P2S_EN(0) | RK3126_LVDS_MODE_EN(0)); 210 } 211 212 static const struct rockchip_lvds_funcs rk3126_lvds_funcs = { 213 .enable = rk3126_lvds_enable, 214 .disable = rk3126_lvds_disable, 215 }; 216 217 static const struct rockchip_connector rk3126_lvds_driver_data = { 218 .funcs = &rockchip_lvds_connector_funcs, 219 .data = &rk3126_lvds_funcs, 220 }; 221 222 static void rk3288_lvds_enable(struct rockchip_lvds *lvds, int pipe) 223 { 224 const struct drm_display_mode *mode = lvds->mode; 225 u32 val; 226 227 regmap_write(lvds->grf, RK3288_GRF_SOC_CON6, 228 RK3288_LVDS_LCDC_SEL(pipe)); 229 230 val = RK3288_LVDS_PWRDWN(0) | RK3288_LVDS_CON_CLKINV(0) | 231 RK3288_LVDS_CON_CHASEL(lvds->dual_channel) | 232 RK3288_LVDS_CON_SELECT(lvds->format); 233 234 if (lvds->dual_channel) { 235 u32 h_bp = mode->htotal - mode->hsync_start; 236 237 val |= RK3288_LVDS_CON_ENABLE_2(1) | 238 RK3288_LVDS_CON_ENABLE_1(1) | 239 RK3288_LVDS_CON_STARTSEL(lvds->data_swap); 240 241 if (h_bp % 2) 242 val |= RK3288_LVDS_CON_STARTPHASE(1); 243 else 244 val |= RK3288_LVDS_CON_STARTPHASE(0); 245 } else { 246 val |= RK3288_LVDS_CON_ENABLE_2(0) | 247 RK3288_LVDS_CON_ENABLE_1(1); 248 } 249 250 regmap_write(lvds->grf, RK3288_GRF_SOC_CON7, val); 251 252 rockchip_phy_set_bus_width(lvds->phy, lvds->dual_channel ? 2 : 1); 253 } 254 255 static void rk3288_lvds_disable(struct rockchip_lvds *lvds) 256 { 257 regmap_write(lvds->grf, RK3288_GRF_SOC_CON7, RK3288_LVDS_PWRDWN(1)); 258 } 259 260 static const struct rockchip_lvds_funcs rk3288_lvds_funcs = { 261 .enable = rk3288_lvds_enable, 262 .disable = rk3288_lvds_disable, 263 }; 264 265 static const struct rockchip_connector rk3288_lvds_driver_data = { 266 .funcs = &rockchip_lvds_connector_funcs, 267 .data = &rk3288_lvds_funcs, 268 }; 269 270 static void rk3368_lvds_enable(struct rockchip_lvds *lvds, int pipe) 271 { 272 regmap_write(lvds->grf, RK3368_GRF_SOC_CON7, 273 RK3368_LVDS_SELECT(lvds->format) | 274 RK3368_LVDS_MODE_EN(1) | RK3368_LVDS_MSBSEL(1) | 275 RK3368_LVDS_P2S_EN(1)); 276 } 277 278 static void rk3368_lvds_disable(struct rockchip_lvds *lvds) 279 { 280 regmap_write(lvds->grf, RK3368_GRF_SOC_CON7, 281 RK3368_LVDS_MODE_EN(0) | RK3368_LVDS_P2S_EN(0)); 282 } 283 284 static const struct rockchip_lvds_funcs rk3368_lvds_funcs = { 285 .enable = rk3368_lvds_enable, 286 .disable = rk3368_lvds_disable, 287 }; 288 289 static const struct rockchip_connector rk3368_lvds_driver_data = { 290 .funcs = &rockchip_lvds_connector_funcs, 291 .data = &rk3368_lvds_funcs, 292 }; 293 294 static const struct udevice_id rockchip_lvds_ids[] = { 295 { 296 .compatible = "rockchip,px30-lvds", 297 .data = (ulong)&px30_lvds_driver_data, 298 }, 299 { 300 .compatible = "rockchip,rk3126-lvds", 301 .data = (ulong)&rk3126_lvds_driver_data, 302 }, 303 { 304 .compatible = "rockchip,rk3288-lvds", 305 .data = (ulong)&rk3288_lvds_driver_data, 306 }, 307 { 308 .compatible = "rockchip,rk3368-lvds", 309 .data = (ulong)&rk3368_lvds_driver_data, 310 }, 311 {} 312 }; 313 314 U_BOOT_DRIVER(rockchip_lvds) = { 315 .name = "rockchip_lvds", 316 .id = UCLASS_DISPLAY, 317 .of_match = rockchip_lvds_ids, 318 .probe = rockchip_lvds_probe, 319 .priv_auto_alloc_size = sizeof(struct rockchip_lvds), 320 }; 321