1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2020 Marek Vasut <marex@denx.de> |
4 | * |
5 | * Based on tc358764.c by |
6 | * Andrzej Hajda <a.hajda@samsung.com> |
7 | * Maciej Purski <m.purski@samsung.com> |
8 | * |
9 | * Based on rpi_touchscreen.c by |
10 | * Eric Anholt <eric@anholt.net> |
11 | */ |
12 | |
13 | #include <linux/delay.h> |
14 | #include <linux/gpio/consumer.h> |
15 | #include <linux/mod_devicetable.h> |
16 | #include <linux/module.h> |
17 | #include <linux/of_graph.h> |
18 | #include <linux/regulator/consumer.h> |
19 | |
20 | #include <video/mipi_display.h> |
21 | |
22 | #include <drm/drm_atomic_helper.h> |
23 | #include <drm/drm_crtc.h> |
24 | #include <drm/drm_mipi_dsi.h> |
25 | #include <drm/drm_of.h> |
26 | #include <drm/drm_panel.h> |
27 | #include <drm/drm_print.h> |
28 | #include <drm/drm_probe_helper.h> |
29 | |
30 | /* PPI layer registers */ |
31 | #define PPI_STARTPPI 0x0104 /* START control bit */ |
32 | #define PPI_LPTXTIMECNT 0x0114 /* LPTX timing signal */ |
33 | #define PPI_D0S_ATMR 0x0144 |
34 | #define PPI_D1S_ATMR 0x0148 |
35 | #define PPI_D0S_CLRSIPOCOUNT 0x0164 /* Assertion timer for Lane 0 */ |
36 | #define PPI_D1S_CLRSIPOCOUNT 0x0168 /* Assertion timer for Lane 1 */ |
37 | #define PPI_START_FUNCTION 1 |
38 | |
39 | /* DSI layer registers */ |
40 | #define DSI_STARTDSI 0x0204 /* START control bit of DSI-TX */ |
41 | #define DSI_LANEENABLE 0x0210 /* Enables each lane */ |
42 | #define DSI_RX_START 1 |
43 | |
44 | /* LCDC/DPI Host Registers, based on guesswork that this matches TC358764 */ |
45 | #define LCDCTRL 0x0420 /* Video Path Control */ |
46 | #define LCDCTRL_MSF BIT(0) /* Magic square in RGB666 */ |
47 | #define LCDCTRL_VTGEN BIT(4)/* Use chip clock for timing */ |
48 | #define LCDCTRL_UNK6 BIT(6) /* Unknown */ |
49 | #define LCDCTRL_EVTMODE BIT(5) /* Event mode */ |
50 | #define LCDCTRL_RGB888 BIT(8) /* RGB888 mode */ |
51 | #define LCDCTRL_HSPOL BIT(17) /* Polarity of HSYNC signal */ |
52 | #define LCDCTRL_DEPOL BIT(18) /* Polarity of DE signal */ |
53 | #define LCDCTRL_VSPOL BIT(19) /* Polarity of VSYNC signal */ |
54 | #define LCDCTRL_VSDELAY(v) (((v) & 0xfff) << 20) /* VSYNC delay */ |
55 | |
56 | /* SPI Master Registers */ |
57 | #define SPICMR 0x0450 |
58 | #define SPITCR 0x0454 |
59 | |
60 | /* System Controller Registers */ |
61 | #define SYSCTRL 0x0464 |
62 | |
63 | /* System registers */ |
64 | #define LPX_PERIOD 3 |
65 | |
66 | /* Lane enable PPI and DSI register bits */ |
67 | #define LANEENABLE_CLEN BIT(0) |
68 | #define LANEENABLE_L0EN BIT(1) |
69 | #define LANEENABLE_L1EN BIT(2) |
70 | |
71 | struct tc358762 { |
72 | struct device *dev; |
73 | struct drm_bridge bridge; |
74 | struct regulator *regulator; |
75 | struct drm_bridge *panel_bridge; |
76 | struct gpio_desc *reset_gpio; |
77 | struct drm_display_mode mode; |
78 | bool pre_enabled; |
79 | int error; |
80 | }; |
81 | |
82 | static int tc358762_clear_error(struct tc358762 *ctx) |
83 | { |
84 | int ret = ctx->error; |
85 | |
86 | ctx->error = 0; |
87 | return ret; |
88 | } |
89 | |
90 | static void tc358762_write(struct tc358762 *ctx, u16 addr, u32 val) |
91 | { |
92 | struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); |
93 | ssize_t ret; |
94 | u8 data[6]; |
95 | |
96 | if (ctx->error) |
97 | return; |
98 | |
99 | data[0] = addr; |
100 | data[1] = addr >> 8; |
101 | data[2] = val; |
102 | data[3] = val >> 8; |
103 | data[4] = val >> 16; |
104 | data[5] = val >> 24; |
105 | |
106 | ret = mipi_dsi_generic_write(dsi, payload: data, size: sizeof(data)); |
107 | if (ret < 0) |
108 | ctx->error = ret; |
109 | } |
110 | |
111 | static inline struct tc358762 *bridge_to_tc358762(struct drm_bridge *bridge) |
112 | { |
113 | return container_of(bridge, struct tc358762, bridge); |
114 | } |
115 | |
116 | static int tc358762_init(struct tc358762 *ctx) |
117 | { |
118 | u32 lcdctrl; |
119 | |
120 | tc358762_write(ctx, DSI_LANEENABLE, |
121 | LANEENABLE_L0EN | LANEENABLE_CLEN); |
122 | tc358762_write(ctx, PPI_D0S_CLRSIPOCOUNT, val: 5); |
123 | tc358762_write(ctx, PPI_D1S_CLRSIPOCOUNT, val: 5); |
124 | tc358762_write(ctx, PPI_D0S_ATMR, val: 0); |
125 | tc358762_write(ctx, PPI_D1S_ATMR, val: 0); |
126 | tc358762_write(ctx, PPI_LPTXTIMECNT, LPX_PERIOD); |
127 | |
128 | tc358762_write(ctx, SPICMR, val: 0x00); |
129 | |
130 | lcdctrl = LCDCTRL_VSDELAY(1) | LCDCTRL_RGB888 | |
131 | LCDCTRL_UNK6 | LCDCTRL_VTGEN; |
132 | |
133 | if (ctx->mode.flags & DRM_MODE_FLAG_NHSYNC) |
134 | lcdctrl |= LCDCTRL_HSPOL; |
135 | |
136 | if (ctx->mode.flags & DRM_MODE_FLAG_NVSYNC) |
137 | lcdctrl |= LCDCTRL_VSPOL; |
138 | |
139 | tc358762_write(ctx, LCDCTRL, val: lcdctrl); |
140 | |
141 | tc358762_write(ctx, SYSCTRL, val: 0x040f); |
142 | msleep(msecs: 100); |
143 | |
144 | tc358762_write(ctx, PPI_STARTPPI, PPI_START_FUNCTION); |
145 | tc358762_write(ctx, DSI_STARTDSI, DSI_RX_START); |
146 | |
147 | msleep(msecs: 100); |
148 | |
149 | return tc358762_clear_error(ctx); |
150 | } |
151 | |
152 | static void tc358762_post_disable(struct drm_bridge *bridge, struct drm_bridge_state *state) |
153 | { |
154 | struct tc358762 *ctx = bridge_to_tc358762(bridge); |
155 | int ret; |
156 | |
157 | /* |
158 | * The post_disable hook might be called multiple times. |
159 | * We want to avoid regulator imbalance below. |
160 | */ |
161 | if (!ctx->pre_enabled) |
162 | return; |
163 | |
164 | ctx->pre_enabled = false; |
165 | |
166 | if (ctx->reset_gpio) |
167 | gpiod_set_value_cansleep(desc: ctx->reset_gpio, value: 0); |
168 | |
169 | ret = regulator_disable(regulator: ctx->regulator); |
170 | if (ret < 0) |
171 | dev_err(ctx->dev, "error disabling regulators (%d)\n" , ret); |
172 | } |
173 | |
174 | static void tc358762_pre_enable(struct drm_bridge *bridge, struct drm_bridge_state *state) |
175 | { |
176 | struct tc358762 *ctx = bridge_to_tc358762(bridge); |
177 | int ret; |
178 | |
179 | ret = regulator_enable(regulator: ctx->regulator); |
180 | if (ret < 0) |
181 | dev_err(ctx->dev, "error enabling regulators (%d)\n" , ret); |
182 | |
183 | if (ctx->reset_gpio) { |
184 | gpiod_set_value_cansleep(desc: ctx->reset_gpio, value: 1); |
185 | usleep_range(min: 5000, max: 10000); |
186 | } |
187 | |
188 | ctx->pre_enabled = true; |
189 | } |
190 | |
191 | static void tc358762_enable(struct drm_bridge *bridge, struct drm_bridge_state *state) |
192 | { |
193 | struct tc358762 *ctx = bridge_to_tc358762(bridge); |
194 | int ret; |
195 | |
196 | ret = tc358762_init(ctx); |
197 | if (ret < 0) |
198 | dev_err(ctx->dev, "error initializing bridge (%d)\n" , ret); |
199 | } |
200 | |
201 | static int tc358762_attach(struct drm_bridge *bridge, |
202 | enum drm_bridge_attach_flags flags) |
203 | { |
204 | struct tc358762 *ctx = bridge_to_tc358762(bridge); |
205 | |
206 | return drm_bridge_attach(encoder: bridge->encoder, bridge: ctx->panel_bridge, |
207 | previous: bridge, flags); |
208 | } |
209 | |
210 | static void tc358762_bridge_mode_set(struct drm_bridge *bridge, |
211 | const struct drm_display_mode *mode, |
212 | const struct drm_display_mode *adj) |
213 | { |
214 | struct tc358762 *ctx = bridge_to_tc358762(bridge); |
215 | |
216 | drm_mode_copy(dst: &ctx->mode, src: mode); |
217 | } |
218 | |
219 | static const struct drm_bridge_funcs tc358762_bridge_funcs = { |
220 | .atomic_post_disable = tc358762_post_disable, |
221 | .atomic_pre_enable = tc358762_pre_enable, |
222 | .atomic_enable = tc358762_enable, |
223 | .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, |
224 | .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, |
225 | .atomic_reset = drm_atomic_helper_bridge_reset, |
226 | .attach = tc358762_attach, |
227 | .mode_set = tc358762_bridge_mode_set, |
228 | }; |
229 | |
230 | static int tc358762_parse_dt(struct tc358762 *ctx) |
231 | { |
232 | struct drm_bridge *panel_bridge; |
233 | struct device *dev = ctx->dev; |
234 | |
235 | panel_bridge = devm_drm_of_get_bridge(dev, node: dev->of_node, port: 1, endpoint: 0); |
236 | if (IS_ERR(ptr: panel_bridge)) |
237 | return PTR_ERR(ptr: panel_bridge); |
238 | |
239 | ctx->panel_bridge = panel_bridge; |
240 | |
241 | /* Reset GPIO is optional */ |
242 | ctx->reset_gpio = devm_gpiod_get_optional(dev, con_id: "reset" , flags: GPIOD_OUT_LOW); |
243 | if (IS_ERR(ptr: ctx->reset_gpio)) |
244 | return PTR_ERR(ptr: ctx->reset_gpio); |
245 | |
246 | return 0; |
247 | } |
248 | |
249 | static int tc358762_configure_regulators(struct tc358762 *ctx) |
250 | { |
251 | ctx->regulator = devm_regulator_get(dev: ctx->dev, id: "vddc" ); |
252 | if (IS_ERR(ptr: ctx->regulator)) |
253 | return PTR_ERR(ptr: ctx->regulator); |
254 | |
255 | return 0; |
256 | } |
257 | |
258 | static int tc358762_probe(struct mipi_dsi_device *dsi) |
259 | { |
260 | struct device *dev = &dsi->dev; |
261 | struct tc358762 *ctx; |
262 | int ret; |
263 | |
264 | ctx = devm_kzalloc(dev, size: sizeof(struct tc358762), GFP_KERNEL); |
265 | if (!ctx) |
266 | return -ENOMEM; |
267 | |
268 | mipi_dsi_set_drvdata(dsi, data: ctx); |
269 | |
270 | ctx->dev = dev; |
271 | ctx->pre_enabled = false; |
272 | |
273 | /* TODO: Find out how to get dual-lane mode working */ |
274 | dsi->lanes = 1; |
275 | dsi->format = MIPI_DSI_FMT_RGB888; |
276 | dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | |
277 | MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_VIDEO_HSE; |
278 | |
279 | ret = tc358762_parse_dt(ctx); |
280 | if (ret < 0) |
281 | return ret; |
282 | |
283 | ret = tc358762_configure_regulators(ctx); |
284 | if (ret < 0) |
285 | return ret; |
286 | |
287 | ctx->bridge.funcs = &tc358762_bridge_funcs; |
288 | ctx->bridge.type = DRM_MODE_CONNECTOR_DPI; |
289 | ctx->bridge.of_node = dev->of_node; |
290 | ctx->bridge.pre_enable_prev_first = true; |
291 | |
292 | drm_bridge_add(bridge: &ctx->bridge); |
293 | |
294 | ret = mipi_dsi_attach(dsi); |
295 | if (ret < 0) { |
296 | drm_bridge_remove(bridge: &ctx->bridge); |
297 | dev_err(dev, "failed to attach dsi\n" ); |
298 | } |
299 | |
300 | return ret; |
301 | } |
302 | |
303 | static void tc358762_remove(struct mipi_dsi_device *dsi) |
304 | { |
305 | struct tc358762 *ctx = mipi_dsi_get_drvdata(dsi); |
306 | |
307 | mipi_dsi_detach(dsi); |
308 | drm_bridge_remove(bridge: &ctx->bridge); |
309 | } |
310 | |
311 | static const struct of_device_id tc358762_of_match[] = { |
312 | { .compatible = "toshiba,tc358762" }, |
313 | { } |
314 | }; |
315 | MODULE_DEVICE_TABLE(of, tc358762_of_match); |
316 | |
317 | static struct mipi_dsi_driver tc358762_driver = { |
318 | .probe = tc358762_probe, |
319 | .remove = tc358762_remove, |
320 | .driver = { |
321 | .name = "tc358762" , |
322 | .of_match_table = tc358762_of_match, |
323 | }, |
324 | }; |
325 | module_mipi_dsi_driver(tc358762_driver); |
326 | |
327 | MODULE_AUTHOR("Marek Vasut <marex@denx.de>" ); |
328 | MODULE_DESCRIPTION("MIPI-DSI based Driver for TC358762 DSI/DPI Bridge" ); |
329 | MODULE_LICENSE("GPL v2" ); |
330 | |