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