Merge commit 'main-jb-2012.08.03-B4' into t114-0806
[linux-2.6.git] / arch / arm / mach-tegra / board-harmony-panel.c
1 /*
2  * arch/arm/mach-tegra/board-harmony-panel.c
3  *
4  * Copyright (c) 2010-2012, NVIDIA Corporation.
5  *
6  * This software is licensed under the terms of the GNU General Public
7  * License version 2, as published by the Free Software Foundation, and
8  * may be copied, distributed, and modified under those terms.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  */
16
17 #include <linux/delay.h>
18 #include <linux/resource.h>
19 #include <linux/platform_device.h>
20 #include <asm/mach-types.h>
21 #include <linux/nvhost.h>
22 #include <linux/gpio.h>
23 #include <linux/regulator/consumer.h>
24 #include <linux/pwm_backlight.h>
25
26 #include <mach/dc.h>
27 #include <mach/irqs.h>
28 #include <mach/iomap.h>
29 #include <linux/nvmap.h>
30 #include <mach/tegra_fb.h>
31 #include <mach/fb.h>
32 #include <mach/gpio-tegra.h>
33
34 #include "devices.h"
35 #include "gpio-names.h"
36 #include "board.h"
37 #include "tegra2_host1x_devices.h"
38
39 #define harmony_bl_enb          TEGRA_GPIO_PB5
40 #define harmony_lvds_shutdown   TEGRA_GPIO_PB2
41 #define harmony_en_vdd_pnl      TEGRA_GPIO_PC6
42 #define harmony_bl_vdd          TEGRA_GPIO_PW0
43 #define harmony_bl_pwm          TEGRA_GPIO_PB4
44 #define harmony_hdmi_hpd        TEGRA_GPIO_PN7
45
46 /* panel power on sequence timing */
47 #define harmony_pnl_to_lvds_ms  0
48 #define harmony_lvds_to_bl_ms   200
49
50 static int harmony_backlight_init(struct device *dev)
51 {
52         int ret;
53
54         ret = gpio_request(harmony_bl_enb, "backlight_enb");
55         if (ret < 0)
56                 return ret;
57
58         ret = gpio_direction_output(harmony_bl_enb, 1);
59         if (ret < 0)
60                 gpio_free(harmony_bl_enb);
61
62         return ret;
63 }
64
65 static void harmony_backlight_exit(struct device *dev)
66 {
67         gpio_set_value(harmony_bl_enb, 0);
68         gpio_free(harmony_bl_enb);
69         tegra_gpio_disable(harmony_bl_enb);
70 }
71
72 static int harmony_backlight_notify(struct device *unused, int brightness)
73 {
74         gpio_set_value(harmony_en_vdd_pnl, !!brightness);
75         gpio_set_value(harmony_lvds_shutdown, !!brightness);
76         gpio_set_value(harmony_bl_enb, !!brightness);
77         return brightness;
78 }
79
80 static int harmony_disp1_check_fb(struct device *dev, struct fb_info *info);
81
82 static struct platform_pwm_backlight_data harmony_backlight_data = {
83         .pwm_id         = 0,
84         .max_brightness = 255,
85         .dft_brightness = 224,
86         .pwm_period_ns  = 5000000,
87         .init           = harmony_backlight_init,
88         .exit           = harmony_backlight_exit,
89         .notify         = harmony_backlight_notify,
90         /* Only toggle backlight on fb blank notifications for disp1 */
91         .check_fb       = harmony_disp1_check_fb,
92 };
93
94 static struct platform_device harmony_backlight_device = {
95         .name   = "pwm-backlight",
96         .id     = -1,
97         .dev    = {
98                 .platform_data = &harmony_backlight_data,
99         },
100 };
101
102 static int harmony_panel_enable(void)
103 {
104         gpio_set_value(harmony_en_vdd_pnl, 1);
105         mdelay(harmony_pnl_to_lvds_ms);
106         gpio_set_value(harmony_lvds_shutdown, 1);
107         mdelay(harmony_lvds_to_bl_ms);
108         return 0;
109 }
110
111 static int harmony_panel_disable(void)
112 {
113         gpio_set_value(harmony_lvds_shutdown, 0);
114         gpio_set_value(harmony_en_vdd_pnl, 0);
115         return 0;
116 }
117
118 static int harmony_set_hdmi_power(bool enable)
119 {
120         static struct {
121                 struct regulator *regulator;
122                 const char *name;
123         } regs[] = {
124                 { .name = "avdd_hdmi" },
125                 { .name = "avdd_hdmi_pll" },
126         };
127         int i;
128
129         for (i = 0; i < ARRAY_SIZE(regs); i++) {
130                 if (!regs[i].regulator) {
131                         regs[i].regulator = regulator_get(NULL, regs[i].name);
132
133                         if (IS_ERR(regs[i].regulator)) {
134                                 int ret = PTR_ERR(regs[i].regulator);
135                                 regs[i].regulator = NULL;
136                                 return ret;
137                         }
138                 }
139
140                 if (enable)
141                         regulator_enable(regs[i].regulator);
142                 else
143                         regulator_disable(regs[i].regulator);
144         }
145
146         return 0;
147 }
148
149 static int harmony_hdmi_enable(void)
150 {
151         return harmony_set_hdmi_power(true);
152 }
153
154 static int harmony_hdmi_disable(void)
155 {
156         return harmony_set_hdmi_power(false);
157 }
158
159 static struct resource harmony_disp1_resources[] = {
160         {
161                 .name = "irq",
162                 .start  = INT_DISPLAY_GENERAL,
163                 .end    = INT_DISPLAY_GENERAL,
164                 .flags  = IORESOURCE_IRQ,
165         },
166         {
167                 .name = "regs",
168                 .start  = TEGRA_DISPLAY_BASE,
169                 .end    = TEGRA_DISPLAY_BASE + TEGRA_DISPLAY_SIZE-1,
170                 .flags  = IORESOURCE_MEM,
171         },
172         {
173                 .name = "fbmem",
174                 .flags  = IORESOURCE_MEM,
175         },
176 };
177
178 static struct resource harmony_disp2_resources[] = {
179         {
180                 .name   = "irq",
181                 .start  = INT_DISPLAY_B_GENERAL,
182                 .end    = INT_DISPLAY_B_GENERAL,
183                 .flags  = IORESOURCE_IRQ,
184         },
185         {
186                 .name   = "regs",
187                 .start  = TEGRA_DISPLAY2_BASE,
188                 .end    = TEGRA_DISPLAY2_BASE + TEGRA_DISPLAY2_SIZE - 1,
189                 .flags  = IORESOURCE_MEM,
190         },
191         {
192                 .name   = "fbmem",
193                 .flags  = IORESOURCE_MEM,
194         },
195         {
196                 .name   = "hdmi_regs",
197                 .start  = TEGRA_HDMI_BASE,
198                 .end    = TEGRA_HDMI_BASE + TEGRA_HDMI_SIZE - 1,
199                 .flags  = IORESOURCE_MEM,
200         },
201 };
202
203 static struct tegra_dc_mode harmony_panel_modes[] = {
204         {
205                 .pclk = 42430000,
206                 .h_ref_to_sync = 4,
207                 .v_ref_to_sync = 2,
208                 .h_sync_width = 136,
209                 .v_sync_width = 4,
210                 .h_back_porch = 138,
211                 .v_back_porch = 21,
212                 .h_active = 1024,
213                 .v_active = 600,
214                 .h_front_porch = 34,
215                 .v_front_porch = 4,
216         },
217 };
218
219 static struct tegra_fb_data harmony_fb_data = {
220         .win            = 0,
221         .xres           = 1024,
222         .yres           = 600,
223         .bits_per_pixel = 32,
224         .flags          = TEGRA_FB_FLIP_ON_PROBE,
225 };
226
227 static struct tegra_fb_data harmony_hdmi_fb_data = {
228         .win            = 0,
229         .xres           = 1280,
230         .yres           = 720,
231         .bits_per_pixel = 16,
232 };
233
234 static struct tegra_dc_out harmony_disp1_out = {
235         .type           = TEGRA_DC_OUT_RGB,
236
237         .align          = TEGRA_DC_ALIGN_MSB,
238         .order          = TEGRA_DC_ORDER_RED_BLUE,
239         .depth          = 18,
240         .dither         = TEGRA_DC_ORDERED_DITHER,
241
242         .modes          = harmony_panel_modes,
243         .n_modes        = ARRAY_SIZE(harmony_panel_modes),
244
245         .enable         = harmony_panel_enable,
246         .disable        = harmony_panel_disable,
247 };
248
249 static struct tegra_dc_out harmony_disp2_out = {
250         .type           = TEGRA_DC_OUT_HDMI,
251         .flags          = TEGRA_DC_OUT_HOTPLUG_HIGH,
252
253         .dcc_bus        = 1,
254         .hotplug_gpio   = harmony_hdmi_hpd,
255
256         .align          = TEGRA_DC_ALIGN_MSB,
257         .order          = TEGRA_DC_ORDER_RED_BLUE,
258
259         .enable         = harmony_hdmi_enable,
260         .disable        = harmony_hdmi_disable,
261 };
262
263 static struct tegra_dc_platform_data harmony_disp1_pdata = {
264         .flags          = TEGRA_DC_FLAG_ENABLED,
265         .default_out    = &harmony_disp1_out,
266         .fb             = &harmony_fb_data,
267 };
268
269 static struct tegra_dc_platform_data harmony_disp2_pdata = {
270         .flags          = 0,
271         .default_out    = &harmony_disp2_out,
272         .fb             = &harmony_hdmi_fb_data,
273 };
274
275 static struct nvhost_device harmony_disp1_device = {
276         .name           = "tegradc",
277         .id             = 0,
278         .resource       = harmony_disp1_resources,
279         .num_resources  = ARRAY_SIZE(harmony_disp1_resources),
280         .dev = {
281                 .platform_data = &harmony_disp1_pdata,
282         },
283 };
284
285 static int harmony_disp1_check_fb(struct device *dev, struct fb_info *info)
286 {
287         return info->device == &harmony_disp1_device.dev;
288 }
289
290 static struct nvhost_device harmony_disp2_device = {
291         .name           = "tegradc",
292         .id             = 1,
293         .resource       = harmony_disp2_resources,
294         .num_resources  = ARRAY_SIZE(harmony_disp2_resources),
295         .dev = {
296                 .platform_data = &harmony_disp2_pdata,
297         },
298 };
299
300 #if defined(CONFIG_TEGRA_NVMAP)
301 static struct nvmap_platform_carveout harmony_carveouts[] = {
302         [0] = NVMAP_HEAP_CARVEOUT_IRAM_INIT,
303         [1] = {
304                 .name           = "generic-0",
305                 .usage_mask     = NVMAP_HEAP_CARVEOUT_GENERIC,
306                 .buddy_size     = SZ_32K,
307         },
308 };
309
310 static struct nvmap_platform_data harmony_nvmap_data = {
311         .carveouts      = harmony_carveouts,
312         .nr_carveouts   = ARRAY_SIZE(harmony_carveouts),
313 };
314
315 static struct platform_device harmony_nvmap_device = {
316         .name   = "tegra-nvmap",
317         .id     = -1,
318         .dev    = {
319                 .platform_data = &harmony_nvmap_data,
320         },
321 };
322 #endif
323
324 static struct platform_device *harmony_gfx_devices[] __initdata = {
325 #if defined(CONFIG_TEGRA_NVMAP)
326         &harmony_nvmap_device,
327 #endif
328         &tegra_pwfm0_device,
329         &harmony_backlight_device,
330 };
331
332 int __init harmony_panel_init(void)
333 {
334         int err;
335         struct resource *res;
336
337         gpio_request(harmony_en_vdd_pnl, "en_vdd_pnl");
338         gpio_direction_output(harmony_en_vdd_pnl, 1);
339
340         gpio_request(harmony_bl_vdd, "bl_vdd");
341         gpio_direction_output(harmony_bl_vdd, 1);
342
343         gpio_request(harmony_lvds_shutdown, "lvds_shdn");
344         gpio_direction_output(harmony_lvds_shutdown, 1);
345
346         gpio_request(harmony_hdmi_hpd, "hdmi_hpd");
347         gpio_direction_input(harmony_hdmi_hpd);
348
349 #if defined(CONFIG_TEGRA_NVMAP)
350         harmony_carveouts[1].base = tegra_carveout_start;
351         harmony_carveouts[1].size = tegra_carveout_size;
352 #endif
353
354 #ifdef CONFIG_TEGRA_GRHOST
355         err = tegra2_register_host1x_devices();
356         if (err)
357                 return err;
358 #endif
359
360         err = platform_add_devices(harmony_gfx_devices,
361                                    ARRAY_SIZE(harmony_gfx_devices));
362         if (err)
363                 return err;
364
365         res = nvhost_get_resource_byname(&harmony_disp1_device,
366                 IORESOURCE_MEM, "fbmem");
367         if (res) {
368                 res->start = tegra_fb_start;
369                 res->end = tegra_fb_start + tegra_fb_size - 1;
370         }
371
372         res = nvhost_get_resource_byname(&harmony_disp2_device,
373                 IORESOURCE_MEM, "fbmem");
374         if (res) {
375                 res->start = tegra_fb2_start;
376                 res->end = tegra_fb2_start + tegra_fb2_size - 1;
377         }
378
379         /* Copy the bootloader fb to the fb. */
380         if (tegra_bootloader_fb_start)
381                 tegra_move_framebuffer(tegra_fb_start,
382                         tegra_bootloader_fb_start,
383                         min(tegra_fb_size, tegra_bootloader_fb_size));
384         err = nvhost_device_register(&harmony_disp1_device);
385         if (err)
386                 return err;
387
388         err = nvhost_device_register(&harmony_disp2_device);
389         if (err)
390                 return err;
391
392         return 0;
393 }
394