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
lvds_write(struct rk628 * rk628,u32 reg,u32 val)13 static inline void lvds_write(struct rk628 *rk628, u32 reg, u32 val)
14 {
15 rk628_i2c_write(rk628, reg, val);
16 }
17
lvds_update_bits(struct rk628 * rk628,u32 reg,u32 mask,u32 val)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
rk628_lvds_parse(struct rk628 * rk628,ofnode lvds_np)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
rk628_lvds_enable(struct rk628 * rk628)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
rk628_lvds_disable(struct rk628 * rk628)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