xref: /rk3399_rockchip-uboot/drivers/video/drm/max96755f.c (revision 59487b181d9de99706b5f92c8b974c65e1cfa4f8)
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>
12*59487b18SGuochun 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 
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;
25e2c0a7ffSGuochun Huang 
26e2c0a7ffSGuochun Huang 	dm_i2c_reg_clrset(priv->dev, 0x0331, NUM_LANES,
27e2c0a7ffSGuochun Huang 			  FIELD_PREP(NUM_LANES, priv->num_lanes - 1));
28e2c0a7ffSGuochun Huang 	if (!priv->dpi_deskew_en)
29e2c0a7ffSGuochun Huang 		return;
30e2c0a7ffSGuochun Huang 
31e2c0a7ffSGuochun Huang 	vact = mode->vdisplay;
32e2c0a7ffSGuochun Huang 	vsa = mode->vsync_end - mode->vsync_start;
33e2c0a7ffSGuochun Huang 	vfp = mode->vsync_start - mode->vdisplay;
34e2c0a7ffSGuochun Huang 	vbp = mode->vtotal - mode->vsync_end;
35e2c0a7ffSGuochun Huang 	hact = mode->hdisplay;
36e2c0a7ffSGuochun Huang 	hsa = mode->hsync_end - mode->hsync_start;
37e2c0a7ffSGuochun Huang 	hfp = mode->hsync_start - mode->hdisplay;
38e2c0a7ffSGuochun Huang 	hbp = mode->htotal - mode->hsync_end;
39e2c0a7ffSGuochun Huang 	dm_i2c_reg_write(priv->dev, 0x03A4, 0xc1);
40e2c0a7ffSGuochun Huang 	dm_i2c_reg_write(priv->dev, 0x0385, FIELD_PREP(DPI_HSYNC_WIDTH_L, hsa));
41e2c0a7ffSGuochun Huang 	dm_i2c_reg_write(priv->dev, 0x0386, FIELD_PREP(DPI_VYSNC_WIDTH_L, vsa));
42e2c0a7ffSGuochun Huang 	dm_i2c_reg_write(priv->dev, 0x0387,
43e2c0a7ffSGuochun Huang 			 FIELD_PREP(DPI_VSYNC_WIDTH_H, (vsa >> 8)) |
44e2c0a7ffSGuochun Huang 			 FIELD_PREP(DPI_HSYNC_WIDTH_H, (hsa >> 8)));
45e2c0a7ffSGuochun Huang 	dm_i2c_reg_write(priv->dev, 0x03a5, FIELD_PREP(DPI_VFP_L, vfp));
46e2c0a7ffSGuochun Huang 	dm_i2c_reg_write(priv->dev, 0x03a6,
47e2c0a7ffSGuochun Huang 			 FIELD_PREP(DPI_VBP_L, vbp) |
48e2c0a7ffSGuochun Huang 			 FIELD_PREP(DPI_VFP_H, (vfp >> 8)));
49e2c0a7ffSGuochun Huang 	dm_i2c_reg_write(priv->dev, 0x03a7, FIELD_PREP(DPI_VBP_H, (vbp >> 4)));
50e2c0a7ffSGuochun Huang 	dm_i2c_reg_write(priv->dev, 0x03a8, FIELD_PREP(DPI_VACT_L, vact));
51e2c0a7ffSGuochun Huang 	dm_i2c_reg_write(priv->dev, 0x03a9, FIELD_PREP(DPI_VACT_H, (vact >> 8)));
52e2c0a7ffSGuochun Huang 	dm_i2c_reg_write(priv->dev, 0x03aa, FIELD_PREP(DPI_HFP_L, hfp));
53e2c0a7ffSGuochun Huang 	dm_i2c_reg_write(priv->dev, 0x03ab,
54e2c0a7ffSGuochun Huang 			 FIELD_PREP(DPI_HBP_L, hbp) |
55e2c0a7ffSGuochun Huang 			 FIELD_PREP(DPI_HFP_H, (hfp >> 7)));
56e2c0a7ffSGuochun Huang 	dm_i2c_reg_write(priv->dev, 0x03ac, FIELD_PREP(DPI_HBP_H, (hbp >> 4)));
57e2c0a7ffSGuochun Huang 	dm_i2c_reg_write(priv->dev, 0x03ad, FIELD_PREP(DPI_HACT_L, hact));
58e2c0a7ffSGuochun Huang 	dm_i2c_reg_write(priv->dev, 0x03ae, FIELD_PREP(DPI_HACT_H, (hact >> 8)));
59e2c0a7ffSGuochun Huang }
60e2c0a7ffSGuochun Huang 
61e2c0a7ffSGuochun Huang static void max96755f_bridge_enable(struct rockchip_bridge *bridge)
62e2c0a7ffSGuochun Huang {
63e2c0a7ffSGuochun Huang 	struct udevice *dev = bridge->dev;
64e2c0a7ffSGuochun Huang 	struct max96755f_priv *priv = dev_get_priv(dev->parent);
65e2c0a7ffSGuochun Huang 
66e2c0a7ffSGuochun Huang 	max96755f_mipi_dsi_rx_config(priv);
67e2c0a7ffSGuochun Huang 	if (priv->split_mode) {
68e2c0a7ffSGuochun Huang 		dm_i2c_reg_clrset(dev->parent, 0x0010,
69e2c0a7ffSGuochun Huang 				  RESET_ONESHOT | AUTO_LINK | LINK_CFG,
70e2c0a7ffSGuochun Huang 				  FIELD_PREP(RESET_ONESHOT, 1) |
71e2c0a7ffSGuochun Huang 				  FIELD_PREP(AUTO_LINK, 0) |
72e2c0a7ffSGuochun Huang 				  FIELD_PREP(LINK_CFG, SPLITTER_MODE));
73e2c0a7ffSGuochun Huang 		mdelay(50);
74e2c0a7ffSGuochun Huang 		dm_i2c_reg_clrset(dev->parent, 0x0053,
75e2c0a7ffSGuochun Huang 				  TX_SPLIT_MASK_B | TX_SPLIT_MASK_A | TX_STR_SEL,
76e2c0a7ffSGuochun Huang 				  FIELD_PREP(TX_SPLIT_MASK_B, 0) |
77e2c0a7ffSGuochun Huang 				  FIELD_PREP(TX_SPLIT_MASK_A, 1) |
78e2c0a7ffSGuochun Huang 				  FIELD_PREP(TX_STR_SEL, 0));
79e2c0a7ffSGuochun Huang 		dm_i2c_reg_clrset(dev->parent, 0x0057,
80e2c0a7ffSGuochun Huang 				  TX_SPLIT_MASK_B | TX_SPLIT_MASK_A | TX_STR_SEL,
81e2c0a7ffSGuochun Huang 				  FIELD_PREP(TX_SPLIT_MASK_B, 1) |
82e2c0a7ffSGuochun Huang 				  FIELD_PREP(TX_SPLIT_MASK_A, 0) |
83e2c0a7ffSGuochun Huang 				  FIELD_PREP(TX_STR_SEL, 1));
84e2c0a7ffSGuochun Huang 		dm_i2c_reg_clrset(dev->parent, 0x032a,
85e2c0a7ffSGuochun Huang 				  DV_SWP_AB | DV_CONV | DV_SPL | DV_EN,
86e2c0a7ffSGuochun Huang 				  FIELD_PREP(DV_SWP_AB, priv->dv_swp_ab) |
87e2c0a7ffSGuochun Huang 				  FIELD_PREP(DV_CONV, 1) |
88e2c0a7ffSGuochun Huang 				  FIELD_PREP(DV_SPL, 1) |
89e2c0a7ffSGuochun Huang 				  FIELD_PREP(DV_EN, 1));
90e2c0a7ffSGuochun Huang 		dm_i2c_reg_clrset(dev->parent, 0x0311,
91e2c0a7ffSGuochun Huang 				  START_PORTAX | START_PORTAY,
92e2c0a7ffSGuochun Huang 				  FIELD_PREP(START_PORTAX, 1) |
93e2c0a7ffSGuochun Huang 				  FIELD_PREP(START_PORTAY, 1));
94e2c0a7ffSGuochun Huang 		dm_i2c_reg_clrset(dev->parent, 0x0002,
95e2c0a7ffSGuochun Huang 				  VID_TX_EN_X | VID_TX_EN_Y,
96e2c0a7ffSGuochun Huang 				  FIELD_PREP(VID_TX_EN_X, 1) |
97e2c0a7ffSGuochun Huang 				  FIELD_PREP(VID_TX_EN_Y, 1));
98e2c0a7ffSGuochun Huang 
99e2c0a7ffSGuochun Huang 	} else {
100e2c0a7ffSGuochun Huang 		dm_i2c_reg_clrset(dev->parent, 0x0002, VID_TX_EN_X,
101e2c0a7ffSGuochun Huang 				  FIELD_PREP(VID_TX_EN_X, 1));
102e2c0a7ffSGuochun Huang 		dm_i2c_reg_clrset(dev->parent, 0x0311, START_PORTAX,
103e2c0a7ffSGuochun Huang 				  FIELD_PREP(START_PORTAX, 1));
104e2c0a7ffSGuochun Huang 	}
105e2c0a7ffSGuochun Huang 
106e2c0a7ffSGuochun Huang 	dm_i2c_reg_clrset(dev->parent, 0x0010, RESET_ONESHOT,
107e2c0a7ffSGuochun Huang 			  FIELD_PREP(RESET_ONESHOT, 1));
108e2c0a7ffSGuochun Huang 	mdelay(100);
109e2c0a7ffSGuochun Huang }
110e2c0a7ffSGuochun Huang 
111e2c0a7ffSGuochun Huang static void max96755f_bridge_disable(struct rockchip_bridge *bridge)
112e2c0a7ffSGuochun Huang {
113e2c0a7ffSGuochun Huang 	struct udevice *dev = bridge->dev;
114e2c0a7ffSGuochun Huang 	struct max96755f_priv *priv = dev_get_priv(dev->parent);
115e2c0a7ffSGuochun Huang 
116e2c0a7ffSGuochun Huang 	dm_i2c_reg_clrset(dev->parent, 0x0002, VID_TX_EN_X,
117e2c0a7ffSGuochun Huang 			  FIELD_PREP(VID_TX_EN_X, 0));
118e2c0a7ffSGuochun Huang 
119e2c0a7ffSGuochun Huang 	if (priv->split_mode)
120e2c0a7ffSGuochun Huang 		dm_i2c_reg_clrset(dev->parent, 0x0010,
121e2c0a7ffSGuochun Huang 				  AUTO_LINK | LINK_CFG,
122e2c0a7ffSGuochun Huang 				  FIELD_PREP(AUTO_LINK, 0) |
123e2c0a7ffSGuochun Huang 				  FIELD_PREP(LINK_CFG, LINKA));
124e2c0a7ffSGuochun Huang }
125e2c0a7ffSGuochun Huang 
126e2c0a7ffSGuochun Huang static void max96755f_bridge_mode_set(struct rockchip_bridge *bridge,
127e2c0a7ffSGuochun Huang 				      const struct drm_display_mode *mode)
128e2c0a7ffSGuochun Huang {
129e2c0a7ffSGuochun Huang 	struct udevice *dev = bridge->dev;
130e2c0a7ffSGuochun Huang 	struct max96755f_priv *priv = dev_get_priv(dev->parent);
131e2c0a7ffSGuochun Huang 
132e2c0a7ffSGuochun Huang 	memcpy(&priv->mode, mode, sizeof(struct drm_display_mode));
133e2c0a7ffSGuochun Huang }
134e2c0a7ffSGuochun Huang 
135f1efa5adSGuochun Huang static bool max96755f_bridge_detect(struct rockchip_bridge *bridge)
136f1efa5adSGuochun Huang {
137f1efa5adSGuochun Huang 	struct max96755f_priv *priv = dev_get_priv(bridge->dev->parent);
138f1efa5adSGuochun Huang 
139f1efa5adSGuochun Huang 	if (!dm_gpio_get_value(&priv->lock_gpio))
140f1efa5adSGuochun Huang 		return false;
141f1efa5adSGuochun Huang 
142f1efa5adSGuochun Huang 	return true;
143f1efa5adSGuochun Huang }
144f1efa5adSGuochun Huang 
145e2c0a7ffSGuochun Huang static const struct rockchip_bridge_funcs max96755f_bridge_funcs = {
146e2c0a7ffSGuochun Huang 	.enable = max96755f_bridge_enable,
147e2c0a7ffSGuochun Huang 	.disable = max96755f_bridge_disable,
148e2c0a7ffSGuochun Huang 	.mode_set = max96755f_bridge_mode_set,
149f1efa5adSGuochun Huang 	.detect = max96755f_bridge_detect,
150e2c0a7ffSGuochun Huang };
151e2c0a7ffSGuochun Huang 
152*59487b18SGuochun Huang static int max96755f_bridge_bind(struct udevice *dev)
153*59487b18SGuochun Huang {
154*59487b18SGuochun Huang 	struct mipi_dsi_device *device = dev_get_platdata(dev);
155*59487b18SGuochun Huang 
156*59487b18SGuochun Huang 	device->dev = dev;
157*59487b18SGuochun Huang 	device->lanes = dev_read_u32_default(dev, "dsi,lanes", 4);
158*59487b18SGuochun Huang 	device->format = dev_read_u32_default(dev, "dsi,format",
159*59487b18SGuochun Huang 					      MIPI_DSI_FMT_RGB888);
160*59487b18SGuochun Huang 	device->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
161*59487b18SGuochun Huang 	device->channel = dev_read_u32_default(dev, "reg", 0);
162*59487b18SGuochun Huang 
163*59487b18SGuochun Huang 	return 0;
164*59487b18SGuochun Huang }
165*59487b18SGuochun Huang 
166e2c0a7ffSGuochun Huang static int max96755f_bridge_probe(struct udevice *dev)
167e2c0a7ffSGuochun Huang {
168e2c0a7ffSGuochun Huang 	struct rockchip_bridge *bridge;
169e2c0a7ffSGuochun Huang 	struct max96755f_priv *priv = dev_get_priv(dev->parent);
170f1efa5adSGuochun Huang 	int ret;
171e2c0a7ffSGuochun Huang 
172e2c0a7ffSGuochun Huang 	bridge = calloc(1, sizeof(*bridge));
173e2c0a7ffSGuochun Huang 	if (!bridge)
174e2c0a7ffSGuochun Huang 		return -ENOMEM;
175e2c0a7ffSGuochun Huang 
176e2c0a7ffSGuochun Huang 	dev->driver_data = (ulong)bridge;
177e2c0a7ffSGuochun Huang 	bridge->dev = dev;
178e2c0a7ffSGuochun Huang 	bridge->funcs = &max96755f_bridge_funcs;
179e2c0a7ffSGuochun Huang 
180e2c0a7ffSGuochun Huang 	priv->num_lanes = dev_read_u32_default(dev, "dsi,lanes", 4);
181e2c0a7ffSGuochun Huang 	priv->dv_swp_ab = dev_read_bool(dev, "vd-swap-ab");
182e2c0a7ffSGuochun Huang 	priv->dpi_deskew_en = dev_read_bool(dev, "dpi-deskew-en");
183e2c0a7ffSGuochun Huang 
184f1efa5adSGuochun Huang 	ret = gpio_request_by_name(dev, "lock-gpios", 0, &priv->lock_gpio,
185f1efa5adSGuochun Huang 				   GPIOD_IS_IN);
186f1efa5adSGuochun Huang 	if (ret) {
187f1efa5adSGuochun Huang 		dev_err(dev, "failed to get lock GPIO: %d\n", ret);
188f1efa5adSGuochun Huang 		return ret;
189f1efa5adSGuochun Huang 	}
190f1efa5adSGuochun Huang 
191e2c0a7ffSGuochun Huang 	return 0;
192e2c0a7ffSGuochun Huang }
193e2c0a7ffSGuochun Huang 
194e2c0a7ffSGuochun Huang static const struct udevice_id max96755f_bridge_of_match[] = {
195e2c0a7ffSGuochun Huang 	{ .compatible = "maxim,max96755f-bridge", },
196e2c0a7ffSGuochun Huang 	{ }
197e2c0a7ffSGuochun Huang };
198e2c0a7ffSGuochun Huang 
199e2c0a7ffSGuochun Huang U_BOOT_DRIVER(max96755f_bridge) = {
200e2c0a7ffSGuochun Huang 	.name = "max96755f_bridge",
201e2c0a7ffSGuochun Huang 	.id = UCLASS_VIDEO_BRIDGE,
202e2c0a7ffSGuochun Huang 	.of_match = max96755f_bridge_of_match,
203e2c0a7ffSGuochun Huang 	.probe = max96755f_bridge_probe,
204*59487b18SGuochun Huang 	.bind = max96755f_bridge_bind,
205*59487b18SGuochun Huang 	.platdata_auto_alloc_size = sizeof(struct mipi_dsi_device),
206e2c0a7ffSGuochun Huang };
207