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