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 <dm/of_access.h> 14 #include <syscon.h> 15 #include <regmap.h> 16 #include <dm/device.h> 17 #include <dm/read.h> 18 #include <linux/media-bus-format.h> 19 20 #include "rockchip_display.h" 21 #include "rockchip_connector.h" 22 #include "rockchip_phy.h" 23 #include "rockchip_panel.h" 24 25 #define HIWORD_UPDATE(v, h, l) (((v) << (l)) | (GENMASK(h, l) << 16)) 26 27 #define PX30_GRF_PD_VO_CON1 0x0438 28 #define PX30_LVDS_SELECT(x) HIWORD_UPDATE(x, 14, 13) 29 #define PX30_LVDS_MODE_EN(x) HIWORD_UPDATE(x, 12, 12) 30 #define PX30_LVDS_MSBSEL(x) HIWORD_UPDATE(x, 11, 11) 31 #define PX30_LVDS_P2S_EN(x) HIWORD_UPDATE(x, 6, 6) 32 #define PX30_LVDS_VOP_SEL(x) HIWORD_UPDATE(x, 1, 1) 33 34 #define RK3126_GRF_LVDS_CON0 0x0150 35 #define RK3126_LVDS_P2S_EN(x) HIWORD_UPDATE(x, 9, 9) 36 #define RK3126_LVDS_MODE_EN(x) HIWORD_UPDATE(x, 6, 6) 37 #define RK3126_LVDS_MSBSEL(x) HIWORD_UPDATE(x, 3, 3) 38 #define RK3126_LVDS_SELECT(x) HIWORD_UPDATE(x, 2, 1) 39 40 #define RK3288_GRF_SOC_CON6 0x025c 41 #define RK3288_LVDS_LCDC_SEL(x) HIWORD_UPDATE(x, 3, 3) 42 #define RK3288_GRF_SOC_CON7 0x0260 43 #define RK3288_LVDS_PWRDWN(x) HIWORD_UPDATE(x, 15, 15) 44 #define RK3288_LVDS_CON_ENABLE_2(x) HIWORD_UPDATE(x, 12, 12) 45 #define RK3288_LVDS_CON_ENABLE_1(x) HIWORD_UPDATE(x, 11, 11) 46 #define RK3288_LVDS_CON_DEN_POL(x) HIWORD_UPDATE(x, 10, 10) 47 #define RK3288_LVDS_CON_HS_POL(x) HIWORD_UPDATE(x, 9, 9) 48 #define RK3288_LVDS_CON_CLKINV(x) HIWORD_UPDATE(x, 8, 8) 49 #define RK3288_LVDS_CON_STARTPHASE(x) HIWORD_UPDATE(x, 7, 7) 50 #define RK3288_LVDS_CON_TTL_EN(x) HIWORD_UPDATE(x, 6, 6) 51 #define RK3288_LVDS_CON_STARTSEL(x) HIWORD_UPDATE(x, 5, 5) 52 #define RK3288_LVDS_CON_CHASEL(x) HIWORD_UPDATE(x, 4, 4) 53 #define RK3288_LVDS_CON_MSBSEL(x) HIWORD_UPDATE(x, 3, 3) 54 #define RK3288_LVDS_CON_SELECT(x) HIWORD_UPDATE(x, 2, 0) 55 56 #define RK3368_GRF_SOC_CON7 0x041c 57 #define RK3368_LVDS_SELECT(x) HIWORD_UPDATE(x, 14, 13) 58 #define RK3368_LVDS_MODE_EN(x) HIWORD_UPDATE(x, 12, 12) 59 #define RK3368_LVDS_MSBSEL(x) HIWORD_UPDATE(x, 11, 11) 60 #define RK3368_LVDS_P2S_EN(x) HIWORD_UPDATE(x, 6, 6) 61 62 #define RK3568_GRF_VO_CON0 0x0360 63 #define RK3568_LVDS1_SELECT(x) HIWORD_UPDATE(x, 13, 12) 64 #define RK3568_LVDS1_MSBSEL(x) HIWORD_UPDATE(x, 11, 11) 65 #define RK3568_LVDS0_SELECT(x) HIWORD_UPDATE(x, 5, 4) 66 #define RK3568_LVDS0_MSBSEL(x) HIWORD_UPDATE(x, 3, 3) 67 #define RK3568_GRF_VO_CON2 0x0368 68 #define RK3568_LVDS0_DCLK_INV_SEL(x) HIWORD_UPDATE(x, 9, 9) 69 #define RK3568_LVDS0_DCLK_DIV2_SEL(x) HIWORD_UPDATE(x, 8, 8) 70 #define RK3568_LVDS0_MODE_EN(x) HIWORD_UPDATE(x, 1, 1) 71 #define RK3568_LVDS0_P2S_EN(x) HIWORD_UPDATE(x, 0, 0) 72 #define RK3568_GRF_VO_CON3 0x036c 73 #define RK3568_LVDS1_DCLK_INV_SEL(x) HIWORD_UPDATE(x, 9, 9) 74 #define RK3568_LVDS1_DCLK_DIV2_SEL(x) HIWORD_UPDATE(x, 8, 8) 75 #define RK3568_LVDS1_MODE_EN(x) HIWORD_UPDATE(x, 1, 1) 76 #define RK3568_LVDS1_P2S_EN(x) HIWORD_UPDATE(x, 0, 0) 77 78 enum lvds_format { 79 LVDS_8BIT_MODE_FORMAT_1, 80 LVDS_8BIT_MODE_FORMAT_2, 81 LVDS_8BIT_MODE_FORMAT_3, 82 LVDS_6BIT_MODE, 83 LVDS_10BIT_MODE_FORMAT_1, 84 LVDS_10BIT_MODE_FORMAT_2, 85 }; 86 87 struct rockchip_lvds; 88 89 struct rockchip_lvds_funcs { 90 void (*enable)(struct rockchip_lvds *lvds, int pipe); 91 void (*disable)(struct rockchip_lvds *lvds); 92 }; 93 94 struct rockchip_lvds { 95 int id; 96 struct udevice *dev; 97 struct regmap *grf; 98 struct rockchip_phy *phy; 99 const struct drm_display_mode *mode; 100 const struct rockchip_lvds_funcs *funcs; 101 enum lvds_format format; 102 bool data_swap; 103 bool dual_channel; 104 }; 105 106 static inline struct rockchip_lvds *state_to_lvds(struct display_state *state) 107 { 108 struct connector_state *conn_state = &state->conn_state; 109 110 return dev_get_priv(conn_state->dev); 111 } 112 113 static int rockchip_lvds_connector_pre_init(struct display_state *state) 114 { 115 struct connector_state *conn_state = &state->conn_state; 116 117 conn_state->type = DRM_MODE_CONNECTOR_LVDS; 118 119 return 0; 120 } 121 122 static int rockchip_lvds_connector_init(struct display_state *state) 123 { 124 struct rockchip_lvds *lvds = state_to_lvds(state); 125 struct connector_state *conn_state = &state->conn_state; 126 struct rockchip_panel *panel = state_get_panel(state); 127 128 lvds->mode = &conn_state->mode; 129 lvds->phy = conn_state->phy; 130 conn_state->disp_info = rockchip_get_disp_info(conn_state->type, lvds->id); 131 132 switch (panel->bus_format) { 133 case MEDIA_BUS_FMT_RGB666_1X7X3_JEIDA: /* jeida-18 */ 134 lvds->format = LVDS_6BIT_MODE; 135 break; 136 case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: /* jeida-24 */ 137 lvds->format = LVDS_8BIT_MODE_FORMAT_2; 138 break; 139 case MEDIA_BUS_FMT_RGB101010_1X7X5_JEIDA: /* jeida-30 */ 140 lvds->format = LVDS_10BIT_MODE_FORMAT_1; 141 case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: /* vesa-18 */ 142 lvds->format = LVDS_8BIT_MODE_FORMAT_3; 143 break; 144 case MEDIA_BUS_FMT_RGB101010_1X7X5_SPWG: /* vesa-30 */ 145 lvds->format = LVDS_10BIT_MODE_FORMAT_2; 146 break; 147 case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: /* vesa-24 */ 148 default: 149 lvds->format = LVDS_8BIT_MODE_FORMAT_1; 150 break; 151 } 152 153 conn_state->output_mode = ROCKCHIP_OUT_MODE_P888; 154 155 if ((lvds->format == LVDS_10BIT_MODE_FORMAT_1) || 156 (lvds->format == LVDS_10BIT_MODE_FORMAT_2)) 157 conn_state->output_mode = ROCKCHIP_OUT_MODE_AAAA; 158 159 conn_state->color_space = V4L2_COLORSPACE_DEFAULT; 160 conn_state->output_if = VOP_OUTPUT_IF_LVDS0; 161 162 return 0; 163 } 164 165 static int rockchip_lvds_connector_enable(struct display_state *state) 166 { 167 struct rockchip_lvds *lvds = state_to_lvds(state); 168 struct crtc_state *crtc_state = &state->crtc_state; 169 int pipe = crtc_state->crtc_id; 170 int ret; 171 172 if (lvds->funcs->enable) 173 lvds->funcs->enable(lvds, pipe); 174 175 ret = rockchip_phy_set_mode(lvds->phy, PHY_MODE_VIDEO_LVDS); 176 if (ret) { 177 dev_err(lvds->dev, "failed to set phy mode: %d\n", ret); 178 return ret; 179 } 180 181 rockchip_phy_power_on(lvds->phy); 182 183 return 0; 184 } 185 186 static int rockchip_lvds_connector_disable(struct display_state *state) 187 { 188 struct rockchip_lvds *lvds = state_to_lvds(state); 189 190 rockchip_phy_power_off(lvds->phy); 191 192 if (lvds->funcs->disable) 193 lvds->funcs->disable(lvds); 194 195 return 0; 196 } 197 198 static const struct rockchip_connector_funcs rockchip_lvds_connector_funcs = { 199 .pre_init = rockchip_lvds_connector_pre_init, 200 .init = rockchip_lvds_connector_init, 201 .enable = rockchip_lvds_connector_enable, 202 .disable = rockchip_lvds_connector_disable, 203 }; 204 205 static int rockchip_lvds_probe(struct udevice *dev) 206 { 207 struct rockchip_lvds *lvds = dev_get_priv(dev); 208 const struct rockchip_connector *connector = 209 (const struct rockchip_connector *)dev_get_driver_data(dev); 210 211 lvds->dev = dev; 212 lvds->funcs = connector->data; 213 lvds->grf = syscon_get_regmap(dev_get_parent(dev)); 214 lvds->dual_channel = dev_read_bool(dev, "dual-channel"); 215 lvds->data_swap = dev_read_bool(dev, "rockchip,data-swap"); 216 lvds->id = of_alias_get_id(ofnode_to_np(dev->node), "lvds"); 217 if (lvds->id < 0) 218 lvds->id = 0; 219 220 return 0; 221 } 222 223 static void px30_lvds_enable(struct rockchip_lvds *lvds, int pipe) 224 { 225 regmap_write(lvds->grf, PX30_GRF_PD_VO_CON1, 226 PX30_LVDS_SELECT(lvds->format) | 227 PX30_LVDS_MODE_EN(1) | PX30_LVDS_MSBSEL(1) | 228 PX30_LVDS_P2S_EN(1) | PX30_LVDS_VOP_SEL(pipe)); 229 } 230 231 static void px30_lvds_disable(struct rockchip_lvds *lvds) 232 { 233 regmap_write(lvds->grf, PX30_GRF_PD_VO_CON1, 234 PX30_LVDS_MODE_EN(0) | PX30_LVDS_P2S_EN(0)); 235 } 236 237 static const struct rockchip_lvds_funcs px30_lvds_funcs = { 238 .enable = px30_lvds_enable, 239 .disable = px30_lvds_disable, 240 }; 241 242 static const struct rockchip_connector px30_lvds_driver_data = { 243 .funcs = &rockchip_lvds_connector_funcs, 244 .data = &px30_lvds_funcs, 245 }; 246 247 static void rk3126_lvds_enable(struct rockchip_lvds *lvds, int pipe) 248 { 249 regmap_write(lvds->grf, RK3126_GRF_LVDS_CON0, 250 RK3126_LVDS_P2S_EN(1) | RK3126_LVDS_MODE_EN(1) | 251 RK3126_LVDS_MSBSEL(1) | RK3126_LVDS_SELECT(lvds->format)); 252 } 253 254 static void rk3126_lvds_disable(struct rockchip_lvds *lvds) 255 { 256 regmap_write(lvds->grf, RK3126_GRF_LVDS_CON0, 257 RK3126_LVDS_P2S_EN(0) | RK3126_LVDS_MODE_EN(0)); 258 } 259 260 static const struct rockchip_lvds_funcs rk3126_lvds_funcs = { 261 .enable = rk3126_lvds_enable, 262 .disable = rk3126_lvds_disable, 263 }; 264 265 static const struct rockchip_connector rk3126_lvds_driver_data = { 266 .funcs = &rockchip_lvds_connector_funcs, 267 .data = &rk3126_lvds_funcs, 268 }; 269 270 static void rk3288_lvds_enable(struct rockchip_lvds *lvds, int pipe) 271 { 272 const struct drm_display_mode *mode = lvds->mode; 273 u32 val; 274 275 regmap_write(lvds->grf, RK3288_GRF_SOC_CON6, 276 RK3288_LVDS_LCDC_SEL(pipe)); 277 278 val = RK3288_LVDS_PWRDWN(0) | RK3288_LVDS_CON_CLKINV(0) | 279 RK3288_LVDS_CON_CHASEL(lvds->dual_channel) | 280 RK3288_LVDS_CON_SELECT(lvds->format); 281 282 if (lvds->dual_channel) { 283 u32 h_bp = mode->htotal - mode->hsync_start; 284 285 val |= RK3288_LVDS_CON_ENABLE_2(1) | 286 RK3288_LVDS_CON_ENABLE_1(1) | 287 RK3288_LVDS_CON_STARTSEL(lvds->data_swap); 288 289 if (h_bp % 2) 290 val |= RK3288_LVDS_CON_STARTPHASE(1); 291 else 292 val |= RK3288_LVDS_CON_STARTPHASE(0); 293 } else { 294 val |= RK3288_LVDS_CON_ENABLE_2(0) | 295 RK3288_LVDS_CON_ENABLE_1(1); 296 } 297 298 regmap_write(lvds->grf, RK3288_GRF_SOC_CON7, val); 299 300 rockchip_phy_set_bus_width(lvds->phy, lvds->dual_channel ? 2 : 1); 301 } 302 303 static void rk3288_lvds_disable(struct rockchip_lvds *lvds) 304 { 305 regmap_write(lvds->grf, RK3288_GRF_SOC_CON7, RK3288_LVDS_PWRDWN(1)); 306 } 307 308 static const struct rockchip_lvds_funcs rk3288_lvds_funcs = { 309 .enable = rk3288_lvds_enable, 310 .disable = rk3288_lvds_disable, 311 }; 312 313 static const struct rockchip_connector rk3288_lvds_driver_data = { 314 .funcs = &rockchip_lvds_connector_funcs, 315 .data = &rk3288_lvds_funcs, 316 }; 317 318 static void rk3368_lvds_enable(struct rockchip_lvds *lvds, int pipe) 319 { 320 regmap_write(lvds->grf, RK3368_GRF_SOC_CON7, 321 RK3368_LVDS_SELECT(lvds->format) | 322 RK3368_LVDS_MODE_EN(1) | RK3368_LVDS_MSBSEL(1) | 323 RK3368_LVDS_P2S_EN(1)); 324 } 325 326 static void rk3368_lvds_disable(struct rockchip_lvds *lvds) 327 { 328 regmap_write(lvds->grf, RK3368_GRF_SOC_CON7, 329 RK3368_LVDS_MODE_EN(0) | RK3368_LVDS_P2S_EN(0)); 330 } 331 332 static const struct rockchip_lvds_funcs rk3368_lvds_funcs = { 333 .enable = rk3368_lvds_enable, 334 .disable = rk3368_lvds_disable, 335 }; 336 337 static const struct rockchip_connector rk3368_lvds_driver_data = { 338 .funcs = &rockchip_lvds_connector_funcs, 339 .data = &rk3368_lvds_funcs, 340 }; 341 342 static void rk3568_lvds_enable(struct rockchip_lvds *lvds, int pipe) 343 { 344 regmap_write(lvds->grf, RK3568_GRF_VO_CON2, 345 RK3568_LVDS0_MODE_EN(1) | RK3568_LVDS0_P2S_EN(1) | 346 RK3568_LVDS0_DCLK_INV_SEL(1)); 347 regmap_write(lvds->grf, RK3568_GRF_VO_CON0, 348 RK3568_LVDS0_SELECT(lvds->format) | RK3568_LVDS0_MSBSEL(1)); 349 } 350 351 static void rk3568_lvds_disable(struct rockchip_lvds *lvds) 352 { 353 regmap_write(lvds->grf, RK3568_GRF_VO_CON2, RK3568_LVDS0_MODE_EN(0)); 354 } 355 356 static const struct rockchip_lvds_funcs rk3568_lvds_funcs = { 357 .enable = rk3568_lvds_enable, 358 .disable = rk3568_lvds_disable, 359 }; 360 361 static const struct rockchip_connector rk3568_lvds_driver_data = { 362 .funcs = &rockchip_lvds_connector_funcs, 363 .data = &rk3568_lvds_funcs, 364 }; 365 366 static const struct udevice_id rockchip_lvds_ids[] = { 367 { 368 .compatible = "rockchip,px30-lvds", 369 .data = (ulong)&px30_lvds_driver_data, 370 }, 371 { 372 .compatible = "rockchip,rk3126-lvds", 373 .data = (ulong)&rk3126_lvds_driver_data, 374 }, 375 { 376 .compatible = "rockchip,rk3288-lvds", 377 .data = (ulong)&rk3288_lvds_driver_data, 378 }, 379 { 380 .compatible = "rockchip,rk3368-lvds", 381 .data = (ulong)&rk3368_lvds_driver_data, 382 }, 383 { 384 .compatible = "rockchip,rk3568-lvds", 385 .data = (ulong)&rk3568_lvds_driver_data, 386 }, 387 {} 388 }; 389 390 U_BOOT_DRIVER(rockchip_lvds) = { 391 .name = "rockchip_lvds", 392 .id = UCLASS_DISPLAY, 393 .of_match = rockchip_lvds_ids, 394 .probe = rockchip_lvds_probe, 395 .priv_auto_alloc_size = sizeof(struct rockchip_lvds), 396 }; 397