aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c
diff options
context:
space:
mode:
authorRob Clark <robdclark@gmail.com>2013-01-08 16:04:28 -0500
committerRob Clark <robdclark@gmail.com>2013-02-19 17:57:44 -0500
commit16ea975eac671fa40a78594a116a44fef8e3f4a9 (patch)
tree4e83f07ea5856e0b33e8fd0918f11aff4b25547b /drivers/gpu/drm/tilcdc/tilcdc_tfp410.c
parentb3d3de80133d32723224329f87edecaff230fefc (diff)
drm/tilcdc: add TI LCD Controller DRM driver (v4)
A simple DRM/KMS driver for the TI LCD Controller found in various smaller TI parts (AM33xx, OMAPL138, etc). This driver uses the CMA helpers. Currently only the TFP410 DVI encoder is supported (tested with beaglebone + DVI cape). There are also various LCD displays, for which support can be added (as I get hw to test on), and an external i2c HDMI encoder found on some boards. The display controller supports a single CRTC. And the encoder+ connector are split out into sub-devices. Depending on which LCD or external encoder is actually present, the appropriate output module(s) will be loaded. v1: original v2: fix fb refcnting and few other cleanups v3: get +/- vsync/hsync from timings rather than panel-info, add option DT max-bandwidth field so driver doesn't attempt to pick a display mode with too high memory bandwidth, and other small cleanups v4: remove some unneeded stuff from panel-info struct, properly set high bits for hfp/hsw/hbp for rev 2, add DT bindings docs Signed-off-by: Rob Clark <robdclark@gmail.com> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch> Tested-by: Koen Kooi <koen@dominion.thruhere.net>
Diffstat (limited to 'drivers/gpu/drm/tilcdc/tilcdc_tfp410.c')
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_tfp410.c419
1 files changed, 419 insertions, 0 deletions
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c
new file mode 100644
index 000000000000..58d487ba2414
--- /dev/null
+++ b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c
@@ -0,0 +1,419 @@
1/*
2 * Copyright (C) 2012 Texas Instruments
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include <linux/i2c.h>
19#include <linux/of_i2c.h>
20#include <linux/gpio.h>
21#include <linux/of_gpio.h>
22#include <linux/pinctrl/pinmux.h>
23#include <linux/pinctrl/consumer.h>
24
25#include "tilcdc_drv.h"
26
27struct tfp410_module {
28 struct tilcdc_module base;
29 struct i2c_adapter *i2c;
30 int gpio;
31};
32#define to_tfp410_module(x) container_of(x, struct tfp410_module, base)
33
34
35static const struct tilcdc_panel_info dvi_info = {
36 .ac_bias = 255,
37 .ac_bias_intrpt = 0,
38 .dma_burst_sz = 16,
39 .bpp = 16,
40 .fdd = 0x80,
41 .tft_alt_mode = 0,
42 .sync_edge = 0,
43 .sync_ctrl = 1,
44 .raster_order = 0,
45};
46
47/*
48 * Encoder:
49 */
50
51struct tfp410_encoder {
52 struct drm_encoder base;
53 struct tfp410_module *mod;
54 int dpms;
55};
56#define to_tfp410_encoder(x) container_of(x, struct tfp410_encoder, base)
57
58
59static void tfp410_encoder_destroy(struct drm_encoder *encoder)
60{
61 struct tfp410_encoder *tfp410_encoder = to_tfp410_encoder(encoder);
62 drm_encoder_cleanup(encoder);
63 kfree(tfp410_encoder);
64}
65
66static void tfp410_encoder_dpms(struct drm_encoder *encoder, int mode)
67{
68 struct tfp410_encoder *tfp410_encoder = to_tfp410_encoder(encoder);
69
70 if (tfp410_encoder->dpms == mode)
71 return;
72
73 if (mode == DRM_MODE_DPMS_ON) {
74 DBG("Power on");
75 gpio_direction_output(tfp410_encoder->mod->gpio, 1);
76 } else {
77 DBG("Power off");
78 gpio_direction_output(tfp410_encoder->mod->gpio, 0);
79 }
80
81 tfp410_encoder->dpms = mode;
82}
83
84static bool tfp410_encoder_mode_fixup(struct drm_encoder *encoder,
85 const struct drm_display_mode *mode,
86 struct drm_display_mode *adjusted_mode)
87{
88 /* nothing needed */
89 return true;
90}
91
92static void tfp410_encoder_prepare(struct drm_encoder *encoder)
93{
94 tfp410_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
95 tilcdc_crtc_set_panel_info(encoder->crtc, &dvi_info);
96}
97
98static void tfp410_encoder_commit(struct drm_encoder *encoder)
99{
100 tfp410_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
101}
102
103static void tfp410_encoder_mode_set(struct drm_encoder *encoder,
104 struct drm_display_mode *mode,
105 struct drm_display_mode *adjusted_mode)
106{
107 /* nothing needed */
108}
109
110static const struct drm_encoder_funcs tfp410_encoder_funcs = {
111 .destroy = tfp410_encoder_destroy,
112};
113
114static const struct drm_encoder_helper_funcs tfp410_encoder_helper_funcs = {
115 .dpms = tfp410_encoder_dpms,
116 .mode_fixup = tfp410_encoder_mode_fixup,
117 .prepare = tfp410_encoder_prepare,
118 .commit = tfp410_encoder_commit,
119 .mode_set = tfp410_encoder_mode_set,
120};
121
122static struct drm_encoder *tfp410_encoder_create(struct drm_device *dev,
123 struct tfp410_module *mod)
124{
125 struct tfp410_encoder *tfp410_encoder;
126 struct drm_encoder *encoder;
127 int ret;
128
129 tfp410_encoder = kzalloc(sizeof(*tfp410_encoder), GFP_KERNEL);
130 if (!tfp410_encoder) {
131 dev_err(dev->dev, "allocation failed\n");
132 return NULL;
133 }
134
135 tfp410_encoder->dpms = DRM_MODE_DPMS_OFF;
136 tfp410_encoder->mod = mod;
137
138 encoder = &tfp410_encoder->base;
139 encoder->possible_crtcs = 1;
140
141 ret = drm_encoder_init(dev, encoder, &tfp410_encoder_funcs,
142 DRM_MODE_ENCODER_TMDS);
143 if (ret < 0)
144 goto fail;
145
146 drm_encoder_helper_add(encoder, &tfp410_encoder_helper_funcs);
147
148 return encoder;
149
150fail:
151 tfp410_encoder_destroy(encoder);
152 return NULL;
153}
154
155/*
156 * Connector:
157 */
158
159struct tfp410_connector {
160 struct drm_connector base;
161
162 struct drm_encoder *encoder; /* our connected encoder */
163 struct tfp410_module *mod;
164};
165#define to_tfp410_connector(x) container_of(x, struct tfp410_connector, base)
166
167
168static void tfp410_connector_destroy(struct drm_connector *connector)
169{
170 struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector);
171 drm_connector_cleanup(connector);
172 kfree(tfp410_connector);
173}
174
175static enum drm_connector_status tfp410_connector_detect(
176 struct drm_connector *connector,
177 bool force)
178{
179 struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector);
180
181 if (drm_probe_ddc(tfp410_connector->mod->i2c))
182 return connector_status_connected;
183
184 return connector_status_unknown;
185}
186
187static int tfp410_connector_get_modes(struct drm_connector *connector)
188{
189 struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector);
190 struct edid *edid;
191 int ret = 0;
192
193 edid = drm_get_edid(connector, tfp410_connector->mod->i2c);
194
195 drm_mode_connector_update_edid_property(connector, edid);
196
197 if (edid) {
198 ret = drm_add_edid_modes(connector, edid);
199 kfree(edid);
200 }
201
202 return ret;
203}
204
205static int tfp410_connector_mode_valid(struct drm_connector *connector,
206 struct drm_display_mode *mode)
207{
208 struct tilcdc_drm_private *priv = connector->dev->dev_private;
209 /* our only constraints are what the crtc can generate: */
210 return tilcdc_crtc_mode_valid(priv->crtc, mode);
211}
212
213static struct drm_encoder *tfp410_connector_best_encoder(
214 struct drm_connector *connector)
215{
216 struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector);
217 return tfp410_connector->encoder;
218}
219
220static const struct drm_connector_funcs tfp410_connector_funcs = {
221 .destroy = tfp410_connector_destroy,
222 .dpms = drm_helper_connector_dpms,
223 .detect = tfp410_connector_detect,
224 .fill_modes = drm_helper_probe_single_connector_modes,
225};
226
227static const struct drm_connector_helper_funcs tfp410_connector_helper_funcs = {
228 .get_modes = tfp410_connector_get_modes,
229 .mode_valid = tfp410_connector_mode_valid,
230 .best_encoder = tfp410_connector_best_encoder,
231};
232
233static struct drm_connector *tfp410_connector_create(struct drm_device *dev,
234 struct tfp410_module *mod, struct drm_encoder *encoder)
235{
236 struct tfp410_connector *tfp410_connector;
237 struct drm_connector *connector;
238 int ret;
239
240 tfp410_connector = kzalloc(sizeof(*tfp410_connector), GFP_KERNEL);
241 if (!tfp410_connector) {
242 dev_err(dev->dev, "allocation failed\n");
243 return NULL;
244 }
245
246 tfp410_connector->encoder = encoder;
247 tfp410_connector->mod = mod;
248
249 connector = &tfp410_connector->base;
250
251 drm_connector_init(dev, connector, &tfp410_connector_funcs,
252 DRM_MODE_CONNECTOR_DVID);
253 drm_connector_helper_add(connector, &tfp410_connector_helper_funcs);
254
255 connector->polled = DRM_CONNECTOR_POLL_CONNECT |
256 DRM_CONNECTOR_POLL_DISCONNECT;
257
258 connector->interlace_allowed = 0;
259 connector->doublescan_allowed = 0;
260
261 ret = drm_mode_connector_attach_encoder(connector, encoder);
262 if (ret)
263 goto fail;
264
265 drm_sysfs_connector_add(connector);
266
267 return connector;
268
269fail:
270 tfp410_connector_destroy(connector);
271 return NULL;
272}
273
274/*
275 * Module:
276 */
277
278static int tfp410_modeset_init(struct tilcdc_module *mod, struct drm_device *dev)
279{
280 struct tfp410_module *tfp410_mod = to_tfp410_module(mod);
281 struct tilcdc_drm_private *priv = dev->dev_private;
282 struct drm_encoder *encoder;
283 struct drm_connector *connector;
284
285 encoder = tfp410_encoder_create(dev, tfp410_mod);
286 if (!encoder)
287 return -ENOMEM;
288
289 connector = tfp410_connector_create(dev, tfp410_mod, encoder);
290 if (!connector)
291 return -ENOMEM;
292
293 priv->encoders[priv->num_encoders++] = encoder;
294 priv->connectors[priv->num_connectors++] = connector;
295
296 return 0;
297}
298
299static void tfp410_destroy(struct tilcdc_module *mod)
300{
301 struct tfp410_module *tfp410_mod = to_tfp410_module(mod);
302
303 if (tfp410_mod->i2c)
304 i2c_put_adapter(tfp410_mod->i2c);
305
306 if (!IS_ERR_VALUE(tfp410_mod->gpio))
307 gpio_free(tfp410_mod->gpio);
308
309 tilcdc_module_cleanup(mod);
310 kfree(tfp410_mod);
311}
312
313static const struct tilcdc_module_ops tfp410_module_ops = {
314 .modeset_init = tfp410_modeset_init,
315 .destroy = tfp410_destroy,
316};
317
318/*
319 * Device:
320 */
321
322static struct of_device_id tfp410_of_match[];
323
324static int tfp410_probe(struct platform_device *pdev)
325{
326 struct device_node *node = pdev->dev.of_node;
327 struct device_node *i2c_node;
328 struct tfp410_module *tfp410_mod;
329 struct tilcdc_module *mod;
330 struct pinctrl *pinctrl;
331 uint32_t i2c_phandle;
332 int ret = -EINVAL;
333
334 /* bail out early if no DT data: */
335 if (!node) {
336 dev_err(&pdev->dev, "device-tree data is missing\n");
337 return -ENXIO;
338 }
339
340 tfp410_mod = kzalloc(sizeof(*tfp410_mod), GFP_KERNEL);
341 if (!tfp410_mod)
342 return -ENOMEM;
343
344 mod = &tfp410_mod->base;
345
346 tilcdc_module_init(mod, "tfp410", &tfp410_module_ops);
347
348 pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
349 if (IS_ERR(pinctrl))
350 dev_warn(&pdev->dev, "pins are not configured\n");
351
352 if (of_property_read_u32(node, "i2c", &i2c_phandle)) {
353 dev_err(&pdev->dev, "could not get i2c bus phandle\n");
354 goto fail;
355 }
356
357 i2c_node = of_find_node_by_phandle(i2c_phandle);
358 if (!i2c_node) {
359 dev_err(&pdev->dev, "could not get i2c bus node\n");
360 goto fail;
361 }
362
363 tfp410_mod->i2c = of_find_i2c_adapter_by_node(i2c_node);
364 if (!tfp410_mod->i2c) {
365 dev_err(&pdev->dev, "could not get i2c\n");
366 goto fail;
367 }
368
369 of_node_put(i2c_node);
370
371 tfp410_mod->gpio = of_get_named_gpio_flags(node, "powerdn-gpio",
372 0, NULL);
373 if (IS_ERR_VALUE(tfp410_mod->gpio)) {
374 dev_warn(&pdev->dev, "No power down GPIO\n");
375 } else {
376 ret = gpio_request(tfp410_mod->gpio, "DVI_PDn");
377 if (ret) {
378 dev_err(&pdev->dev, "could not get DVI_PDn gpio\n");
379 goto fail;
380 }
381 }
382
383 return 0;
384
385fail:
386 tfp410_destroy(mod);
387 return ret;
388}
389
390static int tfp410_remove(struct platform_device *pdev)
391{
392 return 0;
393}
394
395static struct of_device_id tfp410_of_match[] = {
396 { .compatible = "ti,tilcdc,tfp410", },
397 { },
398};
399MODULE_DEVICE_TABLE(of, tfp410_of_match);
400
401struct platform_driver tfp410_driver = {
402 .probe = tfp410_probe,
403 .remove = tfp410_remove,
404 .driver = {
405 .owner = THIS_MODULE,
406 .name = "tfp410",
407 .of_match_table = tfp410_of_match,
408 },
409};
410
411int __init tilcdc_tfp410_init(void)
412{
413 return platform_driver_register(&tfp410_driver);
414}
415
416void __exit tilcdc_tfp410_fini(void)
417{
418 platform_driver_unregister(&tfp410_driver);
419}