1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2021 Rockchip Electronics Co. Ltd. 4 * 5 * Author: Guochun Huang <hero.huang@rock-chips.com> 6 */ 7 8 #include "rk628.h" 9 #include "rk628_lvds.h" 10 #include "rk628_combtxphy.h" 11 #include "panel.h" 12 13 static inline void lvds_write(struct rk628 *rk628, u32 reg, u32 val) 14 { 15 rk628_i2c_write(rk628, reg, val); 16 } 17 18 static inline void lvds_update_bits(struct rk628 *rk628, u32 reg, 19 u32 mask, u32 val) 20 { 21 rk628_i2c_update_bits(rk628, reg, mask, val); 22 } 23 24 int rk628_lvds_parse(struct rk628 *rk628, ofnode lvds_np) 25 { 26 const char *string = NULL; 27 int ret; 28 29 if (!ofnode_valid(lvds_np)) 30 return -EINVAL; 31 32 string = ofnode_read_string(lvds_np, "bus-format"); 33 if (!strcmp(string, "jeida_24")) 34 rk628->lvds.format = LVDS_FORMAT_JEIDA_24BIT; 35 else if (!strcmp(string, "jeida_18")) 36 rk628->lvds.format = LVDS_FORMAT_JEIDA_18BIT; 37 else if (!strcmp(string, "vesa_18")) 38 rk628->lvds.format = LVDS_FORMAT_VESA_18BIT; 39 else 40 rk628->lvds.format = LVDS_FORMAT_VESA_24BIT; 41 42 string = ofnode_read_string(lvds_np, "link-type"); 43 if (!strcmp(string, "dual_link_odd_even_pixels")) 44 rk628->lvds.link_type = LVDS_DUAL_LINK_ODD_EVEN_PIXELS; 45 else if (!strcmp(string, "dual_link_even_odd_pixels")) 46 rk628->lvds.link_type = LVDS_DUAL_LINK_EVEN_ODD_PIXELS; 47 else if (!strcmp(string, "dual_link_left_right_pixels")) 48 rk628->lvds.link_type = LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS; 49 else if (!strcmp(string, "dual_link_right_left_pixels")) 50 rk628->lvds.link_type = LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS; 51 else 52 rk628->lvds.link_type = LVDS_SINGLE_LINK; 53 54 ret = rk628_panel_info_get(rk628, lvds_np); 55 if (ret) 56 return ret; 57 58 return 0; 59 } 60 61 void rk628_lvds_enable(struct rk628 *rk628) 62 { 63 enum lvds_link_type link_type = rk628->lvds.link_type; 64 enum lvds_format format = rk628->lvds.format; 65 const struct drm_display_mode *mode = &rk628->dst_mode; 66 u32 val, mask, bus_width; 67 68 mask = SW_OUTPUT_MODE_MASK; 69 val = SW_OUTPUT_MODE(OUTPUT_MODE_LVDS); 70 71 if (rk628->version == RK628F_VERSION) { 72 mask = SW_OUTPUT_COMBTX_MODE_MASK; 73 val = SW_OUTPUT_COMBTX_MODE(OUTPUT_MODE_LVDS); 74 } 75 76 lvds_update_bits(rk628, GRF_SYSTEM_CON0, mask, val); 77 78 switch (link_type) { 79 case LVDS_DUAL_LINK_ODD_EVEN_PIXELS: 80 val = SW_LVDS_CON_CHASEL(1) | SW_LVDS_CON_STARTSEL(0) | 81 SW_LVDS_CON_DUAL_SEL(0); 82 bus_width = COMBTXPHY_MODULEA_EN | COMBTXPHY_MODULEB_EN; 83 break; 84 case LVDS_DUAL_LINK_EVEN_ODD_PIXELS: 85 val = SW_LVDS_CON_CHASEL(1) | SW_LVDS_CON_STARTSEL(1) | 86 SW_LVDS_CON_DUAL_SEL(0); 87 bus_width = COMBTXPHY_MODULEA_EN | COMBTXPHY_MODULEB_EN; 88 break; 89 case LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS: 90 val = SW_LVDS_CON_CHASEL(1) | SW_LVDS_CON_STARTSEL(0) | 91 SW_LVDS_CON_DUAL_SEL(1); 92 lvds_update_bits(rk628, GRF_POST_PROC_CON, 93 SW_SPLIT_EN, SW_SPLIT_EN); 94 bus_width = COMBTXPHY_MODULEA_EN | COMBTXPHY_MODULEB_EN; 95 break; 96 case LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS: 97 val = SW_LVDS_CON_CHASEL(1) | SW_LVDS_CON_STARTSEL(1) | 98 SW_LVDS_CON_DUAL_SEL(1); 99 lvds_update_bits(rk628, GRF_POST_PROC_CON, 100 SW_SPLIT_EN, SW_SPLIT_EN); 101 bus_width = COMBTXPHY_MODULEA_EN | COMBTXPHY_MODULEB_EN; 102 break; 103 case LVDS_SINGLE_LINK: 104 default: 105 val = SW_LVDS_CON_CHASEL(0) | SW_LVDS_CON_STARTSEL(0) | 106 SW_LVDS_CON_DUAL_SEL(0); 107 bus_width = COMBTXPHY_MODULEA_EN; 108 break; 109 } 110 111 val |= SW_LVDS_CON_SELECT(format) | SW_LVDS_CON_MSBSEL(0) | 112 SW_LVDS_CON_CLKINV(0); 113 lvds_write(rk628, GRF_LVDS_TX_CON, val); 114 115 bus_width |= (mode->clock / 1000) << 8; 116 rk628_combtxphy_set_bus_width(rk628, bus_width); 117 rk628_combtxphy_set_mode(rk628, PHY_MODE_VIDEO_LVDS); 118 rk628_combtxphy_power_on(rk628); 119 rk628_panel_prepare(rk628); 120 rk628_panel_enable(rk628); 121 } 122 123 void rk628_lvds_disable(struct rk628 *rk628) 124 { 125 rk628_panel_disable(rk628); 126 rk628_panel_unprepare(rk628); 127 rk628_combtxphy_power_off(rk628); 128 } 129