ARM: debug: qcom: add UART addresses to Kconfig help for APQ8084
[pandora-kernel.git] / drivers / video / fbdev / omap2 / displays-new / connector-hdmi.c
1 /*
2  * HDMI Connector driver
3  *
4  * Copyright (C) 2013 Texas Instruments
5  * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License version 2 as published by
9  * the Free Software Foundation.
10  */
11
12 #include <linux/slab.h>
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/of.h>
16
17 #include <drm/drm_edid.h>
18
19 #include <video/omapdss.h>
20 #include <video/omap-panel-data.h>
21
22 static const struct omap_video_timings hdmic_default_timings = {
23         .x_res          = 640,
24         .y_res          = 480,
25         .pixelclock     = 25175000,
26         .hsw            = 96,
27         .hfp            = 16,
28         .hbp            = 48,
29         .vsw            = 2,
30         .vfp            = 11,
31         .vbp            = 31,
32
33         .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
34         .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
35
36         .interlace      = false,
37 };
38
39 struct panel_drv_data {
40         struct omap_dss_device dssdev;
41         struct omap_dss_device *in;
42
43         struct device *dev;
44
45         struct omap_video_timings timings;
46 };
47
48 #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
49
50 static int hdmic_connect(struct omap_dss_device *dssdev)
51 {
52         struct panel_drv_data *ddata = to_panel_data(dssdev);
53         struct omap_dss_device *in = ddata->in;
54         int r;
55
56         dev_dbg(ddata->dev, "connect\n");
57
58         if (omapdss_device_is_connected(dssdev))
59                 return 0;
60
61         r = in->ops.hdmi->connect(in, dssdev);
62         if (r)
63                 return r;
64
65         return 0;
66 }
67
68 static void hdmic_disconnect(struct omap_dss_device *dssdev)
69 {
70         struct panel_drv_data *ddata = to_panel_data(dssdev);
71         struct omap_dss_device *in = ddata->in;
72
73         dev_dbg(ddata->dev, "disconnect\n");
74
75         if (!omapdss_device_is_connected(dssdev))
76                 return;
77
78         in->ops.hdmi->disconnect(in, dssdev);
79 }
80
81 static int hdmic_enable(struct omap_dss_device *dssdev)
82 {
83         struct panel_drv_data *ddata = to_panel_data(dssdev);
84         struct omap_dss_device *in = ddata->in;
85         int r;
86
87         dev_dbg(ddata->dev, "enable\n");
88
89         if (!omapdss_device_is_connected(dssdev))
90                 return -ENODEV;
91
92         if (omapdss_device_is_enabled(dssdev))
93                 return 0;
94
95         in->ops.hdmi->set_timings(in, &ddata->timings);
96
97         r = in->ops.hdmi->enable(in);
98         if (r)
99                 return r;
100
101         dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
102
103         return r;
104 }
105
106 static void hdmic_disable(struct omap_dss_device *dssdev)
107 {
108         struct panel_drv_data *ddata = to_panel_data(dssdev);
109         struct omap_dss_device *in = ddata->in;
110
111         dev_dbg(ddata->dev, "disable\n");
112
113         if (!omapdss_device_is_enabled(dssdev))
114                 return;
115
116         in->ops.hdmi->disable(in);
117
118         dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
119 }
120
121 static void hdmic_set_timings(struct omap_dss_device *dssdev,
122                 struct omap_video_timings *timings)
123 {
124         struct panel_drv_data *ddata = to_panel_data(dssdev);
125         struct omap_dss_device *in = ddata->in;
126
127         ddata->timings = *timings;
128         dssdev->panel.timings = *timings;
129
130         in->ops.hdmi->set_timings(in, timings);
131 }
132
133 static void hdmic_get_timings(struct omap_dss_device *dssdev,
134                 struct omap_video_timings *timings)
135 {
136         struct panel_drv_data *ddata = to_panel_data(dssdev);
137
138         *timings = ddata->timings;
139 }
140
141 static int hdmic_check_timings(struct omap_dss_device *dssdev,
142                 struct omap_video_timings *timings)
143 {
144         struct panel_drv_data *ddata = to_panel_data(dssdev);
145         struct omap_dss_device *in = ddata->in;
146
147         return in->ops.hdmi->check_timings(in, timings);
148 }
149
150 static int hdmic_read_edid(struct omap_dss_device *dssdev,
151                 u8 *edid, int len)
152 {
153         struct panel_drv_data *ddata = to_panel_data(dssdev);
154         struct omap_dss_device *in = ddata->in;
155
156         return in->ops.hdmi->read_edid(in, edid, len);
157 }
158
159 static bool hdmic_detect(struct omap_dss_device *dssdev)
160 {
161         struct panel_drv_data *ddata = to_panel_data(dssdev);
162         struct omap_dss_device *in = ddata->in;
163
164         return in->ops.hdmi->detect(in);
165 }
166
167 static int hdmic_audio_enable(struct omap_dss_device *dssdev)
168 {
169         struct panel_drv_data *ddata = to_panel_data(dssdev);
170         struct omap_dss_device *in = ddata->in;
171         int r;
172
173         /* enable audio only if the display is active */
174         if (!omapdss_device_is_enabled(dssdev))
175                 return -EPERM;
176
177         r = in->ops.hdmi->audio_enable(in);
178         if (r)
179                 return r;
180
181         dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED;
182
183         return 0;
184 }
185
186 static void hdmic_audio_disable(struct omap_dss_device *dssdev)
187 {
188         struct panel_drv_data *ddata = to_panel_data(dssdev);
189         struct omap_dss_device *in = ddata->in;
190
191         in->ops.hdmi->audio_disable(in);
192
193         dssdev->audio_state = OMAP_DSS_AUDIO_DISABLED;
194 }
195
196 static int hdmic_audio_start(struct omap_dss_device *dssdev)
197 {
198         struct panel_drv_data *ddata = to_panel_data(dssdev);
199         struct omap_dss_device *in = ddata->in;
200         int r;
201
202         /*
203          * No need to check the panel state. It was checked when trasitioning
204          * to AUDIO_ENABLED.
205          */
206         if (dssdev->audio_state != OMAP_DSS_AUDIO_ENABLED)
207                 return -EPERM;
208
209         r = in->ops.hdmi->audio_start(in);
210         if (r)
211                 return r;
212
213         dssdev->audio_state = OMAP_DSS_AUDIO_PLAYING;
214
215         return 0;
216 }
217
218 static void hdmic_audio_stop(struct omap_dss_device *dssdev)
219 {
220         struct panel_drv_data *ddata = to_panel_data(dssdev);
221         struct omap_dss_device *in = ddata->in;
222
223         in->ops.hdmi->audio_stop(in);
224
225         dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED;
226 }
227
228 static bool hdmic_audio_supported(struct omap_dss_device *dssdev)
229 {
230         struct panel_drv_data *ddata = to_panel_data(dssdev);
231         struct omap_dss_device *in = ddata->in;
232
233         if (!omapdss_device_is_enabled(dssdev))
234                 return false;
235
236         return in->ops.hdmi->audio_supported(in);
237 }
238
239 static int hdmic_audio_config(struct omap_dss_device *dssdev,
240                 struct omap_dss_audio *audio)
241 {
242         struct panel_drv_data *ddata = to_panel_data(dssdev);
243         struct omap_dss_device *in = ddata->in;
244         int r;
245
246         /* config audio only if the display is active */
247         if (!omapdss_device_is_enabled(dssdev))
248                 return -EPERM;
249
250         r = in->ops.hdmi->audio_config(in, audio);
251         if (r)
252                 return r;
253
254         dssdev->audio_state = OMAP_DSS_AUDIO_CONFIGURED;
255
256         return 0;
257 }
258
259 static struct omap_dss_driver hdmic_driver = {
260         .connect                = hdmic_connect,
261         .disconnect             = hdmic_disconnect,
262
263         .enable                 = hdmic_enable,
264         .disable                = hdmic_disable,
265
266         .set_timings            = hdmic_set_timings,
267         .get_timings            = hdmic_get_timings,
268         .check_timings          = hdmic_check_timings,
269
270         .get_resolution         = omapdss_default_get_resolution,
271
272         .read_edid              = hdmic_read_edid,
273         .detect                 = hdmic_detect,
274
275         .audio_enable           = hdmic_audio_enable,
276         .audio_disable          = hdmic_audio_disable,
277         .audio_start            = hdmic_audio_start,
278         .audio_stop             = hdmic_audio_stop,
279         .audio_supported        = hdmic_audio_supported,
280         .audio_config           = hdmic_audio_config,
281 };
282
283 static int hdmic_probe_pdata(struct platform_device *pdev)
284 {
285         struct panel_drv_data *ddata = platform_get_drvdata(pdev);
286         struct connector_hdmi_platform_data *pdata;
287         struct omap_dss_device *in, *dssdev;
288
289         pdata = dev_get_platdata(&pdev->dev);
290
291         in = omap_dss_find_output(pdata->source);
292         if (in == NULL) {
293                 dev_err(&pdev->dev, "Failed to find video source\n");
294                 return -EPROBE_DEFER;
295         }
296
297         ddata->in = in;
298
299         dssdev = &ddata->dssdev;
300         dssdev->name = pdata->name;
301
302         return 0;
303 }
304
305 static int hdmic_probe_of(struct platform_device *pdev)
306 {
307         struct panel_drv_data *ddata = platform_get_drvdata(pdev);
308         struct device_node *node = pdev->dev.of_node;
309         struct omap_dss_device *in;
310
311         in = omapdss_of_find_source_for_first_ep(node);
312         if (IS_ERR(in)) {
313                 dev_err(&pdev->dev, "failed to find video source\n");
314                 return PTR_ERR(in);
315         }
316
317         ddata->in = in;
318
319         return 0;
320 }
321
322 static int hdmic_probe(struct platform_device *pdev)
323 {
324         struct panel_drv_data *ddata;
325         struct omap_dss_device *dssdev;
326         int r;
327
328         ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
329         if (!ddata)
330                 return -ENOMEM;
331
332         platform_set_drvdata(pdev, ddata);
333         ddata->dev = &pdev->dev;
334
335         if (dev_get_platdata(&pdev->dev)) {
336                 r = hdmic_probe_pdata(pdev);
337                 if (r)
338                         return r;
339         } else if (pdev->dev.of_node) {
340                 r = hdmic_probe_of(pdev);
341                 if (r)
342                         return r;
343         } else {
344                 return -ENODEV;
345         }
346
347         ddata->timings = hdmic_default_timings;
348
349         dssdev = &ddata->dssdev;
350         dssdev->driver = &hdmic_driver;
351         dssdev->dev = &pdev->dev;
352         dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
353         dssdev->owner = THIS_MODULE;
354         dssdev->panel.timings = hdmic_default_timings;
355
356         r = omapdss_register_display(dssdev);
357         if (r) {
358                 dev_err(&pdev->dev, "Failed to register panel\n");
359                 goto err_reg;
360         }
361
362         return 0;
363 err_reg:
364         omap_dss_put_device(ddata->in);
365         return r;
366 }
367
368 static int __exit hdmic_remove(struct platform_device *pdev)
369 {
370         struct panel_drv_data *ddata = platform_get_drvdata(pdev);
371         struct omap_dss_device *dssdev = &ddata->dssdev;
372         struct omap_dss_device *in = ddata->in;
373
374         omapdss_unregister_display(&ddata->dssdev);
375
376         hdmic_disable(dssdev);
377         hdmic_disconnect(dssdev);
378
379         omap_dss_put_device(in);
380
381         return 0;
382 }
383
384 static const struct of_device_id hdmic_of_match[] = {
385         { .compatible = "omapdss,hdmi-connector", },
386         {},
387 };
388
389 MODULE_DEVICE_TABLE(of, hdmic_of_match);
390
391 static struct platform_driver hdmi_connector_driver = {
392         .probe  = hdmic_probe,
393         .remove = __exit_p(hdmic_remove),
394         .driver = {
395                 .name   = "connector-hdmi",
396                 .owner  = THIS_MODULE,
397                 .of_match_table = hdmic_of_match,
398         },
399 };
400
401 module_platform_driver(hdmi_connector_driver);
402
403 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
404 MODULE_DESCRIPTION("HDMI Connector driver");
405 MODULE_LICENSE("GPL");