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