1e2c0a7ffSGuochun Huang // SPDX-License-Identifier: GPL-2.0+
2e2c0a7ffSGuochun Huang /*
3e2c0a7ffSGuochun Huang * (C) Copyright 2022 Rockchip Electronics Co., Ltd
4e2c0a7ffSGuochun Huang */
5e2c0a7ffSGuochun Huang
6e2c0a7ffSGuochun Huang #include <common.h>
7e2c0a7ffSGuochun Huang #include <dm.h>
8e2c0a7ffSGuochun Huang #include <errno.h>
9e2c0a7ffSGuochun Huang #include <i2c.h>
10e2c0a7ffSGuochun Huang #include <max96755f.h>
11e2c0a7ffSGuochun Huang #include <video_bridge.h>
1259487b18SGuochun Huang #include <drm/drm_mipi_dsi.h>
13e2c0a7ffSGuochun Huang #include <dm/of_access.h>
14e2c0a7ffSGuochun Huang #include <linux/media-bus-format.h>
15e2c0a7ffSGuochun Huang
16e2c0a7ffSGuochun Huang #include "rockchip_bridge.h"
17e2c0a7ffSGuochun Huang #include "rockchip_display.h"
18e2c0a7ffSGuochun Huang #include "rockchip_panel.h"
19e2c0a7ffSGuochun Huang
max96755f_mipi_dsi_rx_config(struct max96755f_priv * priv)20e2c0a7ffSGuochun Huang static void max96755f_mipi_dsi_rx_config(struct max96755f_priv *priv)
21e2c0a7ffSGuochun Huang {
22e2c0a7ffSGuochun Huang struct drm_display_mode *mode = &priv->mode;
23e2c0a7ffSGuochun Huang u32 hfp, hsa, hbp, hact;
24e2c0a7ffSGuochun Huang u32 vact, vsa, vfp, vbp;
25*da42fac8SGuochun Huang u8 lane_map;
26e2c0a7ffSGuochun Huang
27e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(priv->dev, 0x0331, NUM_LANES,
28e2c0a7ffSGuochun Huang FIELD_PREP(NUM_LANES, priv->num_lanes - 1));
29*da42fac8SGuochun Huang
30*da42fac8SGuochun Huang lane_map = (priv->dsi_lane_map[0] & 0xff) << 4 |
31*da42fac8SGuochun Huang (priv->dsi_lane_map[1] & 0xff) << 6 |
32*da42fac8SGuochun Huang (priv->dsi_lane_map[2] & 0xff) << 0 |
33*da42fac8SGuochun Huang (priv->dsi_lane_map[3] & 0xff) << 2;
34*da42fac8SGuochun Huang
35*da42fac8SGuochun Huang dm_i2c_reg_write(priv->dev, 0x0332, lane_map);
36*da42fac8SGuochun Huang
37e2c0a7ffSGuochun Huang if (!priv->dpi_deskew_en)
38e2c0a7ffSGuochun Huang return;
39e2c0a7ffSGuochun Huang
40e2c0a7ffSGuochun Huang vact = mode->vdisplay;
41e2c0a7ffSGuochun Huang vsa = mode->vsync_end - mode->vsync_start;
42e2c0a7ffSGuochun Huang vfp = mode->vsync_start - mode->vdisplay;
43e2c0a7ffSGuochun Huang vbp = mode->vtotal - mode->vsync_end;
44e2c0a7ffSGuochun Huang hact = mode->hdisplay;
45e2c0a7ffSGuochun Huang hsa = mode->hsync_end - mode->hsync_start;
46e2c0a7ffSGuochun Huang hfp = mode->hsync_start - mode->hdisplay;
47e2c0a7ffSGuochun Huang hbp = mode->htotal - mode->hsync_end;
48e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03A4, 0xc1);
49e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x0385, FIELD_PREP(DPI_HSYNC_WIDTH_L, hsa));
50e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x0386, FIELD_PREP(DPI_VYSNC_WIDTH_L, vsa));
51e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x0387,
52e2c0a7ffSGuochun Huang FIELD_PREP(DPI_VSYNC_WIDTH_H, (vsa >> 8)) |
53e2c0a7ffSGuochun Huang FIELD_PREP(DPI_HSYNC_WIDTH_H, (hsa >> 8)));
54e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03a5, FIELD_PREP(DPI_VFP_L, vfp));
55e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03a6,
56e2c0a7ffSGuochun Huang FIELD_PREP(DPI_VBP_L, vbp) |
57e2c0a7ffSGuochun Huang FIELD_PREP(DPI_VFP_H, (vfp >> 8)));
58e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03a7, FIELD_PREP(DPI_VBP_H, (vbp >> 4)));
59e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03a8, FIELD_PREP(DPI_VACT_L, vact));
60e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03a9, FIELD_PREP(DPI_VACT_H, (vact >> 8)));
61e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03aa, FIELD_PREP(DPI_HFP_L, hfp));
62e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03ab,
63e2c0a7ffSGuochun Huang FIELD_PREP(DPI_HBP_L, hbp) |
64e2c0a7ffSGuochun Huang FIELD_PREP(DPI_HFP_H, (hfp >> 7)));
65e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03ac, FIELD_PREP(DPI_HBP_H, (hbp >> 4)));
66e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03ad, FIELD_PREP(DPI_HACT_L, hact));
67e2c0a7ffSGuochun Huang dm_i2c_reg_write(priv->dev, 0x03ae, FIELD_PREP(DPI_HACT_H, (hact >> 8)));
68e2c0a7ffSGuochun Huang }
69e2c0a7ffSGuochun Huang
max96755f_bridge_enable(struct rockchip_bridge * bridge)70e2c0a7ffSGuochun Huang static void max96755f_bridge_enable(struct rockchip_bridge *bridge)
71e2c0a7ffSGuochun Huang {
72e2c0a7ffSGuochun Huang struct udevice *dev = bridge->dev;
73e2c0a7ffSGuochun Huang struct max96755f_priv *priv = dev_get_priv(dev->parent);
74e2c0a7ffSGuochun Huang
75e2c0a7ffSGuochun Huang max96755f_mipi_dsi_rx_config(priv);
76e2c0a7ffSGuochun Huang if (priv->split_mode) {
77e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x0010,
78e2c0a7ffSGuochun Huang RESET_ONESHOT | AUTO_LINK | LINK_CFG,
79e2c0a7ffSGuochun Huang FIELD_PREP(RESET_ONESHOT, 1) |
80e2c0a7ffSGuochun Huang FIELD_PREP(AUTO_LINK, 0) |
81e2c0a7ffSGuochun Huang FIELD_PREP(LINK_CFG, SPLITTER_MODE));
82e2c0a7ffSGuochun Huang mdelay(50);
83e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x0053,
84e2c0a7ffSGuochun Huang TX_SPLIT_MASK_B | TX_SPLIT_MASK_A | TX_STR_SEL,
85e2c0a7ffSGuochun Huang FIELD_PREP(TX_SPLIT_MASK_B, 0) |
86e2c0a7ffSGuochun Huang FIELD_PREP(TX_SPLIT_MASK_A, 1) |
87e2c0a7ffSGuochun Huang FIELD_PREP(TX_STR_SEL, 0));
88e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x0057,
89e2c0a7ffSGuochun Huang TX_SPLIT_MASK_B | TX_SPLIT_MASK_A | TX_STR_SEL,
90e2c0a7ffSGuochun Huang FIELD_PREP(TX_SPLIT_MASK_B, 1) |
91e2c0a7ffSGuochun Huang FIELD_PREP(TX_SPLIT_MASK_A, 0) |
92e2c0a7ffSGuochun Huang FIELD_PREP(TX_STR_SEL, 1));
93e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x032a,
94e2c0a7ffSGuochun Huang DV_SWP_AB | DV_CONV | DV_SPL | DV_EN,
95e2c0a7ffSGuochun Huang FIELD_PREP(DV_SWP_AB, priv->dv_swp_ab) |
96e2c0a7ffSGuochun Huang FIELD_PREP(DV_CONV, 1) |
97e2c0a7ffSGuochun Huang FIELD_PREP(DV_SPL, 1) |
98e2c0a7ffSGuochun Huang FIELD_PREP(DV_EN, 1));
99e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x0311,
100e2c0a7ffSGuochun Huang START_PORTAX | START_PORTAY,
101e2c0a7ffSGuochun Huang FIELD_PREP(START_PORTAX, 1) |
102e2c0a7ffSGuochun Huang FIELD_PREP(START_PORTAY, 1));
103e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x0002,
104e2c0a7ffSGuochun Huang VID_TX_EN_X | VID_TX_EN_Y,
105e2c0a7ffSGuochun Huang FIELD_PREP(VID_TX_EN_X, 1) |
106e2c0a7ffSGuochun Huang FIELD_PREP(VID_TX_EN_Y, 1));
107e2c0a7ffSGuochun Huang
108e2c0a7ffSGuochun Huang } else {
109e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x0002, VID_TX_EN_X,
110e2c0a7ffSGuochun Huang FIELD_PREP(VID_TX_EN_X, 1));
111e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x0311, START_PORTAX,
112e2c0a7ffSGuochun Huang FIELD_PREP(START_PORTAX, 1));
113e2c0a7ffSGuochun Huang }
114e2c0a7ffSGuochun Huang
115e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x0010, RESET_ONESHOT,
116e2c0a7ffSGuochun Huang FIELD_PREP(RESET_ONESHOT, 1));
117e2c0a7ffSGuochun Huang mdelay(100);
118e2c0a7ffSGuochun Huang }
119e2c0a7ffSGuochun Huang
max96755f_bridge_disable(struct rockchip_bridge * bridge)120e2c0a7ffSGuochun Huang static void max96755f_bridge_disable(struct rockchip_bridge *bridge)
121e2c0a7ffSGuochun Huang {
122e2c0a7ffSGuochun Huang struct udevice *dev = bridge->dev;
123e2c0a7ffSGuochun Huang struct max96755f_priv *priv = dev_get_priv(dev->parent);
124e2c0a7ffSGuochun Huang
125e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x0002, VID_TX_EN_X,
126e2c0a7ffSGuochun Huang FIELD_PREP(VID_TX_EN_X, 0));
127e2c0a7ffSGuochun Huang
128e2c0a7ffSGuochun Huang if (priv->split_mode)
129e2c0a7ffSGuochun Huang dm_i2c_reg_clrset(dev->parent, 0x0010,
130e2c0a7ffSGuochun Huang AUTO_LINK | LINK_CFG,
131e2c0a7ffSGuochun Huang FIELD_PREP(AUTO_LINK, 0) |
132e2c0a7ffSGuochun Huang FIELD_PREP(LINK_CFG, LINKA));
133e2c0a7ffSGuochun Huang }
134e2c0a7ffSGuochun Huang
max96755f_bridge_mode_set(struct rockchip_bridge * bridge,const struct drm_display_mode * mode)135e2c0a7ffSGuochun Huang static void max96755f_bridge_mode_set(struct rockchip_bridge *bridge,
136e2c0a7ffSGuochun Huang const struct drm_display_mode *mode)
137e2c0a7ffSGuochun Huang {
138e2c0a7ffSGuochun Huang struct udevice *dev = bridge->dev;
139e2c0a7ffSGuochun Huang struct max96755f_priv *priv = dev_get_priv(dev->parent);
140e2c0a7ffSGuochun Huang
141e2c0a7ffSGuochun Huang memcpy(&priv->mode, mode, sizeof(struct drm_display_mode));
142e2c0a7ffSGuochun Huang }
143e2c0a7ffSGuochun Huang
max96755f_bridge_detect(struct rockchip_bridge * bridge)144f1efa5adSGuochun Huang static bool max96755f_bridge_detect(struct rockchip_bridge *bridge)
145f1efa5adSGuochun Huang {
146f1efa5adSGuochun Huang struct max96755f_priv *priv = dev_get_priv(bridge->dev->parent);
147f1efa5adSGuochun Huang
148f1efa5adSGuochun Huang if (!dm_gpio_get_value(&priv->lock_gpio))
149f1efa5adSGuochun Huang return false;
150f1efa5adSGuochun Huang
151f1efa5adSGuochun Huang return true;
152f1efa5adSGuochun Huang }
153f1efa5adSGuochun Huang
154e2c0a7ffSGuochun Huang static const struct rockchip_bridge_funcs max96755f_bridge_funcs = {
155e2c0a7ffSGuochun Huang .enable = max96755f_bridge_enable,
156e2c0a7ffSGuochun Huang .disable = max96755f_bridge_disable,
157e2c0a7ffSGuochun Huang .mode_set = max96755f_bridge_mode_set,
158f1efa5adSGuochun Huang .detect = max96755f_bridge_detect,
159e2c0a7ffSGuochun Huang };
160e2c0a7ffSGuochun Huang
max96755f_bridge_bind(struct udevice * dev)16159487b18SGuochun Huang static int max96755f_bridge_bind(struct udevice *dev)
16259487b18SGuochun Huang {
16359487b18SGuochun Huang struct mipi_dsi_device *device = dev_get_platdata(dev);
16459487b18SGuochun Huang
16559487b18SGuochun Huang device->dev = dev;
16659487b18SGuochun Huang device->lanes = dev_read_u32_default(dev, "dsi,lanes", 4);
16759487b18SGuochun Huang device->format = dev_read_u32_default(dev, "dsi,format",
16859487b18SGuochun Huang MIPI_DSI_FMT_RGB888);
16959487b18SGuochun Huang device->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
17059487b18SGuochun Huang device->channel = dev_read_u32_default(dev, "reg", 0);
17159487b18SGuochun Huang
17259487b18SGuochun Huang return 0;
17359487b18SGuochun Huang }
17459487b18SGuochun Huang
max96755f_bridge_probe(struct udevice * dev)175e2c0a7ffSGuochun Huang static int max96755f_bridge_probe(struct udevice *dev)
176e2c0a7ffSGuochun Huang {
177e2c0a7ffSGuochun Huang struct rockchip_bridge *bridge;
178e2c0a7ffSGuochun Huang struct max96755f_priv *priv = dev_get_priv(dev->parent);
179*da42fac8SGuochun Huang const struct device_node *np = ofnode_to_np(dev->node);
180*da42fac8SGuochun Huang int i, len, ret;
181e2c0a7ffSGuochun Huang
182e2c0a7ffSGuochun Huang bridge = calloc(1, sizeof(*bridge));
183e2c0a7ffSGuochun Huang if (!bridge)
184e2c0a7ffSGuochun Huang return -ENOMEM;
185e2c0a7ffSGuochun Huang
186e2c0a7ffSGuochun Huang dev->driver_data = (ulong)bridge;
187e2c0a7ffSGuochun Huang bridge->dev = dev;
188e2c0a7ffSGuochun Huang bridge->funcs = &max96755f_bridge_funcs;
189e2c0a7ffSGuochun Huang
190e2c0a7ffSGuochun Huang priv->num_lanes = dev_read_u32_default(dev, "dsi,lanes", 4);
191e2c0a7ffSGuochun Huang priv->dv_swp_ab = dev_read_bool(dev, "vd-swap-ab");
192e2c0a7ffSGuochun Huang priv->dpi_deskew_en = dev_read_bool(dev, "dpi-deskew-en");
193e2c0a7ffSGuochun Huang
194*da42fac8SGuochun Huang for ( i = 0; i < priv->num_lanes; i++)
195*da42fac8SGuochun Huang priv->dsi_lane_map[i] = i;
196*da42fac8SGuochun Huang
197*da42fac8SGuochun Huang if (of_find_property(np, "maxim,dsi-lane-map", &len)) {
198*da42fac8SGuochun Huang len /= sizeof(u32);
199*da42fac8SGuochun Huang if (priv->num_lanes != len) {
200*da42fac8SGuochun Huang printf("invalid number of lane map\n");
201*da42fac8SGuochun Huang return -EINVAL;
202*da42fac8SGuochun Huang }
203*da42fac8SGuochun Huang }
204*da42fac8SGuochun Huang
205*da42fac8SGuochun Huang ret = of_read_u32_array(np, "maxim,dsi-lane-map",
206*da42fac8SGuochun Huang priv->dsi_lane_map, priv->num_lanes);
207*da42fac8SGuochun Huang if (ret) {
208*da42fac8SGuochun Huang printf("get dsi lane map failed\n");
209*da42fac8SGuochun Huang return -EINVAL;
210*da42fac8SGuochun Huang }
211*da42fac8SGuochun Huang
212f1efa5adSGuochun Huang ret = gpio_request_by_name(dev, "lock-gpios", 0, &priv->lock_gpio,
213f1efa5adSGuochun Huang GPIOD_IS_IN);
214f1efa5adSGuochun Huang if (ret) {
215f1efa5adSGuochun Huang dev_err(dev, "failed to get lock GPIO: %d\n", ret);
216f1efa5adSGuochun Huang return ret;
217f1efa5adSGuochun Huang }
218f1efa5adSGuochun Huang
219e2c0a7ffSGuochun Huang return 0;
220e2c0a7ffSGuochun Huang }
221e2c0a7ffSGuochun Huang
222e2c0a7ffSGuochun Huang static const struct udevice_id max96755f_bridge_of_match[] = {
223e2c0a7ffSGuochun Huang { .compatible = "maxim,max96755f-bridge", },
224e2c0a7ffSGuochun Huang { }
225e2c0a7ffSGuochun Huang };
226e2c0a7ffSGuochun Huang
227e2c0a7ffSGuochun Huang U_BOOT_DRIVER(max96755f_bridge) = {
228e2c0a7ffSGuochun Huang .name = "max96755f_bridge",
229e2c0a7ffSGuochun Huang .id = UCLASS_VIDEO_BRIDGE,
230e2c0a7ffSGuochun Huang .of_match = max96755f_bridge_of_match,
231e2c0a7ffSGuochun Huang .probe = max96755f_bridge_probe,
23259487b18SGuochun Huang .bind = max96755f_bridge_bind,
23359487b18SGuochun Huang .platdata_auto_alloc_size = sizeof(struct mipi_dsi_device),
234e2c0a7ffSGuochun Huang };
235