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 enum lvds_format { 62 LVDS_8BIT_MODE_FORMAT_1, 63 LVDS_8BIT_MODE_FORMAT_2, 64 LVDS_8BIT_MODE_FORMAT_3, 65 LVDS_6BIT_MODE, 66 LVDS_10BIT_MODE_FORMAT_1, 67 LVDS_10BIT_MODE_FORMAT_2, 68 }; 69 70 struct rockchip_lvds; 71 72 struct rockchip_lvds_funcs { 73 void (*enable)(struct rockchip_lvds *lvds, int pipe); 74 void (*disable)(struct rockchip_lvds *lvds); 75 }; 76 77 struct rockchip_lvds { 78 struct udevice *dev; 79 struct regmap *grf; 80 struct rockchip_phy *phy; 81 const struct drm_display_mode *mode; 82 const struct rockchip_lvds_funcs *funcs; 83 enum lvds_format format; 84 bool data_swap; 85 bool dual_channel; 86 }; 87 88 static inline struct rockchip_lvds *state_to_lvds(struct display_state *state) 89 { 90 struct connector_state *conn_state = &state->conn_state; 91 92 return dev_get_priv(conn_state->dev); 93 } 94 95 static int rockchip_lvds_connector_init(struct display_state *state) 96 { 97 struct rockchip_lvds *lvds = state_to_lvds(state); 98 struct connector_state *conn_state = &state->conn_state; 99 struct rockchip_panel *panel = state_get_panel(state); 100 101 lvds->mode = &conn_state->mode; 102 lvds->phy = conn_state->phy; 103 104 switch (panel->bus_format) { 105 case MEDIA_BUS_FMT_RGB666_1X7X3_JEIDA: /* jeida-18 */ 106 lvds->format = LVDS_6BIT_MODE; 107 break; 108 case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: /* jeida-24 */ 109 lvds->format = LVDS_8BIT_MODE_FORMAT_2; 110 break; 111 case MEDIA_BUS_FMT_RGB101010_1X7X5_JEIDA: /* jeida-30 */ 112 lvds->format = LVDS_10BIT_MODE_FORMAT_1; 113 case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: /* vesa-18 */ 114 lvds->format = LVDS_8BIT_MODE_FORMAT_3; 115 break; 116 case MEDIA_BUS_FMT_RGB101010_1X7X5_SPWG: /* vesa-30 */ 117 lvds->format = LVDS_10BIT_MODE_FORMAT_2; 118 break; 119 case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: /* vesa-24 */ 120 default: 121 lvds->format = LVDS_8BIT_MODE_FORMAT_1; 122 break; 123 } 124 125 conn_state->type = DRM_MODE_CONNECTOR_LVDS; 126 conn_state->output_mode = ROCKCHIP_OUT_MODE_P888; 127 128 if ((lvds->format == LVDS_10BIT_MODE_FORMAT_1) || 129 (lvds->format == LVDS_10BIT_MODE_FORMAT_2)) 130 conn_state->output_mode = ROCKCHIP_OUT_MODE_AAAA; 131 132 conn_state->color_space = V4L2_COLORSPACE_DEFAULT; 133 134 return 0; 135 } 136 137 static int rockchip_lvds_connector_enable(struct display_state *state) 138 { 139 struct rockchip_lvds *lvds = state_to_lvds(state); 140 struct crtc_state *crtc_state = &state->crtc_state; 141 int pipe = crtc_state->crtc_id; 142 int ret; 143 144 if (lvds->funcs->enable) 145 lvds->funcs->enable(lvds, pipe); 146 147 ret = rockchip_phy_set_mode(lvds->phy, PHY_MODE_VIDEO_LVDS); 148 if (ret) { 149 dev_err(lvds->dev, "failed to set phy mode: %d\n", ret); 150 return ret; 151 } 152 153 rockchip_phy_power_on(lvds->phy); 154 155 return 0; 156 } 157 158 static int rockchip_lvds_connector_disable(struct display_state *state) 159 { 160 struct rockchip_lvds *lvds = state_to_lvds(state); 161 162 rockchip_phy_power_off(lvds->phy); 163 164 if (lvds->funcs->disable) 165 lvds->funcs->disable(lvds); 166 167 return 0; 168 } 169 170 static const struct rockchip_connector_funcs rockchip_lvds_connector_funcs = { 171 .init = rockchip_lvds_connector_init, 172 .enable = rockchip_lvds_connector_enable, 173 .disable = rockchip_lvds_connector_disable, 174 }; 175 176 static int rockchip_lvds_probe(struct udevice *dev) 177 { 178 struct rockchip_lvds *lvds = dev_get_priv(dev); 179 const struct rockchip_connector *connector = 180 (const struct rockchip_connector *)dev_get_driver_data(dev); 181 182 lvds->dev = dev; 183 lvds->funcs = connector->data; 184 lvds->grf = syscon_get_regmap(dev_get_parent(dev)); 185 lvds->dual_channel = dev_read_bool(dev, "dual-channel"); 186 lvds->data_swap = dev_read_bool(dev, "rockchip,data-swap"); 187 188 return 0; 189 } 190 191 static void px30_lvds_enable(struct rockchip_lvds *lvds, int pipe) 192 { 193 regmap_write(lvds->grf, PX30_GRF_PD_VO_CON1, 194 PX30_LVDS_SELECT(lvds->format) | 195 PX30_LVDS_MODE_EN(1) | PX30_LVDS_MSBSEL(1) | 196 PX30_LVDS_P2S_EN(1) | PX30_LVDS_VOP_SEL(pipe)); 197 } 198 199 static void px30_lvds_disable(struct rockchip_lvds *lvds) 200 { 201 regmap_write(lvds->grf, PX30_GRF_PD_VO_CON1, 202 PX30_LVDS_MODE_EN(0) | PX30_LVDS_P2S_EN(0)); 203 } 204 205 static const struct rockchip_lvds_funcs px30_lvds_funcs = { 206 .enable = px30_lvds_enable, 207 .disable = px30_lvds_disable, 208 }; 209 210 static const struct rockchip_connector px30_lvds_driver_data = { 211 .funcs = &rockchip_lvds_connector_funcs, 212 .data = &px30_lvds_funcs, 213 }; 214 215 static void rk3126_lvds_enable(struct rockchip_lvds *lvds, int pipe) 216 { 217 regmap_write(lvds->grf, RK3126_GRF_LVDS_CON0, 218 RK3126_LVDS_P2S_EN(1) | RK3126_LVDS_MODE_EN(1) | 219 RK3126_LVDS_MSBSEL(1) | RK3126_LVDS_SELECT(lvds->format)); 220 } 221 222 static void rk3126_lvds_disable(struct rockchip_lvds *lvds) 223 { 224 regmap_write(lvds->grf, RK3126_GRF_LVDS_CON0, 225 RK3126_LVDS_P2S_EN(0) | RK3126_LVDS_MODE_EN(0)); 226 } 227 228 static const struct rockchip_lvds_funcs rk3126_lvds_funcs = { 229 .enable = rk3126_lvds_enable, 230 .disable = rk3126_lvds_disable, 231 }; 232 233 static const struct rockchip_connector rk3126_lvds_driver_data = { 234 .funcs = &rockchip_lvds_connector_funcs, 235 .data = &rk3126_lvds_funcs, 236 }; 237 238 static void rk3288_lvds_enable(struct rockchip_lvds *lvds, int pipe) 239 { 240 const struct drm_display_mode *mode = lvds->mode; 241 u32 val; 242 243 regmap_write(lvds->grf, RK3288_GRF_SOC_CON6, 244 RK3288_LVDS_LCDC_SEL(pipe)); 245 246 val = RK3288_LVDS_PWRDWN(0) | RK3288_LVDS_CON_CLKINV(0) | 247 RK3288_LVDS_CON_CHASEL(lvds->dual_channel) | 248 RK3288_LVDS_CON_SELECT(lvds->format); 249 250 if (lvds->dual_channel) { 251 u32 h_bp = mode->htotal - mode->hsync_start; 252 253 val |= RK3288_LVDS_CON_ENABLE_2(1) | 254 RK3288_LVDS_CON_ENABLE_1(1) | 255 RK3288_LVDS_CON_STARTSEL(lvds->data_swap); 256 257 if (h_bp % 2) 258 val |= RK3288_LVDS_CON_STARTPHASE(1); 259 else 260 val |= RK3288_LVDS_CON_STARTPHASE(0); 261 } else { 262 val |= RK3288_LVDS_CON_ENABLE_2(0) | 263 RK3288_LVDS_CON_ENABLE_1(1); 264 } 265 266 regmap_write(lvds->grf, RK3288_GRF_SOC_CON7, val); 267 268 rockchip_phy_set_bus_width(lvds->phy, lvds->dual_channel ? 2 : 1); 269 } 270 271 static void rk3288_lvds_disable(struct rockchip_lvds *lvds) 272 { 273 regmap_write(lvds->grf, RK3288_GRF_SOC_CON7, RK3288_LVDS_PWRDWN(1)); 274 } 275 276 static const struct rockchip_lvds_funcs rk3288_lvds_funcs = { 277 .enable = rk3288_lvds_enable, 278 .disable = rk3288_lvds_disable, 279 }; 280 281 static const struct rockchip_connector rk3288_lvds_driver_data = { 282 .funcs = &rockchip_lvds_connector_funcs, 283 .data = &rk3288_lvds_funcs, 284 }; 285 286 static void rk3368_lvds_enable(struct rockchip_lvds *lvds, int pipe) 287 { 288 regmap_write(lvds->grf, RK3368_GRF_SOC_CON7, 289 RK3368_LVDS_SELECT(lvds->format) | 290 RK3368_LVDS_MODE_EN(1) | RK3368_LVDS_MSBSEL(1) | 291 RK3368_LVDS_P2S_EN(1)); 292 } 293 294 static void rk3368_lvds_disable(struct rockchip_lvds *lvds) 295 { 296 regmap_write(lvds->grf, RK3368_GRF_SOC_CON7, 297 RK3368_LVDS_MODE_EN(0) | RK3368_LVDS_P2S_EN(0)); 298 } 299 300 static const struct rockchip_lvds_funcs rk3368_lvds_funcs = { 301 .enable = rk3368_lvds_enable, 302 .disable = rk3368_lvds_disable, 303 }; 304 305 static const struct rockchip_connector rk3368_lvds_driver_data = { 306 .funcs = &rockchip_lvds_connector_funcs, 307 .data = &rk3368_lvds_funcs, 308 }; 309 310 static const struct udevice_id rockchip_lvds_ids[] = { 311 { 312 .compatible = "rockchip,px30-lvds", 313 .data = (ulong)&px30_lvds_driver_data, 314 }, 315 { 316 .compatible = "rockchip,rk3126-lvds", 317 .data = (ulong)&rk3126_lvds_driver_data, 318 }, 319 { 320 .compatible = "rockchip,rk3288-lvds", 321 .data = (ulong)&rk3288_lvds_driver_data, 322 }, 323 { 324 .compatible = "rockchip,rk3368-lvds", 325 .data = (ulong)&rk3368_lvds_driver_data, 326 }, 327 {} 328 }; 329 330 U_BOOT_DRIVER(rockchip_lvds) = { 331 .name = "rockchip_lvds", 332 .id = UCLASS_DISPLAY, 333 .of_match = rockchip_lvds_ids, 334 .probe = rockchip_lvds_probe, 335 .priv_auto_alloc_size = sizeof(struct rockchip_lvds), 336 }; 337