diff options
author | Tomasz Stanislawski <t.stanislaws@samsung.com> | 2011-03-02 11:16:37 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-07-27 16:56:01 -0400 |
commit | a52074ee7ad0b9ed4b4180c843d1c3114374e172 (patch) | |
tree | c847b6c30e476c0e696f2ef3866053339c2cbe83 /drivers/media/video/s5p-tv/hdmi_drv.c | |
parent | 5930ab2ba089e05c8de594c36836724f8df7725e (diff) |
[media] v4l: s5p-tv: add drivers for HDMI on Samsung S5P platform
Add drivers for HDMI outputs on Samsung platforms from S5P family.
Drivers are using:
- v4l2 framework
- runtime PM
Signed-off-by: Tomasz Stanislawski <t.stanislaws@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Reviewed-by: Marek Szyprowski <m.szyprowski@samsung.com>
Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Reviewed-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/s5p-tv/hdmi_drv.c')
-rw-r--r-- | drivers/media/video/s5p-tv/hdmi_drv.c | 1042 |
1 files changed, 1042 insertions, 0 deletions
diff --git a/drivers/media/video/s5p-tv/hdmi_drv.c b/drivers/media/video/s5p-tv/hdmi_drv.c new file mode 100644 index 000000000000..06d6663f4594 --- /dev/null +++ b/drivers/media/video/s5p-tv/hdmi_drv.c | |||
@@ -0,0 +1,1042 @@ | |||
1 | /* | ||
2 | * Samsung HDMI interface driver | ||
3 | * | ||
4 | * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * Tomasz Stanislawski, <t.stanislaws@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published | ||
10 | * by the Free Software Foundiation. either version 2 of the License, | ||
11 | * or (at your option) any later version | ||
12 | */ | ||
13 | |||
14 | #ifdef CONFIG_VIDEO_SAMSUNG_S5P_HDMI_DEBUG | ||
15 | #define DEBUG | ||
16 | #endif | ||
17 | |||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/io.h> | ||
21 | #include <linux/i2c.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <media/v4l2-subdev.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/irq.h> | ||
27 | #include <linux/delay.h> | ||
28 | #include <linux/bug.h> | ||
29 | #include <linux/pm_runtime.h> | ||
30 | #include <linux/clk.h> | ||
31 | #include <linux/regulator/consumer.h> | ||
32 | |||
33 | #include <media/v4l2-common.h> | ||
34 | #include <media/v4l2-dev.h> | ||
35 | #include <media/v4l2-device.h> | ||
36 | |||
37 | #include "regs-hdmi.h" | ||
38 | |||
39 | MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>"); | ||
40 | MODULE_DESCRIPTION("Samsung HDMI"); | ||
41 | MODULE_LICENSE("GPL"); | ||
42 | |||
43 | /* default preset configured on probe */ | ||
44 | #define HDMI_DEFAULT_PRESET V4L2_DV_1080P60 | ||
45 | |||
46 | struct hdmi_resources { | ||
47 | struct clk *hdmi; | ||
48 | struct clk *sclk_hdmi; | ||
49 | struct clk *sclk_pixel; | ||
50 | struct clk *sclk_hdmiphy; | ||
51 | struct clk *hdmiphy; | ||
52 | struct regulator_bulk_data *regul_bulk; | ||
53 | int regul_count; | ||
54 | }; | ||
55 | |||
56 | struct hdmi_device { | ||
57 | /** base address of HDMI registers */ | ||
58 | void __iomem *regs; | ||
59 | /** HDMI interrupt */ | ||
60 | unsigned int irq; | ||
61 | /** pointer to device parent */ | ||
62 | struct device *dev; | ||
63 | /** subdev generated by HDMI device */ | ||
64 | struct v4l2_subdev sd; | ||
65 | /** V4L2 device structure */ | ||
66 | struct v4l2_device v4l2_dev; | ||
67 | /** subdev of HDMIPHY interface */ | ||
68 | struct v4l2_subdev *phy_sd; | ||
69 | /** configuration of current graphic mode */ | ||
70 | const struct hdmi_preset_conf *cur_conf; | ||
71 | /** current preset */ | ||
72 | u32 cur_preset; | ||
73 | /** other resources */ | ||
74 | struct hdmi_resources res; | ||
75 | }; | ||
76 | |||
77 | struct hdmi_driver_data { | ||
78 | int hdmiphy_bus; | ||
79 | }; | ||
80 | |||
81 | struct 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 | |||
113 | struct 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 | |||
126 | struct hdmi_preset_conf { | ||
127 | struct hdmi_core_regs core; | ||
128 | struct hdmi_tg_regs tg; | ||
129 | struct v4l2_mbus_framefmt mbus_fmt; | ||
130 | }; | ||
131 | |||
132 | /* I2C module and id for HDMIPHY */ | ||
133 | static struct i2c_board_info hdmiphy_info = { | ||
134 | I2C_BOARD_INFO("hdmiphy", 0x38), | ||
135 | }; | ||
136 | |||
137 | static struct hdmi_driver_data hdmi_driver_data[] = { | ||
138 | { .hdmiphy_bus = 3 }, | ||
139 | { .hdmiphy_bus = 8 }, | ||
140 | }; | ||
141 | |||
142 | static struct platform_device_id hdmi_driver_types[] = { | ||
143 | { | ||
144 | .name = "s5pv210-hdmi", | ||
145 | .driver_data = (unsigned long)&hdmi_driver_data[0], | ||
146 | }, { | ||
147 | .name = "exynos4-hdmi", | ||
148 | .driver_data = (unsigned long)&hdmi_driver_data[1], | ||
149 | }, { | ||
150 | /* end node */ | ||
151 | } | ||
152 | }; | ||
153 | |||
154 | static const struct v4l2_subdev_ops hdmi_sd_ops; | ||
155 | |||
156 | static struct hdmi_device *sd_to_hdmi_dev(struct v4l2_subdev *sd) | ||
157 | { | ||
158 | return container_of(sd, struct hdmi_device, sd); | ||
159 | } | ||
160 | |||
161 | static inline | ||
162 | void hdmi_write(struct hdmi_device *hdev, u32 reg_id, u32 value) | ||
163 | { | ||
164 | writel(value, hdev->regs + reg_id); | ||
165 | } | ||
166 | |||
167 | static inline | ||
168 | void hdmi_write_mask(struct hdmi_device *hdev, u32 reg_id, u32 value, u32 mask) | ||
169 | { | ||
170 | u32 old = readl(hdev->regs + reg_id); | ||
171 | value = (value & mask) | (old & ~mask); | ||
172 | writel(value, hdev->regs + reg_id); | ||
173 | } | ||
174 | |||
175 | static inline | ||
176 | void hdmi_writeb(struct hdmi_device *hdev, u32 reg_id, u8 value) | ||
177 | { | ||
178 | writeb(value, hdev->regs + reg_id); | ||
179 | } | ||
180 | |||
181 | static inline u32 hdmi_read(struct hdmi_device *hdev, u32 reg_id) | ||
182 | { | ||
183 | return readl(hdev->regs + reg_id); | ||
184 | } | ||
185 | |||
186 | static irqreturn_t hdmi_irq_handler(int irq, void *dev_data) | ||
187 | { | ||
188 | struct hdmi_device *hdev = dev_data; | ||
189 | u32 intc_flag; | ||
190 | |||
191 | (void)irq; | ||
192 | intc_flag = hdmi_read(hdev, HDMI_INTC_FLAG); | ||
193 | /* clearing flags for HPD plug/unplug */ | ||
194 | if (intc_flag & HDMI_INTC_FLAG_HPD_UNPLUG) { | ||
195 | printk(KERN_INFO "unplugged\n"); | ||
196 | hdmi_write_mask(hdev, HDMI_INTC_FLAG, ~0, | ||
197 | HDMI_INTC_FLAG_HPD_UNPLUG); | ||
198 | } | ||
199 | if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) { | ||
200 | printk(KERN_INFO "plugged\n"); | ||
201 | hdmi_write_mask(hdev, HDMI_INTC_FLAG, ~0, | ||
202 | HDMI_INTC_FLAG_HPD_PLUG); | ||
203 | } | ||
204 | |||
205 | return IRQ_HANDLED; | ||
206 | } | ||
207 | |||
208 | static void hdmi_reg_init(struct hdmi_device *hdev) | ||
209 | { | ||
210 | /* enable HPD interrupts */ | ||
211 | hdmi_write_mask(hdev, HDMI_INTC_CON, ~0, HDMI_INTC_EN_GLOBAL | | ||
212 | HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG); | ||
213 | /* choose HDMI mode */ | ||
214 | hdmi_write_mask(hdev, HDMI_MODE_SEL, | ||
215 | HDMI_MODE_HDMI_EN, HDMI_MODE_MASK); | ||
216 | /* disable bluescreen */ | ||
217 | hdmi_write_mask(hdev, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN); | ||
218 | /* choose bluescreen (fecal) color */ | ||
219 | hdmi_writeb(hdev, HDMI_BLUE_SCREEN_0, 0x12); | ||
220 | hdmi_writeb(hdev, HDMI_BLUE_SCREEN_1, 0x34); | ||
221 | hdmi_writeb(hdev, HDMI_BLUE_SCREEN_2, 0x56); | ||
222 | /* enable AVI packet every vsync, fixes purple line problem */ | ||
223 | hdmi_writeb(hdev, HDMI_AVI_CON, 0x02); | ||
224 | /* force YUV444, look to CEA-861-D, table 7 for more detail */ | ||
225 | hdmi_writeb(hdev, HDMI_AVI_BYTE(0), 2 << 5); | ||
226 | hdmi_write_mask(hdev, HDMI_CON_1, 2, 3 << 5); | ||
227 | } | ||
228 | |||
229 | static void hdmi_timing_apply(struct hdmi_device *hdev, | ||
230 | const struct hdmi_preset_conf *conf) | ||
231 | { | ||
232 | const struct hdmi_core_regs *core = &conf->core; | ||
233 | const struct hdmi_tg_regs *tg = &conf->tg; | ||
234 | |||
235 | /* setting core registers */ | ||
236 | hdmi_writeb(hdev, HDMI_H_BLANK_0, core->h_blank[0]); | ||
237 | hdmi_writeb(hdev, HDMI_H_BLANK_1, core->h_blank[1]); | ||
238 | hdmi_writeb(hdev, HDMI_V_BLANK_0, core->v_blank[0]); | ||
239 | hdmi_writeb(hdev, HDMI_V_BLANK_1, core->v_blank[1]); | ||
240 | hdmi_writeb(hdev, HDMI_V_BLANK_2, core->v_blank[2]); | ||
241 | hdmi_writeb(hdev, HDMI_H_V_LINE_0, core->h_v_line[0]); | ||
242 | hdmi_writeb(hdev, HDMI_H_V_LINE_1, core->h_v_line[1]); | ||
243 | hdmi_writeb(hdev, HDMI_H_V_LINE_2, core->h_v_line[2]); | ||
244 | hdmi_writeb(hdev, HDMI_VSYNC_POL, core->vsync_pol[0]); | ||
245 | hdmi_writeb(hdev, HDMI_INT_PRO_MODE, core->int_pro_mode[0]); | ||
246 | hdmi_writeb(hdev, HDMI_V_BLANK_F_0, core->v_blank_f[0]); | ||
247 | hdmi_writeb(hdev, HDMI_V_BLANK_F_1, core->v_blank_f[1]); | ||
248 | hdmi_writeb(hdev, HDMI_V_BLANK_F_2, core->v_blank_f[2]); | ||
249 | hdmi_writeb(hdev, HDMI_H_SYNC_GEN_0, core->h_sync_gen[0]); | ||
250 | hdmi_writeb(hdev, HDMI_H_SYNC_GEN_1, core->h_sync_gen[1]); | ||
251 | hdmi_writeb(hdev, HDMI_H_SYNC_GEN_2, core->h_sync_gen[2]); | ||
252 | hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_0, core->v_sync_gen1[0]); | ||
253 | hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_1, core->v_sync_gen1[1]); | ||
254 | hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_2, core->v_sync_gen1[2]); | ||
255 | hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_0, core->v_sync_gen2[0]); | ||
256 | hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_1, core->v_sync_gen2[1]); | ||
257 | hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_2, core->v_sync_gen2[2]); | ||
258 | hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_0, core->v_sync_gen3[0]); | ||
259 | hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_1, core->v_sync_gen3[1]); | ||
260 | hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_2, core->v_sync_gen3[2]); | ||
261 | /* Timing generator registers */ | ||
262 | hdmi_writeb(hdev, HDMI_TG_H_FSZ_L, tg->h_fsz_l); | ||
263 | hdmi_writeb(hdev, HDMI_TG_H_FSZ_H, tg->h_fsz_h); | ||
264 | hdmi_writeb(hdev, HDMI_TG_HACT_ST_L, tg->hact_st_l); | ||
265 | hdmi_writeb(hdev, HDMI_TG_HACT_ST_H, tg->hact_st_h); | ||
266 | hdmi_writeb(hdev, HDMI_TG_HACT_SZ_L, tg->hact_sz_l); | ||
267 | hdmi_writeb(hdev, HDMI_TG_HACT_SZ_H, tg->hact_sz_h); | ||
268 | hdmi_writeb(hdev, HDMI_TG_V_FSZ_L, tg->v_fsz_l); | ||
269 | hdmi_writeb(hdev, HDMI_TG_V_FSZ_H, tg->v_fsz_h); | ||
270 | hdmi_writeb(hdev, HDMI_TG_VSYNC_L, tg->vsync_l); | ||
271 | hdmi_writeb(hdev, HDMI_TG_VSYNC_H, tg->vsync_h); | ||
272 | hdmi_writeb(hdev, HDMI_TG_VSYNC2_L, tg->vsync2_l); | ||
273 | hdmi_writeb(hdev, HDMI_TG_VSYNC2_H, tg->vsync2_h); | ||
274 | hdmi_writeb(hdev, HDMI_TG_VACT_ST_L, tg->vact_st_l); | ||
275 | hdmi_writeb(hdev, HDMI_TG_VACT_ST_H, tg->vact_st_h); | ||
276 | hdmi_writeb(hdev, HDMI_TG_VACT_SZ_L, tg->vact_sz_l); | ||
277 | hdmi_writeb(hdev, HDMI_TG_VACT_SZ_H, tg->vact_sz_h); | ||
278 | hdmi_writeb(hdev, HDMI_TG_FIELD_CHG_L, tg->field_chg_l); | ||
279 | hdmi_writeb(hdev, HDMI_TG_FIELD_CHG_H, tg->field_chg_h); | ||
280 | hdmi_writeb(hdev, HDMI_TG_VACT_ST2_L, tg->vact_st2_l); | ||
281 | hdmi_writeb(hdev, HDMI_TG_VACT_ST2_H, tg->vact_st2_h); | ||
282 | hdmi_writeb(hdev, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l); | ||
283 | hdmi_writeb(hdev, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h); | ||
284 | hdmi_writeb(hdev, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l); | ||
285 | hdmi_writeb(hdev, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi_h); | ||
286 | hdmi_writeb(hdev, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi_l); | ||
287 | hdmi_writeb(hdev, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h); | ||
288 | hdmi_writeb(hdev, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l); | ||
289 | hdmi_writeb(hdev, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h); | ||
290 | } | ||
291 | |||
292 | static int hdmi_conf_apply(struct hdmi_device *hdmi_dev) | ||
293 | { | ||
294 | struct device *dev = hdmi_dev->dev; | ||
295 | const struct hdmi_preset_conf *conf = hdmi_dev->cur_conf; | ||
296 | struct v4l2_dv_preset preset; | ||
297 | int ret; | ||
298 | |||
299 | dev_dbg(dev, "%s\n", __func__); | ||
300 | |||
301 | /* reset hdmiphy */ | ||
302 | hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT); | ||
303 | mdelay(10); | ||
304 | hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT); | ||
305 | mdelay(10); | ||
306 | |||
307 | /* configure presets */ | ||
308 | preset.preset = hdmi_dev->cur_preset; | ||
309 | ret = v4l2_subdev_call(hdmi_dev->phy_sd, video, s_dv_preset, &preset); | ||
310 | if (ret) { | ||
311 | dev_err(dev, "failed to set preset (%u)\n", preset.preset); | ||
312 | return ret; | ||
313 | } | ||
314 | |||
315 | /* resetting HDMI core */ | ||
316 | hdmi_write_mask(hdmi_dev, HDMI_CORE_RSTOUT, 0, HDMI_CORE_SW_RSTOUT); | ||
317 | mdelay(10); | ||
318 | hdmi_write_mask(hdmi_dev, HDMI_CORE_RSTOUT, ~0, HDMI_CORE_SW_RSTOUT); | ||
319 | mdelay(10); | ||
320 | |||
321 | hdmi_reg_init(hdmi_dev); | ||
322 | |||
323 | /* setting core registers */ | ||
324 | hdmi_timing_apply(hdmi_dev, conf); | ||
325 | |||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | static void hdmi_dumpregs(struct hdmi_device *hdev, char *prefix) | ||
330 | { | ||
331 | #define DUMPREG(reg_id) \ | ||
332 | dev_dbg(hdev->dev, "%s:" #reg_id " = %08x\n", prefix, \ | ||
333 | readl(hdev->regs + reg_id)) | ||
334 | |||
335 | dev_dbg(hdev->dev, "%s: ---- CONTROL REGISTERS ----\n", prefix); | ||
336 | DUMPREG(HDMI_INTC_FLAG); | ||
337 | DUMPREG(HDMI_INTC_CON); | ||
338 | DUMPREG(HDMI_HPD_STATUS); | ||
339 | DUMPREG(HDMI_PHY_RSTOUT); | ||
340 | DUMPREG(HDMI_PHY_VPLL); | ||
341 | DUMPREG(HDMI_PHY_CMU); | ||
342 | DUMPREG(HDMI_CORE_RSTOUT); | ||
343 | |||
344 | dev_dbg(hdev->dev, "%s: ---- CORE REGISTERS ----\n", prefix); | ||
345 | DUMPREG(HDMI_CON_0); | ||
346 | DUMPREG(HDMI_CON_1); | ||
347 | DUMPREG(HDMI_CON_2); | ||
348 | DUMPREG(HDMI_SYS_STATUS); | ||
349 | DUMPREG(HDMI_PHY_STATUS); | ||
350 | DUMPREG(HDMI_STATUS_EN); | ||
351 | DUMPREG(HDMI_HPD); | ||
352 | DUMPREG(HDMI_MODE_SEL); | ||
353 | DUMPREG(HDMI_HPD_GEN); | ||
354 | DUMPREG(HDMI_DC_CONTROL); | ||
355 | DUMPREG(HDMI_VIDEO_PATTERN_GEN); | ||
356 | |||
357 | dev_dbg(hdev->dev, "%s: ---- CORE SYNC REGISTERS ----\n", prefix); | ||
358 | DUMPREG(HDMI_H_BLANK_0); | ||
359 | DUMPREG(HDMI_H_BLANK_1); | ||
360 | DUMPREG(HDMI_V_BLANK_0); | ||
361 | DUMPREG(HDMI_V_BLANK_1); | ||
362 | DUMPREG(HDMI_V_BLANK_2); | ||
363 | DUMPREG(HDMI_H_V_LINE_0); | ||
364 | DUMPREG(HDMI_H_V_LINE_1); | ||
365 | DUMPREG(HDMI_H_V_LINE_2); | ||
366 | DUMPREG(HDMI_VSYNC_POL); | ||
367 | DUMPREG(HDMI_INT_PRO_MODE); | ||
368 | DUMPREG(HDMI_V_BLANK_F_0); | ||
369 | DUMPREG(HDMI_V_BLANK_F_1); | ||
370 | DUMPREG(HDMI_V_BLANK_F_2); | ||
371 | DUMPREG(HDMI_H_SYNC_GEN_0); | ||
372 | DUMPREG(HDMI_H_SYNC_GEN_1); | ||
373 | DUMPREG(HDMI_H_SYNC_GEN_2); | ||
374 | DUMPREG(HDMI_V_SYNC_GEN_1_0); | ||
375 | DUMPREG(HDMI_V_SYNC_GEN_1_1); | ||
376 | DUMPREG(HDMI_V_SYNC_GEN_1_2); | ||
377 | DUMPREG(HDMI_V_SYNC_GEN_2_0); | ||
378 | DUMPREG(HDMI_V_SYNC_GEN_2_1); | ||
379 | DUMPREG(HDMI_V_SYNC_GEN_2_2); | ||
380 | DUMPREG(HDMI_V_SYNC_GEN_3_0); | ||
381 | DUMPREG(HDMI_V_SYNC_GEN_3_1); | ||
382 | DUMPREG(HDMI_V_SYNC_GEN_3_2); | ||
383 | |||
384 | dev_dbg(hdev->dev, "%s: ---- TG REGISTERS ----\n", prefix); | ||
385 | DUMPREG(HDMI_TG_CMD); | ||
386 | DUMPREG(HDMI_TG_H_FSZ_L); | ||
387 | DUMPREG(HDMI_TG_H_FSZ_H); | ||
388 | DUMPREG(HDMI_TG_HACT_ST_L); | ||
389 | DUMPREG(HDMI_TG_HACT_ST_H); | ||
390 | DUMPREG(HDMI_TG_HACT_SZ_L); | ||
391 | DUMPREG(HDMI_TG_HACT_SZ_H); | ||
392 | DUMPREG(HDMI_TG_V_FSZ_L); | ||
393 | DUMPREG(HDMI_TG_V_FSZ_H); | ||
394 | DUMPREG(HDMI_TG_VSYNC_L); | ||
395 | DUMPREG(HDMI_TG_VSYNC_H); | ||
396 | DUMPREG(HDMI_TG_VSYNC2_L); | ||
397 | DUMPREG(HDMI_TG_VSYNC2_H); | ||
398 | DUMPREG(HDMI_TG_VACT_ST_L); | ||
399 | DUMPREG(HDMI_TG_VACT_ST_H); | ||
400 | DUMPREG(HDMI_TG_VACT_SZ_L); | ||
401 | DUMPREG(HDMI_TG_VACT_SZ_H); | ||
402 | DUMPREG(HDMI_TG_FIELD_CHG_L); | ||
403 | DUMPREG(HDMI_TG_FIELD_CHG_H); | ||
404 | DUMPREG(HDMI_TG_VACT_ST2_L); | ||
405 | DUMPREG(HDMI_TG_VACT_ST2_H); | ||
406 | DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L); | ||
407 | DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H); | ||
408 | DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L); | ||
409 | DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_H); | ||
410 | DUMPREG(HDMI_TG_FIELD_TOP_HDMI_L); | ||
411 | DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H); | ||
412 | DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L); | ||
413 | DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H); | ||
414 | #undef DUMPREG | ||
415 | } | ||
416 | |||
417 | static const struct hdmi_preset_conf hdmi_conf_480p = { | ||
418 | .core = { | ||
419 | .h_blank = {0x8a, 0x00}, | ||
420 | .v_blank = {0x0d, 0x6a, 0x01}, | ||
421 | .h_v_line = {0x0d, 0xa2, 0x35}, | ||
422 | .vsync_pol = {0x01}, | ||
423 | .int_pro_mode = {0x00}, | ||
424 | .v_blank_f = {0x00, 0x00, 0x00}, | ||
425 | .h_sync_gen = {0x0e, 0x30, 0x11}, | ||
426 | .v_sync_gen1 = {0x0f, 0x90, 0x00}, | ||
427 | /* other don't care */ | ||
428 | }, | ||
429 | .tg = { | ||
430 | 0x00, /* cmd */ | ||
431 | 0x5a, 0x03, /* h_fsz */ | ||
432 | 0x8a, 0x00, 0xd0, 0x02, /* hact */ | ||
433 | 0x0d, 0x02, /* v_fsz */ | ||
434 | 0x01, 0x00, 0x33, 0x02, /* vsync */ | ||
435 | 0x2d, 0x00, 0xe0, 0x01, /* vact */ | ||
436 | 0x33, 0x02, /* field_chg */ | ||
437 | 0x49, 0x02, /* vact_st2 */ | ||
438 | 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ | ||
439 | 0x01, 0x00, 0x33, 0x02, /* field top/bot */ | ||
440 | }, | ||
441 | .mbus_fmt = { | ||
442 | .width = 720, | ||
443 | .height = 480, | ||
444 | .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */ | ||
445 | .field = V4L2_FIELD_NONE, | ||
446 | }, | ||
447 | }; | ||
448 | |||
449 | static const struct hdmi_preset_conf hdmi_conf_720p60 = { | ||
450 | .core = { | ||
451 | .h_blank = {0x72, 0x01}, | ||
452 | .v_blank = {0xee, 0xf2, 0x00}, | ||
453 | .h_v_line = {0xee, 0x22, 0x67}, | ||
454 | .vsync_pol = {0x00}, | ||
455 | .int_pro_mode = {0x00}, | ||
456 | .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */ | ||
457 | .h_sync_gen = {0x6c, 0x50, 0x02}, | ||
458 | .v_sync_gen1 = {0x0a, 0x50, 0x00}, | ||
459 | /* other don't care */ | ||
460 | }, | ||
461 | .tg = { | ||
462 | 0x00, /* cmd */ | ||
463 | 0x72, 0x06, /* h_fsz */ | ||
464 | 0x72, 0x01, 0x00, 0x05, /* hact */ | ||
465 | 0xee, 0x02, /* v_fsz */ | ||
466 | 0x01, 0x00, 0x33, 0x02, /* vsync */ | ||
467 | 0x1e, 0x00, 0xd0, 0x02, /* vact */ | ||
468 | 0x33, 0x02, /* field_chg */ | ||
469 | 0x49, 0x02, /* vact_st2 */ | ||
470 | 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ | ||
471 | 0x01, 0x00, 0x33, 0x02, /* field top/bot */ | ||
472 | }, | ||
473 | .mbus_fmt = { | ||
474 | .width = 1280, | ||
475 | .height = 720, | ||
476 | .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */ | ||
477 | .field = V4L2_FIELD_NONE, | ||
478 | }, | ||
479 | }; | ||
480 | |||
481 | static const struct hdmi_preset_conf hdmi_conf_1080p50 = { | ||
482 | .core = { | ||
483 | .h_blank = {0xd0, 0x02}, | ||
484 | .v_blank = {0x65, 0x6c, 0x01}, | ||
485 | .h_v_line = {0x65, 0x04, 0xa5}, | ||
486 | .vsync_pol = {0x00}, | ||
487 | .int_pro_mode = {0x00}, | ||
488 | .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */ | ||
489 | .h_sync_gen = {0x0e, 0xea, 0x08}, | ||
490 | .v_sync_gen1 = {0x09, 0x40, 0x00}, | ||
491 | /* other don't care */ | ||
492 | }, | ||
493 | .tg = { | ||
494 | 0x00, /* cmd */ | ||
495 | 0x98, 0x08, /* h_fsz */ | ||
496 | 0x18, 0x01, 0x80, 0x07, /* hact */ | ||
497 | 0x65, 0x04, /* v_fsz */ | ||
498 | 0x01, 0x00, 0x33, 0x02, /* vsync */ | ||
499 | 0x2d, 0x00, 0x38, 0x04, /* vact */ | ||
500 | 0x33, 0x02, /* field_chg */ | ||
501 | 0x49, 0x02, /* vact_st2 */ | ||
502 | 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ | ||
503 | 0x01, 0x00, 0x33, 0x02, /* field top/bot */ | ||
504 | }, | ||
505 | .mbus_fmt = { | ||
506 | .width = 1920, | ||
507 | .height = 1080, | ||
508 | .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */ | ||
509 | .field = V4L2_FIELD_NONE, | ||
510 | }, | ||
511 | }; | ||
512 | |||
513 | static const struct hdmi_preset_conf hdmi_conf_1080p60 = { | ||
514 | .core = { | ||
515 | .h_blank = {0x18, 0x01}, | ||
516 | .v_blank = {0x65, 0x6c, 0x01}, | ||
517 | .h_v_line = {0x65, 0x84, 0x89}, | ||
518 | .vsync_pol = {0x00}, | ||
519 | .int_pro_mode = {0x00}, | ||
520 | .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */ | ||
521 | .h_sync_gen = {0x56, 0x08, 0x02}, | ||
522 | .v_sync_gen1 = {0x09, 0x40, 0x00}, | ||
523 | /* other don't care */ | ||
524 | }, | ||
525 | .tg = { | ||
526 | 0x00, /* cmd */ | ||
527 | 0x98, 0x08, /* h_fsz */ | ||
528 | 0x18, 0x01, 0x80, 0x07, /* hact */ | ||
529 | 0x65, 0x04, /* v_fsz */ | ||
530 | 0x01, 0x00, 0x33, 0x02, /* vsync */ | ||
531 | 0x2d, 0x00, 0x38, 0x04, /* vact */ | ||
532 | 0x33, 0x02, /* field_chg */ | ||
533 | 0x48, 0x02, /* vact_st2 */ | ||
534 | 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ | ||
535 | 0x01, 0x00, 0x33, 0x02, /* field top/bot */ | ||
536 | }, | ||
537 | .mbus_fmt = { | ||
538 | .width = 1920, | ||
539 | .height = 1080, | ||
540 | .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */ | ||
541 | .field = V4L2_FIELD_NONE, | ||
542 | }, | ||
543 | }; | ||
544 | |||
545 | static const struct { | ||
546 | u32 preset; | ||
547 | const struct hdmi_preset_conf *conf; | ||
548 | } hdmi_conf[] = { | ||
549 | { V4L2_DV_480P59_94, &hdmi_conf_480p }, | ||
550 | { V4L2_DV_720P59_94, &hdmi_conf_720p60 }, | ||
551 | { V4L2_DV_1080P50, &hdmi_conf_1080p50 }, | ||
552 | { V4L2_DV_1080P30, &hdmi_conf_1080p60 }, | ||
553 | { V4L2_DV_1080P60, &hdmi_conf_1080p60 }, | ||
554 | }; | ||
555 | |||
556 | static const struct hdmi_preset_conf *hdmi_preset2conf(u32 preset) | ||
557 | { | ||
558 | int i; | ||
559 | |||
560 | for (i = 0; i < ARRAY_SIZE(hdmi_conf); ++i) | ||
561 | if (hdmi_conf[i].preset == preset) | ||
562 | return hdmi_conf[i].conf; | ||
563 | return NULL; | ||
564 | } | ||
565 | |||
566 | static int hdmi_streamon(struct hdmi_device *hdev) | ||
567 | { | ||
568 | struct device *dev = hdev->dev; | ||
569 | struct hdmi_resources *res = &hdev->res; | ||
570 | int ret, tries; | ||
571 | |||
572 | dev_dbg(dev, "%s\n", __func__); | ||
573 | |||
574 | ret = v4l2_subdev_call(hdev->phy_sd, video, s_stream, 1); | ||
575 | if (ret) | ||
576 | return ret; | ||
577 | |||
578 | /* waiting for HDMIPHY's PLL to get to steady state */ | ||
579 | for (tries = 100; tries; --tries) { | ||
580 | u32 val = hdmi_read(hdev, HDMI_PHY_STATUS); | ||
581 | if (val & HDMI_PHY_STATUS_READY) | ||
582 | break; | ||
583 | mdelay(1); | ||
584 | } | ||
585 | /* steady state not achieved */ | ||
586 | if (tries == 0) { | ||
587 | dev_err(dev, "hdmiphy's pll could not reach steady state.\n"); | ||
588 | v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0); | ||
589 | hdmi_dumpregs(hdev, "s_stream"); | ||
590 | return -EIO; | ||
591 | } | ||
592 | |||
593 | /* hdmiphy clock is used for HDMI in streaming mode */ | ||
594 | clk_disable(res->sclk_hdmi); | ||
595 | clk_set_parent(res->sclk_hdmi, res->sclk_hdmiphy); | ||
596 | clk_enable(res->sclk_hdmi); | ||
597 | |||
598 | /* enable HDMI and timing generator */ | ||
599 | hdmi_write_mask(hdev, HDMI_CON_0, ~0, HDMI_EN); | ||
600 | hdmi_write_mask(hdev, HDMI_TG_CMD, ~0, HDMI_TG_EN); | ||
601 | hdmi_dumpregs(hdev, "streamon"); | ||
602 | return 0; | ||
603 | } | ||
604 | |||
605 | static int hdmi_streamoff(struct hdmi_device *hdev) | ||
606 | { | ||
607 | struct device *dev = hdev->dev; | ||
608 | struct hdmi_resources *res = &hdev->res; | ||
609 | |||
610 | dev_dbg(dev, "%s\n", __func__); | ||
611 | |||
612 | hdmi_write_mask(hdev, HDMI_CON_0, 0, HDMI_EN); | ||
613 | hdmi_write_mask(hdev, HDMI_TG_CMD, 0, HDMI_TG_EN); | ||
614 | |||
615 | /* pixel(vpll) clock is used for HDMI in config mode */ | ||
616 | clk_disable(res->sclk_hdmi); | ||
617 | clk_set_parent(res->sclk_hdmi, res->sclk_pixel); | ||
618 | clk_enable(res->sclk_hdmi); | ||
619 | |||
620 | v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0); | ||
621 | |||
622 | hdmi_dumpregs(hdev, "streamoff"); | ||
623 | return 0; | ||
624 | } | ||
625 | |||
626 | static int hdmi_s_stream(struct v4l2_subdev *sd, int enable) | ||
627 | { | ||
628 | struct hdmi_device *hdev = sd_to_hdmi_dev(sd); | ||
629 | struct device *dev = hdev->dev; | ||
630 | |||
631 | dev_dbg(dev, "%s(%d)\n", __func__, enable); | ||
632 | if (enable) | ||
633 | return hdmi_streamon(hdev); | ||
634 | return hdmi_streamoff(hdev); | ||
635 | } | ||
636 | |||
637 | static void hdmi_resource_poweron(struct hdmi_resources *res) | ||
638 | { | ||
639 | /* turn HDMI power on */ | ||
640 | regulator_bulk_enable(res->regul_count, res->regul_bulk); | ||
641 | /* power-on hdmi physical interface */ | ||
642 | clk_enable(res->hdmiphy); | ||
643 | /* use VPP as parent clock; HDMIPHY is not working yet */ | ||
644 | clk_set_parent(res->sclk_hdmi, res->sclk_pixel); | ||
645 | /* turn clocks on */ | ||
646 | clk_enable(res->sclk_hdmi); | ||
647 | } | ||
648 | |||
649 | static void hdmi_resource_poweroff(struct hdmi_resources *res) | ||
650 | { | ||
651 | /* turn clocks off */ | ||
652 | clk_disable(res->sclk_hdmi); | ||
653 | /* power-off hdmiphy */ | ||
654 | clk_disable(res->hdmiphy); | ||
655 | /* turn HDMI power off */ | ||
656 | regulator_bulk_disable(res->regul_count, res->regul_bulk); | ||
657 | } | ||
658 | |||
659 | static int hdmi_s_power(struct v4l2_subdev *sd, int on) | ||
660 | { | ||
661 | struct hdmi_device *hdev = sd_to_hdmi_dev(sd); | ||
662 | int ret; | ||
663 | |||
664 | if (on) | ||
665 | ret = pm_runtime_get_sync(hdev->dev); | ||
666 | else | ||
667 | ret = pm_runtime_put_sync(hdev->dev); | ||
668 | /* only values < 0 indicate errors */ | ||
669 | return IS_ERR_VALUE(ret) ? ret : 0; | ||
670 | } | ||
671 | |||
672 | static int hdmi_s_dv_preset(struct v4l2_subdev *sd, | ||
673 | struct v4l2_dv_preset *preset) | ||
674 | { | ||
675 | struct hdmi_device *hdev = sd_to_hdmi_dev(sd); | ||
676 | struct device *dev = hdev->dev; | ||
677 | const struct hdmi_preset_conf *conf; | ||
678 | |||
679 | conf = hdmi_preset2conf(preset->preset); | ||
680 | if (conf == NULL) { | ||
681 | dev_err(dev, "preset (%u) not supported\n", preset->preset); | ||
682 | return -EINVAL; | ||
683 | } | ||
684 | hdev->cur_conf = conf; | ||
685 | hdev->cur_preset = preset->preset; | ||
686 | return 0; | ||
687 | } | ||
688 | |||
689 | static int hdmi_g_dv_preset(struct v4l2_subdev *sd, | ||
690 | struct v4l2_dv_preset *preset) | ||
691 | { | ||
692 | memset(preset, 0, sizeof(*preset)); | ||
693 | preset->preset = sd_to_hdmi_dev(sd)->cur_preset; | ||
694 | return 0; | ||
695 | } | ||
696 | |||
697 | static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd, | ||
698 | struct v4l2_mbus_framefmt *fmt) | ||
699 | { | ||
700 | struct hdmi_device *hdev = sd_to_hdmi_dev(sd); | ||
701 | struct device *dev = hdev->dev; | ||
702 | |||
703 | dev_dbg(dev, "%s\n", __func__); | ||
704 | if (!hdev->cur_conf) | ||
705 | return -EINVAL; | ||
706 | *fmt = hdev->cur_conf->mbus_fmt; | ||
707 | return 0; | ||
708 | } | ||
709 | |||
710 | static int hdmi_enum_dv_presets(struct v4l2_subdev *sd, | ||
711 | struct v4l2_dv_enum_preset *preset) | ||
712 | { | ||
713 | if (preset->index >= ARRAY_SIZE(hdmi_conf)) | ||
714 | return -EINVAL; | ||
715 | return v4l_fill_dv_preset_info(hdmi_conf[preset->index].preset, preset); | ||
716 | } | ||
717 | |||
718 | static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = { | ||
719 | .s_power = hdmi_s_power, | ||
720 | }; | ||
721 | |||
722 | static const struct v4l2_subdev_video_ops hdmi_sd_video_ops = { | ||
723 | .s_dv_preset = hdmi_s_dv_preset, | ||
724 | .g_dv_preset = hdmi_g_dv_preset, | ||
725 | .enum_dv_presets = hdmi_enum_dv_presets, | ||
726 | .g_mbus_fmt = hdmi_g_mbus_fmt, | ||
727 | .s_stream = hdmi_s_stream, | ||
728 | }; | ||
729 | |||
730 | static const struct v4l2_subdev_ops hdmi_sd_ops = { | ||
731 | .core = &hdmi_sd_core_ops, | ||
732 | .video = &hdmi_sd_video_ops, | ||
733 | }; | ||
734 | |||
735 | static int hdmi_runtime_suspend(struct device *dev) | ||
736 | { | ||
737 | struct v4l2_subdev *sd = dev_get_drvdata(dev); | ||
738 | struct hdmi_device *hdev = sd_to_hdmi_dev(sd); | ||
739 | |||
740 | dev_dbg(dev, "%s\n", __func__); | ||
741 | hdmi_resource_poweroff(&hdev->res); | ||
742 | return 0; | ||
743 | } | ||
744 | |||
745 | static int hdmi_runtime_resume(struct device *dev) | ||
746 | { | ||
747 | struct v4l2_subdev *sd = dev_get_drvdata(dev); | ||
748 | struct hdmi_device *hdev = sd_to_hdmi_dev(sd); | ||
749 | int ret = 0; | ||
750 | |||
751 | dev_dbg(dev, "%s\n", __func__); | ||
752 | |||
753 | hdmi_resource_poweron(&hdev->res); | ||
754 | |||
755 | ret = hdmi_conf_apply(hdev); | ||
756 | if (ret) | ||
757 | goto fail; | ||
758 | |||
759 | dev_dbg(dev, "poweron succeed\n"); | ||
760 | |||
761 | return 0; | ||
762 | |||
763 | fail: | ||
764 | hdmi_resource_poweroff(&hdev->res); | ||
765 | dev_err(dev, "poweron failed\n"); | ||
766 | |||
767 | return ret; | ||
768 | } | ||
769 | |||
770 | static const struct dev_pm_ops hdmi_pm_ops = { | ||
771 | .runtime_suspend = hdmi_runtime_suspend, | ||
772 | .runtime_resume = hdmi_runtime_resume, | ||
773 | }; | ||
774 | |||
775 | static void hdmi_resources_cleanup(struct hdmi_device *hdev) | ||
776 | { | ||
777 | struct hdmi_resources *res = &hdev->res; | ||
778 | |||
779 | dev_dbg(hdev->dev, "HDMI resource cleanup\n"); | ||
780 | /* put clocks, power */ | ||
781 | if (res->regul_count) | ||
782 | regulator_bulk_free(res->regul_count, res->regul_bulk); | ||
783 | /* kfree is NULL-safe */ | ||
784 | kfree(res->regul_bulk); | ||
785 | if (!IS_ERR_OR_NULL(res->hdmiphy)) | ||
786 | clk_put(res->hdmiphy); | ||
787 | if (!IS_ERR_OR_NULL(res->sclk_hdmiphy)) | ||
788 | clk_put(res->sclk_hdmiphy); | ||
789 | if (!IS_ERR_OR_NULL(res->sclk_pixel)) | ||
790 | clk_put(res->sclk_pixel); | ||
791 | if (!IS_ERR_OR_NULL(res->sclk_hdmi)) | ||
792 | clk_put(res->sclk_hdmi); | ||
793 | if (!IS_ERR_OR_NULL(res->hdmi)) | ||
794 | clk_put(res->hdmi); | ||
795 | memset(res, 0, sizeof *res); | ||
796 | } | ||
797 | |||
798 | static int hdmi_resources_init(struct hdmi_device *hdev) | ||
799 | { | ||
800 | struct device *dev = hdev->dev; | ||
801 | struct hdmi_resources *res = &hdev->res; | ||
802 | static char *supply[] = { | ||
803 | "hdmi-en", | ||
804 | "vdd", | ||
805 | "vdd_osc", | ||
806 | "vdd_pll", | ||
807 | }; | ||
808 | int i, ret; | ||
809 | |||
810 | dev_dbg(dev, "HDMI resource init\n"); | ||
811 | |||
812 | memset(res, 0, sizeof *res); | ||
813 | /* get clocks, power */ | ||
814 | |||
815 | res->hdmi = clk_get(dev, "hdmi"); | ||
816 | if (IS_ERR_OR_NULL(res->hdmi)) { | ||
817 | dev_err(dev, "failed to get clock 'hdmi'\n"); | ||
818 | goto fail; | ||
819 | } | ||
820 | res->sclk_hdmi = clk_get(dev, "sclk_hdmi"); | ||
821 | if (IS_ERR_OR_NULL(res->sclk_hdmi)) { | ||
822 | dev_err(dev, "failed to get clock 'sclk_hdmi'\n"); | ||
823 | goto fail; | ||
824 | } | ||
825 | res->sclk_pixel = clk_get(dev, "sclk_pixel"); | ||
826 | if (IS_ERR_OR_NULL(res->sclk_pixel)) { | ||
827 | dev_err(dev, "failed to get clock 'sclk_pixel'\n"); | ||
828 | goto fail; | ||
829 | } | ||
830 | res->sclk_hdmiphy = clk_get(dev, "sclk_hdmiphy"); | ||
831 | if (IS_ERR_OR_NULL(res->sclk_hdmiphy)) { | ||
832 | dev_err(dev, "failed to get clock 'sclk_hdmiphy'\n"); | ||
833 | goto fail; | ||
834 | } | ||
835 | res->hdmiphy = clk_get(dev, "hdmiphy"); | ||
836 | if (IS_ERR_OR_NULL(res->hdmiphy)) { | ||
837 | dev_err(dev, "failed to get clock 'hdmiphy'\n"); | ||
838 | goto fail; | ||
839 | } | ||
840 | res->regul_bulk = kzalloc(ARRAY_SIZE(supply) * | ||
841 | sizeof res->regul_bulk[0], GFP_KERNEL); | ||
842 | if (!res->regul_bulk) { | ||
843 | dev_err(dev, "failed to get memory for regulators\n"); | ||
844 | goto fail; | ||
845 | } | ||
846 | for (i = 0; i < ARRAY_SIZE(supply); ++i) { | ||
847 | res->regul_bulk[i].supply = supply[i]; | ||
848 | res->regul_bulk[i].consumer = NULL; | ||
849 | } | ||
850 | |||
851 | ret = regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk); | ||
852 | if (ret) { | ||
853 | dev_err(dev, "failed to get regulators\n"); | ||
854 | goto fail; | ||
855 | } | ||
856 | res->regul_count = ARRAY_SIZE(supply); | ||
857 | |||
858 | return 0; | ||
859 | fail: | ||
860 | dev_err(dev, "HDMI resource init - failed\n"); | ||
861 | hdmi_resources_cleanup(hdev); | ||
862 | return -ENODEV; | ||
863 | } | ||
864 | |||
865 | static int __devinit hdmi_probe(struct platform_device *pdev) | ||
866 | { | ||
867 | struct device *dev = &pdev->dev; | ||
868 | struct resource *res; | ||
869 | struct i2c_adapter *phy_adapter; | ||
870 | struct v4l2_subdev *sd; | ||
871 | struct hdmi_device *hdmi_dev = NULL; | ||
872 | struct hdmi_driver_data *drv_data; | ||
873 | int ret; | ||
874 | |||
875 | dev_dbg(dev, "probe start\n"); | ||
876 | |||
877 | hdmi_dev = kzalloc(sizeof(*hdmi_dev), GFP_KERNEL); | ||
878 | if (!hdmi_dev) { | ||
879 | dev_err(dev, "out of memory\n"); | ||
880 | ret = -ENOMEM; | ||
881 | goto fail; | ||
882 | } | ||
883 | |||
884 | hdmi_dev->dev = dev; | ||
885 | |||
886 | ret = hdmi_resources_init(hdmi_dev); | ||
887 | if (ret) | ||
888 | goto fail_hdev; | ||
889 | |||
890 | /* mapping HDMI registers */ | ||
891 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
892 | if (res == NULL) { | ||
893 | dev_err(dev, "get memory resource failed.\n"); | ||
894 | ret = -ENXIO; | ||
895 | goto fail_init; | ||
896 | } | ||
897 | |||
898 | hdmi_dev->regs = ioremap(res->start, resource_size(res)); | ||
899 | if (hdmi_dev->regs == NULL) { | ||
900 | dev_err(dev, "register mapping failed.\n"); | ||
901 | ret = -ENXIO; | ||
902 | goto fail_hdev; | ||
903 | } | ||
904 | |||
905 | res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
906 | if (res == NULL) { | ||
907 | dev_err(dev, "get interrupt resource failed.\n"); | ||
908 | ret = -ENXIO; | ||
909 | goto fail_regs; | ||
910 | } | ||
911 | |||
912 | ret = request_irq(res->start, hdmi_irq_handler, 0, "hdmi", hdmi_dev); | ||
913 | if (ret) { | ||
914 | dev_err(dev, "request interrupt failed.\n"); | ||
915 | goto fail_regs; | ||
916 | } | ||
917 | hdmi_dev->irq = res->start; | ||
918 | |||
919 | /* setting v4l2 name to prevent WARN_ON in v4l2_device_register */ | ||
920 | strlcpy(hdmi_dev->v4l2_dev.name, dev_name(dev), | ||
921 | sizeof(hdmi_dev->v4l2_dev.name)); | ||
922 | /* passing NULL owner prevents driver from erasing drvdata */ | ||
923 | ret = v4l2_device_register(NULL, &hdmi_dev->v4l2_dev); | ||
924 | if (ret) { | ||
925 | dev_err(dev, "could not register v4l2 device.\n"); | ||
926 | goto fail_irq; | ||
927 | } | ||
928 | |||
929 | drv_data = (struct hdmi_driver_data *) | ||
930 | platform_get_device_id(pdev)->driver_data; | ||
931 | phy_adapter = i2c_get_adapter(drv_data->hdmiphy_bus); | ||
932 | if (phy_adapter == NULL) { | ||
933 | dev_err(dev, "adapter request failed\n"); | ||
934 | ret = -ENXIO; | ||
935 | goto fail_vdev; | ||
936 | } | ||
937 | |||
938 | hdmi_dev->phy_sd = v4l2_i2c_new_subdev_board(&hdmi_dev->v4l2_dev, | ||
939 | phy_adapter, &hdmiphy_info, NULL); | ||
940 | /* on failure or not adapter is no longer useful */ | ||
941 | i2c_put_adapter(phy_adapter); | ||
942 | if (hdmi_dev->phy_sd == NULL) { | ||
943 | dev_err(dev, "missing subdev for hdmiphy\n"); | ||
944 | ret = -ENODEV; | ||
945 | goto fail_vdev; | ||
946 | } | ||
947 | |||
948 | clk_enable(hdmi_dev->res.hdmi); | ||
949 | |||
950 | pm_runtime_enable(dev); | ||
951 | |||
952 | sd = &hdmi_dev->sd; | ||
953 | v4l2_subdev_init(sd, &hdmi_sd_ops); | ||
954 | sd->owner = THIS_MODULE; | ||
955 | |||
956 | strlcpy(sd->name, "s5p-hdmi", sizeof sd->name); | ||
957 | hdmi_dev->cur_preset = HDMI_DEFAULT_PRESET; | ||
958 | /* FIXME: missing fail preset is not supported */ | ||
959 | hdmi_dev->cur_conf = hdmi_preset2conf(hdmi_dev->cur_preset); | ||
960 | |||
961 | /* storing subdev for call that have only access to struct device */ | ||
962 | dev_set_drvdata(dev, sd); | ||
963 | |||
964 | dev_info(dev, "probe sucessful\n"); | ||
965 | |||
966 | return 0; | ||
967 | |||
968 | fail_vdev: | ||
969 | v4l2_device_unregister(&hdmi_dev->v4l2_dev); | ||
970 | |||
971 | fail_irq: | ||
972 | free_irq(hdmi_dev->irq, hdmi_dev); | ||
973 | |||
974 | fail_regs: | ||
975 | iounmap(hdmi_dev->regs); | ||
976 | |||
977 | fail_init: | ||
978 | hdmi_resources_cleanup(hdmi_dev); | ||
979 | |||
980 | fail_hdev: | ||
981 | kfree(hdmi_dev); | ||
982 | |||
983 | fail: | ||
984 | dev_err(dev, "probe failed\n"); | ||
985 | return ret; | ||
986 | } | ||
987 | |||
988 | static int __devexit hdmi_remove(struct platform_device *pdev) | ||
989 | { | ||
990 | struct device *dev = &pdev->dev; | ||
991 | struct v4l2_subdev *sd = dev_get_drvdata(dev); | ||
992 | struct hdmi_device *hdmi_dev = sd_to_hdmi_dev(sd); | ||
993 | |||
994 | pm_runtime_disable(dev); | ||
995 | clk_disable(hdmi_dev->res.hdmi); | ||
996 | v4l2_device_unregister(&hdmi_dev->v4l2_dev); | ||
997 | disable_irq(hdmi_dev->irq); | ||
998 | free_irq(hdmi_dev->irq, hdmi_dev); | ||
999 | iounmap(hdmi_dev->regs); | ||
1000 | hdmi_resources_cleanup(hdmi_dev); | ||
1001 | kfree(hdmi_dev); | ||
1002 | dev_info(dev, "remove sucessful\n"); | ||
1003 | |||
1004 | return 0; | ||
1005 | } | ||
1006 | |||
1007 | static struct platform_driver hdmi_driver __refdata = { | ||
1008 | .probe = hdmi_probe, | ||
1009 | .remove = __devexit_p(hdmi_remove), | ||
1010 | .id_table = hdmi_driver_types, | ||
1011 | .driver = { | ||
1012 | .name = "s5p-hdmi", | ||
1013 | .owner = THIS_MODULE, | ||
1014 | .pm = &hdmi_pm_ops, | ||
1015 | } | ||
1016 | }; | ||
1017 | |||
1018 | /* D R I V E R I N I T I A L I Z A T I O N */ | ||
1019 | |||
1020 | static int __init hdmi_init(void) | ||
1021 | { | ||
1022 | int ret; | ||
1023 | static const char banner[] __initdata = KERN_INFO \ | ||
1024 | "Samsung HDMI output driver, " | ||
1025 | "(c) 2010-2011 Samsung Electronics Co., Ltd.\n"; | ||
1026 | printk(banner); | ||
1027 | |||
1028 | ret = platform_driver_register(&hdmi_driver); | ||
1029 | if (ret) | ||
1030 | printk(KERN_ERR "HDMI platform driver register failed\n"); | ||
1031 | |||
1032 | return ret; | ||
1033 | } | ||
1034 | module_init(hdmi_init); | ||
1035 | |||
1036 | static void __exit hdmi_exit(void) | ||
1037 | { | ||
1038 | platform_driver_unregister(&hdmi_driver); | ||
1039 | } | ||
1040 | module_exit(hdmi_exit); | ||
1041 | |||
1042 | |||