blob: 62d014c20988553cd31d0626e7911b6ce60127dc [file] [log] [blame]
Thomas Gleixnercaab2772019-06-03 07:44:50 +02001// SPDX-License-Identifier: GPL-2.0-only
Rob Clark16ea9752013-01-08 15:04:28 -06002/*
3 * Copyright (C) 2012 Texas Instruments
4 * Author: Rob Clark <robdclark@gmail.com>
Rob Clark16ea9752013-01-08 15:04:28 -06005 */
6
7#include <linux/i2c.h>
Rob Clark16ea9752013-01-08 15:04:28 -06008#include <linux/gpio.h>
9#include <linux/of_gpio.h>
10#include <linux/pinctrl/pinmux.h>
11#include <linux/pinctrl/consumer.h>
Jyri Sarha018cfbd2016-04-13 19:00:51 +030012#include <drm/drm_atomic_helper.h>
Daniel Vetterfcd70cd2019-01-17 22:03:34 +010013#include <drm/drm_probe_helper.h>
Rob Clark16ea9752013-01-08 15:04:28 -060014
15#include "tilcdc_drv.h"
Baoyou Xie0fd86772016-09-08 19:08:56 +080016#include "tilcdc_tfp410.h"
Rob Clark16ea9752013-01-08 15:04:28 -060017
18struct tfp410_module {
19 struct tilcdc_module base;
20 struct i2c_adapter *i2c;
21 int gpio;
22};
23#define to_tfp410_module(x) container_of(x, struct tfp410_module, base)
24
25
26static const struct tilcdc_panel_info dvi_info = {
27 .ac_bias = 255,
28 .ac_bias_intrpt = 0,
29 .dma_burst_sz = 16,
30 .bpp = 16,
31 .fdd = 0x80,
32 .tft_alt_mode = 0,
33 .sync_edge = 0,
34 .sync_ctrl = 1,
35 .raster_order = 0,
36};
37
38/*
39 * Encoder:
40 */
41
42struct tfp410_encoder {
43 struct drm_encoder base;
44 struct tfp410_module *mod;
45 int dpms;
46};
47#define to_tfp410_encoder(x) container_of(x, struct tfp410_encoder, base)
48
Rob Clark16ea9752013-01-08 15:04:28 -060049static void tfp410_encoder_dpms(struct drm_encoder *encoder, int mode)
50{
51 struct tfp410_encoder *tfp410_encoder = to_tfp410_encoder(encoder);
52
53 if (tfp410_encoder->dpms == mode)
54 return;
55
56 if (mode == DRM_MODE_DPMS_ON) {
57 DBG("Power on");
58 gpio_direction_output(tfp410_encoder->mod->gpio, 1);
59 } else {
60 DBG("Power off");
61 gpio_direction_output(tfp410_encoder->mod->gpio, 0);
62 }
63
64 tfp410_encoder->dpms = mode;
65}
66
Rob Clark16ea9752013-01-08 15:04:28 -060067static void tfp410_encoder_prepare(struct drm_encoder *encoder)
68{
69 tfp410_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
Rob Clark16ea9752013-01-08 15:04:28 -060070}
71
72static void tfp410_encoder_commit(struct drm_encoder *encoder)
73{
74 tfp410_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
75}
76
77static void tfp410_encoder_mode_set(struct drm_encoder *encoder,
78 struct drm_display_mode *mode,
79 struct drm_display_mode *adjusted_mode)
80{
81 /* nothing needed */
82}
83
84static const struct drm_encoder_funcs tfp410_encoder_funcs = {
Jyri Sarhad0ec32c2016-02-23 12:44:27 +020085 .destroy = drm_encoder_cleanup,
Rob Clark16ea9752013-01-08 15:04:28 -060086};
87
88static const struct drm_encoder_helper_funcs tfp410_encoder_helper_funcs = {
89 .dpms = tfp410_encoder_dpms,
Rob Clark16ea9752013-01-08 15:04:28 -060090 .prepare = tfp410_encoder_prepare,
91 .commit = tfp410_encoder_commit,
92 .mode_set = tfp410_encoder_mode_set,
93};
94
95static struct drm_encoder *tfp410_encoder_create(struct drm_device *dev,
96 struct tfp410_module *mod)
97{
98 struct tfp410_encoder *tfp410_encoder;
99 struct drm_encoder *encoder;
100 int ret;
101
Jyri Sarhad0ec32c2016-02-23 12:44:27 +0200102 tfp410_encoder = devm_kzalloc(dev->dev, sizeof(*tfp410_encoder),
103 GFP_KERNEL);
Markus Elfring3366ba32018-02-06 21:51:15 +0100104 if (!tfp410_encoder)
Rob Clark16ea9752013-01-08 15:04:28 -0600105 return NULL;
Rob Clark16ea9752013-01-08 15:04:28 -0600106
107 tfp410_encoder->dpms = DRM_MODE_DPMS_OFF;
108 tfp410_encoder->mod = mod;
109
110 encoder = &tfp410_encoder->base;
111 encoder->possible_crtcs = 1;
112
113 ret = drm_encoder_init(dev, encoder, &tfp410_encoder_funcs,
Ville Syrjälä13a3d912015-12-09 16:20:18 +0200114 DRM_MODE_ENCODER_TMDS, NULL);
Rob Clark16ea9752013-01-08 15:04:28 -0600115 if (ret < 0)
116 goto fail;
117
118 drm_encoder_helper_add(encoder, &tfp410_encoder_helper_funcs);
119
120 return encoder;
121
122fail:
Jyri Sarhad0ec32c2016-02-23 12:44:27 +0200123 drm_encoder_cleanup(encoder);
Rob Clark16ea9752013-01-08 15:04:28 -0600124 return NULL;
125}
126
127/*
128 * Connector:
129 */
130
131struct tfp410_connector {
132 struct drm_connector base;
133
134 struct drm_encoder *encoder; /* our connected encoder */
135 struct tfp410_module *mod;
136};
137#define to_tfp410_connector(x) container_of(x, struct tfp410_connector, base)
138
139
140static void tfp410_connector_destroy(struct drm_connector *connector)
141{
Sachin Kamat62eb3e22014-07-09 17:12:57 +0530142 drm_connector_unregister(connector);
Rob Clark16ea9752013-01-08 15:04:28 -0600143 drm_connector_cleanup(connector);
Rob Clark16ea9752013-01-08 15:04:28 -0600144}
145
146static enum drm_connector_status tfp410_connector_detect(
147 struct drm_connector *connector,
148 bool force)
149{
150 struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector);
151
152 if (drm_probe_ddc(tfp410_connector->mod->i2c))
153 return connector_status_connected;
154
155 return connector_status_unknown;
156}
157
158static int tfp410_connector_get_modes(struct drm_connector *connector)
159{
160 struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector);
161 struct edid *edid;
162 int ret = 0;
163
164 edid = drm_get_edid(connector, tfp410_connector->mod->i2c);
165
Daniel Vetterc555f022018-07-09 10:40:06 +0200166 drm_connector_update_edid_property(connector, edid);
Rob Clark16ea9752013-01-08 15:04:28 -0600167
168 if (edid) {
169 ret = drm_add_edid_modes(connector, edid);
170 kfree(edid);
171 }
172
173 return ret;
174}
175
176static int tfp410_connector_mode_valid(struct drm_connector *connector,
177 struct drm_display_mode *mode)
178{
179 struct tilcdc_drm_private *priv = connector->dev->dev_private;
180 /* our only constraints are what the crtc can generate: */
181 return tilcdc_crtc_mode_valid(priv->crtc, mode);
182}
183
184static struct drm_encoder *tfp410_connector_best_encoder(
185 struct drm_connector *connector)
186{
187 struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector);
188 return tfp410_connector->encoder;
189}
190
191static const struct drm_connector_funcs tfp410_connector_funcs = {
192 .destroy = tfp410_connector_destroy,
Rob Clark16ea9752013-01-08 15:04:28 -0600193 .detect = tfp410_connector_detect,
194 .fill_modes = drm_helper_probe_single_connector_modes,
Jyri Sarha018cfbd2016-04-13 19:00:51 +0300195 .reset = drm_atomic_helper_connector_reset,
196 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
197 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
Rob Clark16ea9752013-01-08 15:04:28 -0600198};
199
200static const struct drm_connector_helper_funcs tfp410_connector_helper_funcs = {
201 .get_modes = tfp410_connector_get_modes,
202 .mode_valid = tfp410_connector_mode_valid,
203 .best_encoder = tfp410_connector_best_encoder,
204};
205
206static struct drm_connector *tfp410_connector_create(struct drm_device *dev,
207 struct tfp410_module *mod, struct drm_encoder *encoder)
208{
209 struct tfp410_connector *tfp410_connector;
210 struct drm_connector *connector;
211 int ret;
212
Jyri Sarhad0ec32c2016-02-23 12:44:27 +0200213 tfp410_connector = devm_kzalloc(dev->dev, sizeof(*tfp410_connector),
214 GFP_KERNEL);
Markus Elfring3366ba32018-02-06 21:51:15 +0100215 if (!tfp410_connector)
Rob Clark16ea9752013-01-08 15:04:28 -0600216 return NULL;
Rob Clark16ea9752013-01-08 15:04:28 -0600217
218 tfp410_connector->encoder = encoder;
219 tfp410_connector->mod = mod;
220
221 connector = &tfp410_connector->base;
222
223 drm_connector_init(dev, connector, &tfp410_connector_funcs,
224 DRM_MODE_CONNECTOR_DVID);
225 drm_connector_helper_add(connector, &tfp410_connector_helper_funcs);
226
227 connector->polled = DRM_CONNECTOR_POLL_CONNECT |
228 DRM_CONNECTOR_POLL_DISCONNECT;
229
230 connector->interlace_allowed = 0;
231 connector->doublescan_allowed = 0;
232
Daniel Vettercde4c442018-07-09 10:40:07 +0200233 ret = drm_connector_attach_encoder(connector, encoder);
Rob Clark16ea9752013-01-08 15:04:28 -0600234 if (ret)
235 goto fail;
236
Rob Clark16ea9752013-01-08 15:04:28 -0600237 return connector;
238
239fail:
240 tfp410_connector_destroy(connector);
241 return NULL;
242}
243
244/*
245 * Module:
246 */
247
248static int tfp410_modeset_init(struct tilcdc_module *mod, struct drm_device *dev)
249{
250 struct tfp410_module *tfp410_mod = to_tfp410_module(mod);
251 struct tilcdc_drm_private *priv = dev->dev_private;
252 struct drm_encoder *encoder;
253 struct drm_connector *connector;
254
255 encoder = tfp410_encoder_create(dev, tfp410_mod);
256 if (!encoder)
257 return -ENOMEM;
258
259 connector = tfp410_connector_create(dev, tfp410_mod, encoder);
260 if (!connector)
261 return -ENOMEM;
262
263 priv->encoders[priv->num_encoders++] = encoder;
264 priv->connectors[priv->num_connectors++] = connector;
265
Jyri Sarha7c979b52016-04-13 18:59:16 +0300266 tilcdc_crtc_set_panel_info(priv->crtc, &dvi_info);
Rob Clark16ea9752013-01-08 15:04:28 -0600267 return 0;
268}
269
Rob Clark16ea9752013-01-08 15:04:28 -0600270static const struct tilcdc_module_ops tfp410_module_ops = {
271 .modeset_init = tfp410_modeset_init,
Rob Clark16ea9752013-01-08 15:04:28 -0600272};
273
274/*
275 * Device:
276 */
277
Rob Clark16ea9752013-01-08 15:04:28 -0600278static int tfp410_probe(struct platform_device *pdev)
279{
280 struct device_node *node = pdev->dev.of_node;
281 struct device_node *i2c_node;
282 struct tfp410_module *tfp410_mod;
283 struct tilcdc_module *mod;
284 struct pinctrl *pinctrl;
285 uint32_t i2c_phandle;
286 int ret = -EINVAL;
287
288 /* bail out early if no DT data: */
289 if (!node) {
290 dev_err(&pdev->dev, "device-tree data is missing\n");
291 return -ENXIO;
292 }
293
Jyri Sarhad0ec32c2016-02-23 12:44:27 +0200294 tfp410_mod = devm_kzalloc(&pdev->dev, sizeof(*tfp410_mod), GFP_KERNEL);
Rob Clark16ea9752013-01-08 15:04:28 -0600295 if (!tfp410_mod)
296 return -ENOMEM;
297
298 mod = &tfp410_mod->base;
Guido Martínez7cdcce92014-06-17 11:17:10 -0300299 pdev->dev.platform_data = mod;
Rob Clark16ea9752013-01-08 15:04:28 -0600300
301 tilcdc_module_init(mod, "tfp410", &tfp410_module_ops);
302
303 pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
304 if (IS_ERR(pinctrl))
305 dev_warn(&pdev->dev, "pins are not configured\n");
306
307 if (of_property_read_u32(node, "i2c", &i2c_phandle)) {
308 dev_err(&pdev->dev, "could not get i2c bus phandle\n");
309 goto fail;
310 }
311
312 i2c_node = of_find_node_by_phandle(i2c_phandle);
313 if (!i2c_node) {
314 dev_err(&pdev->dev, "could not get i2c bus node\n");
315 goto fail;
316 }
317
318 tfp410_mod->i2c = of_find_i2c_adapter_by_node(i2c_node);
319 if (!tfp410_mod->i2c) {
320 dev_err(&pdev->dev, "could not get i2c\n");
Guido Martínez7cdcce92014-06-17 11:17:10 -0300321 of_node_put(i2c_node);
Rob Clark16ea9752013-01-08 15:04:28 -0600322 goto fail;
323 }
324
325 of_node_put(i2c_node);
326
327 tfp410_mod->gpio = of_get_named_gpio_flags(node, "powerdn-gpio",
328 0, NULL);
Arnd Bergmann287980e2016-05-27 23:23:25 +0200329 if (tfp410_mod->gpio < 0) {
Rob Clark16ea9752013-01-08 15:04:28 -0600330 dev_warn(&pdev->dev, "No power down GPIO\n");
331 } else {
332 ret = gpio_request(tfp410_mod->gpio, "DVI_PDn");
333 if (ret) {
334 dev_err(&pdev->dev, "could not get DVI_PDn gpio\n");
Guido Martínez7cdcce92014-06-17 11:17:10 -0300335 goto fail_adapter;
Rob Clark16ea9752013-01-08 15:04:28 -0600336 }
337 }
338
339 return 0;
340
Guido Martínez7cdcce92014-06-17 11:17:10 -0300341fail_adapter:
342 i2c_put_adapter(tfp410_mod->i2c);
343
Rob Clark16ea9752013-01-08 15:04:28 -0600344fail:
Guido Martínez7cdcce92014-06-17 11:17:10 -0300345 tilcdc_module_cleanup(mod);
Rob Clark16ea9752013-01-08 15:04:28 -0600346 return ret;
347}
348
349static int tfp410_remove(struct platform_device *pdev)
350{
Guido Martínez7cdcce92014-06-17 11:17:10 -0300351 struct tilcdc_module *mod = dev_get_platdata(&pdev->dev);
352 struct tfp410_module *tfp410_mod = to_tfp410_module(mod);
353
354 i2c_put_adapter(tfp410_mod->i2c);
355 gpio_free(tfp410_mod->gpio);
356
357 tilcdc_module_cleanup(mod);
Guido Martínez7cdcce92014-06-17 11:17:10 -0300358
Rob Clark16ea9752013-01-08 15:04:28 -0600359 return 0;
360}
361
Arvind Yadavcafdee62017-06-28 11:12:00 +0530362static const struct of_device_id tfp410_of_match[] = {
Rob Clark16ea9752013-01-08 15:04:28 -0600363 { .compatible = "ti,tilcdc,tfp410", },
364 { },
365};
Rob Clark16ea9752013-01-08 15:04:28 -0600366
367struct platform_driver tfp410_driver = {
368 .probe = tfp410_probe,
369 .remove = tfp410_remove,
370 .driver = {
371 .owner = THIS_MODULE,
372 .name = "tfp410",
373 .of_match_table = tfp410_of_match,
374 },
375};
376
377int __init tilcdc_tfp410_init(void)
378{
379 return platform_driver_register(&tfp410_driver);
380}
381
382void __exit tilcdc_tfp410_fini(void)
383{
384 platform_driver_unregister(&tfp410_driver);
385}