* Copyright (C) 2010 Google, Inc.
* Author: Erik Gilling <konkers@android.com>
*
- * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved.
+ * Copyright (c) 2010-2013, NVIDIA CORPORATION, All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/io.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <mach/mc.h>
#include <linux/nvhost.h>
#include <mach/latency_allowance.h>
-#include <mach/iomap.h>
+#include <mach/pm_domains.h>
#include "dc_reg.h"
#include "dc_config.h"
#include "dev.h"
#include "nvsd.h"
+/* HACK! This needs to come from DT */
+#include "../../../../arch/arm/mach-tegra/iomap.h"
+
#define TEGRA_CRC_LATCHED_DELAY 34
#define DC_COM_PIN_OUTPUT_POLARITY1_INIT_VAL 0x01000000
253, 253, 254, 254, 254, 254, 255, 255,
},
};
+
+static struct tegra_dc_cmu default_limited_cmu = {
+ /* lut1 maps sRGB to linear space. */
+ {
+ 0, 1, 2, 4, 5, 6, 7, 9,
+ 10, 11, 12, 14, 15, 16, 18, 20,
+ 21, 23, 25, 27, 29, 31, 33, 35,
+ 37, 40, 42, 45, 48, 50, 53, 56,
+ 59, 62, 66, 69, 72, 76, 79, 83,
+ 87, 91, 95, 99, 103, 107, 112, 116,
+ 121, 126, 131, 136, 141, 146, 151, 156,
+ 162, 168, 173, 179, 185, 191, 197, 204,
+ 210, 216, 223, 230, 237, 244, 251, 258,
+ 265, 273, 280, 288, 296, 304, 312, 320,
+ 329, 337, 346, 354, 363, 372, 381, 390,
+ 400, 409, 419, 428, 438, 448, 458, 469,
+ 479, 490, 500, 511, 522, 533, 544, 555,
+ 567, 578, 590, 602, 614, 626, 639, 651,
+ 664, 676, 689, 702, 715, 728, 742, 755,
+ 769, 783, 797, 811, 825, 840, 854, 869,
+ 884, 899, 914, 929, 945, 960, 976, 992,
+ 1008, 1024, 1041, 1057, 1074, 1091, 1108, 1125,
+ 1142, 1159, 1177, 1195, 1213, 1231, 1249, 1267,
+ 1286, 1304, 1323, 1342, 1361, 1381, 1400, 1420,
+ 1440, 1459, 1480, 1500, 1520, 1541, 1562, 1582,
+ 1603, 1625, 1646, 1668, 1689, 1711, 1733, 1755,
+ 1778, 1800, 1823, 1846, 1869, 1892, 1916, 1939,
+ 1963, 1987, 2011, 2035, 2059, 2084, 2109, 2133,
+ 2159, 2184, 2209, 2235, 2260, 2286, 2312, 2339,
+ 2365, 2392, 2419, 2446, 2473, 2500, 2527, 2555,
+ 2583, 2611, 2639, 2668, 2696, 2725, 2754, 2783,
+ 2812, 2841, 2871, 2901, 2931, 2961, 2991, 3022,
+ 3052, 3083, 3114, 3146, 3177, 3209, 3240, 3272,
+ 3304, 3337, 3369, 3402, 3435, 3468, 3501, 3535,
+ 3568, 3602, 3636, 3670, 3705, 3739, 3774, 3809,
+ 3844, 3879, 3915, 3950, 3986, 4022, 4059, 4095,
+ },
+ /* csc */
+ {
+ 0x100, 0x000, 0x000,
+ 0x000, 0x100, 0x000,
+ 0x000, 0x000, 0x100,
+ },
+ /*
+ * lut2 maps linear space back to sRGB, where
+ * the output range is [16...235] (limited).
+ */
+ {
+ 16, 17, 17, 18, 19, 19, 20, 21,
+ 22, 22, 23, 24, 24, 25, 26, 26,
+ 27, 27, 28, 29, 29, 30, 30, 31,
+ 31, 32, 32, 32, 33, 33, 34, 34,
+ 35, 35, 35, 36, 36, 36, 37, 37,
+ 38, 38, 38, 39, 39, 39, 40, 40,
+ 40, 41, 41, 41, 41, 42, 42, 42,
+ 43, 43, 43, 43, 44, 44, 44, 45,
+ 45, 45, 45, 46, 46, 46, 46, 47,
+ 47, 47, 47, 48, 48, 48, 48, 49,
+ 49, 49, 49, 49, 50, 50, 50, 50,
+ 51, 51, 51, 51, 51, 52, 52, 52,
+ 52, 53, 53, 53, 53, 53, 54, 54,
+ 54, 54, 54, 55, 55, 55, 55, 55,
+ 56, 56, 56, 56, 56, 56, 57, 57,
+ 57, 57, 57, 58, 58, 58, 58, 58,
+ 58, 59, 59, 59, 59, 59, 60, 60,
+ 60, 60, 60, 60, 61, 61, 61, 61,
+ 61, 61, 62, 62, 62, 62, 62, 62,
+ 63, 63, 63, 63, 63, 63, 63, 64,
+ 64, 64, 64, 64, 64, 65, 65, 65,
+ 65, 65, 65, 65, 66, 66, 66, 66,
+ 66, 66, 67, 67, 67, 67, 67, 67,
+ 67, 68, 68, 68, 68, 68, 68, 68,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 70, 70, 70, 70, 70, 70, 70, 71,
+ 71, 71, 71, 71, 71, 71, 72, 72,
+ 72, 72, 72, 72, 72, 72, 73, 73,
+ 73, 73, 73, 73, 73, 73, 74, 74,
+ 74, 74, 74, 74, 74, 74, 75, 75,
+ 75, 75, 75, 75, 75, 75, 76, 76,
+ 76, 76, 76, 76, 76, 76, 76, 77,
+ 77, 77, 77, 77, 77, 77, 77, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 79, 79, 79, 79, 79, 79, 79, 79,
+ 80, 80, 80, 80, 80, 80, 80, 80,
+ 80, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 82, 82, 82, 82, 82,
+ 82, 82, 82, 82, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 86, 86, 86, 86, 86, 86, 86,
+ 86, 86, 86, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 91, 91, 91, 91,
+ 91, 91, 91, 91, 91, 91, 91, 92,
+ 92, 92, 92, 92, 92, 92, 92, 92,
+ 92, 92, 93, 93, 93, 93, 93, 93,
+ 93, 93, 93, 93, 93, 94, 94, 94,
+ 94, 94, 94, 94, 94, 94, 94, 94,
+ 94, 95, 95, 95, 95, 95, 95, 95,
+ 95, 95, 95, 95, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 97, 97, 97, 97, 97, 97, 97, 97,
+ 97, 97, 97, 97, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 101, 101, 101, 101, 101, 101, 101,
+ 102, 102, 103, 104, 104, 105, 105, 106,
+ 107, 107, 108, 108, 109, 109, 110, 111,
+ 111, 112, 112, 113, 113, 114, 114, 115,
+ 115, 116, 116, 117, 117, 118, 118, 119,
+ 119, 120, 120, 121, 121, 122, 122, 123,
+ 123, 124, 124, 125, 125, 126, 126, 127,
+ 127, 127, 128, 128, 129, 129, 130, 130,
+ 131, 131, 131, 132, 132, 133, 133, 134,
+ 134, 134, 135, 135, 136, 136, 136, 137,
+ 137, 138, 138, 139, 139, 139, 140, 140,
+ 141, 141, 141, 142, 142, 142, 143, 143,
+ 144, 144, 144, 145, 145, 145, 146, 146,
+ 147, 147, 147, 148, 148, 148, 149, 149,
+ 150, 150, 150, 151, 151, 151, 152, 152,
+ 152, 153, 153, 153, 154, 154, 154, 155,
+ 155, 155, 156, 156, 156, 157, 157, 157,
+ 158, 158, 158, 159, 159, 159, 160, 160,
+ 160, 161, 161, 161, 162, 162, 162, 163,
+ 163, 163, 164, 164, 164, 165, 165, 165,
+ 166, 166, 166, 166, 167, 167, 167, 168,
+ 168, 168, 169, 169, 169, 169, 170, 170,
+ 170, 171, 171, 171, 172, 172, 172, 172,
+ 173, 173, 173, 174, 174, 174, 174, 175,
+ 175, 175, 176, 176, 176, 176, 177, 177,
+ 177, 178, 178, 178, 178, 179, 179, 179,
+ 180, 180, 180, 180, 181, 181, 181, 181,
+ 182, 182, 182, 183, 183, 183, 183, 184,
+ 184, 184, 184, 185, 185, 185, 185, 186,
+ 186, 186, 187, 187, 187, 187, 188, 188,
+ 188, 188, 189, 189, 189, 189, 190, 190,
+ 190, 190, 191, 191, 191, 191, 192, 192,
+ 192, 192, 193, 193, 193, 193, 194, 194,
+ 194, 194, 195, 195, 195, 195, 196, 196,
+ 196, 196, 197, 197, 197, 197, 198, 198,
+ 198, 198, 199, 199, 199, 199, 199, 200,
+ 200, 200, 200, 201, 201, 201, 201, 202,
+ 202, 202, 202, 203, 203, 203, 203, 203,
+ 204, 204, 204, 204, 205, 205, 205, 205,
+ 206, 206, 206, 206, 206, 207, 207, 207,
+ 207, 208, 208, 208, 208, 208, 209, 209,
+ 209, 209, 210, 210, 210, 210, 210, 211,
+ 211, 211, 211, 212, 212, 212, 212, 212,
+ 213, 213, 213, 213, 213, 214, 214, 214,
+ 214, 215, 215, 215, 215, 215, 216, 216,
+ 216, 216, 216, 217, 217, 217, 217, 218,
+ 218, 218, 218, 218, 219, 219, 219, 219,
+ 219, 220, 220, 220, 220, 220, 221, 221,
+ 221, 221, 221, 222, 222, 222, 222, 222,
+ 223, 223, 223, 223, 224, 224, 224, 224,
+ 224, 225, 225, 225, 225, 225, 226, 226,
+ 226, 226, 226, 227, 227, 227, 227, 227,
+ 227, 228, 228, 228, 228, 228, 229, 229,
+ 229, 229, 229, 230, 230, 230, 230, 230,
+ 231, 231, 231, 231, 231, 232, 232, 232,
+ 232, 232, 233, 233, 233, 233, 233, 233,
+ 234, 234, 234, 234, 234, 235, 235, 235,
+ },
+};
#endif
void tegra_dc_clk_enable(struct tegra_dc *dc)
}
}
+void tegra_dc_get(struct tegra_dc *dc)
+{
+ tegra_dc_io_start(dc);
+
+ /* extra reference to dc clk */
+ clk_prepare_enable(dc->clk);
+}
+
+void tegra_dc_put(struct tegra_dc *dc)
+{
+ /* balance extra dc clk reference */
+ clk_disable_unprepare(dc->clk);
+
+ tegra_dc_io_end(dc);
+}
+
void tegra_dc_hold_dc_out(struct tegra_dc *dc)
{
+ tegra_dc_get(dc);
if (dc->out_ops->hold)
dc->out_ops->hold(dc);
}
{
if (dc->out_ops->release)
dc->out_ops->release(dc);
+ tegra_dc_put(dc);
}
#define DUMP_REG(a) do { \
char buff[256];
mutex_lock(&dc->lock);
- tegra_dc_io_start(dc);
- tegra_dc_hold_dc_out(dc);
+ tegra_dc_get(dc);
+ tegra_dc_writel(dc, WRITE_MUX_ACTIVE | READ_MUX_ACTIVE,
+ DC_CMD_STATE_ACCESS);
DUMP_REG(DC_CMD_DISPLAY_COMMAND_OPTION0);
DUMP_REG(DC_CMD_DISPLAY_COMMAND);
DUMP_REG(DC_DISP_DATA_ENABLE_OPTIONS);
DUMP_REG(DC_DISP_SERIAL_INTERFACE_OPTIONS);
DUMP_REG(DC_DISP_LCD_SPI_OPTIONS);
+#if !defined(CONFIG_TEGRA_DC_BLENDER_GEN2)
DUMP_REG(DC_DISP_BORDER_COLOR);
+#endif
DUMP_REG(DC_DISP_COLOR_KEY0_LOWER);
DUMP_REG(DC_DISP_COLOR_KEY0_UPPER);
DUMP_REG(DC_DISP_COLOR_KEY1_LOWER);
DUMP_REG(DC_WIN_V_INITIAL_DDA);
DUMP_REG(DC_WIN_DDA_INCREMENT);
DUMP_REG(DC_WIN_LINE_STRIDE);
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
+#if !defined(CONFIG_TEGRA_DC_BLENDER_GEN2)
DUMP_REG(DC_WIN_BUF_STRIDE);
DUMP_REG(DC_WIN_UV_BUF_STRIDE);
#endif
+#if !defined(CONFIG_TEGRA_DC_BLENDER_GEN2) || defined(CONFIG_ARCH_TEGRA_14x_SOC)
DUMP_REG(DC_WIN_BLEND_NOKEY);
DUMP_REG(DC_WIN_BLEND_1WIN);
DUMP_REG(DC_WIN_BLEND_2WIN_X);
DUMP_REG(DC_WIN_BLEND_2WIN_Y);
DUMP_REG(DC_WIN_BLEND_3WIN_XY);
+#endif
DUMP_REG(DC_WINBUF_START_ADDR);
DUMP_REG(DC_WINBUF_START_ADDR_U);
DUMP_REG(DC_WINBUF_START_ADDR_V);
DUMP_REG(DC_COM_PM1_CONTROL);
DUMP_REG(DC_COM_PM1_DUTY_CYCLE);
DUMP_REG(DC_DISP_SD_CONTROL);
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
+#ifdef CONFIG_TEGRA_DC_CMU
DUMP_REG(DC_COM_CMU_CSC_KRR);
DUMP_REG(DC_COM_CMU_CSC_KGR);
DUMP_REG(DC_COM_CMU_CSC_KBR);
DUMP_REG(DC_COM_CMU_CSC_KBB);
#endif
- tegra_dc_release_dc_out(dc);
- tegra_dc_io_end(dc);
+ tegra_dc_put(dc);
mutex_unlock(&dc->lock);
}
dc->stats.underflows_a,
dc->stats.underflows_b,
dc->stats.underflows_c);
+#if defined(CONFIG_ARCH_TEGRA_14x_SOC)
+ seq_printf(s,
+ "underflows_d: %llu\n"
+ "underflows_h: %llu\n"
+ "underflows_t: %llu\n",
+ dc->stats.underflows_d,
+ dc->stats.underflows_h,
+ dc->stats.underflows_t);
+#endif
mutex_unlock(&dc->lock);
return 0;
return single_open(file, dbg_dc_stats_show, inode->i_private);
}
+static int dbg_dc_event_inject_show(struct seq_file *s, void *unused)
+{
+ return 0;
+}
+
+static int dbg_dc_event_inject_write(struct file *file, const char __user *addr,
+ size_t len, loff_t *pos)
+{
+ struct seq_file *m = file->private_data; /* single_open() initialized */
+ struct tegra_dc *dc = m ? m->private : NULL;
+ long event;
+ int ret;
+
+ if (!dc)
+ return -EINVAL;
+
+ ret = kstrtol_from_user(addr, len, 10, &event);
+ if (ret < 0)
+ return ret;
+
+ if (event == 0x1) /* TEGRA_DC_EXT_EVENT_HOTPLUG */
+ tegra_dc_ext_process_hotplug(dc->ndev->id);
+ else if (event == 0x2) /* TEGRA_DC_EXT_EVENT_BANDWIDTH */
+ tegra_dc_ext_process_bandwidth_renegotiate(dc->ndev->id);
+ else {
+ dev_err(&dc->ndev->dev, "Unknown event 0x%lx\n", event);
+ return -EINVAL; /* unknown event number */
+ }
+ return len;
+}
+
static const struct file_operations stats_fops = {
.open = dbg_dc_stats_open,
.read = seq_read,
.release = single_release,
};
+static int dbg_dc_event_inject_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dbg_dc_event_inject_show, inode->i_private);
+}
+
+static const struct file_operations event_inject_fops = {
+ .open = dbg_dc_event_inject_open,
+ .read = seq_read,
+ .write = dbg_dc_event_inject_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static void tegra_dc_remove_debugfs(struct tegra_dc *dc)
{
if (dc->debugdir)
if (!retval)
goto remove_out;
+ retval = debugfs_create_file("event_inject", S_IRUGO, dc->debugdir, dc,
+ &event_inject_fops);
+ if (!retval)
+ goto remove_out;
+
return;
remove_out:
dev_err(&dc->ndev->dev, "could not create debugfs\n");
return 0;
BUG_ON(win > DC_N_WINDOWS);
mutex_lock(&dc->lock);
- tegra_dc_io_start(dc);
- tegra_dc_hold_dc_out(dc);
+ tegra_dc_get(dc);
tegra_dc_writel(dc, WINDOW_A_SELECT << win,
DC_CMD_DISPLAY_WINDOW_HEADER);
stride = tegra_dc_readl(dc, DC_WIN_LINE_STRIDE);
- tegra_dc_release_dc_out(dc);
- tegra_dc_io_end(dc);
+ tegra_dc_put(dc);
mutex_unlock(&dc->lock);
return GET_LINE_STRIDE(stride);
}
{
int sense;
int level;
+ int hpd;
- level = gpio_get_value(dc->out->hotplug_gpio);
+ if (WARN_ON(!dc || !dc->out))
+ return false;
+
+ if (dc->out->hotplug_state != 0) {
+ if (dc->out->hotplug_state == 1) /* force on */
+ return true;
+ if (dc->out->hotplug_state == -1) /* force off */
+ return false;
+ }
+ level = gpio_get_value_cansleep(dc->out->hotplug_gpio);
sense = dc->out->flags & TEGRA_DC_OUT_HOTPLUG_MASK;
- return (sense == TEGRA_DC_OUT_HOTPLUG_HIGH && level) ||
+ hpd = (sense == TEGRA_DC_OUT_HOTPLUG_HIGH && level) ||
(sense == TEGRA_DC_OUT_HOTPLUG_LOW && !level);
+
+ if (dc->out->hotplug_report)
+ dc->out->hotplug_report(hpd);
+
+ return hpd;
}
EXPORT_SYMBOL(tegra_dc_hpd);
return 0;
}
+#ifdef CONFIG_TEGRA_DC_CMU
if (cmu != &dc->cmu) {
tegra_dc_cache_cmu(&dc->cmu, cmu);
tegra_dc_set_cmu(dc, &dc->cmu);
}
- tegra_dc_set_color_control(dc);
+#endif
return 0;
}
int ret;
mutex_lock(&dc->lock);
- tegra_dc_io_start(dc);
- tegra_dc_hold_dc_out(dc);
+ if (!dc->enabled) {
+ mutex_unlock(&dc->lock);
+ return 0;
+ }
+
+ tegra_dc_get(dc);
ret = _tegra_dc_update_cmu(dc, cmu);
+ tegra_dc_set_color_control(dc);
- tegra_dc_release_dc_out(dc);
- tegra_dc_io_end(dc);
+ tegra_dc_put(dc);
mutex_unlock(&dc->lock);
return ret;
void tegra_dc_cmu_enable(struct tegra_dc *dc, bool cmu_enable)
{
- tegra_dc_update_cmu(dc, &dc->cmu);
+ dc->pdata->cmu_enable = cmu_enable;
+ if (dc->pdata->cmu) {
+ tegra_dc_update_cmu(dc, dc->pdata->cmu);
+ } else {
+ if (dc->out->type == TEGRA_DC_OUT_HDMI)
+ tegra_dc_update_cmu(dc, &default_limited_cmu);
+ else
+ tegra_dc_update_cmu(dc, &default_cmu);
+ }
}
#else
#define tegra_dc_cache_cmu(dst_cmu, src_cmu)
u32 max;
mutex_lock(&dc->lock);
- tegra_dc_io_start(dc);
- tegra_dc_hold_dc_out(dc);
+ tegra_dc_get(dc);
max = nvhost_syncpt_incr_max_ext(dc->ndev,
dc->syncpt[i].id, ((dc->enabled) ? 1 : 0));
dc->syncpt[i].max = max;
- tegra_dc_release_dc_out(dc);
- tegra_dc_io_end(dc);
+ tegra_dc_put(dc);
mutex_unlock(&dc->lock);
return max;
{
mutex_lock(&dc->lock);
if (dc->enabled) {
- tegra_dc_io_start(dc);
- tegra_dc_hold_dc_out(dc);
+ tegra_dc_get(dc);
while (dc->syncpt[i].min < val) {
dc->syncpt[i].min++;
nvhost_syncpt_cpu_incr_ext(dc->ndev, dc->syncpt[i].id);
}
- tegra_dc_release_dc_out(dc);
- tegra_dc_io_end(dc);
+ tegra_dc_put(dc);
}
mutex_unlock(&dc->lock);
}
return;
}
- tegra_dc_io_start(dc);
- tegra_dc_hold_dc_out(dc);
+ tegra_dc_get(dc);
ctrl = ((cfg->period << PM_PERIOD_SHIFT) |
(cfg->clk_div << PM_CLK_DIVIDER_SHIFT) |
break;
}
tegra_dc_writel(dc, cmd_state, DC_CMD_STATE_ACCESS);
- tegra_dc_release_dc_out(dc);
- tegra_dc_io_end(dc);
+ tegra_dc_put(dc);
mutex_unlock(&dc->lock);
}
EXPORT_SYMBOL(tegra_dc_config_pwm);
dc->out_ops = &tegra_dc_dsi_ops;
break;
+#ifdef CONFIG_TEGRA_DP
+ case TEGRA_DC_OUT_DP:
+ dc->out_ops = &tegra_dc_dp_ops;
+ break;
+#endif
+
default:
dc->out_ops = NULL;
break;
}
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
- if (out->type == TEGRA_DC_OUT_HDMI)
- dc->powergate_id = TEGRA_POWERGATE_DISB;
- else
- dc->powergate_id = TEGRA_POWERGATE_DISA;
-#endif
-
if (dc->out_ops && dc->out_ops->init)
dc->out_ops->init(dc);
}
u32 val;
mutex_lock(&dc->lock);
- tegra_dc_io_start(dc);
- tegra_dc_hold_dc_out(dc);
+ tegra_dc_get(dc);
val = CRC_ALWAYS_ENABLE | CRC_INPUT_DATA_ACTIVE_DATA |
CRC_ENABLE_ENABLE;
tegra_dc_writel(dc, val, DC_COM_CRC_CONTROL);
tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL);
tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
- tegra_dc_release_dc_out(dc);
- tegra_dc_io_end(dc);
+ tegra_dc_put(dc);
mutex_unlock(&dc->lock);
}
void tegra_dc_disable_crc(struct tegra_dc *dc)
{
mutex_lock(&dc->lock);
- tegra_dc_io_start(dc);
- tegra_dc_hold_dc_out(dc);
+ tegra_dc_get(dc);
tegra_dc_writel(dc, 0x0, DC_COM_CRC_CONTROL);
tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL);
tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
- tegra_dc_release_dc_out(dc);
- tegra_dc_io_end(dc);
+ tegra_dc_put(dc);
mutex_unlock(&dc->lock);
}
goto crc_error;
}
-#ifndef CONFIG_TEGRA_SIMULATION_PLATFORM
- /* TODO: Replace mdelay with code to sync VBlANK, since
- * DC_COM_CRC_CHECKSUM_LATCHED is available after VBLANK */
- mdelay(TEGRA_CRC_LATCHED_DELAY);
-#endif
+ if (!tegra_platform_is_linsim())
+ /* TODO: Replace mdelay with code to sync VBlANK, since
+ * DC_COM_CRC_CHECKSUM_LATCHED is available after VBLANK */
+ mdelay(TEGRA_CRC_LATCHED_DELAY);
mutex_lock(&dc->lock);
- tegra_dc_io_start(dc);
- tegra_dc_hold_dc_out(dc);
+ tegra_dc_get(dc);
crc = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM_LATCHED);
- tegra_dc_release_dc_out(dc);
- tegra_dc_io_end(dc);
+ tegra_dc_put(dc);
mutex_unlock(&dc->lock);
crc_error:
return crc;
static bool tegra_dc_windows_are_dirty(struct tegra_dc *dc)
{
-#ifndef CONFIG_TEGRA_SIMULATION_PLATFORM
u32 val;
+ if (tegra_platform_is_linsim())
+ return false;
+
val = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
- if (val & (WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ))
- return true;
-#endif
+ if (val & WIN_ALL_ACT_REQ)
+ return true;
+
return false;
}
static inline void enable_dc_irq(const struct tegra_dc *dc)
{
-#ifndef CONFIG_TEGRA_FPGA_PLATFORM
- enable_irq(dc->irq);
-#else
- /* Always disable DC interrupts on FPGA. */
- disable_irq(dc->irq);
-#endif
+ if (tegra_platform_is_fpga())
+ /* Always disable DC interrupts on FPGA. */
+ disable_irq(dc->irq);
+ else
+ enable_irq(dc->irq);
}
void tegra_dc_get_fbvblank(struct tegra_dc *dc, struct fb_vblank *vblank)
* c) Initialize completion for next iteration.
*/
- tegra_dc_hold_dc_out(dc);
+ mutex_lock(&dc->one_shot_lp_lock);
+ tegra_dc_get(dc);
dc->out->user_needs_vblank = true;
+ tegra_dc_unmask_interrupt(dc, MSF_INT);
ret = wait_for_completion_interruptible(&dc->out->user_vblank_comp);
init_completion(&dc->out->user_vblank_comp);
- tegra_dc_release_dc_out(dc);
+ tegra_dc_mask_interrupt(dc, MSF_INT);
+ tegra_dc_put(dc);
+ mutex_unlock(&dc->one_shot_lp_lock);
return ret;
}
+static void tegra_dc_prism_update_backlight(struct tegra_dc *dc)
+{
+ /* Do the actual brightness update outside of the mutex dc->lock */
+ if (dc->out->sd_settings && !dc->out->sd_settings->bl_device &&
+ dc->out->sd_settings->bl_device_name) {
+ char *bl_device_name =
+ dc->out->sd_settings->bl_device_name;
+ dc->out->sd_settings->bl_device =
+ get_backlight_device_by_name(bl_device_name);
+ }
+
+ if (dc->out->sd_settings && dc->out->sd_settings->bl_device) {
+ struct backlight_device *bl = dc->out->sd_settings->bl_device;
+ backlight_update_status(bl);
+ }
+}
+
static void tegra_dc_vblank(struct work_struct *work)
{
struct tegra_dc *dc = container_of(work, struct tegra_dc, vblank_work);
return;
}
- tegra_dc_io_start(dc);
- tegra_dc_hold_dc_out(dc);
+ tegra_dc_get(dc);
/* use the new frame's bandwidth setting instead of max(current, new),
* skip this if we're using tegra_dc_one_shot_worker() */
if (!(dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE))
if (!dc->vblank_ref_count)
tegra_dc_mask_interrupt(dc, V_BLANK_INT);
- tegra_dc_release_dc_out(dc);
- tegra_dc_io_end(dc);
+ tegra_dc_put(dc);
mutex_unlock(&dc->lock);
- if (dc->out->sd_settings && !dc->out->sd_settings->bl_device &&
- dc->out->sd_settings->bl_device_name) {
- char *bl_device_name =
- dc->out->sd_settings->bl_device_name;
- dc->out->sd_settings->bl_device =
- get_backlight_device_by_name(bl_device_name);
- }
-
- /* Do the actual brightness update outside of the mutex */
- if (nvsd_updated && dc->out->sd_settings &&
- dc->out->sd_settings->bl_device) {
-
- struct backlight_device *bl = dc->out->sd_settings->bl_device;
- backlight_update_status(bl);
- }
+ /* Do the actual brightness update outside of the mutex dc->lock */
+ if (nvsd_updated)
+ tegra_dc_prism_update_backlight(dc);
}
static void tegra_dc_one_shot_worker(struct work_struct *work)
if (dc->underflow_mask & WIN_C_UF_INT)
dc->stats.underflows_c += tegra_dc_underflow_count(dc,
DC_WINBUF_CD_UFLOW_STATUS);
+#if defined(CONFIG_ARCH_TEGRA_14x_SOC)
+ if (dc->underflow_mask & HC_UF_INT)
+ dc->stats.underflows_h += tegra_dc_underflow_count(dc,
+ DC_WINBUF_HD_UFLOW_STATUS);
+ if (dc->underflow_mask & WIN_D_UF_INT)
+ dc->stats.underflows_d += tegra_dc_underflow_count(dc,
+ DC_WINBUF_DD_UFLOW_STATUS);
+ if (dc->underflow_mask & WIN_T_UF_INT)
+ dc->stats.underflows_t += tegra_dc_underflow_count(dc,
+ DC_WINBUF_TD_UFLOW_STATUS);
+#endif
/* Check for any underflow reset conditions */
for (i = 0; i < DC_N_WINDOWS; i++) {
- if (dc->underflow_mask & (WIN_A_UF_INT << i)) {
+ u32 masks[] = {
+ WIN_A_UF_INT,
+ WIN_B_UF_INT,
+ WIN_C_UF_INT,
+#if defined(CONFIG_ARCH_TEGRA_14x_SOC)
+ WIN_D_UF_INT,
+ HC_UF_INT,
+ WIN_T_UF_INT,
+#endif
+ };
+
+ if (WARN_ONCE(i >= ARRAY_SIZE(masks),
+ "underflow stats unsupported"))
+ break; /* bail if the table above is missing entries */
+ if (!masks[i])
+ continue; /* skip empty entries */
+
+ if (dc->underflow_mask & masks[i]) {
dc->windows[i].underflows++;
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
- if (dc->windows[i].underflows > 4) {
+ if (i < 3 && dc->windows[i].underflows > 4) {
schedule_work(&dc->reset_work);
/* reset counter */
dc->windows[i].underflows = 0;
}
#endif
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
- if (dc->windows[i].underflows > 4) {
+ if (i < 3 && dc->windows[i].underflows > 4) {
trace_display_reset(dc);
tegra_dc_writel(dc, UF_LINE_FLUSH,
DC_DISP_DISP_MISC_CONTROL);
/* Clear the underflow mask now that we've checked it. */
tegra_dc_writel(dc, dc->underflow_mask, DC_CMD_INT_STATUS);
dc->underflow_mask = 0;
- tegra_dc_unmask_interrupt(dc, ALL_UF_INT);
+ tegra_dc_unmask_interrupt(dc, ALL_UF_INT());
trace_underflow(dc);
}
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
-static void tegra_dc_vpulse2_locked(struct tegra_dc *dc)
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
+static void tegra_dc_vpulse2(struct work_struct *work)
{
+ struct tegra_dc *dc = container_of(work, struct tegra_dc, vpulse2_work);
bool nvsd_updated = false;
- if (!dc->enabled)
+ mutex_lock(&dc->lock);
+
+ if (!dc->enabled) {
+ mutex_unlock(&dc->lock);
return;
+ }
+
+ tegra_dc_get(dc);
/* Clear the V_PULSE2_FLIP if no update */
if (!tegra_dc_windows_are_dirty(dc))
/* Mask vpulse2 interrupt if ref-count is zero. */
if (!dc->vpulse2_ref_count)
tegra_dc_mask_interrupt(dc, V_PULSE2_INT);
+
+ tegra_dc_put(dc);
+ mutex_unlock(&dc->lock);
+
+ /* Do the actual brightness update outside of the mutex dc->lock */
+ if (nvsd_updated)
+ tegra_dc_prism_update_backlight(dc);
}
#endif
-#ifndef CONFIG_TEGRA_FPGA_PLATFORM
static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status)
{
/* pending user vblank, so wakeup */
complete(&dc->frame_end_complete);
}
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
if (status & V_PULSE2_INT)
- tegra_dc_vpulse2_locked(dc);
+ queue_work(system_freezable_wq, &dc->vpulse2_work);
#endif
}
tegra_dc_trigger_windows(dc);
}
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
if (status & V_PULSE2_INT)
- tegra_dc_vpulse2_locked(dc);
+ queue_work(system_freezable_wq, &dc->vpulse2_work);
#endif
}
!= div_s64((old_ts - dc->frame_end_timestamp),
dc->frametime_ns)));
}
-#endif
static irqreturn_t tegra_dc_irq(int irq, void *ptr)
{
-#ifndef CONFIG_TEGRA_FPGA_PLATFORM
struct tegra_dc *dc = ptr;
unsigned long status;
unsigned long underflow_mask;
u32 val;
+ int need_disable = 0;
+
+ if (tegra_platform_is_fpga())
+ return IRQ_NONE;
mutex_lock(&dc->lock);
- if (!dc->enabled) {
+ if (!dc->enabled || !tegra_dc_is_powered(dc)) {
mutex_unlock(&dc->lock);
return IRQ_HANDLED;
}
- clk_enable(dc->clk);
- tegra_dc_io_start(dc);
- tegra_dc_hold_dc_out(dc);
+ tegra_dc_get(dc);
if (!nvhost_module_powered_ext(dc->ndev)) {
WARN(1, "IRQ when DC not powered!\n");
status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
tegra_dc_writel(dc, status, DC_CMD_INT_STATUS);
- tegra_dc_release_dc_out(dc);
- tegra_dc_io_end(dc);
- clk_disable(dc->clk);
+ tegra_dc_put(dc);
mutex_unlock(&dc->lock);
return IRQ_HANDLED;
}
/* clear all status flags except underflow, save those for the worker */
status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
- tegra_dc_writel(dc, status & ~ALL_UF_INT, DC_CMD_INT_STATUS);
+ tegra_dc_writel(dc, status & ~ALL_UF_INT(), DC_CMD_INT_STATUS);
val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
- tegra_dc_writel(dc, val & ~ALL_UF_INT, DC_CMD_INT_MASK);
+ tegra_dc_writel(dc, val & ~ALL_UF_INT(), DC_CMD_INT_MASK);
/*
* Overlays can get thier internal state corrupted during and underflow
* if we get 4 consecutive frames with underflows, assume we're
* hosed and reset.
*/
- underflow_mask = status & ALL_UF_INT;
+ underflow_mask = status & ALL_UF_INT();
/* Check underflow */
if (underflow_mask) {
else
tegra_dc_continuous_irq(dc, status);
- tegra_dc_release_dc_out(dc);
- tegra_dc_io_end(dc);
- clk_disable(dc->clk);
+ /* update video mode if it has changed since the last frame */
+ if (status & (FRAME_END_INT | V_BLANK_INT))
+ if (tegra_dc_update_mode(dc))
+ need_disable = 1; /* force display off on error */
+
+ tegra_dc_put(dc);
mutex_unlock(&dc->lock);
+ if (need_disable)
+ tegra_dc_disable(dc);
return IRQ_HANDLED;
-#else /* CONFIG_TEGRA_FPGA_PLATFORM */
- return IRQ_NONE;
-#endif /* !CONFIG_TEGRA_FPGA_PLATFORM */
}
void tegra_dc_set_color_control(struct tegra_dc *dc)
case TEGRA_DC_ORDERED_DITHER:
color_control |= DITHER_CONTROL_ORDERED;
break;
+#ifdef CONFIG_TEGRA_DC_TEMPORAL_DITHER
+ case TEGRA_DC_TEMPORAL_DITHER:
+ color_control |= DITHER_CONTROL_TEMPORAL;
+ break;
+#else
case TEGRA_DC_ERRDIFF_DITHER:
/* The line buffer for error-diffusion dither is limited
* to 1280 pixels per line. This limits the maximum
BUG_ON(dc->mode.h_active > 1280);
color_control |= DITHER_CONTROL_ERRDIFF;
break;
+#endif
+ default:
+ dev_err(&dc->ndev->dev, "Error: Unsupported dithering mode\n");
}
#ifdef CONFIG_TEGRA_DC_CMU
static void tegra_dc_init_vpulse2_int(struct tegra_dc *dc)
{
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
u32 start, end;
unsigned long val;
tegra_dc_writel(dc, 0x00000000, DC_DISP_DISP_MISC_CONTROL);
#endif
/* enable interrupts for vblank, frame_end and underflows */
- int_enable = (FRAME_END_INT | V_BLANK_INT | ALL_UF_INT);
+ int_enable = (FRAME_END_INT | V_BLANK_INT | ALL_UF_INT());
/* for panels with one-shot mode enable tearing effect interrupt */
if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
int_enable |= MSF_INT;
tegra_dc_writel(dc, int_enable, DC_CMD_INT_ENABLE);
- tegra_dc_writel(dc, ALL_UF_INT, DC_CMD_INT_MASK);
+ tegra_dc_writel(dc, ALL_UF_INT(), DC_CMD_INT_MASK);
tegra_dc_init_vpulse2_int(dc);
+#if !defined(CONFIG_TEGRA_DC_BLENDER_GEN2)
tegra_dc_writel(dc, 0x00000000, DC_DISP_BORDER_COLOR);
+#else
+ tegra_dc_writel(dc, 0x00000000, DC_DISP_BLEND_BACKGROUND_COLOR);
+#endif
#ifdef CONFIG_TEGRA_DC_CMU
- if (dc->pdata->cmu)
+ if (dc->pdata->cmu) {
_tegra_dc_update_cmu(dc, dc->pdata->cmu);
- else
- _tegra_dc_update_cmu(dc, &default_cmu);
+ } else {
+ if (dc->out->type == TEGRA_DC_OUT_HDMI)
+ _tegra_dc_update_cmu(dc, &default_limited_cmu);
+ else
+ _tegra_dc_update_cmu(dc, &default_cmu);
+ }
#endif
tegra_dc_set_color_control(dc);
for (i = 0; i < DC_N_WINDOWS; i++) {
tegra_dc_set_scaling_filter(dc);
}
+#ifdef CONFIG_TEGRA_DC_WIN_H
+ /* Window H is set to window mode by default for t14x. */
+ tegra_dc_writel(dc, WINH_CURS_SELECT(1),
+ DC_DISP_BLEND_CURSOR_CONTROL);
+#endif
+
for (i = 0; i < dc->n_windows; i++) {
u32 syncpt = get_syncpt(dc, i);
static bool _tegra_dc_controller_enable(struct tegra_dc *dc)
{
int failed_init = 0;
+ int i;
tegra_dc_unpowergate_locked(dc);
dc->out_ops->enable(dc);
/* force a full blending update */
- dc->blend.z[0] = -1;
+ for (i = 0; i < DC_N_WINDOWS; i++)
+ dc->blend.z[i] = -1;
tegra_dc_ext_enable(dc->ext);
if (dc->out->postpoweron)
dc->out->postpoweron();
+ tegra_log_resume_time();
+ /*
+ * We will need to reinitialize the display the next time panel
+ * is enabled.
+ */
+ dc->out->flags &= ~TEGRA_DC_OUT_INITIALIZED_MODE;
+
tegra_dc_io_end(dc);
return true;
}
msleep(5);
tegra_periph_reset_assert(dc->clk);
msleep(2);
-#ifdef CONFIG_TEGRA_SILICON_PLATFORM
- tegra_periph_reset_deassert(dc->clk);
- msleep(1);
-#endif
+ if (tegra_platform_is_silicon()) {
+ tegra_periph_reset_deassert(dc->clk);
+ msleep(1);
+ }
if (dc->ndev->id == 0 && tegra_dcs[1] != NULL) {
enable_dc_irq(tegra_dcs[1]);
if (dc->enabled)
return true;
- if (!_tegra_dc_controller_enable(dc))
+ pm_runtime_get_sync(&dc->ndev->dev);
+
+ if (!_tegra_dc_controller_enable(dc)) {
+ pm_runtime_put_sync(&dc->ndev->dev);
return false;
+ }
return true;
}
{
unsigned i;
- tegra_dc_hold_dc_out(dc);
+ tegra_dc_get(dc);
if (dc->out && dc->out->prepoweroff)
dc->out->prepoweroff();
disable_irq_nosync(dc->irq);
tegra_dc_clear_bandwidth(dc);
- if (dc->out_ops->release) /* ugly hack */
- tegra_dc_release_dc_out(dc);
- else
- tegra_dc_clk_disable(dc);
if (dc->out && dc->out->disable)
dc->out->disable();
}
}
trace_display_disable(dc);
+
+ tegra_dc_clk_disable(dc);
+ tegra_dc_put(dc);
}
void tegra_dc_stats_enable(struct tegra_dc *dc, bool enable)
if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
mutex_unlock(&dc->one_shot_lock);
+ pm_runtime_put(&dc->ndev->dev);
+
+ tegra_log_suspend_time();
}
void tegra_dc_disable(struct tegra_dc *dc)
to_delayed_work(work), struct tegra_dc, underflow_work);
mutex_lock(&dc->lock);
- tegra_dc_io_start(dc);
- tegra_dc_hold_dc_out(dc);
+ tegra_dc_get(dc);
if (dc->enabled) {
tegra_dc_underflow_handler(dc);
}
- tegra_dc_release_dc_out(dc);
- tegra_dc_io_end(dc);
+ tegra_dc_put(dc);
mutex_unlock(&dc->lock);
}
}
#endif
+static void tegra_dc_add_modes(struct tegra_dc *dc)
+{
+ struct fb_monspecs specs;
+ int i;
+
+ memset(&specs, 0, sizeof(specs));
+ specs.max_x = dc->mode.h_active * 1000;
+ specs.max_y = dc->mode.v_active * 1000;
+ specs.modedb_len = dc->out->n_modes;
+ specs.modedb = kzalloc(specs.modedb_len *
+ sizeof(struct fb_videomode), GFP_KERNEL);
+ for (i = 0; i < dc->out->n_modes; i++)
+ tegra_dc_to_fb_videomode(&specs.modedb[i],
+ &dc->out->modes[i]);
+ tegra_fb_update_monspecs(dc->fb, &specs, NULL);
+ kfree(specs.modedb);
+}
+
static int tegra_dc_probe(struct platform_device *ndev)
{
struct tegra_dc *dc;
struct tegra_dc_mode *mode;
struct clk *clk;
+#ifndef CONFIG_TEGRA_ISOMGR
struct clk *emc_clk;
+#endif
+ int isomgr_client_id = -1;
struct resource *res;
struct resource *base_res;
struct resource *fb_mem = NULL;
dc->win_syncpt[0] = NVSYNCPT_DISP0_A;
dc->win_syncpt[1] = NVSYNCPT_DISP0_B;
dc->win_syncpt[2] = NVSYNCPT_DISP0_C;
+ dc->valid_windows = 0x07;
+#ifdef CONFIG_ARCH_TEGRA_14x_SOC
+ dc->win_syncpt[3] = NVSYNCPT_DISP0_D;
+ dc->win_syncpt[4] = NVSYNCPT_DISP0_H;
+ dc->valid_windows |= 0x18;
+#endif
+ dc->powergate_id = TEGRA_POWERGATE_DISA;
+ isomgr_client_id = TEGRA_ISO_CLIENT_DISP_0;
} else if (TEGRA_DISPLAY2_BASE == res->start) {
dc->vblank_syncpt = NVSYNCPT_VBLANK1;
dc->win_syncpt[0] = NVSYNCPT_DISP1_A;
dc->win_syncpt[1] = NVSYNCPT_DISP1_B;
dc->win_syncpt[2] = NVSYNCPT_DISP1_C;
+ dc->valid_windows = 0x07;
+#ifdef CONFIG_ARCH_TEGRA_14x_SOC
+ dc->win_syncpt[4] = NVSYNCPT_DISP1_H;
+ dc->valid_windows |= 0x10;
+#endif
+ dc->powergate_id = TEGRA_POWERGATE_DISB;
+ isomgr_client_id = TEGRA_ISO_CLIENT_DISP_1;
} else {
dev_err(&ndev->dev,
"Unknown base address %#08x: unable to assign syncpt\n",
goto err_iounmap_reg;
}
- emc_clk = clk_get(&ndev->dev, "emc");
- if (IS_ERR_OR_NULL(emc_clk)) {
- dev_err(&ndev->dev, "can't get emc clock\n");
- ret = -ENOENT;
- goto err_put_clk;
- }
-
dc->clk = clk;
- dc->emc_clk = emc_clk;
dc->shift_clk_div.mul = dc->shift_clk_div.div = 1;
/* Initialize one shot work delay, it will be assigned by dsi
* according to refresh rate later. */
dc->ndev = ndev;
dc->pdata = ndev->dev.platform_data;
- /*
- * The emc is a shared clock, it will be set based on
- * the requirements for each user on the bus.
- */
- dc->emc_clk_rate = 0;
+ dc->bw_kbps = 0;
mutex_init(&dc->lock);
mutex_init(&dc->one_shot_lock);
+ mutex_init(&dc->one_shot_lp_lock);
init_completion(&dc->frame_end_complete);
init_waitqueue_head(&dc->wq);
init_waitqueue_head(&dc->timestamp_wq);
#endif
INIT_WORK(&dc->vblank_work, tegra_dc_vblank);
dc->vblank_ref_count = 0;
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
+ INIT_WORK(&dc->vpulse2_work, tegra_dc_vpulse2);
+#endif
dc->vpulse2_ref_count = 0;
INIT_DELAYED_WORK(&dc->underflow_work, tegra_dc_underflow_worker);
INIT_DELAYED_WORK(&dc->one_shot_work, tegra_dc_one_shot_worker);
ret = tegra_dc_set(dc, ndev->id);
if (ret < 0) {
dev_err(&ndev->dev, "can't add dc\n");
- goto err_free_irq;
+ goto err_put_clk;
}
platform_set_drvdata(ndev, dc);
tegra_dc_set_out(dc, dc->pdata->default_out);
else
dev_err(&ndev->dev, "No default output specified. Leaving output disabled.\n");
+ dc->mode_dirty = false; /* ignore changes tegra_dc_set_out has done */
+
+#ifdef CONFIG_TEGRA_ISOMGR
+ if (isomgr_client_id == -1) {
+ dc->isomgr_handle = NULL;
+ } else {
+ dc->isomgr_handle = tegra_isomgr_register(isomgr_client_id,
+ tegra_dc_calc_min_bandwidth(dc),
+ tegra_dc_bandwidth_renegotiate, dc);
+ if (IS_ERR(dc->isomgr_handle)) {
+ dev_err(&dc->ndev->dev,
+ "could not register isomgr. err=%ld\n",
+ PTR_ERR(dc->isomgr_handle));
+ ret = -ENOENT;
+ goto err_put_clk;
+ }
+ }
+#else
+ /*
+ * The emc is a shared clock, it will be set based on
+ * the requirements for each user on the bus.
+ */
+ emc_clk = clk_get(&ndev->dev, "emc");
+ if (IS_ERR_OR_NULL(emc_clk)) {
+ dev_err(&ndev->dev, "can't get emc clock\n");
+ ret = -ENOENT;
+ goto err_put_clk;
+ }
+ dc->emc_clk = emc_clk;
+#endif
dc->ext = tegra_dc_ext_register(ndev, dc);
if (IS_ERR_OR_NULL(dc->ext)) {
dev_name(&ndev->dev), dc)) {
dev_err(&ndev->dev, "request_irq %d failed\n", irq);
ret = -EBUSY;
- goto err_put_emc_clk;
+ goto err_disable_dc;
}
disable_dc_irq(dc);
- mutex_lock(&dc->lock);
- if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED) {
- _tegra_dc_set_default_videomode(dc);
- dc->enabled = _tegra_dc_enable(dc);
- }
- mutex_unlock(&dc->lock);
+ tegra_pd_add_device(&ndev->dev);
+ pm_runtime_use_autosuspend(&ndev->dev);
+ pm_runtime_set_autosuspend_delay(&ndev->dev, 100);
+ pm_runtime_enable(&ndev->dev);
- mutex_lock(&dc->lock);
if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED) {
_tegra_dc_set_default_videomode(dc);
dc->enabled = _tegra_dc_enable(dc);
}
- mutex_unlock(&dc->lock);
tegra_dc_create_debugfs(dc);
tegra_dc_io_start(dc);
dc->fb = tegra_fb_register(ndev, dc, dc->pdata->fb, fb_mem);
- if (IS_ERR_OR_NULL(dc->fb))
- dc->fb = NULL;
tegra_dc_io_end(dc);
+ if (IS_ERR_OR_NULL(dc->fb)) {
+ dc->fb = NULL;
+ dev_err(&ndev->dev, "failed to register fb\n");
+ goto err_remove_debugfs;
+ }
}
+ if (dc->out && dc->out->n_modes)
+ tegra_dc_add_modes(dc);
+
if (dc->out && dc->out->hotplug_init)
dc->out->hotplug_init(&ndev->dev);
else
dc->connected = true;
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
/* Powergate display module when it's unconnected. */
if (!tegra_dc_get_connected(dc))
tegra_dc_powergate_locked(dc);
-#endif
tegra_dc_create_sysfs(&dc->ndev->dev);
return 0;
-err_free_irq:
+err_remove_debugfs:
+ tegra_dc_remove_debugfs(dc);
free_irq(irq, dc);
-err_put_emc_clk:
+err_disable_dc:
+ if (dc->ext) {
+ tegra_dc_ext_disable(dc->ext);
+ tegra_dc_ext_unregister(dc->ext);
+ }
+ mutex_lock(&dc->lock);
+ if (dc->enabled)
+ _tegra_dc_disable(dc);
+ dc->enabled = false;
+ mutex_unlock(&dc->lock);
+#ifdef CONFIG_SWITCH
+ switch_dev_unregister(&dc->modeset_switch);
+#endif
+#ifdef CONFIG_TEGRA_ISOMGR
+ tegra_isomgr_unregister(dc->isomgr_handle);
+#else
clk_put(emc_clk);
+#endif
err_put_clk:
clk_put(clk);
err_iounmap_reg:
switch_dev_unregister(&dc->modeset_switch);
#endif
free_irq(dc->irq, dc);
+#ifdef CONFIG_TEGRA_ISOMGR
+ if (dc->isomgr_handle) {
+ tegra_isomgr_unregister(dc->isomgr_handle);
+ dc->isomgr_handle = NULL;
+ }
+#else
clk_put(dc->emc_clk);
+#endif
clk_put(dc->clk);
iounmap(dc->base);
if (dc->fb_mem)
mutex_lock(&dc->lock);
dc->suspended = false;
+ /* To pan the fb on resume */
+ tegra_fb_pan_display_reset(dc->fb);
+
if (dc->enabled) {
dc->enabled = false;
_tegra_dc_set_default_videomode(dc);
if (!dc || !dc->enabled)
return;
- tegra_dc_blank(dc);
+ /* Hack: no windows blanking for simulation to save shutdown time */
+ if (!tegra_platform_is_linsim())
+ tegra_dc_blank(dc);
tegra_dc_disable(dc);
}