1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) Rockchip Electronics Co.Ltd
4 * Author:
5 * Guochun Huang <hero.huang@rock-chips.com>
6 */
7
8 #include <asm/unaligned.h>
9 #include <drm/drm_bridge.h>
10 #include <drm/drm_atomic_state_helper.h>
11 #include <drm/drm_mipi_dsi.h>
12 #include <drm/drm_modeset_helper_vtables.h>
13 #include <drm/drm_of.h>
14 #include <drm/drm_print.h>
15 #include <drm/drm_probe_helper.h>
16 #include <drm/drm_panel.h>
17 #include <linux/kernel.h>
18 #include <linux/delay.h>
19 #include <linux/clk.h>
20 #include <linux/gpio/consumer.h>
21 #include <linux/regulator/consumer.h>
22 #include <linux/i2c.h>
23 #include <linux/module.h>
24 #include <linux/of.h>
25 #include <linux/of_gpio.h>
26 #include <linux/regmap.h>
27
28 struct serdes_init_seq {
29 struct reg_sequence *reg_sequence;
30 unsigned int reg_seq_cnt;
31 };
32
33 struct bu18rl82 {
34 struct drm_bridge base;
35 struct drm_panel *panel;
36 struct device *dev;
37 struct regmap *regmap;
38 struct serdes_init_seq *serdes_init_seq;
39 };
40
41 static const struct regmap_config bu18rl82_regmap_config = {
42 .name = "bu18rl82",
43 .reg_bits = 16,
44 .val_bits = 8,
45 .max_register = 0x0700,
46 };
47
bridge_to_bu18rl82(struct drm_bridge * bridge)48 static struct bu18rl82 *bridge_to_bu18rl82(struct drm_bridge *bridge)
49 {
50 return container_of(bridge, struct bu18rl82, base);
51 }
52
bu18rl82_parse_init_seq(struct device * dev,const u16 * data,int length,struct serdes_init_seq * seq)53 static int bu18rl82_parse_init_seq(struct device *dev, const u16 *data,
54 int length, struct serdes_init_seq *seq)
55 {
56 struct reg_sequence *reg_sequence;
57 u16 *buf, *d;
58 unsigned int i, cnt;
59
60 if (!seq)
61 return -EINVAL;
62
63 buf = devm_kmemdup(dev, data, length, GFP_KERNEL);
64 if (!buf)
65 return -ENOMEM;
66
67 d = buf;
68 cnt = length / 4;
69 seq->reg_seq_cnt = cnt;
70
71 seq->reg_sequence = devm_kcalloc(dev, cnt, sizeof(struct reg_sequence), GFP_KERNEL);
72 if (!seq->reg_sequence)
73 return -ENOMEM;
74
75
76 for (i = 0; i < cnt; i++) {
77 reg_sequence = &seq->reg_sequence[i];
78 reg_sequence->reg = get_unaligned_be16(&d[0]);
79 reg_sequence->def = get_unaligned_be16(&d[1]);
80 d += 2;
81 }
82
83 return 0;
84 }
85
bu18rl82_get_init_seq(struct bu18rl82 * bu18rl82)86 static int bu18rl82_get_init_seq(struct bu18rl82 *bu18rl82)
87 {
88 struct device *dev = bu18rl82->dev;
89 struct device_node *np = dev->of_node;
90 const void *data;
91 int len, err;
92
93 data = of_get_property(np, "serdes-init-sequence", &len);
94 if (!data) {
95 dev_err(dev, "failed to get serdes-init-sequence\n");
96 return -EINVAL;
97 }
98
99 bu18rl82->serdes_init_seq = devm_kzalloc(dev, sizeof(*bu18rl82->serdes_init_seq),
100 GFP_KERNEL);
101 if (!bu18rl82->serdes_init_seq)
102 return -ENOMEM;
103
104 err = bu18rl82_parse_init_seq(dev, data, len, bu18rl82->serdes_init_seq);
105 if (err) {
106 dev_err(dev, "failed to parse serdes-init-sequence\n");
107 return err;
108 }
109
110 return 0;
111 }
112
bu18rl82_bridge_get_modes(struct drm_bridge * bridge,struct drm_connector * connector)113 static int bu18rl82_bridge_get_modes(struct drm_bridge *bridge,
114 struct drm_connector *connector)
115 {
116 struct bu18rl82 *bu18rl82 = bridge_to_bu18rl82(bridge);
117
118 return drm_panel_get_modes(bu18rl82->panel, connector);
119 }
120
bu18rl82_bridge_attach(struct drm_bridge * bridge,enum drm_bridge_attach_flags flags)121 static int bu18rl82_bridge_attach(struct drm_bridge *bridge,
122 enum drm_bridge_attach_flags flags)
123 {
124 struct bu18rl82 *bu18rl82 = bridge_to_bu18rl82(bridge);
125 int ret;
126
127 ret = drm_of_find_panel_or_bridge(bu18rl82->dev->of_node, 1, -1,
128 &bu18rl82->panel, NULL);
129 if (ret)
130 return ret;
131
132 return 0;
133 }
134
bu18rl82_bridge_enable(struct drm_bridge * bridge)135 static void bu18rl82_bridge_enable(struct drm_bridge *bridge)
136 {
137 struct bu18rl82 *bu18rl82 = bridge_to_bu18rl82(bridge);
138 struct serdes_init_seq *init_seq = bu18rl82->serdes_init_seq;
139 int count = init_seq->reg_seq_cnt;
140
141 regmap_multi_reg_write(bu18rl82->regmap, init_seq->reg_sequence, count);
142
143 drm_panel_enable(bu18rl82->panel);
144 }
145
bu18rl82_bridge_disable(struct drm_bridge * bridge)146 static void bu18rl82_bridge_disable(struct drm_bridge *bridge)
147 {
148 struct bu18rl82 *bu18rl82 = bridge_to_bu18rl82(bridge);
149
150 drm_panel_disable(bu18rl82->panel);
151
152 regmap_write(bu18rl82->regmap, 0x91, 0x00);
153 }
154
bu18rl82_bridge_pre_enable(struct drm_bridge * bridge)155 static void bu18rl82_bridge_pre_enable(struct drm_bridge *bridge)
156 {
157 struct bu18rl82 *bu18rl82 = bridge_to_bu18rl82(bridge);
158
159 drm_panel_prepare(bu18rl82->panel);
160 }
161
bu18rl82_bridge_post_disable(struct drm_bridge * bridge)162 static void bu18rl82_bridge_post_disable(struct drm_bridge *bridge)
163 {
164 struct bu18rl82 *bu18rl82 = bridge_to_bu18rl82(bridge);
165
166 drm_panel_unprepare(bu18rl82->panel);
167 }
168
169 static const struct drm_bridge_funcs bu18rl82_bridge_funcs = {
170 .attach = bu18rl82_bridge_attach,
171 .enable = bu18rl82_bridge_enable,
172 .disable = bu18rl82_bridge_disable,
173 .pre_enable = bu18rl82_bridge_pre_enable,
174 .post_disable = bu18rl82_bridge_post_disable,
175 .get_modes = bu18rl82_bridge_get_modes,
176 };
177
bu18rl82_i2c_probe(struct i2c_client * client,const struct i2c_device_id * id)178 static int bu18rl82_i2c_probe(struct i2c_client *client,
179 const struct i2c_device_id *id)
180 {
181 struct device *dev = &client->dev;
182 struct bu18rl82 *bu18rl82;
183 int ret;
184
185 bu18rl82 = devm_kzalloc(dev, sizeof(*bu18rl82), GFP_KERNEL);
186 if (!bu18rl82)
187 return -ENOMEM;
188
189 bu18rl82->dev = dev;
190 i2c_set_clientdata(client, bu18rl82);
191
192 bu18rl82->regmap = devm_regmap_init_i2c(client, &bu18rl82_regmap_config);
193 if (IS_ERR(bu18rl82->regmap))
194 return dev_err_probe(dev, PTR_ERR(bu18rl82->regmap),
195 "failed to initialize regmap\n");
196
197 ret = bu18rl82_get_init_seq(bu18rl82);
198 if (ret)
199 return ret;
200
201 bu18rl82->base.funcs = &bu18rl82_bridge_funcs;
202 bu18rl82->base.of_node = dev->of_node;
203 bu18rl82->base.ops = DRM_BRIDGE_OP_MODES;
204
205 drm_bridge_add(&bu18rl82->base);
206
207 return 0;
208 }
209
bu18rl82_i2c_remove(struct i2c_client * client)210 static int bu18rl82_i2c_remove(struct i2c_client *client)
211 {
212 struct bu18rl82 *bu18rl82 = i2c_get_clientdata(client);
213
214 drm_bridge_remove(&bu18rl82->base);
215
216 return 0;
217 }
218
219 static const struct i2c_device_id bu18rl82_i2c_table[] = {
220 { "bu18rl82", 0 },
221 {}
222 };
223 MODULE_DEVICE_TABLE(i2c, bu18rl82_i2c_table);
224
225 static const struct of_device_id bu18rl82_of_match[] = {
226 { .compatible = "rohm,bu18rl82" },
227 {}
228 };
229 MODULE_DEVICE_TABLE(of, bu18rl82_of_match);
230
231 static struct i2c_driver bu18rl82_i2c_driver = {
232 .driver = {
233 .name = "bu18rl82",
234 .of_match_table = bu18rl82_of_match,
235 },
236 .probe = bu18rl82_i2c_probe,
237 .remove = bu18rl82_i2c_remove,
238 .id_table = bu18rl82_i2c_table,
239 };
240 module_i2c_driver(bu18rl82_i2c_driver);
241
242 MODULE_AUTHOR("Guochun Huang <hero.huang@rock-chips.com>");
243 MODULE_DESCRIPTION("Rohm BU18RL82 Clockless Link-BD Deserializer with LVDS Interface");
244 MODULE_LICENSE("GPL");
245