diff options
Diffstat (limited to 'arch/arm/mach-tegra/board-harmony-panel.c')
-rw-r--r-- | arch/arm/mach-tegra/board-harmony-panel.c | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/board-harmony-panel.c b/arch/arm/mach-tegra/board-harmony-panel.c new file mode 100644 index 00000000000..c24039652bc --- /dev/null +++ b/arch/arm/mach-tegra/board-harmony-panel.c | |||
@@ -0,0 +1,398 @@ | |||
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 <mach/nvmap.h> | ||
30 | #include <mach/tegra_fb.h> | ||
31 | #include <mach/fb.h> | ||
32 | |||
33 | #include "devices.h" | ||
34 | #include "gpio-names.h" | ||
35 | #include "board.h" | ||
36 | |||
37 | #define harmony_bl_enb TEGRA_GPIO_PB5 | ||
38 | #define harmony_lvds_shutdown TEGRA_GPIO_PB2 | ||
39 | #define harmony_en_vdd_pnl TEGRA_GPIO_PC6 | ||
40 | #define harmony_bl_vdd TEGRA_GPIO_PW0 | ||
41 | #define harmony_bl_pwm TEGRA_GPIO_PB4 | ||
42 | #define harmony_hdmi_hpd TEGRA_GPIO_PN7 | ||
43 | |||
44 | /* panel power on sequence timing */ | ||
45 | #define harmony_pnl_to_lvds_ms 0 | ||
46 | #define harmony_lvds_to_bl_ms 200 | ||
47 | |||
48 | static int harmony_backlight_init(struct device *dev) | ||
49 | { | ||
50 | int ret; | ||
51 | |||
52 | ret = gpio_request(harmony_bl_enb, "backlight_enb"); | ||
53 | if (ret < 0) | ||
54 | return ret; | ||
55 | |||
56 | ret = gpio_direction_output(harmony_bl_enb, 1); | ||
57 | if (ret < 0) | ||
58 | gpio_free(harmony_bl_enb); | ||
59 | else | ||
60 | tegra_gpio_enable(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 | tegra_gpio_enable(harmony_en_vdd_pnl); | ||
340 | |||
341 | gpio_request(harmony_bl_vdd, "bl_vdd"); | ||
342 | gpio_direction_output(harmony_bl_vdd, 1); | ||
343 | tegra_gpio_enable(harmony_bl_vdd); | ||
344 | |||
345 | gpio_request(harmony_lvds_shutdown, "lvds_shdn"); | ||
346 | gpio_direction_output(harmony_lvds_shutdown, 1); | ||
347 | tegra_gpio_enable(harmony_lvds_shutdown); | ||
348 | |||
349 | gpio_request(harmony_hdmi_hpd, "hdmi_hpd"); | ||
350 | gpio_direction_input(harmony_hdmi_hpd); | ||
351 | tegra_gpio_enable(harmony_hdmi_hpd); | ||
352 | |||
353 | #if defined(CONFIG_TEGRA_NVMAP) | ||
354 | harmony_carveouts[1].base = tegra_carveout_start; | ||
355 | harmony_carveouts[1].size = tegra_carveout_size; | ||
356 | #endif | ||
357 | |||
358 | #ifdef CONFIG_TEGRA_GRHOST | ||
359 | err = nvhost_device_register(&tegra_grhost_device); | ||
360 | if (err) | ||
361 | return err; | ||
362 | #endif | ||
363 | |||
364 | err = platform_add_devices(harmony_gfx_devices, | ||
365 | ARRAY_SIZE(harmony_gfx_devices)); | ||
366 | if (err) | ||
367 | return err; | ||
368 | |||
369 | res = nvhost_get_resource_byname(&harmony_disp1_device, | ||
370 | IORESOURCE_MEM, "fbmem"); | ||
371 | if (res) { | ||
372 | res->start = tegra_fb_start; | ||
373 | res->end = tegra_fb_start + tegra_fb_size - 1; | ||
374 | } | ||
375 | |||
376 | res = nvhost_get_resource_byname(&harmony_disp2_device, | ||
377 | IORESOURCE_MEM, "fbmem"); | ||
378 | if (res) { | ||
379 | res->start = tegra_fb2_start; | ||
380 | res->end = tegra_fb2_start + tegra_fb2_size - 1; | ||
381 | } | ||
382 | |||
383 | /* Copy the bootloader fb to the fb. */ | ||
384 | if (tegra_bootloader_fb_start) | ||
385 | tegra_move_framebuffer(tegra_fb_start, | ||
386 | tegra_bootloader_fb_start, | ||
387 | min(tegra_fb_size, tegra_bootloader_fb_size)); | ||
388 | err = nvhost_device_register(&harmony_disp1_device); | ||
389 | if (err) | ||
390 | return err; | ||
391 | |||
392 | err = nvhost_device_register(&harmony_disp2_device); | ||
393 | if (err) | ||
394 | return err; | ||
395 | |||
396 | return 0; | ||
397 | } | ||
398 | |||