aboutsummaryrefslogblamecommitdiffstats
path: root/arch/arm/mach-tegra/board-harmony-panel.c
blob: c24039652bc53522d45407fa6c012d5bb588ae6e (plain) (tree)













































































































































































































































































































































































































                                                                              
/*
 * arch/arm/mach-tegra/board-harmony-panel.c
 *
 * Copyright (c) 2010-2012, NVIDIA Corporation.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include <linux/delay.h>
#include <linux/resource.h>
#include <linux/platform_device.h>
#include <asm/mach-types.h>
#include <linux/nvhost.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/pwm_backlight.h>

#include <mach/dc.h>
#include <mach/irqs.h>
#include <mach/iomap.h>
#include <mach/nvmap.h>
#include <mach/tegra_fb.h>
#include <mach/fb.h>

#include "devices.h"
#include "gpio-names.h"
#include "board.h"

#define harmony_bl_enb		TEGRA_GPIO_PB5
#define harmony_lvds_shutdown	TEGRA_GPIO_PB2
#define harmony_en_vdd_pnl	TEGRA_GPIO_PC6
#define harmony_bl_vdd		TEGRA_GPIO_PW0
#define harmony_bl_pwm		TEGRA_GPIO_PB4
#define harmony_hdmi_hpd	TEGRA_GPIO_PN7

/* panel power on sequence timing */
#define harmony_pnl_to_lvds_ms	0
#define harmony_lvds_to_bl_ms	200

static int harmony_backlight_init(struct device *dev)
{
	int ret;

	ret = gpio_request(harmony_bl_enb, "backlight_enb");
	if (ret < 0)
		return ret;

	ret = gpio_direction_output(harmony_bl_enb, 1);
	if (ret < 0)
		gpio_free(harmony_bl_enb);
	else
		tegra_gpio_enable(harmony_bl_enb);

	return ret;
}

static void harmony_backlight_exit(struct device *dev)
{
	gpio_set_value(harmony_bl_enb, 0);
	gpio_free(harmony_bl_enb);
	tegra_gpio_disable(harmony_bl_enb);
}

static int harmony_backlight_notify(struct device *unused, int brightness)
{
	gpio_set_value(harmony_en_vdd_pnl, !!brightness);
	gpio_set_value(harmony_lvds_shutdown, !!brightness);
	gpio_set_value(harmony_bl_enb, !!brightness);
	return brightness;
}

static int harmony_disp1_check_fb(struct device *dev, struct fb_info *info);

static struct platform_pwm_backlight_data harmony_backlight_data = {
	.pwm_id		= 0,
	.max_brightness	= 255,
	.dft_brightness	= 224,
	.pwm_period_ns	= 5000000,
	.init		= harmony_backlight_init,
	.exit		= harmony_backlight_exit,
	.notify		= harmony_backlight_notify,
	/* Only toggle backlight on fb blank notifications for disp1 */
	.check_fb	= harmony_disp1_check_fb,
};

static struct platform_device harmony_backlight_device = {
	.name	= "pwm-backlight",
	.id	= -1,
	.dev	= {
		.platform_data = &harmony_backlight_data,
	},
};

static int harmony_panel_enable(void)
{
	gpio_set_value(harmony_en_vdd_pnl, 1);
	mdelay(harmony_pnl_to_lvds_ms);
	gpio_set_value(harmony_lvds_shutdown, 1);
	mdelay(harmony_lvds_to_bl_ms);
	return 0;
}

static int harmony_panel_disable(void)
{
	gpio_set_value(harmony_lvds_shutdown, 0);
	gpio_set_value(harmony_en_vdd_pnl, 0);
	return 0;
}

static int harmony_set_hdmi_power(bool enable)
{
	static struct {
		struct regulator *regulator;
		const char *name;
	} regs[] = {
		{ .name = "avdd_hdmi" },
		{ .name = "avdd_hdmi_pll" },
	};
	int i;

	for (i = 0; i < ARRAY_SIZE(regs); i++) {
		if (!regs[i].regulator) {
			regs[i].regulator = regulator_get(NULL, regs[i].name);

			if (IS_ERR(regs[i].regulator)) {
				int ret = PTR_ERR(regs[i].regulator);
				regs[i].regulator = NULL;
				return ret;
			}
		}

		if (enable)
			regulator_enable(regs[i].regulator);
		else
			regulator_disable(regs[i].regulator);
	}

	return 0;
}

static int harmony_hdmi_enable(void)
{
	return harmony_set_hdmi_power(true);
}

static int harmony_hdmi_disable(void)
{
	return harmony_set_hdmi_power(false);
}

static struct resource harmony_disp1_resources[] = {
	{
		.name = "irq",
		.start  = INT_DISPLAY_GENERAL,
		.end    = INT_DISPLAY_GENERAL,
		.flags  = IORESOURCE_IRQ,
	},
	{
		.name = "regs",
		.start	= TEGRA_DISPLAY_BASE,
		.end	= TEGRA_DISPLAY_BASE + TEGRA_DISPLAY_SIZE-1,
		.flags	= IORESOURCE_MEM,
	},
	{
		.name = "fbmem",
		.flags	= IORESOURCE_MEM,
	},
};

static struct resource harmony_disp2_resources[] = {
	{
		.name	= "irq",
		.start	= INT_DISPLAY_B_GENERAL,
		.end	= INT_DISPLAY_B_GENERAL,
		.flags	= IORESOURCE_IRQ,
	},
	{
		.name	= "regs",
		.start	= TEGRA_DISPLAY2_BASE,
		.end	= TEGRA_DISPLAY2_BASE + TEGRA_DISPLAY2_SIZE - 1,
		.flags	= IORESOURCE_MEM,
	},
	{
		.name	= "fbmem",
		.flags	= IORESOURCE_MEM,
	},
	{
		.name	= "hdmi_regs",
		.start	= TEGRA_HDMI_BASE,
		.end	= TEGRA_HDMI_BASE + TEGRA_HDMI_SIZE - 1,
		.flags	= IORESOURCE_MEM,
	},
};

static struct tegra_dc_mode harmony_panel_modes[] = {
	{
		.pclk = 42430000,
		.h_ref_to_sync = 4,
		.v_ref_to_sync = 2,
		.h_sync_width = 136,
		.v_sync_width = 4,
		.h_back_porch = 138,
		.v_back_porch = 21,
		.h_active = 1024,
		.v_active = 600,
		.h_front_porch = 34,
		.v_front_porch = 4,
	},
};

static struct tegra_fb_data harmony_fb_data = {
	.win		= 0,
	.xres		= 1024,
	.yres		= 600,
	.bits_per_pixel	= 32,
	.flags		= TEGRA_FB_FLIP_ON_PROBE,
};

static struct tegra_fb_data harmony_hdmi_fb_data = {
	.win		= 0,
	.xres		= 1280,
	.yres		= 720,
	.bits_per_pixel	= 16,
};

static struct tegra_dc_out harmony_disp1_out = {
	.type		= TEGRA_DC_OUT_RGB,

	.align		= TEGRA_DC_ALIGN_MSB,
	.order		= TEGRA_DC_ORDER_RED_BLUE,
	.depth		= 18,
	.dither		= TEGRA_DC_ORDERED_DITHER,

	.modes		= harmony_panel_modes,
	.n_modes	= ARRAY_SIZE(harmony_panel_modes),

	.enable		= harmony_panel_enable,
	.disable	= harmony_panel_disable,
};

static struct tegra_dc_out harmony_disp2_out = {
	.type		= TEGRA_DC_OUT_HDMI,
	.flags		= TEGRA_DC_OUT_HOTPLUG_HIGH,

	.dcc_bus	= 1,
	.hotplug_gpio	= harmony_hdmi_hpd,

	.align		= TEGRA_DC_ALIGN_MSB,
	.order		= TEGRA_DC_ORDER_RED_BLUE,

	.enable		= harmony_hdmi_enable,
	.disable	= harmony_hdmi_disable,
};

static struct tegra_dc_platform_data harmony_disp1_pdata = {
	.flags		= TEGRA_DC_FLAG_ENABLED,
	.default_out	= &harmony_disp1_out,
	.fb		= &harmony_fb_data,
};

static struct tegra_dc_platform_data harmony_disp2_pdata = {
	.flags		= 0,
	.default_out	= &harmony_disp2_out,
	.fb		= &harmony_hdmi_fb_data,
};

static struct nvhost_device harmony_disp1_device = {
	.name		= "tegradc",
	.id		= 0,
	.resource	= harmony_disp1_resources,
	.num_resources	= ARRAY_SIZE(harmony_disp1_resources),
	.dev = {
		.platform_data = &harmony_disp1_pdata,
	},
};

static int harmony_disp1_check_fb(struct device *dev, struct fb_info *info)
{
	return info->device == &harmony_disp1_device.dev;
}

static struct nvhost_device harmony_disp2_device = {
	.name		= "tegradc",
	.id		= 1,
	.resource	= harmony_disp2_resources,
	.num_resources	= ARRAY_SIZE(harmony_disp2_resources),
	.dev = {
		.platform_data = &harmony_disp2_pdata,
	},
};

#if defined(CONFIG_TEGRA_NVMAP)
static struct nvmap_platform_carveout harmony_carveouts[] = {
	[0] = NVMAP_HEAP_CARVEOUT_IRAM_INIT,
	[1] = {
		.name		= "generic-0",
		.usage_mask	= NVMAP_HEAP_CARVEOUT_GENERIC,
		.buddy_size	= SZ_32K,
	},
};

static struct nvmap_platform_data harmony_nvmap_data = {
	.carveouts	= harmony_carveouts,
	.nr_carveouts	= ARRAY_SIZE(harmony_carveouts),
};

static struct platform_device harmony_nvmap_device = {
	.name	= "tegra-nvmap",
	.id	= -1,
	.dev	= {
		.platform_data = &harmony_nvmap_data,
	},
};
#endif

static struct platform_device *harmony_gfx_devices[] __initdata = {
#if defined(CONFIG_TEGRA_NVMAP)
	&harmony_nvmap_device,
#endif
	&tegra_pwfm0_device,
	&harmony_backlight_device,
};

int __init harmony_panel_init(void)
{
	int err;
	struct resource *res;

	gpio_request(harmony_en_vdd_pnl, "en_vdd_pnl");
	gpio_direction_output(harmony_en_vdd_pnl, 1);
	tegra_gpio_enable(harmony_en_vdd_pnl);

	gpio_request(harmony_bl_vdd, "bl_vdd");
	gpio_direction_output(harmony_bl_vdd, 1);
	tegra_gpio_enable(harmony_bl_vdd);

	gpio_request(harmony_lvds_shutdown, "lvds_shdn");
	gpio_direction_output(harmony_lvds_shutdown, 1);
	tegra_gpio_enable(harmony_lvds_shutdown);

	gpio_request(harmony_hdmi_hpd, "hdmi_hpd");
	gpio_direction_input(harmony_hdmi_hpd);
	tegra_gpio_enable(harmony_hdmi_hpd);

#if defined(CONFIG_TEGRA_NVMAP)
	harmony_carveouts[1].base = tegra_carveout_start;
	harmony_carveouts[1].size = tegra_carveout_size;
#endif

#ifdef CONFIG_TEGRA_GRHOST
	err = nvhost_device_register(&tegra_grhost_device);
	if (err)
		return err;
#endif

	err = platform_add_devices(harmony_gfx_devices,
				   ARRAY_SIZE(harmony_gfx_devices));
	if (err)
		return err;

	res = nvhost_get_resource_byname(&harmony_disp1_device,
		IORESOURCE_MEM, "fbmem");
	if (res) {
		res->start = tegra_fb_start;
		res->end = tegra_fb_start + tegra_fb_size - 1;
	}

	res = nvhost_get_resource_byname(&harmony_disp2_device,
		IORESOURCE_MEM, "fbmem");
	if (res) {
		res->start = tegra_fb2_start;
		res->end = tegra_fb2_start + tegra_fb2_size - 1;
	}

	/* Copy the bootloader fb to the fb. */
	if (tegra_bootloader_fb_start)
		tegra_move_framebuffer(tegra_fb_start,
			tegra_bootloader_fb_start,
			min(tegra_fb_size, tegra_bootloader_fb_size));
	err = nvhost_device_register(&harmony_disp1_device);
	if (err)
		return err;

	err = nvhost_device_register(&harmony_disp2_device);
	if (err)
		return err;

	return 0;
}