blob: 44a885b92825fa2603060bddda40d50d76a53853 [file] [log] [blame]
Mythri P Kc3198a52011-03-12 12:04:27 +05301/*
2 * hdmi.c
3 *
4 * HDMI interface DSS driver setting for TI's OMAP4 family of processor.
5 * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
6 * Authors: Yong Zhi
7 * Mythri pk <mythripk@ti.com>
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License version 2 as published by
11 * the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU General Public License along with
19 * this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#define DSS_SUBSYS_NAME "HDMI"
23
24#include <linux/kernel.h>
25#include <linux/module.h>
26#include <linux/err.h>
27#include <linux/io.h>
28#include <linux/interrupt.h>
29#include <linux/mutex.h>
30#include <linux/delay.h>
31#include <linux/string.h>
Tomi Valkeinen24e62892011-05-23 11:51:18 +030032#include <linux/platform_device.h>
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +030033#include <linux/pm_runtime.h>
34#include <linux/clk.h>
Tomi Valkeinencca35012012-04-26 14:48:32 +030035#include <linux/gpio.h>
Tomi Valkeinen17486942012-08-15 15:55:04 +030036#include <linux/regulator/consumer.h>
Tomi Valkeinena0b38cc2011-05-11 14:05:07 +030037#include <video/omapdss.h>
Mythri P Kc3198a52011-03-12 12:04:27 +053038
Mythri P K94c52982011-09-08 19:06:21 +053039#include "ti_hdmi.h"
Mythri P Kc3198a52011-03-12 12:04:27 +053040#include "dss.h"
Ricardo Neriad44cc32011-05-18 22:31:56 -050041#include "dss_features.h"
Mythri P Kc3198a52011-03-12 12:04:27 +053042
Mythri P K95a8aeb2011-09-08 19:06:18 +053043#define HDMI_WP 0x0
44#define HDMI_CORE_SYS 0x400
45#define HDMI_CORE_AV 0x900
46#define HDMI_PLLCTRL 0x200
47#define HDMI_PHY 0x300
48
Mythri P K7c1f1ec2011-09-08 19:06:22 +053049/* HDMI EDID Length move this */
50#define HDMI_EDID_MAX_LENGTH 256
51#define EDID_TIMING_DESCRIPTOR_SIZE 0x12
52#define EDID_DESCRIPTOR_BLOCK0_ADDRESS 0x36
53#define EDID_DESCRIPTOR_BLOCK1_ADDRESS 0x80
54#define EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR 4
55#define EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR 4
56
Tomi Valkeinenb44e4582011-08-22 13:16:24 +030057#define HDMI_DEFAULT_REGN 16
Tomi Valkeinen8d887672011-08-22 13:02:52 +030058#define HDMI_DEFAULT_REGM2 1
59
Mythri P Kc3198a52011-03-12 12:04:27 +053060static struct {
61 struct mutex lock;
Mythri P Kc3198a52011-03-12 12:04:27 +053062 struct platform_device *pdev;
Ricardo Neri66a06b02012-11-06 00:19:14 -060063
Mythri P K95a8aeb2011-09-08 19:06:18 +053064 struct hdmi_ip_data ip_data;
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +030065
66 struct clk *sys_clk;
Tomi Valkeinen17486942012-08-15 15:55:04 +030067 struct regulator *vdda_hdmi_dac_reg;
Tomi Valkeinencca35012012-04-26 14:48:32 +030068
69 int ct_cp_hpd_gpio;
70 int ls_oe_gpio;
71 int hpd_gpio;
Archit Taneja81b87f52012-09-26 16:30:49 +053072
Tomi Valkeinen0b450c32013-05-24 13:20:17 +030073 bool core_enabled;
74
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +030075 struct omap_dss_device output;
Mythri P Kc3198a52011-03-12 12:04:27 +053076} hdmi;
77
78/*
79 * Logic for the below structure :
80 * user enters the CEA or VESA timings by specifying the HDMI/DVI code.
81 * There is a correspondence between CEA/VESA timing and code, please
82 * refer to section 6.3 in HDMI 1.3 specification for timing code.
83 *
84 * In the below structure, cea_vesa_timings corresponds to all OMAP4
85 * supported CEA and VESA timing values.code_cea corresponds to the CEA
86 * code, It is used to get the timing from cea_vesa_timing array.Similarly
87 * with code_vesa. Code_index is used for back mapping, that is once EDID
88 * is read from the TV, EDID is parsed to find the timing values and then
89 * map it to corresponding CEA or VESA index.
90 */
91
Mythri P K46095b22012-01-06 17:52:09 +053092static const struct hdmi_config cea_timings[] = {
Archit Tanejacc937e52012-06-24 13:08:10 +053093 {
94 { 640, 480, 25200, 96, 16, 48, 2, 10, 33,
95 OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
96 false, },
97 { 1, HDMI_HDMI },
98 },
99 {
100 { 720, 480, 27027, 62, 16, 60, 6, 9, 30,
101 OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
102 false, },
103 { 2, HDMI_HDMI },
104 },
105 {
106 { 1280, 720, 74250, 40, 110, 220, 5, 5, 20,
107 OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
108 false, },
109 { 4, HDMI_HDMI },
110 },
111 {
112 { 1920, 540, 74250, 44, 88, 148, 5, 2, 15,
113 OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
114 true, },
115 { 5, HDMI_HDMI },
116 },
117 {
118 { 1440, 240, 27027, 124, 38, 114, 3, 4, 15,
119 OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
120 true, },
121 { 6, HDMI_HDMI },
122 },
123 {
124 { 1920, 1080, 148500, 44, 88, 148, 5, 4, 36,
125 OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
126 false, },
127 { 16, HDMI_HDMI },
128 },
129 {
130 { 720, 576, 27000, 64, 12, 68, 5, 5, 39,
131 OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
132 false, },
133 { 17, HDMI_HDMI },
134 },
135 {
136 { 1280, 720, 74250, 40, 440, 220, 5, 5, 20,
137 OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
138 false, },
139 { 19, HDMI_HDMI },
140 },
141 {
142 { 1920, 540, 74250, 44, 528, 148, 5, 2, 15,
143 OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
144 true, },
145 { 20, HDMI_HDMI },
146 },
147 {
148 { 1440, 288, 27000, 126, 24, 138, 3, 2, 19,
149 OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
150 true, },
151 { 21, HDMI_HDMI },
152 },
153 {
154 { 1440, 576, 54000, 128, 24, 136, 5, 5, 39,
155 OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
156 false, },
157 { 29, HDMI_HDMI },
158 },
159 {
160 { 1920, 1080, 148500, 44, 528, 148, 5, 4, 36,
161 OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
162 false, },
163 { 31, HDMI_HDMI },
164 },
165 {
166 { 1920, 1080, 74250, 44, 638, 148, 5, 4, 36,
167 OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
168 false, },
169 { 32, HDMI_HDMI },
170 },
171 {
172 { 2880, 480, 108108, 248, 64, 240, 6, 9, 30,
173 OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
174 false, },
175 { 35, HDMI_HDMI },
176 },
177 {
178 { 2880, 576, 108000, 256, 48, 272, 5, 5, 39,
179 OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
180 false, },
181 { 37, HDMI_HDMI },
182 },
Mythri P K46095b22012-01-06 17:52:09 +0530183};
Archit Tanejacc937e52012-06-24 13:08:10 +0530184
Mythri P K46095b22012-01-06 17:52:09 +0530185static const struct hdmi_config vesa_timings[] = {
Mythri P Ka05ce782012-01-06 17:52:08 +0530186/* VESA From Here */
Archit Tanejacc937e52012-06-24 13:08:10 +0530187 {
188 { 640, 480, 25175, 96, 16, 48, 2, 11, 31,
189 OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
190 false, },
191 { 4, HDMI_DVI },
192 },
193 {
194 { 800, 600, 40000, 128, 40, 88, 4, 1, 23,
195 OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
196 false, },
197 { 9, HDMI_DVI },
198 },
199 {
200 { 848, 480, 33750, 112, 16, 112, 8, 6, 23,
201 OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
202 false, },
203 { 0xE, HDMI_DVI },
204 },
205 {
206 { 1280, 768, 79500, 128, 64, 192, 7, 3, 20,
207 OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
208 false, },
209 { 0x17, HDMI_DVI },
210 },
211 {
212 { 1280, 800, 83500, 128, 72, 200, 6, 3, 22,
213 OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
214 false, },
215 { 0x1C, HDMI_DVI },
216 },
217 {
218 { 1360, 768, 85500, 112, 64, 256, 6, 3, 18,
219 OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
220 false, },
221 { 0x27, HDMI_DVI },
222 },
223 {
224 { 1280, 960, 108000, 112, 96, 312, 3, 1, 36,
225 OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
226 false, },
227 { 0x20, HDMI_DVI },
228 },
229 {
230 { 1280, 1024, 108000, 112, 48, 248, 3, 1, 38,
231 OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
232 false, },
233 { 0x23, HDMI_DVI },
234 },
235 {
236 { 1024, 768, 65000, 136, 24, 160, 6, 3, 29,
237 OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
238 false, },
239 { 0x10, HDMI_DVI },
240 },
241 {
242 { 1400, 1050, 121750, 144, 88, 232, 4, 3, 32,
243 OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
244 false, },
245 { 0x2A, HDMI_DVI },
246 },
247 {
248 { 1440, 900, 106500, 152, 80, 232, 6, 3, 25,
249 OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
250 false, },
251 { 0x2F, HDMI_DVI },
252 },
253 {
254 { 1680, 1050, 146250, 176 , 104, 280, 6, 3, 30,
255 OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
256 false, },
257 { 0x3A, HDMI_DVI },
258 },
259 {
260 { 1366, 768, 85500, 143, 70, 213, 3, 3, 24,
261 OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
262 false, },
263 { 0x51, HDMI_DVI },
264 },
265 {
266 { 1920, 1080, 148500, 44, 148, 80, 5, 4, 36,
267 OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
268 false, },
269 { 0x52, HDMI_DVI },
270 },
271 {
272 { 1280, 768, 68250, 32, 48, 80, 7, 3, 12,
273 OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
274 false, },
275 { 0x16, HDMI_DVI },
276 },
277 {
278 { 1400, 1050, 101000, 32, 48, 80, 4, 3, 23,
279 OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
280 false, },
281 { 0x29, HDMI_DVI },
282 },
283 {
284 { 1680, 1050, 119000, 32, 48, 80, 6, 3, 21,
285 OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
286 false, },
287 { 0x39, HDMI_DVI },
288 },
289 {
290 { 1280, 800, 79500, 32, 48, 80, 6, 3, 14,
291 OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
292 false, },
293 { 0x1B, HDMI_DVI },
294 },
295 {
296 { 1280, 720, 74250, 40, 110, 220, 5, 5, 20,
297 OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
298 false, },
299 { 0x55, HDMI_DVI },
300 },
Tomi Valkeinen7a7ce2c2012-10-24 11:55:39 +0300301 {
302 { 1920, 1200, 154000, 32, 48, 80, 6, 3, 26,
303 OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
304 false, },
305 { 0x44, HDMI_DVI },
306 },
Mythri P Kc3198a52011-03-12 12:04:27 +0530307};
308
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300309static int hdmi_runtime_get(void)
310{
311 int r;
312
313 DSSDBG("hdmi_runtime_get\n");
314
315 r = pm_runtime_get_sync(&hdmi.pdev->dev);
316 WARN_ON(r < 0);
Archit Tanejaa247ce72012-02-10 11:45:52 +0530317 if (r < 0)
Tomi Valkeinen852f0832012-02-17 17:58:04 +0200318 return r;
Archit Tanejaa247ce72012-02-10 11:45:52 +0530319
320 return 0;
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300321}
322
323static void hdmi_runtime_put(void)
324{
325 int r;
326
327 DSSDBG("hdmi_runtime_put\n");
328
Tomi Valkeinen0eaf9f52012-01-23 13:23:08 +0200329 r = pm_runtime_put_sync(&hdmi.pdev->dev);
Tomi Valkeinen5be3aeb2012-06-27 16:37:18 +0300330 WARN_ON(r < 0 && r != -ENOSYS);
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300331}
332
Tomi Valkeinene25001d2013-05-10 15:20:52 +0300333static int hdmi_init_regulator(void)
334{
335 struct regulator *reg;
336
337 if (hdmi.vdda_hdmi_dac_reg != NULL)
338 return 0;
339
340 reg = devm_regulator_get(&hdmi.pdev->dev, "vdda_hdmi_dac");
341
342 /* DT HACK: try VDAC to make omapdss work for o4 sdp/panda */
343 if (IS_ERR(reg))
344 reg = devm_regulator_get(&hdmi.pdev->dev, "VDAC");
345
346 if (IS_ERR(reg)) {
347 DSSERR("can't get VDDA_HDMI_DAC regulator\n");
348 return PTR_ERR(reg);
349 }
350
351 hdmi.vdda_hdmi_dac_reg = reg;
352
353 return 0;
354}
355
Tomi Valkeinen17ae4e82013-04-26 14:48:43 +0300356static int hdmi_init_display(struct omap_dss_device *dssdev)
Mythri P Kc3198a52011-03-12 12:04:27 +0530357{
Tomi Valkeinencca35012012-04-26 14:48:32 +0300358 int r;
359
360 struct gpio gpios[] = {
361 { hdmi.ct_cp_hpd_gpio, GPIOF_OUT_INIT_LOW, "hdmi_ct_cp_hpd" },
362 { hdmi.ls_oe_gpio, GPIOF_OUT_INIT_LOW, "hdmi_ls_oe" },
363 { hdmi.hpd_gpio, GPIOF_DIR_IN, "hdmi_hpd" },
364 };
365
Mythri P Kc3198a52011-03-12 12:04:27 +0530366 DSSDBG("init_display\n");
367
Tomi Valkeinenb2c7d542012-10-18 13:46:29 +0300368 dss_init_hdmi_ip_ops(&hdmi.ip_data, omapdss_get_version());
Tomi Valkeinencca35012012-04-26 14:48:32 +0300369
Tomi Valkeinene25001d2013-05-10 15:20:52 +0300370 r = hdmi_init_regulator();
371 if (r)
372 return r;
Tomi Valkeinen17486942012-08-15 15:55:04 +0300373
Tomi Valkeinencca35012012-04-26 14:48:32 +0300374 r = gpio_request_array(gpios, ARRAY_SIZE(gpios));
375 if (r)
376 return r;
377
Mythri P Kc3198a52011-03-12 12:04:27 +0530378 return 0;
379}
380
Ricardo Neri37584762012-11-06 21:37:14 -0600381static void hdmi_uninit_display(struct omap_dss_device *dssdev)
Tomi Valkeinencca35012012-04-26 14:48:32 +0300382{
383 DSSDBG("uninit_display\n");
384
385 gpio_free(hdmi.ct_cp_hpd_gpio);
386 gpio_free(hdmi.ls_oe_gpio);
387 gpio_free(hdmi.hpd_gpio);
388}
389
Mythri P K46095b22012-01-06 17:52:09 +0530390static const struct hdmi_config *hdmi_find_timing(
391 const struct hdmi_config *timings_arr,
392 int len)
Mythri P Kc3198a52011-03-12 12:04:27 +0530393{
Mythri P K46095b22012-01-06 17:52:09 +0530394 int i;
Mythri P Kc3198a52011-03-12 12:04:27 +0530395
Mythri P K46095b22012-01-06 17:52:09 +0530396 for (i = 0; i < len; i++) {
Mythri P K9e4ed602012-01-06 17:52:10 +0530397 if (timings_arr[i].cm.code == hdmi.ip_data.cfg.cm.code)
Mythri P K46095b22012-01-06 17:52:09 +0530398 return &timings_arr[i];
Mythri P Kc3198a52011-03-12 12:04:27 +0530399 }
Mythri P K46095b22012-01-06 17:52:09 +0530400 return NULL;
401}
402
403static const struct hdmi_config *hdmi_get_timings(void)
404{
405 const struct hdmi_config *arr;
406 int len;
407
Mythri P K9e4ed602012-01-06 17:52:10 +0530408 if (hdmi.ip_data.cfg.cm.mode == HDMI_DVI) {
Mythri P K46095b22012-01-06 17:52:09 +0530409 arr = vesa_timings;
410 len = ARRAY_SIZE(vesa_timings);
411 } else {
412 arr = cea_timings;
413 len = ARRAY_SIZE(cea_timings);
414 }
415
416 return hdmi_find_timing(arr, len);
417}
418
419static bool hdmi_timings_compare(struct omap_video_timings *timing1,
Archit Tanejacc937e52012-06-24 13:08:10 +0530420 const struct omap_video_timings *timing2)
Mythri P K46095b22012-01-06 17:52:09 +0530421{
422 int timing1_vsync, timing1_hsync, timing2_vsync, timing2_hsync;
423
Tomi Valkeinenf236b892012-10-24 11:55:54 +0300424 if ((DIV_ROUND_CLOSEST(timing2->pixel_clock, 1000) ==
425 DIV_ROUND_CLOSEST(timing1->pixel_clock, 1000)) &&
Mythri P K46095b22012-01-06 17:52:09 +0530426 (timing2->x_res == timing1->x_res) &&
427 (timing2->y_res == timing1->y_res)) {
428
429 timing2_hsync = timing2->hfp + timing2->hsw + timing2->hbp;
430 timing1_hsync = timing1->hfp + timing1->hsw + timing1->hbp;
431 timing2_vsync = timing2->vfp + timing2->vsw + timing2->vbp;
432 timing1_vsync = timing2->vfp + timing2->vsw + timing2->vbp;
433
434 DSSDBG("timing1_hsync = %d timing1_vsync = %d"\
435 "timing2_hsync = %d timing2_vsync = %d\n",
436 timing1_hsync, timing1_vsync,
437 timing2_hsync, timing2_vsync);
438
439 if ((timing1_hsync == timing2_hsync) &&
440 (timing1_vsync == timing2_vsync)) {
441 return true;
442 }
443 }
444 return false;
Mythri P Kc3198a52011-03-12 12:04:27 +0530445}
446
447static struct hdmi_cm hdmi_get_code(struct omap_video_timings *timing)
448{
Mythri P K46095b22012-01-06 17:52:09 +0530449 int i;
Mythri P Kc3198a52011-03-12 12:04:27 +0530450 struct hdmi_cm cm = {-1};
451 DSSDBG("hdmi_get_code\n");
452
Mythri P K46095b22012-01-06 17:52:09 +0530453 for (i = 0; i < ARRAY_SIZE(cea_timings); i++) {
454 if (hdmi_timings_compare(timing, &cea_timings[i].timings)) {
455 cm = cea_timings[i].cm;
456 goto end;
457 }
458 }
459 for (i = 0; i < ARRAY_SIZE(vesa_timings); i++) {
460 if (hdmi_timings_compare(timing, &vesa_timings[i].timings)) {
461 cm = vesa_timings[i].cm;
462 goto end;
Mythri P Kc3198a52011-03-12 12:04:27 +0530463 }
464 }
465
Mythri P K46095b22012-01-06 17:52:09 +0530466end: return cm;
Mythri P Kc3198a52011-03-12 12:04:27 +0530467
Mythri P Kc3198a52011-03-12 12:04:27 +0530468}
469
Archit Taneja6cb07b22011-04-12 13:52:25 +0530470static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy,
471 struct hdmi_pll_info *pi)
Mythri P Kc3198a52011-03-12 12:04:27 +0530472{
Archit Taneja6cb07b22011-04-12 13:52:25 +0530473 unsigned long clkin, refclk;
Mythri P Kc3198a52011-03-12 12:04:27 +0530474 u32 mf;
475
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300476 clkin = clk_get_rate(hdmi.sys_clk) / 10000;
Mythri P Kc3198a52011-03-12 12:04:27 +0530477 /*
478 * Input clock is predivided by N + 1
479 * out put of which is reference clk
480 */
Tomi Valkeinen4fdfdf02013-02-12 15:15:21 +0200481
482 pi->regn = HDMI_DEFAULT_REGN;
Tomi Valkeinen8d887672011-08-22 13:02:52 +0300483
Tomi Valkeinenb44e4582011-08-22 13:16:24 +0300484 refclk = clkin / pi->regn;
Mythri P Kc3198a52011-03-12 12:04:27 +0530485
Tomi Valkeinen4fdfdf02013-02-12 15:15:21 +0200486 pi->regm2 = HDMI_DEFAULT_REGM2;
Mythri P Kc3198a52011-03-12 12:04:27 +0530487
488 /*
Mythri P Kdd2116a2012-02-21 12:10:58 +0530489 * multiplier is pixel_clk/ref_clk
490 * Multiplying by 100 to avoid fractional part removal
491 */
492 pi->regm = phy * pi->regm2 / refclk;
493
494 /*
Mythri P Kc3198a52011-03-12 12:04:27 +0530495 * fractional multiplier is remainder of the difference between
496 * multiplier and actual phy(required pixel clock thus should be
497 * multiplied by 2^18(262144) divided by the reference clock
498 */
Mythri P Kdd2116a2012-02-21 12:10:58 +0530499 mf = (phy - pi->regm / pi->regm2 * refclk) * 262144;
500 pi->regmf = pi->regm2 * mf / refclk;
Mythri P Kc3198a52011-03-12 12:04:27 +0530501
502 /*
503 * Dcofreq should be set to 1 if required pixel clock
504 * is greater than 1000MHz
505 */
506 pi->dcofreq = phy > 1000 * 100;
Tomi Valkeinenb44e4582011-08-22 13:16:24 +0300507 pi->regsd = ((pi->regm * clkin / 10) / (pi->regn * 250) + 5) / 10;
Mythri P Kc3198a52011-03-12 12:04:27 +0530508
Mythri P K7b27da52011-09-08 19:06:19 +0530509 /* Set the reference clock to sysclk reference */
510 pi->refsel = HDMI_REFSEL_SYSCLK;
511
Mythri P Kc3198a52011-03-12 12:04:27 +0530512 DSSDBG("M = %d Mf = %d\n", pi->regm, pi->regmf);
513 DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd);
514}
515
Tomi Valkeinenbb426fc2012-10-19 17:42:10 +0300516static int hdmi_power_on_core(struct omap_dss_device *dssdev)
Mythri P Kc3198a52011-03-12 12:04:27 +0530517{
Mythri P K46095b22012-01-06 17:52:09 +0530518 int r;
Mythri P Kc3198a52011-03-12 12:04:27 +0530519
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300520 if (gpio_is_valid(hdmi.ct_cp_hpd_gpio))
521 gpio_set_value(hdmi.ct_cp_hpd_gpio, 1);
522 if (gpio_is_valid(hdmi.ls_oe_gpio))
523 gpio_set_value(hdmi.ls_oe_gpio, 1);
Tomi Valkeinencca35012012-04-26 14:48:32 +0300524
Tomi Valkeinena84b2062012-04-26 14:58:41 +0300525 /* wait 300us after CT_CP_HPD for the 5V power output to reach 90% */
526 udelay(300);
527
Tomi Valkeinen17486942012-08-15 15:55:04 +0300528 r = regulator_enable(hdmi.vdda_hdmi_dac_reg);
529 if (r)
530 goto err_vdac_enable;
531
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300532 r = hdmi_runtime_get();
533 if (r)
Tomi Valkeinencca35012012-04-26 14:48:32 +0300534 goto err_runtime_get;
Mythri P Kc3198a52011-03-12 12:04:27 +0530535
Tomi Valkeinenbb426fc2012-10-19 17:42:10 +0300536 /* Make selection of HDMI in DSS */
537 dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK);
538
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300539 hdmi.core_enabled = true;
540
Tomi Valkeinenbb426fc2012-10-19 17:42:10 +0300541 return 0;
542
543err_runtime_get:
544 regulator_disable(hdmi.vdda_hdmi_dac_reg);
545err_vdac_enable:
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300546 if (gpio_is_valid(hdmi.ct_cp_hpd_gpio))
547 gpio_set_value(hdmi.ct_cp_hpd_gpio, 0);
548 if (gpio_is_valid(hdmi.ls_oe_gpio))
549 gpio_set_value(hdmi.ls_oe_gpio, 0);
Tomi Valkeinenbb426fc2012-10-19 17:42:10 +0300550 return r;
551}
552
553static void hdmi_power_off_core(struct omap_dss_device *dssdev)
554{
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300555 hdmi.core_enabled = false;
556
Tomi Valkeinenbb426fc2012-10-19 17:42:10 +0300557 hdmi_runtime_put();
558 regulator_disable(hdmi.vdda_hdmi_dac_reg);
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300559 if (gpio_is_valid(hdmi.ct_cp_hpd_gpio))
560 gpio_set_value(hdmi.ct_cp_hpd_gpio, 0);
561 if (gpio_is_valid(hdmi.ls_oe_gpio))
562 gpio_set_value(hdmi.ls_oe_gpio, 0);
Tomi Valkeinenbb426fc2012-10-19 17:42:10 +0300563}
564
565static int hdmi_power_on_full(struct omap_dss_device *dssdev)
566{
567 int r;
568 struct omap_video_timings *p;
Tomi Valkeinen7ae9a712013-05-10 15:27:07 +0300569 struct omap_overlay_manager *mgr = hdmi.output.manager;
Tomi Valkeinenbb426fc2012-10-19 17:42:10 +0300570 unsigned long phy;
571
572 r = hdmi_power_on_core(dssdev);
573 if (r)
574 return r;
575
Archit Tanejacea87b92012-09-07 17:56:20 +0530576 dss_mgr_disable(mgr);
Mythri P Kc3198a52011-03-12 12:04:27 +0530577
Archit Taneja78493982012-08-08 16:50:42 +0530578 p = &hdmi.ip_data.cfg.timings;
Mythri P Kc3198a52011-03-12 12:04:27 +0530579
Archit Taneja78493982012-08-08 16:50:42 +0530580 DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res);
Mythri P Kc3198a52011-03-12 12:04:27 +0530581
Mythri P Kc3198a52011-03-12 12:04:27 +0530582 phy = p->pixel_clock;
583
Mythri P K7b27da52011-09-08 19:06:19 +0530584 hdmi_compute_pll(dssdev, phy, &hdmi.ip_data.pll_data);
Mythri P Kc3198a52011-03-12 12:04:27 +0530585
Ricardo Neric0456be2012-04-27 13:48:45 -0500586 hdmi.ip_data.ops->video_disable(&hdmi.ip_data);
Mythri P Kc3198a52011-03-12 12:04:27 +0530587
Mythri P K95a8aeb2011-09-08 19:06:18 +0530588 /* config the PLL and PHY hdmi_set_pll_pwrfirst */
Mythri P K60634a22011-09-08 19:06:26 +0530589 r = hdmi.ip_data.ops->pll_enable(&hdmi.ip_data);
Mythri P Kc3198a52011-03-12 12:04:27 +0530590 if (r) {
591 DSSDBG("Failed to lock PLL\n");
Tomi Valkeinencca35012012-04-26 14:48:32 +0300592 goto err_pll_enable;
Mythri P Kc3198a52011-03-12 12:04:27 +0530593 }
594
Mythri P K60634a22011-09-08 19:06:26 +0530595 r = hdmi.ip_data.ops->phy_enable(&hdmi.ip_data);
Mythri P Kc3198a52011-03-12 12:04:27 +0530596 if (r) {
597 DSSDBG("Failed to start PHY\n");
Ricardo Nerid3b4aa52012-07-30 19:12:02 -0500598 goto err_phy_enable;
Mythri P Kc3198a52011-03-12 12:04:27 +0530599 }
600
Mythri P K60634a22011-09-08 19:06:26 +0530601 hdmi.ip_data.ops->video_configure(&hdmi.ip_data);
Mythri P Kc3198a52011-03-12 12:04:27 +0530602
Mythri P Kc3198a52011-03-12 12:04:27 +0530603 /* bypass TV gamma table */
604 dispc_enable_gamma_table(0);
605
606 /* tv size */
Archit Tanejacea87b92012-09-07 17:56:20 +0530607 dss_mgr_set_timings(mgr, p);
Mythri P Kc3198a52011-03-12 12:04:27 +0530608
Ricardo Neric0456be2012-04-27 13:48:45 -0500609 r = hdmi.ip_data.ops->video_enable(&hdmi.ip_data);
610 if (r)
611 goto err_vid_enable;
Mythri P Kc3198a52011-03-12 12:04:27 +0530612
Archit Tanejacea87b92012-09-07 17:56:20 +0530613 r = dss_mgr_enable(mgr);
Tomi Valkeinen33ca2372011-11-21 13:42:58 +0200614 if (r)
615 goto err_mgr_enable;
Tomi Valkeinen3870c902011-08-31 14:47:11 +0300616
Mythri P Kc3198a52011-03-12 12:04:27 +0530617 return 0;
Tomi Valkeinen33ca2372011-11-21 13:42:58 +0200618
619err_mgr_enable:
Ricardo Neric0456be2012-04-27 13:48:45 -0500620 hdmi.ip_data.ops->video_disable(&hdmi.ip_data);
621err_vid_enable:
Tomi Valkeinen33ca2372011-11-21 13:42:58 +0200622 hdmi.ip_data.ops->phy_disable(&hdmi.ip_data);
Ricardo Nerid3b4aa52012-07-30 19:12:02 -0500623err_phy_enable:
Tomi Valkeinen33ca2372011-11-21 13:42:58 +0200624 hdmi.ip_data.ops->pll_disable(&hdmi.ip_data);
Tomi Valkeinencca35012012-04-26 14:48:32 +0300625err_pll_enable:
Tomi Valkeinenbb426fc2012-10-19 17:42:10 +0300626 hdmi_power_off_core(dssdev);
Mythri P Kc3198a52011-03-12 12:04:27 +0530627 return -EIO;
628}
629
Tomi Valkeinenbb426fc2012-10-19 17:42:10 +0300630static void hdmi_power_off_full(struct omap_dss_device *dssdev)
Mythri P Kc3198a52011-03-12 12:04:27 +0530631{
Tomi Valkeinen7ae9a712013-05-10 15:27:07 +0300632 struct omap_overlay_manager *mgr = hdmi.output.manager;
Archit Tanejacea87b92012-09-07 17:56:20 +0530633
634 dss_mgr_disable(mgr);
Mythri P Kc3198a52011-03-12 12:04:27 +0530635
Ricardo Neric0456be2012-04-27 13:48:45 -0500636 hdmi.ip_data.ops->video_disable(&hdmi.ip_data);
Mythri P K60634a22011-09-08 19:06:26 +0530637 hdmi.ip_data.ops->phy_disable(&hdmi.ip_data);
638 hdmi.ip_data.ops->pll_disable(&hdmi.ip_data);
Tomi Valkeinencca35012012-04-26 14:48:32 +0300639
Tomi Valkeinenbb426fc2012-10-19 17:42:10 +0300640 hdmi_power_off_core(dssdev);
Mythri P Kc3198a52011-03-12 12:04:27 +0530641}
642
643int omapdss_hdmi_display_check_timing(struct omap_dss_device *dssdev,
644 struct omap_video_timings *timings)
645{
646 struct hdmi_cm cm;
647
648 cm = hdmi_get_code(timings);
649 if (cm.code == -1) {
Mythri P Kc3198a52011-03-12 12:04:27 +0530650 return -EINVAL;
651 }
652
653 return 0;
654
655}
656
Archit Taneja78493982012-08-08 16:50:42 +0530657void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev,
658 struct omap_video_timings *timings)
Mythri P Kc3198a52011-03-12 12:04:27 +0530659{
660 struct hdmi_cm cm;
Archit Taneja78493982012-08-08 16:50:42 +0530661 const struct hdmi_config *t;
Mythri P Kc3198a52011-03-12 12:04:27 +0530662
Archit Tanejaed1aa902012-08-15 00:40:31 +0530663 mutex_lock(&hdmi.lock);
664
Archit Taneja78493982012-08-08 16:50:42 +0530665 cm = hdmi_get_code(timings);
666 hdmi.ip_data.cfg.cm = cm;
667
668 t = hdmi_get_timings();
669 if (t != NULL)
670 hdmi.ip_data.cfg = *t;
Tomi Valkeinenfa70dc52011-08-22 14:57:33 +0300671
Tomi Valkeinen5391e872013-05-16 10:44:13 +0300672 dispc_set_tv_pclk(t->timings.pixel_clock * 1000);
673
Archit Tanejaed1aa902012-08-15 00:40:31 +0530674 mutex_unlock(&hdmi.lock);
Mythri P Kc3198a52011-03-12 12:04:27 +0530675}
676
Tomi Valkeinen0b450c32013-05-24 13:20:17 +0300677static void omapdss_hdmi_display_get_timings(struct omap_dss_device *dssdev,
678 struct omap_video_timings *timings)
679{
680 const struct hdmi_config *cfg;
681
682 cfg = hdmi_get_timings();
683 if (cfg == NULL)
684 cfg = &vesa_timings[0];
685
686 memcpy(timings, &cfg->timings, sizeof(cfg->timings));
687}
688
Tomi Valkeinene40402c2012-03-02 18:01:07 +0200689static void hdmi_dump_regs(struct seq_file *s)
Mythri P K162874d2011-09-22 13:37:45 +0530690{
691 mutex_lock(&hdmi.lock);
692
Wei Yongjunf8fb7d72012-10-21 20:54:26 +0800693 if (hdmi_runtime_get()) {
694 mutex_unlock(&hdmi.lock);
Mythri P K162874d2011-09-22 13:37:45 +0530695 return;
Wei Yongjunf8fb7d72012-10-21 20:54:26 +0800696 }
Mythri P K162874d2011-09-22 13:37:45 +0530697
698 hdmi.ip_data.ops->dump_wrapper(&hdmi.ip_data, s);
699 hdmi.ip_data.ops->dump_pll(&hdmi.ip_data, s);
700 hdmi.ip_data.ops->dump_phy(&hdmi.ip_data, s);
701 hdmi.ip_data.ops->dump_core(&hdmi.ip_data, s);
702
703 hdmi_runtime_put();
704 mutex_unlock(&hdmi.lock);
705}
706
Tomi Valkeinen47024562011-08-25 17:12:56 +0300707int omapdss_hdmi_read_edid(u8 *buf, int len)
708{
709 int r;
710
711 mutex_lock(&hdmi.lock);
712
713 r = hdmi_runtime_get();
714 BUG_ON(r);
715
716 r = hdmi.ip_data.ops->read_edid(&hdmi.ip_data, buf, len);
717
718 hdmi_runtime_put();
719 mutex_unlock(&hdmi.lock);
720
721 return r;
722}
723
Tomi Valkeinen759593f2011-08-29 18:10:20 +0300724bool omapdss_hdmi_detect(void)
725{
726 int r;
727
728 mutex_lock(&hdmi.lock);
729
730 r = hdmi_runtime_get();
731 BUG_ON(r);
732
Tomi Valkeinen29356be2013-06-06 13:20:37 +0300733 r = gpio_get_value(hdmi.hpd_gpio);
Tomi Valkeinen759593f2011-08-29 18:10:20 +0300734
735 hdmi_runtime_put();
736 mutex_unlock(&hdmi.lock);
737
738 return r == 1;
739}
740
Mythri P Kc3198a52011-03-12 12:04:27 +0530741int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev)
742{
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +0300743 struct omap_dss_device *out = &hdmi.output;
Mythri P Kc3198a52011-03-12 12:04:27 +0530744 int r = 0;
745
746 DSSDBG("ENTER hdmi_display_enable\n");
747
748 mutex_lock(&hdmi.lock);
749
Archit Tanejacea87b92012-09-07 17:56:20 +0530750 if (out == NULL || out->manager == NULL) {
751 DSSERR("failed to enable display: no output/manager\n");
Tomi Valkeinen05e1d602011-06-23 16:38:21 +0300752 r = -ENODEV;
753 goto err0;
754 }
755
Tomi Valkeinenbb426fc2012-10-19 17:42:10 +0300756 r = hdmi_power_on_full(dssdev);
Mythri P Kc3198a52011-03-12 12:04:27 +0530757 if (r) {
758 DSSERR("failed to power on device\n");
Tomi Valkeinend3923932013-04-25 13:12:07 +0300759 goto err0;
Mythri P Kc3198a52011-03-12 12:04:27 +0530760 }
761
762 mutex_unlock(&hdmi.lock);
763 return 0;
764
Mythri P Kc3198a52011-03-12 12:04:27 +0530765err0:
766 mutex_unlock(&hdmi.lock);
767 return r;
768}
769
770void omapdss_hdmi_display_disable(struct omap_dss_device *dssdev)
771{
772 DSSDBG("Enter hdmi_display_disable\n");
773
774 mutex_lock(&hdmi.lock);
775
Tomi Valkeinenbb426fc2012-10-19 17:42:10 +0300776 hdmi_power_off_full(dssdev);
Mythri P Kc3198a52011-03-12 12:04:27 +0530777
Mythri P Kc3198a52011-03-12 12:04:27 +0530778 mutex_unlock(&hdmi.lock);
779}
780
Tomi Valkeinen44898232012-10-19 17:42:27 +0300781int omapdss_hdmi_core_enable(struct omap_dss_device *dssdev)
782{
783 int r = 0;
784
785 DSSDBG("ENTER omapdss_hdmi_core_enable\n");
786
787 mutex_lock(&hdmi.lock);
788
Tomi Valkeinen44898232012-10-19 17:42:27 +0300789 r = hdmi_power_on_core(dssdev);
790 if (r) {
791 DSSERR("failed to power on device\n");
792 goto err0;
793 }
794
795 mutex_unlock(&hdmi.lock);
796 return 0;
797
798err0:
799 mutex_unlock(&hdmi.lock);
800 return r;
801}
802
803void omapdss_hdmi_core_disable(struct omap_dss_device *dssdev)
804{
805 DSSDBG("Enter omapdss_hdmi_core_disable\n");
806
807 mutex_lock(&hdmi.lock);
808
809 hdmi_power_off_core(dssdev);
810
811 mutex_unlock(&hdmi.lock);
812}
813
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300814static int hdmi_get_clocks(struct platform_device *pdev)
815{
816 struct clk *clk;
817
Archit Tanejab2c9c8e2013-04-08 11:55:00 +0300818 clk = devm_clk_get(&pdev->dev, "sys_clk");
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300819 if (IS_ERR(clk)) {
820 DSSERR("can't get sys_clk\n");
821 return PTR_ERR(clk);
822 }
823
824 hdmi.sys_clk = clk;
825
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300826 return 0;
827}
828
Ricardo Neri35547622012-03-20 21:02:01 -0600829#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
830int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts)
831{
832 u32 deep_color;
Ricardo Neri25a65352012-03-23 15:49:02 -0600833 bool deep_color_correct = false;
Ricardo Neri35547622012-03-20 21:02:01 -0600834 u32 pclk = hdmi.ip_data.cfg.timings.pixel_clock;
835
836 if (n == NULL || cts == NULL)
837 return -EINVAL;
838
839 /* TODO: When implemented, query deep color mode here. */
840 deep_color = 100;
841
Ricardo Neri25a65352012-03-23 15:49:02 -0600842 /*
843 * When using deep color, the default N value (as in the HDMI
844 * specification) yields to an non-integer CTS. Hence, we
845 * modify it while keeping the restrictions described in
846 * section 7.2.1 of the HDMI 1.4a specification.
847 */
Ricardo Neri35547622012-03-20 21:02:01 -0600848 switch (sample_freq) {
849 case 32000:
Ricardo Neri25a65352012-03-23 15:49:02 -0600850 case 48000:
851 case 96000:
852 case 192000:
853 if (deep_color == 125)
854 if (pclk == 27027 || pclk == 74250)
855 deep_color_correct = true;
856 if (deep_color == 150)
857 if (pclk == 27027)
858 deep_color_correct = true;
Ricardo Neri35547622012-03-20 21:02:01 -0600859 break;
860 case 44100:
Ricardo Neri25a65352012-03-23 15:49:02 -0600861 case 88200:
862 case 176400:
863 if (deep_color == 125)
864 if (pclk == 27027)
865 deep_color_correct = true;
Ricardo Neri35547622012-03-20 21:02:01 -0600866 break;
867 default:
Ricardo Neri35547622012-03-20 21:02:01 -0600868 return -EINVAL;
869 }
870
Ricardo Neri25a65352012-03-23 15:49:02 -0600871 if (deep_color_correct) {
872 switch (sample_freq) {
873 case 32000:
874 *n = 8192;
875 break;
876 case 44100:
877 *n = 12544;
878 break;
879 case 48000:
880 *n = 8192;
881 break;
882 case 88200:
883 *n = 25088;
884 break;
885 case 96000:
886 *n = 16384;
887 break;
888 case 176400:
889 *n = 50176;
890 break;
891 case 192000:
892 *n = 32768;
893 break;
894 default:
895 return -EINVAL;
896 }
897 } else {
898 switch (sample_freq) {
899 case 32000:
900 *n = 4096;
901 break;
902 case 44100:
903 *n = 6272;
904 break;
905 case 48000:
906 *n = 6144;
907 break;
908 case 88200:
909 *n = 12544;
910 break;
911 case 96000:
912 *n = 12288;
913 break;
914 case 176400:
915 *n = 25088;
916 break;
917 case 192000:
918 *n = 24576;
919 break;
920 default:
921 return -EINVAL;
922 }
923 }
Ricardo Neri35547622012-03-20 21:02:01 -0600924 /* Calculate CTS. See HDMI 1.3a or 1.4a specifications */
925 *cts = pclk * (*n / 128) * deep_color / (sample_freq / 10);
926
927 return 0;
928}
Ricardo Nerif3a974912012-05-09 21:09:50 -0500929
930int hdmi_audio_enable(void)
931{
932 DSSDBG("audio_enable\n");
933
934 return hdmi.ip_data.ops->audio_enable(&hdmi.ip_data);
935}
936
937void hdmi_audio_disable(void)
938{
939 DSSDBG("audio_disable\n");
940
941 hdmi.ip_data.ops->audio_disable(&hdmi.ip_data);
942}
943
944int hdmi_audio_start(void)
945{
946 DSSDBG("audio_start\n");
947
948 return hdmi.ip_data.ops->audio_start(&hdmi.ip_data);
949}
950
951void hdmi_audio_stop(void)
952{
953 DSSDBG("audio_stop\n");
954
955 hdmi.ip_data.ops->audio_stop(&hdmi.ip_data);
956}
957
958bool hdmi_mode_has_audio(void)
959{
960 if (hdmi.ip_data.cfg.cm.mode == HDMI_HDMI)
961 return true;
962 else
963 return false;
964}
965
966int hdmi_audio_config(struct omap_dss_audio *audio)
967{
968 return hdmi.ip_data.ops->audio_config(&hdmi.ip_data, audio);
969}
970
Ricardo Neri35547622012-03-20 21:02:01 -0600971#endif
972
Tomi Valkeinen17ae4e82013-04-26 14:48:43 +0300973static struct omap_dss_device *hdmi_find_dssdev(struct platform_device *pdev)
Tomi Valkeinen38f3daf2012-05-02 14:55:12 +0300974{
975 struct omap_dss_board_info *pdata = pdev->dev.platform_data;
Tomi Valkeinen2bbcce52012-10-29 12:40:46 +0200976 const char *def_disp_name = omapdss_get_default_display_name();
Tomi Valkeinen15216532012-09-06 14:29:31 +0300977 struct omap_dss_device *def_dssdev;
978 int i;
979
980 def_dssdev = NULL;
Tomi Valkeinen38f3daf2012-05-02 14:55:12 +0300981
982 for (i = 0; i < pdata->num_devices; ++i) {
983 struct omap_dss_device *dssdev = pdata->devices[i];
984
985 if (dssdev->type != OMAP_DISPLAY_TYPE_HDMI)
986 continue;
987
Tomi Valkeinen15216532012-09-06 14:29:31 +0300988 if (def_dssdev == NULL)
989 def_dssdev = dssdev;
Tomi Valkeinencca35012012-04-26 14:48:32 +0300990
Tomi Valkeinen15216532012-09-06 14:29:31 +0300991 if (def_disp_name != NULL &&
992 strcmp(dssdev->name, def_disp_name) == 0) {
993 def_dssdev = dssdev;
994 break;
Tomi Valkeinen38f3daf2012-05-02 14:55:12 +0300995 }
Tomi Valkeinen15216532012-09-06 14:29:31 +0300996 }
Tomi Valkeinen38f3daf2012-05-02 14:55:12 +0300997
Tomi Valkeinen15216532012-09-06 14:29:31 +0300998 return def_dssdev;
999}
1000
Tomi Valkeinenc0980292013-04-26 14:52:23 +03001001static int hdmi_probe_pdata(struct platform_device *pdev)
Tomi Valkeinen15216532012-09-06 14:29:31 +03001002{
Tomi Valkeinen52744842012-09-10 13:58:29 +03001003 struct omap_dss_device *plat_dssdev;
Tomi Valkeinen15216532012-09-06 14:29:31 +03001004 struct omap_dss_device *dssdev;
1005 struct omap_dss_hdmi_data *priv;
1006 int r;
1007
Tomi Valkeinen52744842012-09-10 13:58:29 +03001008 plat_dssdev = hdmi_find_dssdev(pdev);
Tomi Valkeinen15216532012-09-06 14:29:31 +03001009
Tomi Valkeinen52744842012-09-10 13:58:29 +03001010 if (!plat_dssdev)
Tomi Valkeinenc0980292013-04-26 14:52:23 +03001011 return 0;
Tomi Valkeinen52744842012-09-10 13:58:29 +03001012
1013 dssdev = dss_alloc_and_init_device(&pdev->dev);
Tomi Valkeinen15216532012-09-06 14:29:31 +03001014 if (!dssdev)
Tomi Valkeinenc0980292013-04-26 14:52:23 +03001015 return -ENOMEM;
Tomi Valkeinen15216532012-09-06 14:29:31 +03001016
Tomi Valkeinen52744842012-09-10 13:58:29 +03001017 dss_copy_device_pdata(dssdev, plat_dssdev);
1018
Tomi Valkeinen15216532012-09-06 14:29:31 +03001019 priv = dssdev->data;
1020
1021 hdmi.ct_cp_hpd_gpio = priv->ct_cp_hpd_gpio;
1022 hdmi.ls_oe_gpio = priv->ls_oe_gpio;
1023 hdmi.hpd_gpio = priv->hpd_gpio;
1024
1025 r = hdmi_init_display(dssdev);
1026 if (r) {
1027 DSSERR("device %s init failed: %d\n", dssdev->name, r);
Tomi Valkeinen52744842012-09-10 13:58:29 +03001028 dss_put_device(dssdev);
Tomi Valkeinenc0980292013-04-26 14:52:23 +03001029 return r;
Tomi Valkeinen15216532012-09-06 14:29:31 +03001030 }
1031
Tomi Valkeinen486c0e12012-12-07 12:50:08 +02001032 r = omapdss_output_set_device(&hdmi.output, dssdev);
1033 if (r) {
1034 DSSERR("failed to connect output to new device: %s\n",
1035 dssdev->name);
1036 dss_put_device(dssdev);
Tomi Valkeinenc0980292013-04-26 14:52:23 +03001037 return r;
Tomi Valkeinen486c0e12012-12-07 12:50:08 +02001038 }
1039
Tomi Valkeinen52744842012-09-10 13:58:29 +03001040 r = dss_add_device(dssdev);
Tomi Valkeinen15216532012-09-06 14:29:31 +03001041 if (r) {
1042 DSSERR("device %s register failed: %d\n", dssdev->name, r);
Tomi Valkeinen486c0e12012-12-07 12:50:08 +02001043 omapdss_output_unset_device(&hdmi.output);
Ricardo Nerid18bc452012-11-06 00:19:15 -06001044 hdmi_uninit_display(dssdev);
Tomi Valkeinen52744842012-09-10 13:58:29 +03001045 dss_put_device(dssdev);
Tomi Valkeinenc0980292013-04-26 14:52:23 +03001046 return r;
Tomi Valkeinen38f3daf2012-05-02 14:55:12 +03001047 }
Tomi Valkeinenc0980292013-04-26 14:52:23 +03001048
1049 return 0;
Tomi Valkeinen38f3daf2012-05-02 14:55:12 +03001050}
1051
Tomi Valkeinen0b450c32013-05-24 13:20:17 +03001052static int hdmi_connect(struct omap_dss_device *dssdev,
1053 struct omap_dss_device *dst)
1054{
1055 struct omap_overlay_manager *mgr;
1056 int r;
1057
1058 dss_init_hdmi_ip_ops(&hdmi.ip_data, omapdss_get_version());
1059
1060 r = hdmi_init_regulator();
1061 if (r)
1062 return r;
1063
1064 mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);
1065 if (!mgr)
1066 return -ENODEV;
1067
1068 r = dss_mgr_connect(mgr, dssdev);
1069 if (r)
1070 return r;
1071
1072 r = omapdss_output_set_device(dssdev, dst);
1073 if (r) {
1074 DSSERR("failed to connect output to new device: %s\n",
1075 dst->name);
1076 dss_mgr_disconnect(mgr, dssdev);
1077 return r;
1078 }
1079
1080 return 0;
1081}
1082
1083static void hdmi_disconnect(struct omap_dss_device *dssdev,
1084 struct omap_dss_device *dst)
1085{
1086 WARN_ON(dst != dssdev->device);
1087
1088 if (dst != dssdev->device)
1089 return;
1090
1091 omapdss_output_unset_device(dssdev);
1092
1093 if (dssdev->manager)
1094 dss_mgr_disconnect(dssdev->manager, dssdev);
1095}
1096
1097static int hdmi_read_edid(struct omap_dss_device *dssdev,
1098 u8 *edid, int len)
1099{
1100 bool need_enable;
1101 int r;
1102
1103 need_enable = hdmi.core_enabled == false;
1104
1105 if (need_enable) {
1106 r = omapdss_hdmi_core_enable(dssdev);
1107 if (r)
1108 return r;
1109 }
1110
1111 r = omapdss_hdmi_read_edid(edid, len);
1112
1113 if (need_enable)
1114 omapdss_hdmi_core_disable(dssdev);
1115
1116 return r;
1117}
1118
1119#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
1120static int omapdss_hdmi_audio_enable(struct omap_dss_device *dssdev)
1121{
1122 int r;
1123
1124 mutex_lock(&hdmi.lock);
1125
1126 if (!hdmi_mode_has_audio()) {
1127 r = -EPERM;
1128 goto err;
1129 }
1130
1131 r = hdmi_audio_enable();
1132 if (r)
1133 goto err;
1134
1135 mutex_unlock(&hdmi.lock);
1136 return 0;
1137
1138err:
1139 mutex_unlock(&hdmi.lock);
1140 return r;
1141}
1142
1143static void omapdss_hdmi_audio_disable(struct omap_dss_device *dssdev)
1144{
1145 hdmi_audio_disable();
1146}
1147
1148static int omapdss_hdmi_audio_start(struct omap_dss_device *dssdev)
1149{
1150 return hdmi_audio_start();
1151}
1152
1153static void omapdss_hdmi_audio_stop(struct omap_dss_device *dssdev)
1154{
1155 hdmi_audio_stop();
1156}
1157
1158static bool omapdss_hdmi_audio_supported(struct omap_dss_device *dssdev)
1159{
1160 bool r;
1161
1162 mutex_lock(&hdmi.lock);
1163
1164 r = hdmi_mode_has_audio();
1165
1166 mutex_unlock(&hdmi.lock);
1167 return r;
1168}
1169
1170static int omapdss_hdmi_audio_config(struct omap_dss_device *dssdev,
1171 struct omap_dss_audio *audio)
1172{
1173 int r;
1174
1175 mutex_lock(&hdmi.lock);
1176
1177 if (!hdmi_mode_has_audio()) {
1178 r = -EPERM;
1179 goto err;
1180 }
1181
1182 r = hdmi_audio_config(audio);
1183 if (r)
1184 goto err;
1185
1186 mutex_unlock(&hdmi.lock);
1187 return 0;
1188
1189err:
1190 mutex_unlock(&hdmi.lock);
1191 return r;
1192}
1193#else
1194static int omapdss_hdmi_audio_enable(struct omap_dss_device *dssdev)
1195{
1196 return -EPERM;
1197}
1198
1199static void omapdss_hdmi_audio_disable(struct omap_dss_device *dssdev)
1200{
1201}
1202
1203static int omapdss_hdmi_audio_start(struct omap_dss_device *dssdev)
1204{
1205 return -EPERM;
1206}
1207
1208static void omapdss_hdmi_audio_stop(struct omap_dss_device *dssdev)
1209{
1210}
1211
1212static bool omapdss_hdmi_audio_supported(struct omap_dss_device *dssdev)
1213{
1214 return false;
1215}
1216
1217static int omapdss_hdmi_audio_config(struct omap_dss_device *dssdev,
1218 struct omap_dss_audio *audio)
1219{
1220 return -EPERM;
1221}
1222#endif
1223
1224static const struct omapdss_hdmi_ops hdmi_ops = {
1225 .connect = hdmi_connect,
1226 .disconnect = hdmi_disconnect,
1227
1228 .enable = omapdss_hdmi_display_enable,
1229 .disable = omapdss_hdmi_display_disable,
1230
1231 .check_timings = omapdss_hdmi_display_check_timing,
1232 .set_timings = omapdss_hdmi_display_set_timing,
1233 .get_timings = omapdss_hdmi_display_get_timings,
1234
1235 .read_edid = hdmi_read_edid,
1236
1237 .audio_enable = omapdss_hdmi_audio_enable,
1238 .audio_disable = omapdss_hdmi_audio_disable,
1239 .audio_start = omapdss_hdmi_audio_start,
1240 .audio_stop = omapdss_hdmi_audio_stop,
1241 .audio_supported = omapdss_hdmi_audio_supported,
1242 .audio_config = omapdss_hdmi_audio_config,
1243};
1244
Tomi Valkeinen17ae4e82013-04-26 14:48:43 +03001245static void hdmi_init_output(struct platform_device *pdev)
Archit Taneja81b87f52012-09-26 16:30:49 +05301246{
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +03001247 struct omap_dss_device *out = &hdmi.output;
Archit Taneja81b87f52012-09-26 16:30:49 +05301248
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +03001249 out->dev = &pdev->dev;
Archit Taneja81b87f52012-09-26 16:30:49 +05301250 out->id = OMAP_DSS_OUTPUT_HDMI;
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +03001251 out->output_type = OMAP_DISPLAY_TYPE_HDMI;
Tomi Valkeinen7286a082013-02-18 13:06:01 +02001252 out->name = "hdmi.0";
Tomi Valkeinen2eea5ae2013-02-13 11:23:54 +02001253 out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
Tomi Valkeinen0b450c32013-05-24 13:20:17 +03001254 out->ops.hdmi = &hdmi_ops;
Tomi Valkeinenb7328e12013-05-03 11:42:18 +03001255 out->owner = THIS_MODULE;
Archit Taneja81b87f52012-09-26 16:30:49 +05301256
Tomi Valkeinen5d47dbc2013-04-24 13:32:51 +03001257 omapdss_register_output(out);
Archit Taneja81b87f52012-09-26 16:30:49 +05301258}
1259
1260static void __exit hdmi_uninit_output(struct platform_device *pdev)
1261{
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +03001262 struct omap_dss_device *out = &hdmi.output;
Archit Taneja81b87f52012-09-26 16:30:49 +05301263
Tomi Valkeinen5d47dbc2013-04-24 13:32:51 +03001264 omapdss_unregister_output(out);
Archit Taneja81b87f52012-09-26 16:30:49 +05301265}
1266
Mythri P Kc3198a52011-03-12 12:04:27 +05301267/* HDMI HW IP initialisation */
Tomi Valkeinen17ae4e82013-04-26 14:48:43 +03001268static int omapdss_hdmihw_probe(struct platform_device *pdev)
Mythri P Kc3198a52011-03-12 12:04:27 +05301269{
Ricardo Neriaf23cb32012-11-06 00:19:11 -06001270 struct resource *res;
Tomi Valkeinen38f3daf2012-05-02 14:55:12 +03001271 int r;
Mythri P Kc3198a52011-03-12 12:04:27 +05301272
Mythri P Kc3198a52011-03-12 12:04:27 +05301273 hdmi.pdev = pdev;
1274
1275 mutex_init(&hdmi.lock);
Ricardo Neri66a06b02012-11-06 00:19:14 -06001276 mutex_init(&hdmi.ip_data.lock);
Mythri P Kc3198a52011-03-12 12:04:27 +05301277
Ricardo Neriaf23cb32012-11-06 00:19:11 -06001278 res = platform_get_resource(hdmi.pdev, IORESOURCE_MEM, 0);
Mythri P Kc3198a52011-03-12 12:04:27 +05301279
1280 /* Base address taken from platform */
Thierry Redingbc3bad12013-01-21 11:09:23 +01001281 hdmi.ip_data.base_wp = devm_ioremap_resource(&pdev->dev, res);
1282 if (IS_ERR(hdmi.ip_data.base_wp))
1283 return PTR_ERR(hdmi.ip_data.base_wp);
Mythri P Kc3198a52011-03-12 12:04:27 +05301284
Tomi Valkeinenddb1d5c2013-06-06 13:08:35 +03001285 hdmi.ip_data.irq = platform_get_irq(pdev, 0);
1286 if (hdmi.ip_data.irq < 0) {
1287 DSSERR("platform_get_irq failed\n");
1288 return -ENODEV;
1289 }
1290
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001291 r = hdmi_get_clocks(pdev);
1292 if (r) {
Ricardo Neri47e443b2012-11-06 00:19:12 -06001293 DSSERR("can't get clocks\n");
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001294 return r;
1295 }
1296
1297 pm_runtime_enable(&pdev->dev);
1298
Mythri P K95a8aeb2011-09-08 19:06:18 +05301299 hdmi.ip_data.core_sys_offset = HDMI_CORE_SYS;
1300 hdmi.ip_data.core_av_offset = HDMI_CORE_AV;
1301 hdmi.ip_data.pll_offset = HDMI_PLLCTRL;
1302 hdmi.ip_data.phy_offset = HDMI_PHY;
Archit Taneja78493982012-08-08 16:50:42 +05301303
Tomi Valkeinen0b450c32013-05-24 13:20:17 +03001304 hdmi.ct_cp_hpd_gpio = -1;
1305 hdmi.ls_oe_gpio = -1;
1306 hdmi.hpd_gpio = -1;
1307
Tomi Valkeinen002d3682013-02-13 12:17:43 +02001308 hdmi_init_output(pdev);
1309
Ricardo Neri66a06b02012-11-06 00:19:14 -06001310 r = hdmi_panel_init();
1311 if (r) {
1312 DSSERR("can't init panel\n");
Archit Tanejab2c9c8e2013-04-08 11:55:00 +03001313 return r;
Ricardo Neri66a06b02012-11-06 00:19:14 -06001314 }
Mythri P Kc3198a52011-03-12 12:04:27 +05301315
Tomi Valkeinene40402c2012-03-02 18:01:07 +02001316 dss_debugfs_create_file("hdmi", hdmi_dump_regs);
1317
Tomi Valkeinenc6ca5b22013-03-14 15:47:29 +02001318 if (pdev->dev.platform_data) {
1319 r = hdmi_probe_pdata(pdev);
1320 if (r)
1321 goto err_probe;
Tomi Valkeinenc0980292013-04-26 14:52:23 +03001322 }
Tomi Valkeinen35deca32012-03-01 15:45:53 +02001323
Mythri P Kc3198a52011-03-12 12:04:27 +05301324 return 0;
Tomi Valkeinenc6ca5b22013-03-14 15:47:29 +02001325
1326err_probe:
1327 hdmi_panel_exit();
1328 hdmi_uninit_output(pdev);
1329 pm_runtime_disable(&pdev->dev);
1330 return r;
Mythri P Kc3198a52011-03-12 12:04:27 +05301331}
1332
Tomi Valkeinencca35012012-04-26 14:48:32 +03001333static int __exit hdmi_remove_child(struct device *dev, void *data)
1334{
1335 struct omap_dss_device *dssdev = to_dss_device(dev);
1336 hdmi_uninit_display(dssdev);
1337 return 0;
1338}
1339
Tomi Valkeinen6e7e8f02012-02-17 17:41:13 +02001340static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
Mythri P Kc3198a52011-03-12 12:04:27 +05301341{
Tomi Valkeinencca35012012-04-26 14:48:32 +03001342 device_for_each_child(&pdev->dev, NULL, hdmi_remove_child);
1343
Tomi Valkeinen52744842012-09-10 13:58:29 +03001344 dss_unregister_child_devices(&pdev->dev);
Tomi Valkeinen35deca32012-03-01 15:45:53 +02001345
Mythri P Kc3198a52011-03-12 12:04:27 +05301346 hdmi_panel_exit();
1347
Archit Taneja81b87f52012-09-26 16:30:49 +05301348 hdmi_uninit_output(pdev);
1349
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001350 pm_runtime_disable(&pdev->dev);
1351
Mythri P Kc3198a52011-03-12 12:04:27 +05301352 return 0;
1353}
1354
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001355static int hdmi_runtime_suspend(struct device *dev)
1356{
Rajendra Nayakf11766d2012-06-27 14:21:26 +05301357 clk_disable_unprepare(hdmi.sys_clk);
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001358
1359 dispc_runtime_put();
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001360
1361 return 0;
1362}
1363
1364static int hdmi_runtime_resume(struct device *dev)
1365{
1366 int r;
1367
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001368 r = dispc_runtime_get();
1369 if (r < 0)
Tomi Valkeinen852f0832012-02-17 17:58:04 +02001370 return r;
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001371
Rajendra Nayakf11766d2012-06-27 14:21:26 +05301372 clk_prepare_enable(hdmi.sys_clk);
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001373
1374 return 0;
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001375}
1376
1377static const struct dev_pm_ops hdmi_pm_ops = {
1378 .runtime_suspend = hdmi_runtime_suspend,
1379 .runtime_resume = hdmi_runtime_resume,
1380};
1381
Mythri P Kc3198a52011-03-12 12:04:27 +05301382static struct platform_driver omapdss_hdmihw_driver = {
Tomi Valkeinen17ae4e82013-04-26 14:48:43 +03001383 .probe = omapdss_hdmihw_probe,
Tomi Valkeinen6e7e8f02012-02-17 17:41:13 +02001384 .remove = __exit_p(omapdss_hdmihw_remove),
Mythri P Kc3198a52011-03-12 12:04:27 +05301385 .driver = {
1386 .name = "omapdss_hdmi",
1387 .owner = THIS_MODULE,
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001388 .pm = &hdmi_pm_ops,
Mythri P Kc3198a52011-03-12 12:04:27 +05301389 },
1390};
1391
Tomi Valkeinen6e7e8f02012-02-17 17:41:13 +02001392int __init hdmi_init_platform_driver(void)
Mythri P Kc3198a52011-03-12 12:04:27 +05301393{
Tomi Valkeinen17ae4e82013-04-26 14:48:43 +03001394 return platform_driver_register(&omapdss_hdmihw_driver);
Mythri P Kc3198a52011-03-12 12:04:27 +05301395}
1396
Tomi Valkeinen6e7e8f02012-02-17 17:41:13 +02001397void __exit hdmi_uninit_platform_driver(void)
Mythri P Kc3198a52011-03-12 12:04:27 +05301398{
Tomi Valkeinen04c742c2012-02-23 15:32:37 +02001399 platform_driver_unregister(&omapdss_hdmihw_driver);
Mythri P Kc3198a52011-03-12 12:04:27 +05301400}