aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/exynos/exynos_hdmi.c
diff options
context:
space:
mode:
authorSeung-Woo Kim <sw0312.kim@samsung.com>2011-12-21 03:39:39 -0500
committerInki Dae <inki.dae@samsung.com>2011-12-28 21:21:42 -0500
commitd84083268bd707ebb8ed2f4fc26ebc7a0c453a83 (patch)
tree864dbd5eb72fb7ac1865a7aa72dce3dd5f7a4c73 /drivers/gpu/drm/exynos/exynos_hdmi.c
parentc32b06ef7dd63a5e0b14a02c96ef308796c157cc (diff)
drm/exynos: added hdmi display support
This patch is hdmi display support for exynos drm driver. There is already v4l2 based exynos hdmi driver in drivers/media/video/s5p-tv and some low level code is already in s5p-tv and even headers for register define are almost same. but in this patch, we decide not to consider separated common code with s5p-tv. Exynos HDMI is composed of 5 blocks, mixer, vp, hdmi, hdmiphy and ddc. 1. mixer. The piece of hardware responsible for mixing and blending multiple data inputs before passing it to an output device. The mixer is capable of handling up to three image layers. One is the output of VP. Other two are images in RGB format. The blending factor, and layers' priority are controlled by mixer's registers. The output is passed to HDMI. 2. vp (video processor). It is used for processing of NV12/NV21 data. An image stored in RAM is accessed by DMA. The output in YCbCr444 format is send to mixer. 3. hdmi. The piece of HW responsible for generation of HDMI packets. It takes pixel data from mixer and transforms it into data frames. The output is send to HDMIPHY interface. 4. hdmiphy. Physical interface for HDMI. Its duties are sending HDMI packets to HDMI connector. Basically, it contains a PLL that produces source clock for mixer, vp and hdmi. 5. ddc (display data channel). It is dedicated i2c channel to exchange display information as edid with display monitor. With plane support, exynos hdmi driver fully supports two mixer layes and vp layer. Also vp layer supports multi buffer plane pixel formats having non contigus memory spaces. In exynos drm driver, common drm_hdmi driver to interface with drm framework has opertion pointers for mixer and hdmi. this drm_hdmi driver is registered as sub driver of exynos_drm. hdmi has hdmiphy and ddc i2c clients and controls them. mixer controls all overlay layers in both mixer and vp. Vblank interrupts for hdmi are handled by mixer internally because drm framework cannot support multiple irq id. And pipe number is used to check which display device irq happens. History v2: this version - drm plane feature support to handle overlay layers. - multi buffer plane pixel format support for vp layer. - vp layer support RFCv1: original - at https://lkml.org/lkml/2011/11/4/164 Signed-off-by: Seung-Woo Kim <sw0312.kim@samsung.com> Signed-off-by: Inki Dae <inki.dae@samsung.com> Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Diffstat (limited to 'drivers/gpu/drm/exynos/exynos_hdmi.c')
-rw-r--r--drivers/gpu/drm/exynos/exynos_hdmi.c1176
1 files changed, 1176 insertions, 0 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
new file mode 100644
index 00000000000..f48f7ce92f5
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -0,0 +1,1176 @@
1/*
2 * Copyright (C) 2011 Samsung Electronics Co.Ltd
3 * Authors:
4 * Seung-Woo Kim <sw0312.kim@samsung.com>
5 * Inki Dae <inki.dae@samsung.com>
6 * Joonyoung Shim <jy0922.shim@samsung.com>
7 *
8 * Based on drivers/media/video/s5p-tv/hdmi_drv.c
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version.
14 *
15 */
16
17#include "drmP.h"
18#include "drm_edid.h"
19#include "drm_crtc_helper.h"
20
21#include "regs-hdmi.h"
22
23#include <linux/kernel.h>
24#include <linux/spinlock.h>
25#include <linux/wait.h>
26#include <linux/i2c.h>
27#include <linux/module.h>
28#include <linux/platform_device.h>
29#include <linux/interrupt.h>
30#include <linux/irq.h>
31#include <linux/delay.h>
32#include <linux/pm_runtime.h>
33#include <linux/clk.h>
34#include <linux/regulator/consumer.h>
35
36#include <drm/exynos_drm.h>
37
38#include "exynos_drm_drv.h"
39#include "exynos_drm_hdmi.h"
40
41#include "exynos_hdmi.h"
42
43#define HDMI_OVERLAY_NUMBER 3
44#define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev))
45
46static const u8 hdmiphy_conf27[32] = {
47 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
48 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
49 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
50 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
51};
52
53static const u8 hdmiphy_conf27_027[32] = {
54 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
55 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
56 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
57 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
58};
59
60static const u8 hdmiphy_conf74_175[32] = {
61 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
62 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
63 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
64 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
65};
66
67static const u8 hdmiphy_conf74_25[32] = {
68 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
69 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
70 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
71 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
72};
73
74static const u8 hdmiphy_conf148_5[32] = {
75 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
76 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
77 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
78 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
79};
80
81struct hdmi_tg_regs {
82 u8 cmd;
83 u8 h_fsz_l;
84 u8 h_fsz_h;
85 u8 hact_st_l;
86 u8 hact_st_h;
87 u8 hact_sz_l;
88 u8 hact_sz_h;
89 u8 v_fsz_l;
90 u8 v_fsz_h;
91 u8 vsync_l;
92 u8 vsync_h;
93 u8 vsync2_l;
94 u8 vsync2_h;
95 u8 vact_st_l;
96 u8 vact_st_h;
97 u8 vact_sz_l;
98 u8 vact_sz_h;
99 u8 field_chg_l;
100 u8 field_chg_h;
101 u8 vact_st2_l;
102 u8 vact_st2_h;
103 u8 vsync_top_hdmi_l;
104 u8 vsync_top_hdmi_h;
105 u8 vsync_bot_hdmi_l;
106 u8 vsync_bot_hdmi_h;
107 u8 field_top_hdmi_l;
108 u8 field_top_hdmi_h;
109 u8 field_bot_hdmi_l;
110 u8 field_bot_hdmi_h;
111};
112
113struct hdmi_core_regs {
114 u8 h_blank[2];
115 u8 v_blank[3];
116 u8 h_v_line[3];
117 u8 vsync_pol[1];
118 u8 int_pro_mode[1];
119 u8 v_blank_f[3];
120 u8 h_sync_gen[3];
121 u8 v_sync_gen1[3];
122 u8 v_sync_gen2[3];
123 u8 v_sync_gen3[3];
124};
125
126struct hdmi_preset_conf {
127 struct hdmi_core_regs core;
128 struct hdmi_tg_regs tg;
129};
130
131static const struct hdmi_preset_conf hdmi_conf_480p = {
132 .core = {
133 .h_blank = {0x8a, 0x00},
134 .v_blank = {0x0d, 0x6a, 0x01},
135 .h_v_line = {0x0d, 0xa2, 0x35},
136 .vsync_pol = {0x01},
137 .int_pro_mode = {0x00},
138 .v_blank_f = {0x00, 0x00, 0x00},
139 .h_sync_gen = {0x0e, 0x30, 0x11},
140 .v_sync_gen1 = {0x0f, 0x90, 0x00},
141 /* other don't care */
142 },
143 .tg = {
144 0x00, /* cmd */
145 0x5a, 0x03, /* h_fsz */
146 0x8a, 0x00, 0xd0, 0x02, /* hact */
147 0x0d, 0x02, /* v_fsz */
148 0x01, 0x00, 0x33, 0x02, /* vsync */
149 0x2d, 0x00, 0xe0, 0x01, /* vact */
150 0x33, 0x02, /* field_chg */
151 0x49, 0x02, /* vact_st2 */
152 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
153 0x01, 0x00, 0x33, 0x02, /* field top/bot */
154 },
155};
156
157static const struct hdmi_preset_conf hdmi_conf_720p60 = {
158 .core = {
159 .h_blank = {0x72, 0x01},
160 .v_blank = {0xee, 0xf2, 0x00},
161 .h_v_line = {0xee, 0x22, 0x67},
162 .vsync_pol = {0x00},
163 .int_pro_mode = {0x00},
164 .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */
165 .h_sync_gen = {0x6c, 0x50, 0x02},
166 .v_sync_gen1 = {0x0a, 0x50, 0x00},
167 .v_sync_gen2 = {0x01, 0x10, 0x00},
168 .v_sync_gen3 = {0x01, 0x10, 0x00},
169 /* other don't care */
170 },
171 .tg = {
172 0x00, /* cmd */
173 0x72, 0x06, /* h_fsz */
174 0x71, 0x01, 0x01, 0x05, /* hact */
175 0xee, 0x02, /* v_fsz */
176 0x01, 0x00, 0x33, 0x02, /* vsync */
177 0x1e, 0x00, 0xd0, 0x02, /* vact */
178 0x33, 0x02, /* field_chg */
179 0x49, 0x02, /* vact_st2 */
180 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
181 0x01, 0x00, 0x33, 0x02, /* field top/bot */
182 },
183};
184
185static const struct hdmi_preset_conf hdmi_conf_1080i50 = {
186 .core = {
187 .h_blank = {0xd0, 0x02},
188 .v_blank = {0x32, 0xB2, 0x00},
189 .h_v_line = {0x65, 0x04, 0xa5},
190 .vsync_pol = {0x00},
191 .int_pro_mode = {0x01},
192 .v_blank_f = {0x49, 0x2A, 0x23},
193 .h_sync_gen = {0x0E, 0xEA, 0x08},
194 .v_sync_gen1 = {0x07, 0x20, 0x00},
195 .v_sync_gen2 = {0x39, 0x42, 0x23},
196 .v_sync_gen3 = {0x38, 0x87, 0x73},
197 /* other don't care */
198 },
199 .tg = {
200 0x00, /* cmd */
201 0x50, 0x0A, /* h_fsz */
202 0xCF, 0x02, 0x81, 0x07, /* hact */
203 0x65, 0x04, /* v_fsz */
204 0x01, 0x00, 0x33, 0x02, /* vsync */
205 0x16, 0x00, 0x1c, 0x02, /* vact */
206 0x33, 0x02, /* field_chg */
207 0x49, 0x02, /* vact_st2 */
208 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
209 0x01, 0x00, 0x33, 0x02, /* field top/bot */
210 },
211};
212
213static const struct hdmi_preset_conf hdmi_conf_1080p50 = {
214 .core = {
215 .h_blank = {0xd0, 0x02},
216 .v_blank = {0x65, 0x6c, 0x01},
217 .h_v_line = {0x65, 0x04, 0xa5},
218 .vsync_pol = {0x00},
219 .int_pro_mode = {0x00},
220 .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */
221 .h_sync_gen = {0x0e, 0xea, 0x08},
222 .v_sync_gen1 = {0x09, 0x40, 0x00},
223 .v_sync_gen2 = {0x01, 0x10, 0x00},
224 .v_sync_gen3 = {0x01, 0x10, 0x00},
225 /* other don't care */
226 },
227 .tg = {
228 0x00, /* cmd */
229 0x50, 0x0A, /* h_fsz */
230 0xCF, 0x02, 0x81, 0x07, /* hact */
231 0x65, 0x04, /* v_fsz */
232 0x01, 0x00, 0x33, 0x02, /* vsync */
233 0x2d, 0x00, 0x38, 0x04, /* vact */
234 0x33, 0x02, /* field_chg */
235 0x48, 0x02, /* vact_st2 */
236 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
237 0x01, 0x00, 0x33, 0x02, /* field top/bot */
238 },
239};
240
241static const struct hdmi_preset_conf hdmi_conf_1080i60 = {
242 .core = {
243 .h_blank = {0x18, 0x01},
244 .v_blank = {0x32, 0xB2, 0x00},
245 .h_v_line = {0x65, 0x84, 0x89},
246 .vsync_pol = {0x00},
247 .int_pro_mode = {0x01},
248 .v_blank_f = {0x49, 0x2A, 0x23},
249 .h_sync_gen = {0x56, 0x08, 0x02},
250 .v_sync_gen1 = {0x07, 0x20, 0x00},
251 .v_sync_gen2 = {0x39, 0x42, 0x23},
252 .v_sync_gen3 = {0xa4, 0x44, 0x4a},
253 /* other don't care */
254 },
255 .tg = {
256 0x00, /* cmd */
257 0x98, 0x08, /* h_fsz */
258 0x17, 0x01, 0x81, 0x07, /* hact */
259 0x65, 0x04, /* v_fsz */
260 0x01, 0x00, 0x33, 0x02, /* vsync */
261 0x16, 0x00, 0x1c, 0x02, /* vact */
262 0x33, 0x02, /* field_chg */
263 0x49, 0x02, /* vact_st2 */
264 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
265 0x01, 0x00, 0x33, 0x02, /* field top/bot */
266 },
267};
268
269static const struct hdmi_preset_conf hdmi_conf_1080p60 = {
270 .core = {
271 .h_blank = {0x18, 0x01},
272 .v_blank = {0x65, 0x6c, 0x01},
273 .h_v_line = {0x65, 0x84, 0x89},
274 .vsync_pol = {0x00},
275 .int_pro_mode = {0x00},
276 .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */
277 .h_sync_gen = {0x56, 0x08, 0x02},
278 .v_sync_gen1 = {0x09, 0x40, 0x00},
279 .v_sync_gen2 = {0x01, 0x10, 0x00},
280 .v_sync_gen3 = {0x01, 0x10, 0x00},
281 /* other don't care */
282 },
283 .tg = {
284 0x00, /* cmd */
285 0x98, 0x08, /* h_fsz */
286 0x17, 0x01, 0x81, 0x07, /* hact */
287 0x65, 0x04, /* v_fsz */
288 0x01, 0x00, 0x33, 0x02, /* vsync */
289 0x2d, 0x00, 0x38, 0x04, /* vact */
290 0x33, 0x02, /* field_chg */
291 0x48, 0x02, /* vact_st2 */
292 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
293 0x01, 0x00, 0x33, 0x02, /* field top/bot */
294 },
295};
296
297static const struct hdmi_conf hdmi_confs[] = {
298 { 1280, 720, 60, false, hdmiphy_conf74_25, &hdmi_conf_720p60 },
299 { 1280, 720, 50, false, hdmiphy_conf74_25, &hdmi_conf_720p60 },
300 { 720, 480, 60, false, hdmiphy_conf27_027, &hdmi_conf_480p },
301 { 1920, 1080, 50, true, hdmiphy_conf74_25, &hdmi_conf_1080i50 },
302 { 1920, 1080, 50, false, hdmiphy_conf148_5, &hdmi_conf_1080p50 },
303 { 1920, 1080, 60, true, hdmiphy_conf74_25, &hdmi_conf_1080i60 },
304 { 1920, 1080, 60, false, hdmiphy_conf148_5, &hdmi_conf_1080p60 },
305};
306
307
308static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id)
309{
310 return readl(hdata->regs + reg_id);
311}
312
313static inline void hdmi_reg_writeb(struct hdmi_context *hdata,
314 u32 reg_id, u8 value)
315{
316 writeb(value, hdata->regs + reg_id);
317}
318
319static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
320 u32 reg_id, u32 value, u32 mask)
321{
322 u32 old = readl(hdata->regs + reg_id);
323 value = (value & mask) | (old & ~mask);
324 writel(value, hdata->regs + reg_id);
325}
326
327static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix)
328{
329#define DUMPREG(reg_id) \
330 DRM_DEBUG_KMS("%s:" #reg_id " = %08x\n", prefix, \
331 readl(hdata->regs + reg_id))
332 DRM_DEBUG_KMS("%s: ---- CONTROL REGISTERS ----\n", prefix);
333 DUMPREG(HDMI_INTC_FLAG);
334 DUMPREG(HDMI_INTC_CON);
335 DUMPREG(HDMI_HPD_STATUS);
336 DUMPREG(HDMI_PHY_RSTOUT);
337 DUMPREG(HDMI_PHY_VPLL);
338 DUMPREG(HDMI_PHY_CMU);
339 DUMPREG(HDMI_CORE_RSTOUT);
340
341 DRM_DEBUG_KMS("%s: ---- CORE REGISTERS ----\n", prefix);
342 DUMPREG(HDMI_CON_0);
343 DUMPREG(HDMI_CON_1);
344 DUMPREG(HDMI_CON_2);
345 DUMPREG(HDMI_SYS_STATUS);
346 DUMPREG(HDMI_PHY_STATUS);
347 DUMPREG(HDMI_STATUS_EN);
348 DUMPREG(HDMI_HPD);
349 DUMPREG(HDMI_MODE_SEL);
350 DUMPREG(HDMI_HPD_GEN);
351 DUMPREG(HDMI_DC_CONTROL);
352 DUMPREG(HDMI_VIDEO_PATTERN_GEN);
353
354 DRM_DEBUG_KMS("%s: ---- CORE SYNC REGISTERS ----\n", prefix);
355 DUMPREG(HDMI_H_BLANK_0);
356 DUMPREG(HDMI_H_BLANK_1);
357 DUMPREG(HDMI_V_BLANK_0);
358 DUMPREG(HDMI_V_BLANK_1);
359 DUMPREG(HDMI_V_BLANK_2);
360 DUMPREG(HDMI_H_V_LINE_0);
361 DUMPREG(HDMI_H_V_LINE_1);
362 DUMPREG(HDMI_H_V_LINE_2);
363 DUMPREG(HDMI_VSYNC_POL);
364 DUMPREG(HDMI_INT_PRO_MODE);
365 DUMPREG(HDMI_V_BLANK_F_0);
366 DUMPREG(HDMI_V_BLANK_F_1);
367 DUMPREG(HDMI_V_BLANK_F_2);
368 DUMPREG(HDMI_H_SYNC_GEN_0);
369 DUMPREG(HDMI_H_SYNC_GEN_1);
370 DUMPREG(HDMI_H_SYNC_GEN_2);
371 DUMPREG(HDMI_V_SYNC_GEN_1_0);
372 DUMPREG(HDMI_V_SYNC_GEN_1_1);
373 DUMPREG(HDMI_V_SYNC_GEN_1_2);
374 DUMPREG(HDMI_V_SYNC_GEN_2_0);
375 DUMPREG(HDMI_V_SYNC_GEN_2_1);
376 DUMPREG(HDMI_V_SYNC_GEN_2_2);
377 DUMPREG(HDMI_V_SYNC_GEN_3_0);
378 DUMPREG(HDMI_V_SYNC_GEN_3_1);
379 DUMPREG(HDMI_V_SYNC_GEN_3_2);
380
381 DRM_DEBUG_KMS("%s: ---- TG REGISTERS ----\n", prefix);
382 DUMPREG(HDMI_TG_CMD);
383 DUMPREG(HDMI_TG_H_FSZ_L);
384 DUMPREG(HDMI_TG_H_FSZ_H);
385 DUMPREG(HDMI_TG_HACT_ST_L);
386 DUMPREG(HDMI_TG_HACT_ST_H);
387 DUMPREG(HDMI_TG_HACT_SZ_L);
388 DUMPREG(HDMI_TG_HACT_SZ_H);
389 DUMPREG(HDMI_TG_V_FSZ_L);
390 DUMPREG(HDMI_TG_V_FSZ_H);
391 DUMPREG(HDMI_TG_VSYNC_L);
392 DUMPREG(HDMI_TG_VSYNC_H);
393 DUMPREG(HDMI_TG_VSYNC2_L);
394 DUMPREG(HDMI_TG_VSYNC2_H);
395 DUMPREG(HDMI_TG_VACT_ST_L);
396 DUMPREG(HDMI_TG_VACT_ST_H);
397 DUMPREG(HDMI_TG_VACT_SZ_L);
398 DUMPREG(HDMI_TG_VACT_SZ_H);
399 DUMPREG(HDMI_TG_FIELD_CHG_L);
400 DUMPREG(HDMI_TG_FIELD_CHG_H);
401 DUMPREG(HDMI_TG_VACT_ST2_L);
402 DUMPREG(HDMI_TG_VACT_ST2_H);
403 DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L);
404 DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H);
405 DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L);
406 DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_H);
407 DUMPREG(HDMI_TG_FIELD_TOP_HDMI_L);
408 DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H);
409 DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L);
410 DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H);
411#undef DUMPREG
412}
413
414static int hdmi_conf_index(struct drm_display_mode *mode)
415{
416 int i;
417
418 for (i = 0; i < ARRAY_SIZE(hdmi_confs); ++i)
419 if (hdmi_confs[i].width == mode->hdisplay &&
420 hdmi_confs[i].height == mode->vdisplay &&
421 hdmi_confs[i].vrefresh == mode->vrefresh &&
422 hdmi_confs[i].interlace ==
423 ((mode->flags & DRM_MODE_FLAG_INTERLACE) ?
424 true : false))
425 return i;
426
427 return -1;
428}
429
430static bool hdmi_is_connected(void *ctx)
431{
432 struct hdmi_context *hdata = (struct hdmi_context *)ctx;
433 u32 val = hdmi_reg_read(hdata, HDMI_HPD_STATUS);
434
435 if (val)
436 return true;
437
438 return false;
439}
440
441static int hdmi_get_edid(void *ctx, struct drm_connector *connector,
442 u8 *edid, int len)
443{
444 struct edid *raw_edid;
445 struct hdmi_context *hdata = (struct hdmi_context *)ctx;
446
447 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
448
449 if (!hdata->ddc_port)
450 return -ENODEV;
451
452 raw_edid = drm_get_edid(connector, hdata->ddc_port->adapter);
453 if (raw_edid) {
454 memcpy(edid, raw_edid, min((1 + raw_edid->extensions)
455 * EDID_LENGTH, len));
456 DRM_DEBUG_KMS("width[%d] x height[%d]\n",
457 raw_edid->width_cm, raw_edid->height_cm);
458 } else {
459 return -ENODEV;
460 }
461
462 return 0;
463}
464
465static int hdmi_check_timing(void *ctx, void *timing)
466{
467 struct fb_videomode *check_timing = timing;
468 int i;
469
470 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
471
472 DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", check_timing->xres,
473 check_timing->yres, check_timing->refresh,
474 check_timing->vmode);
475
476 for (i = 0; i < ARRAY_SIZE(hdmi_confs); ++i)
477 if (hdmi_confs[i].width == check_timing->xres &&
478 hdmi_confs[i].height == check_timing->yres &&
479 hdmi_confs[i].vrefresh == check_timing->refresh &&
480 hdmi_confs[i].interlace ==
481 ((check_timing->vmode & FB_VMODE_INTERLACED) ?
482 true : false))
483 return 0;
484
485 return -EINVAL;
486}
487
488static int hdmi_display_power_on(void *ctx, int mode)
489{
490 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
491
492 switch (mode) {
493 case DRM_MODE_DPMS_ON:
494 DRM_DEBUG_KMS("hdmi [on]\n");
495 break;
496 case DRM_MODE_DPMS_STANDBY:
497 break;
498 case DRM_MODE_DPMS_SUSPEND:
499 break;
500 case DRM_MODE_DPMS_OFF:
501 DRM_DEBUG_KMS("hdmi [off]\n");
502 break;
503 default:
504 break;
505 }
506
507 return 0;
508}
509
510static struct exynos_hdmi_display_ops display_ops = {
511 .is_connected = hdmi_is_connected,
512 .get_edid = hdmi_get_edid,
513 .check_timing = hdmi_check_timing,
514 .power_on = hdmi_display_power_on,
515};
516
517static void hdmi_conf_reset(struct hdmi_context *hdata)
518{
519 /* disable hpd handle for drm */
520 hdata->hpd_handle = false;
521
522 /* resetting HDMI core */
523 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, 0, HDMI_CORE_SW_RSTOUT);
524 mdelay(10);
525 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, ~0, HDMI_CORE_SW_RSTOUT);
526 mdelay(10);
527
528 /* enable hpd handle for drm */
529 hdata->hpd_handle = true;
530}
531
532static void hdmi_conf_init(struct hdmi_context *hdata)
533{
534 /* disable hpd handle for drm */
535 hdata->hpd_handle = false;
536
537 /* enable HPD interrupts */
538 hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL |
539 HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
540 mdelay(10);
541 hdmi_reg_writemask(hdata, HDMI_INTC_CON, ~0, HDMI_INTC_EN_GLOBAL |
542 HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
543
544 /* choose HDMI mode */
545 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
546 HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
547 /* disable bluescreen */
548 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
549 /* choose bluescreen (fecal) color */
550 hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_0, 0x12);
551 hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_1, 0x34);
552 hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_2, 0x56);
553 /* enable AVI packet every vsync, fixes purple line problem */
554 hdmi_reg_writeb(hdata, HDMI_AVI_CON, 0x02);
555 /* force RGB, look to CEA-861-D, table 7 for more detail */
556 hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(0), 0 << 5);
557 hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5);
558
559 hdmi_reg_writeb(hdata, HDMI_SPD_CON, 0x02);
560 hdmi_reg_writeb(hdata, HDMI_AUI_CON, 0x02);
561 hdmi_reg_writeb(hdata, HDMI_ACR_CON, 0x04);
562
563 /* enable hpd handle for drm */
564 hdata->hpd_handle = true;
565}
566
567static void hdmi_timing_apply(struct hdmi_context *hdata,
568 const struct hdmi_preset_conf *conf)
569{
570 const struct hdmi_core_regs *core = &conf->core;
571 const struct hdmi_tg_regs *tg = &conf->tg;
572 int tries;
573
574 /* setting core registers */
575 hdmi_reg_writeb(hdata, HDMI_H_BLANK_0, core->h_blank[0]);
576 hdmi_reg_writeb(hdata, HDMI_H_BLANK_1, core->h_blank[1]);
577 hdmi_reg_writeb(hdata, HDMI_V_BLANK_0, core->v_blank[0]);
578 hdmi_reg_writeb(hdata, HDMI_V_BLANK_1, core->v_blank[1]);
579 hdmi_reg_writeb(hdata, HDMI_V_BLANK_2, core->v_blank[2]);
580 hdmi_reg_writeb(hdata, HDMI_H_V_LINE_0, core->h_v_line[0]);
581 hdmi_reg_writeb(hdata, HDMI_H_V_LINE_1, core->h_v_line[1]);
582 hdmi_reg_writeb(hdata, HDMI_H_V_LINE_2, core->h_v_line[2]);
583 hdmi_reg_writeb(hdata, HDMI_VSYNC_POL, core->vsync_pol[0]);
584 hdmi_reg_writeb(hdata, HDMI_INT_PRO_MODE, core->int_pro_mode[0]);
585 hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_0, core->v_blank_f[0]);
586 hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_1, core->v_blank_f[1]);
587 hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_2, core->v_blank_f[2]);
588 hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_0, core->h_sync_gen[0]);
589 hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_1, core->h_sync_gen[1]);
590 hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_2, core->h_sync_gen[2]);
591 hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_0, core->v_sync_gen1[0]);
592 hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_1, core->v_sync_gen1[1]);
593 hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_2, core->v_sync_gen1[2]);
594 hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_0, core->v_sync_gen2[0]);
595 hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_1, core->v_sync_gen2[1]);
596 hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_2, core->v_sync_gen2[2]);
597 hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_0, core->v_sync_gen3[0]);
598 hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_1, core->v_sync_gen3[1]);
599 hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_2, core->v_sync_gen3[2]);
600 /* Timing generator registers */
601 hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_L, tg->h_fsz_l);
602 hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_H, tg->h_fsz_h);
603 hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_L, tg->hact_st_l);
604 hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_H, tg->hact_st_h);
605 hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_L, tg->hact_sz_l);
606 hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_H, tg->hact_sz_h);
607 hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_L, tg->v_fsz_l);
608 hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_H, tg->v_fsz_h);
609 hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_L, tg->vsync_l);
610 hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_H, tg->vsync_h);
611 hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_L, tg->vsync2_l);
612 hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_H, tg->vsync2_h);
613 hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_L, tg->vact_st_l);
614 hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_H, tg->vact_st_h);
615 hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_L, tg->vact_sz_l);
616 hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_H, tg->vact_sz_h);
617 hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_L, tg->field_chg_l);
618 hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_H, tg->field_chg_h);
619 hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_L, tg->vact_st2_l);
620 hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_H, tg->vact_st2_h);
621 hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l);
622 hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h);
623 hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l);
624 hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi_h);
625 hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi_l);
626 hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h);
627 hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l);
628 hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h);
629
630 /* waiting for HDMIPHY's PLL to get to steady state */
631 for (tries = 100; tries; --tries) {
632 u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS);
633 if (val & HDMI_PHY_STATUS_READY)
634 break;
635 mdelay(1);
636 }
637 /* steady state not achieved */
638 if (tries == 0) {
639 DRM_ERROR("hdmiphy's pll could not reach steady state.\n");
640 hdmi_regs_dump(hdata, "timing apply");
641 }
642
643 clk_disable(hdata->res.sclk_hdmi);
644 clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_hdmiphy);
645 clk_enable(hdata->res.sclk_hdmi);
646
647 /* enable HDMI and timing generator */
648 hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN);
649 if (core->int_pro_mode[0])
650 hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN |
651 HDMI_FIELD_EN);
652 else
653 hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN);
654}
655
656static void hdmiphy_conf_reset(struct hdmi_context *hdata)
657{
658 u8 buffer[2];
659
660 clk_disable(hdata->res.sclk_hdmi);
661 clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_pixel);
662 clk_enable(hdata->res.sclk_hdmi);
663
664 /* operation mode */
665 buffer[0] = 0x1f;
666 buffer[1] = 0x00;
667
668 if (hdata->hdmiphy_port)
669 i2c_master_send(hdata->hdmiphy_port, buffer, 2);
670
671 /* reset hdmiphy */
672 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
673 mdelay(10);
674 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
675 mdelay(10);
676}
677
678static void hdmiphy_conf_apply(struct hdmi_context *hdata)
679{
680 u8 buffer[32];
681 u8 operation[2];
682 u8 read_buffer[32] = {0, };
683 int ret;
684 int i;
685
686 if (!hdata->hdmiphy_port) {
687 DRM_ERROR("hdmiphy is not attached\n");
688 return;
689 }
690
691 /* pixel clock */
692 memcpy(buffer, hdmi_confs[hdata->cur_conf].hdmiphy_data, 32);
693 ret = i2c_master_send(hdata->hdmiphy_port, buffer, 32);
694 if (ret != 32) {
695 DRM_ERROR("failed to configure HDMIPHY via I2C\n");
696 return;
697 }
698
699 mdelay(10);
700
701 /* operation mode */
702 operation[0] = 0x1f;
703 operation[1] = 0x80;
704
705 ret = i2c_master_send(hdata->hdmiphy_port, operation, 2);
706 if (ret != 2) {
707 DRM_ERROR("failed to enable hdmiphy\n");
708 return;
709 }
710
711 ret = i2c_master_recv(hdata->hdmiphy_port, read_buffer, 32);
712 if (ret < 0) {
713 DRM_ERROR("failed to read hdmiphy config\n");
714 return;
715 }
716
717 for (i = 0; i < ret; i++)
718 DRM_DEBUG_KMS("hdmiphy[0x%02x] write[0x%02x] - "
719 "recv [0x%02x]\n", i, buffer[i], read_buffer[i]);
720}
721
722static void hdmi_conf_apply(struct hdmi_context *hdata)
723{
724 const struct hdmi_preset_conf *conf =
725 hdmi_confs[hdata->cur_conf].conf;
726
727 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
728
729 hdmiphy_conf_reset(hdata);
730 hdmiphy_conf_apply(hdata);
731
732 hdmi_conf_reset(hdata);
733 hdmi_conf_init(hdata);
734
735 /* setting core registers */
736 hdmi_timing_apply(hdata, conf);
737
738 hdmi_regs_dump(hdata, "start");
739}
740
741static void hdmi_mode_set(void *ctx, void *mode)
742{
743 struct hdmi_context *hdata = (struct hdmi_context *)ctx;
744 int conf_idx;
745
746 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
747
748 conf_idx = hdmi_conf_index(mode);
749 if (conf_idx >= 0 && conf_idx < ARRAY_SIZE(hdmi_confs))
750 hdata->cur_conf = conf_idx;
751 else
752 DRM_DEBUG_KMS("not supported mode\n");
753}
754
755static void hdmi_commit(void *ctx)
756{
757 struct hdmi_context *hdata = (struct hdmi_context *)ctx;
758
759 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
760
761 hdmi_conf_apply(hdata);
762
763 hdata->enabled = true;
764}
765
766static void hdmi_disable(void *ctx)
767{
768 struct hdmi_context *hdata = (struct hdmi_context *)ctx;
769
770 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
771
772 if (hdata->enabled) {
773 hdmiphy_conf_reset(hdata);
774 hdmi_conf_reset(hdata);
775 }
776}
777
778static struct exynos_hdmi_manager_ops manager_ops = {
779 .mode_set = hdmi_mode_set,
780 .commit = hdmi_commit,
781 .disable = hdmi_disable,
782};
783
784/*
785 * Handle hotplug events outside the interrupt handler proper.
786 */
787static void hdmi_hotplug_func(struct work_struct *work)
788{
789 struct hdmi_context *hdata =
790 container_of(work, struct hdmi_context, hotplug_work);
791 struct exynos_drm_hdmi_context *ctx =
792 (struct exynos_drm_hdmi_context *)hdata->parent_ctx;
793
794 drm_helper_hpd_irq_event(ctx->drm_dev);
795}
796
797static irqreturn_t hdmi_irq_handler(int irq, void *arg)
798{
799 struct exynos_drm_hdmi_context *ctx = arg;
800 struct hdmi_context *hdata = (struct hdmi_context *)ctx->ctx;
801 u32 intc_flag;
802
803 intc_flag = hdmi_reg_read(hdata, HDMI_INTC_FLAG);
804 /* clearing flags for HPD plug/unplug */
805 if (intc_flag & HDMI_INTC_FLAG_HPD_UNPLUG) {
806 DRM_DEBUG_KMS("unplugged, handling:%d\n", hdata->hpd_handle);
807 hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0,
808 HDMI_INTC_FLAG_HPD_UNPLUG);
809 }
810 if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) {
811 DRM_DEBUG_KMS("plugged, handling:%d\n", hdata->hpd_handle);
812 hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0,
813 HDMI_INTC_FLAG_HPD_PLUG);
814 }
815
816 if (ctx->drm_dev && hdata->hpd_handle)
817 queue_work(hdata->wq, &hdata->hotplug_work);
818
819 return IRQ_HANDLED;
820}
821
822static int __devinit hdmi_resources_init(struct hdmi_context *hdata)
823{
824 struct device *dev = hdata->dev;
825 struct hdmi_resources *res = &hdata->res;
826 static char *supply[] = {
827 "hdmi-en",
828 "vdd",
829 "vdd_osc",
830 "vdd_pll",
831 };
832 int i, ret;
833
834 DRM_DEBUG_KMS("HDMI resource init\n");
835
836 memset(res, 0, sizeof *res);
837
838 /* get clocks, power */
839 res->hdmi = clk_get(dev, "hdmi");
840 if (IS_ERR_OR_NULL(res->hdmi)) {
841 DRM_ERROR("failed to get clock 'hdmi'\n");
842 goto fail;
843 }
844 res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
845 if (IS_ERR_OR_NULL(res->sclk_hdmi)) {
846 DRM_ERROR("failed to get clock 'sclk_hdmi'\n");
847 goto fail;
848 }
849 res->sclk_pixel = clk_get(dev, "sclk_pixel");
850 if (IS_ERR_OR_NULL(res->sclk_pixel)) {
851 DRM_ERROR("failed to get clock 'sclk_pixel'\n");
852 goto fail;
853 }
854 res->sclk_hdmiphy = clk_get(dev, "sclk_hdmiphy");
855 if (IS_ERR_OR_NULL(res->sclk_hdmiphy)) {
856 DRM_ERROR("failed to get clock 'sclk_hdmiphy'\n");
857 goto fail;
858 }
859 res->hdmiphy = clk_get(dev, "hdmiphy");
860 if (IS_ERR_OR_NULL(res->hdmiphy)) {
861 DRM_ERROR("failed to get clock 'hdmiphy'\n");
862 goto fail;
863 }
864
865 clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
866
867 res->regul_bulk = kzalloc(ARRAY_SIZE(supply) *
868 sizeof res->regul_bulk[0], GFP_KERNEL);
869 if (!res->regul_bulk) {
870 DRM_ERROR("failed to get memory for regulators\n");
871 goto fail;
872 }
873 for (i = 0; i < ARRAY_SIZE(supply); ++i) {
874 res->regul_bulk[i].supply = supply[i];
875 res->regul_bulk[i].consumer = NULL;
876 }
877 ret = regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk);
878 if (ret) {
879 DRM_ERROR("failed to get regulators\n");
880 goto fail;
881 }
882 res->regul_count = ARRAY_SIZE(supply);
883
884 return 0;
885fail:
886 DRM_ERROR("HDMI resource init - failed\n");
887 return -ENODEV;
888}
889
890static int hdmi_resources_cleanup(struct hdmi_context *hdata)
891{
892 struct hdmi_resources *res = &hdata->res;
893
894 regulator_bulk_free(res->regul_count, res->regul_bulk);
895 /* kfree is NULL-safe */
896 kfree(res->regul_bulk);
897 if (!IS_ERR_OR_NULL(res->hdmiphy))
898 clk_put(res->hdmiphy);
899 if (!IS_ERR_OR_NULL(res->sclk_hdmiphy))
900 clk_put(res->sclk_hdmiphy);
901 if (!IS_ERR_OR_NULL(res->sclk_pixel))
902 clk_put(res->sclk_pixel);
903 if (!IS_ERR_OR_NULL(res->sclk_hdmi))
904 clk_put(res->sclk_hdmi);
905 if (!IS_ERR_OR_NULL(res->hdmi))
906 clk_put(res->hdmi);
907 memset(res, 0, sizeof *res);
908
909 return 0;
910}
911
912static void hdmi_resource_poweron(struct hdmi_context *hdata)
913{
914 struct hdmi_resources *res = &hdata->res;
915
916 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
917
918 /* turn HDMI power on */
919 regulator_bulk_enable(res->regul_count, res->regul_bulk);
920 /* power-on hdmi physical interface */
921 clk_enable(res->hdmiphy);
922 /* turn clocks on */
923 clk_enable(res->hdmi);
924 clk_enable(res->sclk_hdmi);
925
926 hdmiphy_conf_reset(hdata);
927 hdmi_conf_reset(hdata);
928 hdmi_conf_init(hdata);
929
930}
931
932static void hdmi_resource_poweroff(struct hdmi_context *hdata)
933{
934 struct hdmi_resources *res = &hdata->res;
935
936 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
937
938 /* turn clocks off */
939 clk_disable(res->sclk_hdmi);
940 clk_disable(res->hdmi);
941 /* power-off hdmiphy */
942 clk_disable(res->hdmiphy);
943 /* turn HDMI power off */
944 regulator_bulk_disable(res->regul_count, res->regul_bulk);
945}
946
947static int hdmi_runtime_suspend(struct device *dev)
948{
949 struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
950
951 DRM_DEBUG_KMS("%s\n", __func__);
952
953 hdmi_resource_poweroff((struct hdmi_context *)ctx->ctx);
954
955 return 0;
956}
957
958static int hdmi_runtime_resume(struct device *dev)
959{
960 struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
961
962 DRM_DEBUG_KMS("%s\n", __func__);
963
964 hdmi_resource_poweron((struct hdmi_context *)ctx->ctx);
965
966 return 0;
967}
968
969static const struct dev_pm_ops hdmi_pm_ops = {
970 .runtime_suspend = hdmi_runtime_suspend,
971 .runtime_resume = hdmi_runtime_resume,
972};
973
974static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy;
975
976void hdmi_attach_ddc_client(struct i2c_client *ddc)
977{
978 if (ddc)
979 hdmi_ddc = ddc;
980}
981EXPORT_SYMBOL(hdmi_attach_ddc_client);
982
983void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy)
984{
985 if (hdmiphy)
986 hdmi_hdmiphy = hdmiphy;
987}
988EXPORT_SYMBOL(hdmi_attach_hdmiphy_client);
989
990static int __devinit hdmi_probe(struct platform_device *pdev)
991{
992 struct device *dev = &pdev->dev;
993 struct exynos_drm_hdmi_context *drm_hdmi_ctx;
994 struct hdmi_context *hdata;
995 struct exynos_drm_hdmi_pdata *pdata;
996 struct resource *res;
997 int ret;
998
999 DRM_DEBUG_KMS("[%d]\n", __LINE__);
1000
1001 pdata = pdev->dev.platform_data;
1002 if (!pdata) {
1003 DRM_ERROR("no platform data specified\n");
1004 return -EINVAL;
1005 }
1006
1007 drm_hdmi_ctx = kzalloc(sizeof(*drm_hdmi_ctx), GFP_KERNEL);
1008 if (!drm_hdmi_ctx) {
1009 DRM_ERROR("failed to allocate common hdmi context.\n");
1010 return -ENOMEM;
1011 }
1012
1013 hdata = kzalloc(sizeof(struct hdmi_context), GFP_KERNEL);
1014 if (!hdata) {
1015 DRM_ERROR("out of memory\n");
1016 kfree(drm_hdmi_ctx);
1017 return -ENOMEM;
1018 }
1019
1020 drm_hdmi_ctx->ctx = (void *)hdata;
1021 hdata->parent_ctx = (void *)drm_hdmi_ctx;
1022
1023 platform_set_drvdata(pdev, drm_hdmi_ctx);
1024
1025 hdata->default_win = pdata->default_win;
1026 hdata->default_timing = &pdata->timing;
1027 hdata->default_bpp = pdata->bpp;
1028 hdata->dev = dev;
1029
1030 ret = hdmi_resources_init(hdata);
1031 if (ret) {
1032 ret = -EINVAL;
1033 goto err_data;
1034 }
1035
1036 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1037 if (!res) {
1038 DRM_ERROR("failed to find registers\n");
1039 ret = -ENOENT;
1040 goto err_resource;
1041 }
1042
1043 hdata->regs_res = request_mem_region(res->start, resource_size(res),
1044 dev_name(dev));
1045 if (!hdata->regs_res) {
1046 DRM_ERROR("failed to claim register region\n");
1047 ret = -ENOENT;
1048 goto err_resource;
1049 }
1050
1051 hdata->regs = ioremap(res->start, resource_size(res));
1052 if (!hdata->regs) {
1053 DRM_ERROR("failed to map registers\n");
1054 ret = -ENXIO;
1055 goto err_req_region;
1056 }
1057
1058 /* DDC i2c driver */
1059 if (i2c_add_driver(&ddc_driver)) {
1060 DRM_ERROR("failed to register ddc i2c driver\n");
1061 ret = -ENOENT;
1062 goto err_iomap;
1063 }
1064
1065 hdata->ddc_port = hdmi_ddc;
1066
1067 /* hdmiphy i2c driver */
1068 if (i2c_add_driver(&hdmiphy_driver)) {
1069 DRM_ERROR("failed to register hdmiphy i2c driver\n");
1070 ret = -ENOENT;
1071 goto err_ddc;
1072 }
1073
1074 hdata->hdmiphy_port = hdmi_hdmiphy;
1075
1076 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
1077 if (res == NULL) {
1078 DRM_ERROR("get interrupt resource failed.\n");
1079 ret = -ENXIO;
1080 goto err_hdmiphy;
1081 }
1082
1083 /* create workqueue and hotplug work */
1084 hdata->wq = alloc_workqueue("exynos-drm-hdmi",
1085 WQ_UNBOUND | WQ_NON_REENTRANT, 1);
1086 if (hdata->wq == NULL) {
1087 DRM_ERROR("Failed to create workqueue.\n");
1088 ret = -ENOMEM;
1089 goto err_hdmiphy;
1090 }
1091 INIT_WORK(&hdata->hotplug_work, hdmi_hotplug_func);
1092
1093 /* register hpd interrupt */
1094 ret = request_irq(res->start, hdmi_irq_handler, 0, "drm_hdmi",
1095 drm_hdmi_ctx);
1096 if (ret) {
1097 DRM_ERROR("request interrupt failed.\n");
1098 goto err_workqueue;
1099 }
1100 hdata->irq = res->start;
1101
1102 /* register specific callbacks to common hdmi. */
1103 exynos_drm_display_ops_register(&display_ops);
1104 exynos_drm_manager_ops_register(&manager_ops);
1105
1106 hdmi_resource_poweron(hdata);
1107
1108 return 0;
1109
1110err_workqueue:
1111 destroy_workqueue(hdata->wq);
1112err_hdmiphy:
1113 i2c_del_driver(&hdmiphy_driver);
1114err_ddc:
1115 i2c_del_driver(&ddc_driver);
1116err_iomap:
1117 iounmap(hdata->regs);
1118err_req_region:
1119 release_resource(hdata->regs_res);
1120 kfree(hdata->regs_res);
1121err_resource:
1122 hdmi_resources_cleanup(hdata);
1123err_data:
1124 kfree(hdata);
1125 kfree(drm_hdmi_ctx);
1126 return ret;
1127}
1128
1129static int __devexit hdmi_remove(struct platform_device *pdev)
1130{
1131 struct exynos_drm_hdmi_context *ctx = platform_get_drvdata(pdev);
1132 struct hdmi_context *hdata = (struct hdmi_context *)ctx->ctx;
1133
1134 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
1135
1136 hdmi_resource_poweroff(hdata);
1137
1138 disable_irq(hdata->irq);
1139 free_irq(hdata->irq, hdata);
1140
1141 cancel_work_sync(&hdata->hotplug_work);
1142 destroy_workqueue(hdata->wq);
1143
1144 hdmi_resources_cleanup(hdata);
1145
1146 iounmap(hdata->regs);
1147
1148 release_resource(hdata->regs_res);
1149 kfree(hdata->regs_res);
1150
1151 /* hdmiphy i2c driver */
1152 i2c_del_driver(&hdmiphy_driver);
1153 /* DDC i2c driver */
1154 i2c_del_driver(&ddc_driver);
1155
1156 kfree(hdata);
1157
1158 return 0;
1159}
1160
1161struct platform_driver hdmi_driver = {
1162 .probe = hdmi_probe,
1163 .remove = __devexit_p(hdmi_remove),
1164 .driver = {
1165 .name = "exynos4-hdmi",
1166 .owner = THIS_MODULE,
1167 .pm = &hdmi_pm_ops,
1168 },
1169};
1170EXPORT_SYMBOL(hdmi_driver);
1171
1172MODULE_AUTHOR("Seung-Woo Kim, <sw0312.kim@samsung.com>");
1173MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
1174MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
1175MODULE_DESCRIPTION("Samsung DRM HDMI core Driver");
1176MODULE_LICENSE("GPL");