upload tizen1.0 source
[kernel/linux-2.6.36.git] / arch / arm / mach-s5pv310 / dev-tv.c
1 /* linux/arch/arm/mach-s5pv310/dev-tv.c
2  *
3  * Copyright 20i10 Samsung Electronics
4  *      Tomasz Stanislawski <t.stanislaws@samsung.com>
5  *
6  * S5P series device definition for TV device
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 version 2 as
10  * published by the Free Software Foundation.
11 */
12
13 #include "plat/tv.h"
14
15 #include <mach/gpio.h>
16 #include <plat/gpio-cfg.h>
17 #include <mach/regs-clock.h>
18
19 #include <linux/kernel.h>
20 #include <linux/string.h>
21 #include <linux/platform_device.h>
22 #include <linux/fb.h>
23 #include <linux/gfp.h>
24 #include <linux/dma-mapping.h>
25 #include <linux/clk.h>
26 #include <linux/regulator/consumer.h>
27 #include <linux/delay.h>
28
29 #include <mach/irqs.h>
30 #include <mach/map.h>
31
32 #include <plat/devs.h>
33 #include <plat/cpu.h>
34
35 /* TVOUT related */
36 #define S5P_TV_CONFIGURATION            S5P_PMREG(0x03C20)
37 #define S5P_TV_STATUS                   S5P_PMREG(0x03C24)
38 #define S5P_TV_SYS_PWR                  S5P_PMREG(0x1384)
39 #define S5P_CMU_CLKSTOP_TV_SYS_PWR      S5P_PMREG(0x1144)
40 #define S5P_CMU_RESET_TV_SYS_PWR        S5P_PMREG(0x1164)
41
42 #define S5P_HDMI_PHY_CONTROL            S5P_PMREG(0x0700)
43 #define S5P_CLKOUT_CMU_TOP              S5P_CLKREG(0x0CA00)
44
45 /* macro for setting registeres */
46 #define SETREG(name, value, mask) \
47 do { \
48         u32 reg = readl(name); \
49         reg &= ~mask; \
50         reg |= value; \
51         writel(reg, name); \
52         reg = readl(name); \
53         printk(KERN_DEBUG #name " <- %08x\n", reg); \
54 } while (0)
55
56 /* macro for debugging registeres */
57 #define DBGREG(name) \
58 do { \
59         u32 reg = readl(name); \
60         printk(KERN_DEBUG #name " <- %08x\n", reg); \
61 } while (0)
62
63 static void dbg_plat_regs(void);
64
65 /* very simpler tv-power-domain support */
66 static int tv_power_cnt;
67
68 static void tv_power_get(void)
69 {
70         if (++tv_power_cnt == 1) {
71                 int tries;
72                 printk(KERN_ERR "TV power domain on - start\n");
73                 dbg_plat_regs();
74                 SETREG(S5P_CLKGATE_BLOCK, 0x02, 0x02); /* keep here */
75                 SETREG(S5P_CMU_CLKSTOP_TV_SYS_PWR, 1, 1);
76                 SETREG(S5P_CMU_RESET_TV_SYS_PWR, 1, 1);
77                 SETREG(S5P_TV_SYS_PWR, 7, 7);
78                 /* power domain on sequence */
79                 SETREG(S5P_TV_CONFIGURATION, 7, 7);
80                 for (tries = 1000; tries; mdelay(1), tries--)
81                         if ((readl(S5P_TV_STATUS) & 7) == 7)
82                                 break;
83                 WARN(tries == 0, "failed to turn TV power domain on\n");
84                 printk(KERN_ERR "TV power domain on - finish\n");
85                 dbg_plat_regs();
86         }
87 }
88
89 static void tv_power_put(void)
90 {
91         if (--tv_power_cnt == 0) {
92                 int tries;
93                 printk(KERN_ERR "TV power domain off - start\n");
94                 dbg_plat_regs();
95                 SETREG(S5P_TV_CONFIGURATION, 0, 7);
96                 for (tries = 1000; tries; mdelay(1), tries--)
97                         if ((readl(S5P_TV_STATUS) & 7) == 0)
98                                 break;
99                 WARN(tries == 0, "failed to turn TV power domain off\n");
100                 SETREG(S5P_TV_SYS_PWR, 0, 7);
101                 SETREG(S5P_CMU_RESET_TV_SYS_PWR, 0, 1);
102                 SETREG(S5P_CMU_CLKSTOP_TV_SYS_PWR, 0, 1);
103                 SETREG(S5P_CLKGATE_BLOCK, 0x00, 0x02); /* keep here */
104                 printk(KERN_ERR "TV power domain off - finish\n");
105                 dbg_plat_regs();
106         }
107 }
108
109 /* HDMI interface */
110 static struct resource s5p_hdmi_resources[] = {
111         [0] = {
112                 .start  = S5P_PA_HDMI,
113                 .end    = S5P_PA_HDMI + S5P_SZ_HDMI - 1,
114                 .flags  = IORESOURCE_MEM,
115         },
116         [1] = {
117                 .start  = IRQ_HDMI,
118                 .end    = IRQ_HDMI,
119                 .flags  = IORESOURCE_IRQ,
120         },
121 };
122
123 static struct hdmi_platform_data hdmi_pdata;
124
125 struct platform_device s5p_device_hdmi = {
126         .name           = "s5p-hdmi",
127         .id             = -1,
128         .num_resources  = ARRAY_SIZE(s5p_hdmi_resources),
129         .resource       = s5p_hdmi_resources,
130         .dev.platform_data = &hdmi_pdata,
131 };
132 EXPORT_SYMBOL(s5p_device_hdmi);
133
134 static struct hdmi_plat_resource {
135         struct clk *hdmi;
136         struct clk *sclk_hdmi;
137         struct clk *sclk_pixel;
138         struct clk *sclk_hdmiphy;
139         struct regulator *ldo1;
140 } hdmi_plat_resource;
141
142 static void hdmi_deinit(struct device *dev);
143
144 static int hdmi_init(struct device *dev)
145 {
146         struct hdmi_plat_resource *res = &hdmi_plat_resource;
147         dev_info(dev, "platform HDMI Init\n");
148         memset(res, 0, sizeof *res);
149         /* get clocks, power, and GPIOs */
150         gpio_request(S5PV310_GPX3(7), "hpd-plug");
151         gpio_request(S5PV310_GPL1(1), "hdmi-en");
152
153         /* direct HPD to HDMI chip */
154         gpio_direction_input(S5PV310_GPX3(7));
155         s3c_gpio_cfgpin(S5PV310_GPX3(7), S3C_GPIO_SFN(0x3));
156         s3c_gpio_setpull(S5PV310_GPX3(7), S3C_GPIO_PULL_NONE);
157
158         /* move this names somewhere */
159         res->hdmi = clk_get(dev, "hdmi");
160         if (IS_ERR_OR_NULL(res->hdmi)) {
161                 dev_err(dev, "failed to get clock 'hdmi'\n");
162                 goto fail;
163         }
164         res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
165         if (IS_ERR_OR_NULL(res->sclk_hdmi)) {
166                 dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
167                 goto fail;
168         }
169         res->sclk_pixel = clk_get(dev, "sclk_pixel");
170         if (IS_ERR_OR_NULL(res->sclk_pixel)) {
171                 dev_err(dev, "failed to get clock 'sclk_pixel'\n");
172                 goto fail;
173         }
174         res->sclk_hdmiphy = clk_get(dev, "sclk_hdmiphy");
175         if (IS_ERR_OR_NULL(res->sclk_hdmiphy)) {
176                 dev_err(dev, "failed to get clock 'sclk_hdmiphy'\n");
177                 goto fail;
178         }
179         res->ldo1 = regulator_get(dev, "vadc");
180         if (IS_ERR_OR_NULL(res->ldo1)) {
181                 dev_err(dev, "failed to get regulator 'ldo1'\n");
182                 goto fail;
183         }
184
185         /* use VPP as parent clock; HDMIPHY is not working yet */
186         clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
187         /* regulator_enable(res->ldo1); */
188         tv_power_get();
189
190         dbg_plat_regs();
191
192         return 0;
193 fail:
194         dev_err(dev, "platform HDMI Init - failed\n");
195         hdmi_deinit(dev);
196         return -ENODEV;
197 }
198
199 static void hdmi_deinit(struct device *dev)
200 {
201         struct hdmi_plat_resource *res = &hdmi_plat_resource;
202         dev_info(dev, "platform HDMI Deinit\n");
203         /* put clocks, power, and GPIOs */
204         if (!IS_ERR_OR_NULL(res->ldo1)) {
205                 tv_power_put();
206                 /* regulator_disable(res->ldo1); */
207                 regulator_put(res->ldo1);
208         }
209         if (!IS_ERR_OR_NULL(res->sclk_hdmiphy))
210                 clk_put(res->sclk_hdmiphy);
211         if (!IS_ERR_OR_NULL(res->sclk_pixel))
212                 clk_put(res->sclk_pixel);
213         if (!IS_ERR_OR_NULL(res->sclk_hdmi))
214                 clk_put(res->sclk_hdmi);
215         if (!IS_ERR_OR_NULL(res->hdmi))
216                 clk_put(res->hdmi);
217         memset(res, 0, sizeof *res);
218         gpio_free(S5PV310_GPL1(1));
219         gpio_free(S5PV310_GPX3(7));
220 }
221
222 static int hdmi_power_setup(struct device *dev, int en)
223 {
224         struct hdmi_plat_resource *res = &hdmi_plat_resource;
225         if (en) {
226                 dev_info(dev, "HDMI power-on\n");
227                 /* turn HDMI power on */
228                 gpio_direction_output(S5PV310_GPL1(1), 1);
229                 /* tv_power_get(); */
230                 regulator_enable(res->ldo1);
231                 /* turn clocks on */
232                 clk_enable(res->hdmi);
233                 clk_enable(res->sclk_hdmi);
234                 /* power-on hdmi physical interface */
235                 SETREG(S5P_HDMI_PHY_CONTROL, 1, 1);
236         } else {
237                 dev_info(dev, "HDMI power-off\n");
238                 /* power-off hdmiphy */
239                 SETREG(S5P_HDMI_PHY_CONTROL, 0, 1);
240                 /* turn clocks off */
241                 clk_disable(res->sclk_hdmi);
242                 clk_disable(res->hdmi);
243                 /* turn HDMI power off */
244                 regulator_disable(res->ldo1);
245                 /* tv_power_put(); */
246                 gpio_direction_output(S5PV310_GPL1(1), 0);
247         }
248         dbg_plat_regs();
249         return 0;
250 }
251
252 static int hdmi_stream_setup(struct device *dev, int en)
253 {
254         struct hdmi_plat_resource *res = &hdmi_plat_resource;
255         /* NOTE: assumed HDMI power is on */
256         if (en) {
257                 dev_info(dev, "HDMI: stream on\n");
258                 /* hdmiphy clock is used for HDMI in streaming mode */
259                 clk_disable(res->sclk_hdmi);
260                 clk_set_parent(res->sclk_hdmi, res->sclk_hdmiphy);
261                 clk_enable(res->sclk_hdmi);
262                 /* SETREG(S5P_CLKSRC_TV, 0x00000001, 0x00000001); */
263         } else {
264                 dev_info(dev, "HDMI: stream off\n");
265                 /* pixel(vpll) clock is used for HDMI in config mode */
266                 clk_disable(res->sclk_hdmi);
267                 clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
268                 clk_enable(res->sclk_hdmi);
269                 /* SETREG(S5P_CLKSRC_TV, 0x00000000, 0x00000001); */
270         }
271         dbg_plat_regs();
272         return 0;
273 }
274
275 static struct hdmi_platform_data hdmi_pdata = {
276         .init = hdmi_init,
277         .deinit = hdmi_deinit,
278         .power_setup = hdmi_power_setup,
279         .stream_setup = hdmi_stream_setup,
280 };
281
282 /* MIXER */
283 static struct resource s5p_mixer_resources[] = {
284         [0] = {
285                 .start  = S5P_PA_MIXER,
286                 .end    = S5P_PA_MIXER + S5P_SZ_MIXER - 1,
287                 .flags  = IORESOURCE_MEM,
288                 .name   = "mxr"
289         },
290         [1] = {
291                 .start  = S5P_PA_VP,
292                 .end    = S5P_PA_VP + S5P_SZ_VP - 1,
293                 .flags  = IORESOURCE_MEM,
294                 .name   = "vp"
295         },
296         [2] = {
297                 .start  = IRQ_MIXER,
298                 .end    = IRQ_MIXER,
299                 .flags  = IORESOURCE_IRQ,
300                 .name   = "irq"
301         },
302 };
303
304 static struct mxr_platform_data mxr_pdata;
305
306 struct platform_device s5p_device_mixer = {
307         .name           = "s5p-mixer",
308         .id             = -1,
309         .num_resources  = ARRAY_SIZE(s5p_mixer_resources),
310         .resource       = s5p_mixer_resources,
311         .dev            = {
312                 .coherent_dma_mask = DMA_BIT_MASK(32),
313                 .dma_mask = &s5p_device_mixer.dev.coherent_dma_mask,
314                 .platform_data = &mxr_pdata,
315         }
316 };
317 EXPORT_SYMBOL(s5p_device_mixer);
318
319 static struct mxr_plat_resource {
320         struct clk *mixer;
321         struct clk *vp;
322         struct clk *sclk_mixer;
323         struct clk *sclk_hdmi;
324         struct clk *sclk_dac;
325         struct regulator *ldo1;
326 } mxr_plat_resource;
327
328 static void mxr_deinit(struct device *dev);
329
330 static int mxr_init(struct device *dev)
331 {
332         struct mxr_plat_resource *res = &mxr_plat_resource;
333         dev_info(dev, "platform Mixer Init\n");
334         res->mixer = clk_get(dev, "mixer");
335         if (IS_ERR_OR_NULL(res->mixer)) {
336                 dev_err(dev, "failed to get clock 'mixer'\n");
337                 goto fail;
338         }
339         res->vp = clk_get(dev, "vp");
340         if (IS_ERR_OR_NULL(res->vp)) {
341                 dev_err(dev, "failed to get clock 'vp'\n");
342                 goto fail;
343         }
344         res->sclk_mixer = clk_get(dev, "sclk_mixer");
345         if (IS_ERR_OR_NULL(res->sclk_mixer)) {
346                 dev_err(dev, "failed to get clock 'sclk_mixer'\n");
347                 goto fail;
348         }
349         res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
350         if (IS_ERR_OR_NULL(res->sclk_hdmi)) {
351                 dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
352                 goto fail;
353         }
354         res->sclk_dac = clk_get(dev, "sclk_dac");
355         if (IS_ERR_OR_NULL(res->sclk_dac)) {
356                 dev_err(dev, "failed to get clock 'sclk_dac'\n");
357                 goto fail;
358         }
359         res->ldo1 = regulator_get(dev, "vadc");
360         if (IS_ERR_OR_NULL(res->ldo1)) {
361                 dev_err(dev, "failed to get regulator 'ldo1'\n");
362                 goto fail;
363         }
364         /* regulator_enable(res->ldo1); */
365         tv_power_get();
366
367         /* XXX: fixed connetction between MIXER and HDMI */
368         clk_set_parent(res->sclk_mixer, res->sclk_hdmi);
369         dbg_plat_regs();
370         return 0;
371 fail:
372         dev_err(dev, "platform Mixer Init - failed\n");
373         mxr_deinit(dev);
374         return -ENODEV;
375 }
376
377 static void mxr_deinit(struct device *dev)
378 {
379         struct mxr_plat_resource *res = &mxr_plat_resource;
380         dev_info(dev, "platform Mixer denit\n");
381         if (!IS_ERR_OR_NULL(res->ldo1)) {
382                 tv_power_put();
383                 /* regulator_disable(res->ldo1); */
384                 regulator_put(res->ldo1);
385         }
386         if (!IS_ERR_OR_NULL(res->sclk_dac))
387                 clk_put(res->sclk_dac);
388         if (!IS_ERR_OR_NULL(res->sclk_hdmi))
389                 clk_put(res->sclk_hdmi);
390         if (!IS_ERR_OR_NULL(res->sclk_mixer))
391                 clk_put(res->sclk_mixer);
392         if (!IS_ERR_OR_NULL(res->vp))
393                 clk_put(res->vp);
394         if (!IS_ERR_OR_NULL(res->mixer))
395                 clk_put(res->mixer);
396         memset(res, 0, sizeof *res);
397 }
398
399 static int mxr_power_setup(struct device *dev, int en)
400 {
401         struct mxr_plat_resource *res = &mxr_plat_resource;
402         /* enable/disable clocks, power, and GPIOs */
403         if (en) {
404                 dev_info(dev, "MIXER power-on\n");
405                 /* turn MIXER power on */
406                 /* tv_power_get(); */
407                 regulator_enable(res->ldo1);
408                 /* turn clocks on */
409                 clk_enable(res->mixer);
410                 clk_enable(res->vp);
411                 clk_enable(res->sclk_mixer);
412                 /* HDMI CEC (no support) */
413                 /* SETREG(S5P_CLKGATE_IP_PERIR, ~0, 1 << 11); */
414                 /* keep here */
415         } else {
416                 dev_info(dev, "MIXER power-off\n");
417                 /* turn clocks off */
418                 clk_disable(res->sclk_mixer);
419                 clk_disable(res->vp);
420                 clk_disable(res->mixer);
421                 /* turn MIXER power off */
422                 regulator_disable(res->ldo1);
423                 /* tv_power_put(); */
424         }
425         dbg_plat_regs();
426         return 0;
427 }
428
429 static struct mxr_platform_output output[] = {
430         { .output_name = "S5P HDMI connector", .module_name = "s5p-hdmi" },
431 };
432
433 static struct mxr_platform_data mxr_pdata = {
434         .output = output,
435         .output_cnt = ARRAY_SIZE(output),
436         .init = mxr_init,
437         .deinit = mxr_deinit,
438         .power_setup = mxr_power_setup,
439 };
440
441 static void dbg_plat_regs(void)
442 {
443         DBGREG(S5P_CLKSRC_TV);
444         DBGREG(S5P_CLKSRC_MASK_TV);
445         DBGREG(S5P_CLKGATE_IP_TV);
446         DBGREG(S5P_CLKGATE_IP_PERIL); /* remove (unknown?) */
447         DBGREG(S5P_CLKGATE_IP_PERIL); /* I2C HDMI (I2C8) remove */
448         DBGREG(S5P_CLKGATE_IP_PERIR); /* HDMI CEC remove (no support) */
449         DBGREG(S5P_CLKGATE_BLOCK); /* keep here */
450         DBGREG(S5P_CLKSRC_TOP0);
451         DBGREG(S5P_CLKSRC_TOP1);
452         DBGREG(S5P_HDMI_PHY_CONTROL);
453         DBGREG(S5P_CMU_CLKSTOP_TV_SYS_PWR);
454         DBGREG(S5P_CMU_RESET_TV_SYS_PWR);
455         DBGREG(S5P_TV_SYS_PWR);
456         DBGREG(S5P_TV_CONFIGURATION);
457         DBGREG(S5P_TV_STATUS);
458         DBGREG(S5P_CLKDIV_TV);
459 }
460