diff options
-rw-r--r-- | arch/arm/mach-tegra/board-p1852-panel.c | 390 | ||||
-rw-r--r-- | arch/arm/mach-tegra/board-p1852.c | 21 | ||||
-rw-r--r-- | arch/arm/mach-tegra/board-p1852.h | 11 | ||||
-rw-r--r-- | arch/arm/mach-tegra/devices.c | 37 | ||||
-rw-r--r-- | arch/arm/mach-tegra/devices.h | 1 | ||||
-rw-r--r-- | drivers/video/tegra/dc/hdmi.c | 65 |
6 files changed, 496 insertions, 29 deletions
diff --git a/arch/arm/mach-tegra/board-p1852-panel.c b/arch/arm/mach-tegra/board-p1852-panel.c index 4e86476cdd2c..970133d8a78e 100644 --- a/arch/arm/mach-tegra/board-p1852-panel.c +++ b/arch/arm/mach-tegra/board-p1852-panel.c @@ -29,7 +29,18 @@ #include "board.h" #include "devices.h" #include "tegra3_host1x_devices.h" +#include "board-p1852.h" +#include "gpio-names.h" +#define P1852_LVDS_ENA1 TEGRA_GPIO_PV0 +#define P1852_LVDS_ENA2 TEGRA_GPIO_PV1 +#define P1852_HDMI_HPD TEGRA_GPIO_PN7 +#define P1852_HDMI_RGB TEGRA_GPIO_PW1 +#define P1852_LVDS_SER1_ADDR 0xd +#define P1852_LVDS_SER2_ADDR 0xc + + +/* RGB panel requires no special enable/disable */ static int p1852_panel_enable(void) { return 0; @@ -40,6 +51,186 @@ static int p1852_panel_disable(void) return 0; } +/* enable primary LVDS */ +static int p1852_lvds_enable(void) +{ + struct i2c_adapter *adapter; + struct i2c_board_info info = {{0}}; + static struct i2c_client *client; + struct i2c_msg msg[2]; + u8 cmd_buf[] = {0x4, 0}; + int status=-1; + + /* Turn on serializer chip */ + gpio_set_value(P1852_LVDS_ENA1, 1); + + /* Program the serializer */ + adapter = i2c_get_adapter(3); + if (!adapter) + pr_warning("%s: adapter is null\n", __func__); + else { + info.addr = P1852_LVDS_SER1_ADDR; + if (!client) + client = i2c_new_device(adapter, &info); + i2c_put_adapter(adapter); + if (!client) + pr_warning("%s: client is null\n", __func__); + else { + msg[0].addr = P1852_LVDS_SER1_ADDR; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &cmd_buf[0]; + + status = i2c_transfer(client->adapter, msg, 1); + /* ignore first write status */ + + msg[0].addr = P1852_LVDS_SER1_ADDR; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &cmd_buf[0]; + + msg[1].addr = P1852_LVDS_SER1_ADDR; + msg[1].flags = I2C_M_RD; + msg[1].len = 1; + msg[1].buf = &cmd_buf[1]; + + status = i2c_transfer(client->adapter, msg, 2); + if (status < 0) { + pr_warning("%s: i2c failed, addr=0x%x, reg=%d, ret=%d\n", + __func__, P1852_LVDS_SER1_ADDR, cmd_buf[0], status); + } + else { + cmd_buf[1] |= (1 << 2); + cmd_buf[1] |= (1 << 3); + + msg[0].addr = P1852_LVDS_SER1_ADDR; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = &cmd_buf[0]; + + status = i2c_transfer(client->adapter, msg, 1); + if (status < 0) { + pr_warning("%s: i2c err, addr=0x%x, reg=%d, ret=%d\n", + __func__, P1852_LVDS_SER1_ADDR, cmd_buf[0], status); + } + } + } + } + return (status<0 ? status : 0); +} + +/* Disable primary LVDS */ +static int p1852_lvds_disable(void) +{ + /* Turn off serializer chip */ + gpio_set_value(P1852_LVDS_ENA1, 0); + + return 0; +} + +/* Enable secondary LVDS */ +static int p1852_lvds2_enable(void) +{ + struct i2c_adapter *adapter; + struct i2c_board_info info = {{0}}; + static struct i2c_client *client; + struct i2c_msg msg[2]; + u8 cmd_buf[] = {0x4, 0}; + int status=-1; + + /* Enable HDMI HPD */ + /* need nothing here */ + + /* Turn on HDMI-RGB converter */ + gpio_set_value(P1852_HDMI_RGB, 1); + + /* Turn on serializer chip */ + gpio_set_value(P1852_LVDS_ENA2, 1); + + /* Program the serializer */ + adapter = i2c_get_adapter(3); + if (!adapter) + pr_warning("%s: adapter is null\n", __func__); + else { + info.addr = P1852_LVDS_SER2_ADDR; + if (!client) + client = i2c_new_device(adapter, &info); + i2c_put_adapter(adapter); + if (!client) + pr_warning("%s: client is null\n", __func__); + else { + msg[0].addr = P1852_LVDS_SER2_ADDR; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &cmd_buf[0]; + + status = i2c_transfer(client->adapter, msg, 1); + /* ignore first write status */ + + msg[0].addr = P1852_LVDS_SER2_ADDR; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &cmd_buf[0]; + + msg[1].addr = P1852_LVDS_SER2_ADDR; + msg[1].flags = I2C_M_RD; + msg[1].len = 1; + msg[1].buf = &cmd_buf[1]; + + status = i2c_transfer(client->adapter, msg, 2); + if (status < 0) { + pr_warning("%s: i2c failed, addr=0x%x, reg=%d, ret=%d\n", + __func__, P1852_LVDS_SER2_ADDR, cmd_buf[0], status); + } + else { + cmd_buf[1] |= (1 << 2); + cmd_buf[1] |= (1 << 3); + + msg[0].addr = P1852_LVDS_SER2_ADDR; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = &cmd_buf[0]; + + status = i2c_transfer(client->adapter, msg, 1); + if (status < 0) { + pr_warning("%s: i2c err, addr=0x%x, reg=%d, ret=%d\n", + __func__, P1852_LVDS_SER2_ADDR, cmd_buf[0], status); + } + } + } + } + return (status<0 ? status : 0); +} + +/* Disable secondary LVDS */ +static int p1852_lvds2_disable(void) +{ + /* Turn off serializer chip */ + gpio_set_value(P1852_LVDS_ENA2, 0); + + /* Turn off HDMI-RGB converter */ + gpio_set_value(P1852_HDMI_RGB, 0); + + /* Turn off HDMI */ + /* need nothing here */ + + return 0; +} + +/* Enable secondary HDMI */ +static int p1852_hdmi_enable(void) +{ + /* need nothing here */ + return 0; +} + +/* Disable secondary HDMI */ +static int p1852_hdmi_disable(void) +{ + /* need nothing here */ + return 0; +} + #ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT static struct tegra_dc_mode p1852_panel_modes[] = { @@ -123,9 +314,42 @@ static struct tegra_fb_data p1852_fb_data = { #endif +/* Mode data for secondary LVDS out */ +static struct tegra_dc_mode p1852_hdmi_lvds_modes[] = { + { + /* 800x480@60 */ + .pclk = 33260000, + .h_ref_to_sync = 1, + .v_ref_to_sync = 1, + .h_sync_width = 64, + .v_sync_width = 3, + .h_back_porch = 128, + .v_back_porch = 38, + .h_front_porch = 64, + .v_front_porch = 4, + .h_active = 800, + .v_active = 480, + }, +}; + +static struct tegra_fb_data p1852_hdmi_fb_data = { + .win = 0, + .xres = 800, + .yres = 480, + .bits_per_pixel = 32, + .flags = TEGRA_FB_FLIP_ON_PROBE, +}; + +/* Start of DC_OUT data + * disp1 = Primary RGB out + * ser1 = Primary LVDS out + * ser2 = Secondary LVDS out + * hdmi = Secondary HDMI out + */ static struct tegra_dc_out p1852_disp1_out = { .align = TEGRA_DC_ALIGN_MSB, .order = TEGRA_DC_ORDER_RED_BLUE, + .parent_clk = "pll_d_out0", .type = TEGRA_DC_OUT_RGB, .modes = p1852_panel_modes, .n_modes = ARRAY_SIZE(p1852_panel_modes), @@ -133,6 +357,51 @@ static struct tegra_dc_out p1852_disp1_out = { .disable = p1852_panel_disable, }; +static struct tegra_dc_out p1852_ser1_out = { + .align = TEGRA_DC_ALIGN_MSB, + .order = TEGRA_DC_ORDER_RED_BLUE, + .parent_clk = "pll_d_out0", + .type = TEGRA_DC_OUT_RGB, + .modes = p1852_panel_modes, + .n_modes = ARRAY_SIZE(p1852_panel_modes), + .enable = p1852_lvds_enable, + .disable = p1852_lvds_disable, +}; + +static struct tegra_dc_out p1852_ser2_out = { + .align = TEGRA_DC_ALIGN_MSB, + .order = TEGRA_DC_ORDER_RED_BLUE, + .parent_clk = "pll_d2_out0", + .type = TEGRA_DC_OUT_HDMI, + .flags = TEGRA_DC_OUT_HOTPLUG_LOW | + TEGRA_DC_OUT_NVHDCP_POLICY_ON_DEMAND, + .max_pixclock = KHZ2PICOS(148500), + .hotplug_gpio = P1852_HDMI_HPD, + .modes = p1852_hdmi_lvds_modes, + .n_modes = ARRAY_SIZE(p1852_hdmi_lvds_modes), + .enable = p1852_lvds2_enable, + .disable = p1852_lvds2_disable, + .dcc_bus = 3, +}; + +static struct tegra_dc_out p1852_hdmi_out = { + .align = TEGRA_DC_ALIGN_MSB, + .order = TEGRA_DC_ORDER_RED_BLUE, + .parent_clk = "pll_d2_out0", + .type = TEGRA_DC_OUT_HDMI, + .flags = TEGRA_DC_OUT_HOTPLUG_LOW | + TEGRA_DC_OUT_NVHDCP_POLICY_ON_DEMAND, + .max_pixclock = KHZ2PICOS(148500), + .hotplug_gpio = P1852_HDMI_HPD, + .enable = p1852_hdmi_enable, + .disable = p1852_hdmi_disable, + + .dcc_bus = 1, +}; + +/* End of DC_OUT data */ + +/* Start of platform data */ static struct tegra_dc_platform_data p1852_disp1_pdata = { .flags = TEGRA_DC_FLAG_ENABLED, .default_out = &p1852_disp1_out, @@ -140,6 +409,29 @@ static struct tegra_dc_platform_data p1852_disp1_pdata = { .fb = &p1852_fb_data, }; +static struct tegra_dc_platform_data p1852_ser1_pdata = { + .flags = TEGRA_DC_FLAG_ENABLED, + .default_out = &p1852_ser1_out, + .emc_clk_rate = 300000000, + .fb = &p1852_fb_data, +}; + +static struct tegra_dc_platform_data p1852_ser2_pdata = { + .flags = TEGRA_DC_FLAG_ENABLED, + .default_out = &p1852_ser2_out, + .emc_clk_rate = 300000000, + .fb = &p1852_hdmi_fb_data, +}; + +static struct tegra_dc_platform_data p1852_hdmi_pdata = { + .flags = TEGRA_DC_FLAG_ENABLED, + .default_out = &p1852_hdmi_out, + .emc_clk_rate = 300000000, + .fb = &p1852_hdmi_fb_data, +}; + +/* End of platform data */ + static struct nvmap_platform_carveout p1852_carveouts[] = { [0] = { .name = "iram", @@ -166,7 +458,7 @@ static struct platform_device *p1852_gfx_devices[] __initdata = { &tegra_nvmap_device, }; -int __init p1852_panel_init(void) +static int __init p1852_sku2_panel_init(void) { int err; struct resource *res; @@ -174,16 +466,62 @@ int __init p1852_panel_init(void) p1852_carveouts[1].base = tegra_carveout_start; p1852_carveouts[1].size = tegra_carveout_size; tegra_nvmap_device.dev.platform_data = &p1852_nvmap_data; - tegra_disp1_device.dev.platform_data = &p1852_disp1_pdata; + /* + * sku2 has primary LVDS out and secondary LVDS out + * (via HDMI->RGB->Serializer) + */ + tegra_disp1_device.dev.platform_data = &p1852_ser1_pdata; + tegra_disp2_device.dev.platform_data = &p1852_ser2_pdata; +#ifdef CONFIG_TEGRA_GRHOST + err = tegra3_register_host1x_devices(); + if (err) + return err; +#endif + + err = platform_add_devices(p1852_gfx_devices, + ARRAY_SIZE(p1852_gfx_devices)); + +#if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_DC) res = nvhost_get_resource_byname(&tegra_disp1_device, IORESOURCE_MEM, "fbmem"); - if (!res) { - pr_err("No memory resources\n"); - return -ENODEV; + if (res) { + res->start = tegra_fb_start; + res->end = tegra_fb_start + tegra_fb_size - 1; + } + + if (!err) + err = nvhost_device_register(&tegra_disp1_device); + + res = nvhost_get_resource_byname(&tegra_disp2_device, + IORESOURCE_MEM, "fbmem"); + if (res) { + res->start = tegra_fb2_start; + res->end = tegra_fb2_start + tegra_fb2_size - 1; } - res->start = tegra_fb_start; - res->end = tegra_fb_start + tegra_fb_size - 1; + + if (!err) + err = nvhost_device_register(&tegra_disp2_device); +#endif + +#if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_NVAVP) + if (!err) + err = nvhost_device_register(&nvavp_device); +#endif + return err; +} + +static int __init p1852_sku8_panel_init(void) +{ + int err; + struct resource *res; + + p1852_carveouts[1].base = tegra_carveout_start; + p1852_carveouts[1].size = tegra_carveout_size; + tegra_nvmap_device.dev.platform_data = &p1852_nvmap_data; + /* sku 8 has primary RGB out and secondary HDMI out */ + tegra_disp1_device.dev.platform_data = &p1852_disp1_pdata; + tegra_disp2_device.dev.platform_data = &p1852_hdmi_pdata; #ifdef CONFIG_TEGRA_GRHOST err = tegra3_register_host1x_devices(); @@ -193,12 +531,50 @@ int __init p1852_panel_init(void) err = platform_add_devices(p1852_gfx_devices, ARRAY_SIZE(p1852_gfx_devices)); + +#if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_DC) + res = nvhost_get_resource_byname(&tegra_disp1_device, + IORESOURCE_MEM, "fbmem"); + if (res) { + res->start = tegra_fb_start; + res->end = tegra_fb_start + tegra_fb_size - 1; + } + if (!err) err = nvhost_device_register(&tegra_disp1_device); + res = nvhost_get_resource_byname(&tegra_disp2_device, + IORESOURCE_MEM, "fbmem"); + if (res) { + res->start = tegra_fb2_start; + res->end = tegra_fb2_start + tegra_fb2_size - 1; + } + + if (!err) + err = nvhost_device_register(&tegra_disp2_device); +#endif + #if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_NVAVP) if (!err) err = nvhost_device_register(&nvavp_device); #endif return err; } + +int __init p1852_panel_init(void) +{ + int skuid; + + skuid = p1852_get_skuid(); + + switch (skuid) { + case 2: + return p1852_sku2_panel_init(); + case 5: /* Sku 5 display is same as 8 */ + case 8: + return p1852_sku8_panel_init(); + default: + pr_warning("%s: unknown skuid %d\n", __func__, skuid); + return 1; + } +} diff --git a/arch/arm/mach-tegra/board-p1852.c b/arch/arm/mach-tegra/board-p1852.c index 5e7aea1c486d..c195f0f4ca5a 100644 --- a/arch/arm/mach-tegra/board-p1852.c +++ b/arch/arm/mach-tegra/board-p1852.c @@ -609,12 +609,29 @@ static void __init tegra_p1852_init(void) static void __init tegra_p1852_reserve(void) { #if defined(CONFIG_NVMAP_CONVERT_CARVEOUT_TO_IOVMM) - tegra_reserve(0, SZ_8M, 0); + tegra_reserve(0, SZ_8M, SZ_8M); #else - tegra_reserve(SZ_128M, SZ_8M, 0); + tegra_reserve(SZ_128M, SZ_8M, SZ_8M); #endif } +int p1852_get_skuid() +{ + switch (system_rev) { + case TEGRA_P1852_SKU2_A00: + case TEGRA_P1852_SKU2_B00: + return 2; + case TEGRA_P1852_SKU5_A00: + case TEGRA_P1852_SKU5_B00: + return 5; + case TEGRA_P1852_SKU8_A00: + case TEGRA_P1852_SKU8_B00: + return 8; + default: + return -1; + } +} + MACHINE_START(P1852, "p1852") .boot_params = 0x80000100, .init_irq = tegra_init_irq, diff --git a/arch/arm/mach-tegra/board-p1852.h b/arch/arm/mach-tegra/board-p1852.h index 1ac0968f9518..529bd3840e63 100644 --- a/arch/arm/mach-tegra/board-p1852.h +++ b/arch/arm/mach-tegra/board-p1852.h @@ -90,16 +90,27 @@ #define AC_PRESENT_INT (TPS6591X_INT_GPIO4 + TPS6591X_IRQ_BASE) +/* List of P1852 skus - replicated from core/include/nvmachtypes.h */ +#define TEGRA_P1852_SKU2_A00 0x020000UL +#define TEGRA_P1852_SKU2_B00 0x020200UL +#define TEGRA_P1852_SKU5_A00 0x050000UL +#define TEGRA_P1852_SKU5_B00 0x050200UL +#define TEGRA_P1852_SKU8_A00 0x080000UL +#define TEGRA_P1852_SKU8_B00 0x080200UL + int p1852_sdhci_init(void); int p1852_pinmux_init(void); int p1852_panel_init(void); int p1852_gpio_init(void); int p1852_pins_state_init(void); +int p1852_get_skuid(void); + #ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT #define TOUCH_GPIO_IRQ_ATMEL_T9 TEGRA_GPIO_PEE1 #define TOUCH_GPIO_RST_ATMEL_T9 TEGRA_GPIO_PW2 #define TOUCH_BUS_ATMEL_T9 0 #endif + #endif diff --git a/arch/arm/mach-tegra/devices.c b/arch/arm/mach-tegra/devices.c index fb97f94b939f..508320582560 100644 --- a/arch/arm/mach-tegra/devices.c +++ b/arch/arm/mach-tegra/devices.c @@ -1698,6 +1698,43 @@ struct nvhost_device tegra_disp1_device = { .num_resources = ARRAY_SIZE(tegra_disp1_resources), }; +static struct resource tegra_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, + .start = 0, + .end = 0, + }, + { + .name = "hdmi_regs", + .start = TEGRA_HDMI_BASE, + .end = TEGRA_HDMI_BASE + TEGRA_HDMI_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct nvhost_device tegra_disp2_device = { + .name = "tegradc", + .id = 1, + .resource = tegra_disp2_resources, + .num_resources = ARRAY_SIZE(tegra_disp2_resources), + .dev = { + .platform_data = 0, + }, +}; + struct platform_device tegra_nvmap_device = { .name = "tegra-nvmap", .id = -1, diff --git a/arch/arm/mach-tegra/devices.h b/arch/arm/mach-tegra/devices.h index 830f2cf7accc..ca492cdd11db 100644 --- a/arch/arm/mach-tegra/devices.h +++ b/arch/arm/mach-tegra/devices.h @@ -131,6 +131,7 @@ extern struct platform_device debug_uarte_device; #endif extern struct nvhost_device tegra_disp1_device; +extern struct nvhost_device tegra_disp2_device; extern struct platform_device tegra_nvmap_device; #ifndef CONFIG_ARCH_TEGRA_2x_SOC extern struct platform_device tegra_cec_device; diff --git a/drivers/video/tegra/dc/hdmi.c b/drivers/video/tegra/dc/hdmi.c index 79478ea48f83..55d9163d4faf 100644 --- a/drivers/video/tegra/dc/hdmi.c +++ b/drivers/video/tegra/dc/hdmi.c @@ -1370,18 +1370,31 @@ bool tegra_dc_hdmi_detect_test(struct tegra_dc *dc, unsigned char *edid_ptr) err = tegra_edid_get_monspecs_test(hdmi->edid, &specs, edid_ptr); if (err < 0) { - dev_err(&dc->ndev->dev, "error reading edid\n"); - goto fail; - } + /* Check if there's a hard-wired mode, if so, enable it */ + if (dc->out->n_modes) + tegra_dc_enable(dc); + else { + dev_err(&dc->ndev->dev, "error reading edid\n"); + goto fail; + } +#ifdef CONFIG_SWITCH + hdmi->hpd_switch.state = 0; + switch_set_state(&hdmi->hpd_switch, 1); +#endif + dev_info(&dc->ndev->dev, "display detected\n"); - err = tegra_edid_get_eld(hdmi->edid, &hdmi->eld); - if (err < 0) { - dev_err(&dc->ndev->dev, "error populating eld\n"); - goto fail; - } - hdmi->eld_retrieved = true; + dc->connected = true; + tegra_dc_ext_process_hotplug(dc->ndev->id); + } else { + err = tegra_edid_get_eld(hdmi->edid, &hdmi->eld); + if (err < 0) { + dev_err(&dc->ndev->dev, "error populating eld\n"); + goto fail; + } + hdmi->eld_retrieved = true; - tegra_dc_hdmi_detect_config(dc, &specs); + tegra_dc_hdmi_detect_config(dc, &specs); + } return true; @@ -1406,18 +1419,30 @@ static bool tegra_dc_hdmi_detect(struct tegra_dc *dc) err = tegra_edid_get_monspecs(hdmi->edid, &specs); if (err < 0) { - dev_err(&dc->ndev->dev, "error reading edid\n"); - goto fail; - } + if (dc->out->n_modes) + tegra_dc_enable(dc); + else { + dev_err(&dc->ndev->dev, "error reading edid\n"); + goto fail; + } +#ifdef CONFIG_SWITCH + hdmi->hpd_switch.state = 0; + switch_set_state(&hdmi->hpd_switch, 1); +#endif + dev_info(&dc->ndev->dev, "display detected\n"); - err = tegra_edid_get_eld(hdmi->edid, &hdmi->eld); - if (err < 0) { - dev_err(&dc->ndev->dev, "error populating eld\n"); - goto fail; - } - hdmi->eld_retrieved = true; + dc->connected = true; + tegra_dc_ext_process_hotplug(dc->ndev->id); + } else { + err = tegra_edid_get_eld(hdmi->edid, &hdmi->eld); + if (err < 0) { + dev_err(&dc->ndev->dev, "error populating eld\n"); + goto fail; + } + hdmi->eld_retrieved = true; - tegra_dc_hdmi_detect_config(dc, &specs); + tegra_dc_hdmi_detect_config(dc, &specs); + } return true; |