1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2016 Texas Instruments |
4 | * Author: Jyri Sarha <jsarha@ti.com> |
5 | */ |
6 | |
7 | #include <linux/gpio/consumer.h> |
8 | #include <linux/i2c.h> |
9 | #include <linux/media-bus-format.h> |
10 | #include <linux/module.h> |
11 | #include <linux/of_graph.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/workqueue.h> |
14 | |
15 | #include <drm/drm_atomic_helper.h> |
16 | #include <drm/drm_bridge.h> |
17 | #include <drm/drm_crtc.h> |
18 | #include <drm/drm_edid.h> |
19 | #include <drm/drm_print.h> |
20 | #include <drm/drm_probe_helper.h> |
21 | |
22 | #define HOTPLUG_DEBOUNCE_MS 1100 |
23 | |
24 | struct tfp410 { |
25 | struct drm_bridge bridge; |
26 | struct drm_connector connector; |
27 | |
28 | u32 bus_format; |
29 | struct delayed_work hpd_work; |
30 | struct gpio_desc *powerdown; |
31 | |
32 | struct drm_bridge_timings timings; |
33 | struct drm_bridge *next_bridge; |
34 | |
35 | struct device *dev; |
36 | }; |
37 | |
38 | static inline struct tfp410 * |
39 | drm_bridge_to_tfp410(struct drm_bridge *bridge) |
40 | { |
41 | return container_of(bridge, struct tfp410, bridge); |
42 | } |
43 | |
44 | static inline struct tfp410 * |
45 | drm_connector_to_tfp410(struct drm_connector *connector) |
46 | { |
47 | return container_of(connector, struct tfp410, connector); |
48 | } |
49 | |
50 | static int tfp410_get_modes(struct drm_connector *connector) |
51 | { |
52 | struct tfp410 *dvi = drm_connector_to_tfp410(connector); |
53 | struct edid *edid; |
54 | int ret; |
55 | |
56 | if (dvi->next_bridge->ops & DRM_BRIDGE_OP_EDID) { |
57 | edid = drm_bridge_get_edid(bridge: dvi->next_bridge, connector); |
58 | if (!edid) |
59 | DRM_INFO("EDID read failed. Fallback to standard modes\n" ); |
60 | } else { |
61 | edid = NULL; |
62 | } |
63 | |
64 | if (!edid) { |
65 | /* |
66 | * No EDID, fallback on the XGA standard modes and prefer a mode |
67 | * pretty much anything can handle. |
68 | */ |
69 | ret = drm_add_modes_noedid(connector, hdisplay: 1920, vdisplay: 1200); |
70 | drm_set_preferred_mode(connector, hpref: 1024, vpref: 768); |
71 | return ret; |
72 | } |
73 | |
74 | drm_connector_update_edid_property(connector, edid); |
75 | |
76 | ret = drm_add_edid_modes(connector, edid); |
77 | |
78 | kfree(objp: edid); |
79 | |
80 | return ret; |
81 | } |
82 | |
83 | static const struct drm_connector_helper_funcs tfp410_con_helper_funcs = { |
84 | .get_modes = tfp410_get_modes, |
85 | }; |
86 | |
87 | static enum drm_connector_status |
88 | tfp410_connector_detect(struct drm_connector *connector, bool force) |
89 | { |
90 | struct tfp410 *dvi = drm_connector_to_tfp410(connector); |
91 | |
92 | return drm_bridge_detect(bridge: dvi->next_bridge); |
93 | } |
94 | |
95 | static const struct drm_connector_funcs tfp410_con_funcs = { |
96 | .detect = tfp410_connector_detect, |
97 | .fill_modes = drm_helper_probe_single_connector_modes, |
98 | .destroy = drm_connector_cleanup, |
99 | .reset = drm_atomic_helper_connector_reset, |
100 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, |
101 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, |
102 | }; |
103 | |
104 | static void tfp410_hpd_work_func(struct work_struct *work) |
105 | { |
106 | struct tfp410 *dvi; |
107 | |
108 | dvi = container_of(work, struct tfp410, hpd_work.work); |
109 | |
110 | if (dvi->bridge.dev) |
111 | drm_helper_hpd_irq_event(dev: dvi->bridge.dev); |
112 | } |
113 | |
114 | static void tfp410_hpd_callback(void *arg, enum drm_connector_status status) |
115 | { |
116 | struct tfp410 *dvi = arg; |
117 | |
118 | mod_delayed_work(wq: system_wq, dwork: &dvi->hpd_work, |
119 | delay: msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS)); |
120 | } |
121 | |
122 | static int tfp410_attach(struct drm_bridge *bridge, |
123 | enum drm_bridge_attach_flags flags) |
124 | { |
125 | struct tfp410 *dvi = drm_bridge_to_tfp410(bridge); |
126 | int ret; |
127 | |
128 | ret = drm_bridge_attach(encoder: bridge->encoder, bridge: dvi->next_bridge, previous: bridge, |
129 | flags: DRM_BRIDGE_ATTACH_NO_CONNECTOR); |
130 | if (ret < 0) |
131 | return ret; |
132 | |
133 | if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) |
134 | return 0; |
135 | |
136 | if (!bridge->encoder) { |
137 | dev_err(dvi->dev, "Missing encoder\n" ); |
138 | return -ENODEV; |
139 | } |
140 | |
141 | if (dvi->next_bridge->ops & DRM_BRIDGE_OP_DETECT) |
142 | dvi->connector.polled = DRM_CONNECTOR_POLL_HPD; |
143 | else |
144 | dvi->connector.polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; |
145 | |
146 | if (dvi->next_bridge->ops & DRM_BRIDGE_OP_HPD) { |
147 | INIT_DELAYED_WORK(&dvi->hpd_work, tfp410_hpd_work_func); |
148 | drm_bridge_hpd_enable(bridge: dvi->next_bridge, cb: tfp410_hpd_callback, |
149 | data: dvi); |
150 | } |
151 | |
152 | drm_connector_helper_add(connector: &dvi->connector, |
153 | funcs: &tfp410_con_helper_funcs); |
154 | ret = drm_connector_init_with_ddc(dev: bridge->dev, connector: &dvi->connector, |
155 | funcs: &tfp410_con_funcs, |
156 | connector_type: dvi->next_bridge->type, |
157 | ddc: dvi->next_bridge->ddc); |
158 | if (ret) { |
159 | dev_err(dvi->dev, "drm_connector_init_with_ddc() failed: %d\n" , |
160 | ret); |
161 | return ret; |
162 | } |
163 | |
164 | drm_display_info_set_bus_formats(info: &dvi->connector.display_info, |
165 | formats: &dvi->bus_format, num_formats: 1); |
166 | |
167 | drm_connector_attach_encoder(connector: &dvi->connector, encoder: bridge->encoder); |
168 | |
169 | return 0; |
170 | } |
171 | |
172 | static void tfp410_detach(struct drm_bridge *bridge) |
173 | { |
174 | struct tfp410 *dvi = drm_bridge_to_tfp410(bridge); |
175 | |
176 | if (dvi->connector.dev && dvi->next_bridge->ops & DRM_BRIDGE_OP_HPD) { |
177 | drm_bridge_hpd_disable(bridge: dvi->next_bridge); |
178 | cancel_delayed_work_sync(dwork: &dvi->hpd_work); |
179 | } |
180 | } |
181 | |
182 | static void tfp410_enable(struct drm_bridge *bridge) |
183 | { |
184 | struct tfp410 *dvi = drm_bridge_to_tfp410(bridge); |
185 | |
186 | gpiod_set_value_cansleep(desc: dvi->powerdown, value: 0); |
187 | } |
188 | |
189 | static void tfp410_disable(struct drm_bridge *bridge) |
190 | { |
191 | struct tfp410 *dvi = drm_bridge_to_tfp410(bridge); |
192 | |
193 | gpiod_set_value_cansleep(desc: dvi->powerdown, value: 1); |
194 | } |
195 | |
196 | static enum drm_mode_status tfp410_mode_valid(struct drm_bridge *bridge, |
197 | const struct drm_display_info *info, |
198 | const struct drm_display_mode *mode) |
199 | { |
200 | if (mode->clock < 25000) |
201 | return MODE_CLOCK_LOW; |
202 | |
203 | if (mode->clock > 165000) |
204 | return MODE_CLOCK_HIGH; |
205 | |
206 | return MODE_OK; |
207 | } |
208 | |
209 | static u32 *tfp410_get_input_bus_fmts(struct drm_bridge *bridge, |
210 | struct drm_bridge_state *bridge_state, |
211 | struct drm_crtc_state *crtc_state, |
212 | struct drm_connector_state *conn_state, |
213 | u32 output_fmt, |
214 | unsigned int *num_input_fmts) |
215 | { |
216 | struct tfp410 *dvi = drm_bridge_to_tfp410(bridge); |
217 | u32 *input_fmts; |
218 | |
219 | *num_input_fmts = 0; |
220 | |
221 | input_fmts = kzalloc(size: sizeof(*input_fmts), GFP_KERNEL); |
222 | if (!input_fmts) |
223 | return NULL; |
224 | |
225 | *num_input_fmts = 1; |
226 | input_fmts[0] = dvi->bus_format; |
227 | |
228 | return input_fmts; |
229 | } |
230 | |
231 | static int tfp410_atomic_check(struct drm_bridge *bridge, |
232 | struct drm_bridge_state *bridge_state, |
233 | struct drm_crtc_state *crtc_state, |
234 | struct drm_connector_state *conn_state) |
235 | { |
236 | struct tfp410 *dvi = drm_bridge_to_tfp410(bridge); |
237 | |
238 | /* |
239 | * There might be flags negotiation supported in future. |
240 | * Set the bus flags in atomic_check statically for now. |
241 | */ |
242 | bridge_state->input_bus_cfg.flags = dvi->timings.input_bus_flags; |
243 | |
244 | return 0; |
245 | } |
246 | |
247 | static const struct drm_bridge_funcs tfp410_bridge_funcs = { |
248 | .attach = tfp410_attach, |
249 | .detach = tfp410_detach, |
250 | .enable = tfp410_enable, |
251 | .disable = tfp410_disable, |
252 | .mode_valid = tfp410_mode_valid, |
253 | .atomic_reset = drm_atomic_helper_bridge_reset, |
254 | .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, |
255 | .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, |
256 | .atomic_get_input_bus_fmts = tfp410_get_input_bus_fmts, |
257 | .atomic_check = tfp410_atomic_check, |
258 | }; |
259 | |
260 | static const struct drm_bridge_timings tfp410_default_timings = { |
261 | .input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE |
262 | | DRM_BUS_FLAG_DE_HIGH, |
263 | .setup_time_ps = 1200, |
264 | .hold_time_ps = 1300, |
265 | }; |
266 | |
267 | static int tfp410_parse_timings(struct tfp410 *dvi, bool i2c) |
268 | { |
269 | struct drm_bridge_timings *timings = &dvi->timings; |
270 | struct device_node *ep; |
271 | u32 pclk_sample = 0; |
272 | u32 bus_width = 24; |
273 | u32 deskew = 0; |
274 | |
275 | /* Start with defaults. */ |
276 | *timings = tfp410_default_timings; |
277 | |
278 | if (i2c) |
279 | /* |
280 | * In I2C mode timings are configured through the I2C interface. |
281 | * As the driver doesn't support I2C configuration yet, we just |
282 | * go with the defaults (BSEL=1, DSEL=1, DKEN=0, EDGE=1). |
283 | */ |
284 | return 0; |
285 | |
286 | /* |
287 | * In non-I2C mode, timings are configured through the BSEL, DSEL, DKEN |
288 | * and EDGE pins. They are specified in DT through endpoint properties |
289 | * and vendor-specific properties. |
290 | */ |
291 | ep = of_graph_get_endpoint_by_regs(parent: dvi->dev->of_node, port_reg: 0, reg: 0); |
292 | if (!ep) |
293 | return -EINVAL; |
294 | |
295 | /* Get the sampling edge from the endpoint. */ |
296 | of_property_read_u32(np: ep, propname: "pclk-sample" , out_value: &pclk_sample); |
297 | of_property_read_u32(np: ep, propname: "bus-width" , out_value: &bus_width); |
298 | of_node_put(node: ep); |
299 | |
300 | timings->input_bus_flags = DRM_BUS_FLAG_DE_HIGH; |
301 | |
302 | switch (pclk_sample) { |
303 | case 0: |
304 | timings->input_bus_flags |= DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE |
305 | | DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE; |
306 | break; |
307 | case 1: |
308 | timings->input_bus_flags |= DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE |
309 | | DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE; |
310 | break; |
311 | default: |
312 | return -EINVAL; |
313 | } |
314 | |
315 | switch (bus_width) { |
316 | case 12: |
317 | dvi->bus_format = MEDIA_BUS_FMT_RGB888_2X12_LE; |
318 | break; |
319 | case 24: |
320 | dvi->bus_format = MEDIA_BUS_FMT_RGB888_1X24; |
321 | break; |
322 | default: |
323 | return -EINVAL; |
324 | } |
325 | |
326 | /* Get the setup and hold time from vendor-specific properties. */ |
327 | of_property_read_u32(np: dvi->dev->of_node, propname: "ti,deskew" , out_value: &deskew); |
328 | if (deskew > 7) |
329 | return -EINVAL; |
330 | |
331 | timings->setup_time_ps = 1200 - 350 * ((s32)deskew - 4); |
332 | timings->hold_time_ps = max(0, 1300 + 350 * ((s32)deskew - 4)); |
333 | |
334 | return 0; |
335 | } |
336 | |
337 | static int tfp410_init(struct device *dev, bool i2c) |
338 | { |
339 | struct device_node *node; |
340 | struct tfp410 *dvi; |
341 | int ret; |
342 | |
343 | if (!dev->of_node) { |
344 | dev_err(dev, "device-tree data is missing\n" ); |
345 | return -ENXIO; |
346 | } |
347 | |
348 | dvi = devm_kzalloc(dev, size: sizeof(*dvi), GFP_KERNEL); |
349 | if (!dvi) |
350 | return -ENOMEM; |
351 | |
352 | dvi->dev = dev; |
353 | dev_set_drvdata(dev, data: dvi); |
354 | |
355 | dvi->bridge.funcs = &tfp410_bridge_funcs; |
356 | dvi->bridge.of_node = dev->of_node; |
357 | dvi->bridge.timings = &dvi->timings; |
358 | dvi->bridge.type = DRM_MODE_CONNECTOR_DVID; |
359 | |
360 | ret = tfp410_parse_timings(dvi, i2c); |
361 | if (ret) |
362 | return ret; |
363 | |
364 | /* Get the next bridge, connected to port@1. */ |
365 | node = of_graph_get_remote_node(node: dev->of_node, port: 1, endpoint: -1); |
366 | if (!node) |
367 | return -ENODEV; |
368 | |
369 | dvi->next_bridge = of_drm_find_bridge(np: node); |
370 | of_node_put(node); |
371 | |
372 | if (!dvi->next_bridge) |
373 | return -EPROBE_DEFER; |
374 | |
375 | /* Get the powerdown GPIO. */ |
376 | dvi->powerdown = devm_gpiod_get_optional(dev, con_id: "powerdown" , |
377 | flags: GPIOD_OUT_HIGH); |
378 | if (IS_ERR(ptr: dvi->powerdown)) { |
379 | dev_err(dev, "failed to parse powerdown gpio\n" ); |
380 | return PTR_ERR(ptr: dvi->powerdown); |
381 | } |
382 | |
383 | /* Register the DRM bridge. */ |
384 | drm_bridge_add(bridge: &dvi->bridge); |
385 | |
386 | return 0; |
387 | } |
388 | |
389 | static void tfp410_fini(struct device *dev) |
390 | { |
391 | struct tfp410 *dvi = dev_get_drvdata(dev); |
392 | |
393 | drm_bridge_remove(bridge: &dvi->bridge); |
394 | } |
395 | |
396 | static int tfp410_probe(struct platform_device *pdev) |
397 | { |
398 | return tfp410_init(dev: &pdev->dev, i2c: false); |
399 | } |
400 | |
401 | static void tfp410_remove(struct platform_device *pdev) |
402 | { |
403 | tfp410_fini(dev: &pdev->dev); |
404 | } |
405 | |
406 | static const struct of_device_id tfp410_match[] = { |
407 | { .compatible = "ti,tfp410" }, |
408 | {}, |
409 | }; |
410 | MODULE_DEVICE_TABLE(of, tfp410_match); |
411 | |
412 | static struct platform_driver tfp410_platform_driver = { |
413 | .probe = tfp410_probe, |
414 | .remove_new = tfp410_remove, |
415 | .driver = { |
416 | .name = "tfp410-bridge" , |
417 | .of_match_table = tfp410_match, |
418 | }, |
419 | }; |
420 | |
421 | #if IS_ENABLED(CONFIG_I2C) |
422 | /* There is currently no i2c functionality. */ |
423 | static int tfp410_i2c_probe(struct i2c_client *client) |
424 | { |
425 | int reg; |
426 | |
427 | if (!client->dev.of_node || |
428 | of_property_read_u32(np: client->dev.of_node, propname: "reg" , out_value: ®)) { |
429 | dev_err(&client->dev, |
430 | "Can't get i2c reg property from device-tree\n" ); |
431 | return -ENXIO; |
432 | } |
433 | |
434 | return tfp410_init(dev: &client->dev, i2c: true); |
435 | } |
436 | |
437 | static void tfp410_i2c_remove(struct i2c_client *client) |
438 | { |
439 | tfp410_fini(dev: &client->dev); |
440 | } |
441 | |
442 | static const struct i2c_device_id tfp410_i2c_ids[] = { |
443 | { "tfp410" , 0 }, |
444 | { } |
445 | }; |
446 | MODULE_DEVICE_TABLE(i2c, tfp410_i2c_ids); |
447 | |
448 | static struct i2c_driver tfp410_i2c_driver = { |
449 | .driver = { |
450 | .name = "tfp410" , |
451 | .of_match_table = tfp410_match, |
452 | }, |
453 | .id_table = tfp410_i2c_ids, |
454 | .probe = tfp410_i2c_probe, |
455 | .remove = tfp410_i2c_remove, |
456 | }; |
457 | #endif /* IS_ENABLED(CONFIG_I2C) */ |
458 | |
459 | static struct { |
460 | uint i2c:1; |
461 | uint platform:1; |
462 | } tfp410_registered_driver; |
463 | |
464 | static int __init tfp410_module_init(void) |
465 | { |
466 | int ret; |
467 | |
468 | #if IS_ENABLED(CONFIG_I2C) |
469 | ret = i2c_add_driver(&tfp410_i2c_driver); |
470 | if (ret) |
471 | pr_err("%s: registering i2c driver failed: %d" , |
472 | __func__, ret); |
473 | else |
474 | tfp410_registered_driver.i2c = 1; |
475 | #endif |
476 | |
477 | ret = platform_driver_register(&tfp410_platform_driver); |
478 | if (ret) |
479 | pr_err("%s: registering platform driver failed: %d" , |
480 | __func__, ret); |
481 | else |
482 | tfp410_registered_driver.platform = 1; |
483 | |
484 | if (tfp410_registered_driver.i2c || |
485 | tfp410_registered_driver.platform) |
486 | return 0; |
487 | |
488 | return ret; |
489 | } |
490 | module_init(tfp410_module_init); |
491 | |
492 | static void __exit tfp410_module_exit(void) |
493 | { |
494 | #if IS_ENABLED(CONFIG_I2C) |
495 | if (tfp410_registered_driver.i2c) |
496 | i2c_del_driver(driver: &tfp410_i2c_driver); |
497 | #endif |
498 | if (tfp410_registered_driver.platform) |
499 | platform_driver_unregister(&tfp410_platform_driver); |
500 | } |
501 | module_exit(tfp410_module_exit); |
502 | |
503 | MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>" ); |
504 | MODULE_DESCRIPTION("TI TFP410 DVI bridge driver" ); |
505 | MODULE_LICENSE("GPL" ); |
506 | |