diff options
| author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
|---|---|---|
| committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
| commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
| tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/video/tegra/dc | |
| parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) | |
Diffstat (limited to 'drivers/video/tegra/dc')
25 files changed, 16042 insertions, 0 deletions
diff --git a/drivers/video/tegra/dc/Makefile b/drivers/video/tegra/dc/Makefile new file mode 100644 index 00000000000..01f13918ca6 --- /dev/null +++ b/drivers/video/tegra/dc/Makefile | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | GCOV_PROFILE := y | ||
| 2 | obj-y += dc.o | ||
| 3 | obj-y += rgb.o | ||
| 4 | obj-y += hdmi.o | ||
| 5 | obj-$(CONFIG_TEGRA_NVHDCP) += nvhdcp.o | ||
| 6 | obj-y += edid.o | ||
| 7 | obj-y += nvsd.o | ||
| 8 | obj-y += dsi.o | ||
| 9 | obj-y += dc_sysfs.o | ||
| 10 | obj-$(CONFIG_TEGRA_DC_EXTENSIONS) += ext/ | ||
diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c new file mode 100644 index 00000000000..8b3bf041a7d --- /dev/null +++ b/drivers/video/tegra/dc/dc.c | |||
| @@ -0,0 +1,3120 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/dc.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2010 Google, Inc. | ||
| 5 | * Author: Erik Gilling <konkers@android.com> | ||
| 6 | * | ||
| 7 | * Copyright (C) 2010-2012 NVIDIA Corporation | ||
| 8 | * | ||
| 9 | * This software is licensed under the terms of the GNU General Public | ||
| 10 | * License version 2, as published by the Free Software Foundation, and | ||
| 11 | * may be copied, distributed, and modified under those terms. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/kernel.h> | ||
| 22 | #include <linux/err.h> | ||
| 23 | #include <linux/errno.h> | ||
| 24 | #include <linux/interrupt.h> | ||
| 25 | #include <linux/slab.h> | ||
| 26 | #include <linux/io.h> | ||
| 27 | #include <linux/clk.h> | ||
| 28 | #include <linux/mutex.h> | ||
| 29 | #include <linux/delay.h> | ||
| 30 | #include <linux/dma-mapping.h> | ||
| 31 | #include <linux/workqueue.h> | ||
| 32 | #include <linux/ktime.h> | ||
| 33 | #include <linux/debugfs.h> | ||
| 34 | #include <linux/seq_file.h> | ||
| 35 | #include <linux/backlight.h> | ||
| 36 | #include <video/tegrafb.h> | ||
| 37 | #include <drm/drm_fixed.h> | ||
| 38 | #ifdef CONFIG_SWITCH | ||
| 39 | #include <linux/switch.h> | ||
| 40 | #endif | ||
| 41 | |||
| 42 | |||
| 43 | #include <mach/clk.h> | ||
| 44 | #include <mach/dc.h> | ||
| 45 | #include <mach/fb.h> | ||
| 46 | #include <mach/mc.h> | ||
| 47 | #include <linux/nvhost.h> | ||
| 48 | #include <mach/latency_allowance.h> | ||
| 49 | |||
| 50 | #include "dc_reg.h" | ||
| 51 | #include "dc_priv.h" | ||
| 52 | #include "nvsd.h" | ||
| 53 | |||
| 54 | #define TEGRA_CRC_LATCHED_DELAY 34 | ||
| 55 | |||
| 56 | #define DC_COM_PIN_OUTPUT_POLARITY1_INIT_VAL 0x01000000 | ||
| 57 | #define DC_COM_PIN_OUTPUT_POLARITY3_INIT_VAL 0x0 | ||
| 58 | |||
| 59 | #ifndef CONFIG_TEGRA_FPGA_PLATFORM | ||
| 60 | #define ALL_UF_INT (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT) | ||
| 61 | #else | ||
| 62 | /* ignore underflows when on simulation and fpga platform */ | ||
| 63 | #define ALL_UF_INT (0) | ||
| 64 | #endif | ||
| 65 | |||
| 66 | static int no_vsync; | ||
| 67 | |||
| 68 | static void _tegra_dc_controller_disable(struct tegra_dc *dc); | ||
| 69 | |||
| 70 | module_param_named(no_vsync, no_vsync, int, S_IRUGO | S_IWUSR); | ||
| 71 | |||
| 72 | static int use_dynamic_emc = 1; | ||
| 73 | |||
| 74 | module_param_named(use_dynamic_emc, use_dynamic_emc, int, S_IRUGO | S_IWUSR); | ||
| 75 | |||
| 76 | struct tegra_dc *tegra_dcs[TEGRA_MAX_DC]; | ||
| 77 | |||
| 78 | DEFINE_MUTEX(tegra_dc_lock); | ||
| 79 | DEFINE_MUTEX(shared_lock); | ||
| 80 | |||
| 81 | static const struct { | ||
| 82 | bool h; | ||
| 83 | bool v; | ||
| 84 | } can_filter[] = { | ||
| 85 | /* Window A has no filtering */ | ||
| 86 | { false, false }, | ||
| 87 | /* Window B has both H and V filtering */ | ||
| 88 | { true, true }, | ||
| 89 | /* Window C has only H filtering */ | ||
| 90 | { false, true }, | ||
| 91 | }; | ||
| 92 | static inline bool win_use_v_filter(const struct tegra_dc_win *win) | ||
| 93 | { | ||
| 94 | return can_filter[win->idx].v && | ||
| 95 | win->h.full != dfixed_const(win->out_h); | ||
| 96 | } | ||
| 97 | static inline bool win_use_h_filter(const struct tegra_dc_win *win) | ||
| 98 | { | ||
| 99 | return can_filter[win->idx].h && | ||
| 100 | win->w.full != dfixed_const(win->out_w); | ||
| 101 | } | ||
| 102 | |||
| 103 | static inline int tegra_dc_fmt_bpp(int fmt) | ||
| 104 | { | ||
| 105 | switch (fmt) { | ||
| 106 | case TEGRA_WIN_FMT_P1: | ||
| 107 | return 1; | ||
| 108 | |||
| 109 | case TEGRA_WIN_FMT_P2: | ||
| 110 | return 2; | ||
| 111 | |||
| 112 | case TEGRA_WIN_FMT_P4: | ||
| 113 | return 4; | ||
| 114 | |||
| 115 | case TEGRA_WIN_FMT_P8: | ||
| 116 | return 8; | ||
| 117 | |||
| 118 | case TEGRA_WIN_FMT_B4G4R4A4: | ||
| 119 | case TEGRA_WIN_FMT_B5G5R5A: | ||
| 120 | case TEGRA_WIN_FMT_B5G6R5: | ||
| 121 | case TEGRA_WIN_FMT_AB5G5R5: | ||
| 122 | return 16; | ||
| 123 | |||
| 124 | case TEGRA_WIN_FMT_B8G8R8A8: | ||
| 125 | case TEGRA_WIN_FMT_R8G8B8A8: | ||
| 126 | case TEGRA_WIN_FMT_B6x2G6x2R6x2A8: | ||
| 127 | case TEGRA_WIN_FMT_R6x2G6x2B6x2A8: | ||
| 128 | return 32; | ||
| 129 | |||
| 130 | /* for planar formats, size of the Y plane, 8bit */ | ||
| 131 | case TEGRA_WIN_FMT_YCbCr420P: | ||
| 132 | case TEGRA_WIN_FMT_YUV420P: | ||
| 133 | case TEGRA_WIN_FMT_YCbCr422P: | ||
| 134 | case TEGRA_WIN_FMT_YUV422P: | ||
| 135 | case TEGRA_WIN_FMT_YCbCr422R: | ||
| 136 | case TEGRA_WIN_FMT_YUV422R: | ||
| 137 | case TEGRA_WIN_FMT_YCbCr422RA: | ||
| 138 | case TEGRA_WIN_FMT_YUV422RA: | ||
| 139 | return 8; | ||
| 140 | |||
| 141 | case TEGRA_WIN_FMT_YCbCr422: | ||
| 142 | case TEGRA_WIN_FMT_YUV422: | ||
| 143 | /* FIXME: need to know the bpp of these formats */ | ||
| 144 | return 0; | ||
| 145 | } | ||
| 146 | return 0; | ||
| 147 | } | ||
| 148 | |||
| 149 | static inline bool tegra_dc_is_yuv_planar(int fmt) | ||
| 150 | { | ||
| 151 | switch (fmt) { | ||
| 152 | case TEGRA_WIN_FMT_YUV420P: | ||
| 153 | case TEGRA_WIN_FMT_YCbCr420P: | ||
| 154 | case TEGRA_WIN_FMT_YCbCr422P: | ||
| 155 | case TEGRA_WIN_FMT_YUV422P: | ||
| 156 | case TEGRA_WIN_FMT_YCbCr422R: | ||
| 157 | case TEGRA_WIN_FMT_YUV422R: | ||
| 158 | case TEGRA_WIN_FMT_YCbCr422RA: | ||
| 159 | case TEGRA_WIN_FMT_YUV422RA: | ||
| 160 | return true; | ||
| 161 | } | ||
| 162 | return false; | ||
| 163 | } | ||
| 164 | |||
| 165 | #define DUMP_REG(a) do { \ | ||
| 166 | snprintf(buff, sizeof(buff), "%-32s\t%03x\t%08lx\n", \ | ||
| 167 | #a, a, tegra_dc_readl(dc, a)); \ | ||
| 168 | print(data, buff); \ | ||
| 169 | } while (0) | ||
| 170 | |||
| 171 | static void _dump_regs(struct tegra_dc *dc, void *data, | ||
| 172 | void (* print)(void *data, const char *str)) | ||
| 173 | { | ||
| 174 | int i; | ||
| 175 | char buff[256]; | ||
| 176 | |||
| 177 | tegra_dc_io_start(dc); | ||
| 178 | clk_enable(dc->clk); | ||
| 179 | |||
| 180 | DUMP_REG(DC_CMD_DISPLAY_COMMAND_OPTION0); | ||
| 181 | DUMP_REG(DC_CMD_DISPLAY_COMMAND); | ||
| 182 | DUMP_REG(DC_CMD_SIGNAL_RAISE); | ||
| 183 | DUMP_REG(DC_CMD_INT_STATUS); | ||
| 184 | DUMP_REG(DC_CMD_INT_MASK); | ||
| 185 | DUMP_REG(DC_CMD_INT_ENABLE); | ||
| 186 | DUMP_REG(DC_CMD_INT_TYPE); | ||
| 187 | DUMP_REG(DC_CMD_INT_POLARITY); | ||
| 188 | DUMP_REG(DC_CMD_SIGNAL_RAISE1); | ||
| 189 | DUMP_REG(DC_CMD_SIGNAL_RAISE2); | ||
| 190 | DUMP_REG(DC_CMD_SIGNAL_RAISE3); | ||
| 191 | DUMP_REG(DC_CMD_STATE_ACCESS); | ||
| 192 | DUMP_REG(DC_CMD_STATE_CONTROL); | ||
| 193 | DUMP_REG(DC_CMD_DISPLAY_WINDOW_HEADER); | ||
| 194 | DUMP_REG(DC_CMD_REG_ACT_CONTROL); | ||
| 195 | |||
| 196 | DUMP_REG(DC_DISP_DISP_SIGNAL_OPTIONS0); | ||
| 197 | DUMP_REG(DC_DISP_DISP_SIGNAL_OPTIONS1); | ||
| 198 | DUMP_REG(DC_DISP_DISP_WIN_OPTIONS); | ||
| 199 | DUMP_REG(DC_DISP_MEM_HIGH_PRIORITY); | ||
| 200 | DUMP_REG(DC_DISP_MEM_HIGH_PRIORITY_TIMER); | ||
| 201 | DUMP_REG(DC_DISP_DISP_TIMING_OPTIONS); | ||
| 202 | DUMP_REG(DC_DISP_REF_TO_SYNC); | ||
| 203 | DUMP_REG(DC_DISP_SYNC_WIDTH); | ||
| 204 | DUMP_REG(DC_DISP_BACK_PORCH); | ||
| 205 | DUMP_REG(DC_DISP_DISP_ACTIVE); | ||
| 206 | DUMP_REG(DC_DISP_FRONT_PORCH); | ||
| 207 | DUMP_REG(DC_DISP_H_PULSE0_CONTROL); | ||
| 208 | DUMP_REG(DC_DISP_H_PULSE0_POSITION_A); | ||
| 209 | DUMP_REG(DC_DISP_H_PULSE0_POSITION_B); | ||
| 210 | DUMP_REG(DC_DISP_H_PULSE0_POSITION_C); | ||
| 211 | DUMP_REG(DC_DISP_H_PULSE0_POSITION_D); | ||
| 212 | DUMP_REG(DC_DISP_H_PULSE1_CONTROL); | ||
| 213 | DUMP_REG(DC_DISP_H_PULSE1_POSITION_A); | ||
| 214 | DUMP_REG(DC_DISP_H_PULSE1_POSITION_B); | ||
| 215 | DUMP_REG(DC_DISP_H_PULSE1_POSITION_C); | ||
| 216 | DUMP_REG(DC_DISP_H_PULSE1_POSITION_D); | ||
| 217 | DUMP_REG(DC_DISP_H_PULSE2_CONTROL); | ||
| 218 | DUMP_REG(DC_DISP_H_PULSE2_POSITION_A); | ||
| 219 | DUMP_REG(DC_DISP_H_PULSE2_POSITION_B); | ||
| 220 | DUMP_REG(DC_DISP_H_PULSE2_POSITION_C); | ||
| 221 | DUMP_REG(DC_DISP_H_PULSE2_POSITION_D); | ||
| 222 | DUMP_REG(DC_DISP_V_PULSE0_CONTROL); | ||
| 223 | DUMP_REG(DC_DISP_V_PULSE0_POSITION_A); | ||
| 224 | DUMP_REG(DC_DISP_V_PULSE0_POSITION_B); | ||
| 225 | DUMP_REG(DC_DISP_V_PULSE0_POSITION_C); | ||
| 226 | DUMP_REG(DC_DISP_V_PULSE1_CONTROL); | ||
| 227 | DUMP_REG(DC_DISP_V_PULSE1_POSITION_A); | ||
| 228 | DUMP_REG(DC_DISP_V_PULSE1_POSITION_B); | ||
| 229 | DUMP_REG(DC_DISP_V_PULSE1_POSITION_C); | ||
| 230 | DUMP_REG(DC_DISP_V_PULSE2_CONTROL); | ||
| 231 | DUMP_REG(DC_DISP_V_PULSE2_POSITION_A); | ||
| 232 | DUMP_REG(DC_DISP_V_PULSE3_CONTROL); | ||
| 233 | DUMP_REG(DC_DISP_V_PULSE3_POSITION_A); | ||
| 234 | DUMP_REG(DC_DISP_M0_CONTROL); | ||
| 235 | DUMP_REG(DC_DISP_M1_CONTROL); | ||
| 236 | DUMP_REG(DC_DISP_DI_CONTROL); | ||
| 237 | DUMP_REG(DC_DISP_PP_CONTROL); | ||
| 238 | DUMP_REG(DC_DISP_PP_SELECT_A); | ||
| 239 | DUMP_REG(DC_DISP_PP_SELECT_B); | ||
| 240 | DUMP_REG(DC_DISP_PP_SELECT_C); | ||
| 241 | DUMP_REG(DC_DISP_PP_SELECT_D); | ||
| 242 | DUMP_REG(DC_DISP_DISP_CLOCK_CONTROL); | ||
| 243 | DUMP_REG(DC_DISP_DISP_INTERFACE_CONTROL); | ||
| 244 | DUMP_REG(DC_DISP_DISP_COLOR_CONTROL); | ||
| 245 | DUMP_REG(DC_DISP_SHIFT_CLOCK_OPTIONS); | ||
| 246 | DUMP_REG(DC_DISP_DATA_ENABLE_OPTIONS); | ||
| 247 | DUMP_REG(DC_DISP_SERIAL_INTERFACE_OPTIONS); | ||
| 248 | DUMP_REG(DC_DISP_LCD_SPI_OPTIONS); | ||
| 249 | DUMP_REG(DC_DISP_BORDER_COLOR); | ||
| 250 | DUMP_REG(DC_DISP_COLOR_KEY0_LOWER); | ||
| 251 | DUMP_REG(DC_DISP_COLOR_KEY0_UPPER); | ||
| 252 | DUMP_REG(DC_DISP_COLOR_KEY1_LOWER); | ||
| 253 | DUMP_REG(DC_DISP_COLOR_KEY1_UPPER); | ||
| 254 | DUMP_REG(DC_DISP_CURSOR_FOREGROUND); | ||
| 255 | DUMP_REG(DC_DISP_CURSOR_BACKGROUND); | ||
| 256 | DUMP_REG(DC_DISP_CURSOR_START_ADDR); | ||
| 257 | DUMP_REG(DC_DISP_CURSOR_START_ADDR_NS); | ||
| 258 | DUMP_REG(DC_DISP_CURSOR_POSITION); | ||
| 259 | DUMP_REG(DC_DISP_CURSOR_POSITION_NS); | ||
| 260 | DUMP_REG(DC_DISP_INIT_SEQ_CONTROL); | ||
| 261 | DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_A); | ||
| 262 | DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_B); | ||
| 263 | DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_C); | ||
| 264 | DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_D); | ||
| 265 | DUMP_REG(DC_DISP_DC_MCCIF_FIFOCTRL); | ||
| 266 | DUMP_REG(DC_DISP_MCCIF_DISPLAY0A_HYST); | ||
| 267 | DUMP_REG(DC_DISP_MCCIF_DISPLAY0B_HYST); | ||
| 268 | DUMP_REG(DC_DISP_MCCIF_DISPLAY0C_HYST); | ||
| 269 | DUMP_REG(DC_DISP_MCCIF_DISPLAY1B_HYST); | ||
| 270 | DUMP_REG(DC_DISP_DAC_CRT_CTRL); | ||
| 271 | DUMP_REG(DC_DISP_DISP_MISC_CONTROL); | ||
| 272 | |||
| 273 | |||
| 274 | for (i = 0; i < 3; i++) { | ||
| 275 | print(data, "\n"); | ||
| 276 | snprintf(buff, sizeof(buff), "WINDOW %c:\n", 'A' + i); | ||
| 277 | print(data, buff); | ||
| 278 | |||
| 279 | tegra_dc_writel(dc, WINDOW_A_SELECT << i, | ||
| 280 | DC_CMD_DISPLAY_WINDOW_HEADER); | ||
| 281 | DUMP_REG(DC_CMD_DISPLAY_WINDOW_HEADER); | ||
| 282 | DUMP_REG(DC_WIN_WIN_OPTIONS); | ||
| 283 | DUMP_REG(DC_WIN_BYTE_SWAP); | ||
| 284 | DUMP_REG(DC_WIN_BUFFER_CONTROL); | ||
| 285 | DUMP_REG(DC_WIN_COLOR_DEPTH); | ||
| 286 | DUMP_REG(DC_WIN_POSITION); | ||
| 287 | DUMP_REG(DC_WIN_SIZE); | ||
| 288 | DUMP_REG(DC_WIN_PRESCALED_SIZE); | ||
| 289 | DUMP_REG(DC_WIN_H_INITIAL_DDA); | ||
| 290 | DUMP_REG(DC_WIN_V_INITIAL_DDA); | ||
| 291 | DUMP_REG(DC_WIN_DDA_INCREMENT); | ||
| 292 | DUMP_REG(DC_WIN_LINE_STRIDE); | ||
| 293 | DUMP_REG(DC_WIN_BUF_STRIDE); | ||
| 294 | DUMP_REG(DC_WIN_UV_BUF_STRIDE); | ||
| 295 | DUMP_REG(DC_WIN_BLEND_NOKEY); | ||
| 296 | DUMP_REG(DC_WIN_BLEND_1WIN); | ||
| 297 | DUMP_REG(DC_WIN_BLEND_2WIN_X); | ||
| 298 | DUMP_REG(DC_WIN_BLEND_2WIN_Y); | ||
| 299 | DUMP_REG(DC_WIN_BLEND_3WIN_XY); | ||
| 300 | DUMP_REG(DC_WINBUF_START_ADDR); | ||
| 301 | DUMP_REG(DC_WINBUF_START_ADDR_U); | ||
| 302 | DUMP_REG(DC_WINBUF_START_ADDR_V); | ||
| 303 | DUMP_REG(DC_WINBUF_ADDR_H_OFFSET); | ||
| 304 | DUMP_REG(DC_WINBUF_ADDR_V_OFFSET); | ||
| 305 | DUMP_REG(DC_WINBUF_UFLOW_STATUS); | ||
| 306 | DUMP_REG(DC_WIN_CSC_YOF); | ||
| 307 | DUMP_REG(DC_WIN_CSC_KYRGB); | ||
| 308 | DUMP_REG(DC_WIN_CSC_KUR); | ||
| 309 | DUMP_REG(DC_WIN_CSC_KVR); | ||
| 310 | DUMP_REG(DC_WIN_CSC_KUG); | ||
| 311 | DUMP_REG(DC_WIN_CSC_KVG); | ||
| 312 | DUMP_REG(DC_WIN_CSC_KUB); | ||
| 313 | DUMP_REG(DC_WIN_CSC_KVB); | ||
| 314 | } | ||
| 315 | |||
| 316 | DUMP_REG(DC_CMD_DISPLAY_POWER_CONTROL); | ||
| 317 | DUMP_REG(DC_COM_PIN_OUTPUT_ENABLE2); | ||
| 318 | DUMP_REG(DC_COM_PIN_OUTPUT_POLARITY2); | ||
| 319 | DUMP_REG(DC_COM_PIN_OUTPUT_DATA2); | ||
| 320 | DUMP_REG(DC_COM_PIN_INPUT_ENABLE2); | ||
| 321 | DUMP_REG(DC_COM_PIN_OUTPUT_SELECT5); | ||
| 322 | DUMP_REG(DC_DISP_DISP_SIGNAL_OPTIONS0); | ||
| 323 | DUMP_REG(DC_DISP_M1_CONTROL); | ||
| 324 | DUMP_REG(DC_COM_PM1_CONTROL); | ||
| 325 | DUMP_REG(DC_COM_PM1_DUTY_CYCLE); | ||
| 326 | DUMP_REG(DC_DISP_SD_CONTROL); | ||
| 327 | |||
| 328 | clk_disable(dc->clk); | ||
| 329 | tegra_dc_io_end(dc); | ||
| 330 | } | ||
| 331 | |||
| 332 | #undef DUMP_REG | ||
| 333 | |||
| 334 | #ifdef DEBUG | ||
| 335 | static void dump_regs_print(void *data, const char *str) | ||
| 336 | { | ||
| 337 | struct tegra_dc *dc = data; | ||
| 338 | dev_dbg(&dc->ndev->dev, "%s", str); | ||
| 339 | } | ||
| 340 | |||
| 341 | static void dump_regs(struct tegra_dc *dc) | ||
| 342 | { | ||
| 343 | _dump_regs(dc, dc, dump_regs_print); | ||
| 344 | } | ||
| 345 | #else /* !DEBUG */ | ||
| 346 | |||
| 347 | static void dump_regs(struct tegra_dc *dc) {} | ||
| 348 | |||
| 349 | #endif /* DEBUG */ | ||
| 350 | |||
| 351 | #ifdef CONFIG_DEBUG_FS | ||
| 352 | |||
| 353 | static void dbg_regs_print(void *data, const char *str) | ||
| 354 | { | ||
| 355 | struct seq_file *s = data; | ||
| 356 | |||
| 357 | seq_printf(s, "%s", str); | ||
| 358 | } | ||
| 359 | |||
| 360 | #undef DUMP_REG | ||
| 361 | |||
| 362 | static int dbg_dc_show(struct seq_file *s, void *unused) | ||
| 363 | { | ||
| 364 | struct tegra_dc *dc = s->private; | ||
| 365 | |||
| 366 | _dump_regs(dc, s, dbg_regs_print); | ||
| 367 | |||
| 368 | return 0; | ||
| 369 | } | ||
| 370 | |||
| 371 | |||
| 372 | static int dbg_dc_open(struct inode *inode, struct file *file) | ||
| 373 | { | ||
| 374 | return single_open(file, dbg_dc_show, inode->i_private); | ||
| 375 | } | ||
| 376 | |||
| 377 | static const struct file_operations regs_fops = { | ||
| 378 | .open = dbg_dc_open, | ||
| 379 | .read = seq_read, | ||
| 380 | .llseek = seq_lseek, | ||
| 381 | .release = single_release, | ||
| 382 | }; | ||
| 383 | |||
| 384 | static int dbg_dc_mode_show(struct seq_file *s, void *unused) | ||
| 385 | { | ||
| 386 | struct tegra_dc *dc = s->private; | ||
| 387 | struct tegra_dc_mode *m; | ||
| 388 | |||
| 389 | mutex_lock(&dc->lock); | ||
| 390 | m = &dc->mode; | ||
| 391 | seq_printf(s, | ||
| 392 | "pclk: %d\n" | ||
| 393 | "h_ref_to_sync: %d\n" | ||
| 394 | "v_ref_to_sync: %d\n" | ||
| 395 | "h_sync_width: %d\n" | ||
| 396 | "v_sync_width: %d\n" | ||
| 397 | "h_back_porch: %d\n" | ||
| 398 | "v_back_porch: %d\n" | ||
| 399 | "h_active: %d\n" | ||
| 400 | "v_active: %d\n" | ||
| 401 | "h_front_porch: %d\n" | ||
| 402 | "v_front_porch: %d\n" | ||
| 403 | "stereo_mode: %d\n", | ||
| 404 | m->pclk, m->h_ref_to_sync, m->v_ref_to_sync, | ||
| 405 | m->h_sync_width, m->v_sync_width, | ||
| 406 | m->h_back_porch, m->v_back_porch, | ||
| 407 | m->h_active, m->v_active, | ||
| 408 | m->h_front_porch, m->v_front_porch, | ||
| 409 | m->stereo_mode); | ||
| 410 | mutex_unlock(&dc->lock); | ||
| 411 | return 0; | ||
| 412 | } | ||
| 413 | |||
| 414 | static int dbg_dc_mode_open(struct inode *inode, struct file *file) | ||
| 415 | { | ||
| 416 | return single_open(file, dbg_dc_mode_show, inode->i_private); | ||
| 417 | } | ||
| 418 | |||
| 419 | static const struct file_operations mode_fops = { | ||
| 420 | .open = dbg_dc_mode_open, | ||
| 421 | .read = seq_read, | ||
| 422 | .llseek = seq_lseek, | ||
| 423 | .release = single_release, | ||
| 424 | }; | ||
| 425 | |||
| 426 | static int dbg_dc_stats_show(struct seq_file *s, void *unused) | ||
| 427 | { | ||
| 428 | struct tegra_dc *dc = s->private; | ||
| 429 | |||
| 430 | mutex_lock(&dc->lock); | ||
| 431 | seq_printf(s, | ||
| 432 | "underflows: %llu\n" | ||
| 433 | "underflows_a: %llu\n" | ||
| 434 | "underflows_b: %llu\n" | ||
| 435 | "underflows_c: %llu\n", | ||
| 436 | dc->stats.underflows, | ||
| 437 | dc->stats.underflows_a, | ||
| 438 | dc->stats.underflows_b, | ||
| 439 | dc->stats.underflows_c); | ||
| 440 | mutex_unlock(&dc->lock); | ||
| 441 | |||
| 442 | return 0; | ||
| 443 | } | ||
| 444 | |||
| 445 | static int dbg_dc_stats_open(struct inode *inode, struct file *file) | ||
| 446 | { | ||
| 447 | return single_open(file, dbg_dc_stats_show, inode->i_private); | ||
| 448 | } | ||
| 449 | |||
| 450 | static const struct file_operations stats_fops = { | ||
| 451 | .open = dbg_dc_stats_open, | ||
| 452 | .read = seq_read, | ||
| 453 | .llseek = seq_lseek, | ||
| 454 | .release = single_release, | ||
| 455 | }; | ||
| 456 | |||
| 457 | static void __devexit tegra_dc_remove_debugfs(struct tegra_dc *dc) | ||
| 458 | { | ||
| 459 | if (dc->debugdir) | ||
| 460 | debugfs_remove_recursive(dc->debugdir); | ||
| 461 | dc->debugdir = NULL; | ||
| 462 | } | ||
| 463 | |||
| 464 | static void tegra_dc_create_debugfs(struct tegra_dc *dc) | ||
| 465 | { | ||
| 466 | struct dentry *retval; | ||
| 467 | |||
| 468 | dc->debugdir = debugfs_create_dir(dev_name(&dc->ndev->dev), NULL); | ||
| 469 | if (!dc->debugdir) | ||
| 470 | goto remove_out; | ||
| 471 | |||
| 472 | retval = debugfs_create_file("regs", S_IRUGO, dc->debugdir, dc, | ||
| 473 | ®s_fops); | ||
| 474 | if (!retval) | ||
| 475 | goto remove_out; | ||
| 476 | |||
| 477 | retval = debugfs_create_file("mode", S_IRUGO, dc->debugdir, dc, | ||
| 478 | &mode_fops); | ||
| 479 | if (!retval) | ||
| 480 | goto remove_out; | ||
| 481 | |||
| 482 | retval = debugfs_create_file("stats", S_IRUGO, dc->debugdir, dc, | ||
| 483 | &stats_fops); | ||
| 484 | if (!retval) | ||
| 485 | goto remove_out; | ||
| 486 | |||
| 487 | return; | ||
| 488 | remove_out: | ||
| 489 | dev_err(&dc->ndev->dev, "could not create debugfs\n"); | ||
| 490 | tegra_dc_remove_debugfs(dc); | ||
| 491 | } | ||
| 492 | |||
| 493 | #else /* !CONFIG_DEBUGFS */ | ||
| 494 | static inline void tegra_dc_create_debugfs(struct tegra_dc *dc) { }; | ||
| 495 | static inline void __devexit tegra_dc_remove_debugfs(struct tegra_dc *dc) { }; | ||
| 496 | #endif /* CONFIG_DEBUGFS */ | ||
| 497 | |||
| 498 | static int tegra_dc_set(struct tegra_dc *dc, int index) | ||
| 499 | { | ||
| 500 | int ret = 0; | ||
| 501 | |||
| 502 | mutex_lock(&tegra_dc_lock); | ||
| 503 | if (index >= TEGRA_MAX_DC) { | ||
| 504 | ret = -EINVAL; | ||
| 505 | goto out; | ||
| 506 | } | ||
| 507 | |||
| 508 | if (dc != NULL && tegra_dcs[index] != NULL) { | ||
| 509 | ret = -EBUSY; | ||
| 510 | goto out; | ||
| 511 | } | ||
| 512 | |||
| 513 | tegra_dcs[index] = dc; | ||
| 514 | |||
| 515 | out: | ||
| 516 | mutex_unlock(&tegra_dc_lock); | ||
| 517 | |||
| 518 | return ret; | ||
| 519 | } | ||
| 520 | |||
| 521 | static unsigned int tegra_dc_has_multiple_dc(void) | ||
| 522 | { | ||
| 523 | unsigned int idx; | ||
| 524 | unsigned int cnt = 0; | ||
| 525 | struct tegra_dc *dc; | ||
| 526 | |||
| 527 | mutex_lock(&tegra_dc_lock); | ||
| 528 | for (idx = 0; idx < TEGRA_MAX_DC; idx++) | ||
| 529 | cnt += ((dc = tegra_dcs[idx]) != NULL && dc->enabled) ? 1 : 0; | ||
| 530 | mutex_unlock(&tegra_dc_lock); | ||
| 531 | |||
| 532 | return (cnt > 1); | ||
| 533 | } | ||
| 534 | |||
| 535 | struct tegra_dc *tegra_dc_get_dc(unsigned idx) | ||
| 536 | { | ||
| 537 | if (idx < TEGRA_MAX_DC) | ||
| 538 | return tegra_dcs[idx]; | ||
| 539 | else | ||
| 540 | return NULL; | ||
| 541 | } | ||
| 542 | EXPORT_SYMBOL(tegra_dc_get_dc); | ||
| 543 | |||
| 544 | struct tegra_dc_win *tegra_dc_get_window(struct tegra_dc *dc, unsigned win) | ||
| 545 | { | ||
| 546 | if (win >= dc->n_windows) | ||
| 547 | return NULL; | ||
| 548 | |||
| 549 | return &dc->windows[win]; | ||
| 550 | } | ||
| 551 | EXPORT_SYMBOL(tegra_dc_get_window); | ||
| 552 | |||
| 553 | static int get_topmost_window(u32 *depths, unsigned long *wins) | ||
| 554 | { | ||
| 555 | int idx, best = -1; | ||
| 556 | |||
| 557 | for_each_set_bit(idx, wins, DC_N_WINDOWS) { | ||
| 558 | if (best == -1 || depths[idx] < depths[best]) | ||
| 559 | best = idx; | ||
| 560 | } | ||
| 561 | clear_bit(best, wins); | ||
| 562 | return best; | ||
| 563 | } | ||
| 564 | |||
| 565 | bool tegra_dc_get_connected(struct tegra_dc *dc) | ||
| 566 | { | ||
| 567 | return dc->connected; | ||
| 568 | } | ||
| 569 | EXPORT_SYMBOL(tegra_dc_get_connected); | ||
| 570 | |||
| 571 | static u32 blend_topwin(u32 flags) | ||
| 572 | { | ||
| 573 | if (flags & TEGRA_WIN_FLAG_BLEND_COVERAGE) | ||
| 574 | return BLEND(NOKEY, ALPHA, 0xff, 0xff); | ||
| 575 | else if (flags & TEGRA_WIN_FLAG_BLEND_PREMULT) | ||
| 576 | return BLEND(NOKEY, PREMULT, 0xff, 0xff); | ||
| 577 | else | ||
| 578 | return BLEND(NOKEY, FIX, 0xff, 0xff); | ||
| 579 | } | ||
| 580 | |||
| 581 | static u32 blend_2win(int idx, unsigned long behind_mask, u32* flags, int xy) | ||
| 582 | { | ||
| 583 | int other; | ||
| 584 | |||
| 585 | for (other = 0; other < DC_N_WINDOWS; other++) { | ||
| 586 | if (other != idx && (xy-- == 0)) | ||
| 587 | break; | ||
| 588 | } | ||
| 589 | if (BIT(other) & behind_mask) | ||
| 590 | return blend_topwin(flags[idx]); | ||
| 591 | else if (flags[other]) | ||
| 592 | return BLEND(NOKEY, DEPENDANT, 0x00, 0x00); | ||
| 593 | else | ||
| 594 | return BLEND(NOKEY, FIX, 0x00, 0x00); | ||
| 595 | } | ||
| 596 | |||
| 597 | static u32 blend_3win(int idx, unsigned long behind_mask, u32* flags) | ||
| 598 | { | ||
| 599 | unsigned long infront_mask; | ||
| 600 | int first; | ||
| 601 | |||
| 602 | infront_mask = ~(behind_mask | BIT(idx)); | ||
| 603 | infront_mask &= (BIT(DC_N_WINDOWS) - 1); | ||
| 604 | first = ffs(infront_mask) - 1; | ||
| 605 | |||
| 606 | if (!infront_mask) | ||
| 607 | return blend_topwin(flags[idx]); | ||
| 608 | else if (behind_mask && first != -1 && flags[first]) | ||
| 609 | return BLEND(NOKEY, DEPENDANT, 0x00, 0x00); | ||
| 610 | else | ||
| 611 | return BLEND(NOKEY, FIX, 0x0, 0x0); | ||
| 612 | } | ||
| 613 | |||
| 614 | static void tegra_dc_set_blending(struct tegra_dc *dc, struct tegra_dc_blend *blend) | ||
| 615 | { | ||
| 616 | unsigned long mask = BIT(DC_N_WINDOWS) - 1; | ||
| 617 | |||
| 618 | while (mask) { | ||
| 619 | int idx = get_topmost_window(blend->z, &mask); | ||
| 620 | |||
| 621 | tegra_dc_writel(dc, WINDOW_A_SELECT << idx, | ||
| 622 | DC_CMD_DISPLAY_WINDOW_HEADER); | ||
| 623 | tegra_dc_writel(dc, BLEND(NOKEY, FIX, 0xff, 0xff), | ||
| 624 | DC_WIN_BLEND_NOKEY); | ||
| 625 | tegra_dc_writel(dc, BLEND(NOKEY, FIX, 0xff, 0xff), | ||
| 626 | DC_WIN_BLEND_1WIN); | ||
| 627 | tegra_dc_writel(dc, blend_2win(idx, mask, blend->flags, 0), | ||
| 628 | DC_WIN_BLEND_2WIN_X); | ||
| 629 | tegra_dc_writel(dc, blend_2win(idx, mask, blend->flags, 1), | ||
| 630 | DC_WIN_BLEND_2WIN_Y); | ||
| 631 | tegra_dc_writel(dc, blend_3win(idx, mask, blend->flags), | ||
| 632 | DC_WIN_BLEND_3WIN_XY); | ||
| 633 | } | ||
| 634 | } | ||
| 635 | |||
| 636 | static void tegra_dc_init_csc_defaults(struct tegra_dc_csc *csc) | ||
| 637 | { | ||
| 638 | csc->yof = 0x00f0; | ||
| 639 | csc->kyrgb = 0x012a; | ||
| 640 | csc->kur = 0x0000; | ||
| 641 | csc->kvr = 0x0198; | ||
| 642 | csc->kug = 0x039b; | ||
| 643 | csc->kvg = 0x032f; | ||
| 644 | csc->kub = 0x0204; | ||
| 645 | csc->kvb = 0x0000; | ||
| 646 | } | ||
| 647 | |||
| 648 | static void tegra_dc_set_csc(struct tegra_dc *dc, struct tegra_dc_csc *csc) | ||
| 649 | { | ||
| 650 | tegra_dc_writel(dc, csc->yof, DC_WIN_CSC_YOF); | ||
| 651 | tegra_dc_writel(dc, csc->kyrgb, DC_WIN_CSC_KYRGB); | ||
| 652 | tegra_dc_writel(dc, csc->kur, DC_WIN_CSC_KUR); | ||
| 653 | tegra_dc_writel(dc, csc->kvr, DC_WIN_CSC_KVR); | ||
| 654 | tegra_dc_writel(dc, csc->kug, DC_WIN_CSC_KUG); | ||
| 655 | tegra_dc_writel(dc, csc->kvg, DC_WIN_CSC_KVG); | ||
| 656 | tegra_dc_writel(dc, csc->kub, DC_WIN_CSC_KUB); | ||
| 657 | tegra_dc_writel(dc, csc->kvb, DC_WIN_CSC_KVB); | ||
| 658 | } | ||
| 659 | |||
| 660 | int tegra_dc_update_csc(struct tegra_dc *dc, int win_idx) | ||
| 661 | { | ||
| 662 | mutex_lock(&dc->lock); | ||
| 663 | |||
| 664 | if (!dc->enabled) { | ||
| 665 | mutex_unlock(&dc->lock); | ||
| 666 | return -EFAULT; | ||
| 667 | } | ||
| 668 | |||
| 669 | tegra_dc_writel(dc, WINDOW_A_SELECT << win_idx, | ||
| 670 | DC_CMD_DISPLAY_WINDOW_HEADER); | ||
| 671 | |||
| 672 | tegra_dc_set_csc(dc, &dc->windows[win_idx].csc); | ||
| 673 | |||
| 674 | mutex_unlock(&dc->lock); | ||
| 675 | |||
| 676 | return 0; | ||
| 677 | } | ||
| 678 | EXPORT_SYMBOL(tegra_dc_update_csc); | ||
| 679 | |||
| 680 | static void tegra_dc_init_lut_defaults(struct tegra_dc_lut *lut) | ||
| 681 | { | ||
| 682 | int i; | ||
| 683 | for (i = 0; i < 256; i++) | ||
| 684 | lut->r[i] = lut->g[i] = lut->b[i] = (u8)i; | ||
| 685 | } | ||
| 686 | |||
| 687 | static int tegra_dc_loop_lut(struct tegra_dc *dc, | ||
| 688 | struct tegra_dc_win *win, | ||
| 689 | int(*lambda)(struct tegra_dc *dc, int i, u32 rgb)) | ||
| 690 | { | ||
| 691 | struct tegra_dc_lut *lut = &win->lut; | ||
| 692 | struct tegra_dc_lut *global_lut = &dc->fb_lut; | ||
| 693 | int i; | ||
| 694 | for (i = 0; i < 256; i++) { | ||
| 695 | |||
| 696 | u32 r = (u32)lut->r[i]; | ||
| 697 | u32 g = (u32)lut->g[i]; | ||
| 698 | u32 b = (u32)lut->b[i]; | ||
| 699 | |||
| 700 | if (!(win->ppflags & TEGRA_WIN_PPFLAG_CP_FBOVERRIDE)) { | ||
| 701 | r = (u32)global_lut->r[r]; | ||
| 702 | g = (u32)global_lut->g[g]; | ||
| 703 | b = (u32)global_lut->b[b]; | ||
| 704 | } | ||
| 705 | |||
| 706 | if (!lambda(dc, i, r | (g<<8) | (b<<16))) | ||
| 707 | return 0; | ||
| 708 | } | ||
| 709 | return 1; | ||
| 710 | } | ||
| 711 | |||
| 712 | static int tegra_dc_lut_isdefaults_lambda(struct tegra_dc *dc, int i, u32 rgb) | ||
| 713 | { | ||
| 714 | if (rgb != (i | (i<<8) | (i<<16))) | ||
| 715 | return 0; | ||
| 716 | return 1; | ||
| 717 | } | ||
| 718 | |||
| 719 | static int tegra_dc_set_lut_setreg_lambda(struct tegra_dc *dc, int i, u32 rgb) | ||
| 720 | { | ||
| 721 | tegra_dc_writel(dc, rgb, DC_WIN_COLOR_PALETTE(i)); | ||
| 722 | return 1; | ||
| 723 | } | ||
| 724 | |||
| 725 | static void tegra_dc_set_lut(struct tegra_dc *dc, struct tegra_dc_win* win) | ||
| 726 | { | ||
| 727 | unsigned long val = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS); | ||
| 728 | |||
| 729 | tegra_dc_loop_lut(dc, win, tegra_dc_set_lut_setreg_lambda); | ||
| 730 | |||
| 731 | if (win->ppflags & TEGRA_WIN_PPFLAG_CP_ENABLE) | ||
| 732 | val |= CP_ENABLE; | ||
| 733 | else | ||
| 734 | val &= ~CP_ENABLE; | ||
| 735 | |||
| 736 | tegra_dc_writel(dc, val, DC_WIN_WIN_OPTIONS); | ||
| 737 | } | ||
| 738 | |||
| 739 | static int tegra_dc_update_winlut(struct tegra_dc *dc, int win_idx, int fbovr) | ||
| 740 | { | ||
| 741 | struct tegra_dc_win *win = &dc->windows[win_idx]; | ||
| 742 | |||
| 743 | mutex_lock(&dc->lock); | ||
| 744 | |||
| 745 | if (!dc->enabled) { | ||
| 746 | mutex_unlock(&dc->lock); | ||
| 747 | return -EFAULT; | ||
| 748 | } | ||
| 749 | |||
| 750 | if (fbovr > 0) | ||
| 751 | win->ppflags |= TEGRA_WIN_PPFLAG_CP_FBOVERRIDE; | ||
| 752 | else if (fbovr == 0) | ||
| 753 | win->ppflags &= ~TEGRA_WIN_PPFLAG_CP_FBOVERRIDE; | ||
| 754 | |||
| 755 | if (!tegra_dc_loop_lut(dc, win, tegra_dc_lut_isdefaults_lambda)) | ||
| 756 | win->ppflags |= TEGRA_WIN_PPFLAG_CP_ENABLE; | ||
| 757 | else | ||
| 758 | win->ppflags &= ~TEGRA_WIN_PPFLAG_CP_ENABLE; | ||
| 759 | |||
| 760 | tegra_dc_writel(dc, WINDOW_A_SELECT << win_idx, | ||
| 761 | DC_CMD_DISPLAY_WINDOW_HEADER); | ||
| 762 | |||
| 763 | tegra_dc_set_lut(dc, win); | ||
| 764 | |||
| 765 | mutex_unlock(&dc->lock); | ||
| 766 | |||
| 767 | return 0; | ||
| 768 | } | ||
| 769 | |||
| 770 | int tegra_dc_update_lut(struct tegra_dc *dc, int win_idx, int fboveride) | ||
| 771 | { | ||
| 772 | if (win_idx > -1) | ||
| 773 | return tegra_dc_update_winlut(dc, win_idx, fboveride); | ||
| 774 | |||
| 775 | for (win_idx = 0; win_idx < DC_N_WINDOWS; win_idx++) { | ||
| 776 | int err = tegra_dc_update_winlut(dc, win_idx, fboveride); | ||
| 777 | if (err) | ||
| 778 | return err; | ||
| 779 | } | ||
| 780 | |||
| 781 | return 0; | ||
| 782 | } | ||
| 783 | EXPORT_SYMBOL(tegra_dc_update_lut); | ||
| 784 | |||
| 785 | static void tegra_dc_set_scaling_filter(struct tegra_dc *dc) | ||
| 786 | { | ||
| 787 | unsigned i; | ||
| 788 | unsigned v0 = 128; | ||
| 789 | unsigned v1 = 0; | ||
| 790 | /* linear horizontal and vertical filters */ | ||
| 791 | for (i = 0; i < 16; i++) { | ||
| 792 | tegra_dc_writel(dc, (v1 << 16) | (v0 << 8), | ||
| 793 | DC_WIN_H_FILTER_P(i)); | ||
| 794 | |||
| 795 | tegra_dc_writel(dc, v0, | ||
| 796 | DC_WIN_V_FILTER_P(i)); | ||
| 797 | v0 -= 8; | ||
| 798 | v1 += 8; | ||
| 799 | } | ||
| 800 | } | ||
| 801 | |||
| 802 | static void tegra_dc_set_latency_allowance(struct tegra_dc *dc, | ||
| 803 | struct tegra_dc_win *w) | ||
| 804 | { | ||
| 805 | /* windows A, B, C for first and second display */ | ||
| 806 | static const enum tegra_la_id la_id_tab[2][3] = { | ||
| 807 | /* first display */ | ||
| 808 | { TEGRA_LA_DISPLAY_0A, TEGRA_LA_DISPLAY_0B, | ||
| 809 | TEGRA_LA_DISPLAY_0C }, | ||
| 810 | /* second display */ | ||
| 811 | { TEGRA_LA_DISPLAY_0AB, TEGRA_LA_DISPLAY_0BB, | ||
| 812 | TEGRA_LA_DISPLAY_0CB }, | ||
| 813 | }; | ||
| 814 | /* window B V-filter tap for first and second display. */ | ||
| 815 | static const enum tegra_la_id vfilter_tab[2] = { | ||
| 816 | TEGRA_LA_DISPLAY_1B, TEGRA_LA_DISPLAY_1BB, | ||
| 817 | }; | ||
| 818 | unsigned long bw; | ||
| 819 | |||
| 820 | BUG_ON(dc->ndev->id >= ARRAY_SIZE(la_id_tab)); | ||
| 821 | BUG_ON(dc->ndev->id >= ARRAY_SIZE(vfilter_tab)); | ||
| 822 | BUG_ON(w->idx >= ARRAY_SIZE(*la_id_tab)); | ||
| 823 | |||
| 824 | bw = w->new_bandwidth; | ||
| 825 | |||
| 826 | /* tegra_dc_get_bandwidth() treats V filter windows as double | ||
| 827 | * bandwidth, but LA has a seperate client for V filter */ | ||
| 828 | if (w->idx == 1 && win_use_v_filter(w)) | ||
| 829 | bw /= 2; | ||
| 830 | |||
| 831 | /* our bandwidth is in bytes/sec, but LA takes MBps. | ||
| 832 | * round up bandwidth to 1MBps */ | ||
| 833 | bw = bw / 1000000 + 1; | ||
| 834 | |||
| 835 | #ifdef CONFIG_TEGRA_SILICON_PLATFORM | ||
| 836 | tegra_set_latency_allowance(la_id_tab[dc->ndev->id][w->idx], bw); | ||
| 837 | /* if window B, also set the 1B client for the 2-tap V filter. */ | ||
| 838 | if (w->idx == 1) | ||
| 839 | tegra_set_latency_allowance(vfilter_tab[dc->ndev->id], bw); | ||
| 840 | #endif | ||
| 841 | |||
| 842 | w->bandwidth = w->new_bandwidth; | ||
| 843 | } | ||
| 844 | |||
| 845 | static unsigned int tegra_dc_windows_is_overlapped(struct tegra_dc_win *a, | ||
| 846 | struct tegra_dc_win *b) | ||
| 847 | { | ||
| 848 | if (!WIN_IS_ENABLED(a) || !WIN_IS_ENABLED(b)) | ||
| 849 | return 0; | ||
| 850 | |||
| 851 | /* because memory access to load the fifo can overlap, only care | ||
| 852 | * if windows overlap vertically */ | ||
| 853 | return ((a->out_y + a->out_h > b->out_y) && (a->out_y <= b->out_y)) || | ||
| 854 | ((b->out_y + b->out_h > a->out_y) && (b->out_y <= a->out_y)); | ||
| 855 | } | ||
| 856 | |||
| 857 | static unsigned long tegra_dc_find_max_bandwidth(struct tegra_dc_win *wins[], | ||
| 858 | int n) | ||
| 859 | { | ||
| 860 | unsigned i; | ||
| 861 | unsigned j; | ||
| 862 | unsigned overlap_count; | ||
| 863 | unsigned max_bw = 0; | ||
| 864 | |||
| 865 | WARN_ONCE(n > 3, "Code assumes at most 3 windows, bandwidth is likely" | ||
| 866 | "inaccurate.\n"); | ||
| 867 | |||
| 868 | /* If we had a large number of windows, we would compute adjacency | ||
| 869 | * graph representing 2 window overlaps, find all cliques in the graph, | ||
| 870 | * assign bandwidth to each clique, and then select the clique with | ||
| 871 | * maximum bandwidth. But because we have at most 3 windows, | ||
| 872 | * implementing proper Bron-Kerbosh algorithm would be an overkill, | ||
| 873 | * brute force will suffice. | ||
| 874 | * | ||
| 875 | * Thus: find maximum bandwidth for either single or a pair of windows | ||
| 876 | * and count number of window pair overlaps. If there are three | ||
| 877 | * pairs, all 3 window overlap. | ||
| 878 | */ | ||
| 879 | |||
| 880 | overlap_count = 0; | ||
| 881 | for (i = 0; i < n; i++) { | ||
| 882 | unsigned int bw1; | ||
| 883 | |||
| 884 | if (wins[i] == NULL) | ||
| 885 | continue; | ||
| 886 | bw1 = wins[i]->new_bandwidth; | ||
| 887 | if (bw1 > max_bw) | ||
| 888 | /* Single window */ | ||
| 889 | max_bw = bw1; | ||
| 890 | |||
| 891 | for (j = i + 1; j < n; j++) { | ||
| 892 | if (wins[j] == NULL) | ||
| 893 | continue; | ||
| 894 | if (tegra_dc_windows_is_overlapped(wins[i], wins[j])) { | ||
| 895 | unsigned int bw2 = wins[j]->new_bandwidth; | ||
| 896 | if (bw1 + bw2 > max_bw) | ||
| 897 | /* Window pair overlaps */ | ||
| 898 | max_bw = bw1 + bw2; | ||
| 899 | overlap_count++; | ||
| 900 | } | ||
| 901 | } | ||
| 902 | } | ||
| 903 | |||
| 904 | if (overlap_count == 3) | ||
| 905 | /* All three windows overlap */ | ||
| 906 | max_bw = wins[0]->new_bandwidth + wins[1]->new_bandwidth + | ||
| 907 | wins[2]->new_bandwidth; | ||
| 908 | |||
| 909 | return max_bw; | ||
| 910 | } | ||
| 911 | |||
| 912 | /* | ||
| 913 | * Calculate peak EMC bandwidth for each enabled window = | ||
| 914 | * pixel_clock * win_bpp * (use_v_filter ? 2 : 1)) * H_scale_factor * | ||
| 915 | * (windows_tiling ? 2 : 1) | ||
| 916 | * | ||
| 917 | * | ||
| 918 | * note: | ||
| 919 | * (*) We use 2 tap V filter, so need double BW if use V filter | ||
| 920 | * (*) Tiling mode on T30 and DDR3 requires double BW | ||
| 921 | */ | ||
| 922 | static unsigned long tegra_dc_calc_win_bandwidth(struct tegra_dc *dc, | ||
| 923 | struct tegra_dc_win *w) | ||
| 924 | { | ||
| 925 | unsigned long ret; | ||
| 926 | int tiled_windows_bw_multiplier; | ||
| 927 | unsigned long bpp; | ||
| 928 | |||
| 929 | if (!WIN_IS_ENABLED(w)) | ||
| 930 | return 0; | ||
| 931 | |||
| 932 | if (dfixed_trunc(w->w) == 0 || dfixed_trunc(w->h) == 0 || | ||
| 933 | w->out_w == 0 || w->out_h == 0) | ||
| 934 | return 0; | ||
| 935 | |||
| 936 | tiled_windows_bw_multiplier = | ||
| 937 | tegra_mc_get_tiled_memory_bandwidth_multiplier(); | ||
| 938 | |||
| 939 | /* all of tegra's YUV formats(420 and 422) fetch 2 bytes per pixel, | ||
| 940 | * but the size reported by tegra_dc_fmt_bpp for the planar version | ||
| 941 | * is of the luma plane's size only. */ | ||
| 942 | bpp = tegra_dc_is_yuv_planar(w->fmt) ? | ||
| 943 | 2 * tegra_dc_fmt_bpp(w->fmt) : tegra_dc_fmt_bpp(w->fmt); | ||
| 944 | /* perform calculations with most significant bits of pixel clock | ||
| 945 | * to prevent overflow of long. */ | ||
| 946 | ret = (unsigned long)(dc->mode.pclk >> 16) * | ||
| 947 | bpp / 8 * | ||
| 948 | (win_use_v_filter(w) ? 2 : 1) * dfixed_trunc(w->w) / w->out_w * | ||
| 949 | (WIN_IS_TILED(w) ? tiled_windows_bw_multiplier : 1); | ||
| 950 | |||
| 951 | /* | ||
| 952 | * Assuming 48% efficiency: i.e. if we calculate we need 70MBps, we | ||
| 953 | * will request 147MBps from EMC. | ||
| 954 | */ | ||
| 955 | ret = ret * 2 + ret / 10; | ||
| 956 | |||
| 957 | /* if overflowed */ | ||
| 958 | if (ret > (1UL << 31)) | ||
| 959 | return ULONG_MAX; | ||
| 960 | |||
| 961 | return ret << 16; /* restore the scaling we did above */ | ||
| 962 | } | ||
| 963 | |||
| 964 | static unsigned long tegra_dc_get_bandwidth( | ||
| 965 | struct tegra_dc_win *windows[], int n) | ||
| 966 | { | ||
| 967 | int i; | ||
| 968 | |||
| 969 | BUG_ON(n > DC_N_WINDOWS); | ||
| 970 | |||
| 971 | /* emc rate and latency allowance both need to know per window | ||
| 972 | * bandwidths */ | ||
| 973 | for (i = 0; i < n; i++) { | ||
| 974 | struct tegra_dc_win *w = windows[i]; | ||
| 975 | if (w) | ||
| 976 | w->new_bandwidth = tegra_dc_calc_win_bandwidth(w->dc, w); | ||
| 977 | } | ||
| 978 | |||
| 979 | return tegra_dc_find_max_bandwidth(windows, n); | ||
| 980 | } | ||
| 981 | |||
| 982 | /* to save power, call when display memory clients would be idle */ | ||
| 983 | static void tegra_dc_clear_bandwidth(struct tegra_dc *dc) | ||
| 984 | { | ||
| 985 | if (tegra_is_clk_enabled(dc->emc_clk)) | ||
| 986 | clk_disable(dc->emc_clk); | ||
| 987 | dc->emc_clk_rate = 0; | ||
| 988 | } | ||
| 989 | |||
| 990 | static void tegra_dc_program_bandwidth(struct tegra_dc *dc) | ||
| 991 | { | ||
| 992 | unsigned i; | ||
| 993 | |||
| 994 | if (dc->emc_clk_rate != dc->new_emc_clk_rate) { | ||
| 995 | /* going from 0 to non-zero */ | ||
| 996 | if (!dc->emc_clk_rate && !tegra_is_clk_enabled(dc->emc_clk)) | ||
| 997 | clk_enable(dc->emc_clk); | ||
| 998 | |||
| 999 | dc->emc_clk_rate = dc->new_emc_clk_rate; | ||
| 1000 | clk_set_rate(dc->emc_clk, dc->emc_clk_rate); | ||
| 1001 | |||
| 1002 | if (!dc->new_emc_clk_rate) /* going from non-zero to 0 */ | ||
| 1003 | clk_disable(dc->emc_clk); | ||
| 1004 | } | ||
| 1005 | |||
| 1006 | for (i = 0; i < DC_N_WINDOWS; i++) { | ||
| 1007 | struct tegra_dc_win *w = &dc->windows[i]; | ||
| 1008 | if (w->bandwidth != w->new_bandwidth && w->new_bandwidth != 0) | ||
| 1009 | tegra_dc_set_latency_allowance(dc, w); | ||
| 1010 | } | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | static int tegra_dc_set_dynamic_emc(struct tegra_dc_win *windows[], int n) | ||
| 1014 | { | ||
| 1015 | unsigned long new_rate; | ||
| 1016 | struct tegra_dc *dc; | ||
| 1017 | |||
| 1018 | if (!use_dynamic_emc) | ||
| 1019 | return 0; | ||
| 1020 | |||
| 1021 | dc = windows[0]->dc; | ||
| 1022 | |||
| 1023 | /* calculate the new rate based on this POST */ | ||
| 1024 | new_rate = tegra_dc_get_bandwidth(windows, n); | ||
| 1025 | new_rate = EMC_BW_TO_FREQ(new_rate); | ||
| 1026 | |||
| 1027 | if (tegra_dc_has_multiple_dc()) | ||
| 1028 | new_rate = ULONG_MAX; | ||
| 1029 | |||
| 1030 | dc->new_emc_clk_rate = new_rate; | ||
| 1031 | |||
| 1032 | return 0; | ||
| 1033 | } | ||
| 1034 | |||
| 1035 | static inline u32 compute_dda_inc(fixed20_12 in, unsigned out_int, | ||
| 1036 | bool v, unsigned Bpp) | ||
| 1037 | { | ||
| 1038 | /* | ||
| 1039 | * min(round((prescaled_size_in_pixels - 1) * 0x1000 / | ||
| 1040 | * (post_scaled_size_in_pixels - 1)), MAX) | ||
| 1041 | * Where the value of MAX is as follows: | ||
| 1042 | * For V_DDA_INCREMENT: 15.0 (0xF000) | ||
| 1043 | * For H_DDA_INCREMENT: 4.0 (0x4000) for 4 Bytes/pix formats. | ||
| 1044 | * 8.0 (0x8000) for 2 Bytes/pix formats. | ||
| 1045 | */ | ||
| 1046 | |||
| 1047 | fixed20_12 out = dfixed_init(out_int); | ||
| 1048 | u32 dda_inc; | ||
| 1049 | int max; | ||
| 1050 | |||
| 1051 | if (v) { | ||
| 1052 | max = 15; | ||
| 1053 | } else { | ||
| 1054 | switch (Bpp) { | ||
| 1055 | default: | ||
| 1056 | WARN_ON_ONCE(1); | ||
| 1057 | /* fallthrough */ | ||
| 1058 | case 4: | ||
| 1059 | max = 4; | ||
| 1060 | break; | ||
| 1061 | case 2: | ||
| 1062 | max = 8; | ||
| 1063 | break; | ||
| 1064 | } | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | out.full = max_t(u32, out.full - dfixed_const(1), dfixed_const(1)); | ||
| 1068 | in.full -= dfixed_const(1); | ||
| 1069 | |||
| 1070 | dda_inc = dfixed_div(in, out); | ||
| 1071 | |||
| 1072 | dda_inc = min_t(u32, dda_inc, dfixed_const(max)); | ||
| 1073 | |||
| 1074 | return dda_inc; | ||
| 1075 | } | ||
| 1076 | |||
| 1077 | static inline u32 compute_initial_dda(fixed20_12 in) | ||
| 1078 | { | ||
| 1079 | return dfixed_frac(in); | ||
| 1080 | } | ||
| 1081 | |||
| 1082 | /* does not support updating windows on multiple dcs in one call */ | ||
| 1083 | int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n) | ||
| 1084 | { | ||
| 1085 | struct tegra_dc *dc; | ||
| 1086 | unsigned long update_mask = GENERAL_ACT_REQ; | ||
| 1087 | unsigned long val; | ||
| 1088 | bool update_blend = false; | ||
| 1089 | int i; | ||
| 1090 | |||
| 1091 | dc = windows[0]->dc; | ||
| 1092 | |||
| 1093 | if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) { | ||
| 1094 | /* Acquire one_shot_lock to avoid race condition between | ||
| 1095 | * cancellation of old delayed work and schedule of new | ||
| 1096 | * delayed work. */ | ||
| 1097 | mutex_lock(&dc->one_shot_lock); | ||
| 1098 | cancel_delayed_work_sync(&dc->one_shot_work); | ||
| 1099 | } | ||
| 1100 | mutex_lock(&dc->lock); | ||
| 1101 | |||
| 1102 | if (!dc->enabled) { | ||
| 1103 | mutex_unlock(&dc->lock); | ||
| 1104 | if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) | ||
| 1105 | mutex_unlock(&dc->one_shot_lock); | ||
| 1106 | return -EFAULT; | ||
| 1107 | } | ||
| 1108 | |||
| 1109 | if (no_vsync) | ||
| 1110 | tegra_dc_writel(dc, WRITE_MUX_ACTIVE | READ_MUX_ACTIVE, DC_CMD_STATE_ACCESS); | ||
| 1111 | else | ||
| 1112 | tegra_dc_writel(dc, WRITE_MUX_ASSEMBLY | READ_MUX_ASSEMBLY, DC_CMD_STATE_ACCESS); | ||
| 1113 | |||
| 1114 | for (i = 0; i < DC_N_WINDOWS; i++) { | ||
| 1115 | tegra_dc_writel(dc, WINDOW_A_SELECT << i, | ||
| 1116 | DC_CMD_DISPLAY_WINDOW_HEADER); | ||
| 1117 | tegra_dc_writel(dc, 0, DC_WIN_WIN_OPTIONS); | ||
| 1118 | if (!no_vsync) | ||
| 1119 | update_mask |= WIN_A_ACT_REQ << i; | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | for (i = 0; i < n; i++) { | ||
| 1123 | struct tegra_dc_win *win = windows[i]; | ||
| 1124 | unsigned h_dda; | ||
| 1125 | unsigned v_dda; | ||
| 1126 | fixed20_12 h_offset, v_offset; | ||
| 1127 | bool invert_h = (win->flags & TEGRA_WIN_FLAG_INVERT_H) != 0; | ||
| 1128 | bool invert_v = (win->flags & TEGRA_WIN_FLAG_INVERT_V) != 0; | ||
| 1129 | bool yuvp = tegra_dc_is_yuv_planar(win->fmt); | ||
| 1130 | unsigned Bpp = tegra_dc_fmt_bpp(win->fmt) / 8; | ||
| 1131 | /* Bytes per pixel of bandwidth, used for dda_inc calculation */ | ||
| 1132 | unsigned Bpp_bw = Bpp * (yuvp ? 2 : 1); | ||
| 1133 | const bool filter_h = win_use_h_filter(win); | ||
| 1134 | const bool filter_v = win_use_v_filter(win); | ||
| 1135 | |||
| 1136 | if (win->z != dc->blend.z[win->idx]) { | ||
| 1137 | dc->blend.z[win->idx] = win->z; | ||
| 1138 | update_blend = true; | ||
| 1139 | } | ||
| 1140 | if ((win->flags & TEGRA_WIN_BLEND_FLAGS_MASK) != | ||
| 1141 | dc->blend.flags[win->idx]) { | ||
| 1142 | dc->blend.flags[win->idx] = | ||
| 1143 | win->flags & TEGRA_WIN_BLEND_FLAGS_MASK; | ||
| 1144 | update_blend = true; | ||
| 1145 | } | ||
| 1146 | |||
| 1147 | tegra_dc_writel(dc, WINDOW_A_SELECT << win->idx, | ||
| 1148 | DC_CMD_DISPLAY_WINDOW_HEADER); | ||
| 1149 | |||
| 1150 | if (!no_vsync) | ||
| 1151 | update_mask |= WIN_A_ACT_REQ << win->idx; | ||
| 1152 | |||
| 1153 | if (!WIN_IS_ENABLED(win)) { | ||
| 1154 | tegra_dc_writel(dc, 0, DC_WIN_WIN_OPTIONS); | ||
| 1155 | continue; | ||
| 1156 | } | ||
| 1157 | |||
| 1158 | tegra_dc_writel(dc, win->fmt, DC_WIN_COLOR_DEPTH); | ||
| 1159 | tegra_dc_writel(dc, 0, DC_WIN_BYTE_SWAP); | ||
| 1160 | |||
| 1161 | tegra_dc_writel(dc, | ||
| 1162 | V_POSITION(win->out_y) | H_POSITION(win->out_x), | ||
| 1163 | DC_WIN_POSITION); | ||
| 1164 | tegra_dc_writel(dc, | ||
| 1165 | V_SIZE(win->out_h) | H_SIZE(win->out_w), | ||
| 1166 | DC_WIN_SIZE); | ||
| 1167 | tegra_dc_writel(dc, | ||
| 1168 | V_PRESCALED_SIZE(dfixed_trunc(win->h)) | | ||
| 1169 | H_PRESCALED_SIZE(dfixed_trunc(win->w) * Bpp), | ||
| 1170 | DC_WIN_PRESCALED_SIZE); | ||
| 1171 | |||
| 1172 | h_dda = compute_dda_inc(win->w, win->out_w, false, Bpp_bw); | ||
| 1173 | v_dda = compute_dda_inc(win->h, win->out_h, true, Bpp_bw); | ||
| 1174 | tegra_dc_writel(dc, V_DDA_INC(v_dda) | H_DDA_INC(h_dda), | ||
| 1175 | DC_WIN_DDA_INCREMENT); | ||
| 1176 | h_dda = compute_initial_dda(win->x); | ||
| 1177 | v_dda = compute_initial_dda(win->y); | ||
| 1178 | tegra_dc_writel(dc, h_dda, DC_WIN_H_INITIAL_DDA); | ||
| 1179 | tegra_dc_writel(dc, v_dda, DC_WIN_V_INITIAL_DDA); | ||
| 1180 | |||
| 1181 | tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE); | ||
| 1182 | tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE); | ||
| 1183 | tegra_dc_writel(dc, | ||
| 1184 | (unsigned long)win->phys_addr, | ||
| 1185 | DC_WINBUF_START_ADDR); | ||
| 1186 | |||
| 1187 | if (!yuvp) { | ||
| 1188 | tegra_dc_writel(dc, win->stride, DC_WIN_LINE_STRIDE); | ||
| 1189 | } else { | ||
| 1190 | tegra_dc_writel(dc, | ||
| 1191 | (unsigned long)win->phys_addr_u, | ||
| 1192 | DC_WINBUF_START_ADDR_U); | ||
| 1193 | tegra_dc_writel(dc, | ||
| 1194 | (unsigned long)win->phys_addr_v, | ||
| 1195 | DC_WINBUF_START_ADDR_V); | ||
| 1196 | tegra_dc_writel(dc, | ||
| 1197 | LINE_STRIDE(win->stride) | | ||
| 1198 | UV_LINE_STRIDE(win->stride_uv), | ||
| 1199 | DC_WIN_LINE_STRIDE); | ||
| 1200 | } | ||
| 1201 | |||
| 1202 | h_offset = win->x; | ||
| 1203 | if (invert_h) { | ||
| 1204 | h_offset.full += win->w.full - dfixed_const(1); | ||
| 1205 | } | ||
| 1206 | |||
| 1207 | v_offset = win->y; | ||
| 1208 | if (invert_v) { | ||
| 1209 | v_offset.full += win->h.full - dfixed_const(1); | ||
| 1210 | } | ||
| 1211 | |||
| 1212 | tegra_dc_writel(dc, dfixed_trunc(h_offset) * Bpp, | ||
| 1213 | DC_WINBUF_ADDR_H_OFFSET); | ||
| 1214 | tegra_dc_writel(dc, dfixed_trunc(v_offset), | ||
| 1215 | DC_WINBUF_ADDR_V_OFFSET); | ||
| 1216 | |||
| 1217 | if (WIN_IS_TILED(win)) | ||
| 1218 | tegra_dc_writel(dc, | ||
| 1219 | DC_WIN_BUFFER_ADDR_MODE_TILE | | ||
| 1220 | DC_WIN_BUFFER_ADDR_MODE_TILE_UV, | ||
| 1221 | DC_WIN_BUFFER_ADDR_MODE); | ||
| 1222 | else | ||
| 1223 | tegra_dc_writel(dc, | ||
| 1224 | DC_WIN_BUFFER_ADDR_MODE_LINEAR | | ||
| 1225 | DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV, | ||
| 1226 | DC_WIN_BUFFER_ADDR_MODE); | ||
| 1227 | |||
| 1228 | val = WIN_ENABLE; | ||
| 1229 | if (yuvp) | ||
| 1230 | val |= CSC_ENABLE; | ||
| 1231 | else if (tegra_dc_fmt_bpp(win->fmt) < 24) | ||
| 1232 | val |= COLOR_EXPAND; | ||
| 1233 | |||
| 1234 | if (win->ppflags & TEGRA_WIN_PPFLAG_CP_ENABLE) | ||
| 1235 | val |= CP_ENABLE; | ||
| 1236 | |||
| 1237 | if (filter_h) | ||
| 1238 | val |= H_FILTER_ENABLE; | ||
| 1239 | if (filter_v) | ||
| 1240 | val |= V_FILTER_ENABLE; | ||
| 1241 | |||
| 1242 | if (invert_h) | ||
| 1243 | val |= H_DIRECTION_DECREMENT; | ||
| 1244 | if (invert_v) | ||
| 1245 | val |= V_DIRECTION_DECREMENT; | ||
| 1246 | |||
| 1247 | tegra_dc_writel(dc, val, DC_WIN_WIN_OPTIONS); | ||
| 1248 | |||
| 1249 | win->dirty = no_vsync ? 0 : 1; | ||
| 1250 | |||
| 1251 | dev_dbg(&dc->ndev->dev, "%s():idx=%d z=%d x=%d y=%d w=%d h=%d " | ||
| 1252 | "out_x=%u out_y=%u out_w=%u out_h=%u " | ||
| 1253 | "fmt=%d yuvp=%d Bpp=%u filter_h=%d filter_v=%d", | ||
| 1254 | __func__, win->idx, win->z, | ||
| 1255 | dfixed_trunc(win->x), dfixed_trunc(win->y), | ||
| 1256 | dfixed_trunc(win->w), dfixed_trunc(win->h), | ||
| 1257 | win->out_x, win->out_y, win->out_w, win->out_h, | ||
| 1258 | win->fmt, yuvp, Bpp, filter_h, filter_v); | ||
| 1259 | } | ||
| 1260 | |||
| 1261 | if (update_blend) { | ||
| 1262 | tegra_dc_set_blending(dc, &dc->blend); | ||
| 1263 | for (i = 0; i < DC_N_WINDOWS; i++) { | ||
| 1264 | if (!no_vsync) | ||
| 1265 | dc->windows[i].dirty = 1; | ||
| 1266 | update_mask |= WIN_A_ACT_REQ << i; | ||
| 1267 | } | ||
| 1268 | } | ||
| 1269 | |||
| 1270 | tegra_dc_set_dynamic_emc(windows, n); | ||
| 1271 | |||
| 1272 | tegra_dc_writel(dc, update_mask << 8, DC_CMD_STATE_CONTROL); | ||
| 1273 | |||
| 1274 | tegra_dc_writel(dc, FRAME_END_INT | V_BLANK_INT, DC_CMD_INT_STATUS); | ||
| 1275 | if (!no_vsync) { | ||
| 1276 | val = tegra_dc_readl(dc, DC_CMD_INT_MASK); | ||
| 1277 | val |= (FRAME_END_INT | V_BLANK_INT | ALL_UF_INT); | ||
| 1278 | tegra_dc_writel(dc, val, DC_CMD_INT_MASK); | ||
| 1279 | } else { | ||
| 1280 | val = tegra_dc_readl(dc, DC_CMD_INT_MASK); | ||
| 1281 | val &= ~(FRAME_END_INT | V_BLANK_INT | ALL_UF_INT); | ||
| 1282 | tegra_dc_writel(dc, val, DC_CMD_INT_MASK); | ||
| 1283 | } | ||
| 1284 | |||
| 1285 | if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) | ||
| 1286 | schedule_delayed_work(&dc->one_shot_work, | ||
| 1287 | msecs_to_jiffies(dc->one_shot_delay_ms)); | ||
| 1288 | |||
| 1289 | /* update EMC clock if calculated bandwidth has changed */ | ||
| 1290 | tegra_dc_program_bandwidth(dc); | ||
| 1291 | |||
| 1292 | if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) | ||
| 1293 | update_mask |= NC_HOST_TRIG; | ||
| 1294 | |||
| 1295 | tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL); | ||
| 1296 | |||
| 1297 | mutex_unlock(&dc->lock); | ||
| 1298 | if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) | ||
| 1299 | mutex_unlock(&dc->one_shot_lock); | ||
| 1300 | |||
| 1301 | return 0; | ||
| 1302 | } | ||
| 1303 | EXPORT_SYMBOL(tegra_dc_update_windows); | ||
| 1304 | |||
| 1305 | u32 tegra_dc_get_syncpt_id(const struct tegra_dc *dc, int i) | ||
| 1306 | { | ||
| 1307 | return dc->syncpt[i].id; | ||
| 1308 | } | ||
| 1309 | EXPORT_SYMBOL(tegra_dc_get_syncpt_id); | ||
| 1310 | |||
| 1311 | u32 tegra_dc_incr_syncpt_max(struct tegra_dc *dc, int i) | ||
| 1312 | { | ||
| 1313 | u32 max; | ||
| 1314 | |||
| 1315 | mutex_lock(&dc->lock); | ||
| 1316 | max = nvhost_syncpt_incr_max(&nvhost_get_host(dc->ndev)->syncpt, | ||
| 1317 | dc->syncpt[i].id, ((dc->enabled) ? 1 : 0)); | ||
| 1318 | dc->syncpt[i].max = max; | ||
| 1319 | mutex_unlock(&dc->lock); | ||
| 1320 | |||
| 1321 | return max; | ||
| 1322 | } | ||
| 1323 | |||
| 1324 | void tegra_dc_incr_syncpt_min(struct tegra_dc *dc, int i, u32 val) | ||
| 1325 | { | ||
| 1326 | mutex_lock(&dc->lock); | ||
| 1327 | if ( dc->enabled ) | ||
| 1328 | while (dc->syncpt[i].min < val) { | ||
| 1329 | dc->syncpt[i].min++; | ||
| 1330 | nvhost_syncpt_cpu_incr( | ||
| 1331 | &nvhost_get_host(dc->ndev)->syncpt, | ||
| 1332 | dc->syncpt[i].id); | ||
| 1333 | } | ||
| 1334 | mutex_unlock(&dc->lock); | ||
| 1335 | } | ||
| 1336 | |||
| 1337 | static bool tegra_dc_windows_are_clean(struct tegra_dc_win *windows[], | ||
| 1338 | int n) | ||
| 1339 | { | ||
| 1340 | int i; | ||
| 1341 | |||
| 1342 | for (i = 0; i < n; i++) { | ||
| 1343 | if (windows[i]->dirty) | ||
| 1344 | return false; | ||
| 1345 | } | ||
| 1346 | |||
| 1347 | return true; | ||
| 1348 | } | ||
| 1349 | |||
| 1350 | /* does not support syncing windows on multiple dcs in one call */ | ||
| 1351 | int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n) | ||
| 1352 | { | ||
| 1353 | if (n < 1 || n > DC_N_WINDOWS) | ||
| 1354 | return -EINVAL; | ||
| 1355 | |||
| 1356 | if (!windows[0]->dc->enabled) | ||
| 1357 | return -EFAULT; | ||
| 1358 | |||
| 1359 | #ifdef CONFIG_TEGRA_SIMULATION_PLATFORM | ||
| 1360 | /* Don't want to timeout on simulator */ | ||
| 1361 | return wait_event_interruptible(windows[0]->dc->wq, | ||
| 1362 | tegra_dc_windows_are_clean(windows, n)); | ||
| 1363 | #else | ||
| 1364 | return wait_event_interruptible_timeout(windows[0]->dc->wq, | ||
| 1365 | tegra_dc_windows_are_clean(windows, n), | ||
| 1366 | HZ); | ||
| 1367 | #endif | ||
| 1368 | } | ||
| 1369 | EXPORT_SYMBOL(tegra_dc_sync_windows); | ||
| 1370 | |||
| 1371 | static unsigned long tegra_dc_clk_get_rate(struct tegra_dc *dc) | ||
| 1372 | { | ||
| 1373 | #ifdef CONFIG_TEGRA_SILICON_PLATFORM | ||
| 1374 | return clk_get_rate(dc->clk); | ||
| 1375 | #else | ||
| 1376 | return 27000000; | ||
| 1377 | #endif | ||
| 1378 | } | ||
| 1379 | |||
| 1380 | static unsigned long tegra_dc_pclk_round_rate(struct tegra_dc *dc, int pclk) | ||
| 1381 | { | ||
| 1382 | unsigned long rate; | ||
| 1383 | unsigned long div; | ||
| 1384 | |||
| 1385 | rate = tegra_dc_clk_get_rate(dc); | ||
| 1386 | |||
| 1387 | div = DIV_ROUND_CLOSEST(rate * 2, pclk); | ||
| 1388 | |||
| 1389 | if (div < 2) | ||
| 1390 | return 0; | ||
| 1391 | |||
| 1392 | return rate * 2 / div; | ||
| 1393 | } | ||
| 1394 | |||
| 1395 | static unsigned long tegra_dc_pclk_predict_rate(struct clk *parent, int pclk) | ||
| 1396 | { | ||
| 1397 | unsigned long rate; | ||
| 1398 | unsigned long div; | ||
| 1399 | |||
| 1400 | rate = clk_get_rate(parent); | ||
| 1401 | |||
| 1402 | div = DIV_ROUND_CLOSEST(rate * 2, pclk); | ||
| 1403 | |||
| 1404 | if (div < 2) | ||
| 1405 | return 0; | ||
| 1406 | |||
| 1407 | return rate * 2 / div; | ||
| 1408 | } | ||
| 1409 | |||
| 1410 | void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk) | ||
| 1411 | { | ||
| 1412 | int pclk; | ||
| 1413 | |||
| 1414 | if (dc->out->type == TEGRA_DC_OUT_RGB) { | ||
| 1415 | unsigned long rate; | ||
| 1416 | struct clk *parent_clk = | ||
| 1417 | clk_get_sys(NULL, dc->out->parent_clk ? : "pll_p"); | ||
| 1418 | |||
| 1419 | if (dc->out->parent_clk_backup && | ||
| 1420 | (parent_clk == clk_get_sys(NULL, "pll_p"))) { | ||
| 1421 | rate = tegra_dc_pclk_predict_rate( | ||
| 1422 | parent_clk, dc->mode.pclk); | ||
| 1423 | /* use pll_d as last resort */ | ||
| 1424 | if (rate < (dc->mode.pclk / 100 * 99) || | ||
| 1425 | rate > (dc->mode.pclk / 100 * 109)) | ||
| 1426 | parent_clk = clk_get_sys( | ||
| 1427 | NULL, dc->out->parent_clk_backup); | ||
| 1428 | } | ||
| 1429 | |||
| 1430 | if (clk_get_parent(clk) != parent_clk) | ||
| 1431 | clk_set_parent(clk, parent_clk); | ||
| 1432 | |||
| 1433 | if (parent_clk != clk_get_sys(NULL, "pll_p")) { | ||
| 1434 | struct clk *base_clk = clk_get_parent(parent_clk); | ||
| 1435 | |||
| 1436 | /* Assuming either pll_d or pll_d2 is used */ | ||
| 1437 | rate = dc->mode.pclk * 2; | ||
| 1438 | |||
| 1439 | if (rate != clk_get_rate(base_clk)) | ||
| 1440 | clk_set_rate(base_clk, rate); | ||
| 1441 | } | ||
| 1442 | } | ||
| 1443 | |||
| 1444 | if (dc->out->type == TEGRA_DC_OUT_HDMI) { | ||
| 1445 | unsigned long rate; | ||
| 1446 | struct clk *parent_clk = | ||
| 1447 | clk_get_sys(NULL, dc->out->parent_clk ? : "pll_d_out0"); | ||
| 1448 | struct clk *base_clk = clk_get_parent(parent_clk); | ||
| 1449 | |||
| 1450 | /* | ||
| 1451 | * Providing dynamic frequency rate setting for T20/T30 HDMI. | ||
| 1452 | * The required rate needs to be setup at 4x multiplier, | ||
| 1453 | * as out0 is 1/2 of the actual PLL output. | ||
| 1454 | */ | ||
| 1455 | |||
| 1456 | rate = dc->mode.pclk * 4; | ||
| 1457 | if (rate != clk_get_rate(base_clk)) | ||
| 1458 | clk_set_rate(base_clk, rate); | ||
| 1459 | |||
| 1460 | if (clk_get_parent(clk) != parent_clk) | ||
| 1461 | clk_set_parent(clk, parent_clk); | ||
| 1462 | } | ||
| 1463 | |||
| 1464 | if (dc->out->type == TEGRA_DC_OUT_DSI) { | ||
| 1465 | unsigned long rate; | ||
| 1466 | struct clk *parent_clk; | ||
| 1467 | struct clk *base_clk; | ||
| 1468 | |||
| 1469 | if (clk == dc->clk) { | ||
| 1470 | parent_clk = clk_get_sys(NULL, | ||
| 1471 | dc->out->parent_clk ? : "pll_d_out0"); | ||
| 1472 | base_clk = clk_get_parent(parent_clk); | ||
| 1473 | tegra_clk_cfg_ex(base_clk, | ||
| 1474 | TEGRA_CLK_PLLD_DSI_OUT_ENB, 1); | ||
| 1475 | } else { | ||
| 1476 | if (dc->pdata->default_out->dsi->dsi_instance) { | ||
| 1477 | parent_clk = clk_get_sys(NULL, | ||
| 1478 | dc->out->parent_clk ? : "pll_d2_out0"); | ||
| 1479 | base_clk = clk_get_parent(parent_clk); | ||
| 1480 | tegra_clk_cfg_ex(base_clk, | ||
| 1481 | TEGRA_CLK_PLLD_CSI_OUT_ENB, 1); | ||
| 1482 | } else { | ||
| 1483 | parent_clk = clk_get_sys(NULL, | ||
| 1484 | dc->out->parent_clk ? : "pll_d_out0"); | ||
| 1485 | base_clk = clk_get_parent(parent_clk); | ||
| 1486 | tegra_clk_cfg_ex(base_clk, | ||
| 1487 | TEGRA_CLK_PLLD_DSI_OUT_ENB, 1); | ||
| 1488 | } | ||
| 1489 | } | ||
| 1490 | |||
| 1491 | rate = dc->mode.pclk * dc->shift_clk_div * 2; | ||
| 1492 | if (rate != clk_get_rate(base_clk)) | ||
| 1493 | clk_set_rate(base_clk, rate); | ||
| 1494 | |||
| 1495 | if (clk_get_parent(clk) != parent_clk) | ||
| 1496 | clk_set_parent(clk, parent_clk); | ||
| 1497 | } | ||
| 1498 | |||
| 1499 | pclk = tegra_dc_pclk_round_rate(dc, dc->mode.pclk); | ||
| 1500 | tegra_dvfs_set_rate(clk, pclk); | ||
| 1501 | } | ||
| 1502 | |||
| 1503 | /* return non-zero if constraint is violated */ | ||
| 1504 | static int calc_h_ref_to_sync(const struct tegra_dc_mode *mode, int *href) | ||
| 1505 | { | ||
| 1506 | long a, b; | ||
| 1507 | |||
| 1508 | /* Constraint 5: H_REF_TO_SYNC >= 0 */ | ||
| 1509 | a = 0; | ||
| 1510 | |||
| 1511 | /* Constraint 6: H_FRONT_PORT >= (H_REF_TO_SYNC + 1) */ | ||
| 1512 | b = mode->h_front_porch - 1; | ||
| 1513 | |||
| 1514 | /* Constraint 1: H_REF_TO_SYNC + H_SYNC_WIDTH + H_BACK_PORCH > 11 */ | ||
| 1515 | if (a + mode->h_sync_width + mode->h_back_porch <= 11) | ||
| 1516 | a = 1 + 11 - mode->h_sync_width - mode->h_back_porch; | ||
| 1517 | /* check Constraint 1 and 6 */ | ||
| 1518 | if (a > b) | ||
| 1519 | return 1; | ||
| 1520 | |||
| 1521 | /* Constraint 4: H_SYNC_WIDTH >= 1 */ | ||
| 1522 | if (mode->h_sync_width < 1) | ||
| 1523 | return 4; | ||
| 1524 | |||
| 1525 | /* Constraint 7: H_DISP_ACTIVE >= 16 */ | ||
| 1526 | if (mode->h_active < 16) | ||
| 1527 | return 7; | ||
| 1528 | |||
| 1529 | if (href) { | ||
| 1530 | if (b > a && a % 2) | ||
| 1531 | *href = a + 1; /* use smallest even value */ | ||
| 1532 | else | ||
| 1533 | *href = a; /* even or only possible value */ | ||
| 1534 | } | ||
| 1535 | |||
| 1536 | return 0; | ||
| 1537 | } | ||
| 1538 | |||
| 1539 | static int calc_v_ref_to_sync(const struct tegra_dc_mode *mode, int *vref) | ||
| 1540 | { | ||
| 1541 | long a; | ||
| 1542 | a = 1; /* Constraint 5: V_REF_TO_SYNC >= 1 */ | ||
| 1543 | |||
| 1544 | /* Constraint 2: V_REF_TO_SYNC + V_SYNC_WIDTH + V_BACK_PORCH > 1 */ | ||
| 1545 | if (a + mode->v_sync_width + mode->v_back_porch <= 1) | ||
| 1546 | a = 1 + 1 - mode->v_sync_width - mode->v_back_porch; | ||
| 1547 | |||
| 1548 | /* Constraint 6 */ | ||
| 1549 | if (mode->v_front_porch < a + 1) | ||
| 1550 | a = mode->v_front_porch - 1; | ||
| 1551 | |||
| 1552 | /* Constraint 4: V_SYNC_WIDTH >= 1 */ | ||
| 1553 | if (mode->v_sync_width < 1) | ||
| 1554 | return 4; | ||
| 1555 | |||
| 1556 | /* Constraint 7: V_DISP_ACTIVE >= 16 */ | ||
| 1557 | if (mode->v_active < 16) | ||
| 1558 | return 7; | ||
| 1559 | |||
| 1560 | if (vref) | ||
| 1561 | *vref = a; | ||
| 1562 | return 0; | ||
| 1563 | } | ||
| 1564 | |||
| 1565 | static int calc_ref_to_sync(struct tegra_dc_mode *mode) | ||
| 1566 | { | ||
| 1567 | int ret; | ||
| 1568 | ret = calc_h_ref_to_sync(mode, &mode->h_ref_to_sync); | ||
| 1569 | if (ret) | ||
| 1570 | return ret; | ||
| 1571 | ret = calc_v_ref_to_sync(mode, &mode->v_ref_to_sync); | ||
| 1572 | if (ret) | ||
| 1573 | return ret; | ||
| 1574 | |||
| 1575 | return 0; | ||
| 1576 | } | ||
| 1577 | |||
| 1578 | static bool check_ref_to_sync(struct tegra_dc_mode *mode) | ||
| 1579 | { | ||
| 1580 | /* Constraint 1: H_REF_TO_SYNC + H_SYNC_WIDTH + H_BACK_PORCH > 11. */ | ||
| 1581 | if (mode->h_ref_to_sync + mode->h_sync_width + mode->h_back_porch <= 11) | ||
| 1582 | return false; | ||
| 1583 | |||
| 1584 | /* Constraint 2: V_REF_TO_SYNC + V_SYNC_WIDTH + V_BACK_PORCH > 1. */ | ||
| 1585 | if (mode->v_ref_to_sync + mode->v_sync_width + mode->v_back_porch <= 1) | ||
| 1586 | return false; | ||
| 1587 | |||
| 1588 | /* Constraint 3: V_FRONT_PORCH + V_SYNC_WIDTH + V_BACK_PORCH > 1 | ||
| 1589 | * (vertical blank). */ | ||
| 1590 | if (mode->v_front_porch + mode->v_sync_width + mode->v_back_porch <= 1) | ||
| 1591 | return false; | ||
| 1592 | |||
| 1593 | /* Constraint 4: V_SYNC_WIDTH >= 1; H_SYNC_WIDTH >= 1. */ | ||
| 1594 | if (mode->v_sync_width < 1 || mode->h_sync_width < 1) | ||
| 1595 | return false; | ||
| 1596 | |||
| 1597 | /* Constraint 5: V_REF_TO_SYNC >= 1; H_REF_TO_SYNC >= 0. */ | ||
| 1598 | if (mode->v_ref_to_sync < 1 || mode->h_ref_to_sync < 0) | ||
| 1599 | return false; | ||
| 1600 | |||
| 1601 | /* Constraint 6: V_FRONT_PORT >= (V_REF_TO_SYNC + 1); | ||
| 1602 | * H_FRONT_PORT >= (H_REF_TO_SYNC + 1). */ | ||
| 1603 | if (mode->v_front_porch < mode->v_ref_to_sync + 1 || | ||
| 1604 | mode->h_front_porch < mode->h_ref_to_sync + 1) | ||
| 1605 | return false; | ||
| 1606 | |||
| 1607 | /* Constraint 7: H_DISP_ACTIVE >= 16; V_DISP_ACTIVE >= 16. */ | ||
| 1608 | if (mode->h_active < 16 || mode->v_active < 16) | ||
| 1609 | return false; | ||
| 1610 | |||
| 1611 | return true; | ||
| 1612 | } | ||
| 1613 | |||
| 1614 | #ifdef DEBUG | ||
| 1615 | /* return in 1000ths of a Hertz */ | ||
| 1616 | static int calc_refresh(const struct tegra_dc_mode *m) | ||
| 1617 | { | ||
| 1618 | long h_total, v_total, refresh; | ||
| 1619 | h_total = m->h_active + m->h_front_porch + m->h_back_porch + | ||
| 1620 | m->h_sync_width; | ||
| 1621 | v_total = m->v_active + m->v_front_porch + m->v_back_porch + | ||
| 1622 | m->v_sync_width; | ||
| 1623 | refresh = m->pclk / h_total; | ||
| 1624 | refresh *= 1000; | ||
| 1625 | refresh /= v_total; | ||
| 1626 | return refresh; | ||
| 1627 | } | ||
| 1628 | |||
| 1629 | static void print_mode(struct tegra_dc *dc, | ||
| 1630 | const struct tegra_dc_mode *mode, const char *note) | ||
| 1631 | { | ||
| 1632 | if (mode) { | ||
| 1633 | int refresh = calc_refresh(dc, mode); | ||
| 1634 | dev_info(&dc->ndev->dev, "%s():MODE:%dx%d@%d.%03uHz pclk=%d\n", | ||
| 1635 | note ? note : "", | ||
| 1636 | mode->h_active, mode->v_active, | ||
| 1637 | refresh / 1000, refresh % 1000, | ||
| 1638 | mode->pclk); | ||
| 1639 | } | ||
| 1640 | } | ||
| 1641 | #else /* !DEBUG */ | ||
| 1642 | static inline void print_mode(struct tegra_dc *dc, | ||
| 1643 | const struct tegra_dc_mode *mode, const char *note) { } | ||
| 1644 | #endif /* DEBUG */ | ||
| 1645 | |||
| 1646 | static inline void enable_dc_irq(unsigned int irq) | ||
| 1647 | { | ||
| 1648 | #ifndef CONFIG_TEGRA_FPGA_PLATFORM | ||
| 1649 | enable_irq(irq); | ||
| 1650 | #else | ||
| 1651 | /* Always disable DC interrupts on FPGA. */ | ||
| 1652 | disable_irq(irq); | ||
| 1653 | #endif | ||
| 1654 | } | ||
| 1655 | |||
| 1656 | static inline void disable_dc_irq(unsigned int irq) | ||
| 1657 | { | ||
| 1658 | disable_irq(irq); | ||
| 1659 | } | ||
| 1660 | |||
| 1661 | static int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode) | ||
| 1662 | { | ||
| 1663 | unsigned long val; | ||
| 1664 | unsigned long rate; | ||
| 1665 | unsigned long div; | ||
| 1666 | unsigned long pclk; | ||
| 1667 | |||
| 1668 | print_mode(dc, mode, __func__); | ||
| 1669 | |||
| 1670 | /* use default EMC rate when switching modes */ | ||
| 1671 | dc->new_emc_clk_rate = tegra_dc_get_default_emc_clk_rate(dc); | ||
| 1672 | tegra_dc_program_bandwidth(dc); | ||
| 1673 | |||
| 1674 | tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS); | ||
| 1675 | tegra_dc_writel(dc, mode->h_ref_to_sync | (mode->v_ref_to_sync << 16), | ||
| 1676 | DC_DISP_REF_TO_SYNC); | ||
| 1677 | tegra_dc_writel(dc, mode->h_sync_width | (mode->v_sync_width << 16), | ||
| 1678 | DC_DISP_SYNC_WIDTH); | ||
| 1679 | tegra_dc_writel(dc, mode->h_back_porch | (mode->v_back_porch << 16), | ||
| 1680 | DC_DISP_BACK_PORCH); | ||
| 1681 | tegra_dc_writel(dc, mode->h_active | (mode->v_active << 16), | ||
| 1682 | DC_DISP_DISP_ACTIVE); | ||
| 1683 | tegra_dc_writel(dc, mode->h_front_porch | (mode->v_front_porch << 16), | ||
| 1684 | DC_DISP_FRONT_PORCH); | ||
| 1685 | |||
| 1686 | tegra_dc_writel(dc, DE_SELECT_ACTIVE | DE_CONTROL_NORMAL, | ||
| 1687 | DC_DISP_DATA_ENABLE_OPTIONS); | ||
| 1688 | |||
| 1689 | /* TODO: MIPI/CRT/HDMI clock cals */ | ||
| 1690 | |||
| 1691 | val = DISP_DATA_FORMAT_DF1P1C; | ||
| 1692 | |||
| 1693 | if (dc->out->align == TEGRA_DC_ALIGN_MSB) | ||
| 1694 | val |= DISP_DATA_ALIGNMENT_MSB; | ||
| 1695 | else | ||
| 1696 | val |= DISP_DATA_ALIGNMENT_LSB; | ||
| 1697 | |||
| 1698 | if (dc->out->order == TEGRA_DC_ORDER_RED_BLUE) | ||
| 1699 | val |= DISP_DATA_ORDER_RED_BLUE; | ||
| 1700 | else | ||
| 1701 | val |= DISP_DATA_ORDER_BLUE_RED; | ||
| 1702 | |||
| 1703 | tegra_dc_writel(dc, val, DC_DISP_DISP_INTERFACE_CONTROL); | ||
| 1704 | |||
| 1705 | rate = tegra_dc_clk_get_rate(dc); | ||
| 1706 | |||
| 1707 | pclk = tegra_dc_pclk_round_rate(dc, mode->pclk); | ||
| 1708 | if (pclk < (mode->pclk / 100 * 99) || | ||
| 1709 | pclk > (mode->pclk / 100 * 109)) { | ||
| 1710 | dev_err(&dc->ndev->dev, | ||
| 1711 | "can't divide %ld clock to %d -1/+9%% %ld %d %d\n", | ||
| 1712 | rate, mode->pclk, | ||
| 1713 | pclk, (mode->pclk / 100 * 99), | ||
| 1714 | (mode->pclk / 100 * 109)); | ||
| 1715 | return -EINVAL; | ||
| 1716 | } | ||
| 1717 | |||
| 1718 | div = (rate * 2 / pclk) - 2; | ||
| 1719 | |||
| 1720 | tegra_dc_writel(dc, 0x00010001, | ||
| 1721 | DC_DISP_SHIFT_CLOCK_OPTIONS); | ||
| 1722 | tegra_dc_writel(dc, PIXEL_CLK_DIVIDER_PCD1 | SHIFT_CLK_DIVIDER(div), | ||
| 1723 | DC_DISP_DISP_CLOCK_CONTROL); | ||
| 1724 | |||
| 1725 | #ifdef CONFIG_SWITCH | ||
| 1726 | switch_set_state(&dc->modeset_switch, | ||
| 1727 | (mode->h_active << 16) | mode->v_active); | ||
| 1728 | #endif | ||
| 1729 | |||
| 1730 | tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL); | ||
| 1731 | tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); | ||
| 1732 | |||
| 1733 | return 0; | ||
| 1734 | } | ||
| 1735 | |||
| 1736 | |||
| 1737 | int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode) | ||
| 1738 | { | ||
| 1739 | memcpy(&dc->mode, mode, sizeof(dc->mode)); | ||
| 1740 | |||
| 1741 | print_mode(dc, mode, __func__); | ||
| 1742 | |||
| 1743 | return 0; | ||
| 1744 | } | ||
| 1745 | EXPORT_SYMBOL(tegra_dc_set_mode); | ||
| 1746 | |||
| 1747 | int tegra_dc_set_fb_mode(struct tegra_dc *dc, | ||
| 1748 | const struct fb_videomode *fbmode, bool stereo_mode) | ||
| 1749 | { | ||
| 1750 | struct tegra_dc_mode mode; | ||
| 1751 | |||
| 1752 | if (!fbmode->pixclock) | ||
| 1753 | return -EINVAL; | ||
| 1754 | |||
| 1755 | mode.pclk = PICOS2KHZ(fbmode->pixclock) * 1000; | ||
| 1756 | mode.h_sync_width = fbmode->hsync_len; | ||
| 1757 | mode.v_sync_width = fbmode->vsync_len; | ||
| 1758 | mode.h_back_porch = fbmode->left_margin; | ||
| 1759 | mode.v_back_porch = fbmode->upper_margin; | ||
| 1760 | mode.h_active = fbmode->xres; | ||
| 1761 | mode.v_active = fbmode->yres; | ||
| 1762 | mode.h_front_porch = fbmode->right_margin; | ||
| 1763 | mode.v_front_porch = fbmode->lower_margin; | ||
| 1764 | mode.stereo_mode = stereo_mode; | ||
| 1765 | if (dc->out->type == TEGRA_DC_OUT_HDMI) { | ||
| 1766 | /* HDMI controller requires h_ref=1, v_ref=1 */ | ||
| 1767 | mode.h_ref_to_sync = 1; | ||
| 1768 | mode.v_ref_to_sync = 1; | ||
| 1769 | } else { | ||
| 1770 | calc_ref_to_sync(&mode); | ||
| 1771 | } | ||
| 1772 | if (!check_ref_to_sync(&mode)) { | ||
| 1773 | dev_err(&dc->ndev->dev, | ||
| 1774 | "Display timing doesn't meet restrictions.\n"); | ||
| 1775 | return -EINVAL; | ||
| 1776 | } | ||
| 1777 | dev_info(&dc->ndev->dev, "Using mode %dx%d pclk=%d href=%d vref=%d\n", | ||
| 1778 | mode.h_active, mode.v_active, mode.pclk, | ||
| 1779 | mode.h_ref_to_sync, mode.v_ref_to_sync | ||
| 1780 | ); | ||
| 1781 | |||
| 1782 | #ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT | ||
| 1783 | /* Double the pixel clock and update v_active only for frame packed mode */ | ||
| 1784 | if (mode.stereo_mode) { | ||
| 1785 | mode.pclk *= 2; | ||
| 1786 | /* total v_active = yres*2 + activespace */ | ||
| 1787 | mode.v_active = fbmode->yres*2 + | ||
| 1788 | fbmode->vsync_len + | ||
| 1789 | fbmode->upper_margin + | ||
| 1790 | fbmode->lower_margin; | ||
| 1791 | } | ||
| 1792 | #endif | ||
| 1793 | |||
| 1794 | mode.flags = 0; | ||
| 1795 | |||
| 1796 | if (!(fbmode->sync & FB_SYNC_HOR_HIGH_ACT)) | ||
| 1797 | mode.flags |= TEGRA_DC_MODE_FLAG_NEG_H_SYNC; | ||
| 1798 | |||
| 1799 | if (!(fbmode->sync & FB_SYNC_VERT_HIGH_ACT)) | ||
| 1800 | mode.flags |= TEGRA_DC_MODE_FLAG_NEG_V_SYNC; | ||
| 1801 | |||
| 1802 | return tegra_dc_set_mode(dc, &mode); | ||
| 1803 | } | ||
| 1804 | EXPORT_SYMBOL(tegra_dc_set_fb_mode); | ||
| 1805 | |||
| 1806 | void | ||
| 1807 | tegra_dc_config_pwm(struct tegra_dc *dc, struct tegra_dc_pwm_params *cfg) | ||
| 1808 | { | ||
| 1809 | unsigned int ctrl; | ||
| 1810 | unsigned long out_sel; | ||
| 1811 | unsigned long cmd_state; | ||
| 1812 | |||
| 1813 | mutex_lock(&dc->lock); | ||
| 1814 | if (!dc->enabled) { | ||
| 1815 | mutex_unlock(&dc->lock); | ||
| 1816 | return; | ||
| 1817 | } | ||
| 1818 | |||
| 1819 | ctrl = ((cfg->period << PM_PERIOD_SHIFT) | | ||
| 1820 | (cfg->clk_div << PM_CLK_DIVIDER_SHIFT) | | ||
| 1821 | cfg->clk_select); | ||
| 1822 | |||
| 1823 | /* The new value should be effected immediately */ | ||
| 1824 | cmd_state = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS); | ||
| 1825 | tegra_dc_writel(dc, (cmd_state | (1 << 2)), DC_CMD_STATE_ACCESS); | ||
| 1826 | |||
| 1827 | if (cfg->switch_to_sfio && cfg->gpio_conf_to_sfio) | ||
| 1828 | cfg->switch_to_sfio(cfg->gpio_conf_to_sfio); | ||
| 1829 | else | ||
| 1830 | dev_err(&dc->ndev->dev, "Error: Need gpio_conf_to_sfio\n"); | ||
| 1831 | |||
| 1832 | switch (cfg->which_pwm) { | ||
| 1833 | case TEGRA_PWM_PM0: | ||
| 1834 | /* Select the LM0 on PM0 */ | ||
| 1835 | out_sel = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_SELECT5); | ||
| 1836 | out_sel &= ~(7 << 0); | ||
| 1837 | out_sel |= (3 << 0); | ||
| 1838 | tegra_dc_writel(dc, out_sel, DC_COM_PIN_OUTPUT_SELECT5); | ||
| 1839 | tegra_dc_writel(dc, ctrl, DC_COM_PM0_CONTROL); | ||
| 1840 | tegra_dc_writel(dc, cfg->duty_cycle, DC_COM_PM0_DUTY_CYCLE); | ||
| 1841 | break; | ||
| 1842 | case TEGRA_PWM_PM1: | ||
| 1843 | /* Select the LM1 on PM1 */ | ||
| 1844 | out_sel = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_SELECT5); | ||
| 1845 | out_sel &= ~(7 << 4); | ||
| 1846 | out_sel |= (3 << 4); | ||
| 1847 | tegra_dc_writel(dc, out_sel, DC_COM_PIN_OUTPUT_SELECT5); | ||
| 1848 | tegra_dc_writel(dc, ctrl, DC_COM_PM1_CONTROL); | ||
| 1849 | tegra_dc_writel(dc, cfg->duty_cycle, DC_COM_PM1_DUTY_CYCLE); | ||
| 1850 | break; | ||
| 1851 | default: | ||
| 1852 | dev_err(&dc->ndev->dev, "Error: Need which_pwm\n"); | ||
| 1853 | break; | ||
| 1854 | } | ||
| 1855 | tegra_dc_writel(dc, cmd_state, DC_CMD_STATE_ACCESS); | ||
| 1856 | mutex_unlock(&dc->lock); | ||
| 1857 | } | ||
| 1858 | EXPORT_SYMBOL(tegra_dc_config_pwm); | ||
| 1859 | |||
| 1860 | void tegra_dc_set_out_pin_polars(struct tegra_dc *dc, | ||
| 1861 | const struct tegra_dc_out_pin *pins, | ||
| 1862 | const unsigned int n_pins) | ||
| 1863 | { | ||
| 1864 | unsigned int i; | ||
| 1865 | |||
| 1866 | int name; | ||
| 1867 | int pol; | ||
| 1868 | |||
| 1869 | u32 pol1, pol3; | ||
| 1870 | |||
| 1871 | u32 set1, unset1; | ||
| 1872 | u32 set3, unset3; | ||
| 1873 | |||
| 1874 | set1 = set3 = unset1 = unset3 = 0; | ||
| 1875 | |||
| 1876 | for (i = 0; i < n_pins; i++) { | ||
| 1877 | name = (pins + i)->name; | ||
| 1878 | pol = (pins + i)->pol; | ||
| 1879 | |||
| 1880 | /* set polarity by name */ | ||
| 1881 | switch (name) { | ||
| 1882 | case TEGRA_DC_OUT_PIN_DATA_ENABLE: | ||
| 1883 | if (pol == TEGRA_DC_OUT_PIN_POL_LOW) | ||
| 1884 | set3 |= LSPI_OUTPUT_POLARITY_LOW; | ||
| 1885 | else | ||
| 1886 | unset3 |= LSPI_OUTPUT_POLARITY_LOW; | ||
| 1887 | break; | ||
| 1888 | case TEGRA_DC_OUT_PIN_H_SYNC: | ||
| 1889 | if (pol == TEGRA_DC_OUT_PIN_POL_LOW) | ||
| 1890 | set1 |= LHS_OUTPUT_POLARITY_LOW; | ||
| 1891 | else | ||
| 1892 | unset1 |= LHS_OUTPUT_POLARITY_LOW; | ||
| 1893 | break; | ||
| 1894 | case TEGRA_DC_OUT_PIN_V_SYNC: | ||
| 1895 | if (pol == TEGRA_DC_OUT_PIN_POL_LOW) | ||
| 1896 | set1 |= LVS_OUTPUT_POLARITY_LOW; | ||
| 1897 | else | ||
| 1898 | unset1 |= LVS_OUTPUT_POLARITY_LOW; | ||
| 1899 | break; | ||
| 1900 | case TEGRA_DC_OUT_PIN_PIXEL_CLOCK: | ||
| 1901 | if (pol == TEGRA_DC_OUT_PIN_POL_LOW) | ||
| 1902 | set1 |= LSC0_OUTPUT_POLARITY_LOW; | ||
| 1903 | else | ||
| 1904 | unset1 |= LSC0_OUTPUT_POLARITY_LOW; | ||
| 1905 | break; | ||
| 1906 | default: | ||
| 1907 | printk("Invalid argument in function %s\n", | ||
| 1908 | __FUNCTION__); | ||
| 1909 | break; | ||
| 1910 | } | ||
| 1911 | } | ||
| 1912 | |||
| 1913 | pol1 = DC_COM_PIN_OUTPUT_POLARITY1_INIT_VAL; | ||
| 1914 | pol3 = DC_COM_PIN_OUTPUT_POLARITY3_INIT_VAL; | ||
| 1915 | |||
| 1916 | pol1 |= set1; | ||
| 1917 | pol1 &= ~unset1; | ||
| 1918 | |||
| 1919 | pol3 |= set3; | ||
| 1920 | pol3 &= ~unset3; | ||
| 1921 | |||
| 1922 | tegra_dc_writel(dc, pol1, DC_COM_PIN_OUTPUT_POLARITY1); | ||
| 1923 | tegra_dc_writel(dc, pol3, DC_COM_PIN_OUTPUT_POLARITY3); | ||
| 1924 | } | ||
| 1925 | |||
| 1926 | static void tegra_dc_set_out(struct tegra_dc *dc, struct tegra_dc_out *out) | ||
| 1927 | { | ||
| 1928 | dc->out = out; | ||
| 1929 | |||
| 1930 | if (out->n_modes > 0) | ||
| 1931 | tegra_dc_set_mode(dc, &dc->out->modes[0]); | ||
| 1932 | |||
| 1933 | switch (out->type) { | ||
| 1934 | case TEGRA_DC_OUT_RGB: | ||
| 1935 | dc->out_ops = &tegra_dc_rgb_ops; | ||
| 1936 | break; | ||
| 1937 | |||
| 1938 | case TEGRA_DC_OUT_HDMI: | ||
| 1939 | dc->out_ops = &tegra_dc_hdmi_ops; | ||
| 1940 | break; | ||
| 1941 | |||
| 1942 | case TEGRA_DC_OUT_DSI: | ||
| 1943 | dc->out_ops = &tegra_dc_dsi_ops; | ||
| 1944 | break; | ||
| 1945 | |||
| 1946 | default: | ||
| 1947 | dc->out_ops = NULL; | ||
| 1948 | break; | ||
| 1949 | } | ||
| 1950 | |||
| 1951 | if (dc->out_ops && dc->out_ops->init) | ||
| 1952 | dc->out_ops->init(dc); | ||
| 1953 | |||
| 1954 | } | ||
| 1955 | |||
| 1956 | unsigned tegra_dc_get_out_height(const struct tegra_dc *dc) | ||
| 1957 | { | ||
| 1958 | if (dc->out) | ||
| 1959 | return dc->out->height; | ||
| 1960 | else | ||
| 1961 | return 0; | ||
| 1962 | } | ||
| 1963 | EXPORT_SYMBOL(tegra_dc_get_out_height); | ||
| 1964 | |||
| 1965 | unsigned tegra_dc_get_out_width(const struct tegra_dc *dc) | ||
| 1966 | { | ||
| 1967 | if (dc->out) | ||
| 1968 | return dc->out->width; | ||
| 1969 | else | ||
| 1970 | return 0; | ||
| 1971 | } | ||
| 1972 | EXPORT_SYMBOL(tegra_dc_get_out_width); | ||
| 1973 | |||
| 1974 | unsigned tegra_dc_get_out_max_pixclock(const struct tegra_dc *dc) | ||
| 1975 | { | ||
| 1976 | if (dc->out && dc->out->max_pixclock) | ||
| 1977 | return dc->out->max_pixclock; | ||
| 1978 | else | ||
| 1979 | return 0; | ||
| 1980 | } | ||
| 1981 | EXPORT_SYMBOL(tegra_dc_get_out_max_pixclock); | ||
| 1982 | |||
| 1983 | void tegra_dc_enable_crc(struct tegra_dc *dc) | ||
| 1984 | { | ||
| 1985 | u32 val; | ||
| 1986 | tegra_dc_io_start(dc); | ||
| 1987 | |||
| 1988 | val = CRC_ALWAYS_ENABLE | CRC_INPUT_DATA_ACTIVE_DATA | | ||
| 1989 | CRC_ENABLE_ENABLE; | ||
| 1990 | tegra_dc_writel(dc, val, DC_COM_CRC_CONTROL); | ||
| 1991 | tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL); | ||
| 1992 | tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); | ||
| 1993 | } | ||
| 1994 | |||
| 1995 | void tegra_dc_disable_crc(struct tegra_dc *dc) | ||
| 1996 | { | ||
| 1997 | tegra_dc_writel(dc, 0x0, DC_COM_CRC_CONTROL); | ||
| 1998 | tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL); | ||
| 1999 | tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); | ||
| 2000 | |||
| 2001 | tegra_dc_io_end(dc); | ||
| 2002 | } | ||
| 2003 | |||
| 2004 | u32 tegra_dc_read_checksum_latched(struct tegra_dc *dc) | ||
| 2005 | { | ||
| 2006 | int crc = 0; | ||
| 2007 | |||
| 2008 | if(!dc) { | ||
| 2009 | dev_err(&dc->ndev->dev, "Failed to get dc.\n"); | ||
| 2010 | goto crc_error; | ||
| 2011 | } | ||
| 2012 | |||
| 2013 | /* TODO: Replace mdelay with code to sync VBlANK, since | ||
| 2014 | * DC_COM_CRC_CHECKSUM_LATCHED is available after VBLANK */ | ||
| 2015 | mdelay(TEGRA_CRC_LATCHED_DELAY); | ||
| 2016 | |||
| 2017 | crc = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM_LATCHED); | ||
| 2018 | crc_error: | ||
| 2019 | return crc; | ||
| 2020 | } | ||
| 2021 | |||
| 2022 | static void tegra_dc_vblank(struct work_struct *work) | ||
| 2023 | { | ||
| 2024 | struct tegra_dc *dc = container_of(work, struct tegra_dc, vblank_work); | ||
| 2025 | bool nvsd_updated = false; | ||
| 2026 | |||
| 2027 | mutex_lock(&dc->lock); | ||
| 2028 | |||
| 2029 | /* Update the SD brightness */ | ||
| 2030 | if (dc->enabled && dc->out->sd_settings) | ||
| 2031 | nvsd_updated = nvsd_update_brightness(dc); | ||
| 2032 | |||
| 2033 | mutex_unlock(&dc->lock); | ||
| 2034 | |||
| 2035 | /* Do the actual brightness update outside of the mutex */ | ||
| 2036 | if (nvsd_updated && dc->out->sd_settings && | ||
| 2037 | dc->out->sd_settings->bl_device) { | ||
| 2038 | |||
| 2039 | struct platform_device *pdev = dc->out->sd_settings->bl_device; | ||
| 2040 | struct backlight_device *bl = platform_get_drvdata(pdev); | ||
| 2041 | if (bl) | ||
| 2042 | backlight_update_status(bl); | ||
| 2043 | } | ||
| 2044 | } | ||
| 2045 | |||
| 2046 | /* Must acquire dc lock and dc one-shot lock before invoking this function. | ||
| 2047 | * Acquire dc one-shot lock first and then dc lock. */ | ||
| 2048 | void tegra_dc_host_trigger(struct tegra_dc *dc) | ||
| 2049 | { | ||
| 2050 | /* We release the lock here to prevent deadlock between | ||
| 2051 | * cancel_delayed_work_sync and one-shot work. */ | ||
| 2052 | mutex_unlock(&dc->lock); | ||
| 2053 | |||
| 2054 | cancel_delayed_work_sync(&dc->one_shot_work); | ||
| 2055 | mutex_lock(&dc->lock); | ||
| 2056 | |||
| 2057 | schedule_delayed_work(&dc->one_shot_work, | ||
| 2058 | msecs_to_jiffies(dc->one_shot_delay_ms)); | ||
| 2059 | tegra_dc_program_bandwidth(dc); | ||
| 2060 | tegra_dc_writel(dc, NC_HOST_TRIG, DC_CMD_STATE_CONTROL); | ||
| 2061 | } | ||
| 2062 | |||
| 2063 | static void tegra_dc_one_shot_worker(struct work_struct *work) | ||
| 2064 | { | ||
| 2065 | struct tegra_dc *dc = container_of( | ||
| 2066 | to_delayed_work(work), struct tegra_dc, one_shot_work); | ||
| 2067 | mutex_lock(&dc->lock); | ||
| 2068 | /* memory client has gone idle */ | ||
| 2069 | tegra_dc_clear_bandwidth(dc); | ||
| 2070 | mutex_unlock(&dc->lock); | ||
| 2071 | } | ||
| 2072 | |||
| 2073 | /* return an arbitrarily large number if count overflow occurs. | ||
| 2074 | * make it a nice base-10 number to show up in stats output */ | ||
| 2075 | static u64 tegra_dc_underflow_count(struct tegra_dc *dc, unsigned reg) | ||
| 2076 | { | ||
| 2077 | unsigned count = tegra_dc_readl(dc, reg); | ||
| 2078 | tegra_dc_writel(dc, 0, reg); | ||
| 2079 | return ((count & 0x80000000) == 0) ? count : 10000000000ll; | ||
| 2080 | } | ||
| 2081 | |||
| 2082 | static void tegra_dc_underflow_handler(struct tegra_dc *dc) | ||
| 2083 | { | ||
| 2084 | u32 val; | ||
| 2085 | int i; | ||
| 2086 | |||
| 2087 | dc->stats.underflows++; | ||
| 2088 | if (dc->underflow_mask & WIN_A_UF_INT) | ||
| 2089 | dc->stats.underflows_a += tegra_dc_underflow_count(dc, | ||
| 2090 | DC_WINBUF_AD_UFLOW_STATUS); | ||
| 2091 | if (dc->underflow_mask & WIN_B_UF_INT) | ||
| 2092 | dc->stats.underflows_b += tegra_dc_underflow_count(dc, | ||
| 2093 | DC_WINBUF_BD_UFLOW_STATUS); | ||
| 2094 | if (dc->underflow_mask & WIN_C_UF_INT) | ||
| 2095 | dc->stats.underflows_c += tegra_dc_underflow_count(dc, | ||
| 2096 | DC_WINBUF_CD_UFLOW_STATUS); | ||
| 2097 | |||
| 2098 | /* Check for any underflow reset conditions */ | ||
| 2099 | for (i = 0; i < DC_N_WINDOWS; i++) { | ||
| 2100 | if (dc->underflow_mask & (WIN_A_UF_INT << i)) { | ||
| 2101 | dc->windows[i].underflows++; | ||
| 2102 | |||
| 2103 | #ifdef CONFIG_ARCH_TEGRA_2x_SOC | ||
| 2104 | if (dc->windows[i].underflows > 4) | ||
| 2105 | schedule_work(&dc->reset_work); | ||
| 2106 | #endif | ||
| 2107 | } else { | ||
| 2108 | dc->windows[i].underflows = 0; | ||
| 2109 | } | ||
| 2110 | } | ||
| 2111 | |||
| 2112 | /* Clear the underflow mask now that we've checked it. */ | ||
| 2113 | tegra_dc_writel(dc, dc->underflow_mask, DC_CMD_INT_STATUS); | ||
| 2114 | dc->underflow_mask = 0; | ||
| 2115 | val = tegra_dc_readl(dc, DC_CMD_INT_MASK); | ||
| 2116 | tegra_dc_writel(dc, val | ALL_UF_INT, DC_CMD_INT_MASK); | ||
| 2117 | } | ||
| 2118 | |||
| 2119 | #ifndef CONFIG_TEGRA_FPGA_PLATFORM | ||
| 2120 | static bool tegra_dc_windows_are_dirty(struct tegra_dc *dc) | ||
| 2121 | { | ||
| 2122 | #ifndef CONFIG_TEGRA_SIMULATION_PLATFORM | ||
| 2123 | u32 val; | ||
| 2124 | |||
| 2125 | val = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); | ||
| 2126 | if (val & (WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE)) | ||
| 2127 | return true; | ||
| 2128 | #endif | ||
| 2129 | return false; | ||
| 2130 | } | ||
| 2131 | |||
| 2132 | static void tegra_dc_trigger_windows(struct tegra_dc *dc) | ||
| 2133 | { | ||
| 2134 | u32 val, i; | ||
| 2135 | u32 completed = 0; | ||
| 2136 | u32 dirty = 0; | ||
| 2137 | |||
| 2138 | val = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); | ||
| 2139 | for (i = 0; i < DC_N_WINDOWS; i++) { | ||
| 2140 | #ifdef CONFIG_TEGRA_SIMULATION_PLATFORM | ||
| 2141 | /* FIXME: this is not needed when the simulator | ||
| 2142 | clears WIN_x_UPDATE bits as in HW */ | ||
| 2143 | dc->windows[i].dirty = 0; | ||
| 2144 | completed = 1; | ||
| 2145 | #else | ||
| 2146 | if (!(val & (WIN_A_UPDATE << i))) { | ||
| 2147 | dc->windows[i].dirty = 0; | ||
| 2148 | completed = 1; | ||
| 2149 | } else { | ||
| 2150 | dirty = 1; | ||
| 2151 | } | ||
| 2152 | #endif | ||
| 2153 | } | ||
| 2154 | |||
| 2155 | if (!dirty) { | ||
| 2156 | val = tegra_dc_readl(dc, DC_CMD_INT_MASK); | ||
| 2157 | if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) | ||
| 2158 | val &= ~V_BLANK_INT; | ||
| 2159 | else | ||
| 2160 | val &= ~FRAME_END_INT; | ||
| 2161 | tegra_dc_writel(dc, val, DC_CMD_INT_MASK); | ||
| 2162 | } | ||
| 2163 | |||
| 2164 | if (completed) { | ||
| 2165 | if (!dirty) { | ||
| 2166 | /* With the last completed window, go ahead | ||
| 2167 | and enable the vblank interrupt for nvsd. */ | ||
| 2168 | val = tegra_dc_readl(dc, DC_CMD_INT_MASK); | ||
| 2169 | val |= V_BLANK_INT; | ||
| 2170 | tegra_dc_writel(dc, val, DC_CMD_INT_MASK); | ||
| 2171 | } | ||
| 2172 | |||
| 2173 | wake_up(&dc->wq); | ||
| 2174 | } | ||
| 2175 | } | ||
| 2176 | |||
| 2177 | static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status) | ||
| 2178 | { | ||
| 2179 | if (status & V_BLANK_INT) { | ||
| 2180 | /* Sync up windows. */ | ||
| 2181 | tegra_dc_trigger_windows(dc); | ||
| 2182 | |||
| 2183 | /* Schedule any additional bottom-half vblank actvities. */ | ||
| 2184 | schedule_work(&dc->vblank_work); | ||
| 2185 | } | ||
| 2186 | |||
| 2187 | if (status & FRAME_END_INT) { | ||
| 2188 | /* Mark the frame_end as complete. */ | ||
| 2189 | if (!completion_done(&dc->frame_end_complete)) | ||
| 2190 | complete(&dc->frame_end_complete); | ||
| 2191 | } | ||
| 2192 | } | ||
| 2193 | |||
| 2194 | static void tegra_dc_continuous_irq(struct tegra_dc *dc, unsigned long status) | ||
| 2195 | { | ||
| 2196 | if (status & V_BLANK_INT) { | ||
| 2197 | /* Schedule any additional bottom-half vblank actvities. */ | ||
| 2198 | schedule_work(&dc->vblank_work); | ||
| 2199 | |||
| 2200 | /* All windows updated. Mask subsequent V_BLANK interrupts */ | ||
| 2201 | if (!tegra_dc_windows_are_dirty(dc)) { | ||
| 2202 | u32 val; | ||
| 2203 | |||
| 2204 | val = tegra_dc_readl(dc, DC_CMD_INT_MASK); | ||
| 2205 | val &= ~V_BLANK_INT; | ||
| 2206 | tegra_dc_writel(dc, val, DC_CMD_INT_MASK); | ||
| 2207 | } | ||
| 2208 | } | ||
| 2209 | |||
| 2210 | if (status & FRAME_END_INT) { | ||
| 2211 | /* Mark the frame_end as complete. */ | ||
| 2212 | if (!completion_done(&dc->frame_end_complete)) | ||
| 2213 | complete(&dc->frame_end_complete); | ||
| 2214 | |||
| 2215 | tegra_dc_trigger_windows(dc); | ||
| 2216 | } | ||
| 2217 | } | ||
| 2218 | #endif | ||
| 2219 | |||
| 2220 | static irqreturn_t tegra_dc_irq(int irq, void *ptr) | ||
| 2221 | { | ||
| 2222 | #ifndef CONFIG_TEGRA_FPGA_PLATFORM | ||
| 2223 | struct tegra_dc *dc = ptr; | ||
| 2224 | unsigned long status; | ||
| 2225 | unsigned long underflow_mask; | ||
| 2226 | u32 val; | ||
| 2227 | |||
| 2228 | if (!nvhost_module_powered(nvhost_get_host(dc->ndev)->dev)) { | ||
| 2229 | WARN(1, "IRQ when DC not powered!\n"); | ||
| 2230 | tegra_dc_io_start(dc); | ||
| 2231 | status = tegra_dc_readl(dc, DC_CMD_INT_STATUS); | ||
| 2232 | tegra_dc_writel(dc, status, DC_CMD_INT_STATUS); | ||
| 2233 | tegra_dc_io_end(dc); | ||
| 2234 | return IRQ_HANDLED; | ||
| 2235 | } | ||
| 2236 | |||
| 2237 | /* clear all status flags except underflow, save those for the worker */ | ||
| 2238 | status = tegra_dc_readl(dc, DC_CMD_INT_STATUS); | ||
| 2239 | tegra_dc_writel(dc, status & ~ALL_UF_INT, DC_CMD_INT_STATUS); | ||
| 2240 | val = tegra_dc_readl(dc, DC_CMD_INT_MASK); | ||
| 2241 | tegra_dc_writel(dc, val & ~ALL_UF_INT, DC_CMD_INT_MASK); | ||
| 2242 | |||
| 2243 | /* | ||
| 2244 | * Overlays can get thier internal state corrupted during and underflow | ||
| 2245 | * condition. The only way to fix this state is to reset the DC. | ||
| 2246 | * if we get 4 consecutive frames with underflows, assume we're | ||
| 2247 | * hosed and reset. | ||
| 2248 | */ | ||
| 2249 | underflow_mask = status & ALL_UF_INT; | ||
| 2250 | |||
| 2251 | /* Check underflow */ | ||
| 2252 | if (underflow_mask) { | ||
| 2253 | dc->underflow_mask |= underflow_mask; | ||
| 2254 | schedule_delayed_work(&dc->underflow_work, | ||
| 2255 | msecs_to_jiffies(1)); | ||
| 2256 | } | ||
| 2257 | |||
| 2258 | if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) | ||
| 2259 | tegra_dc_one_shot_irq(dc, status); | ||
| 2260 | else | ||
| 2261 | tegra_dc_continuous_irq(dc, status); | ||
| 2262 | |||
| 2263 | return IRQ_HANDLED; | ||
| 2264 | #else /* CONFIG_TEGRA_FPGA_PLATFORM */ | ||
| 2265 | return IRQ_NONE; | ||
| 2266 | #endif /* !CONFIG_TEGRA_FPGA_PLATFORM */ | ||
| 2267 | } | ||
| 2268 | |||
| 2269 | static void tegra_dc_set_color_control(struct tegra_dc *dc) | ||
| 2270 | { | ||
| 2271 | u32 color_control; | ||
| 2272 | |||
| 2273 | switch (dc->out->depth) { | ||
| 2274 | case 3: | ||
| 2275 | color_control = BASE_COLOR_SIZE111; | ||
| 2276 | break; | ||
| 2277 | |||
| 2278 | case 6: | ||
| 2279 | color_control = BASE_COLOR_SIZE222; | ||
| 2280 | break; | ||
| 2281 | |||
| 2282 | case 8: | ||
| 2283 | color_control = BASE_COLOR_SIZE332; | ||
| 2284 | break; | ||
| 2285 | |||
| 2286 | case 9: | ||
| 2287 | color_control = BASE_COLOR_SIZE333; | ||
| 2288 | break; | ||
| 2289 | |||
| 2290 | case 12: | ||
| 2291 | color_control = BASE_COLOR_SIZE444; | ||
| 2292 | break; | ||
| 2293 | |||
| 2294 | case 15: | ||
| 2295 | color_control = BASE_COLOR_SIZE555; | ||
| 2296 | break; | ||
| 2297 | |||
| 2298 | case 16: | ||
| 2299 | color_control = BASE_COLOR_SIZE565; | ||
| 2300 | break; | ||
| 2301 | |||
| 2302 | case 18: | ||
| 2303 | color_control = BASE_COLOR_SIZE666; | ||
| 2304 | break; | ||
| 2305 | |||
| 2306 | default: | ||
| 2307 | color_control = BASE_COLOR_SIZE888; | ||
| 2308 | break; | ||
| 2309 | } | ||
| 2310 | |||
| 2311 | switch (dc->out->dither) { | ||
| 2312 | case TEGRA_DC_DISABLE_DITHER: | ||
| 2313 | color_control |= DITHER_CONTROL_DISABLE; | ||
| 2314 | break; | ||
| 2315 | case TEGRA_DC_ORDERED_DITHER: | ||
| 2316 | color_control |= DITHER_CONTROL_ORDERED; | ||
| 2317 | break; | ||
| 2318 | case TEGRA_DC_ERRDIFF_DITHER: | ||
| 2319 | /* The line buffer for error-diffusion dither is limited | ||
| 2320 | * to 1280 pixels per line. This limits the maximum | ||
| 2321 | * horizontal active area size to 1280 pixels when error | ||
| 2322 | * diffusion is enabled. | ||
| 2323 | */ | ||
| 2324 | BUG_ON(dc->mode.h_active > 1280); | ||
| 2325 | color_control |= DITHER_CONTROL_ERRDIFF; | ||
| 2326 | break; | ||
| 2327 | } | ||
| 2328 | |||
| 2329 | tegra_dc_writel(dc, color_control, DC_DISP_DISP_COLOR_CONTROL); | ||
| 2330 | } | ||
| 2331 | |||
| 2332 | static u32 get_syncpt(struct tegra_dc *dc, int idx) | ||
| 2333 | { | ||
| 2334 | u32 syncpt_id; | ||
| 2335 | |||
| 2336 | switch (dc->ndev->id) { | ||
| 2337 | case 0: | ||
| 2338 | switch (idx) { | ||
| 2339 | case 0: | ||
| 2340 | syncpt_id = NVSYNCPT_DISP0_A; | ||
| 2341 | break; | ||
| 2342 | case 1: | ||
| 2343 | syncpt_id = NVSYNCPT_DISP0_B; | ||
| 2344 | break; | ||
| 2345 | case 2: | ||
| 2346 | syncpt_id = NVSYNCPT_DISP0_C; | ||
| 2347 | break; | ||
| 2348 | default: | ||
| 2349 | BUG(); | ||
| 2350 | break; | ||
| 2351 | } | ||
| 2352 | break; | ||
| 2353 | case 1: | ||
| 2354 | switch (idx) { | ||
| 2355 | case 0: | ||
| 2356 | syncpt_id = NVSYNCPT_DISP1_A; | ||
| 2357 | break; | ||
| 2358 | case 1: | ||
| 2359 | syncpt_id = NVSYNCPT_DISP1_B; | ||
| 2360 | break; | ||
| 2361 | case 2: | ||
| 2362 | syncpt_id = NVSYNCPT_DISP1_C; | ||
| 2363 | break; | ||
| 2364 | default: | ||
| 2365 | BUG(); | ||
| 2366 | break; | ||
| 2367 | } | ||
| 2368 | break; | ||
| 2369 | default: | ||
| 2370 | BUG(); | ||
| 2371 | break; | ||
| 2372 | } | ||
| 2373 | |||
| 2374 | return syncpt_id; | ||
| 2375 | } | ||
| 2376 | |||
| 2377 | static int tegra_dc_init(struct tegra_dc *dc) | ||
| 2378 | { | ||
| 2379 | int i; | ||
| 2380 | |||
| 2381 | tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); | ||
| 2382 | if (dc->ndev->id == 0) { | ||
| 2383 | tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY0A, | ||
| 2384 | TEGRA_MC_PRIO_MED); | ||
| 2385 | tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY0B, | ||
| 2386 | TEGRA_MC_PRIO_MED); | ||
| 2387 | tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY0C, | ||
| 2388 | TEGRA_MC_PRIO_MED); | ||
| 2389 | tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY1B, | ||
| 2390 | TEGRA_MC_PRIO_MED); | ||
| 2391 | tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAYHC, | ||
| 2392 | TEGRA_MC_PRIO_HIGH); | ||
| 2393 | } else if (dc->ndev->id == 1) { | ||
| 2394 | tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY0AB, | ||
| 2395 | TEGRA_MC_PRIO_MED); | ||
| 2396 | tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY0BB, | ||
| 2397 | TEGRA_MC_PRIO_MED); | ||
| 2398 | tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY0CB, | ||
| 2399 | TEGRA_MC_PRIO_MED); | ||
| 2400 | tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY1BB, | ||
| 2401 | TEGRA_MC_PRIO_MED); | ||
| 2402 | tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAYHCB, | ||
| 2403 | TEGRA_MC_PRIO_HIGH); | ||
| 2404 | } | ||
| 2405 | tegra_dc_writel(dc, 0x00000100 | dc->vblank_syncpt, | ||
| 2406 | DC_CMD_CONT_SYNCPT_VSYNC); | ||
| 2407 | tegra_dc_writel(dc, 0x00004700, DC_CMD_INT_TYPE); | ||
| 2408 | tegra_dc_writel(dc, 0x0001c700, DC_CMD_INT_POLARITY); | ||
| 2409 | tegra_dc_writel(dc, 0x00202020, DC_DISP_MEM_HIGH_PRIORITY); | ||
| 2410 | tegra_dc_writel(dc, 0x00010101, DC_DISP_MEM_HIGH_PRIORITY_TIMER); | ||
| 2411 | |||
| 2412 | /* enable interrupts for vblank, frame_end and underflows */ | ||
| 2413 | tegra_dc_writel(dc, (FRAME_END_INT | V_BLANK_INT | ALL_UF_INT), | ||
| 2414 | DC_CMD_INT_ENABLE); | ||
| 2415 | tegra_dc_writel(dc, ALL_UF_INT, DC_CMD_INT_MASK); | ||
| 2416 | |||
| 2417 | tegra_dc_writel(dc, 0x00000000, DC_DISP_BORDER_COLOR); | ||
| 2418 | |||
| 2419 | tegra_dc_set_color_control(dc); | ||
| 2420 | for (i = 0; i < DC_N_WINDOWS; i++) { | ||
| 2421 | struct tegra_dc_win *win = &dc->windows[i]; | ||
| 2422 | tegra_dc_writel(dc, WINDOW_A_SELECT << i, | ||
| 2423 | DC_CMD_DISPLAY_WINDOW_HEADER); | ||
| 2424 | tegra_dc_set_csc(dc, &win->csc); | ||
| 2425 | tegra_dc_set_lut(dc, win); | ||
| 2426 | tegra_dc_set_scaling_filter(dc); | ||
| 2427 | } | ||
| 2428 | |||
| 2429 | |||
| 2430 | for (i = 0; i < dc->n_windows; i++) { | ||
| 2431 | u32 syncpt = get_syncpt(dc, i); | ||
| 2432 | |||
| 2433 | dc->syncpt[i].id = syncpt; | ||
| 2434 | |||
| 2435 | dc->syncpt[i].min = dc->syncpt[i].max = | ||
| 2436 | nvhost_syncpt_read(&nvhost_get_host(dc->ndev)->syncpt, | ||
| 2437 | syncpt); | ||
| 2438 | } | ||
| 2439 | |||
| 2440 | print_mode(dc, &dc->mode, __func__); | ||
| 2441 | |||
| 2442 | if (dc->mode.pclk) | ||
| 2443 | if (tegra_dc_program_mode(dc, &dc->mode)) | ||
| 2444 | return -EINVAL; | ||
| 2445 | |||
| 2446 | /* Initialize SD AFTER the modeset. | ||
| 2447 | nvsd_init handles the sd_settings = NULL case. */ | ||
| 2448 | nvsd_init(dc, dc->out->sd_settings); | ||
| 2449 | |||
| 2450 | return 0; | ||
| 2451 | } | ||
| 2452 | |||
| 2453 | static bool _tegra_dc_controller_enable(struct tegra_dc *dc) | ||
| 2454 | { | ||
| 2455 | int failed_init = 0; | ||
| 2456 | |||
| 2457 | if (dc->out->enable) | ||
| 2458 | dc->out->enable(); | ||
| 2459 | |||
| 2460 | tegra_dc_setup_clk(dc, dc->clk); | ||
| 2461 | clk_enable(dc->clk); | ||
| 2462 | |||
| 2463 | /* do not accept interrupts during initialization */ | ||
| 2464 | tegra_dc_writel(dc, 0, DC_CMD_INT_ENABLE); | ||
| 2465 | tegra_dc_writel(dc, 0, DC_CMD_INT_MASK); | ||
| 2466 | |||
| 2467 | enable_dc_irq(dc->irq); | ||
| 2468 | |||
| 2469 | failed_init = tegra_dc_init(dc); | ||
| 2470 | if (failed_init) { | ||
| 2471 | _tegra_dc_controller_disable(dc); | ||
| 2472 | return false; | ||
| 2473 | } | ||
| 2474 | |||
| 2475 | if (dc->out_ops && dc->out_ops->enable) | ||
| 2476 | dc->out_ops->enable(dc); | ||
| 2477 | |||
| 2478 | if (dc->out->postpoweron) | ||
| 2479 | dc->out->postpoweron(); | ||
| 2480 | |||
| 2481 | /* force a full blending update */ | ||
| 2482 | dc->blend.z[0] = -1; | ||
| 2483 | |||
| 2484 | tegra_dc_ext_enable(dc->ext); | ||
| 2485 | |||
| 2486 | return true; | ||
| 2487 | } | ||
| 2488 | |||
| 2489 | #ifdef CONFIG_ARCH_TEGRA_2x_SOC | ||
| 2490 | static bool _tegra_dc_controller_reset_enable(struct tegra_dc *dc) | ||
| 2491 | { | ||
| 2492 | bool ret = true; | ||
| 2493 | |||
| 2494 | if (dc->out->enable) | ||
| 2495 | dc->out->enable(); | ||
| 2496 | |||
| 2497 | tegra_dc_setup_clk(dc, dc->clk); | ||
| 2498 | clk_enable(dc->clk); | ||
| 2499 | |||
| 2500 | if (dc->ndev->id == 0 && tegra_dcs[1] != NULL) { | ||
| 2501 | mutex_lock(&tegra_dcs[1]->lock); | ||
| 2502 | disable_irq(tegra_dcs[1]->irq); | ||
| 2503 | } else if (dc->ndev->id == 1 && tegra_dcs[0] != NULL) { | ||
| 2504 | mutex_lock(&tegra_dcs[0]->lock); | ||
| 2505 | disable_irq(tegra_dcs[0]->irq); | ||
| 2506 | } | ||
| 2507 | |||
| 2508 | msleep(5); | ||
| 2509 | tegra_periph_reset_assert(dc->clk); | ||
| 2510 | msleep(2); | ||
| 2511 | #ifdef CONFIG_TEGRA_SILICON_PLATFORM | ||
| 2512 | tegra_periph_reset_deassert(dc->clk); | ||
| 2513 | msleep(1); | ||
| 2514 | #endif | ||
| 2515 | |||
| 2516 | if (dc->ndev->id == 0 && tegra_dcs[1] != NULL) { | ||
| 2517 | enable_dc_irq(tegra_dcs[1]->irq); | ||
| 2518 | mutex_unlock(&tegra_dcs[1]->lock); | ||
| 2519 | } else if (dc->ndev->id == 1 && tegra_dcs[0] != NULL) { | ||
| 2520 | enable_dc_irq(tegra_dcs[0]->irq); | ||
| 2521 | mutex_unlock(&tegra_dcs[0]->lock); | ||
| 2522 | } | ||
| 2523 | |||
| 2524 | enable_dc_irq(dc->irq); | ||
| 2525 | |||
| 2526 | if (tegra_dc_init(dc)) { | ||
| 2527 | dev_err(&dc->ndev->dev, "cannot initialize\n"); | ||
| 2528 | ret = false; | ||
| 2529 | } | ||
| 2530 | |||
| 2531 | if (dc->out_ops && dc->out_ops->enable) | ||
| 2532 | dc->out_ops->enable(dc); | ||
| 2533 | |||
| 2534 | if (dc->out->postpoweron) | ||
| 2535 | dc->out->postpoweron(); | ||
| 2536 | |||
| 2537 | /* force a full blending update */ | ||
| 2538 | dc->blend.z[0] = -1; | ||
| 2539 | |||
| 2540 | tegra_dc_ext_enable(dc->ext); | ||
| 2541 | |||
| 2542 | if (!ret) { | ||
| 2543 | dev_err(&dc->ndev->dev, "initialization failed,disabling"); | ||
| 2544 | _tegra_dc_controller_disable(dc); | ||
| 2545 | } | ||
| 2546 | |||
| 2547 | return ret; | ||
| 2548 | } | ||
| 2549 | #endif | ||
| 2550 | |||
| 2551 | static bool _tegra_dc_enable(struct tegra_dc *dc) | ||
| 2552 | { | ||
| 2553 | if (dc->mode.pclk == 0) | ||
| 2554 | return false; | ||
| 2555 | |||
| 2556 | if (!dc->out) | ||
| 2557 | return false; | ||
| 2558 | |||
| 2559 | tegra_dc_io_start(dc); | ||
| 2560 | |||
| 2561 | return _tegra_dc_controller_enable(dc); | ||
| 2562 | } | ||
| 2563 | |||
| 2564 | void tegra_dc_enable(struct tegra_dc *dc) | ||
| 2565 | { | ||
| 2566 | mutex_lock(&dc->lock); | ||
| 2567 | |||
| 2568 | if (!dc->enabled) | ||
| 2569 | dc->enabled = _tegra_dc_enable(dc); | ||
| 2570 | |||
| 2571 | mutex_unlock(&dc->lock); | ||
| 2572 | } | ||
| 2573 | |||
| 2574 | static void _tegra_dc_controller_disable(struct tegra_dc *dc) | ||
| 2575 | { | ||
| 2576 | unsigned i; | ||
| 2577 | |||
| 2578 | if (dc->out_ops && dc->out_ops->disable) | ||
| 2579 | dc->out_ops->disable(dc); | ||
| 2580 | |||
| 2581 | tegra_dc_writel(dc, 0, DC_CMD_INT_MASK); | ||
| 2582 | tegra_dc_writel(dc, 0, DC_CMD_INT_ENABLE); | ||
| 2583 | disable_irq(dc->irq); | ||
| 2584 | |||
| 2585 | tegra_dc_clear_bandwidth(dc); | ||
| 2586 | clk_disable(dc->clk); | ||
| 2587 | tegra_dvfs_set_rate(dc->clk, 0); | ||
| 2588 | |||
| 2589 | if (dc->out && dc->out->disable) | ||
| 2590 | dc->out->disable(); | ||
| 2591 | |||
| 2592 | for (i = 0; i < dc->n_windows; i++) { | ||
| 2593 | struct tegra_dc_win *w = &dc->windows[i]; | ||
| 2594 | |||
| 2595 | /* reset window bandwidth */ | ||
| 2596 | w->bandwidth = 0; | ||
| 2597 | w->new_bandwidth = 0; | ||
| 2598 | |||
| 2599 | /* disable windows */ | ||
| 2600 | w->flags &= ~TEGRA_WIN_FLAG_ENABLED; | ||
| 2601 | |||
| 2602 | /* flush any pending syncpt waits */ | ||
| 2603 | while (dc->syncpt[i].min < dc->syncpt[i].max) { | ||
| 2604 | dc->syncpt[i].min++; | ||
| 2605 | nvhost_syncpt_cpu_incr( | ||
| 2606 | &nvhost_get_host(dc->ndev)->syncpt, | ||
| 2607 | dc->syncpt[i].id); | ||
| 2608 | } | ||
| 2609 | } | ||
| 2610 | } | ||
| 2611 | |||
| 2612 | void tegra_dc_stats_enable(struct tegra_dc *dc, bool enable) | ||
| 2613 | { | ||
| 2614 | #if 0 /* underflow interrupt is already enabled by dc reset worker */ | ||
| 2615 | u32 val; | ||
| 2616 | if (dc->enabled) { | ||
| 2617 | val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE); | ||
| 2618 | if (enable) | ||
| 2619 | val |= (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT); | ||
| 2620 | else | ||
| 2621 | val &= ~(WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT); | ||
| 2622 | tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE); | ||
| 2623 | } | ||
| 2624 | #endif | ||
| 2625 | } | ||
| 2626 | |||
| 2627 | bool tegra_dc_stats_get(struct tegra_dc *dc) | ||
| 2628 | { | ||
| 2629 | #if 0 /* right now it is always enabled */ | ||
| 2630 | u32 val; | ||
| 2631 | bool res; | ||
| 2632 | |||
| 2633 | if (dc->enabled) { | ||
| 2634 | val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE); | ||
| 2635 | res = !!(val & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)); | ||
| 2636 | } else { | ||
| 2637 | res = false; | ||
| 2638 | } | ||
| 2639 | |||
| 2640 | return res; | ||
| 2641 | #endif | ||
| 2642 | return true; | ||
| 2643 | } | ||
| 2644 | |||
| 2645 | /* make the screen blank by disabling all windows */ | ||
| 2646 | void tegra_dc_blank(struct tegra_dc *dc) | ||
| 2647 | { | ||
| 2648 | struct tegra_dc_win *dcwins[DC_N_WINDOWS]; | ||
| 2649 | unsigned i; | ||
| 2650 | |||
| 2651 | for (i = 0; i < DC_N_WINDOWS; i++) { | ||
| 2652 | dcwins[i] = tegra_dc_get_window(dc, i); | ||
| 2653 | dcwins[i]->flags &= ~TEGRA_WIN_FLAG_ENABLED; | ||
| 2654 | } | ||
| 2655 | |||
| 2656 | tegra_dc_update_windows(dcwins, DC_N_WINDOWS); | ||
| 2657 | tegra_dc_sync_windows(dcwins, DC_N_WINDOWS); | ||
| 2658 | } | ||
| 2659 | |||
| 2660 | static void _tegra_dc_disable(struct tegra_dc *dc) | ||
| 2661 | { | ||
| 2662 | _tegra_dc_controller_disable(dc); | ||
| 2663 | tegra_dc_io_end(dc); | ||
| 2664 | } | ||
| 2665 | |||
| 2666 | void tegra_dc_disable(struct tegra_dc *dc) | ||
| 2667 | { | ||
| 2668 | tegra_dc_ext_disable(dc->ext); | ||
| 2669 | |||
| 2670 | /* it's important that new underflow work isn't scheduled before the | ||
| 2671 | * lock is acquired. */ | ||
| 2672 | cancel_delayed_work_sync(&dc->underflow_work); | ||
| 2673 | if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) { | ||
| 2674 | mutex_lock(&dc->one_shot_lock); | ||
| 2675 | cancel_delayed_work_sync(&dc->one_shot_work); | ||
| 2676 | } | ||
| 2677 | |||
| 2678 | mutex_lock(&dc->lock); | ||
| 2679 | |||
| 2680 | if (dc->enabled) { | ||
| 2681 | dc->enabled = false; | ||
| 2682 | |||
| 2683 | if (!dc->suspended) | ||
| 2684 | _tegra_dc_disable(dc); | ||
| 2685 | } | ||
| 2686 | |||
| 2687 | #ifdef CONFIG_SWITCH | ||
| 2688 | switch_set_state(&dc->modeset_switch, 0); | ||
| 2689 | #endif | ||
| 2690 | |||
| 2691 | mutex_unlock(&dc->lock); | ||
| 2692 | if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) | ||
| 2693 | mutex_unlock(&dc->one_shot_lock); | ||
| 2694 | } | ||
| 2695 | |||
| 2696 | #ifdef CONFIG_ARCH_TEGRA_2x_SOC | ||
| 2697 | static void tegra_dc_reset_worker(struct work_struct *work) | ||
| 2698 | { | ||
| 2699 | struct tegra_dc *dc = | ||
| 2700 | container_of(work, struct tegra_dc, reset_work); | ||
| 2701 | |||
| 2702 | unsigned long val = 0; | ||
| 2703 | |||
| 2704 | mutex_lock(&shared_lock); | ||
| 2705 | |||
| 2706 | dev_warn(&dc->ndev->dev, "overlay stuck in underflow state. resetting.\n"); | ||
| 2707 | |||
| 2708 | tegra_dc_ext_disable(dc->ext); | ||
| 2709 | |||
| 2710 | mutex_lock(&dc->lock); | ||
| 2711 | |||
| 2712 | if (dc->enabled == false) | ||
| 2713 | goto unlock; | ||
| 2714 | |||
| 2715 | dc->enabled = false; | ||
| 2716 | |||
| 2717 | /* | ||
| 2718 | * off host read bus | ||
| 2719 | */ | ||
| 2720 | val = tegra_dc_readl(dc, DC_CMD_CONT_SYNCPT_VSYNC); | ||
| 2721 | val &= ~(0x00000100); | ||
| 2722 | tegra_dc_writel(dc, val, DC_CMD_CONT_SYNCPT_VSYNC); | ||
| 2723 | |||
| 2724 | /* | ||
| 2725 | * set DC to STOP mode | ||
| 2726 | */ | ||
| 2727 | tegra_dc_writel(dc, DISP_CTRL_MODE_STOP, DC_CMD_DISPLAY_COMMAND); | ||
| 2728 | |||
| 2729 | msleep(10); | ||
| 2730 | |||
| 2731 | _tegra_dc_controller_disable(dc); | ||
| 2732 | |||
| 2733 | /* _tegra_dc_controller_reset_enable deasserts reset */ | ||
| 2734 | _tegra_dc_controller_reset_enable(dc); | ||
| 2735 | |||
| 2736 | dc->enabled = true; | ||
| 2737 | unlock: | ||
| 2738 | mutex_unlock(&dc->lock); | ||
| 2739 | mutex_unlock(&shared_lock); | ||
| 2740 | } | ||
| 2741 | #endif | ||
| 2742 | |||
| 2743 | static void tegra_dc_underflow_worker(struct work_struct *work) | ||
| 2744 | { | ||
| 2745 | struct tegra_dc *dc = container_of( | ||
| 2746 | to_delayed_work(work), struct tegra_dc, underflow_work); | ||
| 2747 | |||
| 2748 | mutex_lock(&dc->lock); | ||
| 2749 | if (dc->enabled) { | ||
| 2750 | tegra_dc_underflow_handler(dc); | ||
| 2751 | } | ||
| 2752 | mutex_unlock(&dc->lock); | ||
| 2753 | } | ||
| 2754 | |||
| 2755 | #ifdef CONFIG_SWITCH | ||
| 2756 | static ssize_t switch_modeset_print_mode(struct switch_dev *sdev, char *buf) | ||
| 2757 | { | ||
| 2758 | struct tegra_dc *dc = | ||
| 2759 | container_of(sdev, struct tegra_dc, modeset_switch); | ||
| 2760 | |||
| 2761 | if (!sdev->state) | ||
| 2762 | return sprintf(buf, "offline\n"); | ||
| 2763 | |||
| 2764 | return sprintf(buf, "%dx%d\n", dc->mode.h_active, dc->mode.v_active); | ||
| 2765 | } | ||
| 2766 | #endif | ||
| 2767 | |||
| 2768 | static int tegra_dc_probe(struct nvhost_device *ndev) | ||
| 2769 | { | ||
| 2770 | struct tegra_dc *dc; | ||
| 2771 | struct clk *clk; | ||
| 2772 | struct clk *emc_clk; | ||
| 2773 | struct resource *res; | ||
| 2774 | struct resource *base_res; | ||
| 2775 | struct resource *fb_mem = NULL; | ||
| 2776 | int ret = 0; | ||
| 2777 | void __iomem *base; | ||
| 2778 | int irq; | ||
| 2779 | int i; | ||
| 2780 | |||
| 2781 | if (!ndev->dev.platform_data) { | ||
| 2782 | dev_err(&ndev->dev, "no platform data\n"); | ||
| 2783 | return -ENOENT; | ||
| 2784 | } | ||
| 2785 | |||
| 2786 | dc = kzalloc(sizeof(struct tegra_dc), GFP_KERNEL); | ||
| 2787 | if (!dc) { | ||
| 2788 | dev_err(&ndev->dev, "can't allocate memory for tegra_dc\n"); | ||
| 2789 | return -ENOMEM; | ||
| 2790 | } | ||
| 2791 | |||
| 2792 | irq = nvhost_get_irq_byname(ndev, "irq"); | ||
| 2793 | if (irq <= 0) { | ||
| 2794 | dev_err(&ndev->dev, "no irq\n"); | ||
| 2795 | ret = -ENOENT; | ||
| 2796 | goto err_free; | ||
| 2797 | } | ||
| 2798 | |||
| 2799 | res = nvhost_get_resource_byname(ndev, IORESOURCE_MEM, "regs"); | ||
| 2800 | if (!res) { | ||
| 2801 | dev_err(&ndev->dev, "no mem resource\n"); | ||
| 2802 | ret = -ENOENT; | ||
| 2803 | goto err_free; | ||
| 2804 | } | ||
| 2805 | |||
| 2806 | base_res = request_mem_region(res->start, resource_size(res), ndev->name); | ||
| 2807 | if (!base_res) { | ||
| 2808 | dev_err(&ndev->dev, "request_mem_region failed\n"); | ||
| 2809 | ret = -EBUSY; | ||
| 2810 | goto err_free; | ||
| 2811 | } | ||
| 2812 | |||
| 2813 | base = ioremap(res->start, resource_size(res)); | ||
| 2814 | if (!base) { | ||
| 2815 | dev_err(&ndev->dev, "registers can't be mapped\n"); | ||
| 2816 | ret = -EBUSY; | ||
| 2817 | goto err_release_resource_reg; | ||
| 2818 | } | ||
| 2819 | |||
| 2820 | fb_mem = nvhost_get_resource_byname(ndev, IORESOURCE_MEM, "fbmem"); | ||
| 2821 | |||
| 2822 | clk = clk_get(&ndev->dev, NULL); | ||
| 2823 | if (IS_ERR_OR_NULL(clk)) { | ||
| 2824 | dev_err(&ndev->dev, "can't get clock\n"); | ||
| 2825 | ret = -ENOENT; | ||
| 2826 | goto err_iounmap_reg; | ||
| 2827 | } | ||
| 2828 | |||
| 2829 | emc_clk = clk_get(&ndev->dev, "emc"); | ||
| 2830 | if (IS_ERR_OR_NULL(emc_clk)) { | ||
| 2831 | dev_err(&ndev->dev, "can't get emc clock\n"); | ||
| 2832 | ret = -ENOENT; | ||
| 2833 | goto err_put_clk; | ||
| 2834 | } | ||
| 2835 | |||
| 2836 | dc->clk = clk; | ||
| 2837 | dc->emc_clk = emc_clk; | ||
| 2838 | dc->shift_clk_div = 1; | ||
| 2839 | /* Initialize one shot work delay, it will be assigned by dsi | ||
| 2840 | * according to refresh rate later. */ | ||
| 2841 | dc->one_shot_delay_ms = 40; | ||
| 2842 | |||
| 2843 | dc->base_res = base_res; | ||
| 2844 | dc->base = base; | ||
| 2845 | dc->irq = irq; | ||
| 2846 | dc->ndev = ndev; | ||
| 2847 | dc->pdata = ndev->dev.platform_data; | ||
| 2848 | |||
| 2849 | /* | ||
| 2850 | * The emc is a shared clock, it will be set based on | ||
| 2851 | * the requirements for each user on the bus. | ||
| 2852 | */ | ||
| 2853 | dc->emc_clk_rate = 0; | ||
| 2854 | |||
| 2855 | if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED) | ||
| 2856 | dc->enabled = true; | ||
| 2857 | |||
| 2858 | mutex_init(&dc->lock); | ||
| 2859 | mutex_init(&dc->one_shot_lock); | ||
| 2860 | init_completion(&dc->frame_end_complete); | ||
| 2861 | init_waitqueue_head(&dc->wq); | ||
| 2862 | #ifdef CONFIG_ARCH_TEGRA_2x_SOC | ||
| 2863 | INIT_WORK(&dc->reset_work, tegra_dc_reset_worker); | ||
| 2864 | #endif | ||
| 2865 | INIT_WORK(&dc->vblank_work, tegra_dc_vblank); | ||
| 2866 | INIT_DELAYED_WORK(&dc->underflow_work, tegra_dc_underflow_worker); | ||
| 2867 | INIT_DELAYED_WORK(&dc->one_shot_work, tegra_dc_one_shot_worker); | ||
| 2868 | |||
| 2869 | tegra_dc_init_lut_defaults(&dc->fb_lut); | ||
| 2870 | |||
| 2871 | dc->n_windows = DC_N_WINDOWS; | ||
| 2872 | for (i = 0; i < dc->n_windows; i++) { | ||
| 2873 | struct tegra_dc_win *win = &dc->windows[i]; | ||
| 2874 | win->idx = i; | ||
| 2875 | win->dc = dc; | ||
| 2876 | tegra_dc_init_csc_defaults(&win->csc); | ||
| 2877 | tegra_dc_init_lut_defaults(&win->lut); | ||
| 2878 | } | ||
| 2879 | |||
| 2880 | ret = tegra_dc_set(dc, ndev->id); | ||
| 2881 | if (ret < 0) { | ||
| 2882 | dev_err(&ndev->dev, "can't add dc\n"); | ||
| 2883 | goto err_free_irq; | ||
| 2884 | } | ||
| 2885 | |||
| 2886 | nvhost_set_drvdata(ndev, dc); | ||
| 2887 | |||
| 2888 | #ifdef CONFIG_SWITCH | ||
| 2889 | dc->modeset_switch.name = dev_name(&ndev->dev); | ||
| 2890 | dc->modeset_switch.state = 0; | ||
| 2891 | dc->modeset_switch.print_state = switch_modeset_print_mode; | ||
| 2892 | switch_dev_register(&dc->modeset_switch); | ||
| 2893 | #endif | ||
| 2894 | |||
| 2895 | if (dc->pdata->default_out) | ||
| 2896 | tegra_dc_set_out(dc, dc->pdata->default_out); | ||
| 2897 | else | ||
| 2898 | dev_err(&ndev->dev, "No default output specified. Leaving output disabled.\n"); | ||
| 2899 | |||
| 2900 | dc->vblank_syncpt = (dc->ndev->id == 0) ? | ||
| 2901 | NVSYNCPT_VBLANK0 : NVSYNCPT_VBLANK1; | ||
| 2902 | |||
| 2903 | dc->ext = tegra_dc_ext_register(ndev, dc); | ||
| 2904 | if (IS_ERR_OR_NULL(dc->ext)) { | ||
| 2905 | dev_warn(&ndev->dev, "Failed to enable Tegra DC extensions.\n"); | ||
| 2906 | dc->ext = NULL; | ||
| 2907 | } | ||
| 2908 | |||
| 2909 | /* interrupt handler must be registered before tegra_fb_register() */ | ||
| 2910 | if (request_irq(irq, tegra_dc_irq, IRQF_DISABLED, | ||
| 2911 | dev_name(&ndev->dev), dc)) { | ||
| 2912 | dev_err(&ndev->dev, "request_irq %d failed\n", irq); | ||
| 2913 | ret = -EBUSY; | ||
| 2914 | goto err_put_emc_clk; | ||
| 2915 | } | ||
| 2916 | |||
| 2917 | /* hack to balance enable_irq calls in _tegra_dc_enable() */ | ||
| 2918 | disable_dc_irq(dc->irq); | ||
| 2919 | |||
| 2920 | mutex_lock(&dc->lock); | ||
| 2921 | if (dc->enabled) | ||
| 2922 | _tegra_dc_enable(dc); | ||
| 2923 | mutex_unlock(&dc->lock); | ||
| 2924 | |||
| 2925 | tegra_dc_create_debugfs(dc); | ||
| 2926 | |||
| 2927 | dev_info(&ndev->dev, "probed\n"); | ||
| 2928 | |||
| 2929 | if (dc->pdata->fb) { | ||
| 2930 | if (dc->pdata->fb->bits_per_pixel == -1) { | ||
| 2931 | unsigned long fmt; | ||
| 2932 | tegra_dc_writel(dc, | ||
| 2933 | WINDOW_A_SELECT << dc->pdata->fb->win, | ||
| 2934 | DC_CMD_DISPLAY_WINDOW_HEADER); | ||
| 2935 | |||
| 2936 | fmt = tegra_dc_readl(dc, DC_WIN_COLOR_DEPTH); | ||
| 2937 | dc->pdata->fb->bits_per_pixel = | ||
| 2938 | tegra_dc_fmt_bpp(fmt); | ||
| 2939 | } | ||
| 2940 | |||
| 2941 | dc->fb = tegra_fb_register(ndev, dc, dc->pdata->fb, fb_mem); | ||
| 2942 | if (IS_ERR_OR_NULL(dc->fb)) | ||
| 2943 | dc->fb = NULL; | ||
| 2944 | } | ||
| 2945 | |||
| 2946 | if (dc->out && dc->out->hotplug_init) | ||
| 2947 | dc->out->hotplug_init(); | ||
| 2948 | |||
| 2949 | if (dc->out_ops && dc->out_ops->detect) | ||
| 2950 | dc->out_ops->detect(dc); | ||
| 2951 | else | ||
| 2952 | dc->connected = true; | ||
| 2953 | |||
| 2954 | tegra_dc_create_sysfs(&dc->ndev->dev); | ||
| 2955 | |||
| 2956 | return 0; | ||
| 2957 | |||
| 2958 | err_free_irq: | ||
| 2959 | free_irq(irq, dc); | ||
| 2960 | err_put_emc_clk: | ||
| 2961 | clk_put(emc_clk); | ||
| 2962 | err_put_clk: | ||
| 2963 | clk_put(clk); | ||
| 2964 | err_iounmap_reg: | ||
| 2965 | iounmap(base); | ||
| 2966 | if (fb_mem) | ||
| 2967 | release_resource(fb_mem); | ||
| 2968 | err_release_resource_reg: | ||
| 2969 | release_resource(base_res); | ||
| 2970 | err_free: | ||
| 2971 | kfree(dc); | ||
| 2972 | |||
| 2973 | return ret; | ||
| 2974 | } | ||
| 2975 | |||
| 2976 | static int tegra_dc_remove(struct nvhost_device *ndev) | ||
| 2977 | { | ||
| 2978 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
| 2979 | |||
| 2980 | tegra_dc_remove_sysfs(&dc->ndev->dev); | ||
| 2981 | tegra_dc_remove_debugfs(dc); | ||
| 2982 | |||
| 2983 | if (dc->fb) { | ||
| 2984 | tegra_fb_unregister(dc->fb); | ||
| 2985 | if (dc->fb_mem) | ||
| 2986 | release_resource(dc->fb_mem); | ||
| 2987 | } | ||
| 2988 | |||
| 2989 | tegra_dc_ext_disable(dc->ext); | ||
| 2990 | |||
| 2991 | if (dc->ext) | ||
| 2992 | tegra_dc_ext_unregister(dc->ext); | ||
| 2993 | |||
| 2994 | if (dc->enabled) | ||
| 2995 | _tegra_dc_disable(dc); | ||
| 2996 | |||
| 2997 | #ifdef CONFIG_SWITCH | ||
| 2998 | switch_dev_unregister(&dc->modeset_switch); | ||
| 2999 | #endif | ||
| 3000 | free_irq(dc->irq, dc); | ||
| 3001 | clk_put(dc->emc_clk); | ||
| 3002 | clk_put(dc->clk); | ||
| 3003 | iounmap(dc->base); | ||
| 3004 | if (dc->fb_mem) | ||
| 3005 | release_resource(dc->base_res); | ||
| 3006 | kfree(dc); | ||
| 3007 | tegra_dc_set(NULL, ndev->id); | ||
| 3008 | return 0; | ||
| 3009 | } | ||
| 3010 | |||
| 3011 | #ifdef CONFIG_PM | ||
| 3012 | static int tegra_dc_suspend(struct nvhost_device *ndev, pm_message_t state) | ||
| 3013 | { | ||
| 3014 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
| 3015 | |||
| 3016 | dev_info(&ndev->dev, "suspend\n"); | ||
| 3017 | |||
| 3018 | tegra_dc_ext_disable(dc->ext); | ||
| 3019 | |||
| 3020 | mutex_lock(&dc->lock); | ||
| 3021 | |||
| 3022 | if (dc->out_ops && dc->out_ops->suspend) | ||
| 3023 | dc->out_ops->suspend(dc); | ||
| 3024 | |||
| 3025 | if (dc->enabled) { | ||
| 3026 | _tegra_dc_disable(dc); | ||
| 3027 | |||
| 3028 | dc->suspended = true; | ||
| 3029 | } | ||
| 3030 | |||
| 3031 | if (dc->out && dc->out->postsuspend) { | ||
| 3032 | dc->out->postsuspend(); | ||
| 3033 | if (dc->out->type && dc->out->type == TEGRA_DC_OUT_HDMI) | ||
| 3034 | /* | ||
| 3035 | * avoid resume event due to voltage falling | ||
| 3036 | */ | ||
| 3037 | msleep(100); | ||
| 3038 | } | ||
| 3039 | |||
| 3040 | mutex_unlock(&dc->lock); | ||
| 3041 | |||
| 3042 | return 0; | ||
| 3043 | } | ||
| 3044 | |||
| 3045 | static int tegra_dc_resume(struct nvhost_device *ndev) | ||
| 3046 | { | ||
| 3047 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
| 3048 | |||
| 3049 | dev_info(&ndev->dev, "resume\n"); | ||
| 3050 | |||
| 3051 | mutex_lock(&dc->lock); | ||
| 3052 | dc->suspended = false; | ||
| 3053 | |||
| 3054 | if (dc->enabled) | ||
| 3055 | _tegra_dc_enable(dc); | ||
| 3056 | |||
| 3057 | if (dc->out && dc->out->hotplug_init) | ||
| 3058 | dc->out->hotplug_init(); | ||
| 3059 | |||
| 3060 | if (dc->out_ops && dc->out_ops->resume) | ||
| 3061 | dc->out_ops->resume(dc); | ||
| 3062 | mutex_unlock(&dc->lock); | ||
| 3063 | |||
| 3064 | return 0; | ||
| 3065 | } | ||
| 3066 | |||
| 3067 | #endif /* CONFIG_PM */ | ||
| 3068 | |||
| 3069 | extern int suspend_set(const char *val, struct kernel_param *kp) | ||
| 3070 | { | ||
| 3071 | if (!strcmp(val, "dump")) | ||
| 3072 | dump_regs(tegra_dcs[0]); | ||
| 3073 | #ifdef CONFIG_PM | ||
| 3074 | else if (!strcmp(val, "suspend")) | ||
| 3075 | tegra_dc_suspend(tegra_dcs[0]->ndev, PMSG_SUSPEND); | ||
| 3076 | else if (!strcmp(val, "resume")) | ||
| 3077 | tegra_dc_resume(tegra_dcs[0]->ndev); | ||
| 3078 | #endif | ||
| 3079 | |||
| 3080 | return 0; | ||
| 3081 | } | ||
| 3082 | |||
| 3083 | extern int suspend_get(char *buffer, struct kernel_param *kp) | ||
| 3084 | { | ||
| 3085 | return 0; | ||
| 3086 | } | ||
| 3087 | |||
| 3088 | int suspend; | ||
| 3089 | |||
| 3090 | module_param_call(suspend, suspend_set, suspend_get, &suspend, 0644); | ||
| 3091 | |||
| 3092 | struct nvhost_driver tegra_dc_driver = { | ||
| 3093 | .driver = { | ||
| 3094 | .name = "tegradc", | ||
| 3095 | .owner = THIS_MODULE, | ||
| 3096 | }, | ||
| 3097 | .probe = tegra_dc_probe, | ||
| 3098 | .remove = tegra_dc_remove, | ||
| 3099 | #ifdef CONFIG_PM | ||
| 3100 | .suspend = tegra_dc_suspend, | ||
| 3101 | .resume = tegra_dc_resume, | ||
| 3102 | #endif | ||
| 3103 | }; | ||
| 3104 | |||
| 3105 | static int __init tegra_dc_module_init(void) | ||
| 3106 | { | ||
| 3107 | int ret = tegra_dc_ext_module_init(); | ||
| 3108 | if (ret) | ||
| 3109 | return ret; | ||
| 3110 | return nvhost_driver_register(&tegra_dc_driver); | ||
| 3111 | } | ||
| 3112 | |||
| 3113 | static void __exit tegra_dc_module_exit(void) | ||
| 3114 | { | ||
| 3115 | nvhost_driver_unregister(&tegra_dc_driver); | ||
| 3116 | tegra_dc_ext_module_exit(); | ||
| 3117 | } | ||
| 3118 | |||
| 3119 | module_exit(tegra_dc_module_exit); | ||
| 3120 | module_init(tegra_dc_module_init); | ||
diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h new file mode 100644 index 00000000000..a10e648debc --- /dev/null +++ b/drivers/video/tegra/dc/dc_priv.h | |||
| @@ -0,0 +1,220 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/dc_priv.h | ||
| 3 | * | ||
| 4 | * Copyright (C) 2010 Google, Inc. | ||
| 5 | * Author: Erik Gilling <konkers@android.com> | ||
| 6 | * | ||
| 7 | * This software is licensed under the terms of the GNU General Public | ||
| 8 | * License version 2, as published by the Free Software Foundation, and | ||
| 9 | * may be copied, distributed, and modified under those terms. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | |||
| 18 | #ifndef __DRIVERS_VIDEO_TEGRA_DC_DC_PRIV_H | ||
| 19 | #define __DRIVERS_VIDEO_TEGRA_DC_DC_PRIV_H | ||
| 20 | |||
| 21 | #include <linux/io.h> | ||
| 22 | #include <linux/mutex.h> | ||
| 23 | #include <linux/wait.h> | ||
| 24 | #include <linux/completion.h> | ||
| 25 | #include <linux/switch.h> | ||
| 26 | |||
| 27 | #include <mach/dc.h> | ||
| 28 | |||
| 29 | #include "../host/dev.h" | ||
| 30 | #include "../host/host1x/host1x_syncpt.h" | ||
| 31 | |||
| 32 | #include <mach/tegra_dc_ext.h> | ||
| 33 | |||
| 34 | #define WIN_IS_TILED(win) ((win)->flags & TEGRA_WIN_FLAG_TILED) | ||
| 35 | #define WIN_IS_ENABLED(win) ((win)->flags & TEGRA_WIN_FLAG_ENABLED) | ||
| 36 | |||
| 37 | #define NEED_UPDATE_EMC_ON_EVERY_FRAME (windows_idle_detection_time == 0) | ||
| 38 | |||
| 39 | /* DDR: 8 bytes transfer per clock */ | ||
| 40 | #define DDR_BW_TO_FREQ(bw) ((bw) / 8) | ||
| 41 | |||
| 42 | #if defined(CONFIG_TEGRA_EMC_TO_DDR_CLOCK) | ||
| 43 | #define EMC_BW_TO_FREQ(bw) (DDR_BW_TO_FREQ(bw) * CONFIG_TEGRA_EMC_TO_DDR_CLOCK) | ||
| 44 | #else | ||
| 45 | #define EMC_BW_TO_FREQ(bw) (DDR_BW_TO_FREQ(bw) * 2) | ||
| 46 | #endif | ||
| 47 | |||
| 48 | struct tegra_dc; | ||
| 49 | |||
| 50 | struct tegra_dc_blend { | ||
| 51 | unsigned z[DC_N_WINDOWS]; | ||
| 52 | unsigned flags[DC_N_WINDOWS]; | ||
| 53 | }; | ||
| 54 | |||
| 55 | struct tegra_dc_out_ops { | ||
| 56 | /* initialize output. dc clocks are not on at this point */ | ||
| 57 | int (*init)(struct tegra_dc *dc); | ||
| 58 | /* destroy output. dc clocks are not on at this point */ | ||
| 59 | void (*destroy)(struct tegra_dc *dc); | ||
| 60 | /* detect connected display. can sleep.*/ | ||
| 61 | bool (*detect)(struct tegra_dc *dc); | ||
| 62 | /* enable output. dc clocks are on at this point */ | ||
| 63 | void (*enable)(struct tegra_dc *dc); | ||
| 64 | /* disable output. dc clocks are on at this point */ | ||
| 65 | void (*disable)(struct tegra_dc *dc); | ||
| 66 | |||
| 67 | /* suspend output. dc clocks are on at this point */ | ||
| 68 | void (*suspend)(struct tegra_dc *dc); | ||
| 69 | /* resume output. dc clocks are on at this point */ | ||
| 70 | void (*resume)(struct tegra_dc *dc); | ||
| 71 | }; | ||
| 72 | |||
| 73 | struct tegra_dc { | ||
| 74 | struct nvhost_device *ndev; | ||
| 75 | struct tegra_dc_platform_data *pdata; | ||
| 76 | |||
| 77 | struct resource *base_res; | ||
| 78 | void __iomem *base; | ||
| 79 | int irq; | ||
| 80 | |||
| 81 | struct clk *clk; | ||
| 82 | struct clk *emc_clk; | ||
| 83 | int emc_clk_rate; | ||
| 84 | int new_emc_clk_rate; | ||
| 85 | u32 shift_clk_div; | ||
| 86 | |||
| 87 | bool connected; | ||
| 88 | bool enabled; | ||
| 89 | bool suspended; | ||
| 90 | |||
| 91 | struct tegra_dc_out *out; | ||
| 92 | struct tegra_dc_out_ops *out_ops; | ||
| 93 | void *out_data; | ||
| 94 | |||
| 95 | struct tegra_dc_mode mode; | ||
| 96 | |||
| 97 | struct tegra_dc_win windows[DC_N_WINDOWS]; | ||
| 98 | struct tegra_dc_blend blend; | ||
| 99 | int n_windows; | ||
| 100 | |||
| 101 | wait_queue_head_t wq; | ||
| 102 | |||
| 103 | struct mutex lock; | ||
| 104 | struct mutex one_shot_lock; | ||
| 105 | |||
| 106 | struct resource *fb_mem; | ||
| 107 | struct tegra_fb_info *fb; | ||
| 108 | |||
| 109 | struct { | ||
| 110 | u32 id; | ||
| 111 | u32 min; | ||
| 112 | u32 max; | ||
| 113 | } syncpt[DC_N_WINDOWS]; | ||
| 114 | u32 vblank_syncpt; | ||
| 115 | |||
| 116 | unsigned long underflow_mask; | ||
| 117 | struct work_struct reset_work; | ||
| 118 | |||
| 119 | #ifdef CONFIG_SWITCH | ||
| 120 | struct switch_dev modeset_switch; | ||
| 121 | #endif | ||
| 122 | |||
| 123 | struct completion frame_end_complete; | ||
| 124 | |||
| 125 | struct work_struct vblank_work; | ||
| 126 | |||
| 127 | struct { | ||
| 128 | u64 underflows; | ||
| 129 | u64 underflows_a; | ||
| 130 | u64 underflows_b; | ||
| 131 | u64 underflows_c; | ||
| 132 | } stats; | ||
| 133 | |||
| 134 | struct tegra_dc_ext *ext; | ||
| 135 | |||
| 136 | #ifdef CONFIG_DEBUG_FS | ||
| 137 | struct dentry *debugdir; | ||
| 138 | #endif | ||
| 139 | struct tegra_dc_lut fb_lut; | ||
| 140 | struct delayed_work underflow_work; | ||
| 141 | u32 one_shot_delay_ms; | ||
| 142 | struct delayed_work one_shot_work; | ||
| 143 | }; | ||
| 144 | |||
| 145 | static inline void tegra_dc_io_start(struct tegra_dc *dc) | ||
| 146 | { | ||
| 147 | nvhost_module_busy(nvhost_get_host(dc->ndev)->dev); | ||
| 148 | } | ||
| 149 | |||
| 150 | static inline void tegra_dc_io_end(struct tegra_dc *dc) | ||
| 151 | { | ||
| 152 | nvhost_module_idle(nvhost_get_host(dc->ndev)->dev); | ||
| 153 | } | ||
| 154 | |||
| 155 | static inline unsigned long tegra_dc_readl(struct tegra_dc *dc, | ||
| 156 | unsigned long reg) | ||
| 157 | { | ||
| 158 | BUG_ON(!nvhost_module_powered(nvhost_get_host(dc->ndev)->dev)); | ||
| 159 | return readl(dc->base + reg * 4); | ||
| 160 | } | ||
| 161 | |||
| 162 | static inline void tegra_dc_writel(struct tegra_dc *dc, unsigned long val, | ||
| 163 | unsigned long reg) | ||
| 164 | { | ||
| 165 | BUG_ON(!nvhost_module_powered(nvhost_get_host(dc->ndev)->dev)); | ||
| 166 | writel(val, dc->base + reg * 4); | ||
| 167 | } | ||
| 168 | |||
| 169 | static inline void _tegra_dc_write_table(struct tegra_dc *dc, const u32 *table, | ||
| 170 | unsigned len) | ||
| 171 | { | ||
| 172 | int i; | ||
| 173 | |||
| 174 | for (i = 0; i < len; i++) | ||
| 175 | tegra_dc_writel(dc, table[i * 2 + 1], table[i * 2]); | ||
| 176 | } | ||
| 177 | |||
| 178 | #define tegra_dc_write_table(dc, table) \ | ||
| 179 | _tegra_dc_write_table(dc, table, ARRAY_SIZE(table) / 2) | ||
| 180 | |||
| 181 | static inline void tegra_dc_set_outdata(struct tegra_dc *dc, void *data) | ||
| 182 | { | ||
| 183 | dc->out_data = data; | ||
| 184 | } | ||
| 185 | |||
| 186 | static inline void *tegra_dc_get_outdata(struct tegra_dc *dc) | ||
| 187 | { | ||
| 188 | return dc->out_data; | ||
| 189 | } | ||
| 190 | |||
| 191 | static inline unsigned long tegra_dc_get_default_emc_clk_rate( | ||
| 192 | struct tegra_dc *dc) | ||
| 193 | { | ||
| 194 | return dc->pdata->emc_clk_rate ? dc->pdata->emc_clk_rate : ULONG_MAX; | ||
| 195 | } | ||
| 196 | |||
| 197 | void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk); | ||
| 198 | |||
| 199 | extern struct tegra_dc_out_ops tegra_dc_rgb_ops; | ||
| 200 | extern struct tegra_dc_out_ops tegra_dc_hdmi_ops; | ||
| 201 | extern struct tegra_dc_out_ops tegra_dc_dsi_ops; | ||
| 202 | |||
| 203 | /* defined in dc_sysfs.c, used by dc.c */ | ||
| 204 | void __devexit tegra_dc_remove_sysfs(struct device *dev); | ||
| 205 | void tegra_dc_create_sysfs(struct device *dev); | ||
| 206 | |||
| 207 | /* defined in dc.c, used by dc_sysfs.c */ | ||
| 208 | void tegra_dc_stats_enable(struct tegra_dc *dc, bool enable); | ||
| 209 | bool tegra_dc_stats_get(struct tegra_dc *dc); | ||
| 210 | |||
| 211 | /* defined in dc.c, used by dc_sysfs.c */ | ||
| 212 | u32 tegra_dc_read_checksum_latched(struct tegra_dc *dc); | ||
| 213 | void tegra_dc_enable_crc(struct tegra_dc *dc); | ||
| 214 | void tegra_dc_disable_crc(struct tegra_dc *dc); | ||
| 215 | |||
| 216 | void tegra_dc_set_out_pin_polars(struct tegra_dc *dc, | ||
| 217 | const struct tegra_dc_out_pin *pins, | ||
| 218 | const unsigned int n_pins); | ||
| 219 | #endif | ||
| 220 | |||
diff --git a/drivers/video/tegra/dc/dc_reg.h b/drivers/video/tegra/dc/dc_reg.h new file mode 100644 index 00000000000..22379a19408 --- /dev/null +++ b/drivers/video/tegra/dc/dc_reg.h | |||
| @@ -0,0 +1,555 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/dc_reg.h | ||
| 3 | * | ||
| 4 | * Copyright (C) 2010 Google, Inc. | ||
| 5 | * Author: Erik Gilling <konkers@android.com> | ||
| 6 | * | ||
| 7 | * Copyright (C) 2010-2011 NVIDIA Corporation | ||
| 8 | * | ||
| 9 | * This software is licensed under the terms of the GNU General Public | ||
| 10 | * License version 2, as published by the Free Software Foundation, and | ||
| 11 | * may be copied, distributed, and modified under those terms. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __DRIVERS_VIDEO_TEGRA_DC_DC_REG_H | ||
| 21 | #define __DRIVERS_VIDEO_TEGRA_DC_DC_REG_H | ||
| 22 | |||
| 23 | #define DC_CMD_GENERAL_INCR_SYNCPT 0x000 | ||
| 24 | #define DC_CMD_GENERAL_INCR_SYNCPT_CNTRL 0x001 | ||
| 25 | #define DC_CMD_GENERAL_INCR_SYNCPT_ERROR 0x002 | ||
| 26 | #define DC_CMD_WIN_A_INCR_SYNCPT 0x008 | ||
| 27 | #define DC_CMD_WIN_A_INCR_SYNCPT_CNTRL 0x009 | ||
| 28 | #define DC_CMD_WIN_A_INCR_SYNCPT_ERROR 0x00a | ||
| 29 | #define DC_CMD_WIN_B_INCR_SYNCPT 0x010 | ||
| 30 | #define DC_CMD_WIN_B_INCR_SYNCPT_CNTRL 0x011 | ||
| 31 | #define DC_CMD_WIN_B_INCR_SYNCPT_ERROR 0x012 | ||
| 32 | #define DC_CMD_WIN_C_INCR_SYNCPT 0x018 | ||
| 33 | #define DC_CMD_WIN_C_INCR_SYNCPT_CNTRL 0x019 | ||
| 34 | #define DC_CMD_WIN_C_INCR_SYNCPT_ERROR 0x01a | ||
| 35 | #define DC_CMD_CONT_SYNCPT_VSYNC 0x028 | ||
| 36 | #define DC_CMD_DISPLAY_COMMAND_OPTION0 0x031 | ||
| 37 | #define MSF_POLARITY_HIGH (0 << 0) | ||
| 38 | #define MSF_POLARITY_LOW (1 << 0) | ||
| 39 | #define MSF_DISABLE (0 << 1) | ||
| 40 | #define MSF_ENABLE (1 << 1) | ||
| 41 | #define MSF_LSPI (0 << 2) | ||
| 42 | #define MSF_LDC (1 << 2) | ||
| 43 | #define MSF_LSDI (2 << 2) | ||
| 44 | |||
| 45 | #define DC_CMD_DISPLAY_COMMAND 0x032 | ||
| 46 | #define DISP_COMMAND_RAISE (1 << 0) | ||
| 47 | #define DISP_CTRL_MODE_STOP (0 << 5) | ||
| 48 | #define DISP_CTRL_MODE_C_DISPLAY (1 << 5) | ||
| 49 | #define DISP_CTRL_MODE_NC_DISPLAY (2 << 5) | ||
| 50 | #define DISP_COMMAND_RAISE_VECTOR(x) (((x) & 0x1f) << 22) | ||
| 51 | #define DISP_COMMAND_RAISE_CHANNEL_ID(x) (((x) & 0xf) << 27) | ||
| 52 | |||
| 53 | #define DC_CMD_SIGNAL_RAISE 0x033 | ||
| 54 | #define DC_CMD_DISPLAY_POWER_CONTROL 0x036 | ||
| 55 | #define PW0_ENABLE (1 << 0) | ||
| 56 | #define PW1_ENABLE (1 << 2) | ||
| 57 | #define PW2_ENABLE (1 << 4) | ||
| 58 | #define PW3_ENABLE (1 << 6) | ||
| 59 | #define PW4_ENABLE (1 << 8) | ||
| 60 | #define PM0_ENABLE (1 << 16) | ||
| 61 | #define PM1_ENABLE (1 << 18) | ||
| 62 | #define SPI_ENABLE (1 << 24) | ||
| 63 | #define HSPI_ENABLE (1 << 25) | ||
| 64 | |||
| 65 | #define DC_CMD_INT_STATUS 0x037 | ||
| 66 | #define DC_CMD_INT_MASK 0x038 | ||
| 67 | #define DC_CMD_INT_ENABLE 0x039 | ||
| 68 | #define DC_CMD_INT_TYPE 0x03a | ||
| 69 | #define DC_CMD_INT_POLARITY 0x03b | ||
| 70 | #define CTXSW_INT (1 << 0) | ||
| 71 | #define FRAME_END_INT (1 << 1) | ||
| 72 | #define V_BLANK_INT (1 << 2) | ||
| 73 | #define H_BLANK_INT (1 << 3) | ||
| 74 | #define V_PULSE3_INT (1 << 4) | ||
| 75 | #define SPI_BUSY_INT (1 << 7) | ||
| 76 | #define WIN_A_UF_INT (1 << 8) | ||
| 77 | #define WIN_B_UF_INT (1 << 9) | ||
| 78 | #define WIN_C_UF_INT (1 << 10) | ||
| 79 | #define MSF_INT (1 << 12) | ||
| 80 | #define SSF_INT (1 << 13) | ||
| 81 | #define WIN_A_OF_INT (1 << 14) | ||
| 82 | #define WIN_B_OF_INT (1 << 15) | ||
| 83 | #define WIN_C_OF_INT (1 << 16) | ||
| 84 | #define GPIO_0_INT (1 << 18) | ||
| 85 | #define GPIO_1_INT (1 << 19) | ||
| 86 | #define GPIO_2_INT (1 << 20) | ||
| 87 | |||
| 88 | #define DC_CMD_SIGNAL_RAISE1 0x03c | ||
| 89 | #define DC_CMD_SIGNAL_RAISE2 0x03d | ||
| 90 | #define DC_CMD_SIGNAL_RAISE3 0x03e | ||
| 91 | #define DC_CMD_STATE_ACCESS 0x040 | ||
| 92 | #define READ_MUX_ASSEMBLY (0 << 0) | ||
| 93 | #define READ_MUX_ACTIVE (1 << 0) | ||
| 94 | #define WRITE_MUX_ASSEMBLY (0 << 2) | ||
| 95 | #define WRITE_MUX_ACTIVE (1 << 2) | ||
| 96 | |||
| 97 | #define DC_CMD_STATE_CONTROL 0x041 | ||
| 98 | #define GENERAL_ACT_REQ (1 << 0) | ||
| 99 | #define WIN_A_ACT_REQ (1 << 1) | ||
| 100 | #define WIN_B_ACT_REQ (1 << 2) | ||
| 101 | #define WIN_C_ACT_REQ (1 << 3) | ||
| 102 | #define GENERAL_UPDATE (1 << 8) | ||
| 103 | #define WIN_A_UPDATE (1 << 9) | ||
| 104 | #define WIN_B_UPDATE (1 << 10) | ||
| 105 | #define WIN_C_UPDATE (1 << 11) | ||
| 106 | #define NC_HOST_TRIG (1 << 24) | ||
| 107 | |||
| 108 | #define DC_CMD_DISPLAY_WINDOW_HEADER 0x042 | ||
| 109 | #define WINDOW_A_SELECT (1 << 4) | ||
| 110 | #define WINDOW_B_SELECT (1 << 5) | ||
| 111 | #define WINDOW_C_SELECT (1 << 6) | ||
| 112 | |||
| 113 | #define DC_CMD_REG_ACT_CONTROL 0x043 | ||
| 114 | |||
| 115 | #define DC_COM_CRC_CONTROL 0x300 | ||
| 116 | #define CRC_ALWAYS_ENABLE (1 << 3) | ||
| 117 | #define CRC_ALWAYS_DISABLE (0 << 3) | ||
| 118 | #define CRC_INPUT_DATA_ACTIVE_DATA (1 << 2) | ||
| 119 | #define CRC_INPUT_DATA_FULL_FRAME (0 << 2) | ||
| 120 | #define CRC_WAIT_TWO_VSYNC (1 << 1) | ||
| 121 | #define CRC_WAIT_ONE_VSYNC (0 << 1) | ||
| 122 | #define CRC_ENABLE_ENABLE (1 << 0) | ||
| 123 | #define CRC_ENABLE_DISABLE (0 << 0) | ||
| 124 | #define DC_COM_CRC_CHECKSUM 0x301 | ||
| 125 | #define DC_COM_PIN_OUTPUT_ENABLE0 0x302 | ||
| 126 | #define DC_COM_PIN_OUTPUT_ENABLE1 0x303 | ||
| 127 | #define DC_COM_PIN_OUTPUT_ENABLE2 0x304 | ||
| 128 | #define DC_COM_PIN_OUTPUT_ENABLE3 0x305 | ||
| 129 | #define PIN_OUTPUT_LSPI_OUTPUT_EN (1 << 8) | ||
| 130 | #define PIN_OUTPUT_LSPI_OUTPUT_DIS (1 << 8) | ||
| 131 | #define DC_COM_PIN_OUTPUT_POLARITY0 0x306 | ||
| 132 | |||
| 133 | #define DC_COM_PIN_OUTPUT_POLARITY1 0x307 | ||
| 134 | #define LHS_OUTPUT_POLARITY_LOW (1 << 30) | ||
| 135 | #define LVS_OUTPUT_POLARITY_LOW (1 << 28) | ||
| 136 | #define LSC0_OUTPUT_POLARITY_LOW (1 << 24) | ||
| 137 | |||
| 138 | #define DC_COM_PIN_OUTPUT_POLARITY2 0x308 | ||
| 139 | |||
| 140 | #define DC_COM_PIN_OUTPUT_POLARITY3 0x309 | ||
| 141 | #define LSPI_OUTPUT_POLARITY_LOW (1 << 8) | ||
| 142 | |||
| 143 | #define DC_COM_PIN_OUTPUT_DATA0 0x30a | ||
| 144 | #define DC_COM_PIN_OUTPUT_DATA1 0x30b | ||
| 145 | #define DC_COM_PIN_OUTPUT_DATA2 0x30c | ||
| 146 | #define DC_COM_PIN_OUTPUT_DATA3 0x30d | ||
| 147 | #define DC_COM_PIN_INPUT_ENABLE0 0x30e | ||
| 148 | #define DC_COM_PIN_INPUT_ENABLE1 0x30f | ||
| 149 | #define DC_COM_PIN_INPUT_ENABLE2 0x310 | ||
| 150 | #define DC_COM_PIN_INPUT_ENABLE3 0x311 | ||
| 151 | #define PIN_INPUT_LSPI_INPUT_EN (1 << 8) | ||
| 152 | #define PIN_INPUT_LSPI_INPUT_DIS (1 << 8) | ||
| 153 | #define DC_COM_PIN_INPUT_DATA0 0x312 | ||
| 154 | #define DC_COM_PIN_INPUT_DATA1 0x313 | ||
| 155 | #define DC_COM_PIN_OUTPUT_SELECT0 0x314 | ||
| 156 | #define DC_COM_PIN_OUTPUT_SELECT1 0x315 | ||
| 157 | #define DC_COM_PIN_OUTPUT_SELECT2 0x316 | ||
| 158 | #define DC_COM_PIN_OUTPUT_SELECT3 0x317 | ||
| 159 | #define DC_COM_PIN_OUTPUT_SELECT4 0x318 | ||
| 160 | #define DC_COM_PIN_OUTPUT_SELECT5 0x319 | ||
| 161 | #define DC_COM_PIN_OUTPUT_SELECT6 0x31a | ||
| 162 | |||
| 163 | #define PIN5_LM1_LCD_M1_OUTPUT_MASK (7 << 4) | ||
| 164 | #define PIN5_LM1_LCD_M1_OUTPUT_M1 (0 << 4) | ||
| 165 | #define PIN5_LM1_LCD_M1_OUTPUT_LD21 (2 << 4) | ||
| 166 | #define PIN5_LM1_LCD_M1_OUTPUT_PM1 (3 << 4) | ||
| 167 | |||
| 168 | #define PIN1_LHS_OUTPUT (1 << 30) | ||
| 169 | #define PIN1_LVS_OUTPUT (1 << 28) | ||
| 170 | |||
| 171 | #define DC_COM_PIN_MISC_CONTROL 0x31b | ||
| 172 | #define DC_COM_PM0_CONTROL 0x31c | ||
| 173 | #define DC_COM_PM0_DUTY_CYCLE 0x31d | ||
| 174 | #define DC_COM_PM1_CONTROL 0x31e | ||
| 175 | #define DC_COM_PM1_DUTY_CYCLE 0x31f | ||
| 176 | |||
| 177 | #define PM_PERIOD_SHIFT 18 | ||
| 178 | #define PM_CLK_DIVIDER_SHIFT 4 | ||
| 179 | |||
| 180 | #define DC_COM_SPI_CONTROL 0x320 | ||
| 181 | #define DC_COM_SPI_START_BYTE 0x321 | ||
| 182 | #define DC_COM_HSPI_WRITE_DATA_AB 0x322 | ||
| 183 | #define DC_COM_HSPI_WRITE_DATA_CD 0x323 | ||
| 184 | #define DC_COM_HSPI_CS_DC 0x324 | ||
| 185 | #define DC_COM_SCRATCH_REGISTER_A 0x325 | ||
| 186 | #define DC_COM_SCRATCH_REGISTER_B 0x326 | ||
| 187 | #define DC_COM_GPIO_CTRL 0x327 | ||
| 188 | #define DC_COM_GPIO_DEBOUNCE_COUNTER 0x328 | ||
| 189 | #define DC_COM_CRC_CHECKSUM_LATCHED 0x329 | ||
| 190 | |||
| 191 | #define DC_DISP_DISP_SIGNAL_OPTIONS0 0x400 | ||
| 192 | #define H_PULSE_0_ENABLE (1 << 8) | ||
| 193 | #define H_PULSE_1_ENABLE (1 << 10) | ||
| 194 | #define H_PULSE_2_ENABLE (1 << 12) | ||
| 195 | #define V_PULSE_0_ENABLE (1 << 16) | ||
| 196 | #define V_PULSE_1_ENABLE (1 << 18) | ||
| 197 | #define V_PULSE_2_ENABLE (1 << 19) | ||
| 198 | #define V_PULSE_3_ENABLE (1 << 20) | ||
| 199 | #define M0_ENABLE (1 << 24) | ||
| 200 | #define M1_ENABLE (1 << 26) | ||
| 201 | |||
| 202 | #define DC_DISP_DISP_SIGNAL_OPTIONS1 0x401 | ||
| 203 | #define DI_ENABLE (1 << 16) | ||
| 204 | #define PP_ENABLE (1 << 18) | ||
| 205 | |||
| 206 | #define DC_DISP_DISP_WIN_OPTIONS 0x402 | ||
| 207 | #define CURSOR_ENABLE (1 << 16) | ||
| 208 | #define TVO_ENABLE (1 << 28) | ||
| 209 | #define DSI_ENABLE (1 << 29) | ||
| 210 | #define HDMI_ENABLE (1 << 30) | ||
| 211 | |||
| 212 | #define DC_DISP_MEM_HIGH_PRIORITY 0x403 | ||
| 213 | #define DC_DISP_MEM_HIGH_PRIORITY_TIMER 0x404 | ||
| 214 | #define DC_DISP_DISP_TIMING_OPTIONS 0x405 | ||
| 215 | #define VSYNC_H_POSITION(x) ((x) & 0xfff) | ||
| 216 | |||
| 217 | #define DC_DISP_REF_TO_SYNC 0x406 | ||
| 218 | #define DC_DISP_SYNC_WIDTH 0x407 | ||
| 219 | #define DC_DISP_BACK_PORCH 0x408 | ||
| 220 | #define DC_DISP_DISP_ACTIVE 0x409 | ||
| 221 | #define DC_DISP_FRONT_PORCH 0x40a | ||
| 222 | #define DC_DISP_H_PULSE0_CONTROL 0x40b | ||
| 223 | #define DC_DISP_H_PULSE0_POSITION_A 0x40c | ||
| 224 | #define DC_DISP_H_PULSE0_POSITION_B 0x40d | ||
| 225 | #define DC_DISP_H_PULSE0_POSITION_C 0x40e | ||
| 226 | #define DC_DISP_H_PULSE0_POSITION_D 0x40f | ||
| 227 | #define DC_DISP_H_PULSE1_CONTROL 0x410 | ||
| 228 | #define DC_DISP_H_PULSE1_POSITION_A 0x411 | ||
| 229 | #define DC_DISP_H_PULSE1_POSITION_B 0x412 | ||
| 230 | #define DC_DISP_H_PULSE1_POSITION_C 0x413 | ||
| 231 | #define DC_DISP_H_PULSE1_POSITION_D 0x414 | ||
| 232 | #define DC_DISP_H_PULSE2_CONTROL 0x415 | ||
| 233 | #define DC_DISP_H_PULSE2_POSITION_A 0x416 | ||
| 234 | #define DC_DISP_H_PULSE2_POSITION_B 0x417 | ||
| 235 | #define DC_DISP_H_PULSE2_POSITION_C 0x418 | ||
| 236 | #define DC_DISP_H_PULSE2_POSITION_D 0x419 | ||
| 237 | #define DC_DISP_V_PULSE0_CONTROL 0x41a | ||
| 238 | #define DC_DISP_V_PULSE0_POSITION_A 0x41b | ||
| 239 | #define DC_DISP_V_PULSE0_POSITION_B 0x41c | ||
| 240 | #define DC_DISP_V_PULSE0_POSITION_C 0x41d | ||
| 241 | #define DC_DISP_V_PULSE1_CONTROL 0x41e | ||
| 242 | #define DC_DISP_V_PULSE1_POSITION_A 0x41f | ||
| 243 | #define DC_DISP_V_PULSE1_POSITION_B 0x420 | ||
| 244 | #define DC_DISP_V_PULSE1_POSITION_C 0x421 | ||
| 245 | #define DC_DISP_V_PULSE2_CONTROL 0x422 | ||
| 246 | #define DC_DISP_V_PULSE2_POSITION_A 0x423 | ||
| 247 | #define DC_DISP_V_PULSE3_CONTROL 0x424 | ||
| 248 | #define DC_DISP_V_PULSE3_POSITION_A 0x425 | ||
| 249 | #define DC_DISP_M0_CONTROL 0x426 | ||
| 250 | #define DC_DISP_M1_CONTROL 0x427 | ||
| 251 | #define DC_DISP_DI_CONTROL 0x428 | ||
| 252 | #define DC_DISP_PP_CONTROL 0x429 | ||
| 253 | #define DC_DISP_PP_SELECT_A 0x42a | ||
| 254 | #define DC_DISP_PP_SELECT_B 0x42b | ||
| 255 | #define DC_DISP_PP_SELECT_C 0x42c | ||
| 256 | #define DC_DISP_PP_SELECT_D 0x42d | ||
| 257 | |||
| 258 | #define PULSE_MODE_NORMAL (0 << 3) | ||
| 259 | #define PULSE_MODE_ONE_CLOCK (1 << 3) | ||
| 260 | #define PULSE_POLARITY_HIGH (0 << 4) | ||
| 261 | #define PULSE_POLARITY_LOW (1 << 4) | ||
| 262 | #define PULSE_QUAL_ALWAYS (0 << 6) | ||
| 263 | #define PULSE_QUAL_VACTIVE (2 << 6) | ||
| 264 | #define PULSE_QUAL_VACTIVE1 (3 << 6) | ||
| 265 | #define PULSE_LAST_START_A (0 << 8) | ||
| 266 | #define PULSE_LAST_END_A (1 << 8) | ||
| 267 | #define PULSE_LAST_START_B (2 << 8) | ||
| 268 | #define PULSE_LAST_END_B (3 << 8) | ||
| 269 | #define PULSE_LAST_START_C (4 << 8) | ||
| 270 | #define PULSE_LAST_END_C (5 << 8) | ||
| 271 | #define PULSE_LAST_START_D (6 << 8) | ||
| 272 | #define PULSE_LAST_END_D (7 << 8) | ||
| 273 | |||
| 274 | #define PULSE_START(x) ((x) & 0xfff) | ||
| 275 | #define PULSE_END(x) (((x) & 0xfff) << 16) | ||
| 276 | |||
| 277 | #define DC_DISP_DISP_CLOCK_CONTROL 0x42e | ||
| 278 | #define PIXEL_CLK_DIVIDER_PCD1 (0 << 8) | ||
| 279 | #define PIXEL_CLK_DIVIDER_PCD1H (1 << 8) | ||
| 280 | #define PIXEL_CLK_DIVIDER_PCD2 (2 << 8) | ||
| 281 | #define PIXEL_CLK_DIVIDER_PCD3 (3 << 8) | ||
| 282 | #define PIXEL_CLK_DIVIDER_PCD4 (4 << 8) | ||
| 283 | #define PIXEL_CLK_DIVIDER_PCD6 (5 << 8) | ||
| 284 | #define PIXEL_CLK_DIVIDER_PCD8 (6 << 8) | ||
| 285 | #define PIXEL_CLK_DIVIDER_PCD9 (7 << 8) | ||
| 286 | #define PIXEL_CLK_DIVIDER_PCD12 (8 << 8) | ||
| 287 | #define PIXEL_CLK_DIVIDER_PCD16 (9 << 8) | ||
| 288 | #define PIXEL_CLK_DIVIDER_PCD18 (10 << 8) | ||
| 289 | #define PIXEL_CLK_DIVIDER_PCD24 (11 << 8) | ||
| 290 | #define PIXEL_CLK_DIVIDER_PCD13 (12 << 8) | ||
| 291 | #define SHIFT_CLK_DIVIDER(x) ((x) & 0xff) | ||
| 292 | |||
| 293 | #define DC_DISP_DISP_INTERFACE_CONTROL 0x42f | ||
| 294 | #define DISP_DATA_FORMAT_DF1P1C (0 << 0) | ||
| 295 | #define DISP_DATA_FORMAT_DF1P2C24B (1 << 0) | ||
| 296 | #define DISP_DATA_FORMAT_DF1P2C18B (2 << 0) | ||
| 297 | #define DISP_DATA_FORMAT_DF1P2C16B (3 << 0) | ||
| 298 | #define DISP_DATA_FORMAT_DF2S (5 << 0) | ||
| 299 | #define DISP_DATA_FORMAT_DF3S (6 << 0) | ||
| 300 | #define DISP_DATA_FORMAT_DFSPI (7 << 0) | ||
| 301 | #define DISP_DATA_FORMAT_DF1P3C24B (8 << 0) | ||
| 302 | #define DISP_DATA_FORMAT_DF1P3C18B (9 << 0) | ||
| 303 | #define DISP_DATA_ALIGNMENT_MSB (0 << 8) | ||
| 304 | #define DISP_DATA_ALIGNMENT_LSB (1 << 8) | ||
| 305 | #define DISP_DATA_ORDER_RED_BLUE (0 << 9) | ||
| 306 | #define DISP_DATA_ORDER_BLUE_RED (1 << 9) | ||
| 307 | |||
| 308 | #define DC_DISP_DISP_COLOR_CONTROL 0x430 | ||
| 309 | #define BASE_COLOR_SIZE666 (0 << 0) | ||
| 310 | #define BASE_COLOR_SIZE111 (1 << 0) | ||
| 311 | #define BASE_COLOR_SIZE222 (2 << 0) | ||
| 312 | #define BASE_COLOR_SIZE333 (3 << 0) | ||
| 313 | #define BASE_COLOR_SIZE444 (4 << 0) | ||
| 314 | #define BASE_COLOR_SIZE555 (5 << 0) | ||
| 315 | #define BASE_COLOR_SIZE565 (6 << 0) | ||
| 316 | #define BASE_COLOR_SIZE332 (7 << 0) | ||
| 317 | #define BASE_COLOR_SIZE888 (8 << 0) | ||
| 318 | |||
| 319 | #define DITHER_CONTROL_DISABLE (0 << 8) | ||
| 320 | #define DITHER_CONTROL_ORDERED (2 << 8) | ||
| 321 | #define DITHER_CONTROL_ERRDIFF (3 << 8) | ||
| 322 | |||
| 323 | #define DC_DISP_SHIFT_CLOCK_OPTIONS 0x431 | ||
| 324 | #define DC_DISP_DATA_ENABLE_OPTIONS 0x432 | ||
| 325 | #define DE_SELECT_ACTIVE_BLANK 0x0 | ||
| 326 | #define DE_SELECT_ACTIVE 0x1 | ||
| 327 | #define DE_SELECT_ACTIVE_IS 0x2 | ||
| 328 | #define DE_CONTROL_ONECLK (0 << 2) | ||
| 329 | #define DE_CONTROL_NORMAL (1 << 2) | ||
| 330 | #define DE_CONTROL_EARLY_EXT (2 << 2) | ||
| 331 | #define DE_CONTROL_EARLY (3 << 2) | ||
| 332 | #define DE_CONTROL_ACTIVE_BLANK (4 << 2) | ||
| 333 | |||
| 334 | #define DC_DISP_SERIAL_INTERFACE_OPTIONS 0x433 | ||
| 335 | #define DC_DISP_LCD_SPI_OPTIONS 0x434 | ||
| 336 | #define DC_DISP_BORDER_COLOR 0x435 | ||
| 337 | #define DC_DISP_COLOR_KEY0_LOWER 0x436 | ||
| 338 | #define DC_DISP_COLOR_KEY0_UPPER 0x437 | ||
| 339 | #define DC_DISP_COLOR_KEY1_LOWER 0x438 | ||
| 340 | #define DC_DISP_COLOR_KEY1_UPPER 0x439 | ||
| 341 | |||
| 342 | #define DC_DISP_CURSOR_FOREGROUND 0x43c | ||
| 343 | #define DC_DISP_CURSOR_BACKGROUND 0x43d | ||
| 344 | #define CURSOR_COLOR(_r, _g, _b) ((_r) | ((_g) << 8) | ((_b) << 16)) | ||
| 345 | |||
| 346 | #define DC_DISP_CURSOR_START_ADDR 0x43e | ||
| 347 | #define DC_DISP_CURSOR_START_ADDR_NS 0x43f | ||
| 348 | #define CURSOR_START_ADDR_MASK (((1 << 22) - 1) << 10) | ||
| 349 | #define CURSOR_START_ADDR(_addr) ((_addr) >> 10) | ||
| 350 | #define CURSOR_SIZE_64 (1 << 24) | ||
| 351 | |||
| 352 | #define DC_DISP_CURSOR_POSITION 0x440 | ||
| 353 | #define CURSOR_POSITION(_x, _y) \ | ||
| 354 | (((_x) & ((1 << 16) - 1)) | \ | ||
| 355 | (((_y) & ((1 << 16) - 1)) << 16)) | ||
| 356 | |||
| 357 | #define DC_DISP_CURSOR_POSITION_NS 0x441 | ||
| 358 | #define DC_DISP_INIT_SEQ_CONTROL 0x442 | ||
| 359 | #define DC_DISP_SPI_INIT_SEQ_DATA_A 0x443 | ||
| 360 | #define DC_DISP_SPI_INIT_SEQ_DATA_B 0x444 | ||
| 361 | #define DC_DISP_SPI_INIT_SEQ_DATA_C 0x445 | ||
| 362 | #define DC_DISP_SPI_INIT_SEQ_DATA_D 0x446 | ||
| 363 | #define DC_DISP_DC_MCCIF_FIFOCTRL 0x480 | ||
| 364 | #define DC_DISP_MCCIF_DISPLAY0A_HYST 0x481 | ||
| 365 | #define DC_DISP_MCCIF_DISPLAY0B_HYST 0x482 | ||
| 366 | #define DC_DISP_MCCIF_DISPLAY0C_HYST 0x483 | ||
| 367 | #define DC_DISP_MCCIF_DISPLAY1B_HYST 0x484 | ||
| 368 | #define DC_DISP_DAC_CRT_CTRL 0x4c0 | ||
| 369 | #define DC_DISP_DISP_MISC_CONTROL 0x4c1 | ||
| 370 | |||
| 371 | #define DC_WIN_COLOR_PALETTE(x) (0x500 + (x)) | ||
| 372 | |||
| 373 | #define DC_WIN_PALETTE_COLOR_EXT 0x600 | ||
| 374 | #define DC_WIN_H_FILTER_P(x) (0x601 + (x)) | ||
| 375 | #define DC_WIN_CSC_YOF 0x611 | ||
| 376 | #define DC_WIN_CSC_KYRGB 0x612 | ||
| 377 | #define DC_WIN_CSC_KUR 0x613 | ||
| 378 | #define DC_WIN_CSC_KVR 0x614 | ||
| 379 | #define DC_WIN_CSC_KUG 0x615 | ||
| 380 | #define DC_WIN_CSC_KVG 0x616 | ||
| 381 | #define DC_WIN_CSC_KUB 0x617 | ||
| 382 | #define DC_WIN_CSC_KVB 0x618 | ||
| 383 | #define DC_WIN_V_FILTER_P(x) (0x619 + (x)) | ||
| 384 | #define DC_WIN_WIN_OPTIONS 0x700 | ||
| 385 | #define H_DIRECTION_INCREMENT (0 << 0) | ||
| 386 | #define H_DIRECTION_DECREMENT (1 << 0) | ||
| 387 | #define V_DIRECTION_INCREMENT (0 << 2) | ||
| 388 | #define V_DIRECTION_DECREMENT (1 << 2) | ||
| 389 | #define COLOR_EXPAND (1 << 6) | ||
| 390 | #define H_FILTER_ENABLE (1 << 8) | ||
| 391 | #define V_FILTER_ENABLE (1 << 10) | ||
| 392 | #define CP_ENABLE (1 << 16) | ||
| 393 | #define CSC_ENABLE (1 << 18) | ||
| 394 | #define DV_ENABLE (1 << 20) | ||
| 395 | #define WIN_ENABLE (1 << 30) | ||
| 396 | |||
| 397 | #define DC_WIN_BYTE_SWAP 0x701 | ||
| 398 | #define BYTE_SWAP_NOSWAP 0 | ||
| 399 | #define BYTE_SWAP_SWAP2 1 | ||
| 400 | #define BYTE_SWAP_SWAP4 2 | ||
| 401 | #define BYTE_SWAP_SWAP4HW 3 | ||
| 402 | |||
| 403 | #define DC_WIN_BUFFER_CONTROL 0x702 | ||
| 404 | #define BUFFER_CONTROL_HOST 0 | ||
| 405 | #define BUFFER_CONTROL_VI 1 | ||
| 406 | #define BUFFER_CONTROL_EPP 2 | ||
| 407 | #define BUFFER_CONTROL_MPEGE 3 | ||
| 408 | #define BUFFER_CONTROL_SB2D 4 | ||
| 409 | |||
| 410 | #define DC_WIN_COLOR_DEPTH 0x703 | ||
| 411 | |||
| 412 | #define DC_WIN_POSITION 0x704 | ||
| 413 | #define H_POSITION(x) (((x) & 0xfff) << 0) | ||
| 414 | #define V_POSITION(x) (((x) & 0xfff) << 16) | ||
| 415 | |||
| 416 | #define DC_WIN_SIZE 0x705 | ||
| 417 | #define H_SIZE(x) (((x) & 0xfff) << 0) | ||
| 418 | #define V_SIZE(x) (((x) & 0xfff) << 16) | ||
| 419 | |||
| 420 | #define DC_WIN_PRESCALED_SIZE 0x706 | ||
| 421 | #define H_PRESCALED_SIZE(x) (((x) & 0x3fff) << 0) | ||
| 422 | #define V_PRESCALED_SIZE(x) (((x) & 0xfff) << 16) | ||
| 423 | |||
| 424 | #define DC_WIN_H_INITIAL_DDA 0x707 | ||
| 425 | #define DC_WIN_V_INITIAL_DDA 0x708 | ||
| 426 | #define DC_WIN_DDA_INCREMENT 0x709 | ||
| 427 | #define H_DDA_INC(x) (((x) & 0xffff) << 0) | ||
| 428 | #define V_DDA_INC(x) (((x) & 0xffff) << 16) | ||
| 429 | |||
| 430 | #define DC_WIN_LINE_STRIDE 0x70a | ||
| 431 | #define LINE_STRIDE(x) (x) | ||
| 432 | #define UV_LINE_STRIDE(x) (((x) & 0xffff) << 16) | ||
| 433 | #define DC_WIN_BUF_STRIDE 0x70b | ||
| 434 | #define DC_WIN_UV_BUF_STRIDE 0x70c | ||
| 435 | #define DC_WIN_BUFFER_ADDR_MODE 0x70d | ||
| 436 | #define DC_WIN_BUFFER_ADDR_MODE_LINEAR (0 << 0) | ||
| 437 | #define DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV (0 << 16) | ||
| 438 | #define DC_WIN_BUFFER_ADDR_MODE_TILE (1 << 0) | ||
| 439 | #define DC_WIN_BUFFER_ADDR_MODE_TILE_UV (1 << 16) | ||
| 440 | #define DC_WIN_DV_CONTROL 0x70e | ||
| 441 | #define DC_WIN_BLEND_NOKEY 0x70f | ||
| 442 | #define DC_WIN_BLEND_1WIN 0x710 | ||
| 443 | #define DC_WIN_BLEND_2WIN_X 0x711 | ||
| 444 | #define DC_WIN_BLEND_2WIN_Y 0x712 | ||
| 445 | #define DC_WIN_BLEND_3WIN_XY 0x713 | ||
| 446 | #define CKEY_NOKEY (0 << 0) | ||
| 447 | #define CKEY_KEY0 (1 << 0) | ||
| 448 | #define CKEY_KEY1 (2 << 0) | ||
| 449 | #define CKEY_KEY01 (3 << 0) | ||
| 450 | #define BLEND_CONTROL_FIX (0 << 2) | ||
| 451 | #define BLEND_CONTROL_ALPHA (1 << 2) | ||
| 452 | #define BLEND_CONTROL_DEPENDANT (2 << 2) | ||
| 453 | #define BLEND_CONTROL_PREMULT (3 << 2) | ||
| 454 | #define BLEND_WEIGHT0(x) (((x) & 0xff) << 8) | ||
| 455 | #define BLEND_WEIGHT1(x) (((x) & 0xff) << 16) | ||
| 456 | #define BLEND(key, control, weight0, weight1) \ | ||
| 457 | (CKEY_ ## key | BLEND_CONTROL_ ## control | \ | ||
| 458 | BLEND_WEIGHT0(weight0) | BLEND_WEIGHT1(weight1)) | ||
| 459 | |||
| 460 | |||
| 461 | #define DC_WIN_HP_FETCH_CONTROL 0x714 | ||
| 462 | #define DC_WINBUF_START_ADDR 0x800 | ||
| 463 | #define DC_WINBUF_START_ADDR_NS 0x801 | ||
| 464 | #define DC_WINBUF_START_ADDR_U 0x802 | ||
| 465 | #define DC_WINBUF_START_ADDR_U_NS 0x803 | ||
| 466 | #define DC_WINBUF_START_ADDR_V 0x804 | ||
| 467 | #define DC_WINBUF_START_ADDR_V_NS 0x805 | ||
| 468 | #define DC_WINBUF_ADDR_H_OFFSET 0x806 | ||
| 469 | #define DC_WINBUF_ADDR_H_OFFSET_NS 0x807 | ||
| 470 | #define DC_WINBUF_ADDR_V_OFFSET 0x808 | ||
| 471 | #define DC_WINBUF_ADDR_V_OFFSET_NS 0x809 | ||
| 472 | #define DC_WINBUF_UFLOW_STATUS 0x80a | ||
| 473 | |||
| 474 | /* direct versions of DC_WINBUF_UFLOW_STATUS */ | ||
| 475 | #define DC_WINBUF_AD_UFLOW_STATUS 0xbca | ||
| 476 | #define DC_WINBUF_BD_UFLOW_STATUS 0xdca | ||
| 477 | #define DC_WINBUF_CD_UFLOW_STATUS 0xfca | ||
| 478 | |||
| 479 | #define DC_DISP_SD_CONTROL 0x4c2 | ||
| 480 | #define SD_ENABLE_NORMAL (1 << 0) | ||
| 481 | #define SD_ENABLE_ONESHOT (2 << 0) | ||
| 482 | #define SD_USE_VID_LUMA (1 << 2) | ||
| 483 | #define SD_BIN_WIDTH_ONE (0 << 3) | ||
| 484 | #define SD_BIN_WIDTH_TWO (1 << 3) | ||
| 485 | #define SD_BIN_WIDTH_FOUR (2 << 3) | ||
| 486 | #define SD_BIN_WIDTH_EIGHT (3 << 3) | ||
| 487 | #define SD_BIN_WIDTH_MASK (3 << 3) | ||
| 488 | #define SD_AGGRESSIVENESS(x) (((x) & 0x7) << 5) | ||
| 489 | #define SD_HW_UPDATE_DLY(x) (((x) & 0x3) << 8) | ||
| 490 | #define SD_ONESHOT_ENABLE (1 << 10) | ||
| 491 | #define SD_CORRECTION_MODE_AUTO (0 << 11) | ||
| 492 | #define SD_CORRECTION_MODE_MAN (1 << 11) | ||
| 493 | |||
| 494 | #define NUM_BIN_WIDTHS 4 | ||
| 495 | #define STEPS_PER_AGG_LVL 64 | ||
| 496 | #define STEPS_PER_AGG_CHG_LOG2 5 | ||
| 497 | #define STEPS_PER_AGG_CHG (1<<STEPS_PER_AGG_CHG_LOG2) | ||
| 498 | #define ADJ_PHASE_STEP 8 | ||
| 499 | #define K_STEP 4 | ||
| 500 | |||
| 501 | #define DC_DISP_SD_CSC_COEFF 0x4c3 | ||
| 502 | #define SD_CSC_COEFF_R(x) (((x) & 0xf) << 4) | ||
| 503 | #define SD_CSC_COEFF_G(x) (((x) & 0xf) << 12) | ||
| 504 | #define SD_CSC_COEFF_B(x) (((x) & 0xf) << 20) | ||
| 505 | |||
| 506 | #define DC_DISP_SD_LUT(i) (0x4c4 + i) | ||
| 507 | #define DC_DISP_SD_LUT_NUM 9 | ||
| 508 | #define SD_LUT_R(x) (((x) & 0xff) << 0) | ||
| 509 | #define SD_LUT_G(x) (((x) & 0xff) << 8) | ||
| 510 | #define SD_LUT_B(x) (((x) & 0xff) << 16) | ||
| 511 | |||
| 512 | #define DC_DISP_SD_FLICKER_CONTROL 0x4cd | ||
| 513 | #define SD_FC_TIME_LIMIT(x) (((x) & 0xff) << 0) | ||
| 514 | #define SD_FC_THRESHOLD(x) (((x) & 0xff) << 8) | ||
| 515 | |||
| 516 | #define DC_DISP_SD_PIXEL_COUNT 0x4ce | ||
| 517 | |||
| 518 | #define DC_DISP_SD_HISTOGRAM(i) (0x4cf + i) | ||
| 519 | #define DC_DISP_SD_HISTOGRAM_NUM 8 | ||
| 520 | #define SD_HISTOGRAM_BIN_0(val) (((val) & (0xff << 0)) >> 0) | ||
| 521 | #define SD_HISTOGRAM_BIN_1(val) (((val) & (0xff << 8)) >> 8) | ||
| 522 | #define SD_HISTOGRAM_BIN_2(val) (((val) & (0xff << 16)) >> 16) | ||
| 523 | #define SD_HISTOGRAM_BIN_3(val) (((val) & (0xff << 24)) >> 24) | ||
| 524 | |||
| 525 | #define DC_DISP_SD_BL_PARAMETERS 0x4d7 | ||
| 526 | #define SD_BLP_TIME_CONSTANT(x) (((x) & 0x7ff) << 0) | ||
| 527 | #define SD_BLP_STEP(x) (((x) & 0xff) << 16) | ||
| 528 | |||
| 529 | #define DC_DISP_SD_BL_TF(i) (0x4d8 + i) | ||
| 530 | #define DC_DISP_SD_BL_TF_NUM 4 | ||
| 531 | #define SD_BL_TF_POINT_0(x) (((x) & 0xff) << 0) | ||
| 532 | #define SD_BL_TF_POINT_1(x) (((x) & 0xff) << 8) | ||
| 533 | #define SD_BL_TF_POINT_2(x) (((x) & 0xff) << 16) | ||
| 534 | #define SD_BL_TF_POINT_3(x) (((x) & 0xff) << 24) | ||
| 535 | |||
| 536 | #define DC_DISP_SD_BL_CONTROL 0x4dc | ||
| 537 | #define SD_BLC_MODE_MAN (0 << 0) | ||
| 538 | #define SD_BLC_MODE_AUTO (1 << 1) | ||
| 539 | #define SD_BLC_BRIGHTNESS(val) (((val) & (0xff << 8)) >> 8) | ||
| 540 | |||
| 541 | #define DC_DISP_SD_HW_K_VALUES 0x4dd | ||
| 542 | #define SD_HW_K_R(val) (((val) & (0x3ff << 0)) >> 0) | ||
| 543 | #define SD_HW_K_G(val) (((val) & (0x3ff << 10)) >> 10) | ||
| 544 | #define SD_HW_K_B(val) (((val) & (0x3ff << 20)) >> 20) | ||
| 545 | |||
| 546 | #define DC_DISP_SD_MAN_K_VALUES 0x4de | ||
| 547 | #define SD_MAN_K_R(x) (((x) & 0x3ff) << 0) | ||
| 548 | #define SD_MAN_K_G(x) (((x) & 0x3ff) << 10) | ||
| 549 | #define SD_MAN_K_B(x) (((x) & 0x3ff) << 20) | ||
| 550 | |||
| 551 | #define NUM_AGG_PRI_LVLS 4 | ||
| 552 | #define SD_AGG_PRI_LVL(x) ((x) >> 3) | ||
| 553 | #define SD_GET_AGG(x) ((x) & 0x7) | ||
| 554 | |||
| 555 | #endif | ||
diff --git a/drivers/video/tegra/dc/dc_sysfs.c b/drivers/video/tegra/dc/dc_sysfs.c new file mode 100644 index 00000000000..6bb18382e6e --- /dev/null +++ b/drivers/video/tegra/dc/dc_sysfs.c | |||
| @@ -0,0 +1,327 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/dc_sysfs.c | ||
| 3 | * | ||
| 4 | * Copyright (c) 2011, NVIDIA Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 14 | * more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License along | ||
| 17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <linux/platform_device.h> | ||
| 22 | #include <linux/kernel.h> | ||
| 23 | |||
| 24 | #include <mach/dc.h> | ||
| 25 | #include <mach/fb.h> | ||
| 26 | |||
| 27 | #include "dc_reg.h" | ||
| 28 | #include "dc_priv.h" | ||
| 29 | #include "nvsd.h" | ||
| 30 | |||
| 31 | static ssize_t mode_show(struct device *device, | ||
| 32 | struct device_attribute *attr, char *buf) | ||
| 33 | { | ||
| 34 | struct nvhost_device *ndev = to_nvhost_device(device); | ||
| 35 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
| 36 | struct tegra_dc_mode *m; | ||
| 37 | ssize_t res; | ||
| 38 | |||
| 39 | mutex_lock(&dc->lock); | ||
| 40 | m = &dc->mode; | ||
| 41 | res = snprintf(buf, PAGE_SIZE, | ||
| 42 | "pclk: %d\n" | ||
| 43 | "h_ref_to_sync: %d\n" | ||
| 44 | "v_ref_to_sync: %d\n" | ||
| 45 | "h_sync_width: %d\n" | ||
| 46 | "v_sync_width: %d\n" | ||
| 47 | "h_back_porch: %d\n" | ||
| 48 | "v_back_porch: %d\n" | ||
| 49 | "h_active: %d\n" | ||
| 50 | "v_active: %d\n" | ||
| 51 | "h_front_porch: %d\n" | ||
| 52 | "v_front_porch: %d\n" | ||
| 53 | "stereo_mode: %d\n", | ||
| 54 | m->pclk, m->h_ref_to_sync, m->v_ref_to_sync, | ||
| 55 | m->h_sync_width, m->v_sync_width, | ||
| 56 | m->h_back_porch, m->v_back_porch, | ||
| 57 | m->h_active, m->v_active, | ||
| 58 | m->h_front_porch, m->v_front_porch, | ||
| 59 | m->stereo_mode); | ||
| 60 | mutex_unlock(&dc->lock); | ||
| 61 | |||
| 62 | return res; | ||
| 63 | } | ||
| 64 | |||
| 65 | static DEVICE_ATTR(mode, S_IRUGO, mode_show, NULL); | ||
| 66 | |||
| 67 | static ssize_t stats_enable_show(struct device *dev, | ||
| 68 | struct device_attribute *attr, char *buf) | ||
| 69 | { | ||
| 70 | struct nvhost_device *ndev = to_nvhost_device(dev); | ||
| 71 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
| 72 | bool enabled; | ||
| 73 | |||
| 74 | if (mutex_lock_killable(&dc->lock)) | ||
| 75 | return -EINTR; | ||
| 76 | enabled = tegra_dc_stats_get(dc); | ||
| 77 | mutex_unlock(&dc->lock); | ||
| 78 | |||
| 79 | return snprintf(buf, PAGE_SIZE, "%d", enabled); | ||
| 80 | } | ||
| 81 | |||
| 82 | static ssize_t stats_enable_store(struct device *dev, | ||
| 83 | struct device_attribute *attr, const char *buf, size_t count) | ||
| 84 | { | ||
| 85 | struct nvhost_device *ndev = to_nvhost_device(dev); | ||
| 86 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
| 87 | unsigned long val = 0; | ||
| 88 | |||
| 89 | if (strict_strtoul(buf, 10, &val) < 0) | ||
| 90 | return -EINVAL; | ||
| 91 | |||
| 92 | if (mutex_lock_killable(&dc->lock)) | ||
| 93 | return -EINTR; | ||
| 94 | tegra_dc_stats_enable(dc, !!val); | ||
| 95 | mutex_unlock(&dc->lock); | ||
| 96 | |||
| 97 | return count; | ||
| 98 | } | ||
| 99 | |||
| 100 | static DEVICE_ATTR(stats_enable, S_IRUGO|S_IWUSR, | ||
| 101 | stats_enable_show, stats_enable_store); | ||
| 102 | |||
| 103 | static ssize_t enable_show(struct device *device, | ||
| 104 | struct device_attribute *attr, char *buf) | ||
| 105 | { | ||
| 106 | struct nvhost_device *ndev = to_nvhost_device(device); | ||
| 107 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
| 108 | ssize_t res; | ||
| 109 | |||
| 110 | mutex_lock(&dc->lock); | ||
| 111 | res = snprintf(buf, PAGE_SIZE, "%d\n", dc->enabled); | ||
| 112 | mutex_unlock(&dc->lock); | ||
| 113 | return res; | ||
| 114 | } | ||
| 115 | |||
| 116 | static ssize_t enable_store(struct device *dev, | ||
| 117 | struct device_attribute *attr, const char *buf, size_t count) | ||
| 118 | { | ||
| 119 | struct nvhost_device *ndev = to_nvhost_device(dev); | ||
| 120 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
| 121 | unsigned long val = 0; | ||
| 122 | |||
| 123 | if (strict_strtoul(buf, 10, &val) < 0) | ||
| 124 | return -EINVAL; | ||
| 125 | |||
| 126 | if (val) { | ||
| 127 | tegra_dc_enable(dc); | ||
| 128 | } else { | ||
| 129 | tegra_dc_disable(dc); | ||
| 130 | } | ||
| 131 | |||
| 132 | return count; | ||
| 133 | } | ||
| 134 | |||
| 135 | static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR, enable_show, enable_store); | ||
| 136 | |||
| 137 | static ssize_t crc_checksum_latched_show(struct device *device, | ||
| 138 | struct device_attribute *attr, char *buf) | ||
| 139 | { | ||
| 140 | struct nvhost_device *ndev = to_nvhost_device(device); | ||
| 141 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
| 142 | |||
| 143 | u32 crc; | ||
| 144 | |||
| 145 | if (!dc->enabled) { | ||
| 146 | dev_err(&dc->ndev->dev, "Failed to get dc.\n"); | ||
| 147 | return -EFAULT; | ||
| 148 | } | ||
| 149 | |||
| 150 | crc = tegra_dc_read_checksum_latched(dc); | ||
| 151 | |||
| 152 | return snprintf(buf, PAGE_SIZE, "%u", crc); | ||
| 153 | } | ||
| 154 | |||
| 155 | static ssize_t crc_checksum_latched_store(struct device *dev, | ||
| 156 | struct device_attribute *attr, const char *buf, size_t count) | ||
| 157 | { | ||
| 158 | struct nvhost_device *ndev = to_nvhost_device(dev); | ||
| 159 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
| 160 | unsigned long val = 0; | ||
| 161 | |||
| 162 | if (!dc->enabled) { | ||
| 163 | dev_err(&dc->ndev->dev, "Failed to get dc.\n"); | ||
| 164 | return -EFAULT; | ||
| 165 | } | ||
| 166 | |||
| 167 | if (strict_strtoul(buf, 10, &val) < 0) | ||
| 168 | return -EINVAL; | ||
| 169 | |||
| 170 | if (val == 1) { | ||
| 171 | tegra_dc_enable_crc(dc); | ||
| 172 | dev_err(&dc->ndev->dev, "crc is enabled.\n"); | ||
| 173 | } else if (val == 0) { | ||
| 174 | tegra_dc_disable_crc(dc); | ||
| 175 | dev_err(&dc->ndev->dev, "crc is disabled.\n"); | ||
| 176 | } else | ||
| 177 | dev_err(&dc->ndev->dev, "Invalid input.\n"); | ||
| 178 | |||
| 179 | return count; | ||
| 180 | } | ||
| 181 | static DEVICE_ATTR(crc_checksum_latched, S_IRUGO|S_IWUSR, | ||
| 182 | crc_checksum_latched_show, crc_checksum_latched_store); | ||
| 183 | |||
| 184 | #define ORIENTATION_PORTRAIT "portrait" | ||
| 185 | #define ORIENTATION_LANDSCAPE "landscape" | ||
| 186 | |||
| 187 | static ssize_t orientation_3d_show(struct device *dev, | ||
| 188 | struct device_attribute *attr, char *buf) | ||
| 189 | { | ||
| 190 | struct nvhost_device *ndev = to_nvhost_device(dev); | ||
| 191 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
| 192 | struct tegra_dc_out *dc_out = dc->out; | ||
| 193 | const char *orientation; | ||
| 194 | switch (dc_out->stereo->orientation) { | ||
| 195 | case TEGRA_DC_STEREO_LANDSCAPE: | ||
| 196 | orientation = ORIENTATION_LANDSCAPE; | ||
| 197 | break; | ||
| 198 | case TEGRA_DC_STEREO_PORTRAIT: | ||
| 199 | orientation = ORIENTATION_PORTRAIT; | ||
| 200 | break; | ||
| 201 | default: | ||
| 202 | pr_err("Invalid value is stored for stereo_orientation.\n"); | ||
| 203 | return -EINVAL; | ||
| 204 | } | ||
| 205 | return snprintf(buf, PAGE_SIZE, "%s\n", orientation); | ||
| 206 | } | ||
| 207 | |||
| 208 | static ssize_t orientation_3d_store(struct device *dev, | ||
| 209 | struct device_attribute *attr, const char *buf, size_t cnt) | ||
| 210 | { | ||
| 211 | struct nvhost_device *ndev = to_nvhost_device(dev); | ||
| 212 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
| 213 | struct tegra_dc_out *dc_out = dc->out; | ||
| 214 | struct tegra_stereo_out *stereo = dc_out->stereo; | ||
| 215 | int orientation; | ||
| 216 | |||
| 217 | if (0 == strncmp(buf, ORIENTATION_PORTRAIT, | ||
| 218 | min(cnt, ARRAY_SIZE(ORIENTATION_PORTRAIT) - 1))) { | ||
| 219 | orientation = TEGRA_DC_STEREO_PORTRAIT; | ||
| 220 | } else if (0 == strncmp(buf, ORIENTATION_LANDSCAPE, | ||
| 221 | min(cnt, ARRAY_SIZE(ORIENTATION_LANDSCAPE) - 1))) { | ||
| 222 | orientation = TEGRA_DC_STEREO_LANDSCAPE; | ||
| 223 | } else { | ||
| 224 | pr_err("Invalid property value for stereo_orientation.\n"); | ||
| 225 | return -EINVAL; | ||
| 226 | } | ||
| 227 | stereo->orientation = orientation; | ||
| 228 | stereo->set_orientation(orientation); | ||
| 229 | return cnt; | ||
| 230 | } | ||
| 231 | |||
| 232 | static DEVICE_ATTR(stereo_orientation, | ||
| 233 | S_IRUGO|S_IWUSR, orientation_3d_show, orientation_3d_store); | ||
| 234 | |||
| 235 | #define MODE_2D "2d" | ||
| 236 | #define MODE_3D "3d" | ||
| 237 | |||
| 238 | static ssize_t mode_3d_show(struct device *dev, | ||
| 239 | struct device_attribute *attr, char *buf) | ||
| 240 | { | ||
| 241 | struct nvhost_device *ndev = to_nvhost_device(dev); | ||
| 242 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
| 243 | struct tegra_dc_out *dc_out = dc->out; | ||
| 244 | const char *mode; | ||
| 245 | switch (dc_out->stereo->mode_2d_3d) { | ||
| 246 | case TEGRA_DC_STEREO_MODE_2D: | ||
| 247 | mode = MODE_2D; | ||
| 248 | break; | ||
| 249 | case TEGRA_DC_STEREO_MODE_3D: | ||
| 250 | mode = MODE_3D; | ||
| 251 | break; | ||
| 252 | default: | ||
| 253 | pr_err("Invalid value is stored for stereo_mode.\n"); | ||
| 254 | return -EINVAL; | ||
| 255 | } | ||
| 256 | return snprintf(buf, PAGE_SIZE, "%s\n", mode); | ||
| 257 | } | ||
| 258 | |||
| 259 | static ssize_t mode_3d_store(struct device *dev, | ||
| 260 | struct device_attribute *attr, const char *buf, size_t cnt) | ||
| 261 | { | ||
| 262 | struct nvhost_device *ndev = to_nvhost_device(dev); | ||
| 263 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
| 264 | struct tegra_dc_out *dc_out = dc->out; | ||
| 265 | struct tegra_stereo_out *stereo = dc_out->stereo; | ||
| 266 | int mode; | ||
| 267 | |||
| 268 | if (0 == strncmp(buf, MODE_2D, min(cnt, ARRAY_SIZE(MODE_2D) - 1))) { | ||
| 269 | mode = TEGRA_DC_STEREO_MODE_2D; | ||
| 270 | } else if (0 == strncmp(buf, MODE_3D, | ||
| 271 | min(cnt, ARRAY_SIZE(MODE_3D) - 1))) { | ||
| 272 | mode = TEGRA_DC_STEREO_MODE_3D; | ||
| 273 | } else { | ||
| 274 | pr_err("Invalid property value for stereo_mode.\n"); | ||
| 275 | return -EINVAL; | ||
| 276 | } | ||
| 277 | stereo->mode_2d_3d = mode; | ||
| 278 | stereo->set_mode(mode); | ||
| 279 | return cnt; | ||
| 280 | } | ||
| 281 | |||
| 282 | static DEVICE_ATTR(stereo_mode, | ||
| 283 | S_IRUGO|S_IWUSR, mode_3d_show, mode_3d_store); | ||
| 284 | |||
| 285 | void __devexit tegra_dc_remove_sysfs(struct device *dev) | ||
| 286 | { | ||
| 287 | struct nvhost_device *ndev = to_nvhost_device(dev); | ||
| 288 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
| 289 | struct tegra_dc_sd_settings *sd_settings = dc->out->sd_settings; | ||
| 290 | |||
| 291 | device_remove_file(dev, &dev_attr_mode); | ||
| 292 | device_remove_file(dev, &dev_attr_enable); | ||
| 293 | device_remove_file(dev, &dev_attr_stats_enable); | ||
| 294 | device_remove_file(dev, &dev_attr_crc_checksum_latched); | ||
| 295 | |||
| 296 | if (dc->out->stereo) { | ||
| 297 | device_remove_file(dev, &dev_attr_stereo_orientation); | ||
| 298 | device_remove_file(dev, &dev_attr_stereo_mode); | ||
| 299 | } | ||
| 300 | |||
| 301 | if (sd_settings) | ||
| 302 | nvsd_remove_sysfs(dev); | ||
| 303 | } | ||
| 304 | |||
| 305 | void tegra_dc_create_sysfs(struct device *dev) | ||
| 306 | { | ||
| 307 | struct nvhost_device *ndev = to_nvhost_device(dev); | ||
| 308 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
| 309 | struct tegra_dc_sd_settings *sd_settings = dc->out->sd_settings; | ||
| 310 | int error = 0; | ||
| 311 | |||
| 312 | error |= device_create_file(dev, &dev_attr_mode); | ||
| 313 | error |= device_create_file(dev, &dev_attr_enable); | ||
| 314 | error |= device_create_file(dev, &dev_attr_stats_enable); | ||
| 315 | error |= device_create_file(dev, &dev_attr_crc_checksum_latched); | ||
| 316 | |||
| 317 | if (dc->out->stereo) { | ||
| 318 | error |= device_create_file(dev, &dev_attr_stereo_orientation); | ||
| 319 | error |= device_create_file(dev, &dev_attr_stereo_mode); | ||
| 320 | } | ||
| 321 | |||
| 322 | if (sd_settings) | ||
| 323 | error |= nvsd_create_sysfs(dev); | ||
| 324 | |||
| 325 | if (error) | ||
| 326 | dev_err(&ndev->dev, "Failed to create sysfs attributes!\n"); | ||
| 327 | } | ||
diff --git a/drivers/video/tegra/dc/dsi.c b/drivers/video/tegra/dc/dsi.c new file mode 100644 index 00000000000..c33d6e0a58b --- /dev/null +++ b/drivers/video/tegra/dc/dsi.c | |||
| @@ -0,0 +1,3042 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/dsi.c | ||
| 3 | * | ||
| 4 | * Copyright (c) 2011, 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/clk.h> | ||
| 18 | #include <linux/delay.h> | ||
| 19 | #include <linux/err.h> | ||
| 20 | #include <linux/fb.h> | ||
| 21 | #include <linux/gpio.h> | ||
| 22 | #include <linux/interrupt.h> | ||
| 23 | #include <linux/kernel.h> | ||
| 24 | #include <linux/slab.h> | ||
| 25 | #include <linux/spinlock.h> | ||
| 26 | #include <linux/workqueue.h> | ||
| 27 | |||
| 28 | #include <mach/clk.h> | ||
| 29 | #include <mach/dc.h> | ||
| 30 | #include <mach/fb.h> | ||
| 31 | #include <mach/csi.h> | ||
| 32 | #include <linux/nvhost.h> | ||
| 33 | |||
| 34 | #include "dc_reg.h" | ||
| 35 | #include "dc_priv.h" | ||
| 36 | #include "dsi_regs.h" | ||
| 37 | #include "dsi.h" | ||
| 38 | |||
| 39 | #define DSI_USE_SYNC_POINTS 1 | ||
| 40 | #define S_TO_MS(x) (1000 * (x)) | ||
| 41 | |||
| 42 | #define DSI_MODULE_NOT_INIT 0x0 | ||
| 43 | #define DSI_MODULE_INIT 0x1 | ||
| 44 | |||
| 45 | #define DSI_LPHS_NOT_INIT 0x0 | ||
| 46 | #define DSI_LPHS_IN_LP_MODE 0x1 | ||
| 47 | #define DSI_LPHS_IN_HS_MODE 0x2 | ||
| 48 | |||
| 49 | #define DSI_VIDEO_TYPE_NOT_INIT 0x0 | ||
| 50 | #define DSI_VIDEO_TYPE_VIDEO_MODE 0x1 | ||
| 51 | #define DSI_VIDEO_TYPE_CMD_MODE 0x2 | ||
| 52 | |||
| 53 | #define DSI_DRIVEN_MODE_NOT_INIT 0x0 | ||
| 54 | #define DSI_DRIVEN_MODE_DC 0x1 | ||
| 55 | #define DSI_DRIVEN_MODE_HOST 0x2 | ||
| 56 | |||
| 57 | #define DSI_PHYCLK_OUT_DIS 0x0 | ||
| 58 | #define DSI_PHYCLK_OUT_EN 0x1 | ||
| 59 | |||
| 60 | #define DSI_PHYCLK_NOT_INIT 0x0 | ||
| 61 | #define DSI_PHYCLK_CONTINUOUS 0x1 | ||
| 62 | #define DSI_PHYCLK_TX_ONLY 0x2 | ||
| 63 | |||
| 64 | #define DSI_CLK_BURST_NOT_INIT 0x0 | ||
| 65 | #define DSI_CLK_BURST_NONE_BURST 0x1 | ||
| 66 | #define DSI_CLK_BURST_BURST_MODE 0x2 | ||
| 67 | |||
| 68 | #define DSI_DC_STREAM_DISABLE 0x0 | ||
| 69 | #define DSI_DC_STREAM_ENABLE 0x1 | ||
| 70 | |||
| 71 | #define DSI_LP_OP_NOT_INIT 0x0 | ||
| 72 | #define DSI_LP_OP_WRITE 0x1 | ||
| 73 | #define DSI_LP_OP_READ 0x2 | ||
| 74 | |||
| 75 | static bool enable_read_debug; | ||
| 76 | module_param(enable_read_debug, bool, 0644); | ||
| 77 | MODULE_PARM_DESC(enable_read_debug, | ||
| 78 | "Enable to print read fifo and return packet type"); | ||
| 79 | |||
| 80 | struct dsi_status { | ||
| 81 | unsigned init:2; | ||
| 82 | |||
| 83 | unsigned lphs:2; | ||
| 84 | |||
| 85 | unsigned vtype:2; | ||
| 86 | unsigned driven:2; | ||
| 87 | |||
| 88 | unsigned clk_out:2; | ||
| 89 | unsigned clk_mode:2; | ||
| 90 | unsigned clk_burst:2; | ||
| 91 | |||
| 92 | unsigned lp_op:2; | ||
| 93 | |||
| 94 | unsigned dc_stream:1; | ||
| 95 | }; | ||
| 96 | |||
| 97 | /* source of video data */ | ||
| 98 | enum { | ||
| 99 | TEGRA_DSI_DRIVEN_BY_DC, | ||
| 100 | TEGRA_DSI_DRIVEN_BY_HOST, | ||
| 101 | }; | ||
| 102 | |||
| 103 | struct tegra_dc_dsi_data { | ||
| 104 | struct tegra_dc *dc; | ||
| 105 | void __iomem *base; | ||
| 106 | struct resource *base_res; | ||
| 107 | |||
| 108 | struct clk *dc_clk; | ||
| 109 | struct clk *dsi_clk; | ||
| 110 | bool clk_ref; | ||
| 111 | |||
| 112 | struct mutex lock; | ||
| 113 | |||
| 114 | /* data from board info */ | ||
| 115 | struct tegra_dsi_out info; | ||
| 116 | |||
| 117 | struct dsi_status status; | ||
| 118 | |||
| 119 | struct dsi_phy_timing_inclk phy_timing; | ||
| 120 | |||
| 121 | u8 driven_mode; | ||
| 122 | u8 controller_index; | ||
| 123 | |||
| 124 | u8 pixel_scaler_mul; | ||
| 125 | u8 pixel_scaler_div; | ||
| 126 | |||
| 127 | u32 default_shift_clk_div; | ||
| 128 | u32 default_pixel_clk_khz; | ||
| 129 | u32 default_hs_clk_khz; | ||
| 130 | |||
| 131 | u32 shift_clk_div; | ||
| 132 | u32 target_hs_clk_khz; | ||
| 133 | u32 target_lp_clk_khz; | ||
| 134 | |||
| 135 | u32 syncpt_id; | ||
| 136 | u32 syncpt_val; | ||
| 137 | |||
| 138 | u16 current_bit_clk_ns; | ||
| 139 | u32 current_dsi_clk_khz; | ||
| 140 | |||
| 141 | u32 dsi_control_val; | ||
| 142 | |||
| 143 | bool ulpm; | ||
| 144 | bool enabled; | ||
| 145 | }; | ||
| 146 | |||
| 147 | const u32 dsi_pkt_seq_reg[NUMOF_PKT_SEQ] = { | ||
| 148 | DSI_PKT_SEQ_0_LO, | ||
| 149 | DSI_PKT_SEQ_0_HI, | ||
| 150 | DSI_PKT_SEQ_1_LO, | ||
| 151 | DSI_PKT_SEQ_1_HI, | ||
| 152 | DSI_PKT_SEQ_2_LO, | ||
| 153 | DSI_PKT_SEQ_2_HI, | ||
| 154 | DSI_PKT_SEQ_3_LO, | ||
| 155 | DSI_PKT_SEQ_3_HI, | ||
| 156 | DSI_PKT_SEQ_4_LO, | ||
| 157 | DSI_PKT_SEQ_4_HI, | ||
| 158 | DSI_PKT_SEQ_5_LO, | ||
| 159 | DSI_PKT_SEQ_5_HI, | ||
| 160 | }; | ||
| 161 | |||
| 162 | const u32 dsi_pkt_seq_video_non_burst_syne[NUMOF_PKT_SEQ] = { | ||
| 163 | PKT_ID0(CMD_VS) | PKT_LEN0(0) | PKT_ID1(CMD_EOT) | PKT_LEN1(0) | PKT_LP, | ||
| 164 | 0, | ||
| 165 | PKT_ID0(CMD_VE) | PKT_LEN0(0) | PKT_ID1(CMD_EOT) | PKT_LEN1(0) | PKT_LP, | ||
| 166 | 0, | ||
| 167 | PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_EOT) | PKT_LEN1(0) | PKT_LP, | ||
| 168 | 0, | ||
| 169 | PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_BLNK) | PKT_LEN1(1) | | ||
| 170 | PKT_ID2(CMD_HE) | PKT_LEN2(0), | ||
| 171 | PKT_ID3(CMD_BLNK) | PKT_LEN3(2) | PKT_ID4(CMD_RGB) | PKT_LEN4(3) | | ||
| 172 | PKT_ID5(CMD_BLNK) | PKT_LEN5(4), | ||
| 173 | PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_EOT) | PKT_LEN1(0) | PKT_LP, | ||
| 174 | 0, | ||
| 175 | PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_BLNK) | PKT_LEN1(1) | | ||
| 176 | PKT_ID2(CMD_HE) | PKT_LEN2(0), | ||
| 177 | PKT_ID3(CMD_BLNK) | PKT_LEN3(2) | PKT_ID4(CMD_RGB) | PKT_LEN4(3) | | ||
| 178 | PKT_ID5(CMD_BLNK) | PKT_LEN5(4), | ||
| 179 | }; | ||
| 180 | |||
| 181 | const u32 dsi_pkt_seq_video_non_burst[NUMOF_PKT_SEQ] = { | ||
| 182 | PKT_ID0(CMD_VS) | PKT_LEN0(0) | PKT_ID1(CMD_EOT) | PKT_LEN1(0) | PKT_LP, | ||
| 183 | 0, | ||
| 184 | PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_EOT) | PKT_LEN1(0) | PKT_LP, | ||
| 185 | 0, | ||
| 186 | PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_EOT) | PKT_LEN1(0) | PKT_LP, | ||
| 187 | 0, | ||
| 188 | PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_BLNK) | PKT_LEN1(2) | | ||
| 189 | PKT_ID2(CMD_RGB) | PKT_LEN2(3), | ||
| 190 | PKT_ID3(CMD_BLNK) | PKT_LEN3(4), | ||
| 191 | PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_EOT) | PKT_LEN1(0) | PKT_LP, | ||
| 192 | 0, | ||
| 193 | PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_BLNK) | PKT_LEN1(2) | | ||
| 194 | PKT_ID2(CMD_RGB) | PKT_LEN2(3), | ||
| 195 | PKT_ID3(CMD_BLNK) | PKT_LEN3(4), | ||
| 196 | }; | ||
| 197 | |||
| 198 | static const u32 dsi_pkt_seq_video_burst[NUMOF_PKT_SEQ] = { | ||
| 199 | PKT_ID0(CMD_VS) | PKT_LEN0(0) | PKT_ID1(CMD_EOT) | PKT_LEN1(7) | PKT_LP, | ||
| 200 | 0, | ||
| 201 | PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_EOT) | PKT_LEN1(7) | PKT_LP, | ||
| 202 | 0, | ||
| 203 | PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_EOT) | PKT_LEN1(7) | PKT_LP, | ||
| 204 | 0, | ||
| 205 | PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_BLNK) | PKT_LEN1(2)| | ||
| 206 | PKT_ID2(CMD_RGB) | PKT_LEN2(3) | PKT_LP, | ||
| 207 | PKT_ID0(CMD_EOT) | PKT_LEN0(7), | ||
| 208 | PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_EOT) | PKT_LEN1(7) | PKT_LP, | ||
| 209 | 0, | ||
| 210 | PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_BLNK) | PKT_LEN1(2)| | ||
| 211 | PKT_ID2(CMD_RGB) | PKT_LEN2(3) | PKT_LP, | ||
| 212 | PKT_ID0(CMD_EOT) | PKT_LEN0(7), | ||
| 213 | }; | ||
| 214 | |||
| 215 | static const u32 dsi_pkt_seq_video_burst_no_eot[NUMOF_PKT_SEQ] = { | ||
| 216 | PKT_ID0(CMD_VS) | PKT_LEN0(0) | PKT_ID1(CMD_EOT) | PKT_LEN1(0) | PKT_LP, | ||
| 217 | 0, | ||
| 218 | PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_EOT) | PKT_LEN1(0) | PKT_LP, | ||
| 219 | 0, | ||
| 220 | PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_EOT) | PKT_LEN1(0) | PKT_LP, | ||
| 221 | 0, | ||
| 222 | PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_BLNK) | PKT_LEN1(2)| | ||
| 223 | PKT_ID2(CMD_RGB) | PKT_LEN2(3) | PKT_LP, | ||
| 224 | PKT_ID0(CMD_EOT) | PKT_LEN0(0), | ||
| 225 | PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_EOT) | PKT_LEN1(0) | PKT_LP, | ||
| 226 | 0, | ||
| 227 | PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_BLNK) | PKT_LEN1(2)| | ||
| 228 | PKT_ID2(CMD_RGB) | PKT_LEN2(3) | PKT_LP, | ||
| 229 | PKT_ID0(CMD_EOT) | PKT_LEN0(0), | ||
| 230 | }; | ||
| 231 | |||
| 232 | /* TODO: verify with hw about this format */ | ||
| 233 | const u32 dsi_pkt_seq_cmd_mode[NUMOF_PKT_SEQ] = { | ||
| 234 | 0, | ||
| 235 | 0, | ||
| 236 | 0, | ||
| 237 | 0, | ||
| 238 | 0, | ||
| 239 | 0, | ||
| 240 | PKT_ID0(CMD_LONGW) | PKT_LEN0(3) | PKT_ID1(CMD_EOT) | PKT_LEN1(7), | ||
| 241 | 0, | ||
| 242 | 0, | ||
| 243 | 0, | ||
| 244 | PKT_ID0(CMD_LONGW) | PKT_LEN0(3) | PKT_ID1(CMD_EOT) | PKT_LEN1(7), | ||
| 245 | 0, | ||
| 246 | }; | ||
| 247 | |||
| 248 | const u32 init_reg[] = { | ||
| 249 | DSI_INT_ENABLE, | ||
| 250 | DSI_INT_STATUS, | ||
| 251 | DSI_INT_MASK, | ||
| 252 | DSI_INIT_SEQ_DATA_0, | ||
| 253 | DSI_INIT_SEQ_DATA_1, | ||
| 254 | DSI_INIT_SEQ_DATA_2, | ||
| 255 | DSI_INIT_SEQ_DATA_3, | ||
| 256 | DSI_INIT_SEQ_DATA_4, | ||
| 257 | DSI_INIT_SEQ_DATA_5, | ||
| 258 | DSI_INIT_SEQ_DATA_6, | ||
| 259 | DSI_INIT_SEQ_DATA_7, | ||
| 260 | DSI_DCS_CMDS, | ||
| 261 | DSI_PKT_SEQ_0_LO, | ||
| 262 | DSI_PKT_SEQ_1_LO, | ||
| 263 | DSI_PKT_SEQ_2_LO, | ||
| 264 | DSI_PKT_SEQ_3_LO, | ||
| 265 | DSI_PKT_SEQ_4_LO, | ||
| 266 | DSI_PKT_SEQ_5_LO, | ||
| 267 | DSI_PKT_SEQ_0_HI, | ||
| 268 | DSI_PKT_SEQ_1_HI, | ||
| 269 | DSI_PKT_SEQ_2_HI, | ||
| 270 | DSI_PKT_SEQ_3_HI, | ||
| 271 | DSI_PKT_SEQ_4_HI, | ||
| 272 | DSI_PKT_SEQ_5_HI, | ||
| 273 | DSI_CONTROL, | ||
| 274 | DSI_HOST_DSI_CONTROL, | ||
| 275 | DSI_PAD_CONTROL, | ||
| 276 | DSI_PAD_CONTROL_CD, | ||
| 277 | DSI_SOL_DELAY, | ||
| 278 | DSI_MAX_THRESHOLD, | ||
| 279 | DSI_TRIGGER, | ||
| 280 | DSI_TX_CRC, | ||
| 281 | DSI_INIT_SEQ_CONTROL, | ||
| 282 | DSI_PKT_LEN_0_1, | ||
| 283 | DSI_PKT_LEN_2_3, | ||
| 284 | DSI_PKT_LEN_4_5, | ||
| 285 | DSI_PKT_LEN_6_7, | ||
| 286 | }; | ||
| 287 | |||
| 288 | inline unsigned long tegra_dsi_readl(struct tegra_dc_dsi_data *dsi, u32 reg) | ||
| 289 | { | ||
| 290 | BUG_ON(!nvhost_module_powered(nvhost_get_host(dsi->dc->ndev)->dev)); | ||
| 291 | return readl(dsi->base + reg * 4); | ||
| 292 | } | ||
| 293 | EXPORT_SYMBOL(tegra_dsi_readl); | ||
| 294 | |||
| 295 | inline void tegra_dsi_writel(struct tegra_dc_dsi_data *dsi, u32 val, u32 reg) | ||
| 296 | { | ||
| 297 | BUG_ON(!nvhost_module_powered(nvhost_get_host(dsi->dc->ndev)->dev)); | ||
| 298 | writel(val, dsi->base + reg * 4); | ||
| 299 | } | ||
| 300 | EXPORT_SYMBOL(tegra_dsi_writel); | ||
| 301 | |||
| 302 | static int tegra_dsi_syncpt(struct tegra_dc_dsi_data *dsi) | ||
| 303 | { | ||
| 304 | u32 val; | ||
| 305 | int ret; | ||
| 306 | |||
| 307 | ret = 0; | ||
| 308 | |||
| 309 | dsi->syncpt_val = nvhost_syncpt_read( | ||
| 310 | &nvhost_get_host(dsi->dc->ndev)->syncpt, | ||
| 311 | dsi->syncpt_id); | ||
| 312 | |||
| 313 | val = DSI_INCR_SYNCPT_COND(OP_DONE) | | ||
| 314 | DSI_INCR_SYNCPT_INDX(dsi->syncpt_id); | ||
| 315 | tegra_dsi_writel(dsi, val, DSI_INCR_SYNCPT); | ||
| 316 | |||
| 317 | /* TODO: Use interrupt rather than polling */ | ||
| 318 | ret = nvhost_syncpt_wait(&nvhost_get_host(dsi->dc->ndev)->syncpt, | ||
| 319 | dsi->syncpt_id, dsi->syncpt_val + 1); | ||
| 320 | if (ret < 0) { | ||
| 321 | dev_err(&dsi->dc->ndev->dev, "DSI sync point failure\n"); | ||
| 322 | goto fail; | ||
| 323 | } | ||
| 324 | |||
| 325 | (dsi->syncpt_val)++; | ||
| 326 | return 0; | ||
| 327 | fail: | ||
| 328 | return ret; | ||
| 329 | } | ||
| 330 | |||
| 331 | static u32 tegra_dsi_get_hs_clk_rate(struct tegra_dc_dsi_data *dsi) | ||
| 332 | { | ||
| 333 | u32 dsi_clock_rate_khz; | ||
| 334 | |||
| 335 | switch (dsi->info.video_burst_mode) { | ||
| 336 | case TEGRA_DSI_VIDEO_BURST_MODE_LOW_SPEED: | ||
| 337 | case TEGRA_DSI_VIDEO_BURST_MODE_MEDIUM_SPEED: | ||
| 338 | case TEGRA_DSI_VIDEO_BURST_MODE_FAST_SPEED: | ||
| 339 | case TEGRA_DSI_VIDEO_BURST_MODE_FASTEST_SPEED: | ||
| 340 | /* Calculate DSI HS clock rate for DSI burst mode */ | ||
| 341 | dsi_clock_rate_khz = dsi->default_pixel_clk_khz * | ||
| 342 | dsi->shift_clk_div; | ||
| 343 | break; | ||
| 344 | case TEGRA_DSI_VIDEO_NONE_BURST_MODE: | ||
| 345 | case TEGRA_DSI_VIDEO_NONE_BURST_MODE_WITH_SYNC_END: | ||
| 346 | case TEGRA_DSI_VIDEO_BURST_MODE_LOWEST_SPEED: | ||
| 347 | default: | ||
| 348 | /* Clock rate is default DSI clock rate for non-burst mode */ | ||
| 349 | dsi_clock_rate_khz = dsi->default_hs_clk_khz; | ||
| 350 | break; | ||
| 351 | } | ||
| 352 | |||
| 353 | return dsi_clock_rate_khz; | ||
| 354 | } | ||
| 355 | |||
| 356 | static u32 tegra_dsi_get_lp_clk_rate(struct tegra_dc_dsi_data *dsi, u8 lp_op) | ||
| 357 | { | ||
| 358 | u32 dsi_clock_rate_khz; | ||
| 359 | |||
| 360 | if (dsi->info.enable_hs_clock_on_lp_cmd_mode) | ||
| 361 | if (dsi->info.hs_clk_in_lp_cmd_mode_freq_khz) | ||
| 362 | dsi_clock_rate_khz = | ||
| 363 | dsi->info.hs_clk_in_lp_cmd_mode_freq_khz; | ||
| 364 | else | ||
| 365 | dsi_clock_rate_khz = tegra_dsi_get_hs_clk_rate(dsi); | ||
| 366 | else | ||
| 367 | if (lp_op == DSI_LP_OP_READ) | ||
| 368 | dsi_clock_rate_khz = | ||
| 369 | dsi->info.lp_read_cmd_mode_freq_khz; | ||
| 370 | else | ||
| 371 | dsi_clock_rate_khz = | ||
| 372 | dsi->info.lp_cmd_mode_freq_khz; | ||
| 373 | |||
| 374 | return dsi_clock_rate_khz; | ||
| 375 | } | ||
| 376 | |||
| 377 | static u32 tegra_dsi_get_shift_clk_div(struct tegra_dc_dsi_data *dsi) | ||
| 378 | { | ||
| 379 | u32 shift_clk_div; | ||
| 380 | u32 max_shift_clk_div; | ||
| 381 | u32 burst_width; | ||
| 382 | u32 burst_width_max; | ||
| 383 | |||
| 384 | /* Get the real value of default shift_clk_div. default_shift_clk_div | ||
| 385 | * holds the real value of shift_clk_div. | ||
| 386 | */ | ||
| 387 | shift_clk_div = dsi->default_shift_clk_div; | ||
| 388 | |||
| 389 | /* Calculate shift_clk_div which can matche the video_burst_mode. */ | ||
| 390 | if (dsi->info.video_burst_mode >= | ||
| 391 | TEGRA_DSI_VIDEO_BURST_MODE_LOWEST_SPEED) { | ||
| 392 | /* The max_shift_clk_div is multiplied by 10 to save the | ||
| 393 | * fraction | ||
| 394 | */ | ||
| 395 | if (dsi->info.max_panel_freq_khz >= dsi->default_hs_clk_khz) | ||
| 396 | max_shift_clk_div = dsi->info.max_panel_freq_khz | ||
| 397 | * shift_clk_div * 10 / dsi->default_hs_clk_khz; | ||
| 398 | else | ||
| 399 | max_shift_clk_div = shift_clk_div * 10; | ||
| 400 | |||
| 401 | burst_width = dsi->info.video_burst_mode | ||
| 402 | - TEGRA_DSI_VIDEO_BURST_MODE_LOWEST_SPEED; | ||
| 403 | burst_width_max = TEGRA_DSI_VIDEO_BURST_MODE_FASTEST_SPEED | ||
| 404 | - TEGRA_DSI_VIDEO_BURST_MODE_LOWEST_SPEED; | ||
| 405 | |||
| 406 | shift_clk_div = (max_shift_clk_div - shift_clk_div * 10) * | ||
| 407 | burst_width / (burst_width_max * 10) + shift_clk_div; | ||
| 408 | } | ||
| 409 | |||
| 410 | return shift_clk_div; | ||
| 411 | } | ||
| 412 | |||
| 413 | static void tegra_dsi_init_sw(struct tegra_dc *dc, | ||
| 414 | struct tegra_dc_dsi_data *dsi) | ||
| 415 | { | ||
| 416 | u32 h_width_pixels; | ||
| 417 | u32 v_width_lines; | ||
| 418 | u32 pixel_clk_hz; | ||
| 419 | u32 byte_clk_hz; | ||
| 420 | u32 plld_clk_mhz; | ||
| 421 | |||
| 422 | switch (dsi->info.pixel_format) { | ||
| 423 | case TEGRA_DSI_PIXEL_FORMAT_16BIT_P: | ||
| 424 | /* 2 bytes per pixel */ | ||
| 425 | dsi->pixel_scaler_mul = 2; | ||
| 426 | dsi->pixel_scaler_div = 1; | ||
| 427 | break; | ||
| 428 | case TEGRA_DSI_PIXEL_FORMAT_18BIT_P: | ||
| 429 | /* 2.25 bytes per pixel */ | ||
| 430 | dsi->pixel_scaler_mul = 9; | ||
| 431 | dsi->pixel_scaler_div = 4; | ||
| 432 | break; | ||
| 433 | case TEGRA_DSI_PIXEL_FORMAT_18BIT_NP: | ||
| 434 | case TEGRA_DSI_PIXEL_FORMAT_24BIT_P: | ||
| 435 | /* 3 bytes per pixel */ | ||
| 436 | dsi->pixel_scaler_mul = 3; | ||
| 437 | dsi->pixel_scaler_div = 1; | ||
| 438 | break; | ||
| 439 | default: | ||
| 440 | break; | ||
| 441 | } | ||
| 442 | |||
| 443 | dsi->controller_index = dc->ndev->id; | ||
| 444 | dsi->ulpm = false; | ||
| 445 | dsi->enabled = false; | ||
| 446 | dsi->clk_ref = false; | ||
| 447 | |||
| 448 | dsi->dsi_control_val = | ||
| 449 | DSI_CONTROL_VIRTUAL_CHANNEL(dsi->info.virtual_channel) | | ||
| 450 | DSI_CONTROL_NUM_DATA_LANES(dsi->info.n_data_lanes - 1) | | ||
| 451 | DSI_CONTROL_VID_SOURCE(dsi->controller_index) | | ||
| 452 | DSI_CONTROL_DATA_FORMAT(dsi->info.pixel_format); | ||
| 453 | |||
| 454 | /* Below we are going to calculate dsi and dc clock rate. | ||
| 455 | * Calcuate the horizontal and vertical width. | ||
| 456 | */ | ||
| 457 | h_width_pixels = dc->mode.h_back_porch + dc->mode.h_front_porch + | ||
| 458 | dc->mode.h_sync_width + dc->mode.h_active; | ||
| 459 | v_width_lines = dc->mode.v_back_porch + dc->mode.v_front_porch + | ||
| 460 | dc->mode.v_sync_width + dc->mode.v_active; | ||
| 461 | |||
| 462 | /* Calculate minimum required pixel rate. */ | ||
| 463 | pixel_clk_hz = h_width_pixels * v_width_lines * dsi->info.refresh_rate; | ||
| 464 | if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) { | ||
| 465 | if (dsi->info.rated_refresh_rate >= dsi->info.refresh_rate) | ||
| 466 | dev_info(&dc->ndev->dev, "DSI: measured refresh rate " | ||
| 467 | "should be larger than rated refresh rate.\n"); | ||
| 468 | dc->mode.rated_pclk = h_width_pixels * v_width_lines * | ||
| 469 | dsi->info.rated_refresh_rate; | ||
| 470 | } | ||
| 471 | |||
| 472 | /* Calculate minimum byte rate on DSI interface. */ | ||
| 473 | byte_clk_hz = (pixel_clk_hz * dsi->pixel_scaler_mul) / | ||
| 474 | (dsi->pixel_scaler_div * dsi->info.n_data_lanes); | ||
| 475 | |||
| 476 | /* Round up to multiple of mega hz. */ | ||
| 477 | plld_clk_mhz = DIV_ROUND_UP((byte_clk_hz * NUMOF_BIT_PER_BYTE), | ||
| 478 | 1000000); | ||
| 479 | |||
| 480 | /* Calculate default real shift_clk_div. */ | ||
| 481 | dsi->default_shift_clk_div = (NUMOF_BIT_PER_BYTE / 2) * | ||
| 482 | dsi->pixel_scaler_mul / (dsi->pixel_scaler_div * | ||
| 483 | dsi->info.n_data_lanes); | ||
| 484 | /* Calculate default DSI hs clock. DSI interface is double data rate. | ||
| 485 | * Data is transferred on both rising and falling edge of clk, div by 2 | ||
| 486 | * to get the actual clock rate. | ||
| 487 | */ | ||
| 488 | dsi->default_hs_clk_khz = plld_clk_mhz * 1000 / 2; | ||
| 489 | dsi->default_pixel_clk_khz = plld_clk_mhz * 1000 / 2 | ||
| 490 | / dsi->default_shift_clk_div; | ||
| 491 | |||
| 492 | /* Get the actual shift_clk_div and clock rates. */ | ||
| 493 | dsi->shift_clk_div = tegra_dsi_get_shift_clk_div(dsi); | ||
| 494 | dsi->target_lp_clk_khz = | ||
| 495 | tegra_dsi_get_lp_clk_rate(dsi, DSI_LP_OP_WRITE); | ||
| 496 | dsi->target_hs_clk_khz = tegra_dsi_get_hs_clk_rate(dsi); | ||
| 497 | |||
| 498 | dev_info(&dc->ndev->dev, "DSI: HS clock rate is %d\n", | ||
| 499 | dsi->target_hs_clk_khz); | ||
| 500 | |||
| 501 | dsi->controller_index = dc->ndev->id; | ||
| 502 | |||
| 503 | #if DSI_USE_SYNC_POINTS | ||
| 504 | dsi->syncpt_id = NVSYNCPT_DSI; | ||
| 505 | #endif | ||
| 506 | |||
| 507 | /* | ||
| 508 | * Force video clock to be continuous mode if | ||
| 509 | * enable_hs_clock_on_lp_cmd_mode is set | ||
| 510 | */ | ||
| 511 | if (dsi->info.enable_hs_clock_on_lp_cmd_mode) { | ||
| 512 | if (dsi->info.video_clock_mode != | ||
| 513 | TEGRA_DSI_VIDEO_CLOCK_CONTINUOUS) | ||
| 514 | dev_warn(&dc->ndev->dev, | ||
| 515 | "Force clock continuous mode\n"); | ||
| 516 | |||
| 517 | dsi->info.video_clock_mode = TEGRA_DSI_VIDEO_CLOCK_CONTINUOUS; | ||
| 518 | } | ||
| 519 | |||
| 520 | } | ||
| 521 | |||
| 522 | #define SELECT_T_PHY(platform_t_phy_ns, default_phy, clk_ns, hw_inc) ( \ | ||
| 523 | (platform_t_phy_ns) ? ( \ | ||
| 524 | ((DSI_CONVERT_T_PHY_NS_TO_T_PHY(platform_t_phy_ns, clk_ns, hw_inc)) < 0 ? 0 : \ | ||
| 525 | (DSI_CONVERT_T_PHY_NS_TO_T_PHY(platform_t_phy_ns, clk_ns, hw_inc)))) : \ | ||
| 526 | ((default_phy) < 0 ? 0 : (default_phy))) | ||
| 527 | |||
| 528 | static void tegra_dsi_get_clk_phy_timing(struct tegra_dc_dsi_data *dsi, | ||
| 529 | struct dsi_phy_timing_inclk *phy_timing_clk, u32 clk_ns) | ||
| 530 | { | ||
| 531 | phy_timing_clk->t_tlpx = SELECT_T_PHY( | ||
| 532 | dsi->info.phy_timing.t_tlpx_ns, | ||
| 533 | T_TLPX_DEFAULT(clk_ns), clk_ns, T_TLPX_HW_INC); | ||
| 534 | |||
| 535 | phy_timing_clk->t_clktrail = SELECT_T_PHY( | ||
| 536 | dsi->info.phy_timing.t_clktrail_ns, | ||
| 537 | T_CLKTRAIL_DEFAULT(clk_ns), clk_ns, T_CLKTRAIL_HW_INC); | ||
| 538 | |||
| 539 | phy_timing_clk->t_clkpost = SELECT_T_PHY( | ||
| 540 | dsi->info.phy_timing.t_clkpost_ns, | ||
| 541 | T_CLKPOST_DEFAULT(clk_ns), clk_ns, T_CLKPOST_HW_INC); | ||
| 542 | |||
| 543 | phy_timing_clk->t_clkzero = SELECT_T_PHY( | ||
| 544 | dsi->info.phy_timing.t_clkzero_ns, | ||
| 545 | T_CLKZERO_DEFAULT(clk_ns), clk_ns, T_CLKZERO_HW_INC); | ||
| 546 | |||
| 547 | phy_timing_clk->t_clkprepare = SELECT_T_PHY( | ||
| 548 | dsi->info.phy_timing.t_clkprepare_ns, | ||
| 549 | T_CLKPREPARE_DEFAULT(clk_ns), clk_ns, T_CLKPREPARE_HW_INC); | ||
| 550 | |||
| 551 | phy_timing_clk->t_clkpre = SELECT_T_PHY( | ||
| 552 | dsi->info.phy_timing.t_clkpre_ns, | ||
| 553 | T_CLKPRE_DEFAULT, clk_ns, T_CLKPRE_HW_INC); | ||
| 554 | } | ||
| 555 | |||
| 556 | static void tegra_dsi_get_hs_phy_timing(struct tegra_dc_dsi_data *dsi, | ||
| 557 | struct dsi_phy_timing_inclk *phy_timing_clk, u32 clk_ns) | ||
| 558 | { | ||
| 559 | phy_timing_clk->t_tlpx = SELECT_T_PHY( | ||
| 560 | dsi->info.phy_timing.t_tlpx_ns, | ||
| 561 | T_TLPX_DEFAULT(clk_ns), clk_ns, T_TLPX_HW_INC); | ||
| 562 | |||
| 563 | phy_timing_clk->t_hsdexit = SELECT_T_PHY( | ||
| 564 | dsi->info.phy_timing.t_hsdexit_ns, | ||
| 565 | T_HSEXIT_DEFAULT(clk_ns), clk_ns, T_HSEXIT_HW_INC); | ||
| 566 | |||
| 567 | phy_timing_clk->t_hstrail = SELECT_T_PHY( | ||
| 568 | dsi->info.phy_timing.t_hstrail_ns, | ||
| 569 | T_HSTRAIL_DEFAULT(clk_ns), clk_ns, T_HSTRAIL_HW_INC); | ||
| 570 | |||
| 571 | phy_timing_clk->t_datzero = SELECT_T_PHY( | ||
| 572 | dsi->info.phy_timing.t_datzero_ns, | ||
| 573 | T_DATZERO_DEFAULT(clk_ns), clk_ns, T_DATZERO_HW_INC); | ||
| 574 | |||
| 575 | phy_timing_clk->t_hsprepare = SELECT_T_PHY( | ||
| 576 | dsi->info.phy_timing.t_hsprepare_ns, | ||
| 577 | T_HSPREPARE_DEFAULT(clk_ns), clk_ns, T_HSPREPARE_HW_INC); | ||
| 578 | } | ||
| 579 | |||
| 580 | static void tegra_dsi_get_escape_phy_timing(struct tegra_dc_dsi_data *dsi, | ||
| 581 | struct dsi_phy_timing_inclk *phy_timing_clk, u32 clk_ns) | ||
| 582 | { | ||
| 583 | phy_timing_clk->t_tlpx = SELECT_T_PHY( | ||
| 584 | dsi->info.phy_timing.t_tlpx_ns, | ||
| 585 | T_TLPX_DEFAULT(clk_ns), clk_ns, T_TLPX_HW_INC); | ||
| 586 | } | ||
| 587 | |||
| 588 | static void tegra_dsi_get_bta_phy_timing(struct tegra_dc_dsi_data *dsi, | ||
| 589 | struct dsi_phy_timing_inclk *phy_timing_clk, u32 clk_ns) | ||
| 590 | { | ||
| 591 | phy_timing_clk->t_tlpx = SELECT_T_PHY( | ||
| 592 | dsi->info.phy_timing.t_tlpx_ns, | ||
| 593 | T_TLPX_DEFAULT(clk_ns), clk_ns, T_TLPX_HW_INC); | ||
| 594 | |||
| 595 | phy_timing_clk->t_taget = SELECT_T_PHY( | ||
| 596 | dsi->info.phy_timing.t_taget_ns, | ||
| 597 | T_TAGET_DEFAULT(clk_ns), clk_ns, T_TAGET_HW_INC); | ||
| 598 | |||
| 599 | phy_timing_clk->t_tasure = SELECT_T_PHY( | ||
| 600 | dsi->info.phy_timing.t_tasure_ns, | ||
| 601 | T_TASURE_DEFAULT(clk_ns), clk_ns, T_TASURE_HW_INC); | ||
| 602 | |||
| 603 | phy_timing_clk->t_tago = SELECT_T_PHY( | ||
| 604 | dsi->info.phy_timing.t_tago_ns, | ||
| 605 | T_TAGO_DEFAULT(clk_ns), clk_ns, T_TAGO_HW_INC); | ||
| 606 | } | ||
| 607 | |||
| 608 | static void tegra_dsi_get_ulps_phy_timing(struct tegra_dc_dsi_data *dsi, | ||
| 609 | struct dsi_phy_timing_inclk *phy_timing_clk, u32 clk_ns) | ||
| 610 | { | ||
| 611 | phy_timing_clk->t_tlpx = SELECT_T_PHY( | ||
| 612 | dsi->info.phy_timing.t_tlpx_ns, | ||
| 613 | T_TLPX_DEFAULT(clk_ns), clk_ns, T_TLPX_HW_INC); | ||
| 614 | |||
| 615 | phy_timing_clk->t_wakeup = SELECT_T_PHY( | ||
| 616 | dsi->info.phy_timing.t_wakeup_ns, | ||
| 617 | T_WAKEUP_DEFAULT, clk_ns, T_WAKEUP_HW_INC); | ||
| 618 | } | ||
| 619 | |||
| 620 | #undef SELECT_T_PHY | ||
| 621 | |||
| 622 | static void tegra_dsi_get_phy_timing(struct tegra_dc_dsi_data *dsi, | ||
| 623 | struct dsi_phy_timing_inclk *phy_timing_clk, | ||
| 624 | u32 clk_ns, u8 lphs) | ||
| 625 | { | ||
| 626 | if (lphs == DSI_LPHS_IN_HS_MODE) { | ||
| 627 | tegra_dsi_get_clk_phy_timing(dsi, phy_timing_clk, clk_ns); | ||
| 628 | tegra_dsi_get_hs_phy_timing(dsi, phy_timing_clk, clk_ns); | ||
| 629 | } else { | ||
| 630 | /* default is LP mode */ | ||
| 631 | tegra_dsi_get_escape_phy_timing(dsi, phy_timing_clk, clk_ns); | ||
| 632 | tegra_dsi_get_bta_phy_timing(dsi, phy_timing_clk, clk_ns); | ||
| 633 | tegra_dsi_get_ulps_phy_timing(dsi, phy_timing_clk, clk_ns); | ||
| 634 | if (dsi->info.enable_hs_clock_on_lp_cmd_mode) | ||
| 635 | tegra_dsi_get_clk_phy_timing | ||
| 636 | (dsi, phy_timing_clk, clk_ns); | ||
| 637 | } | ||
| 638 | } | ||
| 639 | |||
| 640 | static int tegra_dsi_mipi_phy_timing_range(struct tegra_dc_dsi_data *dsi, | ||
| 641 | struct dsi_phy_timing_inclk *phy_timing, | ||
| 642 | u32 clk_ns, u8 lphs) | ||
| 643 | { | ||
| 644 | #define CHECK_RANGE(val, min, max) ( \ | ||
| 645 | ((min) == NOT_DEFINED ? 0 : (val) < (min)) || \ | ||
| 646 | ((max) == NOT_DEFINED ? 0 : (val) > (max)) ? -EINVAL : 0) | ||
| 647 | |||
| 648 | int err = 0; | ||
| 649 | |||
| 650 | err = CHECK_RANGE( | ||
| 651 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 652 | phy_timing->t_tlpx, clk_ns, T_TLPX_HW_INC), | ||
| 653 | MIPI_T_TLPX_NS_MIN, MIPI_T_TLPX_NS_MAX); | ||
| 654 | if (err < 0) { | ||
| 655 | dev_warn(&dsi->dc->ndev->dev, | ||
| 656 | "dsi: Tlpx mipi range violated\n"); | ||
| 657 | goto fail; | ||
| 658 | } | ||
| 659 | |||
| 660 | if (lphs == DSI_LPHS_IN_HS_MODE) { | ||
| 661 | err = CHECK_RANGE( | ||
| 662 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 663 | phy_timing->t_hsdexit, clk_ns, T_HSEXIT_HW_INC), | ||
| 664 | MIPI_T_HSEXIT_NS_MIN, MIPI_T_HSEXIT_NS_MAX); | ||
| 665 | if (err < 0) { | ||
| 666 | dev_warn(&dsi->dc->ndev->dev, | ||
| 667 | "dsi: HsExit mipi range violated\n"); | ||
| 668 | goto fail; | ||
| 669 | } | ||
| 670 | |||
| 671 | err = CHECK_RANGE( | ||
| 672 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 673 | phy_timing->t_hstrail, clk_ns, T_HSTRAIL_HW_INC), | ||
| 674 | MIPI_T_HSTRAIL_NS_MIN(clk_ns), MIPI_T_HSTRAIL_NS_MAX); | ||
| 675 | if (err < 0) { | ||
| 676 | dev_warn(&dsi->dc->ndev->dev, | ||
| 677 | "dsi: HsTrail mipi range violated\n"); | ||
| 678 | goto fail; | ||
| 679 | } | ||
| 680 | |||
| 681 | err = CHECK_RANGE( | ||
| 682 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 683 | phy_timing->t_datzero, clk_ns, T_DATZERO_HW_INC), | ||
| 684 | MIPI_T_HSZERO_NS_MIN, MIPI_T_HSZERO_NS_MAX); | ||
| 685 | if (err < 0) { | ||
| 686 | dev_warn(&dsi->dc->ndev->dev, | ||
| 687 | "dsi: HsZero mipi range violated\n"); | ||
| 688 | goto fail; | ||
| 689 | } | ||
| 690 | |||
| 691 | err = CHECK_RANGE( | ||
| 692 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 693 | phy_timing->t_hsprepare, clk_ns, T_HSPREPARE_HW_INC), | ||
| 694 | MIPI_T_HSPREPARE_NS_MIN(clk_ns), | ||
| 695 | MIPI_T_HSPREPARE_NS_MAX(clk_ns)); | ||
| 696 | if (err < 0) { | ||
| 697 | dev_warn(&dsi->dc->ndev->dev, | ||
| 698 | "dsi: HsPrepare mipi range violated\n"); | ||
| 699 | goto fail; | ||
| 700 | } | ||
| 701 | |||
| 702 | err = CHECK_RANGE( | ||
| 703 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 704 | phy_timing->t_hsprepare, clk_ns, T_HSPREPARE_HW_INC) + | ||
| 705 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 706 | phy_timing->t_datzero, clk_ns, T_DATZERO_HW_INC), | ||
| 707 | MIPI_T_HSPREPARE_ADD_HSZERO_NS_MIN(clk_ns), | ||
| 708 | MIPI_T_HSPREPARE_ADD_HSZERO_NS_MAX); | ||
| 709 | if (err < 0) { | ||
| 710 | dev_warn(&dsi->dc->ndev->dev, | ||
| 711 | "dsi: HsPrepare + HsZero mipi range violated\n"); | ||
| 712 | goto fail; | ||
| 713 | } | ||
| 714 | } else { | ||
| 715 | /* default is LP mode */ | ||
| 716 | err = CHECK_RANGE( | ||
| 717 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 718 | phy_timing->t_wakeup, clk_ns, T_WAKEUP_HW_INC), | ||
| 719 | MIPI_T_WAKEUP_NS_MIN, MIPI_T_WAKEUP_NS_MAX); | ||
| 720 | if (err < 0) { | ||
| 721 | dev_warn(&dsi->dc->ndev->dev, | ||
| 722 | "dsi: WakeUp mipi range violated\n"); | ||
| 723 | goto fail; | ||
| 724 | } | ||
| 725 | |||
| 726 | err = CHECK_RANGE( | ||
| 727 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 728 | phy_timing->t_tasure, clk_ns, T_TASURE_HW_INC), | ||
| 729 | MIPI_T_TASURE_NS_MIN(DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 730 | phy_timing->t_tlpx, clk_ns, T_TLPX_HW_INC)), | ||
| 731 | MIPI_T_TASURE_NS_MAX(DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 732 | phy_timing->t_tlpx, clk_ns, T_TLPX_HW_INC))); | ||
| 733 | if (err < 0) { | ||
| 734 | dev_warn(&dsi->dc->ndev->dev, | ||
| 735 | "dsi: TaSure mipi range violated\n"); | ||
| 736 | goto fail; | ||
| 737 | } | ||
| 738 | } | ||
| 739 | |||
| 740 | if (lphs == DSI_LPHS_IN_HS_MODE || | ||
| 741 | dsi->info.enable_hs_clock_on_lp_cmd_mode) { | ||
| 742 | err = CHECK_RANGE( | ||
| 743 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 744 | phy_timing->t_clktrail, clk_ns, T_CLKTRAIL_HW_INC), | ||
| 745 | MIPI_T_CLKTRAIL_NS_MIN, MIPI_T_CLKTRAIL_NS_MAX); | ||
| 746 | if (err < 0) { | ||
| 747 | dev_warn(&dsi->dc->ndev->dev, | ||
| 748 | "dsi: ClkTrail mipi range violated\n"); | ||
| 749 | goto fail; | ||
| 750 | } | ||
| 751 | |||
| 752 | err = CHECK_RANGE( | ||
| 753 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 754 | phy_timing->t_clkpost, clk_ns, T_CLKPOST_HW_INC), | ||
| 755 | MIPI_T_CLKPOST_NS_MIN(clk_ns), MIPI_T_CLKPOST_NS_MAX); | ||
| 756 | if (err < 0) { | ||
| 757 | dev_warn(&dsi->dc->ndev->dev, | ||
| 758 | "dsi: ClkPost mipi range violated\n"); | ||
| 759 | goto fail; | ||
| 760 | } | ||
| 761 | |||
| 762 | err = CHECK_RANGE( | ||
| 763 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 764 | phy_timing->t_clkzero, clk_ns, T_CLKZERO_HW_INC), | ||
| 765 | MIPI_T_CLKZERO_NS_MIN, MIPI_T_CLKZERO_NS_MAX); | ||
| 766 | if (err < 0) { | ||
| 767 | dev_warn(&dsi->dc->ndev->dev, | ||
| 768 | "dsi: ClkZero mipi range violated\n"); | ||
| 769 | goto fail; | ||
| 770 | } | ||
| 771 | |||
| 772 | err = CHECK_RANGE( | ||
| 773 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 774 | phy_timing->t_clkprepare, clk_ns, T_CLKPREPARE_HW_INC), | ||
| 775 | MIPI_T_CLKPREPARE_NS_MIN, MIPI_T_CLKPREPARE_NS_MAX); | ||
| 776 | if (err < 0) { | ||
| 777 | dev_warn(&dsi->dc->ndev->dev, | ||
| 778 | "dsi: ClkPrepare mipi range violated\n"); | ||
| 779 | goto fail; | ||
| 780 | } | ||
| 781 | |||
| 782 | err = CHECK_RANGE( | ||
| 783 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 784 | phy_timing->t_clkpre, clk_ns, T_CLKPRE_HW_INC), | ||
| 785 | MIPI_T_CLKPRE_NS_MIN, MIPI_T_CLKPRE_NS_MAX); | ||
| 786 | if (err < 0) { | ||
| 787 | dev_warn(&dsi->dc->ndev->dev, | ||
| 788 | "dsi: ClkPre mipi range violated\n"); | ||
| 789 | goto fail; | ||
| 790 | } | ||
| 791 | |||
| 792 | err = CHECK_RANGE( | ||
| 793 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 794 | phy_timing->t_clkprepare, clk_ns, T_CLKPREPARE_HW_INC) + | ||
| 795 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 796 | phy_timing->t_clkzero, clk_ns, T_CLKZERO_HW_INC), | ||
| 797 | MIPI_T_CLKPREPARE_ADD_CLKZERO_NS_MIN, | ||
| 798 | MIPI_T_CLKPREPARE_ADD_CLKZERO_NS_MAX); | ||
| 799 | if (err < 0) { | ||
| 800 | dev_warn(&dsi->dc->ndev->dev, | ||
| 801 | "dsi: ClkPrepare + ClkZero mipi range violated\n"); | ||
| 802 | goto fail; | ||
| 803 | } | ||
| 804 | } | ||
| 805 | fail: | ||
| 806 | #undef CHECK_RANGE | ||
| 807 | return err; | ||
| 808 | } | ||
| 809 | |||
| 810 | static int tegra_dsi_hs_phy_len(struct tegra_dc_dsi_data *dsi, | ||
| 811 | struct dsi_phy_timing_inclk *phy_timing, | ||
| 812 | u32 clk_ns, u8 lphs) | ||
| 813 | { | ||
| 814 | u32 hs_t_phy_ns; | ||
| 815 | u32 clk_t_phy_ns; | ||
| 816 | u32 t_phy_ns; | ||
| 817 | u32 h_blank_ns; | ||
| 818 | struct tegra_dc_mode *modes; | ||
| 819 | u32 t_pix_ns; | ||
| 820 | int err = 0; | ||
| 821 | |||
| 822 | if (!(lphs == DSI_LPHS_IN_HS_MODE)) | ||
| 823 | goto fail; | ||
| 824 | |||
| 825 | modes = dsi->dc->out->modes; | ||
| 826 | t_pix_ns = clk_ns * BITS_PER_BYTE * | ||
| 827 | dsi->pixel_scaler_mul / dsi->pixel_scaler_div; | ||
| 828 | |||
| 829 | hs_t_phy_ns = | ||
| 830 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 831 | phy_timing->t_tlpx, clk_ns, T_TLPX_HW_INC) + | ||
| 832 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 833 | phy_timing->t_hsprepare, clk_ns, T_HSPREPARE_HW_INC) + | ||
| 834 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 835 | phy_timing->t_datzero, clk_ns, T_DATZERO_HW_INC) + | ||
| 836 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 837 | phy_timing->t_hstrail, clk_ns, T_HSTRAIL_HW_INC) + | ||
| 838 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 839 | phy_timing->t_hsdexit, clk_ns, T_HSEXIT_HW_INC); | ||
| 840 | |||
| 841 | clk_t_phy_ns = | ||
| 842 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 843 | phy_timing->t_clkpost, clk_ns, T_CLKPOST_HW_INC) + | ||
| 844 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 845 | phy_timing->t_clktrail, clk_ns, T_CLKTRAIL_HW_INC) + | ||
| 846 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 847 | phy_timing->t_hsdexit, clk_ns, T_HSEXIT_HW_INC) + | ||
| 848 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 849 | phy_timing->t_tlpx, clk_ns, T_TLPX_HW_INC) + | ||
| 850 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 851 | phy_timing->t_clkprepare, clk_ns, T_CLKPREPARE_HW_INC) + | ||
| 852 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 853 | phy_timing->t_clkzero, clk_ns, T_CLKZERO_HW_INC) + | ||
| 854 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 855 | phy_timing->t_clkpre, clk_ns, T_CLKPRE_HW_INC); | ||
| 856 | |||
| 857 | h_blank_ns = t_pix_ns * (modes->h_sync_width + modes->h_back_porch + | ||
| 858 | modes->h_front_porch); | ||
| 859 | |||
| 860 | /* Extra tlpx and byte cycle required by dsi HW */ | ||
| 861 | t_phy_ns = dsi->info.n_data_lanes * (hs_t_phy_ns + clk_t_phy_ns + | ||
| 862 | DSI_CONVERT_T_PHY_TO_T_PHY_NS( | ||
| 863 | phy_timing->t_tlpx, clk_ns, T_TLPX_HW_INC) + | ||
| 864 | clk_ns * BITS_PER_BYTE); | ||
| 865 | |||
| 866 | if (h_blank_ns < t_phy_ns) { | ||
| 867 | err = -EINVAL; | ||
| 868 | dev_err(&dsi->dc->ndev->dev, | ||
| 869 | "dsi: Hblank is smaller than HS trans phy timing\n"); | ||
| 870 | goto fail; | ||
| 871 | } | ||
| 872 | |||
| 873 | return 0; | ||
| 874 | fail: | ||
| 875 | return err; | ||
| 876 | } | ||
| 877 | |||
| 878 | static int tegra_dsi_constraint_phy_timing(struct tegra_dc_dsi_data *dsi, | ||
| 879 | struct dsi_phy_timing_inclk *phy_timing, | ||
| 880 | u32 clk_ns, u8 lphs) | ||
| 881 | { | ||
| 882 | int err = 0; | ||
| 883 | |||
| 884 | err = tegra_dsi_mipi_phy_timing_range(dsi, phy_timing, clk_ns, lphs); | ||
| 885 | if (err < 0) { | ||
| 886 | dev_warn(&dsi->dc->ndev->dev, "dsi: mipi range violated\n"); | ||
| 887 | goto fail; | ||
| 888 | } | ||
| 889 | |||
| 890 | err = tegra_dsi_hs_phy_len(dsi, phy_timing, clk_ns, lphs); | ||
| 891 | if (err < 0) { | ||
| 892 | dev_err(&dsi->dc->ndev->dev, "dsi: Hblank too short\n"); | ||
| 893 | goto fail; | ||
| 894 | } | ||
| 895 | |||
| 896 | /* TODO: add more contraints */ | ||
| 897 | fail: | ||
| 898 | return err; | ||
| 899 | } | ||
| 900 | |||
| 901 | static void tegra_dsi_set_phy_timing(struct tegra_dc_dsi_data *dsi, u8 lphs) | ||
| 902 | { | ||
| 903 | u32 val; | ||
| 904 | struct dsi_phy_timing_inclk phy_timing = dsi->phy_timing; | ||
| 905 | |||
| 906 | tegra_dsi_get_phy_timing | ||
| 907 | (dsi, &phy_timing, dsi->current_bit_clk_ns, lphs); | ||
| 908 | |||
| 909 | tegra_dsi_constraint_phy_timing(dsi, &phy_timing, | ||
| 910 | dsi->current_bit_clk_ns, lphs); | ||
| 911 | |||
| 912 | val = DSI_PHY_TIMING_0_THSDEXIT(phy_timing.t_hsdexit) | | ||
| 913 | DSI_PHY_TIMING_0_THSTRAIL(phy_timing.t_hstrail) | | ||
| 914 | DSI_PHY_TIMING_0_TDATZERO(phy_timing.t_datzero) | | ||
| 915 | DSI_PHY_TIMING_0_THSPREPR(phy_timing.t_hsprepare); | ||
| 916 | tegra_dsi_writel(dsi, val, DSI_PHY_TIMING_0); | ||
| 917 | |||
| 918 | val = DSI_PHY_TIMING_1_TCLKTRAIL(phy_timing.t_clktrail) | | ||
| 919 | DSI_PHY_TIMING_1_TCLKPOST(phy_timing.t_clkpost) | | ||
| 920 | DSI_PHY_TIMING_1_TCLKZERO(phy_timing.t_clkzero) | | ||
| 921 | DSI_PHY_TIMING_1_TTLPX(phy_timing.t_tlpx); | ||
| 922 | tegra_dsi_writel(dsi, val, DSI_PHY_TIMING_1); | ||
| 923 | |||
| 924 | val = DSI_PHY_TIMING_2_TCLKPREPARE(phy_timing.t_clkprepare) | | ||
| 925 | DSI_PHY_TIMING_2_TCLKPRE(phy_timing.t_clkpre) | | ||
| 926 | DSI_PHY_TIMING_2_TWAKEUP(phy_timing.t_wakeup); | ||
| 927 | tegra_dsi_writel(dsi, val, DSI_PHY_TIMING_2); | ||
| 928 | |||
| 929 | val = DSI_BTA_TIMING_TTAGET(phy_timing.t_taget) | | ||
| 930 | DSI_BTA_TIMING_TTASURE(phy_timing.t_tasure) | | ||
| 931 | DSI_BTA_TIMING_TTAGO(phy_timing.t_tago); | ||
| 932 | tegra_dsi_writel(dsi, val, DSI_BTA_TIMING); | ||
| 933 | |||
| 934 | dsi->phy_timing = phy_timing; | ||
| 935 | } | ||
| 936 | |||
| 937 | static u32 tegra_dsi_sol_delay_burst(struct tegra_dc *dc, | ||
| 938 | struct tegra_dc_dsi_data *dsi) | ||
| 939 | { | ||
| 940 | u32 dsi_to_pixel_clk_ratio; | ||
| 941 | u32 temp; | ||
| 942 | u32 temp1; | ||
| 943 | u32 mipi_clk_adj_kHz; | ||
| 944 | u32 sol_delay; | ||
| 945 | struct tegra_dc_mode *dc_modes = &dc->mode; | ||
| 946 | |||
| 947 | /* Get Fdsi/Fpixel ration (note: Fdsi is in bit format) */ | ||
| 948 | dsi_to_pixel_clk_ratio = (dsi->current_dsi_clk_khz * 2 + | ||
| 949 | dsi->default_pixel_clk_khz - 1) / dsi->default_pixel_clk_khz; | ||
| 950 | |||
| 951 | /* Convert Fdsi to byte format */ | ||
| 952 | dsi_to_pixel_clk_ratio *= 1000/8; | ||
| 953 | |||
| 954 | /* Multiplying by 1000 so that we don't loose the fraction part */ | ||
| 955 | temp = dc_modes->h_active * 1000; | ||
| 956 | temp1 = dc_modes->h_active + dc_modes->h_back_porch + | ||
| 957 | dc_modes->h_sync_width; | ||
| 958 | |||
| 959 | sol_delay = temp1 * dsi_to_pixel_clk_ratio - | ||
| 960 | temp * dsi->pixel_scaler_mul / | ||
| 961 | (dsi->pixel_scaler_div * dsi->info.n_data_lanes); | ||
| 962 | |||
| 963 | /* Do rounding on sol delay */ | ||
| 964 | sol_delay = (sol_delay + 1000 - 1)/1000; | ||
| 965 | |||
| 966 | /* TODO: | ||
| 967 | * 1. find out the correct sol fifo depth to use | ||
| 968 | * 2. verify with hw about the clamping function | ||
| 969 | */ | ||
| 970 | if (sol_delay > (480 * 4)) { | ||
| 971 | sol_delay = (480 * 4); | ||
| 972 | mipi_clk_adj_kHz = sol_delay + | ||
| 973 | (dc_modes->h_active * dsi->pixel_scaler_mul) / | ||
| 974 | (dsi->info.n_data_lanes * dsi->pixel_scaler_div); | ||
| 975 | |||
| 976 | mipi_clk_adj_kHz *= (dsi->default_pixel_clk_khz / temp1); | ||
| 977 | |||
| 978 | mipi_clk_adj_kHz *= 4; | ||
| 979 | } | ||
| 980 | |||
| 981 | dsi->target_hs_clk_khz = mipi_clk_adj_kHz; | ||
| 982 | |||
| 983 | return sol_delay; | ||
| 984 | } | ||
| 985 | |||
| 986 | static void tegra_dsi_set_sol_delay(struct tegra_dc *dc, | ||
| 987 | struct tegra_dc_dsi_data *dsi) | ||
| 988 | { | ||
| 989 | u32 sol_delay; | ||
| 990 | |||
| 991 | if (dsi->info.video_burst_mode == TEGRA_DSI_VIDEO_NONE_BURST_MODE || | ||
| 992 | dsi->info.video_burst_mode == | ||
| 993 | TEGRA_DSI_VIDEO_NONE_BURST_MODE_WITH_SYNC_END) { | ||
| 994 | #define VIDEO_FIFO_LATENCY_PIXEL_CLK 8 | ||
| 995 | sol_delay = VIDEO_FIFO_LATENCY_PIXEL_CLK * | ||
| 996 | dsi->pixel_scaler_mul / dsi->pixel_scaler_div; | ||
| 997 | #undef VIDEO_FIFO_LATENCY_PIXEL_CLK | ||
| 998 | dsi->status.clk_burst = DSI_CLK_BURST_NONE_BURST; | ||
| 999 | } else { | ||
| 1000 | sol_delay = tegra_dsi_sol_delay_burst(dc, dsi); | ||
| 1001 | dsi->status.clk_burst = DSI_CLK_BURST_BURST_MODE; | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | tegra_dsi_writel(dsi, DSI_SOL_DELAY_SOL_DELAY(sol_delay), | ||
| 1005 | DSI_SOL_DELAY); | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | static void tegra_dsi_set_timeout(struct tegra_dc_dsi_data *dsi) | ||
| 1009 | { | ||
| 1010 | u32 val; | ||
| 1011 | u32 bytes_per_frame; | ||
| 1012 | u32 timeout = 0; | ||
| 1013 | |||
| 1014 | /* TODO: verify the following equation */ | ||
| 1015 | bytes_per_frame = dsi->current_dsi_clk_khz * 1000 * 2 / | ||
| 1016 | (dsi->info.refresh_rate * 8); | ||
| 1017 | timeout = bytes_per_frame / DSI_CYCLE_COUNTER_VALUE; | ||
| 1018 | timeout = (timeout + DSI_HTX_TO_MARGIN) & 0xffff; | ||
| 1019 | |||
| 1020 | val = DSI_TIMEOUT_0_LRXH_TO(DSI_LRXH_TO_VALUE) | | ||
| 1021 | DSI_TIMEOUT_0_HTX_TO(timeout); | ||
| 1022 | tegra_dsi_writel(dsi, val, DSI_TIMEOUT_0); | ||
| 1023 | |||
| 1024 | if (dsi->info.panel_reset_timeout_msec) | ||
| 1025 | timeout = (dsi->info.panel_reset_timeout_msec * 1000*1000) | ||
| 1026 | / dsi->current_bit_clk_ns; | ||
| 1027 | else | ||
| 1028 | timeout = DSI_PR_TO_VALUE; | ||
| 1029 | |||
| 1030 | val = DSI_TIMEOUT_1_PR_TO(timeout) | | ||
| 1031 | DSI_TIMEOUT_1_TA_TO(DSI_TA_TO_VALUE); | ||
| 1032 | tegra_dsi_writel(dsi, val, DSI_TIMEOUT_1); | ||
| 1033 | |||
| 1034 | val = DSI_TO_TALLY_P_RESET_STATUS(IN_RESET) | | ||
| 1035 | DSI_TO_TALLY_TA_TALLY(DSI_TA_TALLY_VALUE)| | ||
| 1036 | DSI_TO_TALLY_LRXH_TALLY(DSI_LRXH_TALLY_VALUE)| | ||
| 1037 | DSI_TO_TALLY_HTX_TALLY(DSI_HTX_TALLY_VALUE); | ||
| 1038 | tegra_dsi_writel(dsi, val, DSI_TO_TALLY); | ||
| 1039 | } | ||
| 1040 | |||
| 1041 | static void tegra_dsi_setup_video_mode_pkt_length(struct tegra_dc *dc, | ||
| 1042 | struct tegra_dc_dsi_data *dsi) | ||
| 1043 | { | ||
| 1044 | u32 val; | ||
| 1045 | u32 hact_pkt_len; | ||
| 1046 | u32 hsa_pkt_len; | ||
| 1047 | u32 hbp_pkt_len; | ||
| 1048 | u32 hfp_pkt_len; | ||
| 1049 | |||
| 1050 | hact_pkt_len = dc->mode.h_active * dsi->pixel_scaler_mul / | ||
| 1051 | dsi->pixel_scaler_div; | ||
| 1052 | hsa_pkt_len = dc->mode.h_sync_width * dsi->pixel_scaler_mul / | ||
| 1053 | dsi->pixel_scaler_div; | ||
| 1054 | hbp_pkt_len = dc->mode.h_back_porch * dsi->pixel_scaler_mul / | ||
| 1055 | dsi->pixel_scaler_div; | ||
| 1056 | hfp_pkt_len = dc->mode.h_front_porch * dsi->pixel_scaler_mul / | ||
| 1057 | dsi->pixel_scaler_div; | ||
| 1058 | |||
| 1059 | if (dsi->info.video_burst_mode != | ||
| 1060 | TEGRA_DSI_VIDEO_NONE_BURST_MODE_WITH_SYNC_END) | ||
| 1061 | hbp_pkt_len += hsa_pkt_len; | ||
| 1062 | |||
| 1063 | hsa_pkt_len -= DSI_HSYNC_BLNK_PKT_OVERHEAD; | ||
| 1064 | hbp_pkt_len -= DSI_HBACK_PORCH_PKT_OVERHEAD; | ||
| 1065 | hfp_pkt_len -= DSI_HFRONT_PORCH_PKT_OVERHEAD; | ||
| 1066 | |||
| 1067 | val = DSI_PKT_LEN_0_1_LENGTH_0(0) | | ||
| 1068 | DSI_PKT_LEN_0_1_LENGTH_1(hsa_pkt_len); | ||
| 1069 | tegra_dsi_writel(dsi, val, DSI_PKT_LEN_0_1); | ||
| 1070 | |||
| 1071 | val = DSI_PKT_LEN_2_3_LENGTH_2(hbp_pkt_len) | | ||
| 1072 | DSI_PKT_LEN_2_3_LENGTH_3(hact_pkt_len); | ||
| 1073 | tegra_dsi_writel(dsi, val, DSI_PKT_LEN_2_3); | ||
| 1074 | |||
| 1075 | val = DSI_PKT_LEN_4_5_LENGTH_4(hfp_pkt_len) | | ||
| 1076 | DSI_PKT_LEN_4_5_LENGTH_5(0); | ||
| 1077 | tegra_dsi_writel(dsi, val, DSI_PKT_LEN_4_5); | ||
| 1078 | |||
| 1079 | val = DSI_PKT_LEN_6_7_LENGTH_6(0) | DSI_PKT_LEN_6_7_LENGTH_7(0); | ||
| 1080 | tegra_dsi_writel(dsi, val, DSI_PKT_LEN_6_7); | ||
| 1081 | } | ||
| 1082 | |||
| 1083 | static void tegra_dsi_setup_cmd_mode_pkt_length(struct tegra_dc *dc, | ||
| 1084 | struct tegra_dc_dsi_data *dsi) | ||
| 1085 | { | ||
| 1086 | unsigned long val; | ||
| 1087 | unsigned long act_bytes; | ||
| 1088 | |||
| 1089 | act_bytes = dc->mode.h_active * dsi->pixel_scaler_mul / | ||
| 1090 | dsi->pixel_scaler_div + 1; | ||
| 1091 | |||
| 1092 | val = DSI_PKT_LEN_0_1_LENGTH_0(0) | DSI_PKT_LEN_0_1_LENGTH_1(0); | ||
| 1093 | tegra_dsi_writel(dsi, val, DSI_PKT_LEN_0_1); | ||
| 1094 | |||
| 1095 | val = DSI_PKT_LEN_2_3_LENGTH_2(0) | DSI_PKT_LEN_2_3_LENGTH_3(act_bytes); | ||
| 1096 | tegra_dsi_writel(dsi, val, DSI_PKT_LEN_2_3); | ||
| 1097 | |||
| 1098 | val = DSI_PKT_LEN_4_5_LENGTH_4(0) | DSI_PKT_LEN_4_5_LENGTH_5(act_bytes); | ||
| 1099 | tegra_dsi_writel(dsi, val, DSI_PKT_LEN_4_5); | ||
| 1100 | |||
| 1101 | val = DSI_PKT_LEN_6_7_LENGTH_6(0) | DSI_PKT_LEN_6_7_LENGTH_7(0x0f0f); | ||
| 1102 | tegra_dsi_writel(dsi, val, DSI_PKT_LEN_6_7); | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | static void tegra_dsi_set_pkt_length(struct tegra_dc *dc, | ||
| 1106 | struct tegra_dc_dsi_data *dsi) | ||
| 1107 | { | ||
| 1108 | if (dsi->driven_mode == TEGRA_DSI_DRIVEN_BY_HOST) | ||
| 1109 | return; | ||
| 1110 | |||
| 1111 | if (dsi->info.video_data_type == TEGRA_DSI_VIDEO_TYPE_VIDEO_MODE) | ||
| 1112 | tegra_dsi_setup_video_mode_pkt_length(dc, dsi); | ||
| 1113 | else | ||
| 1114 | tegra_dsi_setup_cmd_mode_pkt_length(dc, dsi); | ||
| 1115 | } | ||
| 1116 | |||
| 1117 | static void tegra_dsi_set_pkt_seq(struct tegra_dc *dc, | ||
| 1118 | struct tegra_dc_dsi_data *dsi) | ||
| 1119 | { | ||
| 1120 | const u32 *pkt_seq; | ||
| 1121 | u32 rgb_info; | ||
| 1122 | u32 pkt_seq_3_5_rgb_lo; | ||
| 1123 | u32 pkt_seq_3_5_rgb_hi; | ||
| 1124 | u32 val; | ||
| 1125 | u32 reg; | ||
| 1126 | u8 i; | ||
| 1127 | |||
| 1128 | if (dsi->driven_mode == TEGRA_DSI_DRIVEN_BY_HOST) | ||
| 1129 | return; | ||
| 1130 | |||
| 1131 | switch (dsi->info.pixel_format) { | ||
| 1132 | case TEGRA_DSI_PIXEL_FORMAT_16BIT_P: | ||
| 1133 | rgb_info = CMD_RGB_16BPP; | ||
| 1134 | break; | ||
| 1135 | case TEGRA_DSI_PIXEL_FORMAT_18BIT_P: | ||
| 1136 | rgb_info = CMD_RGB_18BPP; | ||
| 1137 | break; | ||
| 1138 | case TEGRA_DSI_PIXEL_FORMAT_18BIT_NP: | ||
| 1139 | rgb_info = CMD_RGB_18BPPNP; | ||
| 1140 | break; | ||
| 1141 | case TEGRA_DSI_PIXEL_FORMAT_24BIT_P: | ||
| 1142 | default: | ||
| 1143 | rgb_info = CMD_RGB_24BPP; | ||
| 1144 | break; | ||
| 1145 | } | ||
| 1146 | |||
| 1147 | pkt_seq_3_5_rgb_lo = 0; | ||
| 1148 | pkt_seq_3_5_rgb_hi = 0; | ||
| 1149 | if (dsi->info.video_data_type == TEGRA_DSI_VIDEO_TYPE_COMMAND_MODE) | ||
| 1150 | pkt_seq = dsi_pkt_seq_cmd_mode; | ||
| 1151 | else { | ||
| 1152 | switch (dsi->info.video_burst_mode) { | ||
| 1153 | case TEGRA_DSI_VIDEO_BURST_MODE_LOWEST_SPEED: | ||
| 1154 | case TEGRA_DSI_VIDEO_BURST_MODE_LOW_SPEED: | ||
| 1155 | case TEGRA_DSI_VIDEO_BURST_MODE_MEDIUM_SPEED: | ||
| 1156 | case TEGRA_DSI_VIDEO_BURST_MODE_FAST_SPEED: | ||
| 1157 | case TEGRA_DSI_VIDEO_BURST_MODE_FASTEST_SPEED: | ||
| 1158 | pkt_seq_3_5_rgb_lo = | ||
| 1159 | DSI_PKT_SEQ_3_LO_PKT_32_ID(rgb_info); | ||
| 1160 | if (!dsi->info.no_pkt_seq_eot) | ||
| 1161 | pkt_seq = dsi_pkt_seq_video_burst; | ||
| 1162 | else | ||
| 1163 | pkt_seq = dsi_pkt_seq_video_burst_no_eot; | ||
| 1164 | break; | ||
| 1165 | case TEGRA_DSI_VIDEO_NONE_BURST_MODE_WITH_SYNC_END: | ||
| 1166 | pkt_seq_3_5_rgb_hi = | ||
| 1167 | DSI_PKT_SEQ_3_HI_PKT_34_ID(rgb_info); | ||
| 1168 | pkt_seq = dsi_pkt_seq_video_non_burst_syne; | ||
| 1169 | break; | ||
| 1170 | case TEGRA_DSI_VIDEO_NONE_BURST_MODE: | ||
| 1171 | default: | ||
| 1172 | pkt_seq_3_5_rgb_lo = | ||
| 1173 | DSI_PKT_SEQ_3_LO_PKT_32_ID(rgb_info); | ||
| 1174 | pkt_seq = dsi_pkt_seq_video_non_burst; | ||
| 1175 | break; | ||
| 1176 | } | ||
| 1177 | } | ||
| 1178 | |||
| 1179 | for (i = 0; i < NUMOF_PKT_SEQ; i++) { | ||
| 1180 | val = pkt_seq[i]; | ||
| 1181 | reg = dsi_pkt_seq_reg[i]; | ||
| 1182 | if ((reg == DSI_PKT_SEQ_3_LO) || (reg == DSI_PKT_SEQ_5_LO)) | ||
| 1183 | val |= pkt_seq_3_5_rgb_lo; | ||
| 1184 | if ((reg == DSI_PKT_SEQ_3_HI) || (reg == DSI_PKT_SEQ_5_HI)) | ||
| 1185 | val |= pkt_seq_3_5_rgb_hi; | ||
| 1186 | tegra_dsi_writel(dsi, val, reg); | ||
| 1187 | } | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | static void tegra_dsi_reset_underflow_overflow | ||
| 1191 | (struct tegra_dc_dsi_data *dsi) | ||
| 1192 | { | ||
| 1193 | u32 val; | ||
| 1194 | |||
| 1195 | val = tegra_dsi_readl(dsi, DSI_STATUS); | ||
| 1196 | val &= (DSI_STATUS_LB_OVERFLOW(0x1) | DSI_STATUS_LB_UNDERFLOW(0x1)); | ||
| 1197 | if (val) { | ||
| 1198 | if (val & DSI_STATUS_LB_OVERFLOW(0x1)) | ||
| 1199 | dev_warn(&dsi->dc->ndev->dev, | ||
| 1200 | "dsi: video fifo overflow. Resetting flag\n"); | ||
| 1201 | if (val & DSI_STATUS_LB_UNDERFLOW(0x1)) | ||
| 1202 | dev_warn(&dsi->dc->ndev->dev, | ||
| 1203 | "dsi: video fifo underflow. Resetting flag\n"); | ||
| 1204 | val = tegra_dsi_readl(dsi, DSI_HOST_DSI_CONTROL); | ||
| 1205 | val |= DSI_HOST_CONTROL_FIFO_STAT_RESET(0x1); | ||
| 1206 | tegra_dsi_writel(dsi, val, DSI_HOST_DSI_CONTROL); | ||
| 1207 | udelay(5); | ||
| 1208 | } | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | static void tegra_dsi_stop_dc_stream(struct tegra_dc *dc, | ||
| 1212 | struct tegra_dc_dsi_data *dsi) | ||
| 1213 | { | ||
| 1214 | tegra_dc_writel(dc, DISP_CTRL_MODE_STOP, DC_CMD_DISPLAY_COMMAND); | ||
| 1215 | tegra_dc_writel(dc, 0, DC_DISP_DISP_WIN_OPTIONS); | ||
| 1216 | tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL); | ||
| 1217 | tegra_dc_writel(dc, GENERAL_ACT_REQ , DC_CMD_STATE_CONTROL); | ||
| 1218 | |||
| 1219 | dsi->status.dc_stream = DSI_DC_STREAM_DISABLE; | ||
| 1220 | } | ||
| 1221 | |||
| 1222 | static void tegra_dsi_stop_dc_stream_at_frame_end(struct tegra_dc *dc, | ||
| 1223 | struct tegra_dc_dsi_data *dsi) | ||
| 1224 | { | ||
| 1225 | int val; | ||
| 1226 | long timeout; | ||
| 1227 | u32 frame_period = DIV_ROUND_UP(S_TO_MS(1), dsi->info.refresh_rate); | ||
| 1228 | |||
| 1229 | /* stop dc */ | ||
| 1230 | tegra_dsi_stop_dc_stream(dc, dsi); | ||
| 1231 | |||
| 1232 | /* enable frame end interrupt */ | ||
| 1233 | val = tegra_dc_readl(dc, DC_CMD_INT_MASK); | ||
| 1234 | val |= FRAME_END_INT; | ||
| 1235 | tegra_dc_writel(dc, val, DC_CMD_INT_MASK); | ||
| 1236 | |||
| 1237 | /* wait for frame_end completion. | ||
| 1238 | * timeout is 2 frame duration to accomodate for | ||
| 1239 | * internal delay. | ||
| 1240 | */ | ||
| 1241 | timeout = wait_for_completion_interruptible_timeout( | ||
| 1242 | &dc->frame_end_complete, | ||
| 1243 | msecs_to_jiffies(2 * frame_period)); | ||
| 1244 | |||
| 1245 | /* disable frame end interrupt */ | ||
| 1246 | val = tegra_dc_readl(dc, DC_CMD_INT_MASK); | ||
| 1247 | val &= ~FRAME_END_INT; | ||
| 1248 | tegra_dc_writel(dc, val, DC_CMD_INT_MASK); | ||
| 1249 | |||
| 1250 | if (timeout == 0) | ||
| 1251 | dev_warn(&dc->ndev->dev, | ||
| 1252 | "DC doesn't stop at end of frame.\n"); | ||
| 1253 | |||
| 1254 | tegra_dsi_reset_underflow_overflow(dsi); | ||
| 1255 | } | ||
| 1256 | |||
| 1257 | static void tegra_dsi_start_dc_stream(struct tegra_dc *dc, | ||
| 1258 | struct tegra_dc_dsi_data *dsi) | ||
| 1259 | { | ||
| 1260 | u32 val; | ||
| 1261 | |||
| 1262 | tegra_dc_writel(dc, DSI_ENABLE, DC_DISP_DISP_WIN_OPTIONS); | ||
| 1263 | |||
| 1264 | /* TODO: clean up */ | ||
| 1265 | tegra_dc_writel(dc, PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | | ||
| 1266 | PW4_ENABLE | PM0_ENABLE | PM1_ENABLE, | ||
| 1267 | DC_CMD_DISPLAY_POWER_CONTROL); | ||
| 1268 | |||
| 1269 | /* Configure one-shot mode or continuous mode */ | ||
| 1270 | if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) { | ||
| 1271 | /* disable LSPI/LCD_DE output */ | ||
| 1272 | val = PIN_OUTPUT_LSPI_OUTPUT_DIS; | ||
| 1273 | tegra_dc_writel(dc, val, DC_COM_PIN_OUTPUT_ENABLE3); | ||
| 1274 | |||
| 1275 | /* enable MSF & set MSF polarity */ | ||
| 1276 | val = MSF_ENABLE | MSF_LSPI; | ||
| 1277 | if (!dsi->info.te_polarity_low) | ||
| 1278 | val |= MSF_POLARITY_HIGH; | ||
| 1279 | else | ||
| 1280 | val |= MSF_POLARITY_LOW; | ||
| 1281 | tegra_dc_writel(dc, val, DC_CMD_DISPLAY_COMMAND_OPTION0); | ||
| 1282 | |||
| 1283 | /* set non-continuous mode */ | ||
| 1284 | tegra_dc_writel(dc, DISP_CTRL_MODE_NC_DISPLAY, | ||
| 1285 | DC_CMD_DISPLAY_COMMAND); | ||
| 1286 | tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL); | ||
| 1287 | tegra_dc_writel(dc, GENERAL_ACT_REQ | NC_HOST_TRIG, | ||
| 1288 | DC_CMD_STATE_CONTROL); | ||
| 1289 | } else { | ||
| 1290 | /* set continuous mode */ | ||
| 1291 | tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, | ||
| 1292 | DC_CMD_DISPLAY_COMMAND); | ||
| 1293 | tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL); | ||
| 1294 | tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); | ||
| 1295 | } | ||
| 1296 | |||
| 1297 | dsi->status.dc_stream = DSI_DC_STREAM_ENABLE; | ||
| 1298 | } | ||
| 1299 | |||
| 1300 | static void tegra_dsi_set_dc_clk(struct tegra_dc *dc, | ||
| 1301 | struct tegra_dc_dsi_data *dsi) | ||
| 1302 | { | ||
| 1303 | u32 shift_clk_div_register; | ||
| 1304 | u32 val; | ||
| 1305 | |||
| 1306 | /* Get the corresponding register value of shift_clk_div. */ | ||
| 1307 | shift_clk_div_register = dsi->shift_clk_div * 2 - 2; | ||
| 1308 | |||
| 1309 | #ifndef CONFIG_TEGRA_SILICON_PLATFORM | ||
| 1310 | shift_clk_div_register = 1; | ||
| 1311 | #endif | ||
| 1312 | |||
| 1313 | /* TODO: find out if PCD3 option is required */ | ||
| 1314 | val = PIXEL_CLK_DIVIDER_PCD1 | | ||
| 1315 | SHIFT_CLK_DIVIDER(shift_clk_div_register); | ||
| 1316 | tegra_dc_writel(dc, val, DC_DISP_DISP_CLOCK_CONTROL); | ||
| 1317 | } | ||
| 1318 | |||
| 1319 | static void tegra_dsi_set_dsi_clk(struct tegra_dc *dc, | ||
| 1320 | struct tegra_dc_dsi_data *dsi, u32 clk) | ||
| 1321 | { | ||
| 1322 | u32 rm; | ||
| 1323 | |||
| 1324 | /* Round up to MHz */ | ||
| 1325 | rm = clk % 1000; | ||
| 1326 | if (rm != 0) | ||
| 1327 | clk -= rm; | ||
| 1328 | |||
| 1329 | /* Set up pixel clock */ | ||
| 1330 | dc->shift_clk_div = dsi->shift_clk_div; | ||
| 1331 | dc->mode.pclk = (clk * 1000) / dsi->shift_clk_div; | ||
| 1332 | /* TODO: Define one shot work delay in board file. */ | ||
| 1333 | /* Since for one-shot mode, refresh rate is usually set larger than | ||
| 1334 | * expected refresh rate, it needs at least 3 frame period. Less | ||
| 1335 | * delay one shot work is, more powering saving we have. */ | ||
| 1336 | dc->one_shot_delay_ms = 4 * | ||
| 1337 | DIV_ROUND_UP(S_TO_MS(1), dsi->info.refresh_rate); | ||
| 1338 | |||
| 1339 | /* Enable DSI clock */ | ||
| 1340 | tegra_dc_setup_clk(dc, dsi->dsi_clk); | ||
| 1341 | if (!dsi->clk_ref) { | ||
| 1342 | dsi->clk_ref = true; | ||
| 1343 | clk_enable(dsi->dsi_clk); | ||
| 1344 | tegra_periph_reset_deassert(dsi->dsi_clk); | ||
| 1345 | } | ||
| 1346 | dsi->current_dsi_clk_khz = clk_get_rate(dsi->dsi_clk) / 1000; | ||
| 1347 | dsi->current_bit_clk_ns = 1000*1000 / (dsi->current_dsi_clk_khz * 2); | ||
| 1348 | } | ||
| 1349 | |||
| 1350 | static void tegra_dsi_hs_clk_out_enable(struct tegra_dc_dsi_data *dsi) | ||
| 1351 | { | ||
| 1352 | u32 val; | ||
| 1353 | |||
| 1354 | val = tegra_dsi_readl(dsi, DSI_CONTROL); | ||
| 1355 | val &= ~DSI_CONTROL_HS_CLK_CTRL(1); | ||
| 1356 | |||
| 1357 | if (dsi->info.video_clock_mode == TEGRA_DSI_VIDEO_CLOCK_CONTINUOUS) { | ||
| 1358 | val |= DSI_CONTROL_HS_CLK_CTRL(CONTINUOUS); | ||
| 1359 | dsi->status.clk_mode = DSI_PHYCLK_CONTINUOUS; | ||
| 1360 | } else { | ||
| 1361 | val |= DSI_CONTROL_HS_CLK_CTRL(TX_ONLY); | ||
| 1362 | dsi->status.clk_mode = DSI_PHYCLK_TX_ONLY; | ||
| 1363 | } | ||
| 1364 | tegra_dsi_writel(dsi, val, DSI_CONTROL); | ||
| 1365 | |||
| 1366 | val = tegra_dsi_readl(dsi, DSI_HOST_DSI_CONTROL); | ||
| 1367 | val &= ~DSI_HOST_DSI_CONTROL_HIGH_SPEED_TRANS(1); | ||
| 1368 | val |= DSI_HOST_DSI_CONTROL_HIGH_SPEED_TRANS(TEGRA_DSI_HIGH); | ||
| 1369 | tegra_dsi_writel(dsi, val, DSI_HOST_DSI_CONTROL); | ||
| 1370 | |||
| 1371 | dsi->status.clk_out = DSI_PHYCLK_OUT_EN; | ||
| 1372 | } | ||
| 1373 | |||
| 1374 | static void tegra_dsi_hs_clk_out_enable_in_lp(struct tegra_dc_dsi_data *dsi) | ||
| 1375 | { | ||
| 1376 | u32 val; | ||
| 1377 | tegra_dsi_hs_clk_out_enable(dsi); | ||
| 1378 | |||
| 1379 | val = tegra_dsi_readl(dsi, DSI_HOST_DSI_CONTROL); | ||
| 1380 | val &= ~DSI_HOST_DSI_CONTROL_HIGH_SPEED_TRANS(1); | ||
| 1381 | val |= DSI_HOST_DSI_CONTROL_HIGH_SPEED_TRANS(TEGRA_DSI_LOW); | ||
| 1382 | tegra_dsi_writel(dsi, val, DSI_HOST_DSI_CONTROL); | ||
| 1383 | } | ||
| 1384 | |||
| 1385 | static void tegra_dsi_hs_clk_out_disable(struct tegra_dc *dc, | ||
| 1386 | struct tegra_dc_dsi_data *dsi) | ||
| 1387 | { | ||
| 1388 | u32 val; | ||
| 1389 | |||
| 1390 | if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE) | ||
| 1391 | tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi); | ||
| 1392 | |||
| 1393 | tegra_dsi_writel(dsi, TEGRA_DSI_DISABLE, DSI_POWER_CONTROL); | ||
| 1394 | /* stabilization delay */ | ||
| 1395 | udelay(300); | ||
| 1396 | |||
| 1397 | val = tegra_dsi_readl(dsi, DSI_HOST_DSI_CONTROL); | ||
| 1398 | val &= ~DSI_HOST_DSI_CONTROL_HIGH_SPEED_TRANS(1); | ||
| 1399 | val |= DSI_HOST_DSI_CONTROL_HIGH_SPEED_TRANS(TEGRA_DSI_LOW); | ||
| 1400 | tegra_dsi_writel(dsi, val, DSI_HOST_DSI_CONTROL); | ||
| 1401 | |||
| 1402 | tegra_dsi_writel(dsi, TEGRA_DSI_ENABLE, DSI_POWER_CONTROL); | ||
| 1403 | /* stabilization delay */ | ||
| 1404 | udelay(300); | ||
| 1405 | |||
| 1406 | dsi->status.clk_mode = DSI_PHYCLK_NOT_INIT; | ||
| 1407 | dsi->status.clk_out = DSI_PHYCLK_OUT_DIS; | ||
| 1408 | } | ||
| 1409 | |||
| 1410 | static void tegra_dsi_set_control_reg_lp(struct tegra_dc_dsi_data *dsi) | ||
| 1411 | { | ||
| 1412 | u32 dsi_control; | ||
| 1413 | u32 host_dsi_control; | ||
| 1414 | u32 max_threshold; | ||
| 1415 | |||
| 1416 | dsi_control = dsi->dsi_control_val | DSI_CTRL_HOST_DRIVEN; | ||
| 1417 | host_dsi_control = HOST_DSI_CTRL_COMMON | | ||
| 1418 | HOST_DSI_CTRL_HOST_DRIVEN | | ||
| 1419 | DSI_HOST_DSI_CONTROL_HIGH_SPEED_TRANS(TEGRA_DSI_LOW); | ||
| 1420 | max_threshold = DSI_MAX_THRESHOLD_MAX_THRESHOLD(DSI_HOST_FIFO_DEPTH); | ||
| 1421 | |||
| 1422 | tegra_dsi_writel(dsi, max_threshold, DSI_MAX_THRESHOLD); | ||
| 1423 | tegra_dsi_writel(dsi, dsi_control, DSI_CONTROL); | ||
| 1424 | tegra_dsi_writel(dsi, host_dsi_control, DSI_HOST_DSI_CONTROL); | ||
| 1425 | |||
| 1426 | dsi->status.driven = DSI_DRIVEN_MODE_HOST; | ||
| 1427 | dsi->status.clk_burst = DSI_CLK_BURST_NOT_INIT; | ||
| 1428 | dsi->status.vtype = DSI_VIDEO_TYPE_NOT_INIT; | ||
| 1429 | } | ||
| 1430 | |||
| 1431 | static void tegra_dsi_set_control_reg_hs(struct tegra_dc_dsi_data *dsi) | ||
| 1432 | { | ||
| 1433 | u32 dsi_control; | ||
| 1434 | u32 host_dsi_control; | ||
| 1435 | u32 max_threshold; | ||
| 1436 | u32 dcs_cmd; | ||
| 1437 | |||
| 1438 | dsi_control = dsi->dsi_control_val; | ||
| 1439 | host_dsi_control = HOST_DSI_CTRL_COMMON; | ||
| 1440 | max_threshold = 0; | ||
| 1441 | dcs_cmd = 0; | ||
| 1442 | |||
| 1443 | if (dsi->driven_mode == TEGRA_DSI_DRIVEN_BY_HOST) { | ||
| 1444 | dsi_control |= DSI_CTRL_HOST_DRIVEN; | ||
| 1445 | host_dsi_control |= HOST_DSI_CTRL_HOST_DRIVEN; | ||
| 1446 | max_threshold = | ||
| 1447 | DSI_MAX_THRESHOLD_MAX_THRESHOLD(DSI_HOST_FIFO_DEPTH); | ||
| 1448 | dsi->status.driven = DSI_DRIVEN_MODE_HOST; | ||
| 1449 | } else { | ||
| 1450 | dsi_control |= DSI_CTRL_DC_DRIVEN; | ||
| 1451 | host_dsi_control |= HOST_DSI_CTRL_DC_DRIVEN; | ||
| 1452 | max_threshold = | ||
| 1453 | DSI_MAX_THRESHOLD_MAX_THRESHOLD(DSI_VIDEO_FIFO_DEPTH); | ||
| 1454 | dsi->status.driven = DSI_DRIVEN_MODE_DC; | ||
| 1455 | } | ||
| 1456 | |||
| 1457 | if (dsi->info.video_data_type == TEGRA_DSI_VIDEO_TYPE_COMMAND_MODE) { | ||
| 1458 | dsi_control |= DSI_CTRL_CMD_MODE; | ||
| 1459 | dcs_cmd = DSI_DCS_CMDS_LT5_DCS_CMD(DSI_WRITE_MEMORY_START)| | ||
| 1460 | DSI_DCS_CMDS_LT3_DCS_CMD(DSI_WRITE_MEMORY_CONTINUE); | ||
| 1461 | dsi->status.vtype = DSI_VIDEO_TYPE_CMD_MODE; | ||
| 1462 | |||
| 1463 | } else { | ||
| 1464 | dsi_control |= DSI_CTRL_VIDEO_MODE; | ||
| 1465 | dsi->status.vtype = DSI_VIDEO_TYPE_VIDEO_MODE; | ||
| 1466 | } | ||
| 1467 | |||
| 1468 | tegra_dsi_writel(dsi, max_threshold, DSI_MAX_THRESHOLD); | ||
| 1469 | tegra_dsi_writel(dsi, dcs_cmd, DSI_DCS_CMDS); | ||
| 1470 | tegra_dsi_writel(dsi, dsi_control, DSI_CONTROL); | ||
| 1471 | tegra_dsi_writel(dsi, host_dsi_control, DSI_HOST_DSI_CONTROL); | ||
| 1472 | } | ||
| 1473 | |||
| 1474 | static void tegra_dsi_pad_calibration(struct tegra_dc_dsi_data *dsi) | ||
| 1475 | { | ||
| 1476 | u32 val; | ||
| 1477 | |||
| 1478 | val = DSI_PAD_CONTROL_PAD_LPUPADJ(0x1) | | ||
| 1479 | DSI_PAD_CONTROL_PAD_LPDNADJ(0x1) | | ||
| 1480 | DSI_PAD_CONTROL_PAD_PREEMP_EN(0x1) | | ||
| 1481 | DSI_PAD_CONTROL_PAD_SLEWDNADJ(0x6) | | ||
| 1482 | DSI_PAD_CONTROL_PAD_SLEWUPADJ(0x6); | ||
| 1483 | if (!dsi->ulpm) { | ||
| 1484 | val |= DSI_PAD_CONTROL_PAD_PDIO(0) | | ||
| 1485 | DSI_PAD_CONTROL_PAD_PDIO_CLK(0) | | ||
| 1486 | DSI_PAD_CONTROL_PAD_PULLDN_ENAB(TEGRA_DSI_DISABLE); | ||
| 1487 | } else { | ||
| 1488 | val |= DSI_PAD_CONTROL_PAD_PDIO(0x3) | | ||
| 1489 | DSI_PAD_CONTROL_PAD_PDIO_CLK(0x1) | | ||
| 1490 | DSI_PAD_CONTROL_PAD_PULLDN_ENAB(TEGRA_DSI_ENABLE); | ||
| 1491 | } | ||
| 1492 | tegra_dsi_writel(dsi, val, DSI_PAD_CONTROL); | ||
| 1493 | |||
| 1494 | val = MIPI_CAL_TERMOSA(0x4); | ||
| 1495 | tegra_vi_csi_writel(val, CSI_CILA_MIPI_CAL_CONFIG_0); | ||
| 1496 | |||
| 1497 | val = MIPI_CAL_TERMOSB(0x4); | ||
| 1498 | tegra_vi_csi_writel(val, CSI_CILB_MIPI_CAL_CONFIG_0); | ||
| 1499 | |||
| 1500 | val = MIPI_CAL_HSPUOSD(0x3) | MIPI_CAL_HSPDOSD(0x4); | ||
| 1501 | tegra_vi_csi_writel(val, CSI_DSI_MIPI_CAL_CONFIG); | ||
| 1502 | |||
| 1503 | val = PAD_DRIV_DN_REF(0x5) | PAD_DRIV_UP_REF(0x7); | ||
| 1504 | tegra_vi_csi_writel(val, CSI_MIPIBIAS_PAD_CONFIG); | ||
| 1505 | |||
| 1506 | val = PAD_CIL_PDVREG(0x0); | ||
| 1507 | tegra_vi_csi_writel(val, CSI_CIL_PAD_CONFIG); | ||
| 1508 | } | ||
| 1509 | |||
| 1510 | static int tegra_dsi_init_hw(struct tegra_dc *dc, | ||
| 1511 | struct tegra_dc_dsi_data *dsi) | ||
| 1512 | { | ||
| 1513 | u32 i; | ||
| 1514 | |||
| 1515 | tegra_dsi_writel(dsi, | ||
| 1516 | DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_DISABLE), | ||
| 1517 | DSI_POWER_CONTROL); | ||
| 1518 | /* stabilization delay */ | ||
| 1519 | udelay(300); | ||
| 1520 | |||
| 1521 | tegra_dsi_set_dsi_clk(dc, dsi, dsi->target_lp_clk_khz); | ||
| 1522 | if (dsi->info.dsi_instance) { | ||
| 1523 | /* TODO:Set the misc register*/ | ||
| 1524 | } | ||
| 1525 | |||
| 1526 | /* TODO: only need to change the timing for bta */ | ||
| 1527 | tegra_dsi_set_phy_timing(dsi, DSI_LPHS_IN_LP_MODE); | ||
| 1528 | |||
| 1529 | if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE) | ||
| 1530 | tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi); | ||
| 1531 | |||
| 1532 | /* Initializing DSI registers */ | ||
| 1533 | for (i = 0; i < ARRAY_SIZE(init_reg); i++) | ||
| 1534 | tegra_dsi_writel(dsi, 0, init_reg[i]); | ||
| 1535 | |||
| 1536 | tegra_dsi_writel(dsi, dsi->dsi_control_val, DSI_CONTROL); | ||
| 1537 | |||
| 1538 | tegra_dsi_pad_calibration(dsi); | ||
| 1539 | |||
| 1540 | tegra_dsi_writel(dsi, | ||
| 1541 | DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_ENABLE), | ||
| 1542 | DSI_POWER_CONTROL); | ||
| 1543 | /* stabilization delay */ | ||
| 1544 | udelay(300); | ||
| 1545 | |||
| 1546 | dsi->status.init = DSI_MODULE_INIT; | ||
| 1547 | dsi->status.lphs = DSI_LPHS_NOT_INIT; | ||
| 1548 | dsi->status.vtype = DSI_VIDEO_TYPE_NOT_INIT; | ||
| 1549 | dsi->status.driven = DSI_DRIVEN_MODE_NOT_INIT; | ||
| 1550 | dsi->status.clk_out = DSI_PHYCLK_OUT_DIS; | ||
| 1551 | dsi->status.clk_mode = DSI_PHYCLK_NOT_INIT; | ||
| 1552 | dsi->status.clk_burst = DSI_CLK_BURST_NOT_INIT; | ||
| 1553 | dsi->status.dc_stream = DSI_DC_STREAM_DISABLE; | ||
| 1554 | dsi->status.lp_op = DSI_LP_OP_NOT_INIT; | ||
| 1555 | |||
| 1556 | return 0; | ||
| 1557 | } | ||
| 1558 | |||
| 1559 | static int tegra_dsi_set_to_lp_mode(struct tegra_dc *dc, | ||
| 1560 | struct tegra_dc_dsi_data *dsi, u8 lp_op) | ||
| 1561 | { | ||
| 1562 | int err; | ||
| 1563 | |||
| 1564 | if (dsi->status.init != DSI_MODULE_INIT) { | ||
| 1565 | err = -EPERM; | ||
| 1566 | goto fail; | ||
| 1567 | } | ||
| 1568 | |||
| 1569 | if (dsi->status.lphs == DSI_LPHS_IN_LP_MODE && | ||
| 1570 | dsi->status.lp_op == lp_op) | ||
| 1571 | goto success; | ||
| 1572 | |||
| 1573 | if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE) | ||
| 1574 | tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi); | ||
| 1575 | |||
| 1576 | /* disable/enable hs clk according to enable_hs_clock_on_lp_cmd_mode */ | ||
| 1577 | if ((dsi->status.clk_out == DSI_PHYCLK_OUT_EN) && | ||
| 1578 | (!dsi->info.enable_hs_clock_on_lp_cmd_mode)) | ||
| 1579 | tegra_dsi_hs_clk_out_disable(dc, dsi); | ||
| 1580 | |||
| 1581 | dsi->target_lp_clk_khz = tegra_dsi_get_lp_clk_rate(dsi, lp_op); | ||
| 1582 | if (dsi->current_dsi_clk_khz != dsi->target_lp_clk_khz) { | ||
| 1583 | tegra_dsi_set_dsi_clk(dc, dsi, dsi->target_lp_clk_khz); | ||
| 1584 | tegra_dsi_set_timeout(dsi); | ||
| 1585 | } | ||
| 1586 | |||
| 1587 | tegra_dsi_set_phy_timing(dsi, DSI_LPHS_IN_LP_MODE); | ||
| 1588 | |||
| 1589 | tegra_dsi_set_control_reg_lp(dsi); | ||
| 1590 | |||
| 1591 | if ((dsi->status.clk_out == DSI_PHYCLK_OUT_DIS) && | ||
| 1592 | (dsi->info.enable_hs_clock_on_lp_cmd_mode)) | ||
| 1593 | tegra_dsi_hs_clk_out_enable_in_lp(dsi); | ||
| 1594 | |||
| 1595 | dsi->status.lphs = DSI_LPHS_IN_LP_MODE; | ||
| 1596 | dsi->status.lp_op = lp_op; | ||
| 1597 | success: | ||
| 1598 | err = 0; | ||
| 1599 | fail: | ||
| 1600 | return err; | ||
| 1601 | } | ||
| 1602 | |||
| 1603 | static int tegra_dsi_set_to_hs_mode(struct tegra_dc *dc, | ||
| 1604 | struct tegra_dc_dsi_data *dsi) | ||
| 1605 | { | ||
| 1606 | int err; | ||
| 1607 | |||
| 1608 | if (dsi->status.init != DSI_MODULE_INIT) { | ||
| 1609 | err = -EPERM; | ||
| 1610 | goto fail; | ||
| 1611 | } | ||
| 1612 | |||
| 1613 | if (dsi->status.lphs == DSI_LPHS_IN_HS_MODE) | ||
| 1614 | goto success; | ||
| 1615 | |||
| 1616 | if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE) | ||
| 1617 | tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi); | ||
| 1618 | |||
| 1619 | if ((dsi->status.clk_out == DSI_PHYCLK_OUT_EN) && | ||
| 1620 | (!dsi->info.enable_hs_clock_on_lp_cmd_mode)) | ||
| 1621 | tegra_dsi_hs_clk_out_disable(dc, dsi); | ||
| 1622 | |||
| 1623 | if (dsi->current_dsi_clk_khz != dsi->target_hs_clk_khz) { | ||
| 1624 | tegra_dsi_set_dsi_clk(dc, dsi, dsi->target_hs_clk_khz); | ||
| 1625 | tegra_dsi_set_timeout(dsi); | ||
| 1626 | } | ||
| 1627 | |||
| 1628 | tegra_dsi_set_phy_timing(dsi, DSI_LPHS_IN_HS_MODE); | ||
| 1629 | |||
| 1630 | if (dsi->driven_mode == TEGRA_DSI_DRIVEN_BY_DC) { | ||
| 1631 | tegra_dsi_set_pkt_seq(dc, dsi); | ||
| 1632 | tegra_dsi_set_pkt_length(dc, dsi); | ||
| 1633 | tegra_dsi_set_sol_delay(dc, dsi); | ||
| 1634 | tegra_dsi_set_dc_clk(dc, dsi); | ||
| 1635 | } | ||
| 1636 | |||
| 1637 | tegra_dsi_set_control_reg_hs(dsi); | ||
| 1638 | |||
| 1639 | if (dsi->status.clk_out == DSI_PHYCLK_OUT_DIS || | ||
| 1640 | dsi->info.enable_hs_clock_on_lp_cmd_mode) | ||
| 1641 | tegra_dsi_hs_clk_out_enable(dsi); | ||
| 1642 | |||
| 1643 | dsi->status.lphs = DSI_LPHS_IN_HS_MODE; | ||
| 1644 | success: | ||
| 1645 | dsi->status.lp_op = DSI_LP_OP_NOT_INIT; | ||
| 1646 | err = 0; | ||
| 1647 | fail: | ||
| 1648 | return err; | ||
| 1649 | } | ||
| 1650 | |||
| 1651 | static bool tegra_dsi_write_busy(struct tegra_dc_dsi_data *dsi) | ||
| 1652 | { | ||
| 1653 | u32 timeout = 0; | ||
| 1654 | bool retVal = true; | ||
| 1655 | |||
| 1656 | while (timeout <= DSI_MAX_COMMAND_DELAY_USEC) { | ||
| 1657 | if (!(DSI_TRIGGER_HOST_TRIGGER(0x1) & | ||
| 1658 | tegra_dsi_readl(dsi, DSI_TRIGGER))) { | ||
| 1659 | retVal = false; | ||
| 1660 | break; | ||
| 1661 | } | ||
| 1662 | udelay(DSI_COMMAND_DELAY_STEPS_USEC); | ||
| 1663 | timeout += DSI_COMMAND_DELAY_STEPS_USEC; | ||
| 1664 | } | ||
| 1665 | |||
| 1666 | return retVal; | ||
| 1667 | } | ||
| 1668 | |||
| 1669 | static bool tegra_dsi_read_busy(struct tegra_dc_dsi_data *dsi) | ||
| 1670 | { | ||
| 1671 | u32 timeout = 0; | ||
| 1672 | bool retVal = true; | ||
| 1673 | |||
| 1674 | while (timeout < DSI_STATUS_POLLING_DURATION_USEC) { | ||
| 1675 | if (!(DSI_HOST_DSI_CONTROL_IMM_BTA(0x1) & | ||
| 1676 | tegra_dsi_readl(dsi, DSI_HOST_DSI_CONTROL))) { | ||
| 1677 | retVal = false; | ||
| 1678 | break; | ||
| 1679 | } | ||
| 1680 | udelay(DSI_STATUS_POLLING_DELAY_USEC); | ||
| 1681 | timeout += DSI_STATUS_POLLING_DELAY_USEC; | ||
| 1682 | } | ||
| 1683 | |||
| 1684 | return retVal; | ||
| 1685 | } | ||
| 1686 | |||
| 1687 | static bool tegra_dsi_host_busy(struct tegra_dc_dsi_data *dsi) | ||
| 1688 | { | ||
| 1689 | int err = 0; | ||
| 1690 | |||
| 1691 | if (tegra_dsi_write_busy(dsi)) { | ||
| 1692 | err = -EBUSY; | ||
| 1693 | dev_err(&dsi->dc->ndev->dev, | ||
| 1694 | "DSI trigger bit already set\n"); | ||
| 1695 | goto fail; | ||
| 1696 | } | ||
| 1697 | |||
| 1698 | if (tegra_dsi_read_busy(dsi)) { | ||
| 1699 | err = -EBUSY; | ||
| 1700 | dev_err(&dsi->dc->ndev->dev, | ||
| 1701 | "DSI immediate bta bit already set\n"); | ||
| 1702 | goto fail; | ||
| 1703 | } | ||
| 1704 | fail: | ||
| 1705 | return (err < 0 ? true : false); | ||
| 1706 | } | ||
| 1707 | |||
| 1708 | static void tegra_dsi_soft_reset(struct tegra_dc_dsi_data *dsi) | ||
| 1709 | { | ||
| 1710 | u32 trigger; | ||
| 1711 | u32 status; | ||
| 1712 | |||
| 1713 | tegra_dsi_writel(dsi, | ||
| 1714 | DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_DISABLE), | ||
| 1715 | DSI_POWER_CONTROL); | ||
| 1716 | /* stabilization delay */ | ||
| 1717 | udelay(300); | ||
| 1718 | |||
| 1719 | tegra_dsi_writel(dsi, | ||
| 1720 | DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_ENABLE), | ||
| 1721 | DSI_POWER_CONTROL); | ||
| 1722 | /* stabilization delay */ | ||
| 1723 | udelay(300); | ||
| 1724 | |||
| 1725 | /* dsi HW does not clear host trigger bit automatically | ||
| 1726 | * on dsi interface disable if host fifo is empty | ||
| 1727 | */ | ||
| 1728 | trigger = tegra_dsi_readl(dsi, DSI_TRIGGER); | ||
| 1729 | status = tegra_dsi_readl(dsi, DSI_STATUS); | ||
| 1730 | if (trigger & DSI_TRIGGER_HOST_TRIGGER(0x1) && | ||
| 1731 | status & DSI_STATUS_IDLE(0x1)) { | ||
| 1732 | trigger &= ~(DSI_TRIGGER_HOST_TRIGGER(0x1)); | ||
| 1733 | tegra_dsi_writel(dsi, trigger, DSI_TRIGGER); | ||
| 1734 | } | ||
| 1735 | } | ||
| 1736 | |||
| 1737 | static void tegra_dsi_reset_read_count(struct tegra_dc_dsi_data *dsi) | ||
| 1738 | { | ||
| 1739 | u32 val; | ||
| 1740 | |||
| 1741 | val = tegra_dsi_readl(dsi, DSI_STATUS); | ||
| 1742 | val &= DSI_STATUS_RD_FIFO_COUNT(0x1f); | ||
| 1743 | if (val) { | ||
| 1744 | dev_warn(&dsi->dc->ndev->dev, | ||
| 1745 | "DSI read count not zero, resetting\n"); | ||
| 1746 | tegra_dsi_soft_reset(dsi); | ||
| 1747 | } | ||
| 1748 | } | ||
| 1749 | |||
| 1750 | static struct dsi_status *tegra_dsi_save_state_switch_to_host_cmd_mode( | ||
| 1751 | struct tegra_dc_dsi_data *dsi, | ||
| 1752 | struct tegra_dc *dc, | ||
| 1753 | u8 lp_op) | ||
| 1754 | { | ||
| 1755 | struct dsi_status *init_status; | ||
| 1756 | int err; | ||
| 1757 | |||
| 1758 | init_status = kzalloc(sizeof(*init_status), GFP_KERNEL); | ||
| 1759 | if (!init_status) | ||
| 1760 | return ERR_PTR(-ENOMEM); | ||
| 1761 | |||
| 1762 | *init_status = dsi->status; | ||
| 1763 | |||
| 1764 | if (dsi->status.lphs == DSI_LPHS_IN_HS_MODE) { | ||
| 1765 | if (dsi->status.driven == DSI_DRIVEN_MODE_DC) { | ||
| 1766 | if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE) | ||
| 1767 | tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi); | ||
| 1768 | dsi->driven_mode = TEGRA_DSI_DRIVEN_BY_HOST; | ||
| 1769 | if (dsi->info.hs_cmd_mode_supported) { | ||
| 1770 | err = tegra_dsi_set_to_hs_mode(dc, dsi); | ||
| 1771 | if (err < 0) { | ||
| 1772 | dev_err(&dc->ndev->dev, | ||
| 1773 | "Switch to HS host mode failed\n"); | ||
| 1774 | goto fail; | ||
| 1775 | } | ||
| 1776 | } | ||
| 1777 | } | ||
| 1778 | if (!dsi->info.hs_cmd_mode_supported) { | ||
| 1779 | err = | ||
| 1780 | tegra_dsi_set_to_lp_mode(dc, dsi, lp_op); | ||
| 1781 | if (err < 0) { | ||
| 1782 | dev_err(&dc->ndev->dev, | ||
| 1783 | "DSI failed to go to LP mode\n"); | ||
| 1784 | goto fail; | ||
| 1785 | } | ||
| 1786 | } | ||
| 1787 | } else if (dsi->status.lphs == DSI_LPHS_IN_LP_MODE) { | ||
| 1788 | if (dsi->status.lp_op != lp_op) { | ||
| 1789 | err = tegra_dsi_set_to_lp_mode(dc, dsi, lp_op); | ||
| 1790 | if (err < 0) { | ||
| 1791 | dev_err(&dc->ndev->dev, | ||
| 1792 | "DSI failed to go to LP mode\n"); | ||
| 1793 | goto fail; | ||
| 1794 | } | ||
| 1795 | } | ||
| 1796 | } | ||
| 1797 | |||
| 1798 | return init_status; | ||
| 1799 | fail: | ||
| 1800 | kfree(init_status); | ||
| 1801 | return ERR_PTR(err); | ||
| 1802 | } | ||
| 1803 | |||
| 1804 | static struct dsi_status *tegra_dsi_prepare_host_transmission( | ||
| 1805 | struct tegra_dc *dc, | ||
| 1806 | struct tegra_dc_dsi_data *dsi, | ||
| 1807 | u8 lp_op) | ||
| 1808 | { | ||
| 1809 | int err = 0; | ||
| 1810 | struct dsi_status *init_status; | ||
| 1811 | |||
| 1812 | if (dsi->status.init != DSI_MODULE_INIT || | ||
| 1813 | dsi->ulpm) { | ||
| 1814 | err = -EPERM; | ||
| 1815 | goto fail; | ||
| 1816 | } | ||
| 1817 | |||
| 1818 | if (tegra_dsi_host_busy(dsi)) { | ||
| 1819 | tegra_dsi_soft_reset(dsi); | ||
| 1820 | if (tegra_dsi_host_busy(dsi)) { | ||
| 1821 | err = -EBUSY; | ||
| 1822 | dev_err(&dc->ndev->dev, "DSI host busy\n"); | ||
| 1823 | goto fail; | ||
| 1824 | } | ||
| 1825 | } | ||
| 1826 | |||
| 1827 | if (lp_op == DSI_LP_OP_READ) | ||
| 1828 | tegra_dsi_reset_read_count(dsi); | ||
| 1829 | |||
| 1830 | if (dsi->status.lphs == DSI_LPHS_NOT_INIT) { | ||
| 1831 | err = tegra_dsi_set_to_lp_mode(dc, dsi, lp_op); | ||
| 1832 | if (err < 0) { | ||
| 1833 | dev_err(&dc->ndev->dev, "Failed to config LP write\n"); | ||
| 1834 | goto fail; | ||
| 1835 | } | ||
| 1836 | } | ||
| 1837 | |||
| 1838 | init_status = tegra_dsi_save_state_switch_to_host_cmd_mode | ||
| 1839 | (dsi, dc, lp_op); | ||
| 1840 | if (IS_ERR_OR_NULL(init_status)) { | ||
| 1841 | err = PTR_ERR(init_status); | ||
| 1842 | dev_err(&dc->ndev->dev, "DSI state saving failed\n"); | ||
| 1843 | goto fail; | ||
| 1844 | } | ||
| 1845 | |||
| 1846 | return init_status; | ||
| 1847 | fail: | ||
| 1848 | return ERR_PTR(err); | ||
| 1849 | } | ||
| 1850 | |||
| 1851 | static int tegra_dsi_restore_state(struct tegra_dc *dc, | ||
| 1852 | struct tegra_dc_dsi_data *dsi, | ||
| 1853 | struct dsi_status *init_status) | ||
| 1854 | { | ||
| 1855 | bool switch_back_to_dc_mode = false; | ||
| 1856 | bool switch_back_to_hs_mode = false; | ||
| 1857 | bool restart_dc_stream; | ||
| 1858 | int err = 0; | ||
| 1859 | |||
| 1860 | switch_back_to_dc_mode = (dsi->status.driven == | ||
| 1861 | DSI_DRIVEN_MODE_HOST && | ||
| 1862 | init_status->driven == | ||
| 1863 | DSI_DRIVEN_MODE_DC); | ||
| 1864 | switch_back_to_hs_mode = (dsi->status.lphs == | ||
| 1865 | DSI_LPHS_IN_LP_MODE && | ||
| 1866 | init_status->lphs == | ||
| 1867 | DSI_LPHS_IN_HS_MODE); | ||
| 1868 | restart_dc_stream = (dsi->status.dc_stream == | ||
| 1869 | DSI_DC_STREAM_DISABLE && | ||
| 1870 | init_status->dc_stream == | ||
| 1871 | DSI_DC_STREAM_ENABLE); | ||
| 1872 | |||
| 1873 | if (dsi->status.lphs == DSI_LPHS_IN_LP_MODE && | ||
| 1874 | init_status->lphs == DSI_LPHS_IN_LP_MODE) { | ||
| 1875 | if (dsi->status.lp_op != init_status->lp_op) { | ||
| 1876 | err = | ||
| 1877 | tegra_dsi_set_to_lp_mode(dc, dsi, init_status->lp_op); | ||
| 1878 | if (err < 0) { | ||
| 1879 | dev_err(&dc->ndev->dev, | ||
| 1880 | "Failed to config LP mode\n"); | ||
| 1881 | goto fail; | ||
| 1882 | } | ||
| 1883 | } | ||
| 1884 | goto success; | ||
| 1885 | } | ||
| 1886 | |||
| 1887 | if (switch_back_to_dc_mode) | ||
| 1888 | dsi->driven_mode = TEGRA_DSI_DRIVEN_BY_DC; | ||
| 1889 | if (switch_back_to_dc_mode || switch_back_to_hs_mode) { | ||
| 1890 | err = tegra_dsi_set_to_hs_mode(dc, dsi); | ||
| 1891 | if (err < 0) { | ||
| 1892 | dev_err(&dc->ndev->dev, "Failed to config HS mode\n"); | ||
| 1893 | goto fail; | ||
| 1894 | } | ||
| 1895 | } | ||
| 1896 | if (restart_dc_stream) | ||
| 1897 | tegra_dsi_start_dc_stream(dc, dsi); | ||
| 1898 | |||
| 1899 | success: | ||
| 1900 | fail: | ||
| 1901 | kfree(init_status); | ||
| 1902 | return err; | ||
| 1903 | } | ||
| 1904 | |||
| 1905 | static int tegra_dsi_host_trigger(struct tegra_dc_dsi_data *dsi) | ||
| 1906 | { | ||
| 1907 | int status = 0; | ||
| 1908 | |||
| 1909 | if (tegra_dsi_readl(dsi, DSI_TRIGGER)) { | ||
| 1910 | status = -EBUSY; | ||
| 1911 | goto fail; | ||
| 1912 | } | ||
| 1913 | |||
| 1914 | tegra_dsi_writel(dsi, | ||
| 1915 | DSI_TRIGGER_HOST_TRIGGER(TEGRA_DSI_ENABLE), DSI_TRIGGER); | ||
| 1916 | |||
| 1917 | #if DSI_USE_SYNC_POINTS | ||
| 1918 | status = tegra_dsi_syncpt(dsi); | ||
| 1919 | if (status < 0) { | ||
| 1920 | dev_err(&dsi->dc->ndev->dev, | ||
| 1921 | "DSI syncpt for host trigger failed\n"); | ||
| 1922 | goto fail; | ||
| 1923 | } | ||
| 1924 | #else | ||
| 1925 | if (tegra_dsi_write_busy(dsi)) { | ||
| 1926 | status = -EBUSY; | ||
| 1927 | dev_err(&dsi->dc->ndev->dev, | ||
| 1928 | "Timeout waiting on write completion\n"); | ||
| 1929 | } | ||
| 1930 | #endif | ||
| 1931 | |||
| 1932 | fail: | ||
| 1933 | return status; | ||
| 1934 | } | ||
| 1935 | |||
| 1936 | static int _tegra_dsi_write_data(struct tegra_dc_dsi_data *dsi, | ||
| 1937 | u8 *pdata, u8 data_id, u16 data_len) | ||
| 1938 | { | ||
| 1939 | u8 virtual_channel; | ||
| 1940 | u8 *pval; | ||
| 1941 | u32 val; | ||
| 1942 | int err; | ||
| 1943 | |||
| 1944 | err = 0; | ||
| 1945 | |||
| 1946 | virtual_channel = dsi->info.virtual_channel << | ||
| 1947 | DSI_VIR_CHANNEL_BIT_POSITION; | ||
| 1948 | |||
| 1949 | /* always use hw for ecc */ | ||
| 1950 | val = (virtual_channel | data_id) << 0 | | ||
| 1951 | data_len << 8; | ||
| 1952 | tegra_dsi_writel(dsi, val, DSI_WR_DATA); | ||
| 1953 | |||
| 1954 | /* if pdata != NULL, pkt type is long pkt */ | ||
| 1955 | if (pdata != NULL) { | ||
| 1956 | while (data_len) { | ||
| 1957 | if (data_len >= 4) { | ||
| 1958 | val = ((u32 *) pdata)[0]; | ||
| 1959 | data_len -= 4; | ||
| 1960 | pdata += 4; | ||
| 1961 | } else { | ||
| 1962 | val = 0; | ||
| 1963 | pval = (u8 *) &val; | ||
| 1964 | do | ||
| 1965 | *pval++ = *pdata++; | ||
| 1966 | while (--data_len); | ||
| 1967 | } | ||
| 1968 | tegra_dsi_writel(dsi, val, DSI_WR_DATA); | ||
| 1969 | } | ||
| 1970 | } | ||
| 1971 | |||
| 1972 | err = tegra_dsi_host_trigger(dsi); | ||
| 1973 | if (err < 0) | ||
| 1974 | dev_err(&dsi->dc->ndev->dev, "DSI host trigger failed\n"); | ||
| 1975 | |||
| 1976 | return err; | ||
| 1977 | } | ||
| 1978 | |||
| 1979 | int tegra_dsi_write_data(struct tegra_dc *dc, | ||
| 1980 | struct tegra_dc_dsi_data *dsi, | ||
| 1981 | u8 *pdata, u8 data_id, u16 data_len) | ||
| 1982 | { | ||
| 1983 | int err = 0; | ||
| 1984 | struct dsi_status *init_status; | ||
| 1985 | |||
| 1986 | tegra_dc_io_start(dc); | ||
| 1987 | |||
| 1988 | init_status = tegra_dsi_prepare_host_transmission( | ||
| 1989 | dc, dsi, DSI_LP_OP_WRITE); | ||
| 1990 | if (IS_ERR_OR_NULL(init_status)) { | ||
| 1991 | err = PTR_ERR(init_status); | ||
| 1992 | dev_err(&dc->ndev->dev, "DSI host config failed\n"); | ||
| 1993 | goto fail; | ||
| 1994 | } | ||
| 1995 | |||
| 1996 | err = _tegra_dsi_write_data(dsi, pdata, data_id, data_len); | ||
| 1997 | fail: | ||
| 1998 | err = tegra_dsi_restore_state(dc, dsi, init_status); | ||
| 1999 | if (err < 0) | ||
| 2000 | dev_err(&dc->ndev->dev, "Failed to restore prev state\n"); | ||
| 2001 | tegra_dc_io_end(dc); | ||
| 2002 | return err; | ||
| 2003 | } | ||
| 2004 | EXPORT_SYMBOL(tegra_dsi_write_data); | ||
| 2005 | |||
| 2006 | static int tegra_dsi_send_panel_cmd(struct tegra_dc *dc, | ||
| 2007 | struct tegra_dc_dsi_data *dsi, | ||
| 2008 | struct tegra_dsi_cmd *cmd, | ||
| 2009 | u32 n_cmd) | ||
| 2010 | { | ||
| 2011 | u32 i; | ||
| 2012 | int err; | ||
| 2013 | |||
| 2014 | err = 0; | ||
| 2015 | for (i = 0; i < n_cmd; i++) { | ||
| 2016 | struct tegra_dsi_cmd *cur_cmd; | ||
| 2017 | cur_cmd = &cmd[i]; | ||
| 2018 | |||
| 2019 | if (cur_cmd->cmd_type == TEGRA_DSI_DELAY_MS) | ||
| 2020 | mdelay(cur_cmd->sp_len_dly.delay_ms); | ||
| 2021 | else { | ||
| 2022 | err = tegra_dsi_write_data(dc, dsi, | ||
| 2023 | cur_cmd->pdata, | ||
| 2024 | cur_cmd->data_id, | ||
| 2025 | cur_cmd->sp_len_dly.data_len); | ||
| 2026 | if (err < 0) | ||
| 2027 | break; | ||
| 2028 | } | ||
| 2029 | } | ||
| 2030 | return err; | ||
| 2031 | } | ||
| 2032 | |||
| 2033 | static u8 get_8bit_ecc(u32 header) | ||
| 2034 | { | ||
| 2035 | char ecc_parity[24] = { | ||
| 2036 | 0x07, 0x0b, 0x0d, 0x0e, 0x13, 0x15, 0x16, 0x19, | ||
| 2037 | 0x1a, 0x1c, 0x23, 0x25, 0x26, 0x29, 0x2a, 0x2c, | ||
| 2038 | 0x31, 0x32, 0x34, 0x38, 0x1f, 0x2f, 0x37, 0x3b | ||
| 2039 | }; | ||
| 2040 | u8 ecc_byte; | ||
| 2041 | int i; | ||
| 2042 | |||
| 2043 | ecc_byte = 0; | ||
| 2044 | for (i = 0; i < 24; i++) | ||
| 2045 | ecc_byte ^= ((header >> i) & 1) ? ecc_parity[i] : 0x00; | ||
| 2046 | |||
| 2047 | return ecc_byte; | ||
| 2048 | } | ||
| 2049 | |||
| 2050 | /* This function is written to send DCS short write (1 parameter) only. | ||
| 2051 | * This means the cmd will contain only 1 byte of index and 1 byte of value. | ||
| 2052 | * The data type ID is fixed at 0x15 and the ECC is calculated based on the | ||
| 2053 | * data in pdata. | ||
| 2054 | * The command will be sent by hardware every frame. | ||
| 2055 | * pdata should contain both the index + value for each cmd. | ||
| 2056 | * data_len will be the total number of bytes in pdata. | ||
| 2057 | */ | ||
| 2058 | int tegra_dsi_send_panel_short_cmd(struct tegra_dc *dc, u8 *pdata, u8 data_len) | ||
| 2059 | { | ||
| 2060 | u8 ecc8bits = 0, data_len_orig = 0; | ||
| 2061 | u32 val = 0, pkthdr = 0; | ||
| 2062 | int err = 0, count = 0; | ||
| 2063 | struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); | ||
| 2064 | |||
| 2065 | data_len_orig = data_len; | ||
| 2066 | if (pdata != NULL) { | ||
| 2067 | while (data_len) { | ||
| 2068 | if (data_len >= 2) { | ||
| 2069 | pkthdr = (CMD_SHORTW | | ||
| 2070 | (((u16 *)pdata)[0]) << 8 | 0x00 << 24); | ||
| 2071 | ecc8bits = get_8bit_ecc(pkthdr); | ||
| 2072 | val = (pkthdr | (ecc8bits << 24)); | ||
| 2073 | data_len -= 2; | ||
| 2074 | pdata += 2; | ||
| 2075 | count++; | ||
| 2076 | } | ||
| 2077 | switch (count) { | ||
| 2078 | case 1: | ||
| 2079 | tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_0); | ||
| 2080 | break; | ||
| 2081 | case 2: | ||
| 2082 | tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_1); | ||
| 2083 | break; | ||
| 2084 | case 3: | ||
| 2085 | tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_2); | ||
| 2086 | break; | ||
| 2087 | case 4: | ||
| 2088 | tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_3); | ||
| 2089 | break; | ||
| 2090 | case 5: | ||
| 2091 | tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_4); | ||
| 2092 | break; | ||
| 2093 | case 6: | ||
| 2094 | tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_5); | ||
| 2095 | break; | ||
| 2096 | case 7: | ||
| 2097 | tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_6); | ||
| 2098 | break; | ||
| 2099 | case 8: | ||
| 2100 | tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_7); | ||
| 2101 | break; | ||
| 2102 | default: | ||
| 2103 | err = 1; | ||
| 2104 | break; | ||
| 2105 | } | ||
| 2106 | } | ||
| 2107 | } | ||
| 2108 | |||
| 2109 | val = DSI_INIT_SEQ_CONTROL_DSI_FRAME_INIT_BYTE_COUNT(data_len_orig * 2) | ||
| 2110 | | DSI_INIT_SEQ_CONTROL_DSI_SEND_INIT_SEQUENCE(1); | ||
| 2111 | tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_CONTROL); | ||
| 2112 | |||
| 2113 | return err; | ||
| 2114 | } | ||
| 2115 | EXPORT_SYMBOL(tegra_dsi_send_panel_short_cmd); | ||
| 2116 | |||
| 2117 | static int tegra_dsi_bta(struct tegra_dc_dsi_data *dsi) | ||
| 2118 | { | ||
| 2119 | u32 val; | ||
| 2120 | u32 poll_time; | ||
| 2121 | int err; | ||
| 2122 | |||
| 2123 | poll_time = 0; | ||
| 2124 | err = 0; | ||
| 2125 | |||
| 2126 | val = tegra_dsi_readl(dsi, DSI_HOST_DSI_CONTROL); | ||
| 2127 | val |= DSI_HOST_DSI_CONTROL_IMM_BTA(TEGRA_DSI_ENABLE); | ||
| 2128 | tegra_dsi_writel(dsi, val, DSI_HOST_DSI_CONTROL); | ||
| 2129 | |||
| 2130 | #if DSI_USE_SYNC_POINTS | ||
| 2131 | /* FIXME: Workaround for nvhost_syncpt_read */ | ||
| 2132 | dsi->syncpt_val = nvhost_syncpt_update_min( | ||
| 2133 | &nvhost_get_host(dsi->dc->ndev)->syncpt, | ||
| 2134 | dsi->syncpt_id); | ||
| 2135 | |||
| 2136 | val = DSI_INCR_SYNCPT_COND(OP_DONE) | | ||
| 2137 | DSI_INCR_SYNCPT_INDX(dsi->syncpt_id); | ||
| 2138 | tegra_dsi_writel(dsi, val, DSI_INCR_SYNCPT); | ||
| 2139 | |||
| 2140 | /* TODO: Use interrupt rather than polling */ | ||
| 2141 | err = nvhost_syncpt_wait(&nvhost_get_host(dsi->dc->ndev)->syncpt, | ||
| 2142 | dsi->syncpt_id, dsi->syncpt_val + 1); | ||
| 2143 | if (err < 0) | ||
| 2144 | dev_err(&dsi->dc->ndev->dev, | ||
| 2145 | "DSI sync point failure\n"); | ||
| 2146 | else | ||
| 2147 | (dsi->syncpt_val)++; | ||
| 2148 | #else | ||
| 2149 | if (tegra_dsi_read_busy(dsi)) { | ||
| 2150 | err = -EBUSY; | ||
| 2151 | dev_err(&dsi->dc->ndev->dev, | ||
| 2152 | "Timeout wating on read completion\n"); | ||
| 2153 | } | ||
| 2154 | #endif | ||
| 2155 | |||
| 2156 | return err; | ||
| 2157 | } | ||
| 2158 | |||
| 2159 | static int tegra_dsi_parse_read_response(struct tegra_dc *dc, | ||
| 2160 | u32 rd_fifo_cnt, u8 *read_fifo) | ||
| 2161 | { | ||
| 2162 | int err; | ||
| 2163 | u32 payload_size; | ||
| 2164 | |||
| 2165 | payload_size = 0; | ||
| 2166 | err = 0; | ||
| 2167 | |||
| 2168 | switch (read_fifo[0]) { | ||
| 2169 | case DSI_ESCAPE_CMD: | ||
| 2170 | dev_info(&dc->ndev->dev, "escape cmd[0x%x]\n", read_fifo[0]); | ||
| 2171 | break; | ||
| 2172 | case DSI_ACK_NO_ERR: | ||
| 2173 | dev_info(&dc->ndev->dev, | ||
| 2174 | "Panel ack, no err[0x%x]\n", read_fifo[0]); | ||
| 2175 | return err; | ||
| 2176 | default: | ||
| 2177 | dev_info(&dc->ndev->dev, "Invalid read response\n"); | ||
| 2178 | break; | ||
| 2179 | } | ||
| 2180 | |||
| 2181 | switch (read_fifo[4] & 0xff) { | ||
| 2182 | case GEN_LONG_RD_RES: | ||
| 2183 | /* Fall through */ | ||
| 2184 | case DCS_LONG_RD_RES: | ||
| 2185 | payload_size = (read_fifo[5] | | ||
| 2186 | (read_fifo[6] << 8)) & 0xFFFF; | ||
| 2187 | dev_info(&dc->ndev->dev, "Long read response Packet\n" | ||
| 2188 | "payload_size[0x%x]\n", payload_size); | ||
| 2189 | break; | ||
| 2190 | case GEN_1_BYTE_SHORT_RD_RES: | ||
| 2191 | /* Fall through */ | ||
| 2192 | case DCS_1_BYTE_SHORT_RD_RES: | ||
| 2193 | payload_size = 1; | ||
| 2194 | dev_info(&dc->ndev->dev, "Short read response Packet\n" | ||
| 2195 | "payload_size[0x%x]\n", payload_size); | ||
| 2196 | break; | ||
| 2197 | case GEN_2_BYTE_SHORT_RD_RES: | ||
| 2198 | /* Fall through */ | ||
| 2199 | case DCS_2_BYTE_SHORT_RD_RES: | ||
| 2200 | payload_size = 2; | ||
| 2201 | dev_info(&dc->ndev->dev, "Short read response Packet\n" | ||
| 2202 | "payload_size[0x%x]\n", payload_size); | ||
| 2203 | break; | ||
| 2204 | case ACK_ERR_RES: | ||
| 2205 | payload_size = 2; | ||
| 2206 | dev_info(&dc->ndev->dev, "Acknowledge error report response\n" | ||
| 2207 | "Packet payload_size[0x%x]\n", payload_size); | ||
| 2208 | break; | ||
| 2209 | default: | ||
| 2210 | dev_info(&dc->ndev->dev, "Invalid response packet\n"); | ||
| 2211 | err = -EINVAL; | ||
| 2212 | break; | ||
| 2213 | } | ||
| 2214 | return err; | ||
| 2215 | } | ||
| 2216 | |||
| 2217 | static int tegra_dsi_read_fifo(struct tegra_dc *dc, | ||
| 2218 | struct tegra_dc_dsi_data *dsi, | ||
| 2219 | u8 *read_fifo) | ||
| 2220 | { | ||
| 2221 | u32 val; | ||
| 2222 | u32 i; | ||
| 2223 | u32 poll_time = 0; | ||
| 2224 | u32 rd_fifo_cnt; | ||
| 2225 | int err = 0; | ||
| 2226 | u8 *read_fifo_cp = read_fifo; | ||
| 2227 | |||
| 2228 | while (poll_time < DSI_DELAY_FOR_READ_FIFO) { | ||
| 2229 | mdelay(1); | ||
| 2230 | val = tegra_dsi_readl(dsi, DSI_STATUS); | ||
| 2231 | rd_fifo_cnt = val & DSI_STATUS_RD_FIFO_COUNT(0x1f); | ||
| 2232 | if (rd_fifo_cnt << 2 > DSI_READ_FIFO_DEPTH) | ||
| 2233 | dev_err(&dc->ndev->dev, | ||
| 2234 | "DSI RD_FIFO_CNT is greater than RD_FIFO_DEPTH\n"); | ||
| 2235 | break; | ||
| 2236 | poll_time++; | ||
| 2237 | } | ||
| 2238 | |||
| 2239 | if (rd_fifo_cnt == 0) { | ||
| 2240 | dev_info(&dc->ndev->dev, | ||
| 2241 | "DSI RD_FIFO_CNT is zero\n"); | ||
| 2242 | err = -EINVAL; | ||
| 2243 | goto fail; | ||
| 2244 | } | ||
| 2245 | |||
| 2246 | if (val & (DSI_STATUS_LB_UNDERFLOW(0x1) | | ||
| 2247 | DSI_STATUS_LB_OVERFLOW(0x1))) { | ||
| 2248 | dev_warn(&dc->ndev->dev, | ||
| 2249 | "DSI overflow/underflow error\n"); | ||
| 2250 | } | ||
| 2251 | |||
| 2252 | /* Read data from FIFO */ | ||
| 2253 | for (i = 0; i < rd_fifo_cnt; i++) { | ||
| 2254 | val = tegra_dsi_readl(dsi, DSI_RD_DATA); | ||
| 2255 | if (enable_read_debug) | ||
| 2256 | dev_info(&dc->ndev->dev, | ||
| 2257 | "Read data[%d]: 0x%x\n", i, val); | ||
| 2258 | memcpy(read_fifo, &val, 4); | ||
| 2259 | read_fifo += 4; | ||
| 2260 | } | ||
| 2261 | |||
| 2262 | /* Make sure all the data is read from the FIFO */ | ||
| 2263 | val = tegra_dsi_readl(dsi, DSI_STATUS); | ||
| 2264 | val &= DSI_STATUS_RD_FIFO_COUNT(0x1f); | ||
| 2265 | if (val) | ||
| 2266 | dev_err(&dc->ndev->dev, "DSI FIFO_RD_CNT not zero" | ||
| 2267 | " even after reading FIFO_RD_CNT words from read fifo\n"); | ||
| 2268 | |||
| 2269 | if (enable_read_debug) { | ||
| 2270 | err = | ||
| 2271 | tegra_dsi_parse_read_response(dc, rd_fifo_cnt, read_fifo_cp); | ||
| 2272 | if (err < 0) | ||
| 2273 | dev_warn(&dc->ndev->dev, "Unexpected read data\n"); | ||
| 2274 | } | ||
| 2275 | fail: | ||
| 2276 | return err; | ||
| 2277 | } | ||
| 2278 | |||
| 2279 | int tegra_dsi_read_data(struct tegra_dc *dc, | ||
| 2280 | struct tegra_dc_dsi_data *dsi, | ||
| 2281 | u32 max_ret_payload_size, | ||
| 2282 | u32 panel_reg_addr, u8 *read_data) | ||
| 2283 | { | ||
| 2284 | int err = 0; | ||
| 2285 | struct dsi_status *init_status; | ||
| 2286 | |||
| 2287 | tegra_dc_io_start(dc); | ||
| 2288 | |||
| 2289 | init_status = tegra_dsi_prepare_host_transmission( | ||
| 2290 | dc, dsi, DSI_LP_OP_WRITE); | ||
| 2291 | if (IS_ERR_OR_NULL(init_status)) { | ||
| 2292 | err = PTR_ERR(init_status); | ||
| 2293 | dev_err(&dc->ndev->dev, "DSI host config failed\n"); | ||
| 2294 | goto fail; | ||
| 2295 | } | ||
| 2296 | |||
| 2297 | /* Set max return payload size in words */ | ||
| 2298 | err = _tegra_dsi_write_data(dsi, NULL, | ||
| 2299 | dsi_command_max_return_pkt_size, | ||
| 2300 | max_ret_payload_size); | ||
| 2301 | if (err < 0) { | ||
| 2302 | dev_err(&dc->ndev->dev, | ||
| 2303 | "DSI write failed\n"); | ||
| 2304 | goto fail; | ||
| 2305 | } | ||
| 2306 | |||
| 2307 | /* DCS to read given panel register */ | ||
| 2308 | err = _tegra_dsi_write_data(dsi, NULL, | ||
| 2309 | dsi_command_dcs_read_with_no_params, | ||
| 2310 | panel_reg_addr); | ||
| 2311 | if (err < 0) { | ||
| 2312 | dev_err(&dc->ndev->dev, | ||
| 2313 | "DSI write failed\n"); | ||
| 2314 | goto fail; | ||
| 2315 | } | ||
| 2316 | |||
| 2317 | tegra_dsi_reset_read_count(dsi); | ||
| 2318 | |||
| 2319 | if (dsi->status.lp_op == DSI_LP_OP_WRITE) { | ||
| 2320 | err = tegra_dsi_set_to_lp_mode(dc, dsi, DSI_LP_OP_READ); | ||
| 2321 | if (err < 0) { | ||
| 2322 | dev_err(&dc->ndev->dev, | ||
| 2323 | "DSI failed to go to LP read mode\n"); | ||
| 2324 | goto fail; | ||
| 2325 | } | ||
| 2326 | } | ||
| 2327 | |||
| 2328 | err = tegra_dsi_bta(dsi); | ||
| 2329 | if (err < 0) { | ||
| 2330 | dev_err(&dc->ndev->dev, | ||
| 2331 | "DSI IMM BTA timeout\n"); | ||
| 2332 | goto fail; | ||
| 2333 | } | ||
| 2334 | |||
| 2335 | err = tegra_dsi_read_fifo(dc, dsi, read_data); | ||
| 2336 | if (err < 0) { | ||
| 2337 | dev_err(&dc->ndev->dev, "DSI read fifo failure\n"); | ||
| 2338 | goto fail; | ||
| 2339 | } | ||
| 2340 | fail: | ||
| 2341 | err = tegra_dsi_restore_state(dc, dsi, init_status); | ||
| 2342 | if (err < 0) | ||
| 2343 | dev_err(&dc->ndev->dev, "Failed to restore prev state\n"); | ||
| 2344 | tegra_dc_io_end(dc); | ||
| 2345 | return err; | ||
| 2346 | } | ||
| 2347 | EXPORT_SYMBOL(tegra_dsi_read_data); | ||
| 2348 | |||
| 2349 | int tegra_dsi_panel_sanity_check(struct tegra_dc *dc, | ||
| 2350 | struct tegra_dc_dsi_data *dsi) | ||
| 2351 | { | ||
| 2352 | int err = 0; | ||
| 2353 | u8 read_fifo[DSI_READ_FIFO_DEPTH]; | ||
| 2354 | struct dsi_status *init_status; | ||
| 2355 | static struct tegra_dsi_cmd dsi_nop_cmd = | ||
| 2356 | DSI_CMD_SHORT(0x05, 0x0, 0x0); | ||
| 2357 | |||
| 2358 | tegra_dc_io_start(dc); | ||
| 2359 | |||
| 2360 | init_status = tegra_dsi_prepare_host_transmission( | ||
| 2361 | dc, dsi, DSI_LP_OP_WRITE); | ||
| 2362 | if (IS_ERR_OR_NULL(init_status)) { | ||
| 2363 | err = PTR_ERR(init_status); | ||
| 2364 | dev_err(&dc->ndev->dev, "DSI host config failed\n"); | ||
| 2365 | goto fail; | ||
| 2366 | } | ||
| 2367 | |||
| 2368 | err = _tegra_dsi_write_data(dsi, NULL, dsi_nop_cmd.data_id, 0x0); | ||
| 2369 | if (err < 0) { | ||
| 2370 | dev_err(&dc->ndev->dev, "DSI nop write failed\n"); | ||
| 2371 | goto fail; | ||
| 2372 | } | ||
| 2373 | |||
| 2374 | tegra_dsi_reset_read_count(dsi); | ||
| 2375 | |||
| 2376 | if (dsi->status.lp_op == DSI_LP_OP_WRITE) { | ||
| 2377 | err = tegra_dsi_set_to_lp_mode(dc, dsi, DSI_LP_OP_READ); | ||
| 2378 | if (err < 0) { | ||
| 2379 | dev_err(&dc->ndev->dev, | ||
| 2380 | "DSI failed to go to LP read mode\n"); | ||
| 2381 | goto fail; | ||
| 2382 | } | ||
| 2383 | } | ||
| 2384 | |||
| 2385 | err = tegra_dsi_bta(dsi); | ||
| 2386 | if (err < 0) { | ||
| 2387 | dev_err(&dc->ndev->dev, "DSI BTA failed\n"); | ||
| 2388 | goto fail; | ||
| 2389 | } | ||
| 2390 | |||
| 2391 | err = tegra_dsi_read_fifo(dc, dsi, read_fifo); | ||
| 2392 | if (err < 0) { | ||
| 2393 | dev_err(&dc->ndev->dev, "DSI read fifo failure\n"); | ||
| 2394 | goto fail; | ||
| 2395 | } | ||
| 2396 | |||
| 2397 | if (read_fifo[0] != DSI_ACK_NO_ERR) { | ||
| 2398 | dev_warn(&dc->ndev->dev, | ||
| 2399 | "Ack no error trigger message not received\n"); | ||
| 2400 | err = -EAGAIN; | ||
| 2401 | } | ||
| 2402 | fail: | ||
| 2403 | err = tegra_dsi_restore_state(dc, dsi, init_status); | ||
| 2404 | if (err < 0) | ||
| 2405 | dev_err(&dc->ndev->dev, "Failed to restore prev state\n"); | ||
| 2406 | tegra_dc_io_end(dc); | ||
| 2407 | return err; | ||
| 2408 | } | ||
| 2409 | EXPORT_SYMBOL(tegra_dsi_panel_sanity_check); | ||
| 2410 | |||
| 2411 | static int tegra_dsi_enter_ulpm(struct tegra_dc_dsi_data *dsi) | ||
| 2412 | { | ||
| 2413 | u32 val; | ||
| 2414 | int ret; | ||
| 2415 | |||
| 2416 | ret = 0; | ||
| 2417 | |||
| 2418 | val = tegra_dsi_readl(dsi, DSI_HOST_DSI_CONTROL); | ||
| 2419 | val &= ~DSI_HOST_DSI_CONTROL_ULTRA_LOW_POWER(3); | ||
| 2420 | val |= DSI_HOST_DSI_CONTROL_ULTRA_LOW_POWER(ENTER_ULPM); | ||
| 2421 | tegra_dsi_writel(dsi, val, DSI_HOST_DSI_CONTROL); | ||
| 2422 | |||
| 2423 | #if DSI_USE_SYNC_POINTS | ||
| 2424 | ret = tegra_dsi_syncpt(dsi); | ||
| 2425 | if (ret < 0) { | ||
| 2426 | dev_err(&dsi->dc->ndev->dev, | ||
| 2427 | "DSI syncpt for ulpm enter failed\n"); | ||
| 2428 | goto fail; | ||
| 2429 | } | ||
| 2430 | #else | ||
| 2431 | /* TODO: Find exact delay required */ | ||
| 2432 | mdelay(10); | ||
| 2433 | #endif | ||
| 2434 | dsi->ulpm = true; | ||
| 2435 | fail: | ||
| 2436 | return ret; | ||
| 2437 | } | ||
| 2438 | |||
| 2439 | static int tegra_dsi_exit_ulpm(struct tegra_dc_dsi_data *dsi) | ||
| 2440 | { | ||
| 2441 | u32 val; | ||
| 2442 | int ret; | ||
| 2443 | |||
| 2444 | ret = 0; | ||
| 2445 | |||
| 2446 | val = tegra_dsi_readl(dsi, DSI_HOST_DSI_CONTROL); | ||
| 2447 | val &= ~DSI_HOST_DSI_CONTROL_ULTRA_LOW_POWER(3); | ||
| 2448 | val |= DSI_HOST_DSI_CONTROL_ULTRA_LOW_POWER(EXIT_ULPM); | ||
| 2449 | tegra_dsi_writel(dsi, val, DSI_HOST_DSI_CONTROL); | ||
| 2450 | |||
| 2451 | #if DSI_USE_SYNC_POINTS | ||
| 2452 | ret = tegra_dsi_syncpt(dsi); | ||
| 2453 | if (ret < 0) { | ||
| 2454 | dev_err(&dsi->dc->ndev->dev, | ||
| 2455 | "DSI syncpt for ulpm exit failed\n"); | ||
| 2456 | goto fail; | ||
| 2457 | } | ||
| 2458 | #else | ||
| 2459 | /* TODO: Find exact delay required */ | ||
| 2460 | mdelay(10); | ||
| 2461 | #endif | ||
| 2462 | dsi->ulpm = false; | ||
| 2463 | |||
| 2464 | val = tegra_dsi_readl(dsi, DSI_HOST_DSI_CONTROL); | ||
| 2465 | val &= ~DSI_HOST_DSI_CONTROL_ULTRA_LOW_POWER(0x3); | ||
| 2466 | val |= DSI_HOST_DSI_CONTROL_ULTRA_LOW_POWER(NORMAL); | ||
| 2467 | tegra_dsi_writel(dsi, val, DSI_HOST_DSI_CONTROL); | ||
| 2468 | fail: | ||
| 2469 | return ret; | ||
| 2470 | |||
| 2471 | } | ||
| 2472 | |||
| 2473 | static void tegra_dc_dsi_enable(struct tegra_dc *dc) | ||
| 2474 | { | ||
| 2475 | struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); | ||
| 2476 | int err; | ||
| 2477 | u32 val; | ||
| 2478 | |||
| 2479 | tegra_dc_io_start(dc); | ||
| 2480 | mutex_lock(&dsi->lock); | ||
| 2481 | |||
| 2482 | /* Stop DC stream before configuring DSI registers | ||
| 2483 | * to avoid visible glitches on panel during transition | ||
| 2484 | * from bootloader to kernel driver | ||
| 2485 | */ | ||
| 2486 | tegra_dsi_stop_dc_stream(dc, dsi); | ||
| 2487 | |||
| 2488 | if (dsi->enabled) { | ||
| 2489 | if (dsi->ulpm) { | ||
| 2490 | if (tegra_dsi_exit_ulpm(dsi) < 0) { | ||
| 2491 | dev_err(&dc->ndev->dev, | ||
| 2492 | "DSI failed to exit ulpm\n"); | ||
| 2493 | goto fail; | ||
| 2494 | } | ||
| 2495 | } | ||
| 2496 | |||
| 2497 | if (dsi->info.panel_reset) { | ||
| 2498 | err = tegra_dsi_send_panel_cmd(dc, dsi, | ||
| 2499 | dsi->info.dsi_init_cmd, | ||
| 2500 | dsi->info.n_init_cmd); | ||
| 2501 | if (err < 0) { | ||
| 2502 | dev_err(&dc->ndev->dev, | ||
| 2503 | "dsi: error sending dsi init cmd\n"); | ||
| 2504 | goto fail; | ||
| 2505 | } | ||
| 2506 | } else if (dsi->info.dsi_late_resume_cmd) { | ||
| 2507 | err = tegra_dsi_send_panel_cmd(dc, dsi, | ||
| 2508 | dsi->info.dsi_late_resume_cmd, | ||
| 2509 | dsi->info.n_late_resume_cmd); | ||
| 2510 | if (err < 0) { | ||
| 2511 | dev_err(&dc->ndev->dev, | ||
| 2512 | "dsi: error sending late resume cmd\n"); | ||
| 2513 | goto fail; | ||
| 2514 | } | ||
| 2515 | } | ||
| 2516 | } else { | ||
| 2517 | err = tegra_dsi_init_hw(dc, dsi); | ||
| 2518 | if (err < 0) { | ||
| 2519 | dev_err(&dc->ndev->dev, | ||
| 2520 | "dsi: not able to init dsi hardware\n"); | ||
| 2521 | goto fail; | ||
| 2522 | } | ||
| 2523 | |||
| 2524 | if (dsi->ulpm) { | ||
| 2525 | if (tegra_dsi_enter_ulpm(dsi) < 0) { | ||
| 2526 | dev_err(&dc->ndev->dev, | ||
| 2527 | "DSI failed to enter ulpm\n"); | ||
| 2528 | goto fail; | ||
| 2529 | } | ||
| 2530 | |||
| 2531 | val = tegra_dsi_readl(dsi, DSI_PAD_CONTROL); | ||
| 2532 | |||
| 2533 | /* erase bits we're about to set */ | ||
| 2534 | val &= ~(DSI_PAD_CONTROL_PAD_PDIO(0x3) | | ||
| 2535 | DSI_PAD_CONTROL_PAD_PDIO_CLK(0x1) | | ||
| 2536 | DSI_PAD_CONTROL_PAD_PULLDN_ENAB(0x1)); | ||
| 2537 | |||
| 2538 | val |= (DSI_PAD_CONTROL_PAD_PDIO(0) | | ||
| 2539 | DSI_PAD_CONTROL_PAD_PDIO_CLK(0) | | ||
| 2540 | DSI_PAD_CONTROL_PAD_PULLDN_ENAB | ||
| 2541 | (TEGRA_DSI_DISABLE)); | ||
| 2542 | |||
| 2543 | tegra_dsi_writel(dsi, val, DSI_PAD_CONTROL); | ||
| 2544 | if (tegra_dsi_exit_ulpm(dsi) < 0) { | ||
| 2545 | dev_err(&dc->ndev->dev, | ||
| 2546 | "DSI failed to exit ulpm\n"); | ||
| 2547 | goto fail; | ||
| 2548 | } | ||
| 2549 | } | ||
| 2550 | |||
| 2551 | err = tegra_dsi_set_to_lp_mode(dc, dsi, DSI_LP_OP_WRITE); | ||
| 2552 | if (err < 0) { | ||
| 2553 | dev_err(&dc->ndev->dev, | ||
| 2554 | "dsi: not able to set to lp mode\n"); | ||
| 2555 | goto fail; | ||
| 2556 | } | ||
| 2557 | |||
| 2558 | err = tegra_dsi_send_panel_cmd(dc, dsi, dsi->info.dsi_init_cmd, | ||
| 2559 | dsi->info.n_init_cmd); | ||
| 2560 | if (err < 0) { | ||
| 2561 | dev_err(&dc->ndev->dev, | ||
| 2562 | "dsi: error while sending dsi init cmd\n"); | ||
| 2563 | goto fail; | ||
| 2564 | } | ||
| 2565 | |||
| 2566 | err = tegra_dsi_set_to_hs_mode(dc, dsi); | ||
| 2567 | if (err < 0) { | ||
| 2568 | dev_err(&dc->ndev->dev, | ||
| 2569 | "dsi: not able to set to hs mode\n"); | ||
| 2570 | goto fail; | ||
| 2571 | } | ||
| 2572 | |||
| 2573 | dsi->enabled = true; | ||
| 2574 | } | ||
| 2575 | |||
| 2576 | if (dsi->status.driven == DSI_DRIVEN_MODE_DC) | ||
| 2577 | tegra_dsi_start_dc_stream(dc, dsi); | ||
| 2578 | fail: | ||
| 2579 | mutex_unlock(&dsi->lock); | ||
| 2580 | tegra_dc_io_end(dc); | ||
| 2581 | } | ||
| 2582 | |||
| 2583 | static void _tegra_dc_dsi_init(struct tegra_dc *dc) | ||
| 2584 | { | ||
| 2585 | struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); | ||
| 2586 | |||
| 2587 | tegra_dsi_init_sw(dc, dsi); | ||
| 2588 | /* TODO: Configure the CSI pad configuration */ | ||
| 2589 | } | ||
| 2590 | |||
| 2591 | static int tegra_dc_dsi_cp_p_cmd(struct tegra_dsi_cmd *src, | ||
| 2592 | struct tegra_dsi_cmd *dst, u16 n_cmd) | ||
| 2593 | { | ||
| 2594 | u16 i; | ||
| 2595 | u16 len; | ||
| 2596 | |||
| 2597 | memcpy(dst, src, sizeof(*dst) * n_cmd); | ||
| 2598 | |||
| 2599 | for (i = 0; i < n_cmd; i++) | ||
| 2600 | if (src[i].pdata) { | ||
| 2601 | len = sizeof(*src[i].pdata) * | ||
| 2602 | src[i].sp_len_dly.data_len; | ||
| 2603 | dst[i].pdata = kzalloc(len, GFP_KERNEL); | ||
| 2604 | if (!dst[i].pdata) | ||
| 2605 | goto free_cmd_pdata; | ||
| 2606 | memcpy(dst[i].pdata, src[i].pdata, len); | ||
| 2607 | } | ||
| 2608 | |||
| 2609 | return 0; | ||
| 2610 | |||
| 2611 | free_cmd_pdata: | ||
| 2612 | for (--i; i >= 0; i--) | ||
| 2613 | if (dst[i].pdata) | ||
| 2614 | kfree(dst[i].pdata); | ||
| 2615 | return -ENOMEM; | ||
| 2616 | } | ||
| 2617 | |||
| 2618 | static int tegra_dc_dsi_cp_info(struct tegra_dc_dsi_data *dsi, | ||
| 2619 | struct tegra_dsi_out *p_dsi) | ||
| 2620 | { | ||
| 2621 | struct tegra_dsi_cmd *p_init_cmd; | ||
| 2622 | struct tegra_dsi_cmd *p_early_suspend_cmd; | ||
| 2623 | struct tegra_dsi_cmd *p_late_resume_cmd; | ||
| 2624 | struct tegra_dsi_cmd *p_suspend_cmd; | ||
| 2625 | int err; | ||
| 2626 | |||
| 2627 | if (p_dsi->n_data_lanes > MAX_DSI_DATA_LANES) | ||
| 2628 | return -EINVAL; | ||
| 2629 | |||
| 2630 | p_init_cmd = kzalloc(sizeof(*p_init_cmd) * | ||
| 2631 | p_dsi->n_init_cmd, GFP_KERNEL); | ||
| 2632 | if (!p_init_cmd) | ||
| 2633 | return -ENOMEM; | ||
| 2634 | |||
| 2635 | if (p_dsi->dsi_early_suspend_cmd) { | ||
| 2636 | p_early_suspend_cmd = kzalloc(sizeof(*p_early_suspend_cmd) * | ||
| 2637 | p_dsi->n_early_suspend_cmd, | ||
| 2638 | GFP_KERNEL); | ||
| 2639 | if (!p_early_suspend_cmd) { | ||
| 2640 | err = -ENOMEM; | ||
| 2641 | goto err_free_init_cmd; | ||
| 2642 | } | ||
| 2643 | } | ||
| 2644 | |||
| 2645 | if (p_dsi->dsi_late_resume_cmd) { | ||
| 2646 | p_late_resume_cmd = kzalloc(sizeof(*p_late_resume_cmd) * | ||
| 2647 | p_dsi->n_late_resume_cmd, | ||
| 2648 | GFP_KERNEL); | ||
| 2649 | if (!p_late_resume_cmd) { | ||
| 2650 | err = -ENOMEM; | ||
| 2651 | goto err_free_p_early_suspend_cmd; | ||
| 2652 | } | ||
| 2653 | } | ||
| 2654 | |||
| 2655 | p_suspend_cmd = kzalloc(sizeof(*p_suspend_cmd) * p_dsi->n_suspend_cmd, | ||
| 2656 | GFP_KERNEL); | ||
| 2657 | if (!p_suspend_cmd) { | ||
| 2658 | err = -ENOMEM; | ||
| 2659 | goto err_free_p_late_resume_cmd; | ||
| 2660 | } | ||
| 2661 | |||
| 2662 | memcpy(&dsi->info, p_dsi, sizeof(dsi->info)); | ||
| 2663 | |||
| 2664 | /* Copy panel init cmd */ | ||
| 2665 | err = tegra_dc_dsi_cp_p_cmd(p_dsi->dsi_init_cmd, | ||
| 2666 | p_init_cmd, p_dsi->n_init_cmd); | ||
| 2667 | if (err < 0) | ||
| 2668 | goto err_free; | ||
| 2669 | dsi->info.dsi_init_cmd = p_init_cmd; | ||
| 2670 | |||
| 2671 | /* Copy panel early suspend cmd */ | ||
| 2672 | if (p_dsi->dsi_early_suspend_cmd) { | ||
| 2673 | err = tegra_dc_dsi_cp_p_cmd(p_dsi->dsi_early_suspend_cmd, | ||
| 2674 | p_early_suspend_cmd, | ||
| 2675 | p_dsi->n_early_suspend_cmd); | ||
| 2676 | if (err < 0) | ||
| 2677 | goto err_free; | ||
| 2678 | dsi->info.dsi_early_suspend_cmd = p_early_suspend_cmd; | ||
| 2679 | } | ||
| 2680 | |||
| 2681 | /* Copy panel late resume cmd */ | ||
| 2682 | if (p_dsi->dsi_late_resume_cmd) { | ||
| 2683 | err = tegra_dc_dsi_cp_p_cmd(p_dsi->dsi_late_resume_cmd, | ||
| 2684 | p_late_resume_cmd, | ||
| 2685 | p_dsi->n_late_resume_cmd); | ||
| 2686 | if (err < 0) | ||
| 2687 | goto err_free; | ||
| 2688 | dsi->info.dsi_late_resume_cmd = p_late_resume_cmd; | ||
| 2689 | } | ||
| 2690 | |||
| 2691 | /* Copy panel suspend cmd */ | ||
| 2692 | err = tegra_dc_dsi_cp_p_cmd(p_dsi->dsi_suspend_cmd, p_suspend_cmd, | ||
| 2693 | p_dsi->n_suspend_cmd); | ||
| 2694 | if (err < 0) | ||
| 2695 | goto err_free; | ||
| 2696 | dsi->info.dsi_suspend_cmd = p_suspend_cmd; | ||
| 2697 | |||
| 2698 | if (!dsi->info.panel_reset_timeout_msec) | ||
| 2699 | dsi->info.panel_reset_timeout_msec = | ||
| 2700 | DEFAULT_PANEL_RESET_TIMEOUT; | ||
| 2701 | |||
| 2702 | if (!dsi->info.panel_buffer_size_byte) | ||
| 2703 | dsi->info.panel_buffer_size_byte = DEFAULT_PANEL_BUFFER_BYTE; | ||
| 2704 | |||
| 2705 | if (!dsi->info.max_panel_freq_khz) { | ||
| 2706 | dsi->info.max_panel_freq_khz = DEFAULT_MAX_DSI_PHY_CLK_KHZ; | ||
| 2707 | |||
| 2708 | if (dsi->info.video_burst_mode > | ||
| 2709 | TEGRA_DSI_VIDEO_NONE_BURST_MODE_WITH_SYNC_END){ | ||
| 2710 | dev_err(&dsi->dc->ndev->dev, "DSI: max_panel_freq_khz" | ||
| 2711 | "is not set for DSI burst mode.\n"); | ||
| 2712 | dsi->info.video_burst_mode = | ||
| 2713 | TEGRA_DSI_VIDEO_BURST_MODE_LOWEST_SPEED; | ||
| 2714 | } | ||
| 2715 | } | ||
| 2716 | |||
| 2717 | if (!dsi->info.lp_cmd_mode_freq_khz) | ||
| 2718 | dsi->info.lp_cmd_mode_freq_khz = DEFAULT_LP_CMD_MODE_CLK_KHZ; | ||
| 2719 | |||
| 2720 | if (!dsi->info.chip_id || !dsi->info.chip_rev) | ||
| 2721 | dev_warn(&dsi->dc->ndev->dev, | ||
| 2722 | "DSI: Failed to get chip info\n"); | ||
| 2723 | |||
| 2724 | if (!dsi->info.lp_read_cmd_mode_freq_khz) | ||
| 2725 | dsi->info.lp_read_cmd_mode_freq_khz = | ||
| 2726 | dsi->info.lp_cmd_mode_freq_khz; | ||
| 2727 | |||
| 2728 | /* host mode is for testing only */ | ||
| 2729 | dsi->driven_mode = TEGRA_DSI_DRIVEN_BY_DC; | ||
| 2730 | return 0; | ||
| 2731 | |||
| 2732 | err_free: | ||
| 2733 | kfree(p_suspend_cmd); | ||
| 2734 | err_free_p_late_resume_cmd: | ||
| 2735 | kfree(p_late_resume_cmd); | ||
| 2736 | err_free_p_early_suspend_cmd: | ||
| 2737 | kfree(p_early_suspend_cmd); | ||
| 2738 | err_free_init_cmd: | ||
| 2739 | kfree(p_init_cmd); | ||
| 2740 | return err; | ||
| 2741 | } | ||
| 2742 | |||
| 2743 | static int tegra_dc_dsi_init(struct tegra_dc *dc) | ||
| 2744 | { | ||
| 2745 | struct tegra_dc_dsi_data *dsi; | ||
| 2746 | struct resource *res; | ||
| 2747 | struct resource *base_res; | ||
| 2748 | void __iomem *base; | ||
| 2749 | struct clk *dc_clk = NULL; | ||
| 2750 | struct clk *dsi_clk = NULL; | ||
| 2751 | struct tegra_dsi_out *dsi_pdata; | ||
| 2752 | int err; | ||
| 2753 | |||
| 2754 | err = 0; | ||
| 2755 | |||
| 2756 | dsi = kzalloc(sizeof(*dsi), GFP_KERNEL); | ||
| 2757 | if (!dsi) | ||
| 2758 | return -ENOMEM; | ||
| 2759 | |||
| 2760 | res = nvhost_get_resource_byname(dc->ndev, IORESOURCE_MEM, | ||
| 2761 | "dsi_regs"); | ||
| 2762 | if (!res) { | ||
| 2763 | dev_err(&dc->ndev->dev, "dsi: no mem resource\n"); | ||
| 2764 | err = -ENOENT; | ||
| 2765 | goto err_free_dsi; | ||
| 2766 | } | ||
| 2767 | |||
| 2768 | base_res = request_mem_region(res->start, resource_size(res), | ||
| 2769 | dc->ndev->name); | ||
| 2770 | if (!base_res) { | ||
| 2771 | dev_err(&dc->ndev->dev, "dsi: request_mem_region failed\n"); | ||
| 2772 | err = -EBUSY; | ||
| 2773 | goto err_free_dsi; | ||
| 2774 | } | ||
| 2775 | |||
| 2776 | base = ioremap(res->start, resource_size(res)); | ||
| 2777 | if (!base) { | ||
| 2778 | dev_err(&dc->ndev->dev, "dsi: registers can't be mapped\n"); | ||
| 2779 | err = -EBUSY; | ||
| 2780 | goto err_release_regs; | ||
| 2781 | } | ||
| 2782 | |||
| 2783 | dsi_pdata = dc->pdata->default_out->dsi; | ||
| 2784 | if (!dsi_pdata) { | ||
| 2785 | dev_err(&dc->ndev->dev, "dsi: dsi data not available\n"); | ||
| 2786 | goto err_release_regs; | ||
| 2787 | } | ||
| 2788 | |||
| 2789 | if (dsi_pdata->dsi_instance) | ||
| 2790 | dsi_clk = clk_get(&dc->ndev->dev, "dsib"); | ||
| 2791 | else | ||
| 2792 | dsi_clk = clk_get(&dc->ndev->dev, "dsia"); | ||
| 2793 | |||
| 2794 | if (IS_ERR_OR_NULL(dsi_clk)) { | ||
| 2795 | dev_err(&dc->ndev->dev, "dsi: can't get clock\n"); | ||
| 2796 | err = -EBUSY; | ||
| 2797 | goto err_release_regs; | ||
| 2798 | } | ||
| 2799 | |||
| 2800 | dc_clk = clk_get_sys(dev_name(&dc->ndev->dev), NULL); | ||
| 2801 | if (IS_ERR_OR_NULL(dc_clk)) { | ||
| 2802 | dev_err(&dc->ndev->dev, "dsi: dc clock %s unavailable\n", | ||
| 2803 | dev_name(&dc->ndev->dev)); | ||
| 2804 | err = -EBUSY; | ||
| 2805 | goto err_clk_put; | ||
| 2806 | } | ||
| 2807 | |||
| 2808 | mutex_init(&dsi->lock); | ||
| 2809 | dsi->dc = dc; | ||
| 2810 | dsi->base = base; | ||
| 2811 | dsi->base_res = base_res; | ||
| 2812 | dsi->dc_clk = dc_clk; | ||
| 2813 | dsi->dsi_clk = dsi_clk; | ||
| 2814 | |||
| 2815 | err = tegra_dc_dsi_cp_info(dsi, dsi_pdata); | ||
| 2816 | if (err < 0) | ||
| 2817 | goto err_dsi_data; | ||
| 2818 | |||
| 2819 | tegra_dc_set_outdata(dc, dsi); | ||
| 2820 | _tegra_dc_dsi_init(dc); | ||
| 2821 | |||
| 2822 | return 0; | ||
| 2823 | |||
| 2824 | err_dsi_data: | ||
| 2825 | err_clk_put: | ||
| 2826 | clk_put(dsi_clk); | ||
| 2827 | err_release_regs: | ||
| 2828 | release_resource(base_res); | ||
| 2829 | err_free_dsi: | ||
| 2830 | kfree(dsi); | ||
| 2831 | |||
| 2832 | return err; | ||
| 2833 | } | ||
| 2834 | |||
| 2835 | static void tegra_dc_dsi_destroy(struct tegra_dc *dc) | ||
| 2836 | { | ||
| 2837 | struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); | ||
| 2838 | u16 i; | ||
| 2839 | u32 val; | ||
| 2840 | |||
| 2841 | mutex_lock(&dsi->lock); | ||
| 2842 | |||
| 2843 | /* free up the pdata */ | ||
| 2844 | for (i = 0; i < dsi->info.n_init_cmd; i++) { | ||
| 2845 | if (dsi->info.dsi_init_cmd[i].pdata) | ||
| 2846 | kfree(dsi->info.dsi_init_cmd[i].pdata); | ||
| 2847 | } | ||
| 2848 | kfree(dsi->info.dsi_init_cmd); | ||
| 2849 | |||
| 2850 | /* Disable dc stream */ | ||
| 2851 | if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE) | ||
| 2852 | tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi); | ||
| 2853 | |||
| 2854 | /* Disable dsi phy clock */ | ||
| 2855 | if (dsi->status.clk_out == DSI_PHYCLK_OUT_EN) | ||
| 2856 | tegra_dsi_hs_clk_out_disable(dc, dsi); | ||
| 2857 | |||
| 2858 | val = DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_DISABLE); | ||
| 2859 | tegra_dsi_writel(dsi, val, DSI_POWER_CONTROL); | ||
| 2860 | |||
| 2861 | iounmap(dsi->base); | ||
| 2862 | release_resource(dsi->base_res); | ||
| 2863 | |||
| 2864 | clk_put(dsi->dc_clk); | ||
| 2865 | clk_put(dsi->dsi_clk); | ||
| 2866 | |||
| 2867 | mutex_unlock(&dsi->lock); | ||
| 2868 | |||
| 2869 | mutex_destroy(&dsi->lock); | ||
| 2870 | kfree(dsi); | ||
| 2871 | } | ||
| 2872 | |||
| 2873 | static int tegra_dsi_deep_sleep(struct tegra_dc *dc, | ||
| 2874 | struct tegra_dc_dsi_data *dsi) | ||
| 2875 | { | ||
| 2876 | int err = 0; | ||
| 2877 | int val; | ||
| 2878 | struct clk *parent_clk = NULL; | ||
| 2879 | struct clk *base_clk = NULL; | ||
| 2880 | |||
| 2881 | if (!dsi->enabled) { | ||
| 2882 | err = -EPERM; | ||
| 2883 | goto fail; | ||
| 2884 | } | ||
| 2885 | |||
| 2886 | err = tegra_dsi_set_to_lp_mode(dc, dsi, DSI_LP_OP_WRITE); | ||
| 2887 | if (err < 0) { | ||
| 2888 | dev_err(&dc->ndev->dev, | ||
| 2889 | "DSI failed to go to LP mode\n"); | ||
| 2890 | goto fail; | ||
| 2891 | } | ||
| 2892 | |||
| 2893 | /* Suspend panel */ | ||
| 2894 | err = tegra_dsi_send_panel_cmd(dc, dsi, | ||
| 2895 | dsi->info.dsi_suspend_cmd, | ||
| 2896 | dsi->info.n_suspend_cmd); | ||
| 2897 | if (err < 0) { | ||
| 2898 | dev_err(&dc->ndev->dev, | ||
| 2899 | "dsi: Error sending suspend cmd\n"); | ||
| 2900 | goto fail; | ||
| 2901 | } | ||
| 2902 | |||
| 2903 | if (!dsi->ulpm) { | ||
| 2904 | err = tegra_dsi_enter_ulpm(dsi); | ||
| 2905 | if (err < 0) { | ||
| 2906 | dev_err(&dc->ndev->dev, | ||
| 2907 | "DSI failed to enter ulpm\n"); | ||
| 2908 | goto fail; | ||
| 2909 | } | ||
| 2910 | } | ||
| 2911 | |||
| 2912 | /* | ||
| 2913 | * Suspend pad | ||
| 2914 | * It is ok to overwrite previous value of DSI_PAD_CONTROL reg | ||
| 2915 | * because it will be restored properly in resume sequence | ||
| 2916 | */ | ||
| 2917 | val = DSI_PAD_CONTROL_PAD_PDIO(0x3) | | ||
| 2918 | DSI_PAD_CONTROL_PAD_PDIO_CLK(0x1) | | ||
| 2919 | DSI_PAD_CONTROL_PAD_PULLDN_ENAB(TEGRA_DSI_ENABLE); | ||
| 2920 | tegra_dsi_writel(dsi, val, DSI_PAD_CONTROL); | ||
| 2921 | |||
| 2922 | /* Suspend core-logic */ | ||
| 2923 | val = DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_DISABLE); | ||
| 2924 | tegra_dsi_writel(dsi, val, DSI_POWER_CONTROL); | ||
| 2925 | |||
| 2926 | /* Disable dsi fast and slow clock */ | ||
| 2927 | parent_clk = clk_get_parent(dsi->dsi_clk); | ||
| 2928 | base_clk = clk_get_parent(parent_clk); | ||
| 2929 | if (dsi->info.dsi_instance) | ||
| 2930 | tegra_clk_cfg_ex(base_clk, | ||
| 2931 | TEGRA_CLK_PLLD_CSI_OUT_ENB, | ||
| 2932 | 0); | ||
| 2933 | else | ||
| 2934 | tegra_clk_cfg_ex(base_clk, | ||
| 2935 | TEGRA_CLK_PLLD_DSI_OUT_ENB, | ||
| 2936 | 0); | ||
| 2937 | |||
| 2938 | /* Disable dsi source clock */ | ||
| 2939 | clk_disable(dsi->dsi_clk); | ||
| 2940 | |||
| 2941 | dsi->clk_ref = false; | ||
| 2942 | dsi->enabled = false; | ||
| 2943 | |||
| 2944 | return 0; | ||
| 2945 | fail: | ||
| 2946 | return err; | ||
| 2947 | } | ||
| 2948 | |||
| 2949 | static void tegra_dc_dsi_disable(struct tegra_dc *dc) | ||
| 2950 | { | ||
| 2951 | int err; | ||
| 2952 | struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); | ||
| 2953 | |||
| 2954 | tegra_dc_io_start(dc); | ||
| 2955 | mutex_lock(&dsi->lock); | ||
| 2956 | |||
| 2957 | if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE) | ||
| 2958 | tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi); | ||
| 2959 | |||
| 2960 | if (dsi->info.power_saving_suspend) { | ||
| 2961 | if (tegra_dsi_deep_sleep(dc, dsi) < 0) { | ||
| 2962 | dev_err(&dc->ndev->dev, | ||
| 2963 | "DSI failed to enter deep sleep\n"); | ||
| 2964 | goto fail; | ||
| 2965 | } | ||
| 2966 | } else { | ||
| 2967 | if (dsi->info.dsi_early_suspend_cmd) { | ||
| 2968 | err = tegra_dsi_send_panel_cmd(dc, dsi, | ||
| 2969 | dsi->info.dsi_early_suspend_cmd, | ||
| 2970 | dsi->info.n_early_suspend_cmd); | ||
| 2971 | if (err < 0) { | ||
| 2972 | dev_err(&dc->ndev->dev, | ||
| 2973 | "dsi: Error sending early suspend cmd\n"); | ||
| 2974 | goto fail; | ||
| 2975 | } | ||
| 2976 | } | ||
| 2977 | |||
| 2978 | if (!dsi->ulpm) { | ||
| 2979 | if (tegra_dsi_enter_ulpm(dsi) < 0) { | ||
| 2980 | dev_err(&dc->ndev->dev, | ||
| 2981 | "DSI failed to enter ulpm\n"); | ||
| 2982 | goto fail; | ||
| 2983 | } | ||
| 2984 | } | ||
| 2985 | } | ||
| 2986 | |||
| 2987 | fail: | ||
| 2988 | mutex_unlock(&dsi->lock); | ||
| 2989 | tegra_dc_io_end(dc); | ||
| 2990 | } | ||
| 2991 | |||
| 2992 | #ifdef CONFIG_PM | ||
| 2993 | static void tegra_dc_dsi_suspend(struct tegra_dc *dc) | ||
| 2994 | { | ||
| 2995 | struct tegra_dc_dsi_data *dsi; | ||
| 2996 | |||
| 2997 | dsi = tegra_dc_get_outdata(dc); | ||
| 2998 | |||
| 2999 | if (!dsi->enabled) | ||
| 3000 | return; | ||
| 3001 | |||
| 3002 | tegra_dc_io_start(dc); | ||
| 3003 | mutex_lock(&dsi->lock); | ||
| 3004 | |||
| 3005 | if (!dsi->info.power_saving_suspend) { | ||
| 3006 | if (dsi->ulpm) { | ||
| 3007 | if (tegra_dsi_exit_ulpm(dsi) < 0) { | ||
| 3008 | dev_err(&dc->ndev->dev, | ||
| 3009 | "DSI failed to exit ulpm"); | ||
| 3010 | goto fail; | ||
| 3011 | } | ||
| 3012 | } | ||
| 3013 | |||
| 3014 | if (tegra_dsi_deep_sleep(dc, dsi) < 0) { | ||
| 3015 | dev_err(&dc->ndev->dev, | ||
| 3016 | "DSI failed to enter deep sleep\n"); | ||
| 3017 | goto fail; | ||
| 3018 | } | ||
| 3019 | } | ||
| 3020 | fail: | ||
| 3021 | mutex_unlock(&dsi->lock); | ||
| 3022 | tegra_dc_io_end(dc); | ||
| 3023 | } | ||
| 3024 | |||
| 3025 | static void tegra_dc_dsi_resume(struct tegra_dc *dc) | ||
| 3026 | { | ||
| 3027 | /* Not required since tegra_dc_dsi_enable | ||
| 3028 | * will reconfigure the controller from scratch | ||
| 3029 | */ | ||
| 3030 | } | ||
| 3031 | #endif | ||
| 3032 | |||
| 3033 | struct tegra_dc_out_ops tegra_dc_dsi_ops = { | ||
| 3034 | .init = tegra_dc_dsi_init, | ||
| 3035 | .destroy = tegra_dc_dsi_destroy, | ||
| 3036 | .enable = tegra_dc_dsi_enable, | ||
| 3037 | .disable = tegra_dc_dsi_disable, | ||
| 3038 | #ifdef CONFIG_PM | ||
| 3039 | .suspend = tegra_dc_dsi_suspend, | ||
| 3040 | .resume = tegra_dc_dsi_resume, | ||
| 3041 | #endif | ||
| 3042 | }; | ||
diff --git a/drivers/video/tegra/dc/dsi.h b/drivers/video/tegra/dc/dsi.h new file mode 100644 index 00000000000..18ea9c959e8 --- /dev/null +++ b/drivers/video/tegra/dc/dsi.h | |||
| @@ -0,0 +1,375 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/dsi.h | ||
| 3 | * | ||
| 4 | * Copyright (c) 2011, 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 | #ifndef __DRIVERS_VIDEO_TEGRA_DC_DSI_H__ | ||
| 18 | #define __DRIVERS_VIDEO_TEGRA_DC_DSI_H__ | ||
| 19 | |||
| 20 | /* source of video data */ | ||
| 21 | enum { | ||
| 22 | TEGRA_DSI_VIDEO_DRIVEN_BY_DC, | ||
| 23 | TEGRA_DSI_VIDEO_DRIVEN_BY_HOST, | ||
| 24 | }; | ||
| 25 | |||
| 26 | /* Max number of data lanes supported */ | ||
| 27 | #define MAX_DSI_DATA_LANES 2 | ||
| 28 | /* Default Peripheral reset timeout */ | ||
| 29 | #define DSI_PR_TO_VALUE 0x2000 | ||
| 30 | |||
| 31 | /* DCS commands for command mode */ | ||
| 32 | #define DSI_ENTER_PARTIAL_MODE 0x12 | ||
| 33 | #define DSI_SET_PIXEL_FORMAT 0x3A | ||
| 34 | #define DSI_AREA_COLOR_MODE 0x4C | ||
| 35 | #define DSI_SET_PARTIAL_AREA 0x30 | ||
| 36 | #define DSI_SET_PAGE_ADDRESS 0x2B | ||
| 37 | #define DSI_SET_ADDRESS_MODE 0x36 | ||
| 38 | #define DSI_SET_COLUMN_ADDRESS 0x2A | ||
| 39 | #define DSI_WRITE_MEMORY_START 0x2C | ||
| 40 | #define DSI_WRITE_MEMORY_CONTINUE 0x3C | ||
| 41 | #define DSI_MAX_COMMAND_DELAY_USEC 250000 | ||
| 42 | #define DSI_COMMAND_DELAY_STEPS_USEC 10 | ||
| 43 | |||
| 44 | /* Trigger message */ | ||
| 45 | #define DSI_ESCAPE_CMD 0x87 | ||
| 46 | #define DSI_ACK_NO_ERR 0x84 | ||
| 47 | |||
| 48 | /* DSI return packet types */ | ||
| 49 | #define GEN_LONG_RD_RES 0x1A | ||
| 50 | #define DCS_LONG_RD_RES 0x1C | ||
| 51 | #define GEN_1_BYTE_SHORT_RD_RES 0x11 | ||
| 52 | #define DCS_1_BYTE_SHORT_RD_RES 0x21 | ||
| 53 | #define GEN_2_BYTE_SHORT_RD_RES 0x12 | ||
| 54 | #define DCS_2_BYTE_SHORT_RD_RES 0x22 | ||
| 55 | #define ACK_ERR_RES 0x02 | ||
| 56 | |||
| 57 | /* End of Transmit command for HS mode */ | ||
| 58 | #define DSI_CMD_HS_EOT_PACKAGE 0x000F0F08 | ||
| 59 | |||
| 60 | /* Delay required after issuing the trigger*/ | ||
| 61 | #define DSI_COMMAND_COMPLETION_DELAY_USEC 5 | ||
| 62 | |||
| 63 | #define DSI_DELAY_FOR_READ_FIFO 5 | ||
| 64 | |||
| 65 | /* Dsi virtual channel bit position, refer to the DSI specs */ | ||
| 66 | #define DSI_VIR_CHANNEL_BIT_POSITION 6 | ||
| 67 | |||
| 68 | /* DSI packet commands from Host to peripherals */ | ||
| 69 | enum { | ||
| 70 | dsi_command_v_sync_start = 0x01, | ||
| 71 | dsi_command_v_sync_end = 0x11, | ||
| 72 | dsi_command_h_sync_start = 0x21, | ||
| 73 | dsi_command_h_sync_end = 0x31, | ||
| 74 | dsi_command_end_of_transaction = 0x08, | ||
| 75 | dsi_command_blanking = 0x19, | ||
| 76 | dsi_command_null_packet = 0x09, | ||
| 77 | dsi_command_h_active_length_16bpp = 0x0E, | ||
| 78 | dsi_command_h_active_length_18bpp = 0x1E, | ||
| 79 | dsi_command_h_active_length_18bpp_np = 0x2E, | ||
| 80 | dsi_command_h_active_length_24bpp = 0x3E, | ||
| 81 | dsi_command_h_sync_active = dsi_command_blanking, | ||
| 82 | dsi_command_h_back_porch = dsi_command_blanking, | ||
| 83 | dsi_command_h_front_porch = dsi_command_blanking, | ||
| 84 | dsi_command_writ_no_param = 0x05, | ||
| 85 | dsi_command_long_write = 0x39, | ||
| 86 | dsi_command_max_return_pkt_size = 0x37, | ||
| 87 | dsi_command_generic_read_request_with_2_param = 0x24, | ||
| 88 | dsi_command_dcs_read_with_no_params = 0x06, | ||
| 89 | }; | ||
| 90 | |||
| 91 | /* Maximum polling time for reading the dsi status register */ | ||
| 92 | #define DSI_STATUS_POLLING_DURATION_USEC 100000 | ||
| 93 | #define DSI_STATUS_POLLING_DELAY_USEC 100 | ||
| 94 | |||
| 95 | /* | ||
| 96 | * Horizontal Sync Blank Packet Over head | ||
| 97 | * DSI_overhead = size_of(HS packet header) | ||
| 98 | * + size_of(BLANK packet header) + size_of(checksum) | ||
| 99 | * DSI_overhead = 4 + 4 + 2 = 10 | ||
| 100 | */ | ||
| 101 | #define DSI_HSYNC_BLNK_PKT_OVERHEAD 10 | ||
| 102 | |||
| 103 | /* | ||
| 104 | * Horizontal Front Porch Packet Overhead | ||
| 105 | * DSI_overhead = size_of(checksum) | ||
| 106 | * + size_of(BLANK packet header) + size_of(checksum) | ||
| 107 | * DSI_overhead = 2 + 4 + 2 = 8 | ||
| 108 | */ | ||
| 109 | #define DSI_HFRONT_PORCH_PKT_OVERHEAD 8 | ||
| 110 | |||
| 111 | /* | ||
| 112 | * Horizontal Back Porch Packet | ||
| 113 | * DSI_overhead = size_of(HE packet header) | ||
| 114 | * + size_of(BLANK packet header) + size_of(checksum) | ||
| 115 | * + size_of(RGB packet header) | ||
| 116 | * DSI_overhead = 4 + 4 + 2 + 4 = 14 | ||
| 117 | */ | ||
| 118 | #define DSI_HBACK_PORCH_PKT_OVERHEAD 14 | ||
| 119 | |||
| 120 | /* Additional Hs TX timeout margin */ | ||
| 121 | #define DSI_HTX_TO_MARGIN 720 | ||
| 122 | |||
| 123 | #define DSI_CYCLE_COUNTER_VALUE 512 | ||
| 124 | |||
| 125 | #define DSI_LRXH_TO_VALUE 0x2000 | ||
| 126 | |||
| 127 | /* Turn around timeout terminal count */ | ||
| 128 | #define DSI_TA_TO_VALUE 0x2000 | ||
| 129 | |||
| 130 | /* Turn around timeout tally */ | ||
| 131 | #define DSI_TA_TALLY_VALUE 0x0 | ||
| 132 | /* LP Rx timeout tally */ | ||
| 133 | #define DSI_LRXH_TALLY_VALUE 0x0 | ||
| 134 | /* HS Tx Timeout tally */ | ||
| 135 | #define DSI_HTX_TALLY_VALUE 0x0 | ||
| 136 | |||
| 137 | /* DSI Power control settle time 10 micro seconds */ | ||
| 138 | #define DSI_POWER_CONTROL_SETTLE_TIME_US 10 | ||
| 139 | |||
| 140 | #define DSI_HOST_FIFO_DEPTH 64 | ||
| 141 | #define DSI_VIDEO_FIFO_DEPTH 480 | ||
| 142 | #define DSI_READ_FIFO_DEPTH (32 << 2) | ||
| 143 | |||
| 144 | #define NUMOF_BIT_PER_BYTE 8 | ||
| 145 | #define DEFAULT_LP_CMD_MODE_CLK_KHZ 10000 | ||
| 146 | #define DEFAULT_MAX_DSI_PHY_CLK_KHZ (500*1000) | ||
| 147 | #define DEFAULT_PANEL_RESET_TIMEOUT 2 | ||
| 148 | #define DEFAULT_PANEL_BUFFER_BYTE 512 | ||
| 149 | |||
| 150 | /* | ||
| 151 | * TODO: are DSI_HOST_DSI_CONTROL_CRC_RESET(RESET_CRC) and | ||
| 152 | * DSI_HOST_DSI_CONTROL_HOST_TX_TRIG_SRC(IMMEDIATE) required for everyone? | ||
| 153 | */ | ||
| 154 | #define HOST_DSI_CTRL_COMMON \ | ||
| 155 | (DSI_HOST_DSI_CONTROL_PHY_CLK_DIV(DSI_PHY_CLK_DIV1) | \ | ||
| 156 | DSI_HOST_DSI_CONTROL_ULTRA_LOW_POWER(NORMAL) | \ | ||
| 157 | DSI_HOST_DSI_CONTROL_PERIPH_RESET(TEGRA_DSI_DISABLE) | \ | ||
| 158 | DSI_HOST_DSI_CONTROL_RAW_DATA(TEGRA_DSI_DISABLE) | \ | ||
| 159 | DSI_HOST_DSI_CONTROL_IMM_BTA(TEGRA_DSI_DISABLE) | \ | ||
| 160 | DSI_HOST_DSI_CONTROL_PKT_BTA(TEGRA_DSI_DISABLE) | \ | ||
| 161 | DSI_HOST_DSI_CONTROL_CS_ENABLE(TEGRA_DSI_ENABLE) | \ | ||
| 162 | DSI_HOST_DSI_CONTROL_ECC_ENABLE(TEGRA_DSI_ENABLE) | \ | ||
| 163 | DSI_HOST_DSI_CONTROL_PKT_WR_FIFO_SEL(HOST_ONLY)) | ||
| 164 | |||
| 165 | #define HOST_DSI_CTRL_HOST_DRIVEN \ | ||
| 166 | (DSI_HOST_DSI_CONTROL_CRC_RESET(RESET_CRC) | \ | ||
| 167 | DSI_HOST_DSI_CONTROL_HOST_TX_TRIG_SRC(IMMEDIATE)) | ||
| 168 | |||
| 169 | #define HOST_DSI_CTRL_DC_DRIVEN 0 | ||
| 170 | |||
| 171 | #define DSI_CTRL_HOST_DRIVEN (DSI_CONTROL_VID_ENABLE(TEGRA_DSI_DISABLE) | \ | ||
| 172 | DSI_CONTROL_HOST_ENABLE(TEGRA_DSI_ENABLE)) | ||
| 173 | |||
| 174 | #define DSI_CTRL_DC_DRIVEN (DSI_CONTROL_VID_TX_TRIG_SRC(SOL) | \ | ||
| 175 | DSI_CONTROL_VID_ENABLE(TEGRA_DSI_ENABLE) | \ | ||
| 176 | DSI_CONTROL_HOST_ENABLE(TEGRA_DSI_DISABLE)) | ||
| 177 | |||
| 178 | #define DSI_CTRL_CMD_MODE (DSI_CONTROL_VID_DCS_ENABLE(TEGRA_DSI_ENABLE)) | ||
| 179 | |||
| 180 | #define DSI_CTRL_VIDEO_MODE (DSI_CONTROL_VID_DCS_ENABLE(TEGRA_DSI_DISABLE)) | ||
| 181 | |||
| 182 | |||
| 183 | enum { | ||
| 184 | CMD_VS = 0x01, | ||
| 185 | CMD_VE = 0x11, | ||
| 186 | |||
| 187 | CMD_HS = 0x21, | ||
| 188 | CMD_HE = 0x31, | ||
| 189 | |||
| 190 | CMD_EOT = 0x08, | ||
| 191 | CMD_NULL = 0x09, | ||
| 192 | CMD_SHORTW = 0x15, | ||
| 193 | CMD_BLNK = 0x19, | ||
| 194 | CMD_LONGW = 0x39, | ||
| 195 | |||
| 196 | CMD_RGB = 0x00, | ||
| 197 | CMD_RGB_16BPP = 0x0E, | ||
| 198 | CMD_RGB_18BPP = 0x1E, | ||
| 199 | CMD_RGB_18BPPNP = 0x2E, | ||
| 200 | CMD_RGB_24BPP = 0x3E, | ||
| 201 | }; | ||
| 202 | |||
| 203 | #define PKT_ID0(id) (DSI_PKT_SEQ_0_LO_PKT_00_ID(id) | \ | ||
| 204 | DSI_PKT_SEQ_1_LO_PKT_10_EN(TEGRA_DSI_ENABLE)) | ||
| 205 | #define PKT_LEN0(len) (DSI_PKT_SEQ_0_LO_PKT_00_SIZE(len)) | ||
| 206 | |||
| 207 | #define PKT_ID1(id) (DSI_PKT_SEQ_0_LO_PKT_01_ID(id) | \ | ||
| 208 | DSI_PKT_SEQ_1_LO_PKT_11_EN(TEGRA_DSI_ENABLE)) | ||
| 209 | #define PKT_LEN1(len) (DSI_PKT_SEQ_0_LO_PKT_01_SIZE(len)) | ||
| 210 | |||
| 211 | #define PKT_ID2(id) (DSI_PKT_SEQ_0_LO_PKT_02_ID(id) | \ | ||
| 212 | DSI_PKT_SEQ_1_LO_PKT_12_EN(TEGRA_DSI_ENABLE)) | ||
| 213 | #define PKT_LEN2(len) (DSI_PKT_SEQ_0_LO_PKT_02_SIZE(len)) | ||
| 214 | |||
| 215 | #define PKT_ID3(id) (DSI_PKT_SEQ_0_HI_PKT_03_ID(id) | \ | ||
| 216 | DSI_PKT_SEQ_1_HI_PKT_13_EN(TEGRA_DSI_ENABLE)) | ||
| 217 | #define PKT_LEN3(len) (DSI_PKT_SEQ_0_HI_PKT_03_SIZE(len)) | ||
| 218 | |||
| 219 | #define PKT_ID4(id) (DSI_PKT_SEQ_0_HI_PKT_04_ID(id) | \ | ||
| 220 | DSI_PKT_SEQ_1_HI_PKT_14_EN(TEGRA_DSI_ENABLE)) | ||
| 221 | #define PKT_LEN4(len) (DSI_PKT_SEQ_0_HI_PKT_04_SIZE(len)) | ||
| 222 | |||
| 223 | #define PKT_ID5(id) (DSI_PKT_SEQ_0_HI_PKT_05_ID(id) | \ | ||
| 224 | DSI_PKT_SEQ_1_HI_PKT_15_EN(TEGRA_DSI_ENABLE)) | ||
| 225 | #define PKT_LEN5(len) (DSI_PKT_SEQ_0_HI_PKT_05_SIZE(len)) | ||
| 226 | |||
| 227 | #define PKT_LP (DSI_PKT_SEQ_0_LO_SEQ_0_FORCE_LP(TEGRA_DSI_ENABLE)) | ||
| 228 | |||
| 229 | #define NUMOF_PKT_SEQ 12 | ||
| 230 | |||
| 231 | /* Mipi v1.00.00 phy timing range */ | ||
| 232 | #define NOT_DEFINED -1 | ||
| 233 | #define MIPI_T_HSEXIT_NS_MIN 100 | ||
| 234 | #define MIPI_T_HSEXIT_NS_MAX NOT_DEFINED | ||
| 235 | #define MIPI_T_HSTRAIL_NS_MIN(clk_ns) max((8 * (clk_ns)), (60 + 4 * (clk_ns))) | ||
| 236 | #define MIPI_T_HSTRAIL_NS_MAX NOT_DEFINED | ||
| 237 | #define MIPI_T_HSZERO_NS_MIN NOT_DEFINED | ||
| 238 | #define MIPI_T_HSZERO_NS_MAX NOT_DEFINED | ||
| 239 | #define MIPI_T_HSPREPARE_NS_MIN(clk_ns) (40 + 4 * (clk_ns)) | ||
| 240 | #define MIPI_T_HSPREPARE_NS_MAX(clk_ns) (85 + 6 * (clk_ns)) | ||
| 241 | #define MIPI_T_CLKTRAIL_NS_MIN 60 | ||
| 242 | #define MIPI_T_CLKTRAIL_NS_MAX NOT_DEFINED | ||
| 243 | #define MIPI_T_CLKPOST_NS_MIN(clk_ns) (60 + 52 * (clk_ns)) | ||
| 244 | #define MIPI_T_CLKPOST_NS_MAX NOT_DEFINED | ||
| 245 | #define MIPI_T_CLKZERO_NS_MIN NOT_DEFINED | ||
| 246 | #define MIPI_T_CLKZERO_NS_MAX NOT_DEFINED | ||
| 247 | #define MIPI_T_TLPX_NS_MIN 50 | ||
| 248 | #define MIPI_T_TLPX_NS_MAX NOT_DEFINED | ||
| 249 | #define MIPI_T_CLKPREPARE_NS_MIN 38 | ||
| 250 | #define MIPI_T_CLKPREPARE_NS_MAX 95 | ||
| 251 | #define MIPI_T_CLKPRE_NS_MIN 8 | ||
| 252 | #define MIPI_T_CLKPRE_NS_MAX NOT_DEFINED | ||
| 253 | #define MIPI_T_WAKEUP_NS_MIN 1 | ||
| 254 | #define MIPI_T_WAKEUP_NS_MAX NOT_DEFINED | ||
| 255 | #define MIPI_T_TASURE_NS_MIN(tlpx_ns) (tlpx_ns) | ||
| 256 | #define MIPI_T_TASURE_NS_MAX(tlpx_ns) (2 * (tlpx_ns)) | ||
| 257 | #define MIPI_T_HSPREPARE_ADD_HSZERO_NS_MIN(clk_ns) (145 + 10 * (clk_ns)) | ||
| 258 | #define MIPI_T_HSPREPARE_ADD_HSZERO_NS_MAX NOT_DEFINED | ||
| 259 | #define MIPI_T_CLKPREPARE_ADD_CLKZERO_NS_MIN 300 | ||
| 260 | #define MIPI_T_CLKPREPARE_ADD_CLKZERO_NS_MAX NOT_DEFINED | ||
| 261 | |||
| 262 | #define DSI_TBYTE(clk_ns) ((clk_ns) * (BITS_PER_BYTE)) | ||
| 263 | #define DSI_CONVERT_T_PHY_NS_TO_T_PHY(t_phy_ns, clk_ns, hw_inc) \ | ||
| 264 | ((int)((DIV_ROUND_CLOSEST((t_phy_ns), \ | ||
| 265 | (DSI_TBYTE(clk_ns)))) - (hw_inc))) | ||
| 266 | |||
| 267 | #define DSI_CONVERT_T_PHY_TO_T_PHY_NS(t_phy, clk_ns, hw_inc) \ | ||
| 268 | (((t_phy) + (hw_inc)) * (DSI_TBYTE(clk_ns))) | ||
| 269 | |||
| 270 | /* Default phy timing in ns */ | ||
| 271 | #define T_HSEXIT_NS_DEFAULT 120 | ||
| 272 | #define T_HSTRAIL_NS_DEFAULT(clk_ns) \ | ||
| 273 | max((8 * (clk_ns)), (60 + 4 * (clk_ns))) | ||
| 274 | |||
| 275 | #define T_DATZERO_NS_DEFAULT(clk_ns) (145 + 5 * (clk_ns)) | ||
| 276 | #define T_HSPREPARE_NS_DEFAULT(clk_ns) (65 + 5 * (clk_ns)) | ||
| 277 | #define T_CLKTRAIL_NS_DEFAULT 80 | ||
| 278 | #define T_CLKPOST_NS_DEFAULT(clk_ns) (70 + 52 * (clk_ns)) | ||
| 279 | #define T_CLKZERO_NS_DEFAULT 260 | ||
| 280 | #define T_TLPX_NS_DEFAULT 60 | ||
| 281 | #define T_CLKPREPARE_NS_DEFAULT 65 | ||
| 282 | #define T_TAGO_NS_DEFAULT (4 * (T_TLPX_NS_DEFAULT)) | ||
| 283 | #define T_TASURE_NS_DEFAULT (2 * (T_TLPX_NS_DEFAULT)) | ||
| 284 | #define T_TAGET_NS_DEFAULT (5 * (T_TLPX_NS_DEFAULT)) | ||
| 285 | |||
| 286 | /* HW increment to phy register values */ | ||
| 287 | #define T_HSEXIT_HW_INC 1 | ||
| 288 | #define T_HSTRAIL_HW_INC 0 | ||
| 289 | #define T_DATZERO_HW_INC 3 | ||
| 290 | #define T_HSPREPARE_HW_INC 1 | ||
| 291 | #define T_CLKTRAIL_HW_INC 1 | ||
| 292 | #define T_CLKPOST_HW_INC 1 | ||
| 293 | #define T_CLKZERO_HW_INC 1 | ||
| 294 | #define T_TLPX_HW_INC 1 | ||
| 295 | #define T_CLKPREPARE_HW_INC 1 | ||
| 296 | #define T_TAGO_HW_INC 1 | ||
| 297 | #define T_TASURE_HW_INC 1 | ||
| 298 | #define T_TAGET_HW_INC 1 | ||
| 299 | #define T_CLKPRE_HW_INC 1 | ||
| 300 | #define T_WAKEUP_HW_INC 1 | ||
| 301 | |||
| 302 | /* Default phy timing reg values */ | ||
| 303 | #define T_HSEXIT_DEFAULT(clk_ns) \ | ||
| 304 | (DSI_CONVERT_T_PHY_NS_TO_T_PHY( \ | ||
| 305 | T_HSEXIT_NS_DEFAULT, clk_ns, T_HSEXIT_HW_INC)) | ||
| 306 | |||
| 307 | #define T_HSTRAIL_DEFAULT(clk_ns) \ | ||
| 308 | (3 + (DSI_CONVERT_T_PHY_NS_TO_T_PHY( \ | ||
| 309 | T_HSTRAIL_NS_DEFAULT(clk_ns), clk_ns, T_HSTRAIL_HW_INC))) | ||
| 310 | |||
| 311 | #define T_DATZERO_DEFAULT(clk_ns) \ | ||
| 312 | (DSI_CONVERT_T_PHY_NS_TO_T_PHY( \ | ||
| 313 | T_DATZERO_NS_DEFAULT(clk_ns), clk_ns, T_DATZERO_HW_INC)) | ||
| 314 | |||
| 315 | #define T_HSPREPARE_DEFAULT(clk_ns) \ | ||
| 316 | (DSI_CONVERT_T_PHY_NS_TO_T_PHY( \ | ||
| 317 | T_HSPREPARE_NS_DEFAULT(clk_ns), clk_ns, T_HSPREPARE_HW_INC)) | ||
| 318 | |||
| 319 | #define T_CLKTRAIL_DEFAULT(clk_ns) \ | ||
| 320 | (DSI_CONVERT_T_PHY_NS_TO_T_PHY( \ | ||
| 321 | T_CLKTRAIL_NS_DEFAULT, clk_ns, T_CLKTRAIL_HW_INC)) | ||
| 322 | |||
| 323 | #define T_CLKPOST_DEFAULT(clk_ns) \ | ||
| 324 | (DSI_CONVERT_T_PHY_NS_TO_T_PHY( \ | ||
| 325 | T_CLKPOST_NS_DEFAULT(clk_ns), clk_ns, T_CLKPOST_HW_INC)) | ||
| 326 | |||
| 327 | #define T_CLKZERO_DEFAULT(clk_ns) \ | ||
| 328 | (DSI_CONVERT_T_PHY_NS_TO_T_PHY( \ | ||
| 329 | T_CLKZERO_NS_DEFAULT, clk_ns, T_CLKZERO_HW_INC)) | ||
| 330 | |||
| 331 | #define T_TLPX_DEFAULT(clk_ns) \ | ||
| 332 | (DSI_CONVERT_T_PHY_NS_TO_T_PHY( \ | ||
| 333 | T_TLPX_NS_DEFAULT, clk_ns, T_TLPX_HW_INC)) | ||
| 334 | |||
| 335 | #define T_CLKPREPARE_DEFAULT(clk_ns) \ | ||
| 336 | (DSI_CONVERT_T_PHY_NS_TO_T_PHY( \ | ||
| 337 | T_CLKPREPARE_NS_DEFAULT, clk_ns, T_CLKPREPARE_HW_INC)) | ||
| 338 | |||
| 339 | #define T_CLKPRE_DEFAULT 0x1 | ||
| 340 | #define T_WAKEUP_DEFAULT 0x7f | ||
| 341 | |||
| 342 | #define T_TAGO_DEFAULT(clk_ns) \ | ||
| 343 | (DSI_CONVERT_T_PHY_NS_TO_T_PHY( \ | ||
| 344 | T_TAGO_NS_DEFAULT, clk_ns, T_TAGO_HW_INC)) | ||
| 345 | |||
| 346 | #define T_TASURE_DEFAULT(clk_ns) \ | ||
| 347 | (DSI_CONVERT_T_PHY_NS_TO_T_PHY( \ | ||
| 348 | T_TASURE_NS_DEFAULT, clk_ns, T_TASURE_HW_INC)) | ||
| 349 | |||
| 350 | #define T_TAGET_DEFAULT(clk_ns) \ | ||
| 351 | (DSI_CONVERT_T_PHY_NS_TO_T_PHY( \ | ||
| 352 | T_TAGET_NS_DEFAULT, clk_ns, T_TAGET_HW_INC)) | ||
| 353 | |||
| 354 | /* Defines the DSI phy timing parameters */ | ||
| 355 | struct dsi_phy_timing_inclk { | ||
| 356 | unsigned t_hsdexit; | ||
| 357 | unsigned t_hstrail; | ||
| 358 | unsigned t_hsprepare; | ||
| 359 | unsigned t_datzero; | ||
| 360 | |||
| 361 | unsigned t_clktrail; | ||
| 362 | unsigned t_clkpost; | ||
| 363 | unsigned t_clkzero; | ||
| 364 | unsigned t_tlpx; | ||
| 365 | |||
| 366 | unsigned t_clkpre; | ||
| 367 | unsigned t_clkprepare; | ||
| 368 | unsigned t_wakeup; | ||
| 369 | |||
| 370 | unsigned t_taget; | ||
| 371 | unsigned t_tasure; | ||
| 372 | unsigned t_tago; | ||
| 373 | }; | ||
| 374 | |||
| 375 | #endif | ||
diff --git a/drivers/video/tegra/dc/dsi_regs.h b/drivers/video/tegra/dc/dsi_regs.h new file mode 100644 index 00000000000..203ac32bd92 --- /dev/null +++ b/drivers/video/tegra/dc/dsi_regs.h | |||
| @@ -0,0 +1,351 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/dsi_regs.h | ||
| 3 | * | ||
| 4 | * Copyright (c) 2011, 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 | #ifndef __DRIVERS_VIDEO_TEGRA_DC_DSI_REG_H__ | ||
| 18 | #define __DRIVERS_VIDEO_TEGRA_DC_DSI_REG_H__ | ||
| 19 | |||
| 20 | enum { | ||
| 21 | TEGRA_DSI_DISABLE, | ||
| 22 | TEGRA_DSI_ENABLE, | ||
| 23 | }; | ||
| 24 | |||
| 25 | /* These are word offsets from base (not byte offsets) */ | ||
| 26 | enum { | ||
| 27 | OP_DONE = 1, | ||
| 28 | }; | ||
| 29 | #define DSI_INCR_SYNCPT 0x00 | ||
| 30 | #define DSI_INCR_SYNCPT_COND(x) (((x) & 0xff) << 8) | ||
| 31 | #define DSI_INCR_SYNCPT_INDX(x) (((x) & 0xff) << 0) | ||
| 32 | |||
| 33 | #define DSI_INCR_SYNCPT_CNTRL 0x01 | ||
| 34 | #define DSI_INCR_SYNCPT_ERROR 0x02 | ||
| 35 | #define DSI_CTXSW 0x08 | ||
| 36 | #define DSI_RD_DATA 0x09 | ||
| 37 | #define DSI_WR_DATA 0x0a | ||
| 38 | |||
| 39 | #define DSI_POWER_CONTROL 0x0b | ||
| 40 | #define DSI_POWER_CONTROL_LEG_DSI_ENABLE(x) (((x) & 0x1) << 0) | ||
| 41 | |||
| 42 | #define DSI_INT_ENABLE 0x0c | ||
| 43 | #define DSI_INT_STATUS 0x0d | ||
| 44 | #define DSI_INT_MASK 0x0e | ||
| 45 | |||
| 46 | #define DSI_HOST_DSI_CONTROL 0x0f | ||
| 47 | enum { | ||
| 48 | RESET_CRC = 1, | ||
| 49 | }; | ||
| 50 | #define DSI_HOST_CONTROL_FIFO_STAT_RESET(x) (((x) & 0x1) << 21) | ||
| 51 | #define DSI_HOST_DSI_CONTROL_CRC_RESET(x) (((x) & 0x1) << 20) | ||
| 52 | enum { | ||
| 53 | DSI_PHY_CLK_DIV1, | ||
| 54 | DSI_PHY_CLK_DIV2, | ||
| 55 | }; | ||
| 56 | #define DSI_HOST_DSI_CONTROL_PHY_CLK_DIV(x) (((x) & 0x7) << 16) | ||
| 57 | enum { | ||
| 58 | SOL, | ||
| 59 | FIFO_LEVEL, | ||
| 60 | IMMEDIATE, | ||
| 61 | }; | ||
| 62 | #define DSI_HOST_DSI_CONTROL_HOST_TX_TRIG_SRC(x) (((x) & 0x3) << 12) | ||
| 63 | enum { | ||
| 64 | NORMAL, | ||
| 65 | ENTER_ULPM, | ||
| 66 | EXIT_ULPM, | ||
| 67 | }; | ||
| 68 | #define DSI_HOST_DSI_CONTROL_ULTRA_LOW_POWER(x) (((x) & 0x3) << 8) | ||
| 69 | #define DSI_HOST_DSI_CONTROL_PERIPH_RESET(x) (((x) & 0x1) << 7) | ||
| 70 | #define DSI_HOST_DSI_CONTROL_RAW_DATA(x) (((x) & 0x1) << 6) | ||
| 71 | enum { | ||
| 72 | TEGRA_DSI_LOW, | ||
| 73 | TEGRA_DSI_HIGH, | ||
| 74 | }; | ||
| 75 | #define DSI_HOST_DSI_CONTROL_HIGH_SPEED_TRANS(x) (((x) & 0x1) << 5) | ||
| 76 | enum { | ||
| 77 | HOST_ONLY, | ||
| 78 | VIDEO_HOST, | ||
| 79 | }; | ||
| 80 | #define DSI_HOST_DSI_CONTROL_PKT_WR_FIFO_SEL(x) (((x) & 0x1) << 4) | ||
| 81 | #define DSI_HOST_DSI_CONTROL_IMM_BTA(x) (((x) & 0x1) << 3) | ||
| 82 | #define DSI_HOST_DSI_CONTROL_PKT_BTA(x) (((x) & 0x1) << 2) | ||
| 83 | #define DSI_HOST_DSI_CONTROL_CS_ENABLE(x) (((x) & 0x1) << 1) | ||
| 84 | #define DSI_HOST_DSI_CONTROL_ECC_ENABLE(x) (((x) & 0x1) << 0) | ||
| 85 | |||
| 86 | #define DSI_CONTROL 0x10 | ||
| 87 | #define DSI_CONTROL_DBG_ENABLE(x) (((x) & 0x1) << 31) | ||
| 88 | enum { | ||
| 89 | CONTINUOUS, | ||
| 90 | TX_ONLY, | ||
| 91 | }; | ||
| 92 | #define DSI_CONTROL_HS_CLK_CTRL(x) (((x) & 0x1) << 20) | ||
| 93 | #define DSI_CONTROL_VIRTUAL_CHANNEL(x) (((x) & 0x3) << 16) | ||
| 94 | #define DSI_CONTROL_DATA_FORMAT(x) (((x) & 0x3) << 12) | ||
| 95 | #define DSI_CONTROL_VID_TX_TRIG_SRC(x) (((x) & 0x3) << 8) | ||
| 96 | #define DSI_CONTROL_NUM_DATA_LANES(x) (((x) & 0x3) << 4) | ||
| 97 | #define DSI_CONTROL_VID_DCS_ENABLE(x) (((x) & 0x1) << 3) | ||
| 98 | #define DSI_CONTROL_VID_SOURCE(x) (((x) & 0x1) << 2) | ||
| 99 | #define DSI_CONTROL_VID_ENABLE(x) (((x) & 0x1) << 1) | ||
| 100 | #define DSI_CONTROL_HOST_ENABLE(x) (((x) & 0x1) << 0) | ||
| 101 | |||
| 102 | #define DSI_SOL_DELAY 0x11 | ||
| 103 | #define DSI_SOL_DELAY_SOL_DELAY(x) (((x) & 0xffff) << 0) | ||
| 104 | |||
| 105 | #define DSI_MAX_THRESHOLD 0x12 | ||
| 106 | #define DSI_MAX_THRESHOLD_MAX_THRESHOLD(x) (((x) & 0xffff) << 0) | ||
| 107 | |||
| 108 | #define DSI_TRIGGER 0x13 | ||
| 109 | #define DSI_TRIGGER_HOST_TRIGGER(x) (((x) & 0x1) << 1) | ||
| 110 | #define DSI_TRIGGER_VID_TRIGGER(x) (((x) & 0x1) << 0) | ||
| 111 | |||
| 112 | #define DSI_TX_CRC 0x14 | ||
| 113 | #define DSI_TX_CRC_TX_CRC(x) (((x) & 0xffffffff) << 0) | ||
| 114 | |||
| 115 | #define DSI_STATUS 0x15 | ||
| 116 | #define DSI_STATUS_IDLE(x) (((x) & 0x1) << 10) | ||
| 117 | #define DSI_STATUS_LB_UNDERFLOW(x) (((x) & 0x1) << 9) | ||
| 118 | #define DSI_STATUS_LB_OVERFLOW(x) (((x) & 0x1) << 8) | ||
| 119 | #define DSI_STATUS_RD_FIFO_COUNT(x) (((x) & 0x1f) << 0) | ||
| 120 | |||
| 121 | #define DSI_INIT_SEQ_CONTROL 0x1a | ||
| 122 | #define DSI_INIT_SEQ_CONTROL_DSI_FRAME_INIT_BYTE_COUNT(x) \ | ||
| 123 | (((x) & 0x3f) << 8) | ||
| 124 | #define DSI_INIT_SEQ_CONTROL_DSI_SEND_INIT_SEQUENCE(x) \ | ||
| 125 | (((x) & 0xff) << 0) | ||
| 126 | |||
| 127 | #define DSI_INIT_SEQ_DATA_0 0x1b | ||
| 128 | #define DSI_INIT_SEQ_DATA_1 0x1c | ||
| 129 | #define DSI_INIT_SEQ_DATA_2 0x1d | ||
| 130 | #define DSI_INIT_SEQ_DATA_3 0x1e | ||
| 131 | #define DSI_INIT_SEQ_DATA_4 0x1f | ||
| 132 | #define DSI_INIT_SEQ_DATA_5 0x20 | ||
| 133 | #define DSI_INIT_SEQ_DATA_6 0x21 | ||
| 134 | #define DSI_INIT_SEQ_DATA_7 0x22 | ||
| 135 | |||
| 136 | #define DSI_PKT_SEQ_0_LO 0x23 | ||
| 137 | #define DSI_PKT_SEQ_0_LO_SEQ_0_FORCE_LP(x) (((x) & 0x1) << 30) | ||
| 138 | #define DSI_PKT_SEQ_0_LO_PKT_02_EN(x) (((x) & 0x1) << 29) | ||
| 139 | #define DSI_PKT_SEQ_0_LO_PKT_02_ID(x) (((x) & 0x3f) << 23) | ||
| 140 | #define DSI_PKT_SEQ_0_LO_PKT_02_SIZE(x) (((x) & 0x7) << 20) | ||
| 141 | #define DSI_PKT_SEQ_0_LO_PKT_01_EN(x) (((x) & 0x1) << 19) | ||
| 142 | #define DSI_PKT_SEQ_0_LO_PKT_01_ID(x) (((x) & 0x3f) << 13) | ||
| 143 | #define DSI_PKT_SEQ_0_LO_PKT_01_SIZE(x) (((x) & 0x7) << 10) | ||
| 144 | #define DSI_PKT_SEQ_0_LO_PKT_00_EN(x) (((x) & 0x1) << 9) | ||
| 145 | #define DSI_PKT_SEQ_0_LO_PKT_00_ID(x) (((x) & 0x3f) << 3) | ||
| 146 | #define DSI_PKT_SEQ_0_LO_PKT_00_SIZE(x) (((x) & 0x7) << 0) | ||
| 147 | |||
| 148 | #define DSI_PKT_SEQ_0_HI 0x24 | ||
| 149 | #define DSI_PKT_SEQ_0_HI_PKT_05_EN(x) (((x) & 0x1) << 29) | ||
| 150 | #define DSI_PKT_SEQ_0_HI_PKT_05_ID(x) (((x) & 0x3f) << 23) | ||
| 151 | #define DSI_PKT_SEQ_0_HI_PKT_05_SIZE(x) (((x) & 0x7) << 20) | ||
| 152 | #define DSI_PKT_SEQ_0_HI_PKT_04_EN(x) (((x) & 0x1) << 19) | ||
| 153 | #define DSI_PKT_SEQ_0_HI_PKT_04_ID(x) (((x) & 0x3f) << 13) | ||
| 154 | #define DSI_PKT_SEQ_0_HI_PKT_04_SIZE(x) (((x) & 0x7) << 10) | ||
| 155 | #define DSI_PKT_SEQ_0_HI_PKT_03_EN(x) (((x) & 0x1) << 9) | ||
| 156 | #define DSI_PKT_SEQ_0_HI_PKT_03_ID(x) (((x) & 0x3f) << 3) | ||
| 157 | #define DSI_PKT_SEQ_0_HI_PKT_03_SIZE(x) (((x) & 0x7) << 0) | ||
| 158 | |||
| 159 | #define DSI_PKT_SEQ_1_LO 0x25 | ||
| 160 | #define DSI_PKT_SEQ_1_LO_SEQ_1_FORCE_LP(x) (((x) & 0x1) << 30) | ||
| 161 | #define DSI_PKT_SEQ_1_LO_PKT_12_EN(x) (((x) & 0x1) << 29) | ||
| 162 | #define DSI_PKT_SEQ_1_LO_PKT_12_ID(x) (((x) & 0x3f) << 23) | ||
| 163 | #define DSI_PKT_SEQ_1_LO_PKT_12_SIZE(x) (((x) & 0x7) << 20) | ||
| 164 | #define DSI_PKT_SEQ_1_LO_PKT_11_EN(x) (((x) & 0x1) << 19) | ||
| 165 | #define DSI_PKT_SEQ_1_LO_PKT_11_ID(x) (((x) & 0x3f) << 13) | ||
| 166 | #define DSI_PKT_SEQ_1_LO_PKT_11_SIZE(x) (((x) & 0x7) << 10) | ||
| 167 | #define DSI_PKT_SEQ_1_LO_PKT_10_EN(x) (((x) & 0x1) << 9) | ||
| 168 | #define DSI_PKT_SEQ_1_LO_PKT_10_ID(x) (((x) & 0x3f) << 3) | ||
| 169 | #define DSI_PKT_SEQ_1_LO_PKT_10_SIZE(x) (((x) & 0x7) << 0) | ||
| 170 | |||
| 171 | #define DSI_PKT_SEQ_1_HI 0x26 | ||
| 172 | #define DSI_PKT_SEQ_1_HI_PKT_15_EN(x) (((x) & 0x1) << 29) | ||
| 173 | #define DSI_PKT_SEQ_1_HI_PKT_15_ID(x) (((x) & 0x3f) << 23) | ||
| 174 | #define DSI_PKT_SEQ_1_HI_PKT_15_SIZE(x) (((x) & 0x7) << 20) | ||
| 175 | #define DSI_PKT_SEQ_1_HI_PKT_14_EN(x) (((x) & 0x1) << 19) | ||
| 176 | #define DSI_PKT_SEQ_1_HI_PKT_14_ID(x) (((x) & 0x3f) << 13) | ||
| 177 | #define DSI_PKT_SEQ_1_HI_PKT_14_SIZE(x) (((x) & 0x7) << 10) | ||
| 178 | #define DSI_PKT_SEQ_1_HI_PKT_13_EN(x) (((x) & 0x1) << 9) | ||
| 179 | #define DSI_PKT_SEQ_1_HI_PKT_13_ID(x) (((x) & 0x3f) << 3) | ||
| 180 | #define DSI_PKT_SEQ_1_HI_PKT_13_SIZE(x) (((x) & 0x7) << 0) | ||
| 181 | |||
| 182 | #define DSI_PKT_SEQ_2_LO 0x27 | ||
| 183 | #define DSI_PKT_SEQ_2_LO_SEQ_2_FORCE_LP(x) (((x) & 0x1) << 30) | ||
| 184 | #define DSI_PKT_SEQ_2_LO_PKT_22_EN(x) (((x) & 0x1) << 29) | ||
| 185 | #define DSI_PKT_SEQ_2_LO_PKT_22_ID(x) (((x) & 0x3f) << 23) | ||
| 186 | #define DSI_PKT_SEQ_2_LO_PKT_22_SIZE(x) (((x) & 0x7) << 20) | ||
| 187 | #define DSI_PKT_SEQ_2_LO_PKT_21_EN(x) (((x) & 0x1) << 19) | ||
| 188 | #define DSI_PKT_SEQ_2_LO_PKT_21_ID(x) (((x) & 0x3f) << 13) | ||
| 189 | #define DSI_PKT_SEQ_2_LO_PKT_21_SIZE(x) (((x) & 0x7) << 10) | ||
| 190 | #define DSI_PKT_SEQ_2_LO_PKT_20_EN(x) (((x) & 0x1) << 9) | ||
| 191 | #define DSI_PKT_SEQ_2_LO_PKT_20_ID(x) (((x) & 0x3f) << 3) | ||
| 192 | #define DSI_PKT_SEQ_2_LO_PKT_20_SIZE(x) (((x) & 0x7) << 0) | ||
| 193 | |||
| 194 | #define DSI_PKT_SEQ_2_HI 0x28 | ||
| 195 | #define DSI_PKT_SEQ_2_HI_PKT_25_EN(x) (((x) & 0x1) << 29) | ||
| 196 | #define DSI_PKT_SEQ_2_HI_PKT_25_ID(x) (((x) & 0x3f) << 23) | ||
| 197 | #define DSI_PKT_SEQ_2_HI_PKT_25_SIZE(x) (((x) & 0x7) << 20) | ||
| 198 | #define DSI_PKT_SEQ_2_HI_PKT_24_EN(x) (((x) & 0x1) << 19) | ||
| 199 | #define DSI_PKT_SEQ_2_HI_PKT_24_ID(x) (((x) & 0x3f) << 13) | ||
| 200 | #define DSI_PKT_SEQ_2_HI_PKT_24_SIZE(x) (((x) & 0x7) << 10) | ||
| 201 | #define DSI_PKT_SEQ_2_HI_PKT_23_EN(x) (((x) & 0x1) << 9) | ||
| 202 | #define DSI_PKT_SEQ_2_HI_PKT_23_ID(x) (((x) & 0x3f) << 3) | ||
| 203 | #define DSI_PKT_SEQ_2_HI_PKT_23_SIZE(x) (((x) & 0x7) << 0) | ||
| 204 | |||
| 205 | #define DSI_PKT_SEQ_3_LO 0x29 | ||
| 206 | #define DSI_PKT_SEQ_3_LO_SEQ_3_FORCE_LP(x) (((x) & 0x1) << 30) | ||
| 207 | #define DSI_PKT_SEQ_3_LO_PKT_32_EN(x) (((x) & 0x1) << 29) | ||
| 208 | #define DSI_PKT_SEQ_3_LO_PKT_32_ID(x) (((x) & 0x3f) << 23) | ||
| 209 | #define DSI_PKT_SEQ_3_LO_PKT_32_SIZE(x) (((x) & 0x7) << 20) | ||
| 210 | #define DSI_PKT_SEQ_3_LO_PKT_31_EN(x) (((x) & 0x1) << 19) | ||
| 211 | #define DSI_PKT_SEQ_3_LO_PKT_31_ID(x) (((x) & 0x3f) << 13) | ||
| 212 | #define DSI_PKT_SEQ_3_LO_PKT_31_SIZE(x) (((x) & 0x7) << 10) | ||
| 213 | #define DSI_PKT_SEQ_3_LO_PKT_30_EN(x) (((x) & 0x1) << 9) | ||
| 214 | #define DSI_PKT_SEQ_3_LO_PKT_30_ID(x) (((x) & 0x3f) << 3) | ||
| 215 | #define DSI_PKT_SEQ_3_LO_PKT_30_SIZE(x) (((x) & 0x7) << 0) | ||
| 216 | |||
| 217 | #define DSI_PKT_SEQ_3_HI 0x2a | ||
| 218 | #define DSI_PKT_SEQ_3_HI_PKT_35_EN(x) (((x) & 0x1) << 29) | ||
| 219 | #define DSI_PKT_SEQ_3_HI_PKT_35_ID(x) (((x) & 0x3f) << 23) | ||
| 220 | #define DSI_PKT_SEQ_3_HI_PKT_35_SIZE(x) (((x) & 0x7) << 20) | ||
| 221 | #define DSI_PKT_SEQ_3_HI_PKT_34_EN(x) (((x) & 0x1) << 19) | ||
| 222 | #define DSI_PKT_SEQ_3_HI_PKT_34_ID(x) (((x) & 0x3f) << 13) | ||
| 223 | #define DSI_PKT_SEQ_3_HI_PKT_34_SIZE(x) (((x) & 0x7) << 10) | ||
| 224 | #define DSI_PKT_SEQ_3_HI_PKT_33_EN(x) (((x) & 0x1) << 9) | ||
| 225 | #define DSI_PKT_SEQ_3_HI_PKT_33_ID(x) (((x) & 0x3f) << 3) | ||
| 226 | #define DSI_PKT_SEQ_3_HI_PKT_33_SIZE(x) (((x) & 0x7) << 0) | ||
| 227 | |||
| 228 | #define DSI_PKT_SEQ_4_LO 0x2b | ||
| 229 | #define DSI_PKT_SEQ_4_LO_SEQ_4_FORCE_LP(x) (((x) & 0x1) << 30) | ||
| 230 | #define DSI_PKT_SEQ_4_LO_PKT_42_EN(x) (((x) & 0x1) << 29) | ||
| 231 | #define DSI_PKT_SEQ_4_LO_PKT_42_ID(x) (((x) & 0x3f) << 23) | ||
| 232 | #define DSI_PKT_SEQ_4_LO_PKT_42_SIZE(x) (((x) & 0x7) << 20) | ||
| 233 | #define DSI_PKT_SEQ_4_LO_PKT_41_EN(x) (((x) & 0x1) << 19) | ||
| 234 | #define DSI_PKT_SEQ_4_LO_PKT_41_ID(x) (((x) & 0x3f) << 13) | ||
| 235 | #define DSI_PKT_SEQ_4_LO_PKT_41_SIZE(x) (((x) & 0x7) << 10) | ||
| 236 | #define DSI_PKT_SEQ_4_LO_PKT_40_EN(x) (((x) & 0x1) << 9) | ||
| 237 | #define DSI_PKT_SEQ_4_LO_PKT_40_ID(x) (((x) & 0x3f) << 3) | ||
| 238 | #define DSI_PKT_SEQ_4_LO_PKT_40_SIZE(x) (((x) & 0x7) << 0) | ||
| 239 | |||
| 240 | #define DSI_PKT_SEQ_4_HI 0x2c | ||
| 241 | #define DSI_PKT_SEQ_4_HI_PKT_45_EN(x) (((x) & 0x1) << 29) | ||
| 242 | #define DSI_PKT_SEQ_4_HI_PKT_45_ID(x) (((x) & 0x3f) << 23) | ||
| 243 | #define DSI_PKT_SEQ_4_HI_PKT_45_SIZE(x) (((x) & 0x7) << 20) | ||
| 244 | #define DSI_PKT_SEQ_4_HI_PKT_44_EN(x) (((x) & 0x1) << 19) | ||
| 245 | #define DSI_PKT_SEQ_4_HI_PKT_44_ID(x) (((x) & 0x3f) << 13) | ||
| 246 | #define DSI_PKT_SEQ_4_HI_PKT_44_SIZE(x) (((x) & 0x7) << 10) | ||
| 247 | #define DSI_PKT_SEQ_4_HI_PKT_43_EN(x) (((x) & 0x1) << 9) | ||
| 248 | #define DSI_PKT_SEQ_4_HI_PKT_43_ID(x) (((x) & 0x3f) << 3) | ||
| 249 | #define DSI_PKT_SEQ_4_HI_PKT_43_SIZE(x) (((x) & 0x7) << 0) | ||
| 250 | |||
| 251 | #define DSI_PKT_SEQ_5_LO 0x2d | ||
| 252 | #define DSI_PKT_SEQ_5_LO_SEQ_5_FORCE_LP(x) (((x) & 0x1) << 30) | ||
| 253 | #define DSI_PKT_SEQ_5_LO_PKT_52_EN(x) (((x) & 0x1) << 29) | ||
| 254 | #define DSI_PKT_SEQ_5_LO_PKT_52_ID(x) (((x) & 0x3f) << 23) | ||
| 255 | #define DSI_PKT_SEQ_5_LO_PKT_52_SIZE(x) (((x) & 0x7) << 20) | ||
| 256 | #define DSI_PKT_SEQ_5_LO_PKT_51_EN(x) (((x) & 0x1) << 19) | ||
| 257 | #define DSI_PKT_SEQ_5_LO_PKT_51_ID(x) (((x) & 0x3f) << 13) | ||
| 258 | #define DSI_PKT_SEQ_5_LO_PKT_51_SIZE(x) (((x) & 0x7) << 10) | ||
| 259 | #define DSI_PKT_SEQ_5_LO_PKT_50_EN(x) (((x) & 0x1) << 9) | ||
| 260 | #define DSI_PKT_SEQ_5_LO_PKT_50_ID(x) (((x) & 0x3f) << 3) | ||
| 261 | #define DSI_PKT_SEQ_5_LO_PKT_50_SIZE(x) (((x) & 0x7) << 0) | ||
| 262 | |||
| 263 | #define DSI_PKT_SEQ_5_HI 0x2e | ||
| 264 | #define DSI_PKT_SEQ_5_HI_PKT_55_EN(x) (((x) & 0x1) << 29) | ||
| 265 | #define DSI_PKT_SEQ_5_HI_PKT_55_ID(x) (((x) & 0x3f) << 23) | ||
| 266 | #define DSI_PKT_SEQ_5_HI_PKT_55_SIZE(x) (((x) & 0x7) << 20) | ||
| 267 | #define DSI_PKT_SEQ_5_HI_PKT_54_EN(x) (((x) & 0x1) << 19) | ||
| 268 | #define DSI_PKT_SEQ_5_HI_PKT_54_ID(x) (((x) & 0x3f) << 13) | ||
| 269 | #define DSI_PKT_SEQ_5_HI_PKT_54_SIZE(x) (((x) & 0x7) << 10) | ||
| 270 | #define DSI_PKT_SEQ_5_HI_PKT_53_EN(x) (((x) & 0x1) << 9) | ||
| 271 | #define DSI_PKT_SEQ_5_HI_PKT_53_ID(x) (((x) & 0x3f) << 3) | ||
| 272 | #define DSI_PKT_SEQ_5_HI_PKT_53_SIZE(x) (((x) & 0x7) << 0) | ||
| 273 | |||
| 274 | #define DSI_DCS_CMDS 0x33 | ||
| 275 | #define DSI_DCS_CMDS_LT5_DCS_CMD(x) (((x) & 0xff) << 8) | ||
| 276 | #define DSI_DCS_CMDS_LT3_DCS_CMD(x) (((x) & 0xff) << 0) | ||
| 277 | |||
| 278 | #define DSI_PKT_LEN_0_1 0x34 | ||
| 279 | #define DSI_PKT_LEN_0_1_LENGTH_1(x) (((x) & 0xffff) << 16) | ||
| 280 | #define DSI_PKT_LEN_0_1_LENGTH_0(x) (((x) & 0xffff) << 0) | ||
| 281 | |||
| 282 | #define DSI_PKT_LEN_2_3 0x35 | ||
| 283 | #define DSI_PKT_LEN_2_3_LENGTH_3(x) (((x) & 0xffff) << 16) | ||
| 284 | #define DSI_PKT_LEN_2_3_LENGTH_2(x) (((x) & 0xffff) << 0) | ||
| 285 | |||
| 286 | |||
| 287 | #define DSI_PKT_LEN_4_5 0x36 | ||
| 288 | #define DSI_PKT_LEN_4_5_LENGTH_5(x) (((x) & 0xffff) << 16) | ||
| 289 | #define DSI_PKT_LEN_4_5_LENGTH_4(x) (((x) & 0xffff) << 0) | ||
| 290 | |||
| 291 | #define DSI_PKT_LEN_6_7 0x37 | ||
| 292 | #define DSI_PKT_LEN_6_7_LENGTH_7(x) (((x) & 0xffff) << 16) | ||
| 293 | #define DSI_PKT_LEN_6_7_LENGTH_6(x) (((x) & 0xffff) << 0) | ||
| 294 | |||
| 295 | #define DSI_PHY_TIMING_0 0x3c | ||
| 296 | #define DSI_PHY_TIMING_0_THSDEXIT(x) (((x) & 0xff) << 24) | ||
| 297 | #define DSI_PHY_TIMING_0_THSTRAIL(x) (((x) & 0xff) << 16) | ||
| 298 | #define DSI_PHY_TIMING_0_TDATZERO(x) (((x) & 0xff) << 8) | ||
| 299 | #define DSI_PHY_TIMING_0_THSPREPR(x) (((x) & 0xff) << 0) | ||
| 300 | |||
| 301 | #define DSI_PHY_TIMING_1 0x3d | ||
| 302 | #define DSI_PHY_TIMING_1_TCLKTRAIL(x) (((x) & 0xff) << 24) | ||
| 303 | #define DSI_PHY_TIMING_1_TCLKPOST(x) (((x) & 0xff) << 16) | ||
| 304 | #define DSI_PHY_TIMING_1_TCLKZERO(x) (((x) & 0xff) << 8) | ||
| 305 | #define DSI_PHY_TIMING_1_TTLPX(x) (((x) & 0xff) << 0) | ||
| 306 | |||
| 307 | #define DSI_PHY_TIMING_2 0x3e | ||
| 308 | #define DSI_PHY_TIMING_2_TCLKPREPARE(x) (((x) & 0xff) << 16) | ||
| 309 | #define DSI_PHY_TIMING_2_TCLKPRE(x) (((x) & 0xff) << 8) | ||
| 310 | #define DSI_PHY_TIMING_2_TWAKEUP(x) (((x) & 0xff) << 0) | ||
| 311 | |||
| 312 | #define DSI_BTA_TIMING 0x3f | ||
| 313 | #define DSI_BTA_TIMING_TTAGET(x) (((x) & 0xff) << 16) | ||
| 314 | #define DSI_BTA_TIMING_TTASURE(x) (((x) & 0xff) << 8) | ||
| 315 | #define DSI_BTA_TIMING_TTAGO(x) (((x) & 0xff) << 0) | ||
| 316 | |||
| 317 | |||
| 318 | #define DSI_TIMEOUT_0 0x44 | ||
| 319 | #define DSI_TIMEOUT_0_LRXH_TO(x) (((x) & 0xffff) << 16) | ||
| 320 | #define DSI_TIMEOUT_0_HTX_TO(x) (((x) & 0xffff) << 0) | ||
| 321 | |||
| 322 | #define DSI_TIMEOUT_1 0x45 | ||
| 323 | #define DSI_TIMEOUT_1_PR_TO(x) (((x) & 0xffff) << 16) | ||
| 324 | #define DSI_TIMEOUT_1_TA_TO(x) (((x) & 0xffff) << 0) | ||
| 325 | |||
| 326 | #define DSI_TO_TALLY 0x46 | ||
| 327 | enum { | ||
| 328 | IN_RESET, | ||
| 329 | READY, | ||
| 330 | }; | ||
| 331 | #define DSI_TO_TALLY_P_RESET_STATUS(x) (((x) & 0x1) << 24) | ||
| 332 | #define DSI_TO_TALLY_TA_TALLY(x) (((x) & 0xff) << 16) | ||
| 333 | #define DSI_TO_TALLY_LRXH_TALLY(x) (((x) & 0xff) << 8) | ||
| 334 | #define DSI_TO_TALLY_HTX_TALLY(x) (((x) & 0xff) << 0) | ||
| 335 | |||
| 336 | #define DSI_PAD_CONTROL 0x4b | ||
| 337 | #define DSI_PAD_CONTROL_PAD_PULLDN_ENAB(x) (((x) & 0x1) << 28) | ||
| 338 | #define DSI_PAD_CONTROL_PAD_SLEWUPADJ(x) (((x) & 0x7) << 24) | ||
| 339 | #define DSI_PAD_CONTROL_PAD_SLEWDNADJ(x) (((x) & 0x7) << 20) | ||
| 340 | #define DSI_PAD_CONTROL_PAD_PREEMP_EN(x) (((x) & 0x1) << 19) | ||
| 341 | #define DSI_PAD_CONTROL_PAD_PDIO_CLK(x) (((x) & 0x1) << 18) | ||
| 342 | #define DSI_PAD_CONTROL_PAD_PDIO(x) (((x) & 0x3) << 16) | ||
| 343 | #define DSI_PAD_CONTROL_PAD_LPUPADJ(x) (((x) & 0x3) << 14) | ||
| 344 | #define DSI_PAD_CONTROL_PAD_LPDNADJ(x) (((x) & 0x3) << 12) | ||
| 345 | |||
| 346 | #define DSI_PAD_CONTROL_CD 0x4c | ||
| 347 | #define DSI_PAD_CD_STATUS 0x4d | ||
| 348 | #define DSI_VID_MODE_CONTROL 0x4e | ||
| 349 | |||
| 350 | #endif | ||
| 351 | |||
diff --git a/drivers/video/tegra/dc/edid.c b/drivers/video/tegra/dc/edid.c new file mode 100644 index 00000000000..fbcf2cc8e37 --- /dev/null +++ b/drivers/video/tegra/dc/edid.c | |||
| @@ -0,0 +1,619 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/edid.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2010 Google, Inc. | ||
| 5 | * Author: Erik Gilling <konkers@android.com> | ||
| 6 | * | ||
| 7 | * Copyright (C) 2010-2011 NVIDIA Corporation | ||
| 8 | * | ||
| 9 | * This software is licensed under the terms of the GNU General Public | ||
| 10 | * License version 2, as published by the Free Software Foundation, and | ||
| 11 | * may be copied, distributed, and modified under those terms. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | */ | ||
| 19 | |||
| 20 | |||
| 21 | #include <linux/debugfs.h> | ||
| 22 | #include <linux/fb.h> | ||
| 23 | #include <linux/i2c.h> | ||
| 24 | #include <linux/seq_file.h> | ||
| 25 | #include <linux/vmalloc.h> | ||
| 26 | |||
| 27 | #include "edid.h" | ||
| 28 | |||
| 29 | struct tegra_edid_pvt { | ||
| 30 | struct kref refcnt; | ||
| 31 | struct tegra_edid_hdmi_eld eld; | ||
| 32 | bool support_stereo; | ||
| 33 | bool support_underscan; | ||
| 34 | /* Note: dc_edid must remain the last member */ | ||
| 35 | struct tegra_dc_edid dc_edid; | ||
| 36 | }; | ||
| 37 | |||
| 38 | struct tegra_edid { | ||
| 39 | struct i2c_client *client; | ||
| 40 | struct i2c_board_info info; | ||
| 41 | int bus; | ||
| 42 | |||
| 43 | struct tegra_edid_pvt *data; | ||
| 44 | |||
| 45 | struct mutex lock; | ||
| 46 | }; | ||
| 47 | |||
| 48 | #if defined(DEBUG) || defined(CONFIG_DEBUG_FS) | ||
| 49 | static int tegra_edid_show(struct seq_file *s, void *unused) | ||
| 50 | { | ||
| 51 | struct tegra_edid *edid = s->private; | ||
| 52 | struct tegra_dc_edid *data; | ||
| 53 | u8 *buf; | ||
| 54 | int i; | ||
| 55 | |||
| 56 | data = tegra_edid_get_data(edid); | ||
| 57 | if (!data) { | ||
| 58 | seq_printf(s, "No EDID\n"); | ||
| 59 | return 0; | ||
| 60 | } | ||
| 61 | |||
| 62 | buf = data->buf; | ||
| 63 | |||
| 64 | for (i = 0; i < data->len; i++) { | ||
| 65 | if (i % 16 == 0) | ||
| 66 | seq_printf(s, "edid[%03x] =", i); | ||
| 67 | |||
| 68 | seq_printf(s, " %02x", buf[i]); | ||
| 69 | |||
| 70 | if (i % 16 == 15) | ||
| 71 | seq_printf(s, "\n"); | ||
| 72 | } | ||
| 73 | |||
| 74 | tegra_edid_put_data(data); | ||
| 75 | |||
| 76 | return 0; | ||
| 77 | } | ||
| 78 | #endif | ||
| 79 | |||
| 80 | #ifdef CONFIG_DEBUG_FS | ||
| 81 | static int tegra_edid_debug_open(struct inode *inode, struct file *file) | ||
| 82 | { | ||
| 83 | return single_open(file, tegra_edid_show, inode->i_private); | ||
| 84 | } | ||
| 85 | |||
| 86 | static const struct file_operations tegra_edid_debug_fops = { | ||
| 87 | .open = tegra_edid_debug_open, | ||
| 88 | .read = seq_read, | ||
| 89 | .llseek = seq_lseek, | ||
| 90 | .release = single_release, | ||
| 91 | }; | ||
| 92 | |||
| 93 | void tegra_edid_debug_add(struct tegra_edid *edid) | ||
| 94 | { | ||
| 95 | char name[] = "edidX"; | ||
| 96 | |||
| 97 | snprintf(name, sizeof(name), "edid%1d", edid->bus); | ||
| 98 | debugfs_create_file(name, S_IRUGO, NULL, edid, &tegra_edid_debug_fops); | ||
| 99 | } | ||
| 100 | #else | ||
| 101 | void tegra_edid_debug_add(struct tegra_edid *edid) | ||
| 102 | { | ||
| 103 | } | ||
| 104 | #endif | ||
| 105 | |||
| 106 | #ifdef DEBUG | ||
| 107 | static char tegra_edid_dump_buff[16 * 1024]; | ||
| 108 | |||
| 109 | static void tegra_edid_dump(struct tegra_edid *edid) | ||
| 110 | { | ||
| 111 | struct seq_file s; | ||
| 112 | int i; | ||
| 113 | char c; | ||
| 114 | |||
| 115 | memset(&s, 0x0, sizeof(s)); | ||
| 116 | |||
| 117 | s.buf = tegra_edid_dump_buff; | ||
| 118 | s.size = sizeof(tegra_edid_dump_buff); | ||
| 119 | s.private = edid; | ||
| 120 | |||
| 121 | tegra_edid_show(&s, NULL); | ||
| 122 | |||
| 123 | i = 0; | ||
| 124 | while (i < s.count ) { | ||
| 125 | if ((s.count - i) > 256) { | ||
| 126 | c = s.buf[i + 256]; | ||
| 127 | s.buf[i + 256] = 0; | ||
| 128 | printk("%s", s.buf + i); | ||
| 129 | s.buf[i + 256] = c; | ||
| 130 | } else { | ||
| 131 | printk("%s", s.buf + i); | ||
| 132 | } | ||
| 133 | i += 256; | ||
| 134 | } | ||
| 135 | } | ||
| 136 | #else | ||
| 137 | static void tegra_edid_dump(struct tegra_edid *edid) | ||
| 138 | { | ||
| 139 | } | ||
| 140 | #endif | ||
| 141 | |||
| 142 | int tegra_edid_read_block(struct tegra_edid *edid, int block, u8 *data) | ||
| 143 | { | ||
| 144 | u8 block_buf[] = {block >> 1}; | ||
| 145 | u8 cmd_buf[] = {(block & 0x1) * 128}; | ||
| 146 | int status; | ||
| 147 | struct i2c_msg msg[] = { | ||
| 148 | { | ||
| 149 | .addr = 0x30, | ||
| 150 | .flags = 0, | ||
| 151 | .len = 1, | ||
| 152 | .buf = block_buf, | ||
| 153 | }, | ||
| 154 | { | ||
| 155 | .addr = 0x50, | ||
| 156 | .flags = 0, | ||
| 157 | .len = 1, | ||
| 158 | .buf = cmd_buf, | ||
| 159 | }, | ||
| 160 | { | ||
| 161 | .addr = 0x50, | ||
| 162 | .flags = I2C_M_RD, | ||
| 163 | .len = 128, | ||
| 164 | .buf = data, | ||
| 165 | }}; | ||
| 166 | struct i2c_msg *m; | ||
| 167 | int msg_len; | ||
| 168 | |||
| 169 | if (block > 1) { | ||
| 170 | msg_len = 3; | ||
| 171 | m = msg; | ||
| 172 | } else { | ||
| 173 | msg_len = 2; | ||
| 174 | m = &msg[1]; | ||
| 175 | } | ||
| 176 | |||
| 177 | status = i2c_transfer(edid->client->adapter, m, msg_len); | ||
| 178 | |||
| 179 | if (status < 0) | ||
| 180 | return status; | ||
| 181 | |||
| 182 | if (status != msg_len) | ||
| 183 | return -EIO; | ||
| 184 | |||
| 185 | return 0; | ||
| 186 | } | ||
| 187 | |||
| 188 | int tegra_edid_parse_ext_block(const u8 *raw, int idx, | ||
| 189 | struct tegra_edid_pvt *edid) | ||
| 190 | { | ||
| 191 | const u8 *ptr; | ||
| 192 | u8 tmp; | ||
| 193 | u8 code; | ||
| 194 | int len; | ||
| 195 | int i; | ||
| 196 | bool basic_audio = false; | ||
| 197 | |||
| 198 | ptr = &raw[0]; | ||
| 199 | |||
| 200 | /* If CEA 861 block get info for eld struct */ | ||
| 201 | if (edid && ptr) { | ||
| 202 | if (*ptr <= 3) | ||
| 203 | edid->eld.eld_ver = 0x02; | ||
| 204 | edid->eld.cea_edid_ver = ptr[1]; | ||
| 205 | |||
| 206 | /* check for basic audio support in CEA 861 block */ | ||
| 207 | if(raw[3] & (1<<6)) { | ||
| 208 | /* For basic audio, set spk_alloc to Left+Right. | ||
| 209 | * If there is a Speaker Alloc block this will | ||
| 210 | * get over written with that value */ | ||
| 211 | basic_audio = true; | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | if (raw[3] & 0x80) | ||
| 216 | edid->support_underscan = 1; | ||
| 217 | else | ||
| 218 | edid->support_underscan = 0; | ||
| 219 | |||
| 220 | ptr = &raw[4]; | ||
| 221 | |||
| 222 | while (ptr < &raw[idx]) { | ||
| 223 | tmp = *ptr; | ||
| 224 | len = tmp & 0x1f; | ||
| 225 | |||
| 226 | /* HDMI Specification v1.4a, section 8.3.2: | ||
| 227 | * see Table 8-16 for HDMI VSDB format. | ||
| 228 | * data blocks have tags in top 3 bits: | ||
| 229 | * tag code 2: video data block | ||
| 230 | * tag code 3: vendor specific data block | ||
| 231 | */ | ||
| 232 | code = (tmp >> 5) & 0x7; | ||
| 233 | switch (code) { | ||
| 234 | case 1: | ||
| 235 | { | ||
| 236 | edid->eld.sad_count = len; | ||
| 237 | edid->eld.conn_type = 0x00; | ||
| 238 | edid->eld.support_hdcp = 0x00; | ||
| 239 | for (i = 0; (i < len) && (i < ELD_MAX_SAD); i ++) | ||
| 240 | edid->eld.sad[i] = ptr[i + 1]; | ||
| 241 | len++; | ||
| 242 | ptr += len; /* adding the header */ | ||
| 243 | /* Got an audio data block so enable audio */ | ||
| 244 | if(basic_audio == true) | ||
| 245 | edid->eld.spk_alloc = 1; | ||
| 246 | break; | ||
| 247 | } | ||
| 248 | /* case 2 is commented out for now */ | ||
| 249 | case 3: | ||
| 250 | { | ||
| 251 | int j = 0; | ||
| 252 | |||
| 253 | if ((ptr[1] == 0x03) && | ||
| 254 | (ptr[2] == 0x0c) && | ||
| 255 | (ptr[3] == 0)) { | ||
| 256 | edid->eld.port_id[0] = ptr[4]; | ||
| 257 | edid->eld.port_id[1] = ptr[5]; | ||
| 258 | } | ||
| 259 | if ((len >= 8) && | ||
| 260 | (ptr[1] == 0x03) && | ||
| 261 | (ptr[2] == 0x0c) && | ||
| 262 | (ptr[3] == 0)) { | ||
| 263 | j = 8; | ||
| 264 | tmp = ptr[j++]; | ||
| 265 | /* HDMI_Video_present? */ | ||
| 266 | if (tmp & 0x20) { | ||
| 267 | /* Latency_Fields_present? */ | ||
| 268 | if (tmp & 0x80) | ||
| 269 | j += 2; | ||
| 270 | /* I_Latency_Fields_present? */ | ||
| 271 | if (tmp & 0x40) | ||
| 272 | j += 2; | ||
| 273 | /* 3D_present? */ | ||
| 274 | if (j <= len && (ptr[j] & 0x80)) | ||
| 275 | edid->support_stereo = 1; | ||
| 276 | } | ||
| 277 | } | ||
| 278 | if ((len > 5) && | ||
| 279 | (ptr[1] == 0x03) && | ||
| 280 | (ptr[2] == 0x0c) && | ||
| 281 | (ptr[3] == 0)) { | ||
| 282 | |||
| 283 | edid->eld.support_ai = (ptr[6] & 0x80); | ||
| 284 | } | ||
| 285 | |||
| 286 | if ((len > 9) && | ||
| 287 | (ptr[1] == 0x03) && | ||
| 288 | (ptr[2] == 0x0c) && | ||
| 289 | (ptr[3] == 0)) { | ||
| 290 | |||
| 291 | edid->eld.aud_synch_delay = ptr[10]; | ||
| 292 | } | ||
| 293 | len++; | ||
| 294 | ptr += len; /* adding the header */ | ||
| 295 | break; | ||
| 296 | } | ||
| 297 | case 4: | ||
| 298 | { | ||
| 299 | edid->eld.spk_alloc = ptr[1]; | ||
| 300 | len++; | ||
| 301 | ptr += len; /* adding the header */ | ||
| 302 | break; | ||
| 303 | } | ||
| 304 | default: | ||
| 305 | len++; /* len does not include header */ | ||
| 306 | ptr += len; | ||
| 307 | break; | ||
| 308 | } | ||
| 309 | } | ||
| 310 | |||
| 311 | return 0; | ||
| 312 | } | ||
| 313 | |||
| 314 | int tegra_edid_mode_support_stereo(struct fb_videomode *mode) | ||
| 315 | { | ||
| 316 | if (!mode) | ||
| 317 | return 0; | ||
| 318 | |||
| 319 | if (mode->xres == 1280 && | ||
| 320 | mode->yres == 720 && | ||
| 321 | ((mode->refresh == 60) || (mode->refresh == 50))) | ||
| 322 | return 1; | ||
| 323 | |||
| 324 | /* Disabling 1080p stereo mode due to bug 869099. */ | ||
| 325 | /* Must re-enable this to 1 once it is fixed. */ | ||
| 326 | if (mode->xres == 1920 && mode->yres == 1080 && mode->refresh == 24) | ||
| 327 | return 0; | ||
| 328 | |||
| 329 | return 0; | ||
| 330 | } | ||
| 331 | |||
| 332 | static void data_release(struct kref *ref) | ||
| 333 | { | ||
| 334 | struct tegra_edid_pvt *data = | ||
| 335 | container_of(ref, struct tegra_edid_pvt, refcnt); | ||
| 336 | vfree(data); | ||
| 337 | } | ||
| 338 | |||
| 339 | int tegra_edid_get_monspecs_test(struct tegra_edid *edid, | ||
| 340 | struct fb_monspecs *specs, unsigned char *edid_ptr) | ||
| 341 | { | ||
| 342 | int i, j, ret; | ||
| 343 | int extension_blocks; | ||
| 344 | struct tegra_edid_pvt *new_data, *old_data; | ||
| 345 | u8 *data; | ||
| 346 | |||
| 347 | new_data = vmalloc(SZ_32K + sizeof(struct tegra_edid_pvt)); | ||
| 348 | if (!new_data) | ||
| 349 | return -ENOMEM; | ||
| 350 | |||
| 351 | kref_init(&new_data->refcnt); | ||
| 352 | |||
| 353 | new_data->support_stereo = 0; | ||
| 354 | new_data->support_underscan = 0; | ||
| 355 | |||
| 356 | data = new_data->dc_edid.buf; | ||
| 357 | memcpy(data, edid_ptr, 128); | ||
| 358 | |||
| 359 | memset(specs, 0x0, sizeof(struct fb_monspecs)); | ||
| 360 | memset(&new_data->eld, 0x0, sizeof(new_data->eld)); | ||
| 361 | fb_edid_to_monspecs(data, specs); | ||
| 362 | if (specs->modedb == NULL) { | ||
| 363 | ret = -EINVAL; | ||
| 364 | goto fail; | ||
| 365 | } | ||
| 366 | |||
| 367 | memcpy(new_data->eld.monitor_name, specs->monitor, | ||
| 368 | sizeof(specs->monitor)); | ||
| 369 | |||
| 370 | new_data->eld.mnl = strlen(new_data->eld.monitor_name) + 1; | ||
| 371 | new_data->eld.product_id[0] = data[0x8]; | ||
| 372 | new_data->eld.product_id[1] = data[0x9]; | ||
| 373 | new_data->eld.manufacture_id[0] = data[0xA]; | ||
| 374 | new_data->eld.manufacture_id[1] = data[0xB]; | ||
| 375 | |||
| 376 | extension_blocks = data[0x7e]; | ||
| 377 | for (i = 1; i <= extension_blocks; i++) { | ||
| 378 | memcpy(data+128, edid_ptr+128, 128); | ||
| 379 | |||
| 380 | if (data[i * 128] == 0x2) { | ||
| 381 | fb_edid_add_monspecs(data + i * 128, specs); | ||
| 382 | |||
| 383 | tegra_edid_parse_ext_block(data + i * 128, | ||
| 384 | data[i * 128 + 2], new_data); | ||
| 385 | |||
| 386 | if (new_data->support_stereo) { | ||
| 387 | for (j = 0; j < specs->modedb_len; j++) { | ||
| 388 | if (tegra_edid_mode_support_stereo( | ||
| 389 | &specs->modedb[j])) | ||
| 390 | specs->modedb[j].vmode |= | ||
| 391 | #ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT | ||
| 392 | FB_VMODE_STEREO_FRAME_PACK; | ||
| 393 | #else | ||
| 394 | FB_VMODE_STEREO_LEFT_RIGHT; | ||
| 395 | #endif | ||
| 396 | } | ||
| 397 | } | ||
| 398 | } | ||
| 399 | } | ||
| 400 | |||
| 401 | new_data->dc_edid.len = i * 128; | ||
| 402 | |||
| 403 | mutex_lock(&edid->lock); | ||
| 404 | old_data = edid->data; | ||
| 405 | edid->data = new_data; | ||
| 406 | mutex_unlock(&edid->lock); | ||
| 407 | |||
| 408 | if (old_data) | ||
| 409 | kref_put(&old_data->refcnt, data_release); | ||
| 410 | |||
| 411 | tegra_edid_dump(edid); | ||
| 412 | return 0; | ||
| 413 | fail: | ||
| 414 | vfree(new_data); | ||
| 415 | return ret; | ||
| 416 | } | ||
| 417 | |||
| 418 | int tegra_edid_get_monspecs(struct tegra_edid *edid, struct fb_monspecs *specs) | ||
| 419 | { | ||
| 420 | int i; | ||
| 421 | int j; | ||
| 422 | int ret; | ||
| 423 | int extension_blocks; | ||
| 424 | struct tegra_edid_pvt *new_data, *old_data; | ||
| 425 | u8 *data; | ||
| 426 | |||
| 427 | new_data = vmalloc(SZ_32K + sizeof(struct tegra_edid_pvt)); | ||
| 428 | if (!new_data) | ||
| 429 | return -ENOMEM; | ||
| 430 | |||
| 431 | kref_init(&new_data->refcnt); | ||
| 432 | |||
| 433 | new_data->support_stereo = 0; | ||
| 434 | |||
| 435 | data = new_data->dc_edid.buf; | ||
| 436 | |||
| 437 | ret = tegra_edid_read_block(edid, 0, data); | ||
| 438 | if (ret) | ||
| 439 | goto fail; | ||
| 440 | |||
| 441 | memset(specs, 0x0, sizeof(struct fb_monspecs)); | ||
| 442 | memset(&new_data->eld, 0x0, sizeof(new_data->eld)); | ||
| 443 | fb_edid_to_monspecs(data, specs); | ||
| 444 | if (specs->modedb == NULL) { | ||
| 445 | ret = -EINVAL; | ||
| 446 | goto fail; | ||
| 447 | } | ||
| 448 | memcpy(new_data->eld.monitor_name, specs->monitor, sizeof(specs->monitor)); | ||
| 449 | new_data->eld.mnl = strlen(new_data->eld.monitor_name) + 1; | ||
| 450 | new_data->eld.product_id[0] = data[0x8]; | ||
| 451 | new_data->eld.product_id[1] = data[0x9]; | ||
| 452 | new_data->eld.manufacture_id[0] = data[0xA]; | ||
| 453 | new_data->eld.manufacture_id[1] = data[0xB]; | ||
| 454 | |||
| 455 | extension_blocks = data[0x7e]; | ||
| 456 | |||
| 457 | for (i = 1; i <= extension_blocks; i++) { | ||
| 458 | ret = tegra_edid_read_block(edid, i, data + i * 128); | ||
| 459 | if (ret < 0) | ||
| 460 | break; | ||
| 461 | |||
| 462 | if (data[i * 128] == 0x2) { | ||
| 463 | fb_edid_add_monspecs(data + i * 128, specs); | ||
| 464 | |||
| 465 | tegra_edid_parse_ext_block(data + i * 128, | ||
| 466 | data[i * 128 + 2], new_data); | ||
| 467 | |||
| 468 | if (new_data->support_stereo) { | ||
| 469 | for (j = 0; j < specs->modedb_len; j++) { | ||
| 470 | if (tegra_edid_mode_support_stereo( | ||
| 471 | &specs->modedb[j])) | ||
| 472 | specs->modedb[j].vmode |= | ||
| 473 | #ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT | ||
| 474 | FB_VMODE_STEREO_FRAME_PACK; | ||
| 475 | #else | ||
| 476 | FB_VMODE_STEREO_LEFT_RIGHT; | ||
| 477 | #endif | ||
| 478 | } | ||
| 479 | } | ||
| 480 | } | ||
| 481 | } | ||
| 482 | |||
| 483 | new_data->dc_edid.len = i * 128; | ||
| 484 | |||
| 485 | mutex_lock(&edid->lock); | ||
| 486 | old_data = edid->data; | ||
| 487 | edid->data = new_data; | ||
| 488 | mutex_unlock(&edid->lock); | ||
| 489 | |||
| 490 | if (old_data) | ||
| 491 | kref_put(&old_data->refcnt, data_release); | ||
| 492 | |||
| 493 | tegra_edid_dump(edid); | ||
| 494 | return 0; | ||
| 495 | |||
| 496 | fail: | ||
| 497 | vfree(new_data); | ||
| 498 | return ret; | ||
| 499 | } | ||
| 500 | |||
| 501 | int tegra_edid_underscan_supported(struct tegra_edid *edid) | ||
| 502 | { | ||
| 503 | if ((!edid) || (!edid->data)) | ||
| 504 | return 0; | ||
| 505 | |||
| 506 | return edid->data->support_underscan; | ||
| 507 | } | ||
| 508 | |||
| 509 | int tegra_edid_get_eld(struct tegra_edid *edid, struct tegra_edid_hdmi_eld *elddata) | ||
| 510 | { | ||
| 511 | if (!elddata || !edid->data) | ||
| 512 | return -EFAULT; | ||
| 513 | |||
| 514 | memcpy(elddata,&edid->data->eld,sizeof(struct tegra_edid_hdmi_eld)); | ||
| 515 | |||
| 516 | return 0; | ||
| 517 | } | ||
| 518 | |||
| 519 | struct tegra_edid *tegra_edid_create(int bus) | ||
| 520 | { | ||
| 521 | struct tegra_edid *edid; | ||
| 522 | struct i2c_adapter *adapter; | ||
| 523 | int err; | ||
| 524 | |||
| 525 | edid = kzalloc(sizeof(struct tegra_edid), GFP_KERNEL); | ||
| 526 | if (!edid) | ||
| 527 | return ERR_PTR(-ENOMEM); | ||
| 528 | |||
| 529 | mutex_init(&edid->lock); | ||
| 530 | strlcpy(edid->info.type, "tegra_edid", sizeof(edid->info.type)); | ||
| 531 | edid->bus = bus; | ||
| 532 | edid->info.addr = 0x50; | ||
| 533 | edid->info.platform_data = edid; | ||
| 534 | |||
| 535 | adapter = i2c_get_adapter(bus); | ||
| 536 | if (!adapter) { | ||
| 537 | pr_err("can't get adpater for bus %d\n", bus); | ||
| 538 | err = -EBUSY; | ||
| 539 | goto free_edid; | ||
| 540 | } | ||
| 541 | |||
| 542 | edid->client = i2c_new_device(adapter, &edid->info); | ||
| 543 | i2c_put_adapter(adapter); | ||
| 544 | |||
| 545 | if (!edid->client) { | ||
| 546 | pr_err("can't create new device\n"); | ||
| 547 | err = -EBUSY; | ||
| 548 | goto free_edid; | ||
| 549 | } | ||
| 550 | |||
| 551 | tegra_edid_debug_add(edid); | ||
| 552 | |||
| 553 | return edid; | ||
| 554 | |||
| 555 | free_edid: | ||
| 556 | kfree(edid); | ||
| 557 | |||
| 558 | return ERR_PTR(err); | ||
| 559 | } | ||
| 560 | |||
| 561 | void tegra_edid_destroy(struct tegra_edid *edid) | ||
| 562 | { | ||
| 563 | i2c_release_client(edid->client); | ||
| 564 | if (edid->data) | ||
| 565 | kref_put(&edid->data->refcnt, data_release); | ||
| 566 | kfree(edid); | ||
| 567 | } | ||
| 568 | |||
| 569 | struct tegra_dc_edid *tegra_edid_get_data(struct tegra_edid *edid) | ||
| 570 | { | ||
| 571 | struct tegra_edid_pvt *data; | ||
| 572 | |||
| 573 | mutex_lock(&edid->lock); | ||
| 574 | data = edid->data; | ||
| 575 | if (data) | ||
| 576 | kref_get(&data->refcnt); | ||
| 577 | mutex_unlock(&edid->lock); | ||
| 578 | |||
| 579 | return data ? &data->dc_edid : NULL; | ||
| 580 | } | ||
| 581 | |||
| 582 | void tegra_edid_put_data(struct tegra_dc_edid *data) | ||
| 583 | { | ||
| 584 | struct tegra_edid_pvt *pvt; | ||
| 585 | |||
| 586 | if (!data) | ||
| 587 | return; | ||
| 588 | |||
| 589 | pvt = container_of(data, struct tegra_edid_pvt, dc_edid); | ||
| 590 | |||
| 591 | kref_put(&pvt->refcnt, data_release); | ||
| 592 | } | ||
| 593 | |||
| 594 | static const struct i2c_device_id tegra_edid_id[] = { | ||
| 595 | { "tegra_edid", 0 }, | ||
| 596 | { } | ||
| 597 | }; | ||
| 598 | |||
| 599 | MODULE_DEVICE_TABLE(i2c, tegra_edid_id); | ||
| 600 | |||
| 601 | static struct i2c_driver tegra_edid_driver = { | ||
| 602 | .id_table = tegra_edid_id, | ||
| 603 | .driver = { | ||
| 604 | .name = "tegra_edid", | ||
| 605 | }, | ||
| 606 | }; | ||
| 607 | |||
| 608 | static int __init tegra_edid_init(void) | ||
| 609 | { | ||
| 610 | return i2c_add_driver(&tegra_edid_driver); | ||
| 611 | } | ||
| 612 | |||
| 613 | static void __exit tegra_edid_exit(void) | ||
| 614 | { | ||
| 615 | i2c_del_driver(&tegra_edid_driver); | ||
| 616 | } | ||
| 617 | |||
| 618 | module_init(tegra_edid_init); | ||
| 619 | module_exit(tegra_edid_exit); | ||
diff --git a/drivers/video/tegra/dc/edid.h b/drivers/video/tegra/dc/edid.h new file mode 100644 index 00000000000..77db36f4adb --- /dev/null +++ b/drivers/video/tegra/dc/edid.h | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/edid.h | ||
| 3 | * | ||
| 4 | * Copyright (C) 2010 Google, Inc. | ||
| 5 | * Author: Erik Gilling <konkers@android.com> | ||
| 6 | * | ||
| 7 | * This software is licensed under the terms of the GNU General Public | ||
| 8 | * License version 2, as published by the Free Software Foundation, and | ||
| 9 | * may be copied, distributed, and modified under those terms. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | |||
| 18 | #ifndef __DRIVERS_VIDEO_TEGRA_DC_EDID_H | ||
| 19 | #define __DRIVERS_VIDEO_TEGRA_DC_EDID_H | ||
| 20 | |||
| 21 | #include <linux/i2c.h> | ||
| 22 | #include <linux/wait.h> | ||
| 23 | #include <mach/dc.h> | ||
| 24 | |||
| 25 | #define ELD_MAX_MNL 16 | ||
| 26 | #define ELD_MAX_SAD 16 | ||
| 27 | struct tegra_edid; | ||
| 28 | |||
| 29 | /* | ||
| 30 | * ELD: EDID Like Data | ||
| 31 | */ | ||
| 32 | struct tegra_edid_hdmi_eld { | ||
| 33 | u8 baseline_len; | ||
| 34 | u8 eld_ver; | ||
| 35 | u8 cea_edid_ver; | ||
| 36 | char monitor_name[ELD_MAX_MNL + 1]; | ||
| 37 | u8 mnl; | ||
| 38 | u8 manufacture_id[2]; | ||
| 39 | u8 product_id[2]; | ||
| 40 | u8 port_id[8]; | ||
| 41 | u8 support_hdcp; | ||
| 42 | u8 support_ai; | ||
| 43 | u8 conn_type; | ||
| 44 | u8 aud_synch_delay; | ||
| 45 | u8 spk_alloc; | ||
| 46 | u8 sad_count; | ||
| 47 | u8 sad[ELD_MAX_SAD]; | ||
| 48 | }; | ||
| 49 | |||
| 50 | struct tegra_edid *tegra_edid_create(int bus); | ||
| 51 | void tegra_edid_destroy(struct tegra_edid *edid); | ||
| 52 | |||
| 53 | int tegra_edid_get_monspecs_test(struct tegra_edid *edid, | ||
| 54 | struct fb_monspecs *specs, u8 *edid_ptr); | ||
| 55 | int tegra_edid_get_monspecs(struct tegra_edid *edid, struct fb_monspecs *specs); | ||
| 56 | int tegra_edid_get_eld(struct tegra_edid *edid, struct tegra_edid_hdmi_eld *elddata); | ||
| 57 | |||
| 58 | struct tegra_dc_edid *tegra_edid_get_data(struct tegra_edid *edid); | ||
| 59 | void tegra_edid_put_data(struct tegra_dc_edid *data); | ||
| 60 | |||
| 61 | int tegra_edid_underscan_supported(struct tegra_edid *edid); | ||
| 62 | #endif | ||
diff --git a/drivers/video/tegra/dc/ext/Makefile b/drivers/video/tegra/dc/ext/Makefile new file mode 100644 index 00000000000..19860ab5db1 --- /dev/null +++ b/drivers/video/tegra/dc/ext/Makefile | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | obj-y += dev.o | ||
| 2 | obj-y += util.o | ||
| 3 | obj-y += cursor.o | ||
| 4 | obj-y += events.o | ||
| 5 | obj-y += control.o | ||
diff --git a/drivers/video/tegra/dc/ext/control.c b/drivers/video/tegra/dc/ext/control.c new file mode 100644 index 00000000000..9caf3e11c16 --- /dev/null +++ b/drivers/video/tegra/dc/ext/control.c | |||
| @@ -0,0 +1,279 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/ext/control.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2011, NVIDIA Corporation | ||
| 5 | * | ||
| 6 | * Author: Robert Morell <rmorell@nvidia.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License, or | ||
| 11 | * (at your option) any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 16 | * more details. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <linux/device.h> | ||
| 20 | #include <linux/err.h> | ||
| 21 | #include <linux/file.h> | ||
| 22 | #include <linux/fs.h> | ||
| 23 | #include <linux/slab.h> | ||
| 24 | #include <linux/module.h> | ||
| 25 | #include <linux/uaccess.h> | ||
| 26 | |||
| 27 | #include "tegra_dc_ext_priv.h" | ||
| 28 | |||
| 29 | static struct tegra_dc_ext_control g_control; | ||
| 30 | |||
| 31 | int tegra_dc_ext_process_hotplug(int output) | ||
| 32 | { | ||
| 33 | return tegra_dc_ext_queue_hotplug(&g_control, output); | ||
| 34 | } | ||
| 35 | |||
| 36 | static int | ||
| 37 | get_output_properties(struct tegra_dc_ext_control_output_properties *properties) | ||
| 38 | { | ||
| 39 | struct tegra_dc *dc; | ||
| 40 | |||
| 41 | /* TODO: this should be more dynamic */ | ||
| 42 | if (properties->handle > 2) | ||
| 43 | return -EINVAL; | ||
| 44 | |||
| 45 | switch (properties->handle) { | ||
| 46 | case 0: | ||
| 47 | properties->type = TEGRA_DC_EXT_LVDS; | ||
| 48 | break; | ||
| 49 | case 1: | ||
| 50 | properties->type = TEGRA_DC_EXT_HDMI; | ||
| 51 | break; | ||
| 52 | default: | ||
| 53 | return -EINVAL; | ||
| 54 | } | ||
| 55 | |||
| 56 | properties->associated_head = properties->handle; | ||
| 57 | properties->head_mask = (1 << properties->associated_head); | ||
| 58 | |||
| 59 | dc = tegra_dc_get_dc(properties->associated_head); | ||
| 60 | properties->connected = tegra_dc_get_connected(dc); | ||
| 61 | |||
| 62 | return 0; | ||
| 63 | } | ||
| 64 | |||
| 65 | static int get_output_edid(struct tegra_dc_ext_control_output_edid *edid) | ||
| 66 | { | ||
| 67 | struct tegra_dc *dc; | ||
| 68 | size_t user_size = edid->size; | ||
| 69 | struct tegra_dc_edid *dc_edid = NULL; | ||
| 70 | int ret; | ||
| 71 | |||
| 72 | /* TODO: this should be more dynamic */ | ||
| 73 | if (edid->handle > 2) | ||
| 74 | return -EINVAL; | ||
| 75 | |||
| 76 | dc = tegra_dc_get_dc(edid->handle); | ||
| 77 | |||
| 78 | dc_edid = tegra_dc_get_edid(dc); | ||
| 79 | if (IS_ERR(dc_edid)) | ||
| 80 | return PTR_ERR(dc_edid); | ||
| 81 | |||
| 82 | if (!dc_edid) { | ||
| 83 | edid->size = 0; | ||
| 84 | } else { | ||
| 85 | edid->size = dc_edid->len; | ||
| 86 | |||
| 87 | if (user_size < edid->size) { | ||
| 88 | ret = -EFBIG; | ||
| 89 | goto done; | ||
| 90 | } | ||
| 91 | |||
| 92 | if (copy_to_user(edid->data, dc_edid->buf, edid->size)) { | ||
| 93 | ret = -EFAULT; | ||
| 94 | goto done; | ||
| 95 | } | ||
| 96 | |||
| 97 | } | ||
| 98 | |||
| 99 | done: | ||
| 100 | if (dc_edid) | ||
| 101 | tegra_dc_put_edid(dc_edid); | ||
| 102 | |||
| 103 | return ret; | ||
| 104 | } | ||
| 105 | |||
| 106 | static int set_event_mask(struct tegra_dc_ext_control_user *user, u32 mask) | ||
| 107 | { | ||
| 108 | struct list_head *list, *tmp; | ||
| 109 | |||
| 110 | if (mask & ~TEGRA_DC_EXT_EVENT_MASK_ALL) | ||
| 111 | return -EINVAL; | ||
| 112 | |||
| 113 | mutex_lock(&user->lock); | ||
| 114 | |||
| 115 | user->event_mask = mask; | ||
| 116 | |||
| 117 | list_for_each_safe(list, tmp, &user->event_list) { | ||
| 118 | struct tegra_dc_ext_event_list *ev_list; | ||
| 119 | ev_list = list_entry(list, struct tegra_dc_ext_event_list, | ||
| 120 | list); | ||
| 121 | if (!(mask & ev_list->event.type)) { | ||
| 122 | list_del(list); | ||
| 123 | kfree(ev_list); | ||
| 124 | } | ||
| 125 | } | ||
| 126 | mutex_unlock(&user->lock); | ||
| 127 | |||
| 128 | return 0; | ||
| 129 | } | ||
| 130 | |||
| 131 | static int get_capabilities(struct tegra_dc_ext_control_capabilities *caps) | ||
| 132 | { | ||
| 133 | caps->caps = TEGRA_DC_EXT_CAPABILITIES; | ||
| 134 | return 0; | ||
| 135 | } | ||
| 136 | |||
| 137 | static long tegra_dc_ext_control_ioctl(struct file *filp, unsigned int cmd, | ||
| 138 | unsigned long arg) | ||
| 139 | { | ||
| 140 | void __user *user_arg = (void __user *)arg; | ||
| 141 | struct tegra_dc_ext_control_user *user = filp->private_data; | ||
| 142 | |||
| 143 | switch (cmd) { | ||
| 144 | case TEGRA_DC_EXT_CONTROL_GET_NUM_OUTPUTS: | ||
| 145 | { | ||
| 146 | u32 num = tegra_dc_ext_get_num_outputs(); | ||
| 147 | |||
| 148 | if (copy_to_user(user_arg, &num, sizeof(num))) | ||
| 149 | return -EFAULT; | ||
| 150 | |||
| 151 | return 0; | ||
| 152 | } | ||
| 153 | case TEGRA_DC_EXT_CONTROL_GET_OUTPUT_PROPERTIES: | ||
| 154 | { | ||
| 155 | struct tegra_dc_ext_control_output_properties args; | ||
| 156 | int ret; | ||
| 157 | |||
| 158 | if (copy_from_user(&args, user_arg, sizeof(args))) | ||
| 159 | return -EFAULT; | ||
| 160 | |||
| 161 | ret = get_output_properties(&args); | ||
| 162 | |||
| 163 | if (copy_to_user(user_arg, &args, sizeof(args))) | ||
| 164 | return -EFAULT; | ||
| 165 | |||
| 166 | return ret; | ||
| 167 | } | ||
| 168 | case TEGRA_DC_EXT_CONTROL_GET_OUTPUT_EDID: | ||
| 169 | { | ||
| 170 | struct tegra_dc_ext_control_output_edid args; | ||
| 171 | int ret; | ||
| 172 | |||
| 173 | if (copy_from_user(&args, user_arg, sizeof(args))) | ||
| 174 | return -EFAULT; | ||
| 175 | |||
| 176 | ret = get_output_edid(&args); | ||
| 177 | |||
| 178 | if (copy_to_user(user_arg, &args, sizeof(args))) | ||
| 179 | return -EFAULT; | ||
| 180 | |||
| 181 | return ret; | ||
| 182 | } | ||
| 183 | case TEGRA_DC_EXT_CONTROL_SET_EVENT_MASK: | ||
| 184 | return set_event_mask(user, (u32) arg); | ||
| 185 | case TEGRA_DC_EXT_CONTROL_GET_CAPABILITIES: | ||
| 186 | { | ||
| 187 | struct tegra_dc_ext_control_capabilities args; | ||
| 188 | int ret; | ||
| 189 | |||
| 190 | ret = get_capabilities(&args); | ||
| 191 | |||
| 192 | if (copy_to_user(user_arg, &args, sizeof(args))) | ||
| 193 | return -EFAULT; | ||
| 194 | |||
| 195 | return ret; | ||
| 196 | } | ||
| 197 | default: | ||
| 198 | return -EINVAL; | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 202 | static int tegra_dc_ext_control_open(struct inode *inode, struct file *filp) | ||
| 203 | { | ||
| 204 | struct tegra_dc_ext_control_user *user; | ||
| 205 | struct tegra_dc_ext_control *control; | ||
| 206 | |||
| 207 | user = kzalloc(sizeof(*user), GFP_KERNEL); | ||
| 208 | if (!user) | ||
| 209 | return -ENOMEM; | ||
| 210 | |||
| 211 | control = container_of(inode->i_cdev, struct tegra_dc_ext_control, | ||
| 212 | cdev); | ||
| 213 | user->control = control;; | ||
| 214 | |||
| 215 | INIT_LIST_HEAD(&user->event_list); | ||
| 216 | mutex_init(&user->lock); | ||
| 217 | |||
| 218 | filp->private_data = user; | ||
| 219 | |||
| 220 | mutex_lock(&control->lock); | ||
| 221 | list_add(&user->list, &control->users); | ||
| 222 | mutex_unlock(&control->lock); | ||
| 223 | |||
| 224 | return 0; | ||
| 225 | } | ||
| 226 | |||
| 227 | static int tegra_dc_ext_control_release(struct inode *inode, struct file *filp) | ||
| 228 | { | ||
| 229 | struct tegra_dc_ext_control_user *user = filp->private_data; | ||
| 230 | struct tegra_dc_ext_control *control = user->control; | ||
| 231 | |||
| 232 | /* This will free any pending events for this user */ | ||
| 233 | set_event_mask(user, 0); | ||
| 234 | |||
| 235 | mutex_lock(&control->lock); | ||
| 236 | list_del(&user->list); | ||
| 237 | mutex_unlock(&control->lock); | ||
| 238 | |||
| 239 | kfree(user); | ||
| 240 | |||
| 241 | return 0; | ||
| 242 | } | ||
| 243 | |||
| 244 | static const struct file_operations tegra_dc_ext_event_devops = { | ||
| 245 | .owner = THIS_MODULE, | ||
| 246 | .open = tegra_dc_ext_control_open, | ||
| 247 | .release = tegra_dc_ext_control_release, | ||
| 248 | .read = tegra_dc_ext_event_read, | ||
| 249 | .poll = tegra_dc_ext_event_poll, | ||
| 250 | .unlocked_ioctl = tegra_dc_ext_control_ioctl, | ||
| 251 | }; | ||
| 252 | |||
| 253 | int tegra_dc_ext_control_init(void) | ||
| 254 | { | ||
| 255 | struct tegra_dc_ext_control *control = &g_control; | ||
| 256 | int ret; | ||
| 257 | |||
| 258 | cdev_init(&control->cdev, &tegra_dc_ext_event_devops); | ||
| 259 | control->cdev.owner = THIS_MODULE; | ||
| 260 | ret = cdev_add(&control->cdev, tegra_dc_ext_devno, 1); | ||
| 261 | if (ret) | ||
| 262 | return ret; | ||
| 263 | |||
| 264 | control->dev = device_create(tegra_dc_ext_class, | ||
| 265 | NULL, | ||
| 266 | tegra_dc_ext_devno, | ||
| 267 | NULL, | ||
| 268 | "tegra_dc_ctrl"); | ||
| 269 | if (IS_ERR(control->dev)) { | ||
| 270 | ret = PTR_ERR(control->dev); | ||
| 271 | cdev_del(&control->cdev); | ||
| 272 | } | ||
| 273 | |||
| 274 | mutex_init(&control->lock); | ||
| 275 | |||
| 276 | INIT_LIST_HEAD(&control->users); | ||
| 277 | |||
| 278 | return ret; | ||
| 279 | } | ||
diff --git a/drivers/video/tegra/dc/ext/cursor.c b/drivers/video/tegra/dc/ext/cursor.c new file mode 100644 index 00000000000..d8fa5fd8e6d --- /dev/null +++ b/drivers/video/tegra/dc/ext/cursor.c | |||
| @@ -0,0 +1,203 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/ext/cursor.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2011, NVIDIA Corporation | ||
| 5 | * | ||
| 6 | * Author: Robert Morell <rmorell@nvidia.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License, or | ||
| 11 | * (at your option) any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 16 | * more details. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <video/tegra_dc_ext.h> | ||
| 20 | |||
| 21 | #include "tegra_dc_ext_priv.h" | ||
| 22 | |||
| 23 | /* ugh */ | ||
| 24 | #include "../dc_priv.h" | ||
| 25 | #include "../dc_reg.h" | ||
| 26 | |||
| 27 | int tegra_dc_ext_get_cursor(struct tegra_dc_ext_user *user) | ||
| 28 | { | ||
| 29 | struct tegra_dc_ext *ext = user->ext; | ||
| 30 | int ret = 0; | ||
| 31 | |||
| 32 | mutex_lock(&ext->cursor.lock); | ||
| 33 | |||
| 34 | if (!ext->cursor.user) | ||
| 35 | ext->cursor.user = user; | ||
| 36 | else if (ext->cursor.user != user) | ||
| 37 | ret = -EBUSY; | ||
| 38 | |||
| 39 | mutex_unlock(&ext->cursor.lock); | ||
| 40 | |||
| 41 | return ret; | ||
| 42 | } | ||
| 43 | |||
| 44 | int tegra_dc_ext_put_cursor(struct tegra_dc_ext_user *user) | ||
| 45 | { | ||
| 46 | struct tegra_dc_ext *ext = user->ext; | ||
| 47 | int ret = 0; | ||
| 48 | |||
| 49 | mutex_lock(&ext->cursor.lock); | ||
| 50 | |||
| 51 | if (ext->cursor.user == user) | ||
| 52 | ext->cursor.user = 0; | ||
| 53 | else | ||
| 54 | ret = -EACCES; | ||
| 55 | |||
| 56 | mutex_unlock(&ext->cursor.lock); | ||
| 57 | |||
| 58 | return ret; | ||
| 59 | } | ||
| 60 | |||
| 61 | static void set_cursor_image_hw(struct tegra_dc *dc, | ||
| 62 | struct tegra_dc_ext_cursor_image *args, | ||
| 63 | dma_addr_t phys_addr) | ||
| 64 | { | ||
| 65 | tegra_dc_writel(dc, | ||
| 66 | CURSOR_COLOR(args->foreground.r, | ||
| 67 | args->foreground.g, | ||
| 68 | args->foreground.b), | ||
| 69 | DC_DISP_CURSOR_FOREGROUND); | ||
| 70 | tegra_dc_writel(dc, | ||
| 71 | CURSOR_COLOR(args->background.r, | ||
| 72 | args->background.g, | ||
| 73 | args->background.b), | ||
| 74 | DC_DISP_CURSOR_BACKGROUND); | ||
| 75 | |||
| 76 | BUG_ON(phys_addr & ~CURSOR_START_ADDR_MASK); | ||
| 77 | |||
| 78 | tegra_dc_writel(dc, | ||
| 79 | CURSOR_START_ADDR(((unsigned long) phys_addr)) | | ||
| 80 | ((args->flags & TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_64x64) ? | ||
| 81 | CURSOR_SIZE_64 : 0), | ||
| 82 | DC_DISP_CURSOR_START_ADDR); | ||
| 83 | } | ||
| 84 | |||
| 85 | int tegra_dc_ext_set_cursor_image(struct tegra_dc_ext_user *user, | ||
| 86 | struct tegra_dc_ext_cursor_image *args) | ||
| 87 | { | ||
| 88 | struct tegra_dc_ext *ext = user->ext; | ||
| 89 | struct tegra_dc *dc = ext->dc; | ||
| 90 | struct nvmap_handle_ref *handle, *old_handle; | ||
| 91 | dma_addr_t phys_addr; | ||
| 92 | u32 size; | ||
| 93 | int ret; | ||
| 94 | |||
| 95 | if (!user->nvmap) | ||
| 96 | return -EFAULT; | ||
| 97 | |||
| 98 | size = args->flags & (TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_32x32 | | ||
| 99 | TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_64x64); | ||
| 100 | |||
| 101 | if (size != TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_32x32 && | ||
| 102 | size != TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_64x64) | ||
| 103 | return -EINVAL; | ||
| 104 | |||
| 105 | mutex_lock(&ext->cursor.lock); | ||
| 106 | |||
| 107 | if (ext->cursor.user != user) { | ||
| 108 | ret = -EACCES; | ||
| 109 | goto unlock; | ||
| 110 | } | ||
| 111 | |||
| 112 | if (!ext->enabled) { | ||
| 113 | ret = -ENXIO; | ||
| 114 | goto unlock; | ||
| 115 | } | ||
| 116 | |||
| 117 | old_handle = ext->cursor.cur_handle; | ||
| 118 | |||
| 119 | ret = tegra_dc_ext_pin_window(user, args->buff_id, &handle, &phys_addr); | ||
| 120 | if (ret) | ||
| 121 | goto unlock; | ||
| 122 | |||
| 123 | ext->cursor.cur_handle = handle; | ||
| 124 | |||
| 125 | mutex_lock(&dc->lock); | ||
| 126 | |||
| 127 | set_cursor_image_hw(dc, args, phys_addr); | ||
| 128 | |||
| 129 | tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); | ||
| 130 | tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); | ||
| 131 | |||
| 132 | /* XXX sync here? */ | ||
| 133 | |||
| 134 | mutex_unlock(&dc->lock); | ||
| 135 | |||
| 136 | mutex_unlock(&ext->cursor.lock); | ||
| 137 | |||
| 138 | if (old_handle) { | ||
| 139 | nvmap_unpin(ext->nvmap, old_handle); | ||
| 140 | nvmap_free(ext->nvmap, old_handle); | ||
| 141 | } | ||
| 142 | |||
| 143 | return 0; | ||
| 144 | |||
| 145 | unlock: | ||
| 146 | mutex_unlock(&ext->cursor.lock); | ||
| 147 | |||
| 148 | return ret; | ||
| 149 | } | ||
| 150 | |||
| 151 | int tegra_dc_ext_set_cursor(struct tegra_dc_ext_user *user, | ||
| 152 | struct tegra_dc_ext_cursor *args) | ||
| 153 | { | ||
| 154 | struct tegra_dc_ext *ext = user->ext; | ||
| 155 | struct tegra_dc *dc = ext->dc; | ||
| 156 | u32 win_options; | ||
| 157 | bool enable; | ||
| 158 | int ret; | ||
| 159 | |||
| 160 | mutex_lock(&ext->cursor.lock); | ||
| 161 | |||
| 162 | if (ext->cursor.user != user) { | ||
| 163 | ret = -EACCES; | ||
| 164 | goto unlock; | ||
| 165 | } | ||
| 166 | |||
| 167 | if (!ext->enabled) { | ||
| 168 | ret = -ENXIO; | ||
| 169 | goto unlock; | ||
| 170 | } | ||
| 171 | |||
| 172 | enable = !!(args->flags & TEGRA_DC_EXT_CURSOR_FLAGS_VISIBLE); | ||
| 173 | |||
| 174 | mutex_lock(&dc->lock); | ||
| 175 | |||
| 176 | win_options = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); | ||
| 177 | if (!!(win_options & CURSOR_ENABLE) != enable) { | ||
| 178 | win_options &= ~CURSOR_ENABLE; | ||
| 179 | if (enable) | ||
| 180 | win_options |= CURSOR_ENABLE; | ||
| 181 | tegra_dc_writel(dc, win_options, DC_DISP_DISP_WIN_OPTIONS); | ||
| 182 | } | ||
| 183 | |||
| 184 | tegra_dc_writel(dc, CURSOR_POSITION(args->x, args->y), | ||
| 185 | DC_DISP_CURSOR_POSITION); | ||
| 186 | |||
| 187 | tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); | ||
| 188 | tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); | ||
| 189 | |||
| 190 | /* TODO: need to sync here? hopefully can avoid this, but need to | ||
| 191 | * figure out interaction w/ rest of GENERAL_ACT_REQ */ | ||
| 192 | |||
| 193 | mutex_unlock(&dc->lock); | ||
| 194 | |||
| 195 | mutex_unlock(&ext->cursor.lock); | ||
| 196 | |||
| 197 | return 0; | ||
| 198 | |||
| 199 | unlock: | ||
| 200 | mutex_unlock(&ext->cursor.lock); | ||
| 201 | |||
| 202 | return ret; | ||
| 203 | } | ||
diff --git a/drivers/video/tegra/dc/ext/dev.c b/drivers/video/tegra/dc/ext/dev.c new file mode 100644 index 00000000000..04553e77839 --- /dev/null +++ b/drivers/video/tegra/dc/ext/dev.c | |||
| @@ -0,0 +1,975 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/dev.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2011-2012, NVIDIA Corporation | ||
| 5 | * | ||
| 6 | * Author: Robert Morell <rmorell@nvidia.com> | ||
| 7 | * Some code based on fbdev extensions written by: | ||
| 8 | * Erik Gilling <konkers@android.com> | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License as published by | ||
| 12 | * the Free Software Foundation; either version 2 of the License, or | ||
| 13 | * (at your option) any later version. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 16 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 17 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 18 | * more details. | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <linux/file.h> | ||
| 22 | #include <linux/fs.h> | ||
| 23 | #include <linux/uaccess.h> | ||
| 24 | #include <linux/slab.h> | ||
| 25 | #include <linux/workqueue.h> | ||
| 26 | |||
| 27 | #include <video/tegra_dc_ext.h> | ||
| 28 | |||
| 29 | #include <mach/dc.h> | ||
| 30 | #include <mach/nvmap.h> | ||
| 31 | #include <mach/tegra_dc_ext.h> | ||
| 32 | |||
| 33 | /* XXX ew */ | ||
| 34 | #include "../dc_priv.h" | ||
| 35 | /* XXX ew 2 */ | ||
| 36 | #include "../../host/dev.h" | ||
| 37 | /* XXX ew 3 */ | ||
| 38 | #include "../../nvmap/nvmap.h" | ||
| 39 | #include "tegra_dc_ext_priv.h" | ||
| 40 | |||
| 41 | int tegra_dc_ext_devno; | ||
| 42 | struct class *tegra_dc_ext_class; | ||
| 43 | static int head_count; | ||
| 44 | |||
| 45 | struct tegra_dc_ext_flip_win { | ||
| 46 | struct tegra_dc_ext_flip_windowattr attr; | ||
| 47 | struct nvmap_handle_ref *handle[TEGRA_DC_NUM_PLANES]; | ||
| 48 | dma_addr_t phys_addr; | ||
| 49 | dma_addr_t phys_addr_u; | ||
| 50 | dma_addr_t phys_addr_v; | ||
| 51 | u32 syncpt_max; | ||
| 52 | }; | ||
| 53 | |||
| 54 | struct tegra_dc_ext_flip_data { | ||
| 55 | struct tegra_dc_ext *ext; | ||
| 56 | struct work_struct work; | ||
| 57 | struct tegra_dc_ext_flip_win win[DC_N_WINDOWS]; | ||
| 58 | }; | ||
| 59 | |||
| 60 | int tegra_dc_ext_get_num_outputs(void) | ||
| 61 | { | ||
| 62 | /* TODO: decouple output count from head count */ | ||
| 63 | return head_count; | ||
| 64 | } | ||
| 65 | |||
| 66 | static int tegra_dc_ext_set_nvmap_fd(struct tegra_dc_ext_user *user, | ||
| 67 | int fd) | ||
| 68 | { | ||
| 69 | struct nvmap_client *nvmap = NULL; | ||
| 70 | |||
| 71 | if (fd >= 0) { | ||
| 72 | nvmap = nvmap_client_get_file(fd); | ||
| 73 | if (IS_ERR(nvmap)) | ||
| 74 | return PTR_ERR(nvmap); | ||
| 75 | } | ||
| 76 | |||
| 77 | if (user->nvmap) | ||
| 78 | nvmap_client_put(user->nvmap); | ||
| 79 | |||
| 80 | user->nvmap = nvmap; | ||
| 81 | |||
| 82 | return 0; | ||
| 83 | } | ||
| 84 | |||
| 85 | static int tegra_dc_ext_get_window(struct tegra_dc_ext_user *user, | ||
| 86 | unsigned int n) | ||
| 87 | { | ||
| 88 | struct tegra_dc_ext *ext = user->ext; | ||
| 89 | struct tegra_dc_ext_win *win; | ||
| 90 | int ret = 0; | ||
| 91 | |||
| 92 | if (n >= DC_N_WINDOWS) | ||
| 93 | return -EINVAL; | ||
| 94 | |||
| 95 | win = &ext->win[n]; | ||
| 96 | |||
| 97 | mutex_lock(&win->lock); | ||
| 98 | |||
| 99 | if (!win->user) | ||
| 100 | win->user = user; | ||
| 101 | else if (win->user != user) | ||
| 102 | ret = -EBUSY; | ||
| 103 | |||
| 104 | mutex_unlock(&win->lock); | ||
| 105 | |||
| 106 | return ret; | ||
| 107 | } | ||
| 108 | |||
| 109 | static int tegra_dc_ext_put_window(struct tegra_dc_ext_user *user, | ||
| 110 | unsigned int n) | ||
| 111 | { | ||
| 112 | struct tegra_dc_ext *ext = user->ext; | ||
| 113 | struct tegra_dc_ext_win *win; | ||
| 114 | int ret = 0; | ||
| 115 | |||
| 116 | if (n >= DC_N_WINDOWS) | ||
| 117 | return -EINVAL; | ||
| 118 | |||
| 119 | win = &ext->win[n]; | ||
| 120 | |||
| 121 | mutex_lock(&win->lock); | ||
| 122 | |||
| 123 | if (win->user == user) { | ||
| 124 | flush_workqueue(win->flip_wq); | ||
| 125 | win->user = 0; | ||
| 126 | } else { | ||
| 127 | ret = -EACCES; | ||
| 128 | } | ||
| 129 | |||
| 130 | mutex_unlock(&win->lock); | ||
| 131 | |||
| 132 | return ret; | ||
| 133 | } | ||
| 134 | |||
| 135 | static void set_enable(struct tegra_dc_ext *ext, bool en) | ||
| 136 | { | ||
| 137 | int i; | ||
| 138 | |||
| 139 | /* | ||
| 140 | * Take all locks to make sure any flip requests or cursor moves are | ||
| 141 | * out of their critical sections | ||
| 142 | */ | ||
| 143 | for (i = 0; i < ext->dc->n_windows; i++) | ||
| 144 | mutex_lock(&ext->win[i].lock); | ||
| 145 | mutex_lock(&ext->cursor.lock); | ||
| 146 | |||
| 147 | ext->enabled = en; | ||
| 148 | |||
| 149 | mutex_unlock(&ext->cursor.lock); | ||
| 150 | for (i = ext->dc->n_windows - 1; i >= 0 ; i--) | ||
| 151 | mutex_unlock(&ext->win[i].lock); | ||
| 152 | } | ||
| 153 | |||
| 154 | void tegra_dc_ext_enable(struct tegra_dc_ext *ext) | ||
| 155 | { | ||
| 156 | set_enable(ext, true); | ||
| 157 | } | ||
| 158 | |||
| 159 | void tegra_dc_ext_disable(struct tegra_dc_ext *ext) | ||
| 160 | { | ||
| 161 | int i; | ||
| 162 | set_enable(ext, false); | ||
| 163 | |||
| 164 | /* | ||
| 165 | * Flush the flip queue -- note that this must be called with dc->lock | ||
| 166 | * unlocked or else it will hang. | ||
| 167 | */ | ||
| 168 | for (i = 0; i < ext->dc->n_windows; i++) { | ||
| 169 | struct tegra_dc_ext_win *win = &ext->win[i]; | ||
| 170 | |||
| 171 | flush_workqueue(win->flip_wq); | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | static int tegra_dc_ext_set_windowattr(struct tegra_dc_ext *ext, | ||
| 176 | struct tegra_dc_win *win, | ||
| 177 | const struct tegra_dc_ext_flip_win *flip_win) | ||
| 178 | { | ||
| 179 | struct tegra_dc_ext_win *ext_win = &ext->win[win->idx]; | ||
| 180 | |||
| 181 | if (flip_win->handle[TEGRA_DC_Y] == NULL) { | ||
| 182 | win->flags = 0; | ||
| 183 | memset(ext_win->cur_handle, 0, sizeof(ext_win->cur_handle)); | ||
| 184 | return 0; | ||
| 185 | } | ||
| 186 | |||
| 187 | win->flags = TEGRA_WIN_FLAG_ENABLED; | ||
| 188 | if (flip_win->attr.blend == TEGRA_DC_EXT_BLEND_PREMULT) | ||
| 189 | win->flags |= TEGRA_WIN_FLAG_BLEND_PREMULT; | ||
| 190 | else if (flip_win->attr.blend == TEGRA_DC_EXT_BLEND_COVERAGE) | ||
| 191 | win->flags |= TEGRA_WIN_FLAG_BLEND_COVERAGE; | ||
| 192 | if (flip_win->attr.flags & TEGRA_DC_EXT_FLIP_FLAG_TILED) | ||
| 193 | win->flags |= TEGRA_WIN_FLAG_TILED; | ||
| 194 | if (flip_win->attr.flags & TEGRA_DC_EXT_FLIP_FLAG_INVERT_H) | ||
| 195 | win->flags |= TEGRA_WIN_FLAG_INVERT_H; | ||
| 196 | if (flip_win->attr.flags & TEGRA_DC_EXT_FLIP_FLAG_INVERT_V) | ||
| 197 | win->flags |= TEGRA_WIN_FLAG_INVERT_V; | ||
| 198 | win->fmt = flip_win->attr.pixformat; | ||
| 199 | win->x.full = flip_win->attr.x; | ||
| 200 | win->y.full = flip_win->attr.y; | ||
| 201 | win->w.full = flip_win->attr.w; | ||
| 202 | win->h.full = flip_win->attr.h; | ||
| 203 | /* XXX verify that this doesn't go outside display's active region */ | ||
| 204 | win->out_x = flip_win->attr.out_x; | ||
| 205 | win->out_y = flip_win->attr.out_y; | ||
| 206 | win->out_w = flip_win->attr.out_w; | ||
| 207 | win->out_h = flip_win->attr.out_h; | ||
| 208 | win->z = flip_win->attr.z; | ||
| 209 | memcpy(ext_win->cur_handle, flip_win->handle, | ||
| 210 | sizeof(ext_win->cur_handle)); | ||
| 211 | |||
| 212 | /* XXX verify that this won't read outside of the surface */ | ||
| 213 | win->phys_addr = flip_win->phys_addr + flip_win->attr.offset; | ||
| 214 | |||
| 215 | win->phys_addr_u = flip_win->handle[TEGRA_DC_U] ? | ||
| 216 | flip_win->phys_addr_u : flip_win->phys_addr; | ||
| 217 | win->phys_addr_u += flip_win->attr.offset_u; | ||
| 218 | |||
| 219 | win->phys_addr_v = flip_win->handle[TEGRA_DC_V] ? | ||
| 220 | flip_win->phys_addr_v : flip_win->phys_addr; | ||
| 221 | win->phys_addr_v += flip_win->attr.offset_v; | ||
| 222 | |||
| 223 | win->stride = flip_win->attr.stride; | ||
| 224 | win->stride_uv = flip_win->attr.stride_uv; | ||
| 225 | |||
| 226 | if ((s32)flip_win->attr.pre_syncpt_id >= 0) { | ||
| 227 | nvhost_syncpt_wait_timeout( | ||
| 228 | &nvhost_get_host(ext->dc->ndev)->syncpt, | ||
| 229 | flip_win->attr.pre_syncpt_id, | ||
| 230 | flip_win->attr.pre_syncpt_val, | ||
| 231 | msecs_to_jiffies(500), NULL); | ||
| 232 | } | ||
| 233 | |||
| 234 | |||
| 235 | return 0; | ||
| 236 | } | ||
| 237 | |||
| 238 | static void tegra_dc_ext_flip_worker(struct work_struct *work) | ||
| 239 | { | ||
| 240 | struct tegra_dc_ext_flip_data *data = | ||
| 241 | container_of(work, struct tegra_dc_ext_flip_data, work); | ||
| 242 | struct tegra_dc_ext *ext = data->ext; | ||
| 243 | struct tegra_dc_win *wins[DC_N_WINDOWS]; | ||
| 244 | struct nvmap_handle_ref *unpin_handles[DC_N_WINDOWS * | ||
| 245 | TEGRA_DC_NUM_PLANES]; | ||
| 246 | struct nvmap_handle_ref *old_handle; | ||
| 247 | int i, nr_unpin = 0, nr_win = 0; | ||
| 248 | bool skip_flip = false; | ||
| 249 | |||
| 250 | for (i = 0; i < DC_N_WINDOWS; i++) { | ||
| 251 | struct tegra_dc_ext_flip_win *flip_win = &data->win[i]; | ||
| 252 | int index = flip_win->attr.index; | ||
| 253 | struct tegra_dc_win *win; | ||
| 254 | struct tegra_dc_ext_win *ext_win; | ||
| 255 | |||
| 256 | if (index < 0) | ||
| 257 | continue; | ||
| 258 | |||
| 259 | win = tegra_dc_get_window(ext->dc, index); | ||
| 260 | ext_win = &ext->win[index]; | ||
| 261 | |||
| 262 | if (!(atomic_dec_and_test(&ext_win->nr_pending_flips)) && | ||
| 263 | (flip_win->attr.flags & TEGRA_DC_EXT_FLIP_FLAG_CURSOR)) | ||
| 264 | skip_flip = true; | ||
| 265 | |||
| 266 | if (win->flags & TEGRA_WIN_FLAG_ENABLED) { | ||
| 267 | int j; | ||
| 268 | for (j = 0; j < TEGRA_DC_NUM_PLANES; j++) { | ||
| 269 | if (skip_flip) | ||
| 270 | old_handle = flip_win->handle[j]; | ||
| 271 | else | ||
| 272 | old_handle = ext_win->cur_handle[j]; | ||
| 273 | |||
| 274 | if (!old_handle) | ||
| 275 | continue; | ||
| 276 | |||
| 277 | unpin_handles[nr_unpin++] = old_handle; | ||
| 278 | } | ||
| 279 | } | ||
| 280 | |||
| 281 | if (!skip_flip) | ||
| 282 | tegra_dc_ext_set_windowattr(ext, win, &data->win[i]); | ||
| 283 | |||
| 284 | wins[nr_win++] = win; | ||
| 285 | } | ||
| 286 | |||
| 287 | if (!skip_flip) { | ||
| 288 | tegra_dc_update_windows(wins, nr_win); | ||
| 289 | /* TODO: implement swapinterval here */ | ||
| 290 | tegra_dc_sync_windows(wins, nr_win); | ||
| 291 | } | ||
| 292 | |||
| 293 | for (i = 0; i < DC_N_WINDOWS; i++) { | ||
| 294 | struct tegra_dc_ext_flip_win *flip_win = &data->win[i]; | ||
| 295 | int index = flip_win->attr.index; | ||
| 296 | |||
| 297 | if (index < 0) | ||
| 298 | continue; | ||
| 299 | |||
| 300 | tegra_dc_incr_syncpt_min(ext->dc, index, | ||
| 301 | flip_win->syncpt_max); | ||
| 302 | } | ||
| 303 | |||
| 304 | /* unpin and deref previous front buffers */ | ||
| 305 | for (i = 0; i < nr_unpin; i++) { | ||
| 306 | nvmap_unpin(ext->nvmap, unpin_handles[i]); | ||
| 307 | nvmap_free(ext->nvmap, unpin_handles[i]); | ||
| 308 | } | ||
| 309 | |||
| 310 | kfree(data); | ||
| 311 | } | ||
| 312 | |||
| 313 | static int lock_windows_for_flip(struct tegra_dc_ext_user *user, | ||
| 314 | struct tegra_dc_ext_flip *args) | ||
| 315 | { | ||
| 316 | struct tegra_dc_ext *ext = user->ext; | ||
| 317 | u8 idx_mask = 0; | ||
| 318 | int i; | ||
| 319 | |||
| 320 | for (i = 0; i < DC_N_WINDOWS; i++) { | ||
| 321 | int index = args->win[i].index; | ||
| 322 | |||
| 323 | if (index < 0) | ||
| 324 | continue; | ||
| 325 | |||
| 326 | idx_mask |= BIT(index); | ||
| 327 | } | ||
| 328 | |||
| 329 | for (i = 0; i < DC_N_WINDOWS; i++) { | ||
| 330 | struct tegra_dc_ext_win *win; | ||
| 331 | |||
| 332 | if (!(idx_mask & BIT(i))) | ||
| 333 | continue; | ||
| 334 | |||
| 335 | win = &ext->win[i]; | ||
| 336 | |||
| 337 | mutex_lock(&win->lock); | ||
| 338 | |||
| 339 | if (win->user != user) | ||
| 340 | goto fail_unlock; | ||
| 341 | } | ||
| 342 | |||
| 343 | return 0; | ||
| 344 | |||
| 345 | fail_unlock: | ||
| 346 | do { | ||
| 347 | if (!(idx_mask & BIT(i))) | ||
| 348 | continue; | ||
| 349 | |||
| 350 | mutex_unlock(&ext->win[i].lock); | ||
| 351 | } while (i--); | ||
| 352 | |||
| 353 | return -EACCES; | ||
| 354 | } | ||
| 355 | |||
| 356 | static void unlock_windows_for_flip(struct tegra_dc_ext_user *user, | ||
| 357 | struct tegra_dc_ext_flip *args) | ||
| 358 | { | ||
| 359 | struct tegra_dc_ext *ext = user->ext; | ||
| 360 | u8 idx_mask = 0; | ||
| 361 | int i; | ||
| 362 | |||
| 363 | for (i = 0; i < DC_N_WINDOWS; i++) { | ||
| 364 | int index = args->win[i].index; | ||
| 365 | |||
| 366 | if (index < 0) | ||
| 367 | continue; | ||
| 368 | |||
| 369 | idx_mask |= BIT(index); | ||
| 370 | } | ||
| 371 | |||
| 372 | for (i = DC_N_WINDOWS - 1; i >= 0; i--) { | ||
| 373 | if (!(idx_mask & BIT(i))) | ||
| 374 | continue; | ||
| 375 | |||
| 376 | mutex_unlock(&ext->win[i].lock); | ||
| 377 | } | ||
| 378 | } | ||
| 379 | |||
| 380 | static int sanitize_flip_args(struct tegra_dc_ext_user *user, | ||
| 381 | struct tegra_dc_ext_flip *args) | ||
| 382 | { | ||
| 383 | int i, used_windows = 0; | ||
| 384 | |||
| 385 | for (i = 0; i < DC_N_WINDOWS; i++) { | ||
| 386 | int index = args->win[i].index; | ||
| 387 | |||
| 388 | if (index < 0) | ||
| 389 | continue; | ||
| 390 | |||
| 391 | if (index >= DC_N_WINDOWS) | ||
| 392 | return -EINVAL; | ||
| 393 | |||
| 394 | if (used_windows & BIT(index)) | ||
| 395 | return -EINVAL; | ||
| 396 | |||
| 397 | used_windows |= BIT(index); | ||
| 398 | } | ||
| 399 | |||
| 400 | if (!used_windows) | ||
| 401 | return -EINVAL; | ||
| 402 | |||
| 403 | return 0; | ||
| 404 | } | ||
| 405 | |||
| 406 | static int tegra_dc_ext_flip(struct tegra_dc_ext_user *user, | ||
| 407 | struct tegra_dc_ext_flip *args) | ||
| 408 | { | ||
| 409 | struct tegra_dc_ext *ext = user->ext; | ||
| 410 | struct tegra_dc_ext_flip_data *data; | ||
| 411 | int work_index; | ||
| 412 | int i, ret = 0; | ||
| 413 | |||
| 414 | #ifdef CONFIG_ANDROID | ||
| 415 | int index_check[DC_N_WINDOWS] = {0, }; | ||
| 416 | int zero_index_id = 0; | ||
| 417 | #endif | ||
| 418 | |||
| 419 | if (!user->nvmap) | ||
| 420 | return -EFAULT; | ||
| 421 | |||
| 422 | ret = sanitize_flip_args(user, args); | ||
| 423 | if (ret) | ||
| 424 | return ret; | ||
| 425 | |||
| 426 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
| 427 | if (!data) | ||
| 428 | return -ENOMEM; | ||
| 429 | |||
| 430 | INIT_WORK(&data->work, tegra_dc_ext_flip_worker); | ||
| 431 | data->ext = ext; | ||
| 432 | |||
| 433 | #ifdef CONFIG_ANDROID | ||
| 434 | for (i = 0; i < DC_N_WINDOWS; i++) { | ||
| 435 | index_check[i] = args->win[i].index; | ||
| 436 | if (index_check[i] == 0) | ||
| 437 | zero_index_id = i; | ||
| 438 | } | ||
| 439 | |||
| 440 | if (index_check[DC_N_WINDOWS - 1] != 0) { | ||
| 441 | struct tegra_dc_ext_flip_windowattr win_temp; | ||
| 442 | win_temp = args->win[DC_N_WINDOWS - 1]; | ||
| 443 | args->win[DC_N_WINDOWS - 1] = args->win[zero_index_id]; | ||
| 444 | args->win[zero_index_id] = win_temp; | ||
| 445 | } | ||
| 446 | #endif | ||
| 447 | |||
| 448 | for (i = 0; i < DC_N_WINDOWS; i++) { | ||
| 449 | struct tegra_dc_ext_flip_win *flip_win = &data->win[i]; | ||
| 450 | int index = args->win[i].index; | ||
| 451 | |||
| 452 | memcpy(&flip_win->attr, &args->win[i], sizeof(flip_win->attr)); | ||
| 453 | |||
| 454 | if (index < 0) | ||
| 455 | continue; | ||
| 456 | |||
| 457 | ret = tegra_dc_ext_pin_window(user, flip_win->attr.buff_id, | ||
| 458 | &flip_win->handle[TEGRA_DC_Y], | ||
| 459 | &flip_win->phys_addr); | ||
| 460 | if (ret) | ||
| 461 | goto fail_pin; | ||
| 462 | |||
| 463 | if (flip_win->attr.buff_id_u) { | ||
| 464 | ret = tegra_dc_ext_pin_window(user, | ||
| 465 | flip_win->attr.buff_id_u, | ||
| 466 | &flip_win->handle[TEGRA_DC_U], | ||
| 467 | &flip_win->phys_addr_u); | ||
| 468 | if (ret) | ||
| 469 | goto fail_pin; | ||
| 470 | } else { | ||
| 471 | flip_win->handle[TEGRA_DC_U] = NULL; | ||
| 472 | flip_win->phys_addr_u = 0; | ||
| 473 | } | ||
| 474 | |||
| 475 | if (flip_win->attr.buff_id_v) { | ||
| 476 | ret = tegra_dc_ext_pin_window(user, | ||
| 477 | flip_win->attr.buff_id_v, | ||
| 478 | &flip_win->handle[TEGRA_DC_V], | ||
| 479 | &flip_win->phys_addr_v); | ||
| 480 | if (ret) | ||
| 481 | goto fail_pin; | ||
| 482 | } else { | ||
| 483 | flip_win->handle[TEGRA_DC_V] = NULL; | ||
| 484 | flip_win->phys_addr_v = 0; | ||
| 485 | } | ||
| 486 | } | ||
| 487 | |||
| 488 | ret = lock_windows_for_flip(user, args); | ||
| 489 | if (ret) | ||
| 490 | goto fail_pin; | ||
| 491 | |||
| 492 | if (!ext->enabled) { | ||
| 493 | ret = -ENXIO; | ||
| 494 | goto unlock; | ||
| 495 | } | ||
| 496 | |||
| 497 | for (i = 0; i < DC_N_WINDOWS; i++) { | ||
| 498 | u32 syncpt_max; | ||
| 499 | int index = args->win[i].index; | ||
| 500 | struct tegra_dc_win *win; | ||
| 501 | struct tegra_dc_ext_win *ext_win; | ||
| 502 | |||
| 503 | if (index < 0) | ||
| 504 | continue; | ||
| 505 | |||
| 506 | win = tegra_dc_get_window(ext->dc, index); | ||
| 507 | ext_win = &ext->win[index]; | ||
| 508 | |||
| 509 | syncpt_max = tegra_dc_incr_syncpt_max(ext->dc, index); | ||
| 510 | |||
| 511 | data->win[i].syncpt_max = syncpt_max; | ||
| 512 | |||
| 513 | /* | ||
| 514 | * Any of these windows' syncpoints should be equivalent for | ||
| 515 | * the client, so we just send back an arbitrary one of them | ||
| 516 | */ | ||
| 517 | args->post_syncpt_val = syncpt_max; | ||
| 518 | args->post_syncpt_id = tegra_dc_get_syncpt_id(ext->dc, index); | ||
| 519 | work_index = index; | ||
| 520 | |||
| 521 | atomic_inc(&ext->win[work_index].nr_pending_flips); | ||
| 522 | } | ||
| 523 | queue_work(ext->win[work_index].flip_wq, &data->work); | ||
| 524 | |||
| 525 | unlock_windows_for_flip(user, args); | ||
| 526 | |||
| 527 | return 0; | ||
| 528 | |||
| 529 | unlock: | ||
| 530 | unlock_windows_for_flip(user, args); | ||
| 531 | |||
| 532 | fail_pin: | ||
| 533 | for (i = 0; i < DC_N_WINDOWS; i++) { | ||
| 534 | int j; | ||
| 535 | for (j = 0; j < TEGRA_DC_NUM_PLANES; j++) { | ||
| 536 | if (!data->win[i].handle[j]) | ||
| 537 | continue; | ||
| 538 | |||
| 539 | nvmap_unpin(ext->nvmap, data->win[i].handle[j]); | ||
| 540 | nvmap_free(ext->nvmap, data->win[i].handle[j]); | ||
| 541 | } | ||
| 542 | } | ||
| 543 | kfree(data); | ||
| 544 | |||
| 545 | return ret; | ||
| 546 | } | ||
| 547 | |||
| 548 | static int tegra_dc_ext_set_csc(struct tegra_dc_ext_user *user, | ||
| 549 | struct tegra_dc_ext_csc *new_csc) | ||
| 550 | { | ||
| 551 | unsigned int index = new_csc->win_index; | ||
| 552 | struct tegra_dc *dc = user->ext->dc; | ||
| 553 | struct tegra_dc_ext_win *ext_win; | ||
| 554 | struct tegra_dc_csc *csc; | ||
| 555 | |||
| 556 | if (index >= DC_N_WINDOWS) | ||
| 557 | return -EINVAL; | ||
| 558 | |||
| 559 | ext_win = &user->ext->win[index]; | ||
| 560 | csc = &dc->windows[index].csc; | ||
| 561 | |||
| 562 | mutex_lock(&ext_win->lock); | ||
| 563 | |||
| 564 | if (ext_win->user != user) { | ||
| 565 | mutex_unlock(&ext_win->lock); | ||
| 566 | return -EACCES; | ||
| 567 | } | ||
| 568 | |||
| 569 | csc->yof = new_csc->yof; | ||
| 570 | csc->kyrgb = new_csc->kyrgb; | ||
| 571 | csc->kur = new_csc->kur; | ||
| 572 | csc->kvr = new_csc->kvr; | ||
| 573 | csc->kug = new_csc->kug; | ||
| 574 | csc->kvg = new_csc->kvg; | ||
| 575 | csc->kub = new_csc->kub; | ||
| 576 | csc->kvb = new_csc->kvb; | ||
| 577 | |||
| 578 | tegra_dc_update_csc(dc, index); | ||
| 579 | |||
| 580 | mutex_unlock(&ext_win->lock); | ||
| 581 | |||
| 582 | return 0; | ||
| 583 | } | ||
| 584 | |||
| 585 | static int set_lut_channel(u16 *channel_from_user, | ||
| 586 | u8 *channel_to, | ||
| 587 | u32 start, | ||
| 588 | u32 len) | ||
| 589 | { | ||
| 590 | int i; | ||
| 591 | u16 lut16bpp[256]; | ||
| 592 | |||
| 593 | if (channel_from_user) { | ||
| 594 | if (copy_from_user(lut16bpp, channel_from_user, len<<1)) | ||
| 595 | return 1; | ||
| 596 | |||
| 597 | for (i = 0; i < len; i++) | ||
| 598 | channel_to[start+i] = lut16bpp[i]>>8; | ||
| 599 | } else { | ||
| 600 | for (i = 0; i < len; i++) | ||
| 601 | channel_to[start+i] = start+i; | ||
| 602 | } | ||
| 603 | |||
| 604 | return 0; | ||
| 605 | } | ||
| 606 | |||
| 607 | static int tegra_dc_ext_set_lut(struct tegra_dc_ext_user *user, | ||
| 608 | struct tegra_dc_ext_lut *new_lut) | ||
| 609 | { | ||
| 610 | int err; | ||
| 611 | unsigned int index = new_lut->win_index; | ||
| 612 | u32 start = new_lut->start; | ||
| 613 | u32 len = new_lut->len; | ||
| 614 | |||
| 615 | struct tegra_dc *dc = user->ext->dc; | ||
| 616 | struct tegra_dc_ext_win *ext_win; | ||
| 617 | struct tegra_dc_lut *lut; | ||
| 618 | |||
| 619 | if (index >= DC_N_WINDOWS) | ||
| 620 | return -EINVAL; | ||
| 621 | |||
| 622 | if ((start >= 256) || (len > 256) || ((start + len) > 256)) | ||
| 623 | return -EINVAL; | ||
| 624 | |||
| 625 | ext_win = &user->ext->win[index]; | ||
| 626 | lut = &dc->windows[index].lut; | ||
| 627 | |||
| 628 | mutex_lock(&ext_win->lock); | ||
| 629 | |||
| 630 | if (ext_win->user != user) { | ||
| 631 | mutex_unlock(&ext_win->lock); | ||
| 632 | return -EACCES; | ||
| 633 | } | ||
| 634 | |||
| 635 | err = set_lut_channel(new_lut->r, lut->r, start, len) | | ||
| 636 | set_lut_channel(new_lut->g, lut->g, start, len) | | ||
| 637 | set_lut_channel(new_lut->b, lut->b, start, len); | ||
| 638 | |||
| 639 | if (err) { | ||
| 640 | mutex_unlock(&ext_win->lock); | ||
| 641 | return -EFAULT; | ||
| 642 | } | ||
| 643 | |||
| 644 | tegra_dc_update_lut(dc, index, | ||
| 645 | new_lut->flags & TEGRA_DC_EXT_LUT_FLAGS_FBOVERRIDE); | ||
| 646 | |||
| 647 | mutex_unlock(&ext_win->lock); | ||
| 648 | |||
| 649 | return 0; | ||
| 650 | } | ||
| 651 | |||
| 652 | static u32 tegra_dc_ext_get_vblank_syncpt(struct tegra_dc_ext_user *user) | ||
| 653 | { | ||
| 654 | struct tegra_dc *dc = user->ext->dc; | ||
| 655 | |||
| 656 | return dc->vblank_syncpt; | ||
| 657 | } | ||
| 658 | |||
| 659 | static int tegra_dc_ext_get_status(struct tegra_dc_ext_user *user, | ||
| 660 | struct tegra_dc_ext_status *status) | ||
| 661 | { | ||
| 662 | struct tegra_dc *dc = user->ext->dc; | ||
| 663 | |||
| 664 | memset(status, 0, sizeof(*status)); | ||
| 665 | |||
| 666 | if (dc->enabled) | ||
| 667 | status->flags |= TEGRA_DC_EXT_FLAGS_ENABLED; | ||
| 668 | |||
| 669 | return 0; | ||
| 670 | } | ||
| 671 | |||
| 672 | static long tegra_dc_ioctl(struct file *filp, unsigned int cmd, | ||
| 673 | unsigned long arg) | ||
| 674 | { | ||
| 675 | void __user *user_arg = (void __user *)arg; | ||
| 676 | struct tegra_dc_ext_user *user = filp->private_data; | ||
| 677 | |||
| 678 | switch (cmd) { | ||
| 679 | case TEGRA_DC_EXT_SET_NVMAP_FD: | ||
| 680 | return tegra_dc_ext_set_nvmap_fd(user, arg); | ||
| 681 | |||
| 682 | case TEGRA_DC_EXT_GET_WINDOW: | ||
| 683 | return tegra_dc_ext_get_window(user, arg); | ||
| 684 | case TEGRA_DC_EXT_PUT_WINDOW: | ||
| 685 | return tegra_dc_ext_put_window(user, arg); | ||
| 686 | |||
| 687 | case TEGRA_DC_EXT_FLIP: | ||
| 688 | { | ||
| 689 | struct tegra_dc_ext_flip args; | ||
| 690 | int ret; | ||
| 691 | |||
| 692 | if (copy_from_user(&args, user_arg, sizeof(args))) | ||
| 693 | return -EFAULT; | ||
| 694 | |||
| 695 | ret = tegra_dc_ext_flip(user, &args); | ||
| 696 | |||
| 697 | if (copy_to_user(user_arg, &args, sizeof(args))) | ||
| 698 | return -EFAULT; | ||
| 699 | |||
| 700 | return ret; | ||
| 701 | } | ||
| 702 | |||
| 703 | case TEGRA_DC_EXT_GET_CURSOR: | ||
| 704 | return tegra_dc_ext_get_cursor(user); | ||
| 705 | case TEGRA_DC_EXT_PUT_CURSOR: | ||
| 706 | return tegra_dc_ext_put_cursor(user); | ||
| 707 | case TEGRA_DC_EXT_SET_CURSOR_IMAGE: | ||
| 708 | { | ||
| 709 | struct tegra_dc_ext_cursor_image args; | ||
| 710 | |||
| 711 | if (copy_from_user(&args, user_arg, sizeof(args))) | ||
| 712 | return -EFAULT; | ||
| 713 | |||
| 714 | return tegra_dc_ext_set_cursor_image(user, &args); | ||
| 715 | } | ||
| 716 | case TEGRA_DC_EXT_SET_CURSOR: | ||
| 717 | { | ||
| 718 | struct tegra_dc_ext_cursor args; | ||
| 719 | |||
| 720 | if (copy_from_user(&args, user_arg, sizeof(args))) | ||
| 721 | return -EFAULT; | ||
| 722 | |||
| 723 | return tegra_dc_ext_set_cursor(user, &args); | ||
| 724 | } | ||
| 725 | |||
| 726 | case TEGRA_DC_EXT_SET_CSC: | ||
| 727 | { | ||
| 728 | struct tegra_dc_ext_csc args; | ||
| 729 | |||
| 730 | if (copy_from_user(&args, user_arg, sizeof(args))) | ||
| 731 | return -EFAULT; | ||
| 732 | |||
| 733 | return tegra_dc_ext_set_csc(user, &args); | ||
| 734 | } | ||
| 735 | |||
| 736 | case TEGRA_DC_EXT_GET_VBLANK_SYNCPT: | ||
| 737 | { | ||
| 738 | u32 syncpt = tegra_dc_ext_get_vblank_syncpt(user); | ||
| 739 | |||
| 740 | if (copy_to_user(user_arg, &syncpt, sizeof(syncpt))) | ||
| 741 | return -EFAULT; | ||
| 742 | |||
| 743 | return 0; | ||
| 744 | } | ||
| 745 | |||
| 746 | case TEGRA_DC_EXT_GET_STATUS: | ||
| 747 | { | ||
| 748 | struct tegra_dc_ext_status args; | ||
| 749 | int ret; | ||
| 750 | |||
| 751 | ret = tegra_dc_ext_get_status(user, &args); | ||
| 752 | |||
| 753 | if (copy_to_user(user_arg, &args, sizeof(args))) | ||
| 754 | return -EFAULT; | ||
| 755 | |||
| 756 | return ret; | ||
| 757 | } | ||
| 758 | |||
| 759 | case TEGRA_DC_EXT_SET_LUT: | ||
| 760 | { | ||
| 761 | struct tegra_dc_ext_lut args; | ||
| 762 | |||
| 763 | if (copy_from_user(&args, user_arg, sizeof(args))) | ||
| 764 | return -EFAULT; | ||
| 765 | |||
| 766 | return tegra_dc_ext_set_lut(user, &args); | ||
| 767 | } | ||
| 768 | |||
| 769 | default: | ||
| 770 | return -EINVAL; | ||
| 771 | } | ||
| 772 | } | ||
| 773 | |||
| 774 | static int tegra_dc_open(struct inode *inode, struct file *filp) | ||
| 775 | { | ||
| 776 | struct tegra_dc_ext_user *user; | ||
| 777 | struct tegra_dc_ext *ext; | ||
| 778 | |||
| 779 | user = kzalloc(sizeof(*user), GFP_KERNEL); | ||
| 780 | if (!user) | ||
| 781 | return -ENOMEM; | ||
| 782 | |||
| 783 | ext = container_of(inode->i_cdev, struct tegra_dc_ext, cdev); | ||
| 784 | user->ext = ext; | ||
| 785 | |||
| 786 | filp->private_data = user; | ||
| 787 | |||
| 788 | return 0; | ||
| 789 | } | ||
| 790 | |||
| 791 | static int tegra_dc_release(struct inode *inode, struct file *filp) | ||
| 792 | { | ||
| 793 | struct tegra_dc_ext_user *user = filp->private_data; | ||
| 794 | struct tegra_dc_ext *ext = user->ext; | ||
| 795 | unsigned int i; | ||
| 796 | |||
| 797 | for (i = 0; i < DC_N_WINDOWS; i++) { | ||
| 798 | if (ext->win[i].user == user) | ||
| 799 | tegra_dc_ext_put_window(user, i); | ||
| 800 | } | ||
| 801 | if (ext->cursor.user == user) | ||
| 802 | tegra_dc_ext_put_cursor(user); | ||
| 803 | |||
| 804 | if (user->nvmap) | ||
| 805 | nvmap_client_put(user->nvmap); | ||
| 806 | |||
| 807 | kfree(user); | ||
| 808 | |||
| 809 | return 0; | ||
| 810 | } | ||
| 811 | |||
| 812 | static int tegra_dc_ext_setup_windows(struct tegra_dc_ext *ext) | ||
| 813 | { | ||
| 814 | int i, ret; | ||
| 815 | |||
| 816 | for (i = 0; i < ext->dc->n_windows; i++) { | ||
| 817 | struct tegra_dc_ext_win *win = &ext->win[i]; | ||
| 818 | char name[32]; | ||
| 819 | |||
| 820 | win->ext = ext; | ||
| 821 | win->idx = i; | ||
| 822 | |||
| 823 | snprintf(name, sizeof(name), "tegradc.%d/%c", | ||
| 824 | ext->dc->ndev->id, 'a' + i); | ||
| 825 | win->flip_wq = create_singlethread_workqueue(name); | ||
| 826 | if (!win->flip_wq) { | ||
| 827 | ret = -ENOMEM; | ||
| 828 | goto cleanup; | ||
| 829 | } | ||
| 830 | |||
| 831 | mutex_init(&win->lock); | ||
| 832 | } | ||
| 833 | |||
| 834 | return 0; | ||
| 835 | |||
| 836 | cleanup: | ||
| 837 | while (i--) { | ||
| 838 | struct tegra_dc_ext_win *win = &ext->win[i]; | ||
| 839 | destroy_workqueue(win->flip_wq); | ||
| 840 | } | ||
| 841 | |||
| 842 | return ret; | ||
| 843 | } | ||
| 844 | |||
| 845 | static const struct file_operations tegra_dc_devops = { | ||
| 846 | .owner = THIS_MODULE, | ||
| 847 | .open = tegra_dc_open, | ||
| 848 | .release = tegra_dc_release, | ||
| 849 | .unlocked_ioctl = tegra_dc_ioctl, | ||
| 850 | }; | ||
| 851 | |||
| 852 | struct tegra_dc_ext *tegra_dc_ext_register(struct nvhost_device *ndev, | ||
| 853 | struct tegra_dc *dc) | ||
| 854 | { | ||
| 855 | int ret; | ||
| 856 | struct tegra_dc_ext *ext; | ||
| 857 | int devno; | ||
| 858 | |||
| 859 | ext = kzalloc(sizeof(*ext), GFP_KERNEL); | ||
| 860 | if (!ext) | ||
| 861 | return ERR_PTR(-ENOMEM); | ||
| 862 | |||
| 863 | BUG_ON(!tegra_dc_ext_devno); | ||
| 864 | devno = tegra_dc_ext_devno + head_count + 1; | ||
| 865 | |||
| 866 | cdev_init(&ext->cdev, &tegra_dc_devops); | ||
| 867 | ext->cdev.owner = THIS_MODULE; | ||
| 868 | ret = cdev_add(&ext->cdev, devno, 1); | ||
| 869 | if (ret) { | ||
| 870 | dev_err(&ndev->dev, "Failed to create character device\n"); | ||
| 871 | goto cleanup_alloc; | ||
| 872 | } | ||
| 873 | |||
| 874 | ext->dev = device_create(tegra_dc_ext_class, | ||
| 875 | &ndev->dev, | ||
| 876 | devno, | ||
| 877 | NULL, | ||
| 878 | "tegra_dc_%d", | ||
| 879 | ndev->id); | ||
| 880 | |||
| 881 | if (IS_ERR(ext->dev)) { | ||
| 882 | ret = PTR_ERR(ext->dev); | ||
| 883 | goto cleanup_cdev; | ||
| 884 | } | ||
| 885 | |||
| 886 | ext->dc = dc; | ||
| 887 | |||
| 888 | ext->nvmap = nvmap_create_client(nvmap_dev, "tegra_dc_ext"); | ||
| 889 | if (!ext->nvmap) { | ||
| 890 | ret = -ENOMEM; | ||
| 891 | goto cleanup_device; | ||
| 892 | } | ||
| 893 | |||
| 894 | ret = tegra_dc_ext_setup_windows(ext); | ||
| 895 | if (ret) | ||
| 896 | goto cleanup_nvmap; | ||
| 897 | |||
| 898 | mutex_init(&ext->cursor.lock); | ||
| 899 | |||
| 900 | head_count++; | ||
| 901 | |||
| 902 | return ext; | ||
| 903 | |||
| 904 | cleanup_nvmap: | ||
| 905 | nvmap_client_put(ext->nvmap); | ||
| 906 | |||
| 907 | cleanup_device: | ||
| 908 | device_del(ext->dev); | ||
| 909 | |||
| 910 | cleanup_cdev: | ||
| 911 | cdev_del(&ext->cdev); | ||
| 912 | |||
| 913 | cleanup_alloc: | ||
| 914 | kfree(ext); | ||
| 915 | |||
| 916 | return ERR_PTR(ret); | ||
| 917 | } | ||
| 918 | |||
| 919 | void tegra_dc_ext_unregister(struct tegra_dc_ext *ext) | ||
| 920 | { | ||
| 921 | int i; | ||
| 922 | |||
| 923 | for (i = 0; i < ext->dc->n_windows; i++) { | ||
| 924 | struct tegra_dc_ext_win *win = &ext->win[i]; | ||
| 925 | |||
| 926 | flush_workqueue(win->flip_wq); | ||
| 927 | destroy_workqueue(win->flip_wq); | ||
| 928 | } | ||
| 929 | |||
| 930 | nvmap_client_put(ext->nvmap); | ||
| 931 | device_del(ext->dev); | ||
| 932 | cdev_del(&ext->cdev); | ||
| 933 | |||
| 934 | kfree(ext); | ||
| 935 | |||
| 936 | head_count--; | ||
| 937 | } | ||
| 938 | |||
| 939 | int __init tegra_dc_ext_module_init(void) | ||
| 940 | { | ||
| 941 | int ret; | ||
| 942 | |||
| 943 | tegra_dc_ext_class = class_create(THIS_MODULE, "tegra_dc_ext"); | ||
| 944 | if (!tegra_dc_ext_class) { | ||
| 945 | printk(KERN_ERR "tegra_dc_ext: failed to create class\n"); | ||
| 946 | return -ENOMEM; | ||
| 947 | } | ||
| 948 | |||
| 949 | /* Reserve one character device per head, plus the control device */ | ||
| 950 | ret = alloc_chrdev_region(&tegra_dc_ext_devno, | ||
| 951 | 0, TEGRA_MAX_DC + 1, | ||
| 952 | "tegra_dc_ext"); | ||
| 953 | if (ret) | ||
| 954 | goto cleanup_class; | ||
| 955 | |||
| 956 | ret = tegra_dc_ext_control_init(); | ||
| 957 | if (ret) | ||
| 958 | goto cleanup_region; | ||
| 959 | |||
| 960 | return 0; | ||
| 961 | |||
| 962 | cleanup_region: | ||
| 963 | unregister_chrdev_region(tegra_dc_ext_devno, TEGRA_MAX_DC); | ||
| 964 | |||
| 965 | cleanup_class: | ||
| 966 | class_destroy(tegra_dc_ext_class); | ||
| 967 | |||
| 968 | return ret; | ||
| 969 | } | ||
| 970 | |||
| 971 | void __exit tegra_dc_ext_module_exit(void) | ||
| 972 | { | ||
| 973 | unregister_chrdev_region(tegra_dc_ext_devno, TEGRA_MAX_DC); | ||
| 974 | class_destroy(tegra_dc_ext_class); | ||
| 975 | } | ||
diff --git a/drivers/video/tegra/dc/ext/events.c b/drivers/video/tegra/dc/ext/events.c new file mode 100644 index 00000000000..150a1501fce --- /dev/null +++ b/drivers/video/tegra/dc/ext/events.c | |||
| @@ -0,0 +1,197 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/ext/events.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2011, NVIDIA Corporation | ||
| 5 | * | ||
| 6 | * Author: Robert Morell <rmorell@nvidia.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License, or | ||
| 11 | * (at your option) any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 16 | * more details. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <linux/err.h> | ||
| 20 | #include <linux/fs.h> | ||
| 21 | #include <linux/list.h> | ||
| 22 | #include <linux/poll.h> | ||
| 23 | #include <linux/sched.h> | ||
| 24 | #include <linux/slab.h> | ||
| 25 | #include <linux/uaccess.h> | ||
| 26 | |||
| 27 | #include "tegra_dc_ext_priv.h" | ||
| 28 | |||
| 29 | static DECLARE_WAIT_QUEUE_HEAD(event_wait); | ||
| 30 | |||
| 31 | unsigned int tegra_dc_ext_event_poll(struct file *filp, poll_table *wait) | ||
| 32 | { | ||
| 33 | struct tegra_dc_ext_control_user *user = filp->private_data; | ||
| 34 | unsigned int mask = 0; | ||
| 35 | |||
| 36 | poll_wait(filp, &event_wait, wait); | ||
| 37 | |||
| 38 | if (atomic_read(&user->num_events)) | ||
| 39 | mask |= POLLIN; | ||
| 40 | |||
| 41 | return mask; | ||
| 42 | } | ||
| 43 | |||
| 44 | static int get_next_event(struct tegra_dc_ext_control_user *user, | ||
| 45 | struct tegra_dc_ext_event_list *event, | ||
| 46 | bool block) | ||
| 47 | { | ||
| 48 | struct list_head *list = &user->event_list; | ||
| 49 | struct tegra_dc_ext_event_list *next_event; | ||
| 50 | int ret; | ||
| 51 | |||
| 52 | if (block) { | ||
| 53 | ret = wait_event_interruptible(event_wait, | ||
| 54 | atomic_read(&user->num_events)); | ||
| 55 | |||
| 56 | if (unlikely(ret)) { | ||
| 57 | if (ret == -ERESTARTSYS) | ||
| 58 | return -EAGAIN; | ||
| 59 | return ret; | ||
| 60 | } | ||
| 61 | } else { | ||
| 62 | if (!atomic_read(&user->num_events)) | ||
| 63 | return 0; | ||
| 64 | } | ||
| 65 | |||
| 66 | mutex_lock(&user->lock); | ||
| 67 | |||
| 68 | BUG_ON(list_empty(list)); | ||
| 69 | next_event = list_first_entry(list, struct tegra_dc_ext_event_list, | ||
| 70 | list); | ||
| 71 | *event = *next_event; | ||
| 72 | list_del(&next_event->list); | ||
| 73 | kfree(next_event); | ||
| 74 | |||
| 75 | atomic_dec(&user->num_events); | ||
| 76 | |||
| 77 | mutex_unlock(&user->lock); | ||
| 78 | |||
| 79 | return 1; | ||
| 80 | } | ||
| 81 | |||
| 82 | ssize_t tegra_dc_ext_event_read(struct file *filp, char __user *buf, | ||
| 83 | size_t size, loff_t *ppos) | ||
| 84 | { | ||
| 85 | struct tegra_dc_ext_control_user *user = filp->private_data; | ||
| 86 | struct tegra_dc_ext_event_list event_elem; | ||
| 87 | struct tegra_dc_ext_event *event = &event_elem.event; | ||
| 88 | ssize_t retval = 0, to_copy, event_size, pending; | ||
| 89 | loff_t previously_copied = 0; | ||
| 90 | char *to_copy_ptr; | ||
| 91 | |||
| 92 | if (size == 0) | ||
| 93 | return 0; | ||
| 94 | |||
| 95 | if (user->partial_copy) { | ||
| 96 | /* | ||
| 97 | * We didn't transfer the entire event last time, need to | ||
| 98 | * finish it up | ||
| 99 | */ | ||
| 100 | event_elem = user->event_to_copy; | ||
| 101 | previously_copied = user->partial_copy; | ||
| 102 | } else { | ||
| 103 | /* Get the next event, if any */ | ||
| 104 | pending = get_next_event(user, &event_elem, | ||
| 105 | !(filp->f_flags & O_NONBLOCK)); | ||
| 106 | if (pending <= 0) | ||
| 107 | return pending; | ||
| 108 | } | ||
| 109 | |||
| 110 | /* Write the event to the user */ | ||
| 111 | event_size = sizeof(*event) + event->data_size; | ||
| 112 | BUG_ON(event_size <= previously_copied); | ||
| 113 | event_size -= previously_copied; | ||
| 114 | |||
| 115 | to_copy_ptr = (char *)event + previously_copied; | ||
| 116 | to_copy = min_t(ssize_t, size, event_size); | ||
| 117 | if (copy_to_user(buf, to_copy_ptr, to_copy)) { | ||
| 118 | retval = -EFAULT; | ||
| 119 | to_copy = 0; | ||
| 120 | } | ||
| 121 | |||
| 122 | /* Note that we currently only deliver one event at a time */ | ||
| 123 | |||
| 124 | if (event_size > to_copy) { | ||
| 125 | /* | ||
| 126 | * We were only able to copy part of this event. Stash it for | ||
| 127 | * next time. | ||
| 128 | */ | ||
| 129 | user->event_to_copy = event_elem; | ||
| 130 | user->partial_copy = previously_copied + to_copy; | ||
| 131 | } else { | ||
| 132 | user->partial_copy = 0; | ||
| 133 | } | ||
| 134 | |||
| 135 | return to_copy ? to_copy : retval; | ||
| 136 | } | ||
| 137 | |||
| 138 | static int tegra_dc_ext_queue_event(struct tegra_dc_ext_control *control, | ||
| 139 | struct tegra_dc_ext_event *event) | ||
| 140 | { | ||
| 141 | struct list_head *cur; | ||
| 142 | int retval = 0; | ||
| 143 | |||
| 144 | mutex_lock(&control->lock); | ||
| 145 | list_for_each(cur, &control->users) { | ||
| 146 | struct tegra_dc_ext_control_user *user; | ||
| 147 | struct tegra_dc_ext_event_list *ev_list; | ||
| 148 | |||
| 149 | user = container_of(cur, struct tegra_dc_ext_control_user, | ||
| 150 | list); | ||
| 151 | mutex_lock(&user->lock); | ||
| 152 | |||
| 153 | if (!(user->event_mask & event->type)) { | ||
| 154 | mutex_unlock(&user->lock); | ||
| 155 | continue; | ||
| 156 | } | ||
| 157 | |||
| 158 | ev_list = kmalloc(sizeof(*ev_list), GFP_KERNEL); | ||
| 159 | if (!ev_list) { | ||
| 160 | retval = -ENOMEM; | ||
| 161 | mutex_unlock(&user->lock); | ||
| 162 | continue; | ||
| 163 | } | ||
| 164 | |||
| 165 | memcpy(&ev_list->event, event, | ||
| 166 | sizeof(*event) + event->data_size); | ||
| 167 | |||
| 168 | list_add_tail(&ev_list->list, &user->event_list); | ||
| 169 | |||
| 170 | atomic_inc(&user->num_events); | ||
| 171 | |||
| 172 | mutex_unlock(&user->lock); | ||
| 173 | } | ||
| 174 | mutex_unlock(&control->lock); | ||
| 175 | |||
| 176 | /* Is it worth it to track waiters with more granularity? */ | ||
| 177 | wake_up(&event_wait); | ||
| 178 | |||
| 179 | return retval; | ||
| 180 | } | ||
| 181 | |||
| 182 | int tegra_dc_ext_queue_hotplug(struct tegra_dc_ext_control *control, int output) | ||
| 183 | { | ||
| 184 | struct { | ||
| 185 | struct tegra_dc_ext_event event; | ||
| 186 | struct tegra_dc_ext_control_event_hotplug hotplug; | ||
| 187 | } __packed pack; | ||
| 188 | |||
| 189 | pack.event.type = TEGRA_DC_EXT_EVENT_HOTPLUG; | ||
| 190 | pack.event.data_size = sizeof(pack.hotplug); | ||
| 191 | |||
| 192 | pack.hotplug.handle = output; | ||
| 193 | |||
| 194 | tegra_dc_ext_queue_event(control, &pack.event); | ||
| 195 | |||
| 196 | return 0; | ||
| 197 | } | ||
diff --git a/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h b/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h new file mode 100644 index 00000000000..95a637d5a52 --- /dev/null +++ b/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h | |||
| @@ -0,0 +1,147 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h | ||
| 3 | * | ||
| 4 | * Copyright (C) 2011, NVIDIA Corporation | ||
| 5 | * | ||
| 6 | * Author: Robert Morell <rmorell@nvidia.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License, or | ||
| 11 | * (at your option) any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 16 | * more details. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #ifndef __TEGRA_DC_EXT_PRIV_H | ||
| 20 | #define __TEGRA_DC_EXT_PRIV_H | ||
| 21 | |||
| 22 | #include <linux/cdev.h> | ||
| 23 | #include <linux/list.h> | ||
| 24 | #include <linux/mutex.h> | ||
| 25 | #include <linux/poll.h> | ||
| 26 | |||
| 27 | #include <mach/dc.h> | ||
| 28 | #include <mach/nvmap.h> | ||
| 29 | |||
| 30 | #include <video/tegra_dc_ext.h> | ||
| 31 | |||
| 32 | struct tegra_dc_ext; | ||
| 33 | |||
| 34 | struct tegra_dc_ext_user { | ||
| 35 | struct tegra_dc_ext *ext; | ||
| 36 | struct nvmap_client *nvmap; | ||
| 37 | }; | ||
| 38 | |||
| 39 | enum { | ||
| 40 | TEGRA_DC_Y, | ||
| 41 | TEGRA_DC_U, | ||
| 42 | TEGRA_DC_V, | ||
| 43 | TEGRA_DC_NUM_PLANES, | ||
| 44 | }; | ||
| 45 | |||
| 46 | struct tegra_dc_ext_win { | ||
| 47 | struct tegra_dc_ext *ext; | ||
| 48 | |||
| 49 | int idx; | ||
| 50 | |||
| 51 | struct tegra_dc_ext_user *user; | ||
| 52 | |||
| 53 | struct mutex lock; | ||
| 54 | |||
| 55 | /* Current nvmap handle (if any) for Y, U, V planes */ | ||
| 56 | struct nvmap_handle_ref *cur_handle[TEGRA_DC_NUM_PLANES]; | ||
| 57 | |||
| 58 | struct workqueue_struct *flip_wq; | ||
| 59 | |||
| 60 | atomic_t nr_pending_flips; | ||
| 61 | }; | ||
| 62 | |||
| 63 | struct tegra_dc_ext { | ||
| 64 | struct tegra_dc *dc; | ||
| 65 | |||
| 66 | struct cdev cdev; | ||
| 67 | struct device *dev; | ||
| 68 | |||
| 69 | struct nvmap_client *nvmap; | ||
| 70 | |||
| 71 | struct tegra_dc_ext_win win[DC_N_WINDOWS]; | ||
| 72 | |||
| 73 | struct { | ||
| 74 | struct tegra_dc_ext_user *user; | ||
| 75 | struct nvmap_handle_ref *cur_handle; | ||
| 76 | struct mutex lock; | ||
| 77 | } cursor; | ||
| 78 | |||
| 79 | bool enabled; | ||
| 80 | }; | ||
| 81 | |||
| 82 | #define TEGRA_DC_EXT_EVENT_MASK_ALL \ | ||
| 83 | TEGRA_DC_EXT_EVENT_HOTPLUG | ||
| 84 | |||
| 85 | #define TEGRA_DC_EXT_EVENT_MAX_SZ 8 | ||
| 86 | |||
| 87 | struct tegra_dc_ext_event_list { | ||
| 88 | struct tegra_dc_ext_event event; | ||
| 89 | /* The data field _must_ follow the event field. */ | ||
| 90 | char data[TEGRA_DC_EXT_EVENT_MAX_SZ]; | ||
| 91 | |||
| 92 | struct list_head list; | ||
| 93 | }; | ||
| 94 | |||
| 95 | #define TEGRA_DC_EXT_CAPABILITIES \ | ||
| 96 | TEGRA_DC_EXT_CAPABILITIES_CURSOR_MODE | ||
| 97 | |||
| 98 | struct tegra_dc_ext_control_user { | ||
| 99 | struct tegra_dc_ext_control *control; | ||
| 100 | |||
| 101 | struct list_head event_list; | ||
| 102 | atomic_t num_events; | ||
| 103 | |||
| 104 | u32 event_mask; | ||
| 105 | |||
| 106 | struct tegra_dc_ext_event_list event_to_copy; | ||
| 107 | loff_t partial_copy; | ||
| 108 | |||
| 109 | struct mutex lock; | ||
| 110 | |||
| 111 | struct list_head list; | ||
| 112 | }; | ||
| 113 | |||
| 114 | struct tegra_dc_ext_control { | ||
| 115 | struct cdev cdev; | ||
| 116 | struct device *dev; | ||
| 117 | |||
| 118 | struct list_head users; | ||
| 119 | |||
| 120 | struct mutex lock; | ||
| 121 | }; | ||
| 122 | |||
| 123 | extern int tegra_dc_ext_devno; | ||
| 124 | extern struct class *tegra_dc_ext_class; | ||
| 125 | |||
| 126 | extern int tegra_dc_ext_pin_window(struct tegra_dc_ext_user *user, u32 id, | ||
| 127 | struct nvmap_handle_ref **handle, | ||
| 128 | dma_addr_t *phys_addr); | ||
| 129 | |||
| 130 | extern int tegra_dc_ext_get_cursor(struct tegra_dc_ext_user *user); | ||
| 131 | extern int tegra_dc_ext_put_cursor(struct tegra_dc_ext_user *user); | ||
| 132 | extern int tegra_dc_ext_set_cursor_image(struct tegra_dc_ext_user *user, | ||
| 133 | struct tegra_dc_ext_cursor_image *); | ||
| 134 | extern int tegra_dc_ext_set_cursor(struct tegra_dc_ext_user *user, | ||
| 135 | struct tegra_dc_ext_cursor *); | ||
| 136 | |||
| 137 | extern int tegra_dc_ext_control_init(void); | ||
| 138 | |||
| 139 | extern int tegra_dc_ext_queue_hotplug(struct tegra_dc_ext_control *, | ||
| 140 | int output); | ||
| 141 | extern ssize_t tegra_dc_ext_event_read(struct file *filp, char __user *buf, | ||
| 142 | size_t size, loff_t *ppos); | ||
| 143 | extern unsigned int tegra_dc_ext_event_poll(struct file *, poll_table *); | ||
| 144 | |||
| 145 | extern int tegra_dc_ext_get_num_outputs(void); | ||
| 146 | |||
| 147 | #endif /* __TEGRA_DC_EXT_PRIV_H */ | ||
diff --git a/drivers/video/tegra/dc/ext/util.c b/drivers/video/tegra/dc/ext/util.c new file mode 100644 index 00000000000..747085579f1 --- /dev/null +++ b/drivers/video/tegra/dc/ext/util.c | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/ext/util.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2011, NVIDIA Corporation | ||
| 5 | * | ||
| 6 | * Author: Robert Morell <rmorell@nvidia.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License, or | ||
| 11 | * (at your option) any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 16 | * more details. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <linux/err.h> | ||
| 20 | #include <linux/types.h> | ||
| 21 | |||
| 22 | #include <mach/dc.h> | ||
| 23 | #include <mach/nvmap.h> | ||
| 24 | |||
| 25 | /* ugh */ | ||
| 26 | #include "../../nvmap/nvmap.h" | ||
| 27 | |||
| 28 | #include "tegra_dc_ext_priv.h" | ||
| 29 | |||
| 30 | int tegra_dc_ext_pin_window(struct tegra_dc_ext_user *user, u32 id, | ||
| 31 | struct nvmap_handle_ref **handle, | ||
| 32 | dma_addr_t *phys_addr) | ||
| 33 | { | ||
| 34 | struct tegra_dc_ext *ext = user->ext; | ||
| 35 | struct nvmap_handle_ref *win_dup; | ||
| 36 | struct nvmap_handle *win_handle; | ||
| 37 | dma_addr_t phys; | ||
| 38 | |||
| 39 | if (!id) { | ||
| 40 | *handle = NULL; | ||
| 41 | *phys_addr = -1; | ||
| 42 | |||
| 43 | return 0; | ||
| 44 | } | ||
| 45 | |||
| 46 | /* | ||
| 47 | * Take a reference to the buffer using the user's nvmap context, to | ||
| 48 | * make sure they have permissions to access it. | ||
| 49 | */ | ||
| 50 | win_handle = nvmap_get_handle_id(user->nvmap, id); | ||
| 51 | if (!win_handle) | ||
| 52 | return -EACCES; | ||
| 53 | |||
| 54 | /* | ||
| 55 | * Duplicate the buffer's handle into the dc_ext driver's nvmap | ||
| 56 | * context, to ensure that the handle won't be freed as long as it is | ||
| 57 | * in use by display. | ||
| 58 | */ | ||
| 59 | win_dup = nvmap_duplicate_handle_id(ext->nvmap, id); | ||
| 60 | |||
| 61 | /* Release the reference we took in the user's context above */ | ||
| 62 | nvmap_handle_put(win_handle); | ||
| 63 | |||
| 64 | if (IS_ERR(win_dup)) | ||
| 65 | return PTR_ERR(win_dup); | ||
| 66 | |||
| 67 | phys = nvmap_pin(ext->nvmap, win_dup); | ||
| 68 | /* XXX this isn't correct for non-pointers... */ | ||
| 69 | if (IS_ERR((void *)phys)) { | ||
| 70 | nvmap_free(ext->nvmap, win_dup); | ||
| 71 | return PTR_ERR((void *)phys); | ||
| 72 | } | ||
| 73 | |||
| 74 | *phys_addr = phys; | ||
| 75 | *handle = win_dup; | ||
| 76 | |||
| 77 | return 0; | ||
| 78 | } | ||
diff --git a/drivers/video/tegra/dc/hdmi.c b/drivers/video/tegra/dc/hdmi.c new file mode 100644 index 00000000000..cb401a167fd --- /dev/null +++ b/drivers/video/tegra/dc/hdmi.c | |||
| @@ -0,0 +1,2381 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/hdmi.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2010 Google, Inc. | ||
| 5 | * Author: Erik Gilling <konkers@android.com> | ||
| 6 | * | ||
| 7 | * Copyright (C) 2010-2011 NVIDIA Corporation | ||
| 8 | * | ||
| 9 | * This software is licensed under the terms of the GNU General Public | ||
| 10 | * License version 2, as published by the Free Software Foundation, and | ||
| 11 | * may be copied, distributed, and modified under those terms. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/clk.h> | ||
| 21 | #include <linux/delay.h> | ||
| 22 | #include <linux/err.h> | ||
| 23 | #include <linux/fb.h> | ||
| 24 | #include <linux/gpio.h> | ||
| 25 | #include <linux/interrupt.h> | ||
| 26 | #include <linux/kernel.h> | ||
| 27 | #include <linux/slab.h> | ||
| 28 | #include <linux/spinlock.h> | ||
| 29 | #ifdef CONFIG_SWITCH | ||
| 30 | #include <linux/switch.h> | ||
| 31 | #endif | ||
| 32 | #include <linux/workqueue.h> | ||
| 33 | #include <linux/debugfs.h> | ||
| 34 | #include <linux/seq_file.h> | ||
| 35 | #include <linux/device.h> | ||
| 36 | |||
| 37 | #include <mach/clk.h> | ||
| 38 | #include <mach/dc.h> | ||
| 39 | #include <mach/fb.h> | ||
| 40 | #include <linux/nvhost.h> | ||
| 41 | #include <mach/hdmi-audio.h> | ||
| 42 | |||
| 43 | #include <video/tegrafb.h> | ||
| 44 | |||
| 45 | #include "dc_reg.h" | ||
| 46 | #include "dc_priv.h" | ||
| 47 | #include "hdmi_reg.h" | ||
| 48 | #include "hdmi.h" | ||
| 49 | #include "edid.h" | ||
| 50 | #include "nvhdcp.h" | ||
| 51 | |||
| 52 | /* datasheet claims this will always be 216MHz */ | ||
| 53 | #define HDMI_AUDIOCLK_FREQ 216000000 | ||
| 54 | |||
| 55 | #define HDMI_REKEY_DEFAULT 56 | ||
| 56 | |||
| 57 | #define HDMI_ELD_RESERVED1_INDEX 1 | ||
| 58 | #define HDMI_ELD_RESERVED2_INDEX 3 | ||
| 59 | #define HDMI_ELD_VER_INDEX 0 | ||
| 60 | #define HDMI_ELD_BASELINE_LEN_INDEX 2 | ||
| 61 | #define HDMI_ELD_CEA_VER_MNL_INDEX 4 | ||
| 62 | #define HDMI_ELD_SAD_CNT_CON_TYP_SAI_HDCP_INDEX 5 | ||
| 63 | #define HDMI_ELD_AUD_SYNC_DELAY_INDEX 6 | ||
| 64 | #define HDMI_ELD_SPK_ALLOC_INDEX 7 | ||
| 65 | #define HDMI_ELD_PORT_ID_INDEX 8 | ||
| 66 | #define HDMI_ELD_MANF_NAME_INDEX 16 | ||
| 67 | #define HDMI_ELD_PRODUCT_CODE_INDEX 18 | ||
| 68 | #define HDMI_ELD_MONITOR_NAME_INDEX 20 | ||
| 69 | |||
| 70 | struct tegra_dc_hdmi_data { | ||
| 71 | struct tegra_dc *dc; | ||
| 72 | struct tegra_edid *edid; | ||
| 73 | struct tegra_edid_hdmi_eld eld; | ||
| 74 | struct tegra_nvhdcp *nvhdcp; | ||
| 75 | struct delayed_work work; | ||
| 76 | |||
| 77 | struct resource *base_res; | ||
| 78 | void __iomem *base; | ||
| 79 | struct clk *clk; | ||
| 80 | |||
| 81 | struct clk *disp1_clk; | ||
| 82 | struct clk *disp2_clk; | ||
| 83 | struct clk *hda_clk; | ||
| 84 | struct clk *hda2codec_clk; | ||
| 85 | struct clk *hda2hdmi_clk; | ||
| 86 | |||
| 87 | #ifdef CONFIG_SWITCH | ||
| 88 | struct switch_dev hpd_switch; | ||
| 89 | #endif | ||
| 90 | |||
| 91 | spinlock_t suspend_lock; | ||
| 92 | bool suspended; | ||
| 93 | bool eld_retrieved; | ||
| 94 | bool clk_enabled; | ||
| 95 | unsigned audio_freq; | ||
| 96 | unsigned audio_source; | ||
| 97 | |||
| 98 | bool dvi; | ||
| 99 | }; | ||
| 100 | |||
| 101 | struct tegra_dc_hdmi_data *dc_hdmi; | ||
| 102 | |||
| 103 | const struct fb_videomode tegra_dc_hdmi_supported_modes[] = { | ||
| 104 | /* 1280x720p 60hz: EIA/CEA-861-B Format 4 */ | ||
| 105 | { | ||
| 106 | .xres = 1280, | ||
| 107 | .yres = 720, | ||
| 108 | .pixclock = KHZ2PICOS(74250), | ||
| 109 | .hsync_len = 40, /* h_sync_width */ | ||
| 110 | .vsync_len = 5, /* v_sync_width */ | ||
| 111 | .left_margin = 220, /* h_back_porch */ | ||
| 112 | .upper_margin = 20, /* v_back_porch */ | ||
| 113 | .right_margin = 110, /* h_front_porch */ | ||
| 114 | .lower_margin = 5, /* v_front_porch */ | ||
| 115 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 116 | .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 117 | }, | ||
| 118 | |||
| 119 | /* 1280x720p 60hz: EIA/CEA-861-B Format 4 (Stereo)*/ | ||
| 120 | { | ||
| 121 | .xres = 1280, | ||
| 122 | .yres = 720, | ||
| 123 | .pixclock = KHZ2PICOS(74250), | ||
| 124 | .hsync_len = 40, /* h_sync_width */ | ||
| 125 | .vsync_len = 5, /* v_sync_width */ | ||
| 126 | .left_margin = 220, /* h_back_porch */ | ||
| 127 | .upper_margin = 20, /* v_back_porch */ | ||
| 128 | .right_margin = 110, /* h_front_porch */ | ||
| 129 | .lower_margin = 5, /* v_front_porch */ | ||
| 130 | .vmode = FB_VMODE_NONINTERLACED | | ||
| 131 | #ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT | ||
| 132 | FB_VMODE_STEREO_FRAME_PACK, | ||
| 133 | #else | ||
| 134 | FB_VMODE_STEREO_LEFT_RIGHT, | ||
| 135 | #endif | ||
| 136 | .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 137 | }, | ||
| 138 | |||
| 139 | /* 720x480p 59.94hz: EIA/CEA-861-B Formats 2 & 3 */ | ||
| 140 | { | ||
| 141 | .xres = 720, | ||
| 142 | .yres = 480, | ||
| 143 | .pixclock = KHZ2PICOS(27000), | ||
| 144 | .hsync_len = 62, /* h_sync_width */ | ||
| 145 | .vsync_len = 6, /* v_sync_width */ | ||
| 146 | .left_margin = 60, /* h_back_porch */ | ||
| 147 | .upper_margin = 30, /* v_back_porch */ | ||
| 148 | .right_margin = 16, /* h_front_porch */ | ||
| 149 | .lower_margin = 9, /* v_front_porch */ | ||
| 150 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 151 | .sync = 0, | ||
| 152 | }, | ||
| 153 | |||
| 154 | /* 640x480p 60hz: EIA/CEA-861-B Format 1 */ | ||
| 155 | { | ||
| 156 | .xres = 640, | ||
| 157 | .yres = 480, | ||
| 158 | .pixclock = KHZ2PICOS(25200), | ||
| 159 | .hsync_len = 96, /* h_sync_width */ | ||
| 160 | .vsync_len = 2, /* v_sync_width */ | ||
| 161 | .left_margin = 48, /* h_back_porch */ | ||
| 162 | .upper_margin = 33, /* v_back_porch */ | ||
| 163 | .right_margin = 16, /* h_front_porch */ | ||
| 164 | .lower_margin = 10, /* v_front_porch */ | ||
| 165 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 166 | .sync = 0, | ||
| 167 | }, | ||
| 168 | |||
| 169 | /* 720x576p 50hz EIA/CEA-861-B Formats 17 & 18 */ | ||
| 170 | { | ||
| 171 | .xres = 720, | ||
| 172 | .yres = 576, | ||
| 173 | .pixclock = KHZ2PICOS(27000), | ||
| 174 | .hsync_len = 64, /* h_sync_width */ | ||
| 175 | .vsync_len = 5, /* v_sync_width */ | ||
| 176 | .left_margin = 68, /* h_back_porch */ | ||
| 177 | .upper_margin = 39, /* v_back_porch */ | ||
| 178 | .right_margin = 12, /* h_front_porch */ | ||
| 179 | .lower_margin = 5, /* v_front_porch */ | ||
| 180 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 181 | .sync = 0, | ||
| 182 | }, | ||
| 183 | |||
| 184 | /* 1920x1080p 23.98/24hz: EIA/CEA-861-B Format 32 (Stereo)*/ | ||
| 185 | { | ||
| 186 | .xres = 1920, | ||
| 187 | .yres = 1080, | ||
| 188 | .pixclock = KHZ2PICOS(74250), | ||
| 189 | .hsync_len = 44, /* h_sync_width */ | ||
| 190 | .vsync_len = 5, /* v_sync_width */ | ||
| 191 | .left_margin = 148, /* h_back_porch */ | ||
| 192 | .upper_margin = 36, /* v_back_porch */ | ||
| 193 | .right_margin = 638, /* h_front_porch */ | ||
| 194 | .lower_margin = 4, /* v_front_porch */ | ||
| 195 | .vmode = FB_VMODE_NONINTERLACED | | ||
| 196 | #ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT | ||
| 197 | FB_VMODE_STEREO_FRAME_PACK, | ||
| 198 | #else | ||
| 199 | FB_VMODE_STEREO_LEFT_RIGHT, | ||
| 200 | #endif | ||
| 201 | .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 202 | }, | ||
| 203 | |||
| 204 | /* 1920x1080p 30Hz EIA/CEA-861-B Format 34 */ | ||
| 205 | { | ||
| 206 | .xres = 1920, | ||
| 207 | .yres = 1080, | ||
| 208 | .pixclock = KHZ2PICOS(74250), | ||
| 209 | .hsync_len = 44, /* h_sync_width */ | ||
| 210 | .vsync_len = 5, /* v_sync_width */ | ||
| 211 | .left_margin = 148, /* h_back_porch */ | ||
| 212 | .upper_margin = 36, /* v_back_porch */ | ||
| 213 | .right_margin = 88, /* h_front_porch */ | ||
| 214 | .lower_margin = 4, /* v_front_porch */ | ||
| 215 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 216 | .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 217 | }, | ||
| 218 | /* 1920x1080p 59.94/60hz EIA/CEA-861-B Format 16 */ | ||
| 219 | { | ||
| 220 | .xres = 1920, | ||
| 221 | .yres = 1080, | ||
| 222 | .pixclock = KHZ2PICOS(148500), | ||
| 223 | .hsync_len = 44, /* h_sync_width */ | ||
| 224 | .vsync_len = 5, /* v_sync_width */ | ||
| 225 | .left_margin = 148, /* h_back_porch */ | ||
| 226 | .upper_margin = 36, /* v_back_porch */ | ||
| 227 | .right_margin = 88, /* h_front_porch */ | ||
| 228 | .lower_margin = 4, /* v_front_porch */ | ||
| 229 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 230 | .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 231 | }, | ||
| 232 | |||
| 233 | /* | ||
| 234 | * Few VGA/SVGA modes to support monitors with lower | ||
| 235 | * resolutions or to support HDMI<->DVI connection | ||
| 236 | */ | ||
| 237 | |||
| 238 | /* 640x480p 75hz */ | ||
| 239 | { | ||
| 240 | .xres = 640, | ||
| 241 | .yres = 480, | ||
| 242 | .pixclock = KHZ2PICOS(31500), | ||
| 243 | .hsync_len = 96, /* h_sync_width */ | ||
| 244 | .vsync_len = 2, /* v_sync_width */ | ||
| 245 | .left_margin = 48, /* h_back_porch */ | ||
| 246 | .upper_margin = 32, /* v_back_porch */ | ||
| 247 | .right_margin = 16, /* h_front_porch */ | ||
| 248 | .lower_margin = 1, /* v_front_porch */ | ||
| 249 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 250 | .sync = 0, | ||
| 251 | }, | ||
| 252 | /* 720x400p 59hz */ | ||
| 253 | { | ||
| 254 | .xres = 720, | ||
| 255 | .yres = 400, | ||
| 256 | .pixclock = KHZ2PICOS(35500), | ||
| 257 | .hsync_len = 72, /* h_sync_width */ | ||
| 258 | .vsync_len = 3, /* v_sync_width */ | ||
| 259 | .left_margin = 108, /* h_back_porch */ | ||
| 260 | .upper_margin = 42, /* v_back_porch */ | ||
| 261 | .right_margin = 36, /* h_front_porch */ | ||
| 262 | .lower_margin = 1, /* v_front_porch */ | ||
| 263 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 264 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 265 | }, | ||
| 266 | /* 800x600p 60hz */ | ||
| 267 | { | ||
| 268 | .xres = 800, | ||
| 269 | .yres = 600, | ||
| 270 | .pixclock = KHZ2PICOS(40000), | ||
| 271 | .hsync_len = 128, /* h_sync_width */ | ||
| 272 | .vsync_len = 4, /* v_sync_width */ | ||
| 273 | .left_margin = 88, /* h_back_porch */ | ||
| 274 | .upper_margin = 23, /* v_back_porch */ | ||
| 275 | .right_margin = 40, /* h_front_porch */ | ||
| 276 | .lower_margin = 1, /* v_front_porch */ | ||
| 277 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 278 | .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 279 | }, | ||
| 280 | /* 800x600p 75hz */ | ||
| 281 | { | ||
| 282 | .xres = 800, | ||
| 283 | .yres = 600, | ||
| 284 | .pixclock = KHZ2PICOS(49500), | ||
| 285 | .hsync_len = 80, /* h_sync_width */ | ||
| 286 | .vsync_len = 2, /* v_sync_width */ | ||
| 287 | .left_margin = 160, /* h_back_porch */ | ||
| 288 | .upper_margin = 21, /* v_back_porch */ | ||
| 289 | .right_margin = 16, /* h_front_porch */ | ||
| 290 | .lower_margin = 1, /* v_front_porch */ | ||
| 291 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 292 | .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 293 | }, | ||
| 294 | /* 1024x768p 60hz */ | ||
| 295 | { | ||
| 296 | .xres = 1024, | ||
| 297 | .yres = 768, | ||
| 298 | .pixclock = KHZ2PICOS(65000), | ||
| 299 | .hsync_len = 136, /* h_sync_width */ | ||
| 300 | .vsync_len = 6, /* v_sync_width */ | ||
| 301 | .left_margin = 160, /* h_back_porch */ | ||
| 302 | .upper_margin = 29, /* v_back_porch */ | ||
| 303 | .right_margin = 24, /* h_front_porch */ | ||
| 304 | .lower_margin = 3, /* v_front_porch */ | ||
| 305 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 306 | .sync = 0, | ||
| 307 | }, | ||
| 308 | /* 1024x768p 75hz */ | ||
| 309 | { | ||
| 310 | .xres = 1024, | ||
| 311 | .yres = 768, | ||
| 312 | .pixclock = KHZ2PICOS(78800), | ||
| 313 | .hsync_len = 96, /* h_sync_width */ | ||
| 314 | .vsync_len = 3, /* v_sync_width */ | ||
| 315 | .left_margin = 176, /* h_back_porch */ | ||
| 316 | .upper_margin = 28, /* v_back_porch */ | ||
| 317 | .right_margin = 16, /* h_front_porch */ | ||
| 318 | .lower_margin = 1, /* v_front_porch */ | ||
| 319 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 320 | .sync = 0, | ||
| 321 | }, | ||
| 322 | /* 1152x864p 75hz */ | ||
| 323 | { | ||
| 324 | .xres = 1152, | ||
| 325 | .yres = 864, | ||
| 326 | .pixclock = KHZ2PICOS(108000), | ||
| 327 | .hsync_len = 128, /* h_sync_width */ | ||
| 328 | .vsync_len = 3, /* v_sync_width */ | ||
| 329 | .left_margin = 256, /* h_back_porch */ | ||
| 330 | .upper_margin = 32, /* v_back_porch */ | ||
| 331 | .right_margin = 64, /* h_front_porch */ | ||
| 332 | .lower_margin = 1, /* v_front_porch */ | ||
| 333 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 334 | .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 335 | }, | ||
| 336 | /* 1280x800p 60hz */ | ||
| 337 | { | ||
| 338 | .xres = 1280, | ||
| 339 | .yres = 800, | ||
| 340 | .pixclock = KHZ2PICOS(83460), | ||
| 341 | .hsync_len = 136, /* h_sync_width */ | ||
| 342 | .vsync_len = 3, /* v_sync_width */ | ||
| 343 | .left_margin = 200, /* h_back_porch */ | ||
| 344 | .upper_margin = 24, /* v_back_porch */ | ||
| 345 | .right_margin = 64, /* h_front_porch */ | ||
| 346 | .lower_margin = 1, /* v_front_porch */ | ||
| 347 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 348 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 349 | }, | ||
| 350 | /* 1280x960p 60hz */ | ||
| 351 | { | ||
| 352 | .xres = 1280, | ||
| 353 | .yres = 960, | ||
| 354 | .pixclock = KHZ2PICOS(108000), | ||
| 355 | .hsync_len = 136, /* h_sync_width */ | ||
| 356 | .vsync_len = 3, /* v_sync_width */ | ||
| 357 | .left_margin = 216, /* h_back_porch */ | ||
| 358 | .upper_margin = 30, /* v_back_porch */ | ||
| 359 | .right_margin = 80, /* h_front_porch */ | ||
| 360 | .lower_margin = 1, /* v_front_porch */ | ||
| 361 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 362 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 363 | }, | ||
| 364 | /* 1280x1024p 60hz */ | ||
| 365 | { | ||
| 366 | .xres = 1280, | ||
| 367 | .yres = 1024, | ||
| 368 | .pixclock = KHZ2PICOS(108000), | ||
| 369 | .hsync_len = 112, /* h_sync_width */ | ||
| 370 | .vsync_len = 3, /* v_sync_width */ | ||
| 371 | .left_margin = 248, /* h_back_porch */ | ||
| 372 | .upper_margin = 38, /* v_back_porch */ | ||
| 373 | .right_margin = 48, /* h_front_porch */ | ||
| 374 | .lower_margin = 1, /* v_front_porch */ | ||
| 375 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 376 | .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 377 | }, | ||
| 378 | /* 1280x1024p 75hz */ | ||
| 379 | { | ||
| 380 | .xres = 1280, | ||
| 381 | .yres = 1024, | ||
| 382 | .pixclock = KHZ2PICOS(135000), | ||
| 383 | .hsync_len = 144, /* h_sync_width */ | ||
| 384 | .vsync_len = 3, /* v_sync_width */ | ||
| 385 | .left_margin = 248, /* h_back_porch */ | ||
| 386 | .upper_margin = 38, /* v_back_porch */ | ||
| 387 | .right_margin = 16, /* h_front_porch */ | ||
| 388 | .lower_margin = 1, /* v_front_porch */ | ||
| 389 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 390 | .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 391 | }, | ||
| 392 | /* 1368x768p 60hz */ | ||
| 393 | { | ||
| 394 | .xres = 1368, | ||
| 395 | .yres = 768, | ||
| 396 | .pixclock = KHZ2PICOS(85860), | ||
| 397 | .hsync_len = 144, /* h_sync_width */ | ||
| 398 | .vsync_len = 3, /* v_sync_width */ | ||
| 399 | .left_margin = 216, /* h_back_porch */ | ||
| 400 | .upper_margin = 23, /* v_back_porch */ | ||
| 401 | .right_margin = 72, /* h_front_porch */ | ||
| 402 | .lower_margin = 1, /* v_front_porch */ | ||
| 403 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 404 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 405 | }, | ||
| 406 | /* 1440x900p 60hz */ | ||
| 407 | { | ||
| 408 | .xres = 1440, | ||
| 409 | .yres = 900, | ||
| 410 | .pixclock = KHZ2PICOS(106470), | ||
| 411 | .hsync_len = 152, /* h_sync_width */ | ||
| 412 | .vsync_len = 3, /* v_sync_width */ | ||
| 413 | .left_margin = 232, /* h_back_porch */ | ||
| 414 | .upper_margin = 28, /* v_back_porch */ | ||
| 415 | .right_margin = 80, /* h_front_porch */ | ||
| 416 | .lower_margin = 1, /* v_front_porch */ | ||
| 417 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 418 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 419 | }, | ||
| 420 | /* 1600x1200p 60hz */ | ||
| 421 | { | ||
| 422 | .xres = 1600, | ||
| 423 | .yres = 1200, | ||
| 424 | .pixclock = KHZ2PICOS(162000), | ||
| 425 | .hsync_len = 192, /* h_sync_width */ | ||
| 426 | .vsync_len = 3, /* v_sync_width */ | ||
| 427 | .left_margin = 304, /* h_back_porch */ | ||
| 428 | .upper_margin = 46, /* v_back_porch */ | ||
| 429 | .right_margin = 64, /* h_front_porch */ | ||
| 430 | .lower_margin = 1, /* v_front_porch */ | ||
| 431 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 432 | .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 433 | }, | ||
| 434 | /* 1600x1200p 75hz */ | ||
| 435 | { | ||
| 436 | .xres = 1600, | ||
| 437 | .yres = 1200, | ||
| 438 | .pixclock = KHZ2PICOS(202500), | ||
| 439 | .hsync_len = 192, /* h_sync_width */ | ||
| 440 | .vsync_len = 3, /* v_sync_width */ | ||
| 441 | .left_margin = 304, /* h_back_porch */ | ||
| 442 | .upper_margin = 46, /* v_back_porch */ | ||
| 443 | .right_margin = 64, /* h_front_porch */ | ||
| 444 | .lower_margin = 1, /* v_front_porch */ | ||
| 445 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 446 | .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 447 | }, | ||
| 448 | /* 1680x1050p 59.94/60hz */ | ||
| 449 | { | ||
| 450 | .xres = 1680, | ||
| 451 | .yres = 1050, | ||
| 452 | .pixclock = KHZ2PICOS(147140), | ||
| 453 | .hsync_len = 184, /* h_sync_width */ | ||
| 454 | .vsync_len = 3, /* v_sync_width */ | ||
| 455 | .left_margin = 288, /* h_back_porch */ | ||
| 456 | .upper_margin = 33, /* v_back_porch */ | ||
| 457 | .right_margin = 104, /* h_front_porch */ | ||
| 458 | .lower_margin = 1, /* v_front_porch */ | ||
| 459 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 460 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 461 | }, | ||
| 462 | }; | ||
| 463 | |||
| 464 | /* CVT timing representation of VESA modes*/ | ||
| 465 | const struct fb_videomode tegra_dc_hdmi_supported_cvt_modes[] = { | ||
| 466 | |||
| 467 | /* 640x480p 60hz */ | ||
| 468 | { | ||
| 469 | .refresh = 60, | ||
| 470 | .xres = 640, | ||
| 471 | .yres = 480, | ||
| 472 | .pixclock = KHZ2PICOS(23750), | ||
| 473 | .hsync_len = 64, /* h_sync_width */ | ||
| 474 | .vsync_len = 4, /* v_sync_width */ | ||
| 475 | .left_margin = 80, /* h_back_porch */ | ||
| 476 | .upper_margin = 17, /* v_back_porch */ | ||
| 477 | .right_margin = 16, /* h_front_porch */ | ||
| 478 | .lower_margin = 3, /* v_front_porch */ | ||
| 479 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 480 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 481 | }, | ||
| 482 | /* 640x480p 75hz */ | ||
| 483 | { | ||
| 484 | .refresh = 75, | ||
| 485 | .xres = 640, | ||
| 486 | .yres = 480, | ||
| 487 | .pixclock = KHZ2PICOS(30750), | ||
| 488 | .hsync_len = 64, /* h_sync_width */ | ||
| 489 | .vsync_len = 4, /* v_sync_width */ | ||
| 490 | .left_margin = 88, /* h_back_porch */ | ||
| 491 | .upper_margin = 21, /* v_back_porch */ | ||
| 492 | .right_margin = 24, /* h_front_porch */ | ||
| 493 | .lower_margin = 3, /* v_front_porch */ | ||
| 494 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 495 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 496 | }, | ||
| 497 | /* 720x400p 59hz */ | ||
| 498 | { | ||
| 499 | .refresh = 59, | ||
| 500 | .xres = 720, | ||
| 501 | .yres = 400, | ||
| 502 | .pixclock = KHZ2PICOS(22000), | ||
| 503 | .hsync_len = 64, /* h_sync_width */ | ||
| 504 | .vsync_len = 10, /* v_sync_width */ | ||
| 505 | .left_margin = 88, /* h_back_porch */ | ||
| 506 | .upper_margin = 14, /* v_back_porch */ | ||
| 507 | .right_margin = 24, /* h_front_porch */ | ||
| 508 | .lower_margin = 3, /* v_front_porch */ | ||
| 509 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 510 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 511 | }, | ||
| 512 | /* 800x600p 60hz */ | ||
| 513 | { | ||
| 514 | .refresh = 60, | ||
| 515 | .xres = 800, | ||
| 516 | .yres = 600, | ||
| 517 | .pixclock = KHZ2PICOS(38250), | ||
| 518 | .hsync_len = 80, /* h_sync_width */ | ||
| 519 | .vsync_len = 4, /* v_sync_width */ | ||
| 520 | .left_margin = 112, /* h_back_porch */ | ||
| 521 | .upper_margin = 21, /* v_back_porch */ | ||
| 522 | .right_margin = 32, /* h_front_porch */ | ||
| 523 | .lower_margin = 3, /* v_front_porch */ | ||
| 524 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 525 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 526 | }, | ||
| 527 | /* 800x600p 75hz */ | ||
| 528 | { | ||
| 529 | .refresh = 75, | ||
| 530 | .xres = 800, | ||
| 531 | .yres = 600, | ||
| 532 | .pixclock = KHZ2PICOS(49000), | ||
| 533 | .hsync_len = 80, /* h_sync_width */ | ||
| 534 | .vsync_len = 4, /* v_sync_width */ | ||
| 535 | .left_margin = 120, /* h_back_porch */ | ||
| 536 | .upper_margin = 26, /* v_back_porch */ | ||
| 537 | .right_margin = 40, /* h_front_porch */ | ||
| 538 | .lower_margin = 3, /* v_front_porch */ | ||
| 539 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 540 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 541 | }, | ||
| 542 | /* 1024x768p 60hz */ | ||
| 543 | { | ||
| 544 | .refresh = 60, | ||
| 545 | .xres = 1024, | ||
| 546 | .yres = 768, | ||
| 547 | .pixclock = KHZ2PICOS(63500), | ||
| 548 | .hsync_len = 104, /* h_sync_width */ | ||
| 549 | .vsync_len = 4, /* v_sync_width */ | ||
| 550 | .left_margin = 152, /* h_back_porch */ | ||
| 551 | .upper_margin = 27, /* v_back_porch */ | ||
| 552 | .right_margin = 48, /* h_front_porch */ | ||
| 553 | .lower_margin = 3, /* v_front_porch */ | ||
| 554 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 555 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 556 | }, | ||
| 557 | /* 1024x768p 75hz */ | ||
| 558 | { | ||
| 559 | .refresh = 75, | ||
| 560 | .xres = 1024, | ||
| 561 | .yres = 768, | ||
| 562 | .pixclock = KHZ2PICOS(82000), | ||
| 563 | .hsync_len = 104, /* h_sync_width */ | ||
| 564 | .vsync_len = 4, /* v_sync_width */ | ||
| 565 | .left_margin = 168, /* h_back_porch */ | ||
| 566 | .upper_margin = 34, /* v_back_porch */ | ||
| 567 | .right_margin = 64, /* h_front_porch */ | ||
| 568 | .lower_margin = 3, /* v_front_porch */ | ||
| 569 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 570 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 571 | }, | ||
| 572 | /* 1152x864p 75hz */ | ||
| 573 | { | ||
| 574 | .refresh = 75, | ||
| 575 | .xres = 1152, | ||
| 576 | .yres = 864, | ||
| 577 | .pixclock = KHZ2PICOS(104500), | ||
| 578 | .hsync_len = 120, /* h_sync_width */ | ||
| 579 | .vsync_len = 10, /* v_sync_width */ | ||
| 580 | .left_margin = 192, /* h_back_porch */ | ||
| 581 | .upper_margin = 38, /* v_back_porch */ | ||
| 582 | .right_margin = 72, /* h_front_porch */ | ||
| 583 | .lower_margin = 3, /* v_front_porch */ | ||
| 584 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 585 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 586 | }, | ||
| 587 | /* 1280x800p 60hz */ | ||
| 588 | { | ||
| 589 | .refresh = 60, | ||
| 590 | .xres = 1280, | ||
| 591 | .yres = 800, | ||
| 592 | .pixclock = KHZ2PICOS(83500), | ||
| 593 | .hsync_len = 128, /* h_sync_width */ | ||
| 594 | .vsync_len = 6, /* v_sync_width */ | ||
| 595 | .left_margin = 200, /* h_back_porch */ | ||
| 596 | .upper_margin = 28, /* v_back_porch */ | ||
| 597 | .right_margin = 72, /* h_front_porch */ | ||
| 598 | .lower_margin = 3, /* v_front_porch */ | ||
| 599 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 600 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 601 | }, | ||
| 602 | /* 1280x960p 60hz */ | ||
| 603 | { | ||
| 604 | .refresh = 60, | ||
| 605 | .xres = 1280, | ||
| 606 | .yres = 960, | ||
| 607 | .pixclock = KHZ2PICOS(101250), | ||
| 608 | .hsync_len = 128, /* h_sync_width */ | ||
| 609 | .vsync_len = 4, /* v_sync_width */ | ||
| 610 | .left_margin = 208, /* h_back_porch */ | ||
| 611 | .upper_margin = 33, /* v_back_porch */ | ||
| 612 | .right_margin = 80, /* h_front_porch */ | ||
| 613 | .lower_margin = 3, /* v_front_porch */ | ||
| 614 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 615 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 616 | }, | ||
| 617 | /* 1280x1024p 60hz */ | ||
| 618 | { | ||
| 619 | .refresh = 60, | ||
| 620 | .xres = 1280, | ||
| 621 | .yres = 1024, | ||
| 622 | .pixclock = KHZ2PICOS(109000), | ||
| 623 | .hsync_len = 136, /* h_sync_width */ | ||
| 624 | .vsync_len = 7, /* v_sync_width */ | ||
| 625 | .left_margin = 216, /* h_back_porch */ | ||
| 626 | .upper_margin = 36, /* v_back_porch */ | ||
| 627 | .right_margin = 80, /* h_front_porch */ | ||
| 628 | .lower_margin = 3, /* v_front_porch */ | ||
| 629 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 630 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 631 | }, | ||
| 632 | |||
| 633 | /* 1280x1024p 75hz */ | ||
| 634 | { | ||
| 635 | .refresh = 75, | ||
| 636 | .xres = 1280, | ||
| 637 | .yres = 1024, | ||
| 638 | .pixclock = KHZ2PICOS(138750), | ||
| 639 | .hsync_len = 136, /* h_sync_width */ | ||
| 640 | .vsync_len = 7, /* v_sync_width */ | ||
| 641 | .left_margin = 224, /* h_back_porch */ | ||
| 642 | .upper_margin = 45, /* v_back_porch */ | ||
| 643 | .right_margin = 88, /* h_front_porch */ | ||
| 644 | .lower_margin = 3, /* v_front_porch */ | ||
| 645 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 646 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 647 | }, | ||
| 648 | /* 1368x768p 60hz */ | ||
| 649 | { | ||
| 650 | .refresh = 60, | ||
| 651 | .xres = 1368, | ||
| 652 | .yres = 768, | ||
| 653 | .pixclock = KHZ2PICOS(85250), | ||
| 654 | .hsync_len = 136, /* h_sync_width */ | ||
| 655 | .vsync_len = 10, /* v_sync_width */ | ||
| 656 | .left_margin = 208, /* h_back_porch */ | ||
| 657 | .upper_margin = 27, /* v_back_porch */ | ||
| 658 | .right_margin = 72, /* h_front_porch */ | ||
| 659 | .lower_margin = 3, /* v_front_porch */ | ||
| 660 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 661 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 662 | }, | ||
| 663 | /* 1440x900p 60hz */ | ||
| 664 | { | ||
| 665 | .refresh = 60, | ||
| 666 | .xres = 1440, | ||
| 667 | .yres = 900, | ||
| 668 | .pixclock = KHZ2PICOS(106500), | ||
| 669 | .hsync_len = 152, /* h_sync_width */ | ||
| 670 | .vsync_len = 6, /* v_sync_width */ | ||
| 671 | .left_margin = 232, /* h_back_porch */ | ||
| 672 | .upper_margin = 31, /* v_back_porch */ | ||
| 673 | .right_margin = 80, /* h_front_porch */ | ||
| 674 | .lower_margin = 3, /* v_front_porch */ | ||
| 675 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 676 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 677 | }, | ||
| 678 | /* 1600x1200p 60hz */ | ||
| 679 | { | ||
| 680 | .refresh = 60, | ||
| 681 | .xres = 1600, | ||
| 682 | .yres = 1200, | ||
| 683 | .pixclock = KHZ2PICOS(161000), | ||
| 684 | .hsync_len = 168, /* h_sync_width */ | ||
| 685 | .vsync_len = 4, /* v_sync_width */ | ||
| 686 | .left_margin = 280, /* h_back_porch */ | ||
| 687 | .upper_margin = 42, /* v_back_porch */ | ||
| 688 | .right_margin = 112, /* h_front_porch */ | ||
| 689 | .lower_margin = 3, /* v_front_porch */ | ||
| 690 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 691 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 692 | }, | ||
| 693 | /* 1600x1200p 75hz */ | ||
| 694 | { | ||
| 695 | .refresh = 75, | ||
| 696 | .xres = 1600, | ||
| 697 | .yres = 1200, | ||
| 698 | .pixclock = KHZ2PICOS(204750), | ||
| 699 | .hsync_len = 168, /* h_sync_width */ | ||
| 700 | .vsync_len = 4, /* v_sync_width */ | ||
| 701 | .left_margin = 288, /* h_back_porch */ | ||
| 702 | .upper_margin = 52, /* v_back_porch */ | ||
| 703 | .right_margin = 120, /* h_front_porch */ | ||
| 704 | .lower_margin = 3, /* v_front_porch */ | ||
| 705 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 706 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 707 | }, | ||
| 708 | /* 1680x1050p 59.94/60hz */ | ||
| 709 | { | ||
| 710 | .refresh = 60, | ||
| 711 | .xres = 1680, | ||
| 712 | .yres = 1050, | ||
| 713 | .pixclock = KHZ2PICOS(140000), | ||
| 714 | .hsync_len = 168, /* h_sync_width */ | ||
| 715 | .vsync_len = 10, /* v_sync_width */ | ||
| 716 | .left_margin = 272, /* h_back_porch */ | ||
| 717 | .upper_margin = 36, /* v_back_porch */ | ||
| 718 | .right_margin = 104, /* h_front_porch */ | ||
| 719 | .lower_margin = 3, /* v_front_porch */ | ||
| 720 | .vmode = FB_VMODE_NONINTERLACED, | ||
| 721 | .sync = FB_SYNC_VERT_HIGH_ACT, | ||
| 722 | }, | ||
| 723 | }; | ||
| 724 | |||
| 725 | /* table of electrical settings, must be in acending order. */ | ||
| 726 | struct tdms_config { | ||
| 727 | int pclk; | ||
| 728 | u32 pll0; | ||
| 729 | u32 pll1; | ||
| 730 | u32 pe_current; /* pre-emphasis */ | ||
| 731 | u32 drive_current; | ||
| 732 | }; | ||
| 733 | |||
| 734 | #ifndef CONFIG_ARCH_TEGRA_2x_SOC | ||
| 735 | const struct tdms_config tdms_config[] = { | ||
| 736 | { /* 480p modes */ | ||
| 737 | .pclk = 27000000, | ||
| 738 | .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | SOR_PLL_RESISTORSEL | | ||
| 739 | SOR_PLL_VCOCAP(0) | SOR_PLL_TX_REG_LOAD(0), | ||
| 740 | .pll1 = SOR_PLL_TMDS_TERM_ENABLE, | ||
| 741 | .pe_current = PE_CURRENT0(PE_CURRENT_0_0_mA) | | ||
| 742 | PE_CURRENT1(PE_CURRENT_0_0_mA) | | ||
| 743 | PE_CURRENT2(PE_CURRENT_0_0_mA) | | ||
| 744 | PE_CURRENT3(PE_CURRENT_0_0_mA), | ||
| 745 | .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_5_250_mA) | | ||
| 746 | DRIVE_CURRENT_LANE1(DRIVE_CURRENT_5_250_mA) | | ||
| 747 | DRIVE_CURRENT_LANE2(DRIVE_CURRENT_5_250_mA) | | ||
| 748 | DRIVE_CURRENT_LANE3(DRIVE_CURRENT_5_250_mA), | ||
| 749 | }, | ||
| 750 | { /* 720p modes */ | ||
| 751 | .pclk = 74250000, | ||
| 752 | .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | SOR_PLL_RESISTORSEL | | ||
| 753 | SOR_PLL_VCOCAP(1) | SOR_PLL_TX_REG_LOAD(0), | ||
| 754 | .pll1 = SOR_PLL_TMDS_TERM_ENABLE | SOR_PLL_PE_EN, | ||
| 755 | .pe_current = PE_CURRENT0(PE_CURRENT_5_0_mA) | | ||
| 756 | PE_CURRENT1(PE_CURRENT_5_0_mA) | | ||
| 757 | PE_CURRENT2(PE_CURRENT_5_0_mA) | | ||
| 758 | PE_CURRENT3(PE_CURRENT_5_0_mA), | ||
| 759 | .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_5_250_mA) | | ||
| 760 | DRIVE_CURRENT_LANE1(DRIVE_CURRENT_5_250_mA) | | ||
| 761 | DRIVE_CURRENT_LANE2(DRIVE_CURRENT_5_250_mA) | | ||
| 762 | DRIVE_CURRENT_LANE3(DRIVE_CURRENT_5_250_mA), | ||
| 763 | }, | ||
| 764 | { /* 1080p modes */ | ||
| 765 | .pclk = INT_MAX, | ||
| 766 | .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | SOR_PLL_RESISTORSEL | | ||
| 767 | SOR_PLL_VCOCAP(3) | SOR_PLL_TX_REG_LOAD(0), | ||
| 768 | .pll1 = SOR_PLL_TMDS_TERM_ENABLE | SOR_PLL_PE_EN, | ||
| 769 | .pe_current = PE_CURRENT0(PE_CURRENT_5_0_mA) | | ||
| 770 | PE_CURRENT1(PE_CURRENT_5_0_mA) | | ||
| 771 | PE_CURRENT2(PE_CURRENT_5_0_mA) | | ||
| 772 | PE_CURRENT3(PE_CURRENT_5_0_mA), | ||
| 773 | .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_5_250_mA) | | ||
| 774 | DRIVE_CURRENT_LANE1(DRIVE_CURRENT_5_250_mA) | | ||
| 775 | DRIVE_CURRENT_LANE2(DRIVE_CURRENT_5_250_mA) | | ||
| 776 | DRIVE_CURRENT_LANE3(DRIVE_CURRENT_5_250_mA), | ||
| 777 | }, | ||
| 778 | }; | ||
| 779 | #else /* CONFIG_ARCH_TEGRA_2x_SOC */ | ||
| 780 | const struct tdms_config tdms_config[] = { | ||
| 781 | { /* 480p modes */ | ||
| 782 | .pclk = 27000000, | ||
| 783 | .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | SOR_PLL_RESISTORSEL | | ||
| 784 | SOR_PLL_VCOCAP(0) | SOR_PLL_TX_REG_LOAD(3), | ||
| 785 | .pll1 = SOR_PLL_TMDS_TERM_ENABLE, | ||
| 786 | .pe_current = PE_CURRENT0(PE_CURRENT_0_0_mA) | | ||
| 787 | PE_CURRENT1(PE_CURRENT_0_0_mA) | | ||
| 788 | PE_CURRENT2(PE_CURRENT_0_0_mA) | | ||
| 789 | PE_CURRENT3(PE_CURRENT_0_0_mA), | ||
| 790 | .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_7_125_mA) | | ||
| 791 | DRIVE_CURRENT_LANE1(DRIVE_CURRENT_7_125_mA) | | ||
| 792 | DRIVE_CURRENT_LANE2(DRIVE_CURRENT_7_125_mA) | | ||
| 793 | DRIVE_CURRENT_LANE3(DRIVE_CURRENT_7_125_mA), | ||
| 794 | }, | ||
| 795 | { /* 720p modes */ | ||
| 796 | .pclk = 74250000, | ||
| 797 | .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | SOR_PLL_RESISTORSEL | | ||
| 798 | SOR_PLL_VCOCAP(1) | SOR_PLL_TX_REG_LOAD(3), | ||
| 799 | .pll1 = SOR_PLL_TMDS_TERM_ENABLE | SOR_PLL_PE_EN, | ||
| 800 | .pe_current = PE_CURRENT0(PE_CURRENT_6_0_mA) | | ||
| 801 | PE_CURRENT1(PE_CURRENT_6_0_mA) | | ||
| 802 | PE_CURRENT2(PE_CURRENT_6_0_mA) | | ||
| 803 | PE_CURRENT3(PE_CURRENT_6_0_mA), | ||
| 804 | .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_7_125_mA) | | ||
| 805 | DRIVE_CURRENT_LANE1(DRIVE_CURRENT_7_125_mA) | | ||
| 806 | DRIVE_CURRENT_LANE2(DRIVE_CURRENT_7_125_mA) | | ||
| 807 | DRIVE_CURRENT_LANE3(DRIVE_CURRENT_7_125_mA), | ||
| 808 | }, | ||
| 809 | { /* 1080p modes */ | ||
| 810 | .pclk = INT_MAX, | ||
| 811 | .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | SOR_PLL_RESISTORSEL | | ||
| 812 | SOR_PLL_VCOCAP(1) | SOR_PLL_TX_REG_LOAD(3), | ||
| 813 | .pll1 = SOR_PLL_TMDS_TERM_ENABLE | SOR_PLL_PE_EN, | ||
| 814 | .pe_current = PE_CURRENT0(PE_CURRENT_6_0_mA) | | ||
| 815 | PE_CURRENT1(PE_CURRENT_6_0_mA) | | ||
| 816 | PE_CURRENT2(PE_CURRENT_6_0_mA) | | ||
| 817 | PE_CURRENT3(PE_CURRENT_6_0_mA), | ||
| 818 | .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_7_125_mA) | | ||
| 819 | DRIVE_CURRENT_LANE1(DRIVE_CURRENT_7_125_mA) | | ||
| 820 | DRIVE_CURRENT_LANE2(DRIVE_CURRENT_7_125_mA) | | ||
| 821 | DRIVE_CURRENT_LANE3(DRIVE_CURRENT_7_125_mA), | ||
| 822 | }, | ||
| 823 | }; | ||
| 824 | #endif | ||
| 825 | |||
| 826 | struct tegra_hdmi_audio_config { | ||
| 827 | unsigned pix_clock; | ||
| 828 | unsigned n; | ||
| 829 | unsigned cts; | ||
| 830 | unsigned aval; | ||
| 831 | }; | ||
| 832 | |||
| 833 | |||
| 834 | const struct tegra_hdmi_audio_config tegra_hdmi_audio_32k[] = { | ||
| 835 | {25200000, 4096, 25200, 24000}, | ||
| 836 | {27000000, 4096, 27000, 24000}, | ||
| 837 | {74250000, 4096, 74250, 24000}, | ||
| 838 | {148500000, 4096, 148500, 24000}, | ||
| 839 | {0, 0, 0}, | ||
| 840 | }; | ||
| 841 | |||
| 842 | const struct tegra_hdmi_audio_config tegra_hdmi_audio_44_1k[] = { | ||
| 843 | {25200000, 5880, 26250, 25000}, | ||
| 844 | {27000000, 5880, 28125, 25000}, | ||
| 845 | {74250000, 4704, 61875, 20000}, | ||
| 846 | {148500000, 4704, 123750, 20000}, | ||
| 847 | {0, 0, 0}, | ||
| 848 | }; | ||
| 849 | |||
| 850 | const struct tegra_hdmi_audio_config tegra_hdmi_audio_48k[] = { | ||
| 851 | {25200000, 6144, 25200, 24000}, | ||
| 852 | {27000000, 6144, 27000, 24000}, | ||
| 853 | {74250000, 6144, 74250, 24000}, | ||
| 854 | {148500000, 6144, 148500, 24000}, | ||
| 855 | {0, 0, 0}, | ||
| 856 | }; | ||
| 857 | |||
| 858 | const struct tegra_hdmi_audio_config tegra_hdmi_audio_88_2k[] = { | ||
| 859 | {25200000, 11760, 26250, 25000}, | ||
| 860 | {27000000, 11760, 28125, 25000}, | ||
| 861 | {74250000, 9408, 61875, 20000}, | ||
| 862 | {148500000, 9408, 123750, 20000}, | ||
| 863 | {0, 0, 0}, | ||
| 864 | }; | ||
| 865 | |||
| 866 | const struct tegra_hdmi_audio_config tegra_hdmi_audio_96k[] = { | ||
| 867 | {25200000, 12288, 25200, 24000}, | ||
| 868 | {27000000, 12288, 27000, 24000}, | ||
| 869 | {74250000, 12288, 74250, 24000}, | ||
| 870 | {148500000, 12288, 148500, 24000}, | ||
| 871 | {0, 0, 0}, | ||
| 872 | }; | ||
| 873 | |||
| 874 | const struct tegra_hdmi_audio_config tegra_hdmi_audio_176_4k[] = { | ||
| 875 | {25200000, 23520, 26250, 25000}, | ||
| 876 | {27000000, 23520, 28125, 25000}, | ||
| 877 | {74250000, 18816, 61875, 20000}, | ||
| 878 | {148500000, 18816, 123750, 20000}, | ||
| 879 | {0, 0, 0}, | ||
| 880 | }; | ||
| 881 | |||
| 882 | const struct tegra_hdmi_audio_config tegra_hdmi_audio_192k[] = { | ||
| 883 | {25200000, 24576, 25200, 24000}, | ||
| 884 | {27000000, 24576, 27000, 24000}, | ||
| 885 | {74250000, 24576, 74250, 24000}, | ||
| 886 | {148500000, 24576, 148500, 24000}, | ||
| 887 | {0, 0, 0}, | ||
| 888 | }; | ||
| 889 | |||
| 890 | static const struct tegra_hdmi_audio_config | ||
| 891 | *tegra_hdmi_get_audio_config(unsigned audio_freq, unsigned pix_clock) | ||
| 892 | { | ||
| 893 | const struct tegra_hdmi_audio_config *table; | ||
| 894 | |||
| 895 | switch (audio_freq) { | ||
| 896 | case AUDIO_FREQ_32K: | ||
| 897 | table = tegra_hdmi_audio_32k; | ||
| 898 | break; | ||
| 899 | case AUDIO_FREQ_44_1K: | ||
| 900 | table = tegra_hdmi_audio_44_1k; | ||
| 901 | break; | ||
| 902 | case AUDIO_FREQ_48K: | ||
| 903 | table = tegra_hdmi_audio_48k; | ||
| 904 | break; | ||
| 905 | case AUDIO_FREQ_88_2K: | ||
| 906 | table = tegra_hdmi_audio_88_2k; | ||
| 907 | break; | ||
| 908 | case AUDIO_FREQ_96K: | ||
| 909 | table = tegra_hdmi_audio_96k; | ||
| 910 | break; | ||
| 911 | case AUDIO_FREQ_176_4K: | ||
| 912 | table = tegra_hdmi_audio_176_4k; | ||
| 913 | break; | ||
| 914 | case AUDIO_FREQ_192K: | ||
| 915 | table = tegra_hdmi_audio_192k; | ||
| 916 | break; | ||
| 917 | default: | ||
| 918 | return NULL; | ||
| 919 | } | ||
| 920 | |||
| 921 | while (table->pix_clock) { | ||
| 922 | if (table->pix_clock == pix_clock) | ||
| 923 | return table; | ||
| 924 | table++; | ||
| 925 | } | ||
| 926 | |||
| 927 | return NULL; | ||
| 928 | } | ||
| 929 | |||
| 930 | |||
| 931 | unsigned long tegra_hdmi_readl(struct tegra_dc_hdmi_data *hdmi, | ||
| 932 | unsigned long reg) | ||
| 933 | { | ||
| 934 | return readl(hdmi->base + reg * 4); | ||
| 935 | } | ||
| 936 | |||
| 937 | void tegra_hdmi_writel(struct tegra_dc_hdmi_data *hdmi, | ||
| 938 | unsigned long val, unsigned long reg) | ||
| 939 | { | ||
| 940 | writel(val, hdmi->base + reg * 4); | ||
| 941 | } | ||
| 942 | |||
| 943 | static inline void tegra_hdmi_clrsetbits(struct tegra_dc_hdmi_data *hdmi, | ||
| 944 | unsigned long reg, unsigned long clr, | ||
| 945 | unsigned long set) | ||
| 946 | { | ||
| 947 | unsigned long val = tegra_hdmi_readl(hdmi, reg); | ||
| 948 | val &= ~clr; | ||
| 949 | val |= set; | ||
| 950 | tegra_hdmi_writel(hdmi, val, reg); | ||
| 951 | } | ||
| 952 | |||
| 953 | #ifdef CONFIG_DEBUG_FS | ||
| 954 | static int dbg_hdmi_show(struct seq_file *s, void *unused) | ||
| 955 | { | ||
| 956 | struct tegra_dc_hdmi_data *hdmi = s->private; | ||
| 957 | |||
| 958 | #define DUMP_REG(a) do { \ | ||
| 959 | seq_printf(s, "%-32s\t%03x\t%08lx\n", \ | ||
| 960 | #a, a, tegra_hdmi_readl(hdmi, a)); \ | ||
| 961 | } while (0) | ||
| 962 | |||
| 963 | tegra_dc_io_start(hdmi->dc); | ||
| 964 | clk_enable(hdmi->clk); | ||
| 965 | |||
| 966 | DUMP_REG(HDMI_CTXSW); | ||
| 967 | DUMP_REG(HDMI_NV_PDISP_SOR_STATE0); | ||
| 968 | DUMP_REG(HDMI_NV_PDISP_SOR_STATE1); | ||
| 969 | DUMP_REG(HDMI_NV_PDISP_SOR_STATE2); | ||
| 970 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_AN_MSB); | ||
| 971 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_AN_LSB); | ||
| 972 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CN_MSB); | ||
| 973 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CN_LSB); | ||
| 974 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_AKSV_MSB); | ||
| 975 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_AKSV_LSB); | ||
| 976 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_BKSV_MSB); | ||
| 977 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_BKSV_LSB); | ||
| 978 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CKSV_MSB); | ||
| 979 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CKSV_LSB); | ||
| 980 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_DKSV_MSB); | ||
| 981 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_DKSV_LSB); | ||
| 982 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CTRL); | ||
| 983 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CMODE); | ||
| 984 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_MPRIME_MSB); | ||
| 985 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_MPRIME_LSB); | ||
| 986 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_SPRIME_MSB); | ||
| 987 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB2); | ||
| 988 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB1); | ||
| 989 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_RI); | ||
| 990 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CS_MSB); | ||
| 991 | DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CS_LSB); | ||
| 992 | DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_EMU0); | ||
| 993 | DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_EMU_RDATA0); | ||
| 994 | DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_EMU1); | ||
| 995 | DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_EMU2); | ||
| 996 | DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); | ||
| 997 | DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_STATUS); | ||
| 998 | DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER); | ||
| 999 | DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW); | ||
| 1000 | DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH); | ||
| 1001 | DUMP_REG(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL); | ||
| 1002 | DUMP_REG(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_STATUS); | ||
| 1003 | DUMP_REG(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER); | ||
| 1004 | DUMP_REG(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW); | ||
| 1005 | DUMP_REG(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH); | ||
| 1006 | DUMP_REG(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW); | ||
| 1007 | DUMP_REG(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH); | ||
| 1008 | DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_CTRL); | ||
| 1009 | DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_STATUS); | ||
| 1010 | DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_HEADER); | ||
| 1011 | DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_LOW); | ||
| 1012 | DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_HIGH); | ||
| 1013 | DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_LOW); | ||
| 1014 | DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_HIGH); | ||
| 1015 | DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_LOW); | ||
| 1016 | DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_HIGH); | ||
| 1017 | DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_LOW); | ||
| 1018 | DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_HIGH); | ||
| 1019 | DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_CTRL); | ||
| 1020 | DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW); | ||
| 1021 | DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_HIGH); | ||
| 1022 | DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW); | ||
| 1023 | DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH); | ||
| 1024 | DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW); | ||
| 1025 | DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_HIGH); | ||
| 1026 | DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW); | ||
| 1027 | DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_HIGH); | ||
| 1028 | DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW); | ||
| 1029 | DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_HIGH); | ||
| 1030 | DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW); | ||
| 1031 | DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_HIGH); | ||
| 1032 | DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW); | ||
| 1033 | DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_HIGH); | ||
| 1034 | DUMP_REG(HDMI_NV_PDISP_HDMI_CTRL); | ||
| 1035 | DUMP_REG(HDMI_NV_PDISP_HDMI_VSYNC_KEEPOUT); | ||
| 1036 | DUMP_REG(HDMI_NV_PDISP_HDMI_VSYNC_WINDOW); | ||
| 1037 | DUMP_REG(HDMI_NV_PDISP_HDMI_GCP_CTRL); | ||
| 1038 | DUMP_REG(HDMI_NV_PDISP_HDMI_GCP_STATUS); | ||
| 1039 | DUMP_REG(HDMI_NV_PDISP_HDMI_GCP_SUBPACK); | ||
| 1040 | DUMP_REG(HDMI_NV_PDISP_HDMI_CHANNEL_STATUS1); | ||
| 1041 | DUMP_REG(HDMI_NV_PDISP_HDMI_CHANNEL_STATUS2); | ||
| 1042 | DUMP_REG(HDMI_NV_PDISP_HDMI_EMU0); | ||
| 1043 | DUMP_REG(HDMI_NV_PDISP_HDMI_EMU1); | ||
| 1044 | DUMP_REG(HDMI_NV_PDISP_HDMI_EMU1_RDATA); | ||
| 1045 | DUMP_REG(HDMI_NV_PDISP_HDMI_SPARE); | ||
| 1046 | DUMP_REG(HDMI_NV_PDISP_HDMI_SPDIF_CHN_STATUS1); | ||
| 1047 | DUMP_REG(HDMI_NV_PDISP_HDMI_SPDIF_CHN_STATUS2); | ||
| 1048 | DUMP_REG(HDMI_NV_PDISP_HDCPRIF_ROM_CTRL); | ||
| 1049 | DUMP_REG(HDMI_NV_PDISP_SOR_CAP); | ||
| 1050 | DUMP_REG(HDMI_NV_PDISP_SOR_PWR); | ||
| 1051 | DUMP_REG(HDMI_NV_PDISP_SOR_TEST); | ||
| 1052 | DUMP_REG(HDMI_NV_PDISP_SOR_PLL0); | ||
| 1053 | DUMP_REG(HDMI_NV_PDISP_SOR_PLL1); | ||
| 1054 | DUMP_REG(HDMI_NV_PDISP_SOR_PLL2); | ||
| 1055 | DUMP_REG(HDMI_NV_PDISP_SOR_CSTM); | ||
| 1056 | DUMP_REG(HDMI_NV_PDISP_SOR_LVDS); | ||
| 1057 | DUMP_REG(HDMI_NV_PDISP_SOR_CRCA); | ||
| 1058 | DUMP_REG(HDMI_NV_PDISP_SOR_CRCB); | ||
| 1059 | DUMP_REG(HDMI_NV_PDISP_SOR_BLANK); | ||
| 1060 | DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_CTL); | ||
| 1061 | DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST0); | ||
| 1062 | DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST1); | ||
| 1063 | DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST2); | ||
| 1064 | DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST3); | ||
| 1065 | DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST4); | ||
| 1066 | DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST5); | ||
| 1067 | DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST6); | ||
| 1068 | DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST7); | ||
| 1069 | DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST8); | ||
| 1070 | DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST9); | ||
| 1071 | DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INSTA); | ||
| 1072 | DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INSTB); | ||
| 1073 | DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INSTC); | ||
| 1074 | DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INSTD); | ||
| 1075 | DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INSTE); | ||
| 1076 | DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INSTF); | ||
| 1077 | DUMP_REG(HDMI_NV_PDISP_SOR_VCRCA0); | ||
| 1078 | DUMP_REG(HDMI_NV_PDISP_SOR_VCRCA1); | ||
| 1079 | DUMP_REG(HDMI_NV_PDISP_SOR_CCRCA0); | ||
| 1080 | DUMP_REG(HDMI_NV_PDISP_SOR_CCRCA1); | ||
| 1081 | DUMP_REG(HDMI_NV_PDISP_SOR_EDATAA0); | ||
| 1082 | DUMP_REG(HDMI_NV_PDISP_SOR_EDATAA1); | ||
| 1083 | DUMP_REG(HDMI_NV_PDISP_SOR_COUNTA0); | ||
| 1084 | DUMP_REG(HDMI_NV_PDISP_SOR_COUNTA1); | ||
| 1085 | DUMP_REG(HDMI_NV_PDISP_SOR_DEBUGA0); | ||
| 1086 | DUMP_REG(HDMI_NV_PDISP_SOR_DEBUGA1); | ||
| 1087 | DUMP_REG(HDMI_NV_PDISP_SOR_TRIG); | ||
| 1088 | DUMP_REG(HDMI_NV_PDISP_SOR_MSCHECK); | ||
| 1089 | DUMP_REG(HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT); | ||
| 1090 | DUMP_REG(HDMI_NV_PDISP_AUDIO_DEBUG0); | ||
| 1091 | DUMP_REG(HDMI_NV_PDISP_AUDIO_DEBUG1); | ||
| 1092 | DUMP_REG(HDMI_NV_PDISP_AUDIO_DEBUG2); | ||
| 1093 | DUMP_REG(HDMI_NV_PDISP_AUDIO_FS(0)); | ||
| 1094 | DUMP_REG(HDMI_NV_PDISP_AUDIO_FS(1)); | ||
| 1095 | DUMP_REG(HDMI_NV_PDISP_AUDIO_FS(2)); | ||
| 1096 | DUMP_REG(HDMI_NV_PDISP_AUDIO_FS(3)); | ||
| 1097 | DUMP_REG(HDMI_NV_PDISP_AUDIO_FS(4)); | ||
| 1098 | DUMP_REG(HDMI_NV_PDISP_AUDIO_FS(5)); | ||
| 1099 | DUMP_REG(HDMI_NV_PDISP_AUDIO_FS(6)); | ||
| 1100 | DUMP_REG(HDMI_NV_PDISP_AUDIO_PULSE_WIDTH); | ||
| 1101 | DUMP_REG(HDMI_NV_PDISP_AUDIO_THRESHOLD); | ||
| 1102 | DUMP_REG(HDMI_NV_PDISP_AUDIO_CNTRL0); | ||
| 1103 | DUMP_REG(HDMI_NV_PDISP_AUDIO_N); | ||
| 1104 | DUMP_REG(HDMI_NV_PDISP_HDCPRIF_ROM_TIMING); | ||
| 1105 | DUMP_REG(HDMI_NV_PDISP_SOR_REFCLK); | ||
| 1106 | DUMP_REG(HDMI_NV_PDISP_CRC_CONTROL); | ||
| 1107 | DUMP_REG(HDMI_NV_PDISP_INPUT_CONTROL); | ||
| 1108 | DUMP_REG(HDMI_NV_PDISP_SCRATCH); | ||
| 1109 | DUMP_REG(HDMI_NV_PDISP_PE_CURRENT); | ||
| 1110 | DUMP_REG(HDMI_NV_PDISP_KEY_CTRL); | ||
| 1111 | DUMP_REG(HDMI_NV_PDISP_KEY_DEBUG0); | ||
| 1112 | DUMP_REG(HDMI_NV_PDISP_KEY_DEBUG1); | ||
| 1113 | DUMP_REG(HDMI_NV_PDISP_KEY_DEBUG2); | ||
| 1114 | DUMP_REG(HDMI_NV_PDISP_KEY_HDCP_KEY_0); | ||
| 1115 | DUMP_REG(HDMI_NV_PDISP_KEY_HDCP_KEY_1); | ||
| 1116 | DUMP_REG(HDMI_NV_PDISP_KEY_HDCP_KEY_2); | ||
| 1117 | DUMP_REG(HDMI_NV_PDISP_KEY_HDCP_KEY_3); | ||
| 1118 | DUMP_REG(HDMI_NV_PDISP_KEY_HDCP_KEY_TRIG); | ||
| 1119 | DUMP_REG(HDMI_NV_PDISP_KEY_SKEY_INDEX); | ||
| 1120 | #undef DUMP_REG | ||
| 1121 | |||
| 1122 | clk_disable(hdmi->clk); | ||
| 1123 | tegra_dc_io_end(hdmi->dc); | ||
| 1124 | |||
| 1125 | return 0; | ||
| 1126 | } | ||
| 1127 | |||
| 1128 | static int dbg_hdmi_open(struct inode *inode, struct file *file) | ||
| 1129 | { | ||
| 1130 | return single_open(file, dbg_hdmi_show, inode->i_private); | ||
| 1131 | } | ||
| 1132 | |||
| 1133 | static const struct file_operations dbg_fops = { | ||
| 1134 | .open = dbg_hdmi_open, | ||
| 1135 | .read = seq_read, | ||
| 1136 | .llseek = seq_lseek, | ||
| 1137 | .release = single_release, | ||
| 1138 | }; | ||
| 1139 | |||
| 1140 | static struct dentry *hdmidir; | ||
| 1141 | |||
| 1142 | static void tegra_dc_hdmi_debug_create(struct tegra_dc_hdmi_data *hdmi) | ||
| 1143 | { | ||
| 1144 | struct dentry *retval; | ||
| 1145 | |||
| 1146 | hdmidir = debugfs_create_dir("tegra_hdmi", NULL); | ||
| 1147 | if (!hdmidir) | ||
| 1148 | return; | ||
| 1149 | retval = debugfs_create_file("regs", S_IRUGO, hdmidir, hdmi, | ||
| 1150 | &dbg_fops); | ||
| 1151 | if (!retval) | ||
| 1152 | goto free_out; | ||
| 1153 | return; | ||
| 1154 | free_out: | ||
| 1155 | debugfs_remove_recursive(hdmidir); | ||
| 1156 | hdmidir = NULL; | ||
| 1157 | return; | ||
| 1158 | } | ||
| 1159 | #else | ||
| 1160 | static inline void tegra_dc_hdmi_debug_create(struct tegra_dc_hdmi_data *hdmi) | ||
| 1161 | { } | ||
| 1162 | #endif | ||
| 1163 | |||
| 1164 | #define PIXCLOCK_TOLERANCE 200 | ||
| 1165 | |||
| 1166 | static int tegra_dc_calc_clock_per_frame(const struct fb_videomode *mode) | ||
| 1167 | { | ||
| 1168 | return (mode->left_margin + mode->xres + | ||
| 1169 | mode->right_margin + mode->hsync_len) * | ||
| 1170 | (mode->upper_margin + mode->yres + | ||
| 1171 | mode->lower_margin + mode->vsync_len); | ||
| 1172 | } | ||
| 1173 | static bool tegra_dc_hdmi_mode_equal(const struct fb_videomode *mode1, | ||
| 1174 | const struct fb_videomode *mode2) | ||
| 1175 | { | ||
| 1176 | int clock_per_frame1 = tegra_dc_calc_clock_per_frame(mode1); | ||
| 1177 | int clock_per_frame2 = tegra_dc_calc_clock_per_frame(mode2); | ||
| 1178 | |||
| 1179 | /* allows up to 1Hz of pixclock difference */ | ||
| 1180 | return (clock_per_frame1 == clock_per_frame2 && | ||
| 1181 | mode1->xres == mode2->xres && | ||
| 1182 | mode1->yres == mode2->yres && | ||
| 1183 | mode1->vmode == mode2->vmode && | ||
| 1184 | (mode1->pixclock == mode2->pixclock || | ||
| 1185 | (abs(PICOS2KHZ(mode1->pixclock) - | ||
| 1186 | PICOS2KHZ(mode2->pixclock)) * | ||
| 1187 | 1000 / clock_per_frame1 <= 1))); | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | static bool tegra_dc_hdmi_valid_pixclock(const struct tegra_dc *dc, | ||
| 1191 | const struct fb_videomode *mode) | ||
| 1192 | { | ||
| 1193 | unsigned max_pixclock = tegra_dc_get_out_max_pixclock(dc); | ||
| 1194 | if (max_pixclock) { | ||
| 1195 | /* this might look counter-intuitive, | ||
| 1196 | * but pixclock's unit is picos(not Khz) | ||
| 1197 | */ | ||
| 1198 | return mode->pixclock >= max_pixclock; | ||
| 1199 | } else { | ||
| 1200 | return true; | ||
| 1201 | } | ||
| 1202 | } | ||
| 1203 | |||
| 1204 | static bool tegra_dc_cvt_mode_equal(const struct fb_videomode *mode1, | ||
| 1205 | const struct fb_videomode *mode2) | ||
| 1206 | { | ||
| 1207 | return (mode1->xres == mode2->xres && | ||
| 1208 | mode1->yres == mode2->yres && | ||
| 1209 | mode1->refresh == mode2->refresh && | ||
| 1210 | mode1->vmode == mode2->vmode); | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | static bool tegra_dc_reload_mode(struct fb_videomode *mode) | ||
| 1214 | { | ||
| 1215 | int i = 0; | ||
| 1216 | for (i = 0; i < ARRAY_SIZE(tegra_dc_hdmi_supported_cvt_modes); i++) { | ||
| 1217 | const struct fb_videomode *cvt_mode | ||
| 1218 | = &tegra_dc_hdmi_supported_cvt_modes[i]; | ||
| 1219 | if (tegra_dc_cvt_mode_equal(cvt_mode, mode)) { | ||
| 1220 | memcpy(mode, cvt_mode, sizeof(*mode)); | ||
| 1221 | return true; | ||
| 1222 | } | ||
| 1223 | } | ||
| 1224 | return false; | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | |||
| 1228 | static bool tegra_dc_hdmi_mode_filter(const struct tegra_dc *dc, | ||
| 1229 | struct fb_videomode *mode) | ||
| 1230 | { | ||
| 1231 | int i; | ||
| 1232 | int clock_per_frame; | ||
| 1233 | |||
| 1234 | if (!mode->pixclock) | ||
| 1235 | return false; | ||
| 1236 | |||
| 1237 | #ifdef CONFIG_TEGRA_HDMI_74MHZ_LIMIT | ||
| 1238 | if (PICOS2KHZ(mode->pixclock) > 74250) | ||
| 1239 | return false; | ||
| 1240 | #endif | ||
| 1241 | |||
| 1242 | for (i = 0; i < ARRAY_SIZE(tegra_dc_hdmi_supported_modes); i++) { | ||
| 1243 | const struct fb_videomode *supported_mode | ||
| 1244 | = &tegra_dc_hdmi_supported_modes[i]; | ||
| 1245 | if (tegra_dc_hdmi_mode_equal(supported_mode, mode) && | ||
| 1246 | tegra_dc_hdmi_valid_pixclock(dc, supported_mode)) { | ||
| 1247 | if (mode->lower_margin == 1) { | ||
| 1248 | /* This might be the case for HDMI<->DVI | ||
| 1249 | * where std VESA representation will not | ||
| 1250 | * pass constraint V_FRONT_PORCH >= | ||
| 1251 | * V_REF_TO_SYNC + 1.So reload mode in | ||
| 1252 | * CVT timing standards. | ||
| 1253 | */ | ||
| 1254 | if (!tegra_dc_reload_mode(mode)) | ||
| 1255 | return false; | ||
| 1256 | } | ||
| 1257 | else | ||
| 1258 | memcpy(mode, supported_mode, sizeof(*mode)); | ||
| 1259 | |||
| 1260 | mode->flag = FB_MODE_IS_DETAILED; | ||
| 1261 | clock_per_frame = tegra_dc_calc_clock_per_frame(mode); | ||
| 1262 | mode->refresh = (PICOS2KHZ(mode->pixclock) * 1000) | ||
| 1263 | / clock_per_frame; | ||
| 1264 | return true; | ||
| 1265 | } | ||
| 1266 | } | ||
| 1267 | |||
| 1268 | return false; | ||
| 1269 | } | ||
| 1270 | |||
| 1271 | |||
| 1272 | static bool tegra_dc_hdmi_hpd(struct tegra_dc *dc) | ||
| 1273 | { | ||
| 1274 | int sense; | ||
| 1275 | int level; | ||
| 1276 | |||
| 1277 | level = gpio_get_value(dc->out->hotplug_gpio); | ||
| 1278 | |||
| 1279 | sense = dc->out->flags & TEGRA_DC_OUT_HOTPLUG_MASK; | ||
| 1280 | |||
| 1281 | return (sense == TEGRA_DC_OUT_HOTPLUG_HIGH && level) || | ||
| 1282 | (sense == TEGRA_DC_OUT_HOTPLUG_LOW && !level); | ||
| 1283 | } | ||
| 1284 | |||
| 1285 | |||
| 1286 | void tegra_dc_hdmi_detect_config(struct tegra_dc *dc, | ||
| 1287 | struct fb_monspecs *specs) | ||
| 1288 | { | ||
| 1289 | struct tegra_dc_hdmi_data *hdmi = tegra_dc_get_outdata(dc); | ||
| 1290 | |||
| 1291 | /* monitors like to lie about these but they are still useful for | ||
| 1292 | * detecting aspect ratios | ||
| 1293 | */ | ||
| 1294 | dc->out->h_size = specs->max_x * 1000; | ||
| 1295 | dc->out->v_size = specs->max_y * 1000; | ||
| 1296 | |||
| 1297 | hdmi->dvi = !(specs->misc & FB_MISC_HDMI); | ||
| 1298 | |||
| 1299 | tegra_fb_update_monspecs(dc->fb, specs, tegra_dc_hdmi_mode_filter); | ||
| 1300 | #ifdef CONFIG_SWITCH | ||
| 1301 | hdmi->hpd_switch.state = 0; | ||
| 1302 | switch_set_state(&hdmi->hpd_switch, 1); | ||
| 1303 | #endif | ||
| 1304 | dev_info(&dc->ndev->dev, "display detected\n"); | ||
| 1305 | |||
| 1306 | dc->connected = true; | ||
| 1307 | tegra_dc_ext_process_hotplug(dc->ndev->id); | ||
| 1308 | } | ||
| 1309 | |||
| 1310 | /* This function is used to enable DC1 and HDMI for the purpose of testing. */ | ||
| 1311 | bool tegra_dc_hdmi_detect_test(struct tegra_dc *dc, unsigned char *edid_ptr) | ||
| 1312 | { | ||
| 1313 | int err; | ||
| 1314 | struct fb_monspecs specs; | ||
| 1315 | struct tegra_dc_hdmi_data *hdmi = tegra_dc_get_outdata(dc); | ||
| 1316 | |||
| 1317 | if (!hdmi || !edid_ptr) { | ||
| 1318 | dev_err(&dc->ndev->dev, "HDMI test failed to get arguments.\n"); | ||
| 1319 | return false; | ||
| 1320 | } | ||
| 1321 | |||
| 1322 | err = tegra_edid_get_monspecs_test(hdmi->edid, &specs, edid_ptr); | ||
| 1323 | if (err < 0) { | ||
| 1324 | dev_err(&dc->ndev->dev, "error reading edid\n"); | ||
| 1325 | goto fail; | ||
| 1326 | } | ||
| 1327 | |||
| 1328 | err = tegra_edid_get_eld(hdmi->edid, &hdmi->eld); | ||
| 1329 | if (err < 0) { | ||
| 1330 | dev_err(&dc->ndev->dev, "error populating eld\n"); | ||
| 1331 | goto fail; | ||
| 1332 | } | ||
| 1333 | hdmi->eld_retrieved = true; | ||
| 1334 | |||
| 1335 | tegra_dc_hdmi_detect_config(dc, &specs); | ||
| 1336 | |||
| 1337 | return true; | ||
| 1338 | |||
| 1339 | fail: | ||
| 1340 | hdmi->eld_retrieved = false; | ||
| 1341 | #ifdef CONFIG_SWITCH | ||
| 1342 | switch_set_state(&hdmi->hpd_switch, 0); | ||
| 1343 | #endif | ||
| 1344 | tegra_nvhdcp_set_plug(hdmi->nvhdcp, 0); | ||
| 1345 | return false; | ||
| 1346 | } | ||
| 1347 | EXPORT_SYMBOL(tegra_dc_hdmi_detect_test); | ||
| 1348 | |||
| 1349 | static bool tegra_dc_hdmi_detect(struct tegra_dc *dc) | ||
| 1350 | { | ||
| 1351 | struct tegra_dc_hdmi_data *hdmi = tegra_dc_get_outdata(dc); | ||
| 1352 | struct fb_monspecs specs; | ||
| 1353 | int err; | ||
| 1354 | |||
| 1355 | if (!tegra_dc_hdmi_hpd(dc)) | ||
| 1356 | goto fail; | ||
| 1357 | |||
| 1358 | err = tegra_edid_get_monspecs(hdmi->edid, &specs); | ||
| 1359 | if (err < 0) { | ||
| 1360 | dev_err(&dc->ndev->dev, "error reading edid\n"); | ||
| 1361 | goto fail; | ||
| 1362 | } | ||
| 1363 | |||
| 1364 | err = tegra_edid_get_eld(hdmi->edid, &hdmi->eld); | ||
| 1365 | if (err < 0) { | ||
| 1366 | dev_err(&dc->ndev->dev, "error populating eld\n"); | ||
| 1367 | goto fail; | ||
| 1368 | } | ||
| 1369 | hdmi->eld_retrieved = true; | ||
| 1370 | |||
| 1371 | tegra_dc_hdmi_detect_config(dc, &specs); | ||
| 1372 | |||
| 1373 | return true; | ||
| 1374 | |||
| 1375 | fail: | ||
| 1376 | hdmi->eld_retrieved = false; | ||
| 1377 | #ifdef CONFIG_SWITCH | ||
| 1378 | switch_set_state(&hdmi->hpd_switch, 0); | ||
| 1379 | #endif | ||
| 1380 | tegra_nvhdcp_set_plug(hdmi->nvhdcp, 0); | ||
| 1381 | return false; | ||
| 1382 | } | ||
| 1383 | |||
| 1384 | |||
| 1385 | static void tegra_dc_hdmi_detect_worker(struct work_struct *work) | ||
| 1386 | { | ||
| 1387 | struct tegra_dc_hdmi_data *hdmi = | ||
| 1388 | container_of(to_delayed_work(work), struct tegra_dc_hdmi_data, work); | ||
| 1389 | struct tegra_dc *dc = hdmi->dc; | ||
| 1390 | |||
| 1391 | tegra_dc_enable(dc); | ||
| 1392 | msleep(5); | ||
| 1393 | if (!tegra_dc_hdmi_detect(dc)) { | ||
| 1394 | tegra_dc_disable(dc); | ||
| 1395 | tegra_fb_update_monspecs(dc->fb, NULL, NULL); | ||
| 1396 | |||
| 1397 | dc->connected = false; | ||
| 1398 | tegra_dc_ext_process_hotplug(dc->ndev->id); | ||
| 1399 | } | ||
| 1400 | } | ||
| 1401 | |||
| 1402 | static irqreturn_t tegra_dc_hdmi_irq(int irq, void *ptr) | ||
| 1403 | { | ||
| 1404 | struct tegra_dc *dc = ptr; | ||
| 1405 | struct tegra_dc_hdmi_data *hdmi = tegra_dc_get_outdata(dc); | ||
| 1406 | unsigned long flags; | ||
| 1407 | |||
| 1408 | spin_lock_irqsave(&hdmi->suspend_lock, flags); | ||
| 1409 | if (!hdmi->suspended) { | ||
| 1410 | __cancel_delayed_work(&hdmi->work); | ||
| 1411 | if (tegra_dc_hdmi_hpd(dc)) | ||
| 1412 | queue_delayed_work(system_nrt_wq, &hdmi->work, | ||
| 1413 | msecs_to_jiffies(100)); | ||
| 1414 | else | ||
| 1415 | queue_delayed_work(system_nrt_wq, &hdmi->work, | ||
| 1416 | msecs_to_jiffies(30)); | ||
| 1417 | } | ||
| 1418 | spin_unlock_irqrestore(&hdmi->suspend_lock, flags); | ||
| 1419 | |||
| 1420 | return IRQ_HANDLED; | ||
| 1421 | } | ||
| 1422 | |||
| 1423 | static void tegra_dc_hdmi_suspend(struct tegra_dc *dc) | ||
| 1424 | { | ||
| 1425 | struct tegra_dc_hdmi_data *hdmi = tegra_dc_get_outdata(dc); | ||
| 1426 | unsigned long flags; | ||
| 1427 | |||
| 1428 | tegra_nvhdcp_suspend(hdmi->nvhdcp); | ||
| 1429 | spin_lock_irqsave(&hdmi->suspend_lock, flags); | ||
| 1430 | hdmi->suspended = true; | ||
| 1431 | spin_unlock_irqrestore(&hdmi->suspend_lock, flags); | ||
| 1432 | } | ||
| 1433 | |||
| 1434 | static void tegra_dc_hdmi_resume(struct tegra_dc *dc) | ||
| 1435 | { | ||
| 1436 | struct tegra_dc_hdmi_data *hdmi = tegra_dc_get_outdata(dc); | ||
| 1437 | unsigned long flags; | ||
| 1438 | |||
| 1439 | spin_lock_irqsave(&hdmi->suspend_lock, flags); | ||
| 1440 | hdmi->suspended = false; | ||
| 1441 | |||
| 1442 | if (tegra_dc_hdmi_hpd(dc)) | ||
| 1443 | queue_delayed_work(system_nrt_wq, &hdmi->work, | ||
| 1444 | msecs_to_jiffies(100)); | ||
| 1445 | else | ||
| 1446 | queue_delayed_work(system_nrt_wq, &hdmi->work, | ||
| 1447 | msecs_to_jiffies(30)); | ||
| 1448 | |||
| 1449 | spin_unlock_irqrestore(&hdmi->suspend_lock, flags); | ||
| 1450 | tegra_nvhdcp_resume(hdmi->nvhdcp); | ||
| 1451 | } | ||
| 1452 | |||
| 1453 | static ssize_t underscan_show(struct device *dev, | ||
| 1454 | struct device_attribute *attr, char *buf) | ||
| 1455 | { | ||
| 1456 | #ifdef CONFIG_SWITCH | ||
| 1457 | struct tegra_dc_hdmi_data *hdmi = | ||
| 1458 | container_of(dev_get_drvdata(dev), struct tegra_dc_hdmi_data, hpd_switch); | ||
| 1459 | |||
| 1460 | if (hdmi->edid) | ||
| 1461 | return sprintf(buf, "%d\n", tegra_edid_underscan_supported(hdmi->edid)); | ||
| 1462 | else | ||
| 1463 | return 0; | ||
| 1464 | #else | ||
| 1465 | return 0; | ||
| 1466 | #endif | ||
| 1467 | } | ||
| 1468 | |||
| 1469 | static DEVICE_ATTR(underscan, S_IRUGO | S_IWUSR, underscan_show, NULL); | ||
| 1470 | |||
| 1471 | static int tegra_dc_hdmi_init(struct tegra_dc *dc) | ||
| 1472 | { | ||
| 1473 | struct tegra_dc_hdmi_data *hdmi; | ||
| 1474 | struct resource *res; | ||
| 1475 | struct resource *base_res; | ||
| 1476 | int ret; | ||
| 1477 | void __iomem *base; | ||
| 1478 | struct clk *clk = NULL; | ||
| 1479 | struct clk *disp1_clk = NULL; | ||
| 1480 | struct clk *disp2_clk = NULL; | ||
| 1481 | int err; | ||
| 1482 | |||
| 1483 | hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL); | ||
| 1484 | if (!hdmi) | ||
| 1485 | return -ENOMEM; | ||
| 1486 | |||
| 1487 | res = nvhost_get_resource_byname(dc->ndev, IORESOURCE_MEM, "hdmi_regs"); | ||
| 1488 | if (!res) { | ||
| 1489 | dev_err(&dc->ndev->dev, "hdmi: no mem resource\n"); | ||
| 1490 | err = -ENOENT; | ||
| 1491 | goto err_free_hdmi; | ||
| 1492 | } | ||
| 1493 | |||
| 1494 | base_res = request_mem_region(res->start, resource_size(res), dc->ndev->name); | ||
| 1495 | if (!base_res) { | ||
| 1496 | dev_err(&dc->ndev->dev, "hdmi: request_mem_region failed\n"); | ||
| 1497 | err = -EBUSY; | ||
| 1498 | goto err_free_hdmi; | ||
| 1499 | } | ||
| 1500 | |||
| 1501 | base = ioremap(res->start, resource_size(res)); | ||
| 1502 | if (!base) { | ||
| 1503 | dev_err(&dc->ndev->dev, "hdmi: registers can't be mapped\n"); | ||
| 1504 | err = -EBUSY; | ||
| 1505 | goto err_release_resource_reg; | ||
| 1506 | } | ||
| 1507 | |||
| 1508 | clk = clk_get(&dc->ndev->dev, "hdmi"); | ||
| 1509 | if (IS_ERR_OR_NULL(clk)) { | ||
| 1510 | dev_err(&dc->ndev->dev, "hdmi: can't get clock\n"); | ||
| 1511 | err = -ENOENT; | ||
| 1512 | goto err_iounmap_reg; | ||
| 1513 | } | ||
| 1514 | |||
| 1515 | disp1_clk = clk_get_sys("tegradc.0", NULL); | ||
| 1516 | if (IS_ERR_OR_NULL(disp1_clk)) { | ||
| 1517 | dev_err(&dc->ndev->dev, "hdmi: can't disp1 clock\n"); | ||
| 1518 | err = -ENOENT; | ||
| 1519 | goto err_put_clock; | ||
| 1520 | } | ||
| 1521 | |||
| 1522 | disp2_clk = clk_get_sys("tegradc.1", NULL); | ||
| 1523 | if (IS_ERR_OR_NULL(disp2_clk)) { | ||
| 1524 | dev_err(&dc->ndev->dev, "hdmi: can't disp2 clock\n"); | ||
| 1525 | err = -ENOENT; | ||
| 1526 | goto err_put_clock; | ||
| 1527 | } | ||
| 1528 | |||
| 1529 | #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) | ||
| 1530 | hdmi->hda_clk = clk_get_sys("tegra30-hda", "hda"); | ||
| 1531 | if (IS_ERR_OR_NULL(hdmi->hda_clk)) { | ||
| 1532 | dev_err(&dc->ndev->dev, "hdmi: can't get hda clock\n"); | ||
| 1533 | err = -ENOENT; | ||
| 1534 | goto err_put_clock; | ||
| 1535 | } | ||
| 1536 | |||
| 1537 | hdmi->hda2codec_clk = clk_get_sys("tegra30-hda", "hda2codec"); | ||
| 1538 | if (IS_ERR_OR_NULL(hdmi->hda2codec_clk)) { | ||
| 1539 | dev_err(&dc->ndev->dev, "hdmi: can't get hda2codec clock\n"); | ||
| 1540 | err = -ENOENT; | ||
| 1541 | goto err_put_clock; | ||
| 1542 | } | ||
| 1543 | |||
| 1544 | hdmi->hda2hdmi_clk = clk_get_sys("tegra30-hda", "hda2hdmi"); | ||
| 1545 | if (IS_ERR_OR_NULL(hdmi->hda2hdmi_clk)) { | ||
| 1546 | dev_err(&dc->ndev->dev, "hdmi: can't get hda2hdmi clock\n"); | ||
| 1547 | err = -ENOENT; | ||
| 1548 | goto err_put_clock; | ||
| 1549 | } | ||
| 1550 | #endif | ||
| 1551 | |||
| 1552 | /* TODO: support non-hotplug */ | ||
| 1553 | if (request_irq(gpio_to_irq(dc->out->hotplug_gpio), tegra_dc_hdmi_irq, | ||
| 1554 | IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, | ||
| 1555 | dev_name(&dc->ndev->dev), dc)) { | ||
| 1556 | dev_err(&dc->ndev->dev, "hdmi: request_irq %d failed\n", | ||
| 1557 | gpio_to_irq(dc->out->hotplug_gpio)); | ||
| 1558 | err = -EBUSY; | ||
| 1559 | goto err_put_clock; | ||
| 1560 | } | ||
| 1561 | |||
| 1562 | hdmi->edid = tegra_edid_create(dc->out->dcc_bus); | ||
| 1563 | if (IS_ERR_OR_NULL(hdmi->edid)) { | ||
| 1564 | dev_err(&dc->ndev->dev, "hdmi: can't create edid\n"); | ||
| 1565 | err = PTR_ERR(hdmi->edid); | ||
| 1566 | goto err_free_irq; | ||
| 1567 | } | ||
| 1568 | |||
| 1569 | #ifdef CONFIG_TEGRA_NVHDCP | ||
| 1570 | hdmi->nvhdcp = tegra_nvhdcp_create(hdmi, dc->ndev->id, | ||
| 1571 | dc->out->dcc_bus); | ||
| 1572 | if (IS_ERR_OR_NULL(hdmi->nvhdcp)) { | ||
| 1573 | dev_err(&dc->ndev->dev, "hdmi: can't create nvhdcp\n"); | ||
| 1574 | err = PTR_ERR(hdmi->nvhdcp); | ||
| 1575 | goto err_edid_destroy; | ||
| 1576 | } | ||
| 1577 | #else | ||
| 1578 | hdmi->nvhdcp = NULL; | ||
| 1579 | #endif | ||
| 1580 | |||
| 1581 | INIT_DELAYED_WORK(&hdmi->work, tegra_dc_hdmi_detect_worker); | ||
| 1582 | |||
| 1583 | hdmi->dc = dc; | ||
| 1584 | hdmi->base = base; | ||
| 1585 | hdmi->base_res = base_res; | ||
| 1586 | hdmi->clk = clk; | ||
| 1587 | hdmi->disp1_clk = disp1_clk; | ||
| 1588 | hdmi->disp2_clk = disp2_clk; | ||
| 1589 | hdmi->suspended = false; | ||
| 1590 | hdmi->eld_retrieved= false; | ||
| 1591 | hdmi->clk_enabled = false; | ||
| 1592 | hdmi->audio_freq = 44100; | ||
| 1593 | hdmi->audio_source = AUTO; | ||
| 1594 | spin_lock_init(&hdmi->suspend_lock); | ||
| 1595 | |||
| 1596 | #ifdef CONFIG_SWITCH | ||
| 1597 | hdmi->hpd_switch.name = "hdmi"; | ||
| 1598 | ret = switch_dev_register(&hdmi->hpd_switch); | ||
| 1599 | |||
| 1600 | if (!ret) | ||
| 1601 | ret = device_create_file(hdmi->hpd_switch.dev, | ||
| 1602 | &dev_attr_underscan); | ||
| 1603 | BUG_ON(ret != 0); | ||
| 1604 | #endif | ||
| 1605 | |||
| 1606 | dc->out->depth = 24; | ||
| 1607 | |||
| 1608 | tegra_dc_set_outdata(dc, hdmi); | ||
| 1609 | |||
| 1610 | dc_hdmi = hdmi; | ||
| 1611 | /* boards can select default content protection policy */ | ||
| 1612 | if (dc->out->flags & TEGRA_DC_OUT_NVHDCP_POLICY_ON_DEMAND) | ||
| 1613 | tegra_nvhdcp_set_policy(hdmi->nvhdcp, | ||
| 1614 | TEGRA_NVHDCP_POLICY_ON_DEMAND); | ||
| 1615 | else | ||
| 1616 | tegra_nvhdcp_set_policy(hdmi->nvhdcp, | ||
| 1617 | TEGRA_NVHDCP_POLICY_ALWAYS_ON); | ||
| 1618 | |||
| 1619 | tegra_dc_hdmi_debug_create(hdmi); | ||
| 1620 | |||
| 1621 | return 0; | ||
| 1622 | |||
| 1623 | err_edid_destroy: | ||
| 1624 | tegra_edid_destroy(hdmi->edid); | ||
| 1625 | err_free_irq: | ||
| 1626 | free_irq(gpio_to_irq(dc->out->hotplug_gpio), dc); | ||
| 1627 | err_put_clock: | ||
| 1628 | #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) | ||
| 1629 | if (!IS_ERR_OR_NULL(hdmi->hda2hdmi_clk)) | ||
| 1630 | clk_put(hdmi->hda2hdmi_clk); | ||
| 1631 | if (!IS_ERR_OR_NULL(hdmi->hda2codec_clk)) | ||
| 1632 | clk_put(hdmi->hda2codec_clk); | ||
| 1633 | if (!IS_ERR_OR_NULL(hdmi->hda_clk)) | ||
| 1634 | clk_put(hdmi->hda_clk); | ||
| 1635 | #endif | ||
| 1636 | if (!IS_ERR_OR_NULL(disp2_clk)) | ||
| 1637 | clk_put(disp2_clk); | ||
| 1638 | if (!IS_ERR_OR_NULL(disp1_clk)) | ||
| 1639 | clk_put(disp1_clk); | ||
| 1640 | if (!IS_ERR_OR_NULL(clk)) | ||
| 1641 | clk_put(clk); | ||
| 1642 | err_iounmap_reg: | ||
| 1643 | iounmap(base); | ||
| 1644 | err_release_resource_reg: | ||
| 1645 | release_resource(base_res); | ||
| 1646 | err_free_hdmi: | ||
| 1647 | kfree(hdmi); | ||
| 1648 | return err; | ||
| 1649 | } | ||
| 1650 | |||
| 1651 | static void tegra_dc_hdmi_destroy(struct tegra_dc *dc) | ||
| 1652 | { | ||
| 1653 | struct tegra_dc_hdmi_data *hdmi = tegra_dc_get_outdata(dc); | ||
| 1654 | |||
| 1655 | free_irq(gpio_to_irq(dc->out->hotplug_gpio), dc); | ||
| 1656 | cancel_delayed_work_sync(&hdmi->work); | ||
| 1657 | #ifdef CONFIG_SWITCH | ||
| 1658 | switch_dev_unregister(&hdmi->hpd_switch); | ||
| 1659 | #endif | ||
| 1660 | iounmap(hdmi->base); | ||
| 1661 | release_resource(hdmi->base_res); | ||
| 1662 | #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) | ||
| 1663 | clk_put(hdmi->hda2hdmi_clk); | ||
| 1664 | clk_put(hdmi->hda2codec_clk); | ||
| 1665 | clk_put(hdmi->hda_clk); | ||
| 1666 | #endif | ||
| 1667 | clk_put(hdmi->clk); | ||
| 1668 | clk_put(hdmi->disp1_clk); | ||
| 1669 | clk_put(hdmi->disp2_clk); | ||
| 1670 | tegra_edid_destroy(hdmi->edid); | ||
| 1671 | tegra_nvhdcp_destroy(hdmi->nvhdcp); | ||
| 1672 | |||
| 1673 | kfree(hdmi); | ||
| 1674 | |||
| 1675 | } | ||
| 1676 | |||
| 1677 | static void tegra_dc_hdmi_setup_audio_fs_tables(struct tegra_dc *dc) | ||
| 1678 | { | ||
| 1679 | struct tegra_dc_hdmi_data *hdmi = tegra_dc_get_outdata(dc); | ||
| 1680 | int i; | ||
| 1681 | unsigned freqs[] = { | ||
| 1682 | 32000, | ||
| 1683 | 44100, | ||
| 1684 | 48000, | ||
| 1685 | 88200, | ||
| 1686 | 96000, | ||
| 1687 | 176400, | ||
| 1688 | 192000, | ||
| 1689 | }; | ||
| 1690 | |||
| 1691 | for (i = 0; i < ARRAY_SIZE(freqs); i++) { | ||
| 1692 | unsigned f = freqs[i]; | ||
| 1693 | unsigned eight_half; | ||
| 1694 | unsigned delta;; | ||
| 1695 | |||
| 1696 | if (f > 96000) | ||
| 1697 | delta = 2; | ||
| 1698 | else if (f > 48000) | ||
| 1699 | delta = 6; | ||
| 1700 | else | ||
| 1701 | delta = 9; | ||
| 1702 | |||
| 1703 | eight_half = (8 * HDMI_AUDIOCLK_FREQ) / (f * 128); | ||
| 1704 | tegra_hdmi_writel(hdmi, AUDIO_FS_LOW(eight_half - delta) | | ||
| 1705 | AUDIO_FS_HIGH(eight_half + delta), | ||
| 1706 | HDMI_NV_PDISP_AUDIO_FS(i)); | ||
| 1707 | } | ||
| 1708 | } | ||
| 1709 | |||
| 1710 | #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) | ||
| 1711 | static void tegra_dc_hdmi_setup_eld_buff(struct tegra_dc *dc) | ||
| 1712 | { | ||
| 1713 | int i; | ||
| 1714 | int j; | ||
| 1715 | u8 tmp; | ||
| 1716 | |||
| 1717 | struct tegra_dc_hdmi_data *hdmi = tegra_dc_get_outdata(dc); | ||
| 1718 | |||
| 1719 | /* program ELD stuff */ | ||
| 1720 | for (i = 0; i < HDMI_ELD_MONITOR_NAME_INDEX; i++) { | ||
| 1721 | switch (i) { | ||
| 1722 | case HDMI_ELD_VER_INDEX: | ||
| 1723 | tmp = (hdmi->eld.eld_ver << 3); | ||
| 1724 | tegra_hdmi_writel(hdmi, (i << 8) | tmp, | ||
| 1725 | HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0); | ||
| 1726 | break; | ||
| 1727 | case HDMI_ELD_BASELINE_LEN_INDEX: | ||
| 1728 | break; | ||
| 1729 | case HDMI_ELD_CEA_VER_MNL_INDEX: | ||
| 1730 | tmp = (hdmi->eld.cea_edid_ver << 5); | ||
| 1731 | tmp |= (hdmi->eld.mnl & 0x1f); | ||
| 1732 | tegra_hdmi_writel(hdmi, (i << 8) | tmp, | ||
| 1733 | HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0); | ||
| 1734 | break; | ||
| 1735 | case HDMI_ELD_SAD_CNT_CON_TYP_SAI_HDCP_INDEX: | ||
| 1736 | tmp = (hdmi->eld.sad_count << 4); | ||
| 1737 | tmp |= (hdmi->eld.conn_type & 0xC); | ||
| 1738 | tmp |= (hdmi->eld.support_ai & 0x2); | ||
| 1739 | tmp |= (hdmi->eld.support_hdcp & 0x1); | ||
| 1740 | tegra_hdmi_writel(hdmi, (i << 8) | tmp, | ||
| 1741 | HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0); | ||
| 1742 | break; | ||
| 1743 | case HDMI_ELD_AUD_SYNC_DELAY_INDEX: | ||
| 1744 | tegra_hdmi_writel(hdmi, (i << 8) | (hdmi->eld.aud_synch_delay), | ||
| 1745 | HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0); | ||
| 1746 | break; | ||
| 1747 | case HDMI_ELD_SPK_ALLOC_INDEX: | ||
| 1748 | tegra_hdmi_writel(hdmi, (i << 8) | (hdmi->eld.spk_alloc), | ||
| 1749 | HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0); | ||
| 1750 | break; | ||
| 1751 | case HDMI_ELD_PORT_ID_INDEX: | ||
| 1752 | for (j = 0; j < 8;j++) { | ||
| 1753 | tegra_hdmi_writel(hdmi, ((i +j) << 8) | (hdmi->eld.port_id[j]), | ||
| 1754 | HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0); | ||
| 1755 | } | ||
| 1756 | break; | ||
| 1757 | case HDMI_ELD_MANF_NAME_INDEX: | ||
| 1758 | for (j = 0; j < 2;j++) { | ||
| 1759 | tegra_hdmi_writel(hdmi, ((i +j) << 8) | (hdmi->eld.manufacture_id[j]), | ||
| 1760 | HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0); | ||
| 1761 | } | ||
| 1762 | break; | ||
| 1763 | case HDMI_ELD_PRODUCT_CODE_INDEX: | ||
| 1764 | for (j = 0; j < 2;j++) { | ||
| 1765 | tegra_hdmi_writel(hdmi, ((i +j) << 8) | (hdmi->eld.product_id[j]), | ||
| 1766 | HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0); | ||
| 1767 | } | ||
| 1768 | break; | ||
| 1769 | } | ||
| 1770 | } | ||
| 1771 | for (j = 0; j < hdmi->eld.mnl;j++) { | ||
| 1772 | tegra_hdmi_writel(hdmi, ((j + HDMI_ELD_MONITOR_NAME_INDEX) << 8) | | ||
| 1773 | (hdmi->eld.monitor_name[j]), | ||
| 1774 | HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0); | ||
| 1775 | } | ||
| 1776 | for (j = 0; j < hdmi->eld.sad_count;j++) { | ||
| 1777 | tegra_hdmi_writel(hdmi, ((j + HDMI_ELD_MONITOR_NAME_INDEX + hdmi->eld.mnl) << 8) | | ||
| 1778 | (hdmi->eld.sad[j]), | ||
| 1779 | HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0); | ||
| 1780 | } | ||
| 1781 | /* set presence andvalid bit */ | ||
| 1782 | tegra_hdmi_writel(hdmi, 3, HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE_0); | ||
| 1783 | } | ||
| 1784 | #endif | ||
| 1785 | |||
| 1786 | static int tegra_dc_hdmi_setup_audio(struct tegra_dc *dc, unsigned audio_freq, | ||
| 1787 | unsigned audio_source) | ||
| 1788 | { | ||
| 1789 | struct tegra_dc_hdmi_data *hdmi = tegra_dc_get_outdata(dc); | ||
| 1790 | const struct tegra_hdmi_audio_config *config; | ||
| 1791 | unsigned long audio_n; | ||
| 1792 | #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) | ||
| 1793 | unsigned long reg_addr = 0; | ||
| 1794 | #endif | ||
| 1795 | unsigned a_source = AUDIO_CNTRL0_SOURCE_SELECT_AUTO; | ||
| 1796 | |||
| 1797 | if (HDA == audio_source) | ||
| 1798 | a_source = AUDIO_CNTRL0_SOURCE_SELECT_HDAL; | ||
| 1799 | else if (SPDIF == audio_source) | ||
| 1800 | a_source = AUDIO_CNTRL0_SOURCE_SELECT_SPDIF; | ||
| 1801 | |||
| 1802 | #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) | ||
| 1803 | tegra_hdmi_writel(hdmi,a_source | AUDIO_CNTRL0_INJECT_NULLSMPL, | ||
| 1804 | HDMI_NV_PDISP_SOR_AUDIO_CNTRL0_0); | ||
| 1805 | tegra_hdmi_writel(hdmi, | ||
| 1806 | AUDIO_CNTRL0_ERROR_TOLERANCE(6) | | ||
| 1807 | AUDIO_CNTRL0_FRAMES_PER_BLOCK(0xc0), | ||
| 1808 | HDMI_NV_PDISP_AUDIO_CNTRL0); | ||
| 1809 | #else | ||
| 1810 | tegra_hdmi_writel(hdmi, | ||
| 1811 | AUDIO_CNTRL0_ERROR_TOLERANCE(6) | | ||
| 1812 | AUDIO_CNTRL0_FRAMES_PER_BLOCK(0xc0) | | ||
| 1813 | a_source, | ||
| 1814 | HDMI_NV_PDISP_AUDIO_CNTRL0); | ||
| 1815 | #endif | ||
| 1816 | config = tegra_hdmi_get_audio_config(audio_freq, dc->mode.pclk); | ||
| 1817 | if (!config) { | ||
| 1818 | dev_err(&dc->ndev->dev, | ||
| 1819 | "hdmi: can't set audio to %d at %d pix_clock", | ||
| 1820 | audio_freq, dc->mode.pclk); | ||
| 1821 | return -EINVAL; | ||
| 1822 | } | ||
| 1823 | |||
| 1824 | tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_HDMI_ACR_CTRL); | ||
| 1825 | |||
| 1826 | audio_n = AUDIO_N_RESETF | AUDIO_N_GENERATE_ALTERNALTE | | ||
| 1827 | AUDIO_N_VALUE(config->n - 1); | ||
| 1828 | tegra_hdmi_writel(hdmi, audio_n, HDMI_NV_PDISP_AUDIO_N); | ||
| 1829 | |||
| 1830 | tegra_hdmi_writel(hdmi, ACR_SUBPACK_N(config->n) | ACR_ENABLE, | ||
| 1831 | HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH); | ||
| 1832 | |||
| 1833 | tegra_hdmi_writel(hdmi, ACR_SUBPACK_CTS(config->cts), | ||
| 1834 | HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW); | ||
| 1835 | |||
| 1836 | tegra_hdmi_writel(hdmi, SPARE_HW_CTS | SPARE_FORCE_SW_CTS | | ||
| 1837 | SPARE_CTS_RESET_VAL(1), | ||
| 1838 | HDMI_NV_PDISP_HDMI_SPARE); | ||
| 1839 | |||
| 1840 | audio_n &= ~AUDIO_N_RESETF; | ||
| 1841 | tegra_hdmi_writel(hdmi, audio_n, HDMI_NV_PDISP_AUDIO_N); | ||
| 1842 | |||
| 1843 | #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) | ||
| 1844 | switch (audio_freq) { | ||
| 1845 | case AUDIO_FREQ_32K: | ||
| 1846 | reg_addr = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0320_0; | ||
| 1847 | break; | ||
| 1848 | case AUDIO_FREQ_44_1K: | ||
| 1849 | reg_addr = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0441_0; | ||
| 1850 | break; | ||
| 1851 | case AUDIO_FREQ_48K: | ||
| 1852 | reg_addr = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0480_0; | ||
| 1853 | break; | ||
| 1854 | case AUDIO_FREQ_88_2K: | ||
| 1855 | reg_addr = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0882_0; | ||
| 1856 | break; | ||
| 1857 | case AUDIO_FREQ_96K: | ||
| 1858 | reg_addr = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0960_0; | ||
| 1859 | break; | ||
| 1860 | case AUDIO_FREQ_176_4K: | ||
| 1861 | reg_addr = HDMI_NV_PDISP_SOR_AUDIO_AVAL_1764_0; | ||
| 1862 | break; | ||
| 1863 | case AUDIO_FREQ_192K: | ||
| 1864 | reg_addr = HDMI_NV_PDISP_SOR_AUDIO_AVAL_1920_0; | ||
| 1865 | break; | ||
| 1866 | } | ||
| 1867 | |||
| 1868 | tegra_hdmi_writel(hdmi, config->aval, reg_addr); | ||
| 1869 | #endif | ||
| 1870 | tegra_dc_hdmi_setup_audio_fs_tables(dc); | ||
| 1871 | |||
| 1872 | return 0; | ||
| 1873 | } | ||
| 1874 | |||
| 1875 | int tegra_hdmi_setup_audio_freq_source(unsigned audio_freq, unsigned audio_source) | ||
| 1876 | { | ||
| 1877 | struct tegra_dc_hdmi_data *hdmi = dc_hdmi; | ||
| 1878 | |||
| 1879 | if (!hdmi) | ||
| 1880 | return -EAGAIN; | ||
| 1881 | |||
| 1882 | /* check for know freq */ | ||
| 1883 | if (AUDIO_FREQ_32K == audio_freq || | ||
| 1884 | AUDIO_FREQ_44_1K== audio_freq || | ||
| 1885 | AUDIO_FREQ_48K== audio_freq || | ||
| 1886 | AUDIO_FREQ_88_2K== audio_freq || | ||
| 1887 | AUDIO_FREQ_96K== audio_freq || | ||
| 1888 | AUDIO_FREQ_176_4K== audio_freq || | ||
| 1889 | AUDIO_FREQ_192K== audio_freq) { | ||
| 1890 | /* If we can program HDMI, then proceed */ | ||
| 1891 | if (hdmi->clk_enabled) | ||
| 1892 | tegra_dc_hdmi_setup_audio(hdmi->dc, audio_freq,audio_source); | ||
| 1893 | |||
| 1894 | /* Store it for using it in enable */ | ||
| 1895 | hdmi->audio_freq = audio_freq; | ||
| 1896 | hdmi->audio_source = audio_source; | ||
| 1897 | } | ||
| 1898 | else | ||
| 1899 | return -EINVAL; | ||
| 1900 | |||
| 1901 | return 0; | ||
| 1902 | } | ||
| 1903 | EXPORT_SYMBOL(tegra_hdmi_setup_audio_freq_source); | ||
| 1904 | |||
| 1905 | #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) | ||
| 1906 | int tegra_hdmi_setup_hda_presence() | ||
| 1907 | { | ||
| 1908 | struct tegra_dc_hdmi_data *hdmi = dc_hdmi; | ||
| 1909 | |||
| 1910 | if (!hdmi) | ||
| 1911 | return -EAGAIN; | ||
| 1912 | |||
| 1913 | if (hdmi->clk_enabled && hdmi->eld_retrieved) { | ||
| 1914 | /* If HDA_PRESENCE is already set reset it */ | ||
| 1915 | if (tegra_hdmi_readl(hdmi, | ||
| 1916 | HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE_0)) | ||
| 1917 | tegra_hdmi_writel(hdmi, 0, | ||
| 1918 | HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE_0); | ||
| 1919 | |||
| 1920 | tegra_dc_hdmi_setup_eld_buff(hdmi->dc); | ||
| 1921 | } | ||
| 1922 | else | ||
| 1923 | return -ENODEV; | ||
| 1924 | |||
| 1925 | return 0; | ||
| 1926 | } | ||
| 1927 | EXPORT_SYMBOL(tegra_hdmi_setup_hda_presence); | ||
| 1928 | #endif | ||
| 1929 | |||
| 1930 | static void tegra_dc_hdmi_write_infopack(struct tegra_dc *dc, int header_reg, | ||
| 1931 | u8 type, u8 version, void *data, int len) | ||
| 1932 | { | ||
| 1933 | struct tegra_dc_hdmi_data *hdmi = tegra_dc_get_outdata(dc); | ||
| 1934 | u32 subpack[2]; /* extra byte for zero padding of subpack */ | ||
| 1935 | int i; | ||
| 1936 | u8 csum; | ||
| 1937 | |||
| 1938 | /* first byte of data is the checksum */ | ||
| 1939 | csum = type + version + len - 1; | ||
| 1940 | for (i = 1; i < len; i++) | ||
| 1941 | csum +=((u8 *)data)[i]; | ||
| 1942 | ((u8 *)data)[0] = 0x100 - csum; | ||
| 1943 | |||
| 1944 | tegra_hdmi_writel(hdmi, INFOFRAME_HEADER_TYPE(type) | | ||
| 1945 | INFOFRAME_HEADER_VERSION(version) | | ||
| 1946 | INFOFRAME_HEADER_LEN(len - 1), | ||
| 1947 | header_reg); | ||
| 1948 | |||
| 1949 | /* The audio inforame only has one set of subpack registers. The hdmi | ||
| 1950 | * block pads the rest of the data as per the spec so we have to fixup | ||
| 1951 | * the length before filling in the subpacks. | ||
| 1952 | */ | ||
| 1953 | if (header_reg == HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER) | ||
| 1954 | len = 6; | ||
| 1955 | |||
| 1956 | /* each subpack 7 bytes devided into: | ||
| 1957 | * subpack_low - bytes 0 - 3 | ||
| 1958 | * subpack_high - bytes 4 - 6 (with byte 7 padded to 0x00) | ||
| 1959 | */ | ||
| 1960 | for (i = 0; i < len; i++) { | ||
| 1961 | int subpack_idx = i % 7; | ||
| 1962 | |||
| 1963 | if (subpack_idx == 0) | ||
| 1964 | memset(subpack, 0x0, sizeof(subpack)); | ||
| 1965 | |||
| 1966 | ((u8 *)subpack)[subpack_idx] = ((u8 *)data)[i]; | ||
| 1967 | |||
| 1968 | if (subpack_idx == 6 || (i + 1 == len)) { | ||
| 1969 | int reg = header_reg + 1 + (i / 7) * 2; | ||
| 1970 | |||
| 1971 | tegra_hdmi_writel(hdmi, subpack[0], reg); | ||
| 1972 | tegra_hdmi_writel(hdmi, subpack[1], reg + 1); | ||
| 1973 | } | ||
| 1974 | } | ||
| 1975 | } | ||
| 1976 | |||
| 1977 | static void tegra_dc_hdmi_setup_avi_infoframe(struct tegra_dc *dc, bool dvi) | ||
| 1978 | { | ||
| 1979 | struct tegra_dc_hdmi_data *hdmi = tegra_dc_get_outdata(dc); | ||
| 1980 | struct hdmi_avi_infoframe avi; | ||
| 1981 | |||
| 1982 | if (dvi) { | ||
| 1983 | tegra_hdmi_writel(hdmi, 0x0, | ||
| 1984 | HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL); | ||
| 1985 | return; | ||
| 1986 | } | ||
| 1987 | |||
| 1988 | memset(&avi, 0x0, sizeof(avi)); | ||
| 1989 | |||
| 1990 | avi.r = HDMI_AVI_R_SAME; | ||
| 1991 | |||
| 1992 | if (dc->mode.v_active == 480) { | ||
| 1993 | if (dc->mode.h_active == 640) { | ||
| 1994 | avi.m = HDMI_AVI_M_4_3; | ||
| 1995 | avi.vic = 1; | ||
| 1996 | } else { | ||
| 1997 | avi.m = HDMI_AVI_M_16_9; | ||
| 1998 | avi.vic = 3; | ||
| 1999 | } | ||
| 2000 | } else if (dc->mode.v_active == 576) { | ||
| 2001 | /* CEC modes 17 and 18 differ only by the pysical size of the | ||
| 2002 | * screen so we have to calculation the physical aspect | ||
| 2003 | * ratio. 4 * 10 / 3 is 13 | ||
| 2004 | */ | ||
| 2005 | if ((dc->out->h_size * 10) / dc->out->v_size > 14) { | ||
| 2006 | avi.m = HDMI_AVI_M_16_9; | ||
| 2007 | avi.vic = 18; | ||
| 2008 | } else { | ||
| 2009 | avi.m = HDMI_AVI_M_4_3; | ||
| 2010 | avi.vic = 17; | ||
| 2011 | } | ||
| 2012 | } else if (dc->mode.v_active == 720 || | ||
| 2013 | (dc->mode.v_active == 1470 && dc->mode.stereo_mode)) { | ||
| 2014 | /* VIC for both 720p and 720p 3D mode */ | ||
| 2015 | avi.m = HDMI_AVI_M_16_9; | ||
| 2016 | if (dc->mode.h_front_porch == 110) | ||
| 2017 | avi.vic = 4; /* 60 Hz */ | ||
| 2018 | else | ||
| 2019 | avi.vic = 19; /* 50 Hz */ | ||
| 2020 | } else if (dc->mode.v_active == 1080 || | ||
| 2021 | (dc->mode.v_active == 2205 && dc->mode.stereo_mode)) { | ||
| 2022 | /* VIC for both 1080p and 1080p 3D mode */ | ||
| 2023 | avi.m = HDMI_AVI_M_16_9; | ||
| 2024 | if (dc->mode.h_front_porch == 88) | ||
| 2025 | avi.vic = 16; /* 60 Hz */ | ||
| 2026 | else if (dc->mode.h_front_porch == 528) | ||
| 2027 | avi.vic = 31; /* 50 Hz */ | ||
| 2028 | else | ||
| 2029 | avi.vic = 32; /* 24 Hz */ | ||
| 2030 | } else { | ||
| 2031 | avi.m = HDMI_AVI_M_16_9; | ||
| 2032 | avi.vic = 0; | ||
| 2033 | } | ||
| 2034 | |||
| 2035 | |||
| 2036 | tegra_dc_hdmi_write_infopack(dc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER, | ||
| 2037 | HDMI_INFOFRAME_TYPE_AVI, | ||
| 2038 | HDMI_AVI_VERSION, | ||
| 2039 | &avi, sizeof(avi)); | ||
| 2040 | |||
| 2041 | tegra_hdmi_writel(hdmi, INFOFRAME_CTRL_ENABLE, | ||
| 2042 | HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL); | ||
| 2043 | } | ||
| 2044 | |||
| 2045 | static void tegra_dc_hdmi_setup_stereo_infoframe(struct tegra_dc *dc) | ||
| 2046 | { | ||
| 2047 | struct tegra_dc_hdmi_data *hdmi = tegra_dc_get_outdata(dc); | ||
| 2048 | struct hdmi_stereo_infoframe stereo; | ||
| 2049 | u32 val; | ||
| 2050 | |||
| 2051 | if (!dc->mode.stereo_mode) { | ||
| 2052 | val = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); | ||
| 2053 | val &= ~GENERIC_CTRL_ENABLE; | ||
| 2054 | tegra_hdmi_writel(hdmi, val, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); | ||
| 2055 | return; | ||
| 2056 | } | ||
| 2057 | |||
| 2058 | memset(&stereo, 0x0, sizeof(stereo)); | ||
| 2059 | |||
| 2060 | stereo.regid0 = 0x03; | ||
| 2061 | stereo.regid1 = 0x0c; | ||
| 2062 | stereo.regid2 = 0x00; | ||
| 2063 | stereo.hdmi_video_format = 2; /* 3D_Structure present */ | ||
| 2064 | #ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT | ||
| 2065 | stereo._3d_structure = 0; /* frame packing */ | ||
| 2066 | #else | ||
| 2067 | stereo._3d_structure = 8; /* side-by-side (half) */ | ||
| 2068 | stereo._3d_ext_data = 0; /* something which fits into 00XX bit req */ | ||
| 2069 | #endif | ||
| 2070 | |||
| 2071 | tegra_dc_hdmi_write_infopack(dc, HDMI_NV_PDISP_HDMI_GENERIC_HEADER, | ||
| 2072 | HDMI_INFOFRAME_TYPE_VENDOR, | ||
| 2073 | HDMI_VENDOR_VERSION, | ||
| 2074 | &stereo, 6); | ||
| 2075 | |||
| 2076 | val = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); | ||
| 2077 | val |= GENERIC_CTRL_ENABLE; | ||
| 2078 | |||
| 2079 | tegra_hdmi_writel(hdmi, val, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); | ||
| 2080 | } | ||
| 2081 | |||
| 2082 | static void tegra_dc_hdmi_setup_audio_infoframe(struct tegra_dc *dc, bool dvi) | ||
| 2083 | { | ||
| 2084 | struct tegra_dc_hdmi_data *hdmi = tegra_dc_get_outdata(dc); | ||
| 2085 | struct hdmi_audio_infoframe audio; | ||
| 2086 | |||
| 2087 | if (dvi) { | ||
| 2088 | tegra_hdmi_writel(hdmi, 0x0, | ||
| 2089 | HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); | ||
| 2090 | return; | ||
| 2091 | } | ||
| 2092 | |||
| 2093 | memset(&audio, 0x0, sizeof(audio)); | ||
| 2094 | |||
| 2095 | audio.cc = HDMI_AUDIO_CC_2; | ||
| 2096 | tegra_dc_hdmi_write_infopack(dc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER, | ||
| 2097 | HDMI_INFOFRAME_TYPE_AUDIO, | ||
| 2098 | HDMI_AUDIO_VERSION, | ||
| 2099 | &audio, sizeof(audio)); | ||
| 2100 | |||
| 2101 | tegra_hdmi_writel(hdmi, INFOFRAME_CTRL_ENABLE, | ||
| 2102 | HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); | ||
| 2103 | } | ||
| 2104 | |||
| 2105 | static void tegra_dc_hdmi_setup_tdms(struct tegra_dc_hdmi_data *hdmi, | ||
| 2106 | const struct tdms_config *tc) | ||
| 2107 | { | ||
| 2108 | tegra_hdmi_writel(hdmi, tc->pll0, HDMI_NV_PDISP_SOR_PLL0); | ||
| 2109 | tegra_hdmi_writel(hdmi, tc->pll1, HDMI_NV_PDISP_SOR_PLL1); | ||
| 2110 | |||
| 2111 | tegra_hdmi_writel(hdmi, tc->pe_current, HDMI_NV_PDISP_PE_CURRENT); | ||
| 2112 | |||
| 2113 | tegra_hdmi_writel(hdmi, | ||
| 2114 | tc->drive_current | DRIVE_CURRENT_FUSE_OVERRIDE, | ||
| 2115 | HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT); | ||
| 2116 | } | ||
| 2117 | |||
| 2118 | static void tegra_dc_hdmi_enable(struct tegra_dc *dc) | ||
| 2119 | { | ||
| 2120 | struct tegra_dc_hdmi_data *hdmi = tegra_dc_get_outdata(dc); | ||
| 2121 | int pulse_start; | ||
| 2122 | int dispclk_div_8_2; | ||
| 2123 | int retries; | ||
| 2124 | int rekey; | ||
| 2125 | int err; | ||
| 2126 | unsigned long val; | ||
| 2127 | unsigned i; | ||
| 2128 | unsigned long oldrate; | ||
| 2129 | |||
| 2130 | /* enbale power, clocks, resets, etc. */ | ||
| 2131 | |||
| 2132 | /* The upstream DC needs to be clocked for accesses to HDMI to not | ||
| 2133 | * hard lock the system. Because we don't know if HDMI is conencted | ||
| 2134 | * to disp1 or disp2 we need to enable both until we set the DC mux. | ||
| 2135 | */ | ||
| 2136 | clk_enable(hdmi->disp1_clk); | ||
| 2137 | clk_enable(hdmi->disp2_clk); | ||
| 2138 | |||
| 2139 | #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) | ||
| 2140 | /* Enabling HDA clocks before asserting HDA PD and ELDV bits */ | ||
| 2141 | clk_enable(hdmi->hda_clk); | ||
| 2142 | clk_enable(hdmi->hda2codec_clk); | ||
| 2143 | clk_enable(hdmi->hda2hdmi_clk); | ||
| 2144 | #endif | ||
| 2145 | |||
| 2146 | /* back off multiplier before attaching to parent at new rate. */ | ||
| 2147 | oldrate = clk_get_rate(hdmi->clk); | ||
| 2148 | clk_set_rate(hdmi->clk, oldrate / 2); | ||
| 2149 | |||
| 2150 | tegra_dc_setup_clk(dc, hdmi->clk); | ||
| 2151 | clk_set_rate(hdmi->clk, dc->mode.pclk); | ||
| 2152 | |||
| 2153 | clk_enable(hdmi->clk); | ||
| 2154 | tegra_periph_reset_assert(hdmi->clk); | ||
| 2155 | mdelay(1); | ||
| 2156 | tegra_periph_reset_deassert(hdmi->clk); | ||
| 2157 | |||
| 2158 | /* TODO: copy HDCP keys from KFUSE to HDMI */ | ||
| 2159 | |||
| 2160 | /* Program display timing registers: handled by dc */ | ||
| 2161 | |||
| 2162 | /* program HDMI registers and SOR sequencer */ | ||
| 2163 | |||
| 2164 | tegra_dc_writel(dc, VSYNC_H_POSITION(1), DC_DISP_DISP_TIMING_OPTIONS); | ||
| 2165 | tegra_dc_writel(dc, DITHER_CONTROL_DISABLE | BASE_COLOR_SIZE888, | ||
| 2166 | DC_DISP_DISP_COLOR_CONTROL); | ||
| 2167 | |||
| 2168 | /* video_preamble uses h_pulse2 */ | ||
| 2169 | pulse_start = dc->mode.h_ref_to_sync + dc->mode.h_sync_width + | ||
| 2170 | dc->mode.h_back_porch - 10; | ||
| 2171 | tegra_dc_writel(dc, H_PULSE_2_ENABLE, DC_DISP_DISP_SIGNAL_OPTIONS0); | ||
| 2172 | tegra_dc_writel(dc, | ||
| 2173 | PULSE_MODE_NORMAL | | ||
| 2174 | PULSE_POLARITY_HIGH | | ||
| 2175 | PULSE_QUAL_VACTIVE | | ||
| 2176 | PULSE_LAST_END_A, | ||
| 2177 | DC_DISP_H_PULSE2_CONTROL); | ||
| 2178 | tegra_dc_writel(dc, PULSE_START(pulse_start) | PULSE_END(pulse_start + 8), | ||
| 2179 | DC_DISP_H_PULSE2_POSITION_A); | ||
| 2180 | |||
| 2181 | tegra_hdmi_writel(hdmi, | ||
| 2182 | VSYNC_WINDOW_END(0x210) | | ||
| 2183 | VSYNC_WINDOW_START(0x200) | | ||
| 2184 | VSYNC_WINDOW_ENABLE, | ||
| 2185 | HDMI_NV_PDISP_HDMI_VSYNC_WINDOW); | ||
| 2186 | |||
| 2187 | tegra_hdmi_writel(hdmi, | ||
| 2188 | (dc->ndev->id ? HDMI_SRC_DISPLAYB : HDMI_SRC_DISPLAYA) | | ||
| 2189 | ARM_VIDEO_RANGE_LIMITED, | ||
| 2190 | HDMI_NV_PDISP_INPUT_CONTROL); | ||
| 2191 | |||
| 2192 | clk_disable(hdmi->disp1_clk); | ||
| 2193 | clk_disable(hdmi->disp2_clk); | ||
| 2194 | |||
| 2195 | dispclk_div_8_2 = clk_get_rate(hdmi->clk) / 1000000 * 4; | ||
| 2196 | tegra_hdmi_writel(hdmi, | ||
| 2197 | SOR_REFCLK_DIV_INT(dispclk_div_8_2 >> 2) | | ||
| 2198 | SOR_REFCLK_DIV_FRAC(dispclk_div_8_2), | ||
| 2199 | HDMI_NV_PDISP_SOR_REFCLK); | ||
| 2200 | |||
| 2201 | hdmi->clk_enabled = true; | ||
| 2202 | |||
| 2203 | if (!hdmi->dvi) { | ||
| 2204 | err = tegra_dc_hdmi_setup_audio(dc, hdmi->audio_freq, | ||
| 2205 | hdmi->audio_source); | ||
| 2206 | |||
| 2207 | if (err < 0) | ||
| 2208 | hdmi->dvi = true; | ||
| 2209 | } | ||
| 2210 | |||
| 2211 | #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) | ||
| 2212 | if (hdmi->eld_retrieved) | ||
| 2213 | tegra_dc_hdmi_setup_eld_buff(dc); | ||
| 2214 | #endif | ||
| 2215 | |||
| 2216 | rekey = HDMI_REKEY_DEFAULT; | ||
| 2217 | val = HDMI_CTRL_REKEY(rekey); | ||
| 2218 | val |= HDMI_CTRL_MAX_AC_PACKET((dc->mode.h_sync_width + | ||
| 2219 | dc->mode.h_back_porch + | ||
| 2220 | dc->mode.h_front_porch - | ||
| 2221 | rekey - 18) / 32); | ||
| 2222 | if (!hdmi->dvi) | ||
| 2223 | val |= HDMI_CTRL_ENABLE; | ||
| 2224 | tegra_hdmi_writel(hdmi, val, HDMI_NV_PDISP_HDMI_CTRL); | ||
| 2225 | |||
| 2226 | if (hdmi->dvi) | ||
| 2227 | tegra_hdmi_writel(hdmi, 0x0, | ||
| 2228 | HDMI_NV_PDISP_HDMI_GENERIC_CTRL); | ||
| 2229 | else | ||
| 2230 | tegra_hdmi_writel(hdmi, GENERIC_CTRL_AUDIO, | ||
| 2231 | HDMI_NV_PDISP_HDMI_GENERIC_CTRL); | ||
| 2232 | |||
| 2233 | tegra_dc_hdmi_setup_avi_infoframe(dc, hdmi->dvi); | ||
| 2234 | tegra_dc_hdmi_setup_audio_infoframe(dc, hdmi->dvi); | ||
| 2235 | tegra_dc_hdmi_setup_stereo_infoframe(dc); | ||
| 2236 | |||
| 2237 | /* TMDS CONFIG */ | ||
| 2238 | for (i = 0; i < ARRAY_SIZE(tdms_config); i++) { | ||
| 2239 | if (dc->mode.pclk <= tdms_config[i].pclk) { | ||
| 2240 | tegra_dc_hdmi_setup_tdms(hdmi, &tdms_config[i]); | ||
| 2241 | break; | ||
| 2242 | } | ||
| 2243 | } | ||
| 2244 | |||
| 2245 | tegra_hdmi_writel(hdmi, | ||
| 2246 | SOR_SEQ_CTL_PU_PC(0) | | ||
| 2247 | SOR_SEQ_PU_PC_ALT(0) | | ||
| 2248 | SOR_SEQ_PD_PC(8) | | ||
| 2249 | SOR_SEQ_PD_PC_ALT(8), | ||
| 2250 | HDMI_NV_PDISP_SOR_SEQ_CTL); | ||
| 2251 | |||
| 2252 | val = SOR_SEQ_INST_WAIT_TIME(1) | | ||
| 2253 | SOR_SEQ_INST_WAIT_UNITS_VSYNC | | ||
| 2254 | SOR_SEQ_INST_HALT | | ||
| 2255 | SOR_SEQ_INST_PIN_A_LOW | | ||
| 2256 | SOR_SEQ_INST_PIN_B_LOW | | ||
| 2257 | SOR_SEQ_INST_DRIVE_PWM_OUT_LO; | ||
| 2258 | |||
| 2259 | tegra_hdmi_writel(hdmi, val, HDMI_NV_PDISP_SOR_SEQ_INST0); | ||
| 2260 | tegra_hdmi_writel(hdmi, val, HDMI_NV_PDISP_SOR_SEQ_INST8); | ||
| 2261 | |||
| 2262 | val = 0x1c800; | ||
| 2263 | val &= ~SOR_CSTM_ROTCLK(~0); | ||
| 2264 | val |= SOR_CSTM_ROTCLK(2); | ||
| 2265 | tegra_hdmi_writel(hdmi, val, HDMI_NV_PDISP_SOR_CSTM); | ||
| 2266 | |||
| 2267 | |||
| 2268 | tegra_dc_writel(dc, DISP_CTRL_MODE_STOP, DC_CMD_DISPLAY_COMMAND); | ||
| 2269 | tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); | ||
| 2270 | tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); | ||
| 2271 | |||
| 2272 | |||
| 2273 | /* start SOR */ | ||
| 2274 | tegra_hdmi_writel(hdmi, | ||
| 2275 | SOR_PWR_NORMAL_STATE_PU | | ||
| 2276 | SOR_PWR_NORMAL_START_NORMAL | | ||
| 2277 | SOR_PWR_SAFE_STATE_PD | | ||
| 2278 | SOR_PWR_SETTING_NEW_TRIGGER, | ||
| 2279 | HDMI_NV_PDISP_SOR_PWR); | ||
| 2280 | tegra_hdmi_writel(hdmi, | ||
| 2281 | SOR_PWR_NORMAL_STATE_PU | | ||
| 2282 | SOR_PWR_NORMAL_START_NORMAL | | ||
| 2283 | SOR_PWR_SAFE_STATE_PD | | ||
| 2284 | SOR_PWR_SETTING_NEW_DONE, | ||
| 2285 | HDMI_NV_PDISP_SOR_PWR); | ||
| 2286 | |||
| 2287 | retries = 1000; | ||
| 2288 | do { | ||
| 2289 | BUG_ON(--retries < 0); | ||
| 2290 | val = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_PWR); | ||
| 2291 | } while (val & SOR_PWR_SETTING_NEW_PENDING); | ||
| 2292 | |||
| 2293 | val = SOR_STATE_ASY_CRCMODE_COMPLETE | | ||
| 2294 | SOR_STATE_ASY_OWNER_HEAD0 | | ||
| 2295 | SOR_STATE_ASY_SUBOWNER_BOTH | | ||
| 2296 | SOR_STATE_ASY_PROTOCOL_SINGLE_TMDS_A | | ||
| 2297 | SOR_STATE_ASY_DEPOL_POS; | ||
| 2298 | |||
| 2299 | if (dc->mode.flags & TEGRA_DC_MODE_FLAG_NEG_H_SYNC) | ||
| 2300 | val |= SOR_STATE_ASY_HSYNCPOL_NEG; | ||
| 2301 | else | ||
| 2302 | val |= SOR_STATE_ASY_HSYNCPOL_POS; | ||
| 2303 | |||
| 2304 | if (dc->mode.flags & TEGRA_DC_MODE_FLAG_NEG_V_SYNC) | ||
| 2305 | val |= SOR_STATE_ASY_VSYNCPOL_NEG; | ||
| 2306 | else | ||
| 2307 | val |= SOR_STATE_ASY_VSYNCPOL_POS; | ||
| 2308 | |||
| 2309 | tegra_hdmi_writel(hdmi, val, HDMI_NV_PDISP_SOR_STATE2); | ||
| 2310 | |||
| 2311 | val = SOR_STATE_ASY_HEAD_OPMODE_AWAKE | SOR_STATE_ASY_ORMODE_NORMAL; | ||
| 2312 | tegra_hdmi_writel(hdmi, val, HDMI_NV_PDISP_SOR_STATE1); | ||
| 2313 | |||
| 2314 | tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_SOR_STATE0); | ||
| 2315 | tegra_hdmi_writel(hdmi, SOR_STATE_UPDATE, HDMI_NV_PDISP_SOR_STATE0); | ||
| 2316 | tegra_hdmi_writel(hdmi, val | SOR_STATE_ATTACHED, | ||
| 2317 | HDMI_NV_PDISP_SOR_STATE1); | ||
| 2318 | tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_SOR_STATE0); | ||
| 2319 | |||
| 2320 | tegra_dc_writel(dc, HDMI_ENABLE, DC_DISP_DISP_WIN_OPTIONS); | ||
| 2321 | |||
| 2322 | tegra_dc_writel(dc, PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | | ||
| 2323 | PW4_ENABLE | PM0_ENABLE | PM1_ENABLE, | ||
| 2324 | DC_CMD_DISPLAY_POWER_CONTROL); | ||
| 2325 | |||
| 2326 | tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, DC_CMD_DISPLAY_COMMAND); | ||
| 2327 | tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); | ||
| 2328 | tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); | ||
| 2329 | |||
| 2330 | tegra_nvhdcp_set_plug(hdmi->nvhdcp, 1); | ||
| 2331 | } | ||
| 2332 | |||
| 2333 | static void tegra_dc_hdmi_disable(struct tegra_dc *dc) | ||
| 2334 | { | ||
| 2335 | struct tegra_dc_hdmi_data *hdmi = tegra_dc_get_outdata(dc); | ||
| 2336 | |||
| 2337 | tegra_nvhdcp_set_plug(hdmi->nvhdcp, 0); | ||
| 2338 | |||
| 2339 | #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) | ||
| 2340 | tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE_0); | ||
| 2341 | /* sleep 1ms before disabling clocks to ensure HDA gets the interrupt */ | ||
| 2342 | msleep(1); | ||
| 2343 | clk_disable(hdmi->hda2hdmi_clk); | ||
| 2344 | clk_disable(hdmi->hda2codec_clk); | ||
| 2345 | clk_disable(hdmi->hda_clk); | ||
| 2346 | #endif | ||
| 2347 | tegra_periph_reset_assert(hdmi->clk); | ||
| 2348 | hdmi->clk_enabled = false; | ||
| 2349 | clk_disable(hdmi->clk); | ||
| 2350 | tegra_dvfs_set_rate(hdmi->clk, 0); | ||
| 2351 | } | ||
| 2352 | |||
| 2353 | struct tegra_dc_out_ops tegra_dc_hdmi_ops = { | ||
| 2354 | .init = tegra_dc_hdmi_init, | ||
| 2355 | .destroy = tegra_dc_hdmi_destroy, | ||
| 2356 | .enable = tegra_dc_hdmi_enable, | ||
| 2357 | .disable = tegra_dc_hdmi_disable, | ||
| 2358 | .detect = tegra_dc_hdmi_detect, | ||
| 2359 | .suspend = tegra_dc_hdmi_suspend, | ||
| 2360 | .resume = tegra_dc_hdmi_resume, | ||
| 2361 | }; | ||
| 2362 | |||
| 2363 | struct tegra_dc_edid *tegra_dc_get_edid(struct tegra_dc *dc) | ||
| 2364 | { | ||
| 2365 | struct tegra_dc_hdmi_data *hdmi; | ||
| 2366 | |||
| 2367 | /* TODO: Support EDID on non-HDMI devices */ | ||
| 2368 | if (dc->out->type != TEGRA_DC_OUT_HDMI) | ||
| 2369 | return ERR_PTR(-ENODEV); | ||
| 2370 | |||
| 2371 | hdmi = tegra_dc_get_outdata(dc); | ||
| 2372 | |||
| 2373 | return tegra_edid_get_data(hdmi->edid); | ||
| 2374 | } | ||
| 2375 | EXPORT_SYMBOL(tegra_dc_get_edid); | ||
| 2376 | |||
| 2377 | void tegra_dc_put_edid(struct tegra_dc_edid *edid) | ||
| 2378 | { | ||
| 2379 | tegra_edid_put_data(edid); | ||
| 2380 | } | ||
| 2381 | EXPORT_SYMBOL(tegra_dc_put_edid); | ||
diff --git a/drivers/video/tegra/dc/hdmi.h b/drivers/video/tegra/dc/hdmi.h new file mode 100644 index 00000000000..702ab16e87f --- /dev/null +++ b/drivers/video/tegra/dc/hdmi.h | |||
| @@ -0,0 +1,222 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/hdmi.h | ||
| 3 | * | ||
| 4 | * non-tegra specific HDMI declarations | ||
| 5 | * | ||
| 6 | * Copyright (C) 2010 Google, Inc. | ||
| 7 | * Author: Erik Gilling <konkers@android.com> | ||
| 8 | * | ||
| 9 | * Copyright (C) 2010-2011 NVIDIA Corporation | ||
| 10 | * | ||
| 11 | * This software is licensed under the terms of the GNU General Public | ||
| 12 | * License version 2, as published by the Free Software Foundation, and | ||
| 13 | * may be copied, distributed, and modified under those terms. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, | ||
| 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 18 | * GNU General Public License for more details. | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | |||
| 22 | #ifndef __DRIVERS_VIDEO_TEGRA_DC_HDMI_H | ||
| 23 | #define __DRIVERS_VIDEO_TEGRA_DC_HDMI_H | ||
| 24 | |||
| 25 | #define HDMI_INFOFRAME_TYPE_VENDOR 0x81 | ||
| 26 | #define HDMI_INFOFRAME_TYPE_AVI 0x82 | ||
| 27 | #define HDMI_INFOFRAME_TYPE_SPD 0x83 | ||
| 28 | #define HDMI_INFOFRAME_TYPE_AUDIO 0x84 | ||
| 29 | #define HDMI_INFOFRAME_TYPE_MPEG_SRC 0x85 | ||
| 30 | #define HDMI_INFOFRAME_TYPE_NTSC_VBI 0x86 | ||
| 31 | |||
| 32 | /* all fields little endian */ | ||
| 33 | struct hdmi_avi_infoframe { | ||
| 34 | /* PB0 */ | ||
| 35 | u8 csum; | ||
| 36 | |||
| 37 | /* PB1 */ | ||
| 38 | unsigned s:2; /* scan information */ | ||
| 39 | unsigned b:2; /* bar info data valid */ | ||
| 40 | unsigned a:1; /* active info present */ | ||
| 41 | unsigned y:2; /* RGB or YCbCr */ | ||
| 42 | unsigned res1:1; | ||
| 43 | |||
| 44 | /* PB2 */ | ||
| 45 | unsigned r:4; /* active format aspect ratio */ | ||
| 46 | unsigned m:2; /* picture aspect ratio */ | ||
| 47 | unsigned c:2; /* colorimetry */ | ||
| 48 | |||
| 49 | /* PB3 */ | ||
| 50 | unsigned sc:2; /* scan information */ | ||
| 51 | unsigned q:2; /* quantization range */ | ||
| 52 | unsigned ec:3; /* extended colorimetry */ | ||
| 53 | unsigned itc:1; /* it content */ | ||
| 54 | |||
| 55 | /* PB4 */ | ||
| 56 | unsigned vic:7; /* video format id code */ | ||
| 57 | unsigned res4:1; | ||
| 58 | |||
| 59 | /* PB5 */ | ||
| 60 | unsigned pr:4; /* pixel repetition factor */ | ||
| 61 | unsigned cn:2; /* it content type*/ | ||
| 62 | unsigned yq:2; /* ycc quantization range */ | ||
| 63 | |||
| 64 | /* PB6-7 */ | ||
| 65 | u16 top_bar_end_line; | ||
| 66 | |||
| 67 | /* PB8-9 */ | ||
| 68 | u16 bot_bar_start_line; | ||
| 69 | |||
| 70 | /* PB10-11 */ | ||
| 71 | u16 left_bar_end_pixel; | ||
| 72 | |||
| 73 | /* PB12-13 */ | ||
| 74 | u16 right_bar_start_pixel; | ||
| 75 | } __attribute__((packed)); | ||
| 76 | |||
| 77 | #define HDMI_AVI_VERSION 0x02 | ||
| 78 | |||
| 79 | #define HDMI_AVI_Y_RGB 0x0 | ||
| 80 | #define HDMI_AVI_Y_YCBCR_422 0x1 | ||
| 81 | #define HDMI_AVI_Y_YCBCR_444 0x2 | ||
| 82 | |||
| 83 | #define HDMI_AVI_B_VERT 0x1 | ||
| 84 | #define HDMI_AVI_B_HORIZ 0x2 | ||
| 85 | |||
| 86 | #define HDMI_AVI_S_NONE 0x0 | ||
| 87 | #define HDMI_AVI_S_OVERSCAN 0x1 | ||
| 88 | #define HDMI_AVI_S_UNDERSCAN 0x2 | ||
| 89 | |||
| 90 | #define HDMI_AVI_C_NONE 0x0 | ||
| 91 | #define HDMI_AVI_C_SMPTE 0x1 | ||
| 92 | #define HDMI_AVI_C_ITU_R 0x2 | ||
| 93 | #define HDMI_AVI_C_EXTENDED 0x4 | ||
| 94 | |||
| 95 | #define HDMI_AVI_M_4_3 0x1 | ||
| 96 | #define HDMI_AVI_M_16_9 0x2 | ||
| 97 | |||
| 98 | #define HDMI_AVI_R_SAME 0x8 | ||
| 99 | #define HDMI_AVI_R_4_3_CENTER 0x9 | ||
| 100 | #define HDMI_AVI_R_16_9_CENTER 0xa | ||
| 101 | #define HDMI_AVI_R_14_9_CENTER 0xb | ||
| 102 | |||
| 103 | /* all fields little endian */ | ||
| 104 | struct hdmi_audio_infoframe { | ||
| 105 | /* PB0 */ | ||
| 106 | u8 csum; | ||
| 107 | |||
| 108 | /* PB1 */ | ||
| 109 | unsigned cc:3; /* channel count */ | ||
| 110 | unsigned res1:1; | ||
| 111 | unsigned ct:4; /* coding type */ | ||
| 112 | |||
| 113 | /* PB2 */ | ||
| 114 | unsigned ss:2; /* sample size */ | ||
| 115 | unsigned sf:3; /* sample frequency */ | ||
| 116 | unsigned res2:3; | ||
| 117 | |||
| 118 | /* PB3 */ | ||
| 119 | unsigned cxt:5; /* coding extention type */ | ||
| 120 | unsigned res3:3; | ||
| 121 | |||
| 122 | /* PB4 */ | ||
| 123 | u8 ca; /* channel/speaker allocation */ | ||
| 124 | |||
| 125 | /* PB5 */ | ||
| 126 | unsigned res5:3; | ||
| 127 | unsigned lsv:4; /* level shift value */ | ||
| 128 | unsigned dm_inh:1; /* downmix inhibit */ | ||
| 129 | |||
| 130 | /* PB6-10 reserved */ | ||
| 131 | u8 res6; | ||
| 132 | u8 res7; | ||
| 133 | u8 res8; | ||
| 134 | u8 res9; | ||
| 135 | u8 res10; | ||
| 136 | } __attribute__((packed)); | ||
| 137 | |||
| 138 | #define HDMI_AUDIO_VERSION 0x01 | ||
| 139 | |||
| 140 | #define HDMI_AUDIO_CC_STREAM 0x0 /* specified by audio stream */ | ||
| 141 | #define HDMI_AUDIO_CC_2 0x1 | ||
| 142 | #define HDMI_AUDIO_CC_3 0x2 | ||
| 143 | #define HDMI_AUDIO_CC_4 0x3 | ||
| 144 | #define HDMI_AUDIO_CC_5 0x4 | ||
| 145 | #define HDMI_AUDIO_CC_6 0x5 | ||
| 146 | #define HDMI_AUDIO_CC_7 0x6 | ||
| 147 | #define HDMI_AUDIO_CC_8 0x7 | ||
| 148 | |||
| 149 | #define HDMI_AUDIO_CT_STREAM 0x0 /* specified by audio stream */ | ||
| 150 | #define HDMI_AUDIO_CT_PCM 0x1 | ||
| 151 | #define HDMI_AUDIO_CT_AC3 0x2 | ||
| 152 | #define HDMI_AUDIO_CT_MPEG1 0x3 | ||
| 153 | #define HDMI_AUDIO_CT_MP3 0x4 | ||
| 154 | #define HDMI_AUDIO_CT_MPEG2 0x5 | ||
| 155 | #define HDMI_AUDIO_CT_AAC_LC 0x6 | ||
| 156 | #define HDMI_AUDIO_CT_DTS 0x7 | ||
| 157 | #define HDMI_AUDIO_CT_ATRAC 0x8 | ||
| 158 | #define HDMI_AUDIO_CT_DSD 0x9 | ||
| 159 | #define HDMI_AUDIO_CT_E_AC3 0xa | ||
| 160 | #define HDMI_AUDIO_CT_DTS_HD 0xb | ||
| 161 | #define HDMI_AUDIO_CT_MLP 0xc | ||
| 162 | #define HDMI_AUDIO_CT_DST 0xd | ||
| 163 | #define HDMI_AUDIO_CT_WMA_PRO 0xe | ||
| 164 | #define HDMI_AUDIO_CT_CXT 0xf | ||
| 165 | |||
| 166 | #define HDMI_AUDIO_SF_STREAM 0x0 /* specified by audio stream */ | ||
| 167 | #define HDMI_AUIDO_SF_32K 0x1 | ||
| 168 | #define HDMI_AUDIO_SF_44_1K 0x2 | ||
| 169 | #define HDMI_AUDIO_SF_48K 0x3 | ||
| 170 | #define HDMI_AUDIO_SF_88_2K 0x4 | ||
| 171 | #define HDMI_AUDIO_SF_96K 0x5 | ||
| 172 | #define HDMI_AUDIO_SF_176_4K 0x6 | ||
| 173 | #define HDMI_AUDIO_SF_192K 0x7 | ||
| 174 | |||
| 175 | #define HDMI_AUDIO_SS_STREAM 0x0 /* specified by audio stream */ | ||
| 176 | #define HDMI_AUDIO_SS_16BIT 0x1 | ||
| 177 | #define HDMI_AUDIO_SS_20BIT 0x2 | ||
| 178 | #define HDMI_AUDIO_SS_24BIT 0x3 | ||
| 179 | |||
| 180 | #define HDMI_AUDIO_CXT_CT 0x0 /* refer to coding in CT */ | ||
| 181 | #define HDMI_AUDIO_CXT_HE_AAC 0x1 | ||
| 182 | #define HDMI_AUDIO_CXT_HE_AAC_V2 0x2 | ||
| 183 | #define HDMI_AUDIO_CXT_MPEG_SURROUND 0x3 | ||
| 184 | |||
| 185 | /* all fields little endian */ | ||
| 186 | struct hdmi_stereo_infoframe { | ||
| 187 | /* PB0 */ | ||
| 188 | u8 csum; | ||
| 189 | |||
| 190 | /* PB1 */ | ||
| 191 | u8 regid0; | ||
| 192 | |||
| 193 | /* PB2 */ | ||
| 194 | u8 regid1; | ||
| 195 | |||
| 196 | /* PB3 */ | ||
| 197 | u8 regid2; | ||
| 198 | |||
| 199 | /* PB4 */ | ||
| 200 | unsigned res1:5; | ||
| 201 | unsigned hdmi_video_format:3; | ||
| 202 | |||
| 203 | /* PB5 */ | ||
| 204 | unsigned res2:4; | ||
| 205 | unsigned _3d_structure:4; | ||
| 206 | |||
| 207 | /* PB6*/ | ||
| 208 | unsigned res3:4; | ||
| 209 | unsigned _3d_ext_data:4; | ||
| 210 | |||
| 211 | } __attribute__((packed)); | ||
| 212 | |||
| 213 | #define HDMI_VENDOR_VERSION 0x01 | ||
| 214 | |||
| 215 | struct tegra_dc_hdmi_data; | ||
| 216 | |||
| 217 | unsigned long tegra_hdmi_readl(struct tegra_dc_hdmi_data *hdmi, | ||
| 218 | unsigned long reg); | ||
| 219 | void tegra_hdmi_writel(struct tegra_dc_hdmi_data *hdmi, | ||
| 220 | unsigned long val, unsigned long reg); | ||
| 221 | |||
| 222 | #endif | ||
diff --git a/drivers/video/tegra/dc/hdmi_reg.h b/drivers/video/tegra/dc/hdmi_reg.h new file mode 100644 index 00000000000..0bdda43199e --- /dev/null +++ b/drivers/video/tegra/dc/hdmi_reg.h | |||
| @@ -0,0 +1,478 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/hdmi_reg.h | ||
| 3 | * | ||
| 4 | * Copyright (C) 2010 Google, Inc. | ||
| 5 | * Author: Erik Gilling <konkers@android.com> | ||
| 6 | * | ||
| 7 | * This software is licensed under the terms of the GNU General Public | ||
| 8 | * License version 2, as published by the Free Software Foundation, and | ||
| 9 | * may be copied, distributed, and modified under those terms. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | |||
| 18 | #ifndef __DRIVERS_VIDEO_TEGRA_DC_HDMI_REG_H | ||
| 19 | #define __DRIVERS_VIDEO_TEGRA_DC_HDMI_REG_H | ||
| 20 | |||
| 21 | #define HDMI_CTXSW 0x00 | ||
| 22 | #define HDMI_NV_PDISP_SOR_STATE0 0x01 | ||
| 23 | #define SOR_STATE_UPDATE (1 << 0) | ||
| 24 | |||
| 25 | #define HDMI_NV_PDISP_SOR_STATE1 0x02 | ||
| 26 | #define SOR_STATE_ASY_HEAD_OPMODE_SLEEP (0 << 0) | ||
| 27 | #define SOR_STATE_ASY_HEAD_OPMODE_SNOOSE (1 << 0) | ||
| 28 | #define SOR_STATE_ASY_HEAD_OPMODE_AWAKE (2 << 0) | ||
| 29 | #define SOR_STATE_ASY_ORMODE_SAFE (0 << 2) | ||
| 30 | #define SOR_STATE_ASY_ORMODE_NORMAL (1 << 2) | ||
| 31 | #define SOR_STATE_ATTACHED (1 << 3) | ||
| 32 | #define SOR_STATE_ARM_SHOW_VGA (1 << 4) | ||
| 33 | |||
| 34 | #define HDMI_NV_PDISP_SOR_STATE2 0x03 | ||
| 35 | #define SOR_STATE_ASY_OWNER_NONE (0 << 0) | ||
| 36 | #define SOR_STATE_ASY_OWNER_HEAD0 (1 << 0) | ||
| 37 | #define SOR_STATE_ASY_SUBOWNER_NONE (0 << 4) | ||
| 38 | #define SOR_STATE_ASY_SUBOWNER_SUBHEAD0 (1 << 4) | ||
| 39 | #define SOR_STATE_ASY_SUBOWNER_SUBHEAD1 (2 << 4) | ||
| 40 | #define SOR_STATE_ASY_SUBOWNER_BOTH (3 << 4) | ||
| 41 | #define SOR_STATE_ASY_CRCMODE_ACTIVE (0 << 6) | ||
| 42 | #define SOR_STATE_ASY_CRCMODE_COMPLETE (1 << 6) | ||
| 43 | #define SOR_STATE_ASY_CRCMODE_NON_ACTIVE (2 << 6) | ||
| 44 | #define SOR_STATE_ASY_PROTOCOL_SINGLE_TMDS_A (1 << 8) | ||
| 45 | #define SOR_STATE_ASY_PROTOCOL_CUSTOM (15 << 8) | ||
| 46 | #define SOR_STATE_ASY_HSYNCPOL_POS (0 << 12) | ||
| 47 | #define SOR_STATE_ASY_HSYNCPOL_NEG (1 << 12) | ||
| 48 | #define SOR_STATE_ASY_VSYNCPOL_POS (0 << 13) | ||
| 49 | #define SOR_STATE_ASY_VSYNCPOL_NEG (1 << 13) | ||
| 50 | #define SOR_STATE_ASY_DEPOL_POS (0 << 14) | ||
| 51 | #define SOR_STATE_ASY_DEPOL_NEG (1 << 14) | ||
| 52 | |||
| 53 | #define HDMI_NV_PDISP_RG_HDCP_AN_MSB 0x04 | ||
| 54 | #define HDMI_NV_PDISP_RG_HDCP_AN_LSB 0x05 | ||
| 55 | #define HDMI_NV_PDISP_RG_HDCP_CN_MSB 0x06 | ||
| 56 | #define HDMI_NV_PDISP_RG_HDCP_CN_LSB 0x07 | ||
| 57 | #define HDMI_NV_PDISP_RG_HDCP_AKSV_MSB 0x08 | ||
| 58 | #define HDMI_NV_PDISP_RG_HDCP_AKSV_LSB 0x09 | ||
| 59 | #define HDMI_NV_PDISP_RG_HDCP_BKSV_MSB 0x0a | ||
| 60 | #define REPEATER (1 << 31) | ||
| 61 | #define HDMI_NV_PDISP_RG_HDCP_BKSV_LSB 0x0b | ||
| 62 | #define HDMI_NV_PDISP_RG_HDCP_CKSV_MSB 0x0c | ||
| 63 | #define HDMI_NV_PDISP_RG_HDCP_CKSV_LSB 0x0d | ||
| 64 | #define HDMI_NV_PDISP_RG_HDCP_DKSV_MSB 0x0e | ||
| 65 | #define HDMI_NV_PDISP_RG_HDCP_DKSV_LSB 0x0f | ||
| 66 | #define HDMI_NV_PDISP_RG_HDCP_CTRL 0x10 | ||
| 67 | #define HDCP_RUN_YES (1 << 0) | ||
| 68 | #define CRYPT_ENABLED (1 << 1) | ||
| 69 | #define ONEONE_ENABLED (1 << 3) | ||
| 70 | #define AN_VALID (1 << 8) | ||
| 71 | #define R0_VALID (1 << 9) | ||
| 72 | #define SPRIME_VALID (1 << 10) | ||
| 73 | #define MPRIME_VALID (1 << 11) | ||
| 74 | #define SROM_ERR (1 << 13) | ||
| 75 | #define HDMI_NV_PDISP_RG_HDCP_CMODE 0x11 | ||
| 76 | #define TMDS0_LINK0 (1 << 4) | ||
| 77 | #define READ_S (1 << 0) | ||
| 78 | #define READ_M (2 << 0) | ||
| 79 | #define HDMI_NV_PDISP_RG_HDCP_MPRIME_MSB 0x12 | ||
| 80 | #define HDMI_NV_PDISP_RG_HDCP_MPRIME_LSB 0x13 | ||
| 81 | #define HDMI_NV_PDISP_RG_HDCP_SPRIME_MSB 0x14 | ||
| 82 | #define STATUS_CS (1 << 6) | ||
| 83 | #define HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB2 0x15 | ||
| 84 | #define HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB1 0x16 | ||
| 85 | #define HDMI_NV_PDISP_RG_HDCP_RI 0x17 | ||
| 86 | #define HDMI_NV_PDISP_RG_HDCP_CS_MSB 0x18 | ||
| 87 | #define HDMI_NV_PDISP_RG_HDCP_CS_LSB 0x19 | ||
| 88 | #define HDMI_NV_PDISP_HDMI_AUDIO_EMU0 0x1a | ||
| 89 | #define HDMI_NV_PDISP_HDMI_AUDIO_EMU_RDATA0 0x1b | ||
| 90 | #define HDMI_NV_PDISP_HDMI_AUDIO_EMU1 0x1c | ||
| 91 | #define HDMI_NV_PDISP_HDMI_AUDIO_EMU2 0x1d | ||
| 92 | #define HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL 0x1e | ||
| 93 | #define HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_STATUS 0x1f | ||
| 94 | #define HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER 0x20 | ||
| 95 | #define HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW 0x21 | ||
| 96 | #define HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH 0x22 | ||
| 97 | #define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL 0x23 | ||
| 98 | #define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_STATUS 0x24 | ||
| 99 | #define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER 0x25 | ||
| 100 | #define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW 0x26 | ||
| 101 | #define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH 0x27 | ||
| 102 | #define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW 0x28 | ||
| 103 | #define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH 0x29 | ||
| 104 | #define INFOFRAME_CTRL_ENABLE (1 << 0) | ||
| 105 | #define INFOFRAME_CTRL_OTHER (1 << 4) | ||
| 106 | #define INFOFRAME_CTRL_SINGLE (1 << 8) | ||
| 107 | |||
| 108 | #define INFOFRAME_HEADER_TYPE(x) ((x) & 0xff) | ||
| 109 | #define INFOFRAME_HEADER_VERSION(x) (((x) & 0xff) << 8) | ||
| 110 | #define INFOFRAME_HEADER_LEN(x) (((x) & 0xf) << 16) | ||
| 111 | |||
| 112 | #define HDMI_NV_PDISP_HDMI_GENERIC_CTRL 0x2a | ||
| 113 | #define GENERIC_CTRL_ENABLE (1 << 0) | ||
| 114 | #define GENERIC_CTRL_OTHER (1 << 4) | ||
| 115 | #define GENERIC_CTRL_SINGLE (1 << 8) | ||
| 116 | #define GENERIC_CTRL_HBLANK (1 << 12) | ||
| 117 | #define GENERIC_CTRL_AUDIO (1 << 16) | ||
| 118 | |||
| 119 | #define HDMI_NV_PDISP_HDMI_GENERIC_STATUS 0x2b | ||
| 120 | #define HDMI_NV_PDISP_HDMI_GENERIC_HEADER 0x2c | ||
| 121 | #define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_LOW 0x2d | ||
| 122 | #define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_HIGH 0x2e | ||
| 123 | #define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_LOW 0x2f | ||
| 124 | #define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_HIGH 0x30 | ||
| 125 | #define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_LOW 0x31 | ||
| 126 | #define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_HIGH 0x32 | ||
| 127 | #define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_LOW 0x33 | ||
| 128 | #define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_HIGH 0x34 | ||
| 129 | #define HDMI_NV_PDISP_HDMI_ACR_CTRL 0x35 | ||
| 130 | #define HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW 0x36 | ||
| 131 | #define HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_HIGH 0x37 | ||
| 132 | #define HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW 0x38 | ||
| 133 | #define HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH 0x39 | ||
| 134 | #define HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW 0x3a | ||
| 135 | #define HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_HIGH 0x3b | ||
| 136 | #define HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW 0x3c | ||
| 137 | #define HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_HIGH 0x3d | ||
| 138 | #define HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW 0x3e | ||
| 139 | #define HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_HIGH 0x3f | ||
| 140 | #define HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW 0x40 | ||
| 141 | #define HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_HIGH 0x41 | ||
| 142 | #define HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW 0x42 | ||
| 143 | #define HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_HIGH 0x43 | ||
| 144 | #define ACR_SB3(x) (((x) & 0xff) << 8) | ||
| 145 | #define ACR_SB2(x) (((x) & 0xff) << 16) | ||
| 146 | #define ACR_SB1(x) (((x) & 0xff) << 24) | ||
| 147 | #define ACR_SUBPACK_CTS(x) (((x) & 0xffffff) << 8) | ||
| 148 | |||
| 149 | #define ACR_SB6(x) (((x) & 0xff) << 0) | ||
| 150 | #define ACR_SB5(x) (((x) & 0xff) << 8) | ||
| 151 | #define ACR_SB4(x) (((x) & 0xff) << 16) | ||
| 152 | #define ACR_ENABLE (1 << 31) | ||
| 153 | #define ACR_SUBPACK_N(x) ((x) & 0xffffff) | ||
| 154 | |||
| 155 | #define HDMI_NV_PDISP_HDMI_CTRL 0x44 | ||
| 156 | #define HDMI_CTRL_REKEY(x) (((x) & 0x7f) << 0) | ||
| 157 | #define HDMI_CTRL_AUDIO_LAYOUT (1 << 8) | ||
| 158 | #define HDMI_CTRL_SAMPLE_FLAT (1 << 12) | ||
| 159 | #define HDMI_CTRL_MAX_AC_PACKET(x) (((x) & 0x1f) << 16) | ||
| 160 | #define HDMI_CTRL_ENABLE (1 << 30) | ||
| 161 | |||
| 162 | #define HDMI_NV_PDISP_HDMI_VSYNC_KEEPOUT 0x45 | ||
| 163 | #define HDMI_NV_PDISP_HDMI_VSYNC_WINDOW 0x46 | ||
| 164 | #define VSYNC_WINDOW_END(x) (((x) & 0x3ff) << 0) | ||
| 165 | #define VSYNC_WINDOW_START(x) (((x) & 0x3ff) << 16) | ||
| 166 | #define VSYNC_WINDOW_ENABLE (1 << 31) | ||
| 167 | |||
| 168 | #define HDMI_NV_PDISP_HDMI_GCP_CTRL 0x47 | ||
| 169 | #define HDMI_NV_PDISP_HDMI_GCP_STATUS 0x48 | ||
| 170 | #define HDMI_NV_PDISP_HDMI_GCP_SUBPACK 0x49 | ||
| 171 | #define HDMI_NV_PDISP_HDMI_CHANNEL_STATUS1 0x4a | ||
| 172 | #define HDMI_NV_PDISP_HDMI_CHANNEL_STATUS2 0x4b | ||
| 173 | #define HDMI_NV_PDISP_HDMI_EMU0 0x4c | ||
| 174 | #define HDMI_NV_PDISP_HDMI_EMU1 0x4d | ||
| 175 | #define HDMI_NV_PDISP_HDMI_EMU1_RDATA 0x4e | ||
| 176 | #define HDMI_NV_PDISP_HDMI_SPARE 0x4f | ||
| 177 | #define SPARE_HW_CTS (1 << 0) | ||
| 178 | #define SPARE_FORCE_SW_CTS (1 << 1) | ||
| 179 | #define SPARE_CTS_RESET_VAL(x) (((x) & 0x7) << 16) | ||
| 180 | #define SPARE_ACR_PRIORITY_HIGH (0 << 31) | ||
| 181 | #define SPARE_ACR_PRIORITY_LOW (1 << 31) | ||
| 182 | |||
| 183 | #define HDMI_NV_PDISP_HDMI_SPDIF_CHN_STATUS1 0x50 | ||
| 184 | #define HDMI_NV_PDISP_HDMI_SPDIF_CHN_STATUS2 0x51 | ||
| 185 | #define HDMI_NV_PDISP_HDCPRIF_ROM_CTRL 0x53 | ||
| 186 | #define HDMI_NV_PDISP_SOR_CAP 0x54 | ||
| 187 | #define HDMI_NV_PDISP_SOR_PWR 0x55 | ||
| 188 | #define SOR_PWR_NORMAL_STATE_PD (0 << 0) | ||
| 189 | #define SOR_PWR_NORMAL_STATE_PU (1 << 0) | ||
| 190 | #define SOR_PWR_NORMAL_START_NORMAL (0 << 1) | ||
| 191 | #define SOR_PWR_NORMAL_START_ALT (1 << 1) | ||
| 192 | #define SOR_PWR_SAFE_STATE_PD (0 << 16) | ||
| 193 | #define SOR_PWR_SAFE_STATE_PU (1 << 16) | ||
| 194 | #define SOR_PWR_SAFE_START_NORMAL (0 << 17) | ||
| 195 | #define SOR_PWR_SAFE_START_ALT (1 << 17) | ||
| 196 | #define SOR_PWR_HALT_DELAY (1 << 24) | ||
| 197 | #define SOR_PWR_MODE (1 << 28) | ||
| 198 | #define SOR_PWR_SETTING_NEW_DONE (0 << 31) | ||
| 199 | #define SOR_PWR_SETTING_NEW_PENDING (1 << 31) | ||
| 200 | #define SOR_PWR_SETTING_NEW_TRIGGER (1 << 31) | ||
| 201 | |||
| 202 | #define HDMI_NV_PDISP_SOR_TEST 0x56 | ||
| 203 | #define HDMI_NV_PDISP_SOR_PLL0 0x57 | ||
| 204 | #define SOR_PLL_PWR (1 << 0) | ||
| 205 | #define SOR_PLL_PDBG (1 << 1) | ||
| 206 | #define SOR_PLL_VCOPD (1 << 2) | ||
| 207 | #define SOR_PLL_PDPORT (1 << 3) | ||
| 208 | #define SOR_PLL_RESISTORSEL (1 << 4) | ||
| 209 | #define SOR_PLL_PULLDOWN (1 << 5) | ||
| 210 | #define SOR_PLL_VCOCAP(x) (((x) & 0xf) << 8) | ||
| 211 | #define SOR_PLL_BG_V17_S(x) (((x) & 0xf) << 12) | ||
| 212 | #define SOR_PLL_FILTER(x) (((x) & 0xf) << 16) | ||
| 213 | #define SOR_PLL_ICHPMP(x) (((x) & 0xf) << 24) | ||
| 214 | #define SOR_PLL_TX_REG_LOAD(x) (((x) & 0x3) << 28) | ||
| 215 | |||
| 216 | #define HDMI_NV_PDISP_SOR_PLL1 0x58 | ||
| 217 | #define SOR_PLL_TMDS_TERM_ENABLE (1 << 8) | ||
| 218 | #define SOR_PLL_TMDS_TERMADJ(x) (((x) & 0xf) << 9) | ||
| 219 | #define SOR_PLL_LOADADJ(x) (((x) & 0xf) << 20) | ||
| 220 | #define SOR_PLL_PE_EN (1 << 28) | ||
| 221 | #define SOR_PLL_HALF_FULL_PE (1 << 29) | ||
| 222 | #define SOR_PLL_S_D_PIN_PE (1 << 30) | ||
| 223 | |||
| 224 | #define HDMI_NV_PDISP_SOR_PLL2 0x59 | ||
| 225 | #define HDMI_NV_PDISP_SOR_CSTM 0x5a | ||
| 226 | #define SOR_CSTM_PD_TXDA_0 (1 << 0) | ||
| 227 | #define SOR_CSTM_PD_TXDA_1 (1 << 1) | ||
| 228 | #define SOR_CSTM_PD_TXDA_2 (1 << 2) | ||
| 229 | #define SOR_CSTM_PD_TXDA_3 (1 << 3) | ||
| 230 | #define SOR_CSTM_PD_TXDB_0 (1 << 4) | ||
| 231 | #define SOR_CSTM_PD_TXDB_1 (1 << 5) | ||
| 232 | #define SOR_CSTM_PD_TXDB_2 (1 << 6) | ||
| 233 | #define SOR_CSTM_PD_TXDB_3 (1 << 7) | ||
| 234 | #define SOR_CSTM_PD_TXCA (1 << 8) | ||
| 235 | #define SOR_CSTM_PD_TXCB (1 << 9) | ||
| 236 | #define SOR_CSTM_UPPER (1 << 11) | ||
| 237 | #define SOR_CSTM_MODE(x) (((x) & 0x3) << 12) | ||
| 238 | #define SOR_CSTM_LINKACTA (1 << 14) | ||
| 239 | #define SOR_CSTM_LINKACTB (1 << 15) | ||
| 240 | #define SOR_CSTM_LVDS_EN (1 << 16) | ||
| 241 | #define SOR_CSTM_DUP_SYNC (1 << 17) | ||
| 242 | #define SOR_CSTM_NEW_MODE (1 << 18) | ||
| 243 | #define SOR_CSTM_BALANCED (1 << 19) | ||
| 244 | #define SOR_CSTM_PLLDIV (1 << 21) | ||
| 245 | #define SOR_CSTM_ROTCLK(x) (((x) & 0xf) << 24) | ||
| 246 | #define SOR_CSTM_ROTDAT(x) (((x) & 0x7) << 28) | ||
| 247 | |||
| 248 | #define HDMI_NV_PDISP_SOR_LVDS 0x5b | ||
| 249 | #define HDMI_NV_PDISP_SOR_CRCA 0x5c | ||
| 250 | #define HDMI_NV_PDISP_SOR_CRCB 0x5d | ||
| 251 | #define HDMI_NV_PDISP_SOR_BLANK 0x5e | ||
| 252 | #define HDMI_NV_PDISP_SOR_SEQ_CTL 0x5f | ||
| 253 | #define SOR_SEQ_CTL_PU_PC(x) (((x) & 0xf) << 0) | ||
| 254 | #define SOR_SEQ_PU_PC_ALT(x) (((x) & 0xf) << 4) | ||
| 255 | #define SOR_SEQ_PD_PC(x) (((x) & 0xf) << 8) | ||
| 256 | #define SOR_SEQ_PD_PC_ALT(x) (((x) & 0xf) << 12) | ||
| 257 | #define SOR_SEQ_PC(x) (((x) & 0xf) << 16) | ||
| 258 | #define SOR_SEQ_STATUS (1 << 28) | ||
| 259 | #define SOR_SEQ_SWITCH (1 << 30) | ||
| 260 | |||
| 261 | #define HDMI_NV_PDISP_SOR_SEQ_INST0 0x60 | ||
| 262 | #define HDMI_NV_PDISP_SOR_SEQ_INST1 0x61 | ||
| 263 | #define HDMI_NV_PDISP_SOR_SEQ_INST2 0x62 | ||
| 264 | #define HDMI_NV_PDISP_SOR_SEQ_INST3 0x63 | ||
| 265 | #define HDMI_NV_PDISP_SOR_SEQ_INST4 0x64 | ||
| 266 | #define HDMI_NV_PDISP_SOR_SEQ_INST5 0x65 | ||
| 267 | #define HDMI_NV_PDISP_SOR_SEQ_INST6 0x66 | ||
| 268 | #define HDMI_NV_PDISP_SOR_SEQ_INST7 0x67 | ||
| 269 | #define HDMI_NV_PDISP_SOR_SEQ_INST8 0x68 | ||
| 270 | #define HDMI_NV_PDISP_SOR_SEQ_INST9 0x69 | ||
| 271 | #define HDMI_NV_PDISP_SOR_SEQ_INSTA 0x6a | ||
| 272 | #define HDMI_NV_PDISP_SOR_SEQ_INSTB 0x6b | ||
| 273 | #define HDMI_NV_PDISP_SOR_SEQ_INSTC 0x6c | ||
| 274 | #define HDMI_NV_PDISP_SOR_SEQ_INSTD 0x6d | ||
| 275 | #define HDMI_NV_PDISP_SOR_SEQ_INSTE 0x6e | ||
| 276 | #define HDMI_NV_PDISP_SOR_SEQ_INSTF 0x6f | ||
| 277 | #define SOR_SEQ_INST_WAIT_TIME(x) (((x) & 0x3ff) << 0) | ||
| 278 | #define SOR_SEQ_INST_WAIT_UNITS_US (0 << 12) | ||
| 279 | #define SOR_SEQ_INST_WAIT_UNITS_MS (1 << 12) | ||
| 280 | #define SOR_SEQ_INST_WAIT_UNITS_VSYNC (2 << 12) | ||
| 281 | #define SOR_SEQ_INST_HALT (1 << 15) | ||
| 282 | #define SOR_SEQ_INST_PIN_A_LOW (0 << 21) | ||
| 283 | #define SOR_SEQ_INST_PIN_A_HIGH (1 << 21) | ||
| 284 | #define SOR_SEQ_INST_PIN_B_LOW (0 << 22) | ||
| 285 | #define SOR_SEQ_INST_PIN_B_HIGH (1 << 22) | ||
| 286 | #define SOR_SEQ_INST_DRIVE_PWM_OUT_LO (1 << 23) | ||
| 287 | #define SOR_SEQ_INST_TRISTATE_IOS (1 << 24) | ||
| 288 | #define SOR_SEQ_INST_SOR_SEQ_INST_BLACK_DATA (1 << 25) | ||
| 289 | #define SOR_SEQ_INST_BLANK_DE (1 << 26) | ||
| 290 | #define SOR_SEQ_INST_BLANK_H (1 << 27) | ||
| 291 | #define SOR_SEQ_INST_BLANK_V (1 << 28) | ||
| 292 | #define SOR_SEQ_INST_ASSERT_PLL_RESETV (1 << 29) | ||
| 293 | #define SOR_SEQ_INST_POWERDOWN_MACRO (1 << 30) | ||
| 294 | #define SOR_SEQ_INST_PLL_PULLDOWN (1 << 31) | ||
| 295 | |||
| 296 | #define HDMI_NV_PDISP_SOR_VCRCA0 0x72 | ||
| 297 | #define HDMI_NV_PDISP_SOR_VCRCA1 0x73 | ||
| 298 | #define HDMI_NV_PDISP_SOR_CCRCA0 0x74 | ||
| 299 | #define HDMI_NV_PDISP_SOR_CCRCA1 0x75 | ||
| 300 | #define HDMI_NV_PDISP_SOR_EDATAA0 0x76 | ||
| 301 | #define HDMI_NV_PDISP_SOR_EDATAA1 0x77 | ||
| 302 | #define HDMI_NV_PDISP_SOR_COUNTA0 0x78 | ||
| 303 | #define HDMI_NV_PDISP_SOR_COUNTA1 0x79 | ||
| 304 | #define HDMI_NV_PDISP_SOR_DEBUGA0 0x7a | ||
| 305 | #define HDMI_NV_PDISP_SOR_DEBUGA1 0x7b | ||
| 306 | #define HDMI_NV_PDISP_SOR_TRIG 0x7c | ||
| 307 | #define HDMI_NV_PDISP_SOR_MSCHECK 0x7d | ||
| 308 | #define HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT 0x7e | ||
| 309 | #define DRIVE_CURRENT_LANE0(x) (((x) & 0x3f) << 0) | ||
| 310 | #define DRIVE_CURRENT_LANE1(x) (((x) & 0x3f) << 8) | ||
| 311 | #define DRIVE_CURRENT_LANE2(x) (((x) & 0x3f) << 16) | ||
| 312 | #define DRIVE_CURRENT_LANE3(x) (((x) & 0x3f) << 24) | ||
| 313 | #define DRIVE_CURRENT_FUSE_OVERRIDE (1 << 31) | ||
| 314 | #define DRIVE_CURRENT_1_500_mA 0x00 | ||
| 315 | #define DRIVE_CURRENT_1_875_mA 0x01 | ||
| 316 | #define DRIVE_CURRENT_2_250_mA 0x02 | ||
| 317 | #define DRIVE_CURRENT_2_625_mA 0x03 | ||
| 318 | #define DRIVE_CURRENT_3_000_mA 0x04 | ||
| 319 | #define DRIVE_CURRENT_3_375_mA 0x05 | ||
| 320 | #define DRIVE_CURRENT_3_750_mA 0x06 | ||
| 321 | #define DRIVE_CURRENT_4_125_mA 0x07 | ||
| 322 | #define DRIVE_CURRENT_4_500_mA 0x08 | ||
| 323 | #define DRIVE_CURRENT_4_875_mA 0x09 | ||
| 324 | #define DRIVE_CURRENT_5_250_mA 0x0a | ||
| 325 | #define DRIVE_CURRENT_5_625_mA 0x0b | ||
| 326 | #define DRIVE_CURRENT_6_000_mA 0x0c | ||
| 327 | #define DRIVE_CURRENT_6_375_mA 0x0d | ||
| 328 | #define DRIVE_CURRENT_6_750_mA 0x0e | ||
| 329 | #define DRIVE_CURRENT_7_125_mA 0x0f | ||
| 330 | #define DRIVE_CURRENT_7_500_mA 0x10 | ||
| 331 | #define DRIVE_CURRENT_7_875_mA 0x11 | ||
| 332 | #define DRIVE_CURRENT_8_250_mA 0x12 | ||
| 333 | #define DRIVE_CURRENT_8_625_mA 0x13 | ||
| 334 | #define DRIVE_CURRENT_9_000_mA 0x14 | ||
| 335 | #define DRIVE_CURRENT_9_375_mA 0x15 | ||
| 336 | #define DRIVE_CURRENT_9_750_mA 0x16 | ||
| 337 | #define DRIVE_CURRENT_10_125_mA 0x17 | ||
| 338 | #define DRIVE_CURRENT_10_500_mA 0x18 | ||
| 339 | #define DRIVE_CURRENT_10_875_mA 0x19 | ||
| 340 | #define DRIVE_CURRENT_11_250_mA 0x1a | ||
| 341 | #define DRIVE_CURRENT_11_625_mA 0x1b | ||
| 342 | #define DRIVE_CURRENT_12_000_mA 0x1c | ||
| 343 | #define DRIVE_CURRENT_12_375_mA 0x1d | ||
| 344 | #define DRIVE_CURRENT_12_750_mA 0x1e | ||
| 345 | #define DRIVE_CURRENT_13_125_mA 0x1f | ||
| 346 | #define DRIVE_CURRENT_13_500_mA 0x20 | ||
| 347 | #define DRIVE_CURRENT_13_875_mA 0x21 | ||
| 348 | #define DRIVE_CURRENT_14_250_mA 0x22 | ||
| 349 | #define DRIVE_CURRENT_14_625_mA 0x23 | ||
| 350 | #define DRIVE_CURRENT_15_000_mA 0x24 | ||
| 351 | #define DRIVE_CURRENT_15_375_mA 0x25 | ||
| 352 | #define DRIVE_CURRENT_15_750_mA 0x26 | ||
| 353 | #define DRIVE_CURRENT_16_125_mA 0x27 | ||
| 354 | #define DRIVE_CURRENT_16_500_mA 0x28 | ||
| 355 | #define DRIVE_CURRENT_16_875_mA 0x29 | ||
| 356 | #define DRIVE_CURRENT_17_250_mA 0x2a | ||
| 357 | #define DRIVE_CURRENT_17_625_mA 0x2b | ||
| 358 | #define DRIVE_CURRENT_18_000_mA 0x2c | ||
| 359 | #define DRIVE_CURRENT_18_375_mA 0x2d | ||
| 360 | #define DRIVE_CURRENT_18_750_mA 0x2e | ||
| 361 | #define DRIVE_CURRENT_19_125_mA 0x2f | ||
| 362 | #define DRIVE_CURRENT_19_500_mA 0x30 | ||
| 363 | #define DRIVE_CURRENT_19_875_mA 0x31 | ||
| 364 | #define DRIVE_CURRENT_20_250_mA 0x32 | ||
| 365 | #define DRIVE_CURRENT_20_625_mA 0x33 | ||
| 366 | #define DRIVE_CURRENT_21_000_mA 0x34 | ||
| 367 | #define DRIVE_CURRENT_21_375_mA 0x35 | ||
| 368 | #define DRIVE_CURRENT_21_750_mA 0x36 | ||
| 369 | #define DRIVE_CURRENT_22_125_mA 0x37 | ||
| 370 | #define DRIVE_CURRENT_22_500_mA 0x38 | ||
| 371 | #define DRIVE_CURRENT_22_875_mA 0x39 | ||
| 372 | #define DRIVE_CURRENT_23_250_mA 0x3a | ||
| 373 | #define DRIVE_CURRENT_23_625_mA 0x3b | ||
| 374 | #define DRIVE_CURRENT_24_000_mA 0x3c | ||
| 375 | #define DRIVE_CURRENT_24_375_mA 0x3d | ||
| 376 | #define DRIVE_CURRENT_24_750_mA 0x3e | ||
| 377 | |||
| 378 | #define HDMI_NV_PDISP_AUDIO_DEBUG0 0x7f | ||
| 379 | #define HDMI_NV_PDISP_AUDIO_DEBUG1 0x80 | ||
| 380 | #define HDMI_NV_PDISP_AUDIO_DEBUG2 0x81 | ||
| 381 | /* note: datasheet defines FS1..FS7. we have FS(0)..FS(6) */ | ||
| 382 | #define HDMI_NV_PDISP_AUDIO_FS(x) (0x82 + (x)) | ||
| 383 | #define AUDIO_FS_LOW(x) (((x) & 0xfff) << 0) | ||
| 384 | #define AUDIO_FS_HIGH(x) (((x) & 0xfff) << 16) | ||
| 385 | |||
| 386 | |||
| 387 | #define HDMI_NV_PDISP_AUDIO_PULSE_WIDTH 0x89 | ||
| 388 | #define HDMI_NV_PDISP_AUDIO_THRESHOLD 0x8a | ||
| 389 | #define HDMI_NV_PDISP_AUDIO_CNTRL0 0x8b | ||
| 390 | #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) | ||
| 391 | #define HDMI_NV_PDISP_SOR_AUDIO_CNTRL0_0 0xac | ||
| 392 | #define HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0 0xbc | ||
| 393 | #define HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE_0 0xbd | ||
| 394 | #define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0320_0 0xbf | ||
| 395 | #define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0441_0 0xc0 | ||
| 396 | #define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0882_0 0xc1 | ||
| 397 | #define HDMI_NV_PDISP_SOR_AUDIO_AVAL_1764_0 0xc2 | ||
| 398 | #define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0480_0 0xc3 | ||
| 399 | #define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0960_0 0xc4 | ||
| 400 | #define HDMI_NV_PDISP_SOR_AUDIO_AVAL_1920_0 0xc5 | ||
| 401 | #define HDMI_NV_PDISP_SOR_AUDIO_AVAL_DEFAULT_0 0xc6 | ||
| 402 | #endif | ||
| 403 | #define AUDIO_CNTRL0_ERROR_TOLERANCE(x) (((x) & 0xff) << 0) | ||
| 404 | #define AUDIO_CNTRL0_SOFT_RESET (1 << 8) | ||
| 405 | #define AUDIO_CNTRL0_SOFT_RESET_ALL (1 << 12) | ||
| 406 | #define AUDIO_CNTRL0_SAMPLING_FREQ_UNKNOWN (1 << 16) | ||
| 407 | #define AUDIO_CNTRL0_SAMPLING_FREQ_32K (2 << 16) | ||
| 408 | #define AUDIO_CNTRL0_SAMPLING_FREQ_44_1K (0 << 16) | ||
| 409 | #define AUDIO_CNTRL0_SAMPLING_FREQ_48K (2 << 16) | ||
| 410 | #define AUDIO_CNTRL0_SAMPLING_FREQ_88_2K (8 << 16) | ||
| 411 | #define AUDIO_CNTRL0_SAMPLING_FREQ_96K (10 << 16) | ||
| 412 | #define AUDIO_CNTRL0_SAMPLING_FREQ_176_4K (12 << 16) | ||
| 413 | #define AUDIO_CNTRL0_SAMPLING_FREQ_192K (14 << 16) | ||
| 414 | #define AUDIO_CNTRL0_SOURCE_SELECT_AUTO (0 << 20) | ||
| 415 | #define AUDIO_CNTRL0_SOURCE_SELECT_SPDIF (1 << 20) | ||
| 416 | #define AUDIO_CNTRL0_SOURCE_SELECT_HDAL (2 << 20) | ||
| 417 | #define AUDIO_CNTRL0_INJECT_NULLSMPL (1 << 29) | ||
| 418 | #define AUDIO_CNTRL0_FRAMES_PER_BLOCK(x) (((x) & 0xff) << 24) | ||
| 419 | |||
| 420 | #define HDMI_NV_PDISP_AUDIO_N 0x8c | ||
| 421 | #define AUDIO_N_VALUE(x) (((x) & 0xfffff) << 0) | ||
| 422 | #define AUDIO_N_RESETF (1 << 20) | ||
| 423 | #define AUDIO_N_GENERATE_NORMAL (0 << 24) | ||
| 424 | #define AUDIO_N_GENERATE_ALTERNALTE (1 << 24) | ||
| 425 | #define AUDIO_N_LOOKUP_ENABLE (1 << 28) | ||
| 426 | |||
| 427 | #define HDMI_NV_PDISP_HDCPRIF_ROM_TIMING 0x94 | ||
| 428 | #define HDMI_NV_PDISP_SOR_REFCLK 0x95 | ||
| 429 | #define SOR_REFCLK_DIV_INT(x) (((x) & 0xff) << 8) | ||
| 430 | #define SOR_REFCLK_DIV_FRAC(x) (((x) & 0x3) << 6) | ||
| 431 | |||
| 432 | #define HDMI_NV_PDISP_CRC_CONTROL 0x96 | ||
| 433 | #define HDMI_NV_PDISP_INPUT_CONTROL 0x97 | ||
| 434 | #define HDMI_SRC_DISPLAYA (0 << 0) | ||
| 435 | #define HDMI_SRC_DISPLAYB (1 << 0) | ||
| 436 | #define ARM_VIDEO_RANGE_FULL (0 << 1) | ||
| 437 | #define ARM_VIDEO_RANGE_LIMITED (1 << 1) | ||
| 438 | |||
| 439 | #define HDMI_NV_PDISP_SCRATCH 0x98 | ||
| 440 | #define HDMI_NV_PDISP_PE_CURRENT 0x99 | ||
| 441 | #define PE_CURRENT0(x) (((x) & 0xf) << 0) | ||
| 442 | #define PE_CURRENT1(x) (((x) & 0xf) << 8) | ||
| 443 | #define PE_CURRENT2(x) (((x) & 0xf) << 16) | ||
| 444 | #define PE_CURRENT3(x) (((x) & 0xf) << 24) | ||
| 445 | #define PE_CURRENT_0_0_mA 0x0 | ||
| 446 | #define PE_CURRENT_0_5_mA 0x1 | ||
| 447 | #define PE_CURRENT_1_0_mA 0x2 | ||
| 448 | #define PE_CURRENT_1_5_mA 0x3 | ||
| 449 | #define PE_CURRENT_2_0_mA 0x4 | ||
| 450 | #define PE_CURRENT_2_5_mA 0x5 | ||
| 451 | #define PE_CURRENT_3_0_mA 0x6 | ||
| 452 | #define PE_CURRENT_3_5_mA 0x7 | ||
| 453 | #define PE_CURRENT_4_0_mA 0x8 | ||
| 454 | #define PE_CURRENT_4_5_mA 0x9 | ||
| 455 | #define PE_CURRENT_5_0_mA 0xa | ||
| 456 | #define PE_CURRENT_5_5_mA 0xb | ||
| 457 | #define PE_CURRENT_6_0_mA 0xc | ||
| 458 | #define PE_CURRENT_6_5_mA 0xd | ||
| 459 | #define PE_CURRENT_7_0_mA 0xe | ||
| 460 | #define PE_CURRENT_7_5_mA 0xf | ||
| 461 | |||
| 462 | #define HDMI_NV_PDISP_KEY_CTRL 0x9a | ||
| 463 | #define LOCAL_KEYS (1 << 0) | ||
| 464 | #define AUTOINC (1 << 1) | ||
| 465 | #define WRITE16 (1 << 4) | ||
| 466 | #define PKEY_REQUEST_RELOAD_TRIGGER (1 << 5) | ||
| 467 | #define PKEY_LOADED (1 << 6) | ||
| 468 | #define HDMI_NV_PDISP_KEY_DEBUG0 0x9b | ||
| 469 | #define HDMI_NV_PDISP_KEY_DEBUG1 0x9c | ||
| 470 | #define HDMI_NV_PDISP_KEY_DEBUG2 0x9d | ||
| 471 | #define HDMI_NV_PDISP_KEY_HDCP_KEY_0 0x9e | ||
| 472 | #define HDMI_NV_PDISP_KEY_HDCP_KEY_1 0x9f | ||
| 473 | #define HDMI_NV_PDISP_KEY_HDCP_KEY_2 0xa0 | ||
| 474 | #define HDMI_NV_PDISP_KEY_HDCP_KEY_3 0xa1 | ||
| 475 | #define HDMI_NV_PDISP_KEY_HDCP_KEY_TRIG 0xa2 | ||
| 476 | #define HDMI_NV_PDISP_KEY_SKEY_INDEX 0xa3 | ||
| 477 | |||
| 478 | #endif | ||
diff --git a/drivers/video/tegra/dc/nvhdcp.c b/drivers/video/tegra/dc/nvhdcp.c new file mode 100644 index 00000000000..263de07a3da --- /dev/null +++ b/drivers/video/tegra/dc/nvhdcp.c | |||
| @@ -0,0 +1,1259 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/nvhdcp.c | ||
| 3 | * | ||
| 4 | * Copyright (c) 2010-2011, 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/kernel.h> | ||
| 18 | #include <linux/delay.h> | ||
| 19 | #include <linux/i2c.h> | ||
| 20 | #include <linux/miscdevice.h> | ||
| 21 | #include <linux/poll.h> | ||
| 22 | #include <linux/sched.h> | ||
| 23 | #include <linux/uaccess.h> | ||
| 24 | #include <linux/wait.h> | ||
| 25 | #include <linux/workqueue.h> | ||
| 26 | #include <asm/atomic.h> | ||
| 27 | |||
| 28 | #include <mach/dc.h> | ||
| 29 | #include <mach/kfuse.h> | ||
| 30 | |||
| 31 | #include <video/nvhdcp.h> | ||
| 32 | |||
| 33 | #include "dc_reg.h" | ||
| 34 | #include "dc_priv.h" | ||
| 35 | #include "hdmi_reg.h" | ||
| 36 | #include "hdmi.h" | ||
| 37 | |||
| 38 | DECLARE_WAIT_QUEUE_HEAD(wq_worker); | ||
| 39 | |||
| 40 | /* for 0x40 Bcaps */ | ||
| 41 | #define BCAPS_REPEATER (1 << 6) | ||
| 42 | #define BCAPS_READY (1 << 5) | ||
| 43 | #define BCAPS_11 (1 << 1) /* used for both Bcaps and Ainfo */ | ||
| 44 | |||
| 45 | /* for 0x41 Bstatus */ | ||
| 46 | #define BSTATUS_MAX_DEVS_EXCEEDED (1 << 7) | ||
| 47 | #define BSTATUS_MAX_CASCADE_EXCEEDED (1 << 11) | ||
| 48 | |||
| 49 | #ifdef VERBOSE_DEBUG | ||
| 50 | #define nvhdcp_vdbg(...) \ | ||
| 51 | printk("nvhdcp: " __VA_ARGS__) | ||
| 52 | #else | ||
| 53 | #define nvhdcp_vdbg(...) \ | ||
| 54 | ({ \ | ||
| 55 | if(0) \ | ||
| 56 | printk("nvhdcp: " __VA_ARGS__); \ | ||
| 57 | 0; \ | ||
| 58 | }) | ||
| 59 | #endif | ||
| 60 | #define nvhdcp_debug(...) \ | ||
| 61 | pr_debug("nvhdcp: " __VA_ARGS__) | ||
| 62 | #define nvhdcp_err(...) \ | ||
| 63 | pr_err("nvhdcp: Error: " __VA_ARGS__) | ||
| 64 | #define nvhdcp_info(...) \ | ||
| 65 | pr_info("nvhdcp: " __VA_ARGS__) | ||
| 66 | |||
| 67 | |||
| 68 | /* for nvhdcp.state */ | ||
| 69 | enum tegra_nvhdcp_state { | ||
| 70 | STATE_OFF, | ||
| 71 | STATE_UNAUTHENTICATED, | ||
| 72 | STATE_LINK_VERIFY, | ||
| 73 | STATE_RENEGOTIATE, | ||
| 74 | }; | ||
| 75 | |||
| 76 | struct tegra_nvhdcp { | ||
| 77 | struct delayed_work work; | ||
| 78 | struct tegra_dc_hdmi_data *hdmi; | ||
| 79 | struct workqueue_struct *downstream_wq; | ||
| 80 | struct mutex lock; | ||
| 81 | struct miscdevice miscdev; | ||
| 82 | char name[12]; | ||
| 83 | unsigned id; | ||
| 84 | bool plugged; /* true if hotplug detected */ | ||
| 85 | atomic_t policy; /* set policy */ | ||
| 86 | enum tegra_nvhdcp_state state; /* STATE_xxx */ | ||
| 87 | struct i2c_client *client; | ||
| 88 | struct i2c_board_info info; | ||
| 89 | int bus; | ||
| 90 | u32 b_status; | ||
| 91 | u64 a_n; | ||
| 92 | u64 c_n; | ||
| 93 | u64 a_ksv; | ||
| 94 | u64 b_ksv; | ||
| 95 | u64 c_ksv; | ||
| 96 | u64 d_ksv; | ||
| 97 | u8 v_prime[20]; | ||
| 98 | u64 m_prime; | ||
| 99 | u32 num_bksv_list; | ||
| 100 | u64 bksv_list[TEGRA_NVHDCP_MAX_DEVS]; | ||
| 101 | int fail_count; | ||
| 102 | }; | ||
| 103 | |||
| 104 | static inline bool nvhdcp_is_plugged(struct tegra_nvhdcp *nvhdcp) | ||
| 105 | { | ||
| 106 | rmb(); | ||
| 107 | return nvhdcp->plugged; | ||
| 108 | } | ||
| 109 | |||
| 110 | static inline bool nvhdcp_set_plugged(struct tegra_nvhdcp *nvhdcp, bool plugged) | ||
| 111 | { | ||
| 112 | nvhdcp->plugged = plugged; | ||
| 113 | wmb(); | ||
| 114 | return plugged; | ||
| 115 | } | ||
| 116 | |||
| 117 | static int nvhdcp_i2c_read(struct tegra_nvhdcp *nvhdcp, u8 reg, | ||
| 118 | size_t len, void *data) | ||
| 119 | { | ||
| 120 | int status; | ||
| 121 | int retries = 15; | ||
| 122 | struct i2c_msg msg[] = { | ||
| 123 | { | ||
| 124 | .addr = 0x74 >> 1, /* primary link */ | ||
| 125 | .flags = 0, | ||
| 126 | .len = 1, | ||
| 127 | .buf = ®, | ||
| 128 | }, | ||
| 129 | { | ||
| 130 | .addr = 0x74 >> 1, /* primary link */ | ||
| 131 | .flags = I2C_M_RD, | ||
| 132 | .len = len, | ||
| 133 | .buf = data, | ||
| 134 | }, | ||
| 135 | }; | ||
| 136 | |||
| 137 | do { | ||
| 138 | if (!nvhdcp_is_plugged(nvhdcp)) { | ||
| 139 | nvhdcp_err("disconnect during i2c xfer\n"); | ||
| 140 | return -EIO; | ||
| 141 | } | ||
| 142 | status = i2c_transfer(nvhdcp->client->adapter, | ||
| 143 | msg, ARRAY_SIZE(msg)); | ||
| 144 | if ((status < 0) && (retries > 1)) | ||
| 145 | msleep(250); | ||
| 146 | } while ((status < 0) && retries--); | ||
| 147 | |||
| 148 | if (status < 0) { | ||
| 149 | nvhdcp_err("i2c xfer error %d\n", status); | ||
| 150 | return status; | ||
| 151 | } | ||
| 152 | |||
| 153 | return 0; | ||
| 154 | } | ||
| 155 | |||
| 156 | static int nvhdcp_i2c_write(struct tegra_nvhdcp *nvhdcp, u8 reg, | ||
| 157 | size_t len, const void *data) | ||
| 158 | { | ||
| 159 | int status; | ||
| 160 | u8 buf[len + 1]; | ||
| 161 | struct i2c_msg msg[] = { | ||
| 162 | { | ||
| 163 | .addr = 0x74 >> 1, /* primary link */ | ||
| 164 | .flags = 0, | ||
| 165 | .len = len + 1, | ||
| 166 | .buf = buf, | ||
| 167 | }, | ||
| 168 | }; | ||
| 169 | int retries = 15; | ||
| 170 | |||
| 171 | buf[0] = reg; | ||
| 172 | memcpy(buf + 1, data, len); | ||
| 173 | |||
| 174 | do { | ||
| 175 | if (!nvhdcp_is_plugged(nvhdcp)) { | ||
| 176 | nvhdcp_err("disconnect during i2c xfer\n"); | ||
| 177 | return -EIO; | ||
| 178 | } | ||
| 179 | status = i2c_transfer(nvhdcp->client->adapter, | ||
| 180 | msg, ARRAY_SIZE(msg)); | ||
| 181 | if ((status < 0) && (retries > 1)) | ||
| 182 | msleep(250); | ||
| 183 | } while ((status < 0) && retries--); | ||
| 184 | |||
| 185 | if (status < 0) { | ||
| 186 | nvhdcp_err("i2c xfer error %d\n", status); | ||
| 187 | return status; | ||
| 188 | } | ||
| 189 | |||
| 190 | return 0; | ||
| 191 | } | ||
| 192 | |||
| 193 | static inline int nvhdcp_i2c_read8(struct tegra_nvhdcp *nvhdcp, u8 reg, u8 *val) | ||
| 194 | { | ||
| 195 | return nvhdcp_i2c_read(nvhdcp, reg, 1, val); | ||
| 196 | } | ||
| 197 | |||
| 198 | static inline int nvhdcp_i2c_write8(struct tegra_nvhdcp *nvhdcp, u8 reg, u8 val) | ||
| 199 | { | ||
| 200 | return nvhdcp_i2c_write(nvhdcp, reg, 1, &val); | ||
| 201 | } | ||
| 202 | |||
| 203 | static inline int nvhdcp_i2c_read16(struct tegra_nvhdcp *nvhdcp, | ||
| 204 | u8 reg, u16 *val) | ||
| 205 | { | ||
| 206 | u8 buf[2]; | ||
| 207 | int e; | ||
| 208 | |||
| 209 | e = nvhdcp_i2c_read(nvhdcp, reg, sizeof buf, buf); | ||
| 210 | if (e) | ||
| 211 | return e; | ||
| 212 | |||
| 213 | if (val) | ||
| 214 | *val = buf[0] | (u16)buf[1] << 8; | ||
| 215 | |||
| 216 | return 0; | ||
| 217 | } | ||
| 218 | |||
| 219 | static int nvhdcp_i2c_read40(struct tegra_nvhdcp *nvhdcp, u8 reg, u64 *val) | ||
| 220 | { | ||
| 221 | u8 buf[5]; | ||
| 222 | int e, i; | ||
| 223 | u64 n; | ||
| 224 | |||
| 225 | e = nvhdcp_i2c_read(nvhdcp, reg, sizeof buf, buf); | ||
| 226 | if (e) | ||
| 227 | return e; | ||
| 228 | |||
| 229 | for(i = 0, n = 0; i < 5; i++ ) { | ||
| 230 | n <<= 8; | ||
| 231 | n |= buf[4 - i]; | ||
| 232 | } | ||
| 233 | |||
| 234 | if (val) | ||
| 235 | *val = n; | ||
| 236 | |||
| 237 | return 0; | ||
| 238 | } | ||
| 239 | |||
| 240 | static int nvhdcp_i2c_write40(struct tegra_nvhdcp *nvhdcp, u8 reg, u64 val) | ||
| 241 | { | ||
| 242 | char buf[5]; | ||
| 243 | int i; | ||
| 244 | for(i = 0; i < 5; i++ ) { | ||
| 245 | buf[i] = val; | ||
| 246 | val >>= 8; | ||
| 247 | } | ||
| 248 | return nvhdcp_i2c_write(nvhdcp, reg, sizeof buf, buf); | ||
| 249 | } | ||
| 250 | |||
| 251 | static int nvhdcp_i2c_write64(struct tegra_nvhdcp *nvhdcp, u8 reg, u64 val) | ||
| 252 | { | ||
| 253 | char buf[8]; | ||
| 254 | int i; | ||
| 255 | for(i = 0; i < 8; i++ ) { | ||
| 256 | buf[i] = val; | ||
| 257 | val >>= 8; | ||
| 258 | } | ||
| 259 | return nvhdcp_i2c_write(nvhdcp, reg, sizeof buf, buf); | ||
| 260 | } | ||
| 261 | |||
| 262 | |||
| 263 | /* 64-bit link encryption session random number */ | ||
| 264 | static inline u64 get_an(struct tegra_dc_hdmi_data *hdmi) | ||
| 265 | { | ||
| 266 | u64 r; | ||
| 267 | r = (u64)tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_AN_MSB) << 32; | ||
| 268 | r |= tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_AN_LSB); | ||
| 269 | return r; | ||
| 270 | } | ||
| 271 | |||
| 272 | /* 64-bit upstream exchange random number */ | ||
| 273 | static inline void set_cn(struct tegra_dc_hdmi_data *hdmi, u64 c_n) | ||
| 274 | { | ||
| 275 | tegra_hdmi_writel(hdmi, (u32)c_n, HDMI_NV_PDISP_RG_HDCP_CN_LSB); | ||
| 276 | tegra_hdmi_writel(hdmi, c_n >> 32, HDMI_NV_PDISP_RG_HDCP_CN_MSB); | ||
| 277 | } | ||
| 278 | |||
| 279 | |||
| 280 | /* 40-bit transmitter's key selection vector */ | ||
| 281 | static inline u64 get_aksv(struct tegra_dc_hdmi_data *hdmi) | ||
| 282 | { | ||
| 283 | u64 r; | ||
| 284 | r = (u64)tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_AKSV_MSB) << 32; | ||
| 285 | r |= tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_AKSV_LSB); | ||
| 286 | return r; | ||
| 287 | } | ||
| 288 | |||
| 289 | /* 40-bit receiver's key selection vector */ | ||
| 290 | static inline void set_bksv(struct tegra_dc_hdmi_data *hdmi, u64 b_ksv, bool repeater) | ||
| 291 | { | ||
| 292 | if (repeater) | ||
| 293 | b_ksv |= (u64)REPEATER << 32; | ||
| 294 | tegra_hdmi_writel(hdmi, (u32)b_ksv, HDMI_NV_PDISP_RG_HDCP_BKSV_LSB); | ||
| 295 | tegra_hdmi_writel(hdmi, b_ksv >> 32, HDMI_NV_PDISP_RG_HDCP_BKSV_MSB); | ||
| 296 | } | ||
| 297 | |||
| 298 | |||
| 299 | /* 40-bit software's key selection vector */ | ||
| 300 | static inline void set_cksv(struct tegra_dc_hdmi_data *hdmi, u64 c_ksv) | ||
| 301 | { | ||
| 302 | tegra_hdmi_writel(hdmi, (u32)c_ksv, HDMI_NV_PDISP_RG_HDCP_CKSV_LSB); | ||
| 303 | tegra_hdmi_writel(hdmi, c_ksv >> 32, HDMI_NV_PDISP_RG_HDCP_CKSV_MSB); | ||
| 304 | } | ||
| 305 | |||
| 306 | /* 40-bit connection state */ | ||
| 307 | static inline u64 get_cs(struct tegra_dc_hdmi_data *hdmi) | ||
| 308 | { | ||
| 309 | u64 r; | ||
| 310 | r = (u64)tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_CS_MSB) << 32; | ||
| 311 | r |= tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_CS_LSB); | ||
| 312 | return r; | ||
| 313 | } | ||
| 314 | |||
| 315 | /* 40-bit upstream key selection vector */ | ||
| 316 | static inline u64 get_dksv(struct tegra_dc_hdmi_data *hdmi) | ||
| 317 | { | ||
| 318 | u64 r; | ||
| 319 | r = (u64)tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_DKSV_MSB) << 32; | ||
| 320 | r |= tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_DKSV_LSB); | ||
| 321 | return r; | ||
| 322 | } | ||
| 323 | |||
| 324 | /* 64-bit encrypted M0 value */ | ||
| 325 | static inline u64 get_mprime(struct tegra_dc_hdmi_data *hdmi) | ||
| 326 | { | ||
| 327 | u64 r; | ||
| 328 | r = (u64)tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_MPRIME_MSB) << 32; | ||
| 329 | r |= tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_MPRIME_LSB); | ||
| 330 | return r; | ||
| 331 | } | ||
| 332 | |||
| 333 | static inline u16 get_transmitter_ri(struct tegra_dc_hdmi_data *hdmi) | ||
| 334 | { | ||
| 335 | return tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_RI); | ||
| 336 | } | ||
| 337 | |||
| 338 | static inline int get_receiver_ri(struct tegra_nvhdcp *nvhdcp, u16 *r) | ||
| 339 | { | ||
| 340 | return nvhdcp_i2c_read16(nvhdcp, 0x8, r); /* long read */ | ||
| 341 | } | ||
| 342 | |||
| 343 | static int get_bcaps(struct tegra_nvhdcp *nvhdcp, u8 *b_caps) | ||
| 344 | { | ||
| 345 | return nvhdcp_i2c_read8(nvhdcp, 0x40, b_caps); | ||
| 346 | } | ||
| 347 | |||
| 348 | static int get_ksvfifo(struct tegra_nvhdcp *nvhdcp, | ||
| 349 | unsigned num_bksv_list, u64 *ksv_list) | ||
| 350 | { | ||
| 351 | u8 *buf, *p; | ||
| 352 | int e; | ||
| 353 | unsigned i; | ||
| 354 | size_t buf_len = num_bksv_list * 5; | ||
| 355 | |||
| 356 | if (!ksv_list || num_bksv_list > TEGRA_NVHDCP_MAX_DEVS) | ||
| 357 | return -EINVAL; | ||
| 358 | |||
| 359 | if (num_bksv_list == 0) | ||
| 360 | return 0; | ||
| 361 | |||
| 362 | buf = kmalloc(buf_len, GFP_KERNEL); | ||
| 363 | if (IS_ERR_OR_NULL(buf)) | ||
| 364 | return -ENOMEM; | ||
| 365 | |||
| 366 | e = nvhdcp_i2c_read(nvhdcp, 0x43, buf_len, buf); | ||
| 367 | if (e) { | ||
| 368 | kfree(buf); | ||
| 369 | return e; | ||
| 370 | } | ||
| 371 | |||
| 372 | /* load 40-bit keys from repeater into array of u64 */ | ||
| 373 | p = buf; | ||
| 374 | for (i = 0; i < num_bksv_list; i++) { | ||
| 375 | ksv_list[i] = p[0] | ((u64)p[1] << 8) | ((u64)p[2] << 16) | ||
| 376 | | ((u64)p[3] << 24) | ((u64)p[4] << 32); | ||
| 377 | p += 5; | ||
| 378 | } | ||
| 379 | |||
| 380 | kfree(buf); | ||
| 381 | return 0; | ||
| 382 | } | ||
| 383 | |||
| 384 | /* get V' 160-bit SHA-1 hash from repeater */ | ||
| 385 | static int get_vprime(struct tegra_nvhdcp *nvhdcp, u8 *v_prime) | ||
| 386 | { | ||
| 387 | int e, i; | ||
| 388 | |||
| 389 | for (i = 0; i < 20; i += 4) { | ||
| 390 | e = nvhdcp_i2c_read(nvhdcp, 0x20 + i, 4, v_prime + i); | ||
| 391 | if (e) | ||
| 392 | return e; | ||
| 393 | } | ||
| 394 | return 0; | ||
| 395 | } | ||
| 396 | |||
| 397 | |||
| 398 | /* set or clear RUN_YES */ | ||
| 399 | static void hdcp_ctrl_run(struct tegra_dc_hdmi_data *hdmi, bool v) | ||
| 400 | { | ||
| 401 | u32 ctrl; | ||
| 402 | |||
| 403 | if (v) { | ||
| 404 | ctrl = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_CTRL); | ||
| 405 | ctrl |= HDCP_RUN_YES; | ||
| 406 | } else { | ||
| 407 | ctrl = 0; | ||
| 408 | } | ||
| 409 | |||
| 410 | tegra_hdmi_writel(hdmi, ctrl, HDMI_NV_PDISP_RG_HDCP_CTRL); | ||
| 411 | } | ||
| 412 | |||
| 413 | /* wait for any bits in mask to be set in HDMI_NV_PDISP_RG_HDCP_CTRL | ||
| 414 | * sleeps up to 120mS */ | ||
| 415 | static int wait_hdcp_ctrl(struct tegra_dc_hdmi_data *hdmi, u32 mask, u32 *v) | ||
| 416 | { | ||
| 417 | int retries = 13; | ||
| 418 | u32 ctrl; | ||
| 419 | |||
| 420 | do { | ||
| 421 | ctrl = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_CTRL); | ||
| 422 | if ((ctrl & mask)) { | ||
| 423 | if (v) | ||
| 424 | *v = ctrl; | ||
| 425 | break; | ||
| 426 | } | ||
| 427 | if (retries > 1) | ||
| 428 | msleep(10); | ||
| 429 | } while (--retries); | ||
| 430 | if (!retries) { | ||
| 431 | nvhdcp_err("ctrl read timeout (mask=0x%x)\n", mask); | ||
| 432 | return -EIO; | ||
| 433 | } | ||
| 434 | return 0; | ||
| 435 | } | ||
| 436 | |||
| 437 | /* wait for bits in mask to be set to value in HDMI_NV_PDISP_KEY_CTRL | ||
| 438 | * waits up to 100mS */ | ||
| 439 | static int wait_key_ctrl(struct tegra_dc_hdmi_data *hdmi, u32 mask, u32 value) | ||
| 440 | { | ||
| 441 | int retries = 101; | ||
| 442 | u32 ctrl; | ||
| 443 | |||
| 444 | do { | ||
| 445 | msleep(1); | ||
| 446 | ctrl = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_KEY_CTRL); | ||
| 447 | if (((ctrl ^ value) & mask) == 0) | ||
| 448 | break; | ||
| 449 | } while (--retries); | ||
| 450 | if (!retries) { | ||
| 451 | nvhdcp_err("key ctrl read timeout (mask=0x%x)\n", mask); | ||
| 452 | return -EIO; | ||
| 453 | } | ||
| 454 | return 0; | ||
| 455 | } | ||
| 456 | |||
| 457 | /* check that key selection vector is well formed. | ||
| 458 | * NOTE: this function assumes KSV has already been checked against | ||
| 459 | * revocation list. | ||
| 460 | */ | ||
| 461 | static int verify_ksv(u64 k) | ||
| 462 | { | ||
| 463 | unsigned i; | ||
| 464 | |||
| 465 | /* count set bits, must be exactly 20 set to be valid */ | ||
| 466 | for(i = 0; k; i++) | ||
| 467 | k ^= k & -k; | ||
| 468 | |||
| 469 | return (i != 20) ? -EINVAL : 0; | ||
| 470 | } | ||
| 471 | |||
| 472 | /* get Status and Kprime signature - READ_S on TMDS0_LINK0 only */ | ||
| 473 | static int get_s_prime(struct tegra_nvhdcp *nvhdcp, struct tegra_nvhdcp_packet *pkt) | ||
| 474 | { | ||
| 475 | struct tegra_dc_hdmi_data *hdmi = nvhdcp->hdmi; | ||
| 476 | u32 sp_msb, sp_lsb1, sp_lsb2; | ||
| 477 | int e; | ||
| 478 | |||
| 479 | /* if connection isn't authenticated ... */ | ||
| 480 | mutex_lock(&nvhdcp->lock); | ||
| 481 | if (nvhdcp->state != STATE_LINK_VERIFY) { | ||
| 482 | memset(pkt, 0, sizeof *pkt); | ||
| 483 | pkt->packet_results = TEGRA_NVHDCP_RESULT_LINK_FAILED; | ||
| 484 | e = 0; | ||
| 485 | goto err; | ||
| 486 | } | ||
| 487 | |||
| 488 | pkt->packet_results = TEGRA_NVHDCP_RESULT_UNSUCCESSFUL; | ||
| 489 | |||
| 490 | /* we will be taking c_n, c_ksv as input */ | ||
| 491 | if (!(pkt->value_flags & TEGRA_NVHDCP_FLAG_CN) | ||
| 492 | || !(pkt->value_flags & TEGRA_NVHDCP_FLAG_CKSV)) { | ||
| 493 | nvhdcp_err("missing value_flags (0x%x)\n", pkt->value_flags); | ||
| 494 | e = -EINVAL; | ||
| 495 | goto err; | ||
| 496 | } | ||
| 497 | |||
| 498 | pkt->value_flags = 0; | ||
| 499 | |||
| 500 | pkt->a_ksv = nvhdcp->a_ksv; | ||
| 501 | pkt->a_n = nvhdcp->a_n; | ||
| 502 | pkt->value_flags = TEGRA_NVHDCP_FLAG_AKSV | TEGRA_NVHDCP_FLAG_AN; | ||
| 503 | |||
| 504 | nvhdcp_vdbg("%s():cn %llx cksv %llx\n", __func__, pkt->c_n, pkt->c_ksv); | ||
| 505 | |||
| 506 | set_cn(hdmi, pkt->c_n); | ||
| 507 | |||
| 508 | tegra_hdmi_writel(hdmi, TMDS0_LINK0 | READ_S, | ||
| 509 | HDMI_NV_PDISP_RG_HDCP_CMODE); | ||
| 510 | |||
| 511 | set_cksv(hdmi, pkt->c_ksv); | ||
| 512 | |||
| 513 | e = wait_hdcp_ctrl(hdmi, SPRIME_VALID, NULL); | ||
| 514 | if (e) { | ||
| 515 | nvhdcp_err("Sprime read timeout\n"); | ||
| 516 | pkt->packet_results = TEGRA_NVHDCP_RESULT_UNSUCCESSFUL; | ||
| 517 | e = -EIO; | ||
| 518 | goto err; | ||
| 519 | } | ||
| 520 | |||
| 521 | msleep(50); | ||
| 522 | |||
| 523 | /* read 56-bit Sprime plus 16 status bits */ | ||
| 524 | sp_msb = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_SPRIME_MSB); | ||
| 525 | sp_lsb1 = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB1); | ||
| 526 | sp_lsb2 = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB2); | ||
| 527 | |||
| 528 | /* top 8 bits of LSB2 and bottom 8 bits of MSB hold status bits. */ | ||
| 529 | pkt->hdcp_status = ( sp_msb << 8 ) | ( sp_lsb2 >> 24); | ||
| 530 | pkt->value_flags |= TEGRA_NVHDCP_FLAG_S; | ||
| 531 | |||
| 532 | /* 56-bit Kprime */ | ||
| 533 | pkt->k_prime = ((u64)(sp_lsb2 & 0xffffff) << 32) | sp_lsb1; | ||
| 534 | pkt->value_flags |= TEGRA_NVHDCP_FLAG_KP; | ||
| 535 | |||
| 536 | /* is connection state supported? */ | ||
| 537 | if (sp_msb & STATUS_CS) { | ||
| 538 | pkt->cs = get_cs(hdmi); | ||
| 539 | pkt->value_flags |= TEGRA_NVHDCP_FLAG_CS; | ||
| 540 | } | ||
| 541 | |||
| 542 | /* load Dksv */ | ||
| 543 | pkt->d_ksv = get_dksv(hdmi); | ||
| 544 | if (verify_ksv(pkt->d_ksv)) { | ||
| 545 | nvhdcp_err("Dksv invalid!\n"); | ||
| 546 | pkt->packet_results = TEGRA_NVHDCP_RESULT_UNSUCCESSFUL; | ||
| 547 | e = -EIO; /* treat bad Dksv as I/O error */ | ||
| 548 | } | ||
| 549 | pkt->value_flags |= TEGRA_NVHDCP_FLAG_DKSV; | ||
| 550 | |||
| 551 | /* copy current Bksv */ | ||
| 552 | pkt->b_ksv = nvhdcp->b_ksv; | ||
| 553 | pkt->value_flags |= TEGRA_NVHDCP_FLAG_BKSV; | ||
| 554 | |||
| 555 | pkt->packet_results = TEGRA_NVHDCP_RESULT_SUCCESS; | ||
| 556 | mutex_unlock(&nvhdcp->lock); | ||
| 557 | return 0; | ||
| 558 | |||
| 559 | err: | ||
| 560 | mutex_unlock(&nvhdcp->lock); | ||
| 561 | return e; | ||
| 562 | } | ||
| 563 | |||
| 564 | /* get M prime - READ_M on TMDS0_LINK0 only */ | ||
| 565 | static inline int get_m_prime(struct tegra_nvhdcp *nvhdcp, struct tegra_nvhdcp_packet *pkt) | ||
| 566 | { | ||
| 567 | struct tegra_dc_hdmi_data *hdmi = nvhdcp->hdmi; | ||
| 568 | int e; | ||
| 569 | |||
| 570 | pkt->packet_results = TEGRA_NVHDCP_RESULT_UNSUCCESSFUL; | ||
| 571 | |||
| 572 | /* if connection isn't authenticated ... */ | ||
| 573 | mutex_lock(&nvhdcp->lock); | ||
| 574 | if (nvhdcp->state != STATE_LINK_VERIFY) { | ||
| 575 | memset(pkt, 0, sizeof *pkt); | ||
| 576 | pkt->packet_results = TEGRA_NVHDCP_RESULT_LINK_FAILED; | ||
| 577 | e = 0; | ||
| 578 | goto err; | ||
| 579 | } | ||
| 580 | |||
| 581 | pkt->a_ksv = nvhdcp->a_ksv; | ||
| 582 | pkt->a_n = nvhdcp->a_n; | ||
| 583 | pkt->value_flags = TEGRA_NVHDCP_FLAG_AKSV | TEGRA_NVHDCP_FLAG_AN; | ||
| 584 | |||
| 585 | set_cn(hdmi, pkt->c_n); | ||
| 586 | |||
| 587 | tegra_hdmi_writel(hdmi, TMDS0_LINK0 | READ_M, | ||
| 588 | HDMI_NV_PDISP_RG_HDCP_CMODE); | ||
| 589 | |||
| 590 | /* Cksv write triggers Mprime update */ | ||
| 591 | set_cksv(hdmi, pkt->c_ksv); | ||
| 592 | |||
| 593 | e = wait_hdcp_ctrl(hdmi, MPRIME_VALID, NULL); | ||
| 594 | if (e) { | ||
| 595 | nvhdcp_err("Mprime read timeout\n"); | ||
| 596 | e = -EIO; | ||
| 597 | goto err; | ||
| 598 | } | ||
| 599 | msleep(50); | ||
| 600 | |||
| 601 | /* load Mprime */ | ||
| 602 | pkt->m_prime = get_mprime(hdmi); | ||
| 603 | pkt->value_flags |= TEGRA_NVHDCP_FLAG_MP; | ||
| 604 | |||
| 605 | pkt->b_status = nvhdcp->b_status; | ||
| 606 | pkt->value_flags |= TEGRA_NVHDCP_FLAG_BSTATUS; | ||
| 607 | |||
| 608 | /* copy most recent KSVFIFO, if it is non-zero */ | ||
| 609 | pkt->num_bksv_list = nvhdcp->num_bksv_list; | ||
| 610 | if( nvhdcp->num_bksv_list ) { | ||
| 611 | BUILD_BUG_ON(sizeof(pkt->bksv_list) != sizeof(nvhdcp->bksv_list)); | ||
| 612 | memcpy(pkt->bksv_list, nvhdcp->bksv_list, | ||
| 613 | nvhdcp->num_bksv_list * sizeof(*pkt->bksv_list)); | ||
| 614 | pkt->value_flags |= TEGRA_NVHDCP_FLAG_BKSVLIST; | ||
| 615 | } | ||
| 616 | |||
| 617 | /* copy v_prime */ | ||
| 618 | BUILD_BUG_ON(sizeof(pkt->v_prime) != sizeof(nvhdcp->v_prime)); | ||
| 619 | memcpy(pkt->v_prime, nvhdcp->v_prime, sizeof(nvhdcp->v_prime)); | ||
| 620 | pkt->value_flags |= TEGRA_NVHDCP_FLAG_V; | ||
| 621 | |||
| 622 | /* load Dksv */ | ||
| 623 | pkt->d_ksv = get_dksv(hdmi); | ||
| 624 | if (verify_ksv(pkt->d_ksv)) { | ||
| 625 | nvhdcp_err("Dksv invalid!\n"); | ||
| 626 | e = -EIO; | ||
| 627 | goto err; | ||
| 628 | } | ||
| 629 | pkt->value_flags |= TEGRA_NVHDCP_FLAG_DKSV; | ||
| 630 | |||
| 631 | /* copy current Bksv */ | ||
| 632 | pkt->b_ksv = nvhdcp->b_ksv; | ||
| 633 | pkt->value_flags |= TEGRA_NVHDCP_FLAG_BKSV; | ||
| 634 | |||
| 635 | pkt->packet_results = TEGRA_NVHDCP_RESULT_SUCCESS; | ||
| 636 | mutex_unlock(&nvhdcp->lock); | ||
| 637 | return 0; | ||
| 638 | |||
| 639 | err: | ||
| 640 | mutex_unlock(&nvhdcp->lock); | ||
| 641 | return e; | ||
| 642 | } | ||
| 643 | |||
| 644 | static int load_kfuse(struct tegra_dc_hdmi_data *hdmi) | ||
| 645 | { | ||
| 646 | unsigned buf[KFUSE_DATA_SZ / 4]; | ||
| 647 | int e, i; | ||
| 648 | u32 ctrl; | ||
| 649 | u32 tmp; | ||
| 650 | int retries; | ||
| 651 | |||
| 652 | /* copy load kfuse into buffer - only needed for early Tegra parts */ | ||
| 653 | e = tegra_kfuse_read(buf, sizeof buf); | ||
| 654 | if (e) { | ||
| 655 | nvhdcp_err("Kfuse read failure\n"); | ||
| 656 | return e; | ||
| 657 | } | ||
| 658 | |||
| 659 | /* write the kfuse to HDMI SRAM */ | ||
| 660 | |||
| 661 | tegra_hdmi_writel(hdmi, 1, HDMI_NV_PDISP_KEY_CTRL); /* LOAD_KEYS */ | ||
| 662 | |||
| 663 | /* issue a reload */ | ||
| 664 | ctrl = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_KEY_CTRL); | ||
| 665 | tegra_hdmi_writel(hdmi, ctrl | PKEY_REQUEST_RELOAD_TRIGGER | ||
| 666 | | LOCAL_KEYS , HDMI_NV_PDISP_KEY_CTRL); | ||
| 667 | |||
| 668 | e = wait_key_ctrl(hdmi, PKEY_LOADED, PKEY_LOADED); | ||
| 669 | if (e) { | ||
| 670 | nvhdcp_err("key reload timeout\n"); | ||
| 671 | return -EIO; | ||
| 672 | } | ||
| 673 | |||
| 674 | tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_KEY_SKEY_INDEX); | ||
| 675 | |||
| 676 | /* wait for SRAM to be cleared */ | ||
| 677 | retries = 6; | ||
| 678 | do { | ||
| 679 | tmp = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_KEY_DEBUG0); | ||
| 680 | if ((tmp & 1) == 0) break; | ||
| 681 | if (retries > 1) | ||
| 682 | mdelay(1); | ||
| 683 | } while (--retries); | ||
| 684 | if (!retries) { | ||
| 685 | nvhdcp_err("key SRAM clear timeout\n"); | ||
| 686 | return -EIO; | ||
| 687 | } | ||
| 688 | |||
| 689 | for (i = 0; i < KFUSE_DATA_SZ / 4; i += 4) { | ||
| 690 | |||
| 691 | /* load 128-bits*/ | ||
| 692 | tegra_hdmi_writel(hdmi, buf[i], HDMI_NV_PDISP_KEY_HDCP_KEY_0); | ||
| 693 | tegra_hdmi_writel(hdmi, buf[i+1], HDMI_NV_PDISP_KEY_HDCP_KEY_1); | ||
| 694 | tegra_hdmi_writel(hdmi, buf[i+2], HDMI_NV_PDISP_KEY_HDCP_KEY_2); | ||
| 695 | tegra_hdmi_writel(hdmi, buf[i+3], HDMI_NV_PDISP_KEY_HDCP_KEY_3); | ||
| 696 | |||
| 697 | /* trigger LOAD_HDCP_KEY */ | ||
| 698 | tegra_hdmi_writel(hdmi, 0x100, HDMI_NV_PDISP_KEY_HDCP_KEY_TRIG); | ||
| 699 | |||
| 700 | tmp = LOCAL_KEYS | WRITE16; | ||
| 701 | if (i) | ||
| 702 | tmp |= AUTOINC; | ||
| 703 | tegra_hdmi_writel(hdmi, tmp, HDMI_NV_PDISP_KEY_CTRL); | ||
| 704 | |||
| 705 | /* wait for WRITE16 to complete */ | ||
| 706 | e = wait_key_ctrl(hdmi, 0x10, 0); /* WRITE16 */ | ||
| 707 | if (e) { | ||
| 708 | nvhdcp_err("key write timeout\n"); | ||
| 709 | return -EIO; | ||
| 710 | } | ||
| 711 | } | ||
| 712 | |||
| 713 | return 0; | ||
| 714 | } | ||
| 715 | |||
| 716 | static int verify_link(struct tegra_nvhdcp *nvhdcp, bool wait_ri) | ||
| 717 | { | ||
| 718 | struct tegra_dc_hdmi_data *hdmi = nvhdcp->hdmi; | ||
| 719 | int retries = 3; | ||
| 720 | u16 old, rx, tx; | ||
| 721 | int e; | ||
| 722 | |||
| 723 | old = 0; | ||
| 724 | rx = 0; | ||
| 725 | tx = 0; | ||
| 726 | /* retry 3 times to deal with I2C link issues */ | ||
| 727 | do { | ||
| 728 | if (wait_ri) | ||
| 729 | old = get_transmitter_ri(hdmi); | ||
| 730 | |||
| 731 | e = get_receiver_ri(nvhdcp, &rx); | ||
| 732 | if (!e) { | ||
| 733 | if (!rx) { | ||
| 734 | nvhdcp_err("Ri is 0!\n"); | ||
| 735 | return -EINVAL; | ||
| 736 | } | ||
| 737 | |||
| 738 | tx = get_transmitter_ri(hdmi); | ||
| 739 | } else { | ||
| 740 | rx = ~tx; | ||
| 741 | msleep(50); | ||
| 742 | } | ||
| 743 | |||
| 744 | } while (wait_ri && --retries && old != tx); | ||
| 745 | |||
| 746 | nvhdcp_debug("R0 Ri poll:rx=0x%04x tx=0x%04x\n", rx, tx); | ||
| 747 | |||
| 748 | if (!nvhdcp_is_plugged(nvhdcp)) { | ||
| 749 | nvhdcp_err("aborting verify links - lost hdmi connection\n"); | ||
| 750 | return -EIO; | ||
| 751 | } | ||
| 752 | |||
| 753 | if (rx != tx) | ||
| 754 | return -EINVAL; | ||
| 755 | |||
| 756 | return 0; | ||
| 757 | } | ||
| 758 | |||
| 759 | static int get_repeater_info(struct tegra_nvhdcp *nvhdcp) | ||
| 760 | { | ||
| 761 | int e, retries; | ||
| 762 | u8 b_caps; | ||
| 763 | u16 b_status; | ||
| 764 | |||
| 765 | nvhdcp_vdbg("repeater found:fetching repeater info\n"); | ||
| 766 | |||
| 767 | /* wait up to 5 seconds for READY on repeater */ | ||
| 768 | retries = 51; | ||
| 769 | do { | ||
| 770 | if (!nvhdcp_is_plugged(nvhdcp)) { | ||
| 771 | nvhdcp_err("disconnect while waiting for repeater\n"); | ||
| 772 | return -EIO; | ||
| 773 | } | ||
| 774 | |||
| 775 | e = get_bcaps(nvhdcp, &b_caps); | ||
| 776 | if (!e && (b_caps & BCAPS_READY)) { | ||
| 777 | nvhdcp_debug("Bcaps READY from repeater\n"); | ||
| 778 | break; | ||
| 779 | } | ||
| 780 | if (retries > 1) | ||
| 781 | msleep(100); | ||
| 782 | } while (--retries); | ||
| 783 | if (!retries) { | ||
| 784 | nvhdcp_err("repeater Bcaps read timeout\n"); | ||
| 785 | return -ETIMEDOUT; | ||
| 786 | } | ||
| 787 | |||
| 788 | memset(nvhdcp->v_prime, 0, sizeof nvhdcp->v_prime); | ||
| 789 | e = get_vprime(nvhdcp, nvhdcp->v_prime); | ||
| 790 | if (e) { | ||
| 791 | nvhdcp_err("repeater Vprime read failure!\n"); | ||
| 792 | return e; | ||
| 793 | } | ||
| 794 | |||
| 795 | e = nvhdcp_i2c_read16(nvhdcp, 0x41, &b_status); | ||
| 796 | if (e) { | ||
| 797 | nvhdcp_err("Bstatus read failure!\n"); | ||
| 798 | return e; | ||
| 799 | } | ||
| 800 | |||
| 801 | if (b_status & BSTATUS_MAX_DEVS_EXCEEDED) { | ||
| 802 | nvhdcp_err("repeater:max devices (0x%04x)\n", b_status); | ||
| 803 | return -EINVAL; | ||
| 804 | } | ||
| 805 | |||
| 806 | if (b_status & BSTATUS_MAX_CASCADE_EXCEEDED) { | ||
| 807 | nvhdcp_err("repeater:max cascade (0x%04x)\n", b_status); | ||
| 808 | return -EINVAL; | ||
| 809 | } | ||
| 810 | |||
| 811 | nvhdcp->b_status = b_status; | ||
| 812 | nvhdcp->num_bksv_list = b_status & 0x7f; | ||
| 813 | nvhdcp_vdbg("Bstatus 0x%x (devices: %d)\n", | ||
| 814 | b_status, nvhdcp->num_bksv_list); | ||
| 815 | |||
| 816 | memset(nvhdcp->bksv_list, 0, sizeof nvhdcp->bksv_list); | ||
| 817 | e = get_ksvfifo(nvhdcp, nvhdcp->num_bksv_list, nvhdcp->bksv_list); | ||
| 818 | if (e) { | ||
| 819 | nvhdcp_err("repeater:could not read KSVFIFO (err %d)\n", e); | ||
| 820 | return e; | ||
| 821 | } | ||
| 822 | |||
| 823 | return 0; | ||
| 824 | } | ||
| 825 | |||
| 826 | static void nvhdcp_downstream_worker(struct work_struct *work) | ||
| 827 | { | ||
| 828 | struct tegra_nvhdcp *nvhdcp = | ||
| 829 | container_of(to_delayed_work(work), struct tegra_nvhdcp, work); | ||
| 830 | struct tegra_dc_hdmi_data *hdmi = nvhdcp->hdmi; | ||
| 831 | int e; | ||
| 832 | u8 b_caps; | ||
| 833 | u32 tmp; | ||
| 834 | u32 res; | ||
| 835 | |||
| 836 | nvhdcp_vdbg("%s():started thread %s\n", __func__, nvhdcp->name); | ||
| 837 | |||
| 838 | mutex_lock(&nvhdcp->lock); | ||
| 839 | if (nvhdcp->state == STATE_OFF) { | ||
| 840 | nvhdcp_err("nvhdcp failure - giving up\n"); | ||
| 841 | goto err; | ||
| 842 | } | ||
| 843 | nvhdcp->state = STATE_UNAUTHENTICATED; | ||
| 844 | |||
| 845 | /* check plug state to terminate early in case flush_workqueue() */ | ||
| 846 | if (!nvhdcp_is_plugged(nvhdcp)) { | ||
| 847 | nvhdcp_err("worker started while unplugged!\n"); | ||
| 848 | goto lost_hdmi; | ||
| 849 | } | ||
| 850 | nvhdcp_vdbg("%s():hpd=%d\n", __func__, nvhdcp->plugged); | ||
| 851 | |||
| 852 | nvhdcp->a_ksv = 0; | ||
| 853 | nvhdcp->b_ksv = 0; | ||
| 854 | nvhdcp->a_n = 0; | ||
| 855 | |||
| 856 | e = get_bcaps(nvhdcp, &b_caps); | ||
| 857 | if (e) { | ||
| 858 | nvhdcp_err("Bcaps read failure\n"); | ||
| 859 | goto failure; | ||
| 860 | } | ||
| 861 | |||
| 862 | nvhdcp_vdbg("read Bcaps = 0x%02x\n", b_caps); | ||
| 863 | |||
| 864 | nvhdcp_vdbg("kfuse loading ...\n"); | ||
| 865 | |||
| 866 | /* repeater flag in Bskv must be configured before loading fuses */ | ||
| 867 | set_bksv(hdmi, 0, (b_caps & BCAPS_REPEATER)); | ||
| 868 | |||
| 869 | e = load_kfuse(hdmi); | ||
| 870 | if (e) { | ||
| 871 | nvhdcp_err("kfuse could not be loaded\n"); | ||
| 872 | goto failure; | ||
| 873 | } | ||
| 874 | |||
| 875 | hdcp_ctrl_run(hdmi, 1); | ||
| 876 | |||
| 877 | nvhdcp_vdbg("wait AN_VALID ...\n"); | ||
| 878 | |||
| 879 | /* wait for hardware to generate HDCP values */ | ||
| 880 | e = wait_hdcp_ctrl(hdmi, AN_VALID | SROM_ERR, &res); | ||
| 881 | if (e) { | ||
| 882 | nvhdcp_err("An key generation timeout\n"); | ||
| 883 | goto failure; | ||
| 884 | } | ||
| 885 | if (res & SROM_ERR) { | ||
| 886 | nvhdcp_err("SROM error\n"); | ||
| 887 | goto failure; | ||
| 888 | } | ||
| 889 | |||
| 890 | msleep(25); | ||
| 891 | |||
| 892 | nvhdcp->a_ksv = get_aksv(hdmi); | ||
| 893 | nvhdcp->a_n = get_an(hdmi); | ||
| 894 | nvhdcp_vdbg("Aksv is 0x%016llx\n", nvhdcp->a_ksv); | ||
| 895 | nvhdcp_vdbg("An is 0x%016llx\n", nvhdcp->a_n); | ||
| 896 | if (verify_ksv(nvhdcp->a_ksv)) { | ||
| 897 | nvhdcp_err("Aksv verify failure! (0x%016llx)\n", nvhdcp->a_ksv); | ||
| 898 | goto disable; | ||
| 899 | } | ||
| 900 | |||
| 901 | /* write Ainfo to receiver - set 1.1 only if b_caps supports it */ | ||
| 902 | e = nvhdcp_i2c_write8(nvhdcp, 0x15, b_caps & BCAPS_11); | ||
| 903 | if (e) { | ||
| 904 | nvhdcp_err("Ainfo write failure\n"); | ||
| 905 | goto failure; | ||
| 906 | } | ||
| 907 | |||
| 908 | /* write An to receiver */ | ||
| 909 | e = nvhdcp_i2c_write64(nvhdcp, 0x18, nvhdcp->a_n); | ||
| 910 | if (e) { | ||
| 911 | nvhdcp_err("An write failure\n"); | ||
| 912 | goto failure; | ||
| 913 | } | ||
| 914 | |||
| 915 | nvhdcp_vdbg("wrote An = 0x%016llx\n", nvhdcp->a_n); | ||
| 916 | |||
| 917 | /* write Aksv to receiver - triggers auth sequence */ | ||
| 918 | e = nvhdcp_i2c_write40(nvhdcp, 0x10, nvhdcp->a_ksv); | ||
| 919 | if (e) { | ||
| 920 | nvhdcp_err("Aksv write failure\n"); | ||
| 921 | goto failure; | ||
| 922 | } | ||
| 923 | |||
| 924 | nvhdcp_vdbg("wrote Aksv = 0x%010llx\n", nvhdcp->a_ksv); | ||
| 925 | |||
| 926 | /* bail out if unplugged in the middle of negotiation */ | ||
| 927 | if (!nvhdcp_is_plugged(nvhdcp)) | ||
| 928 | goto lost_hdmi; | ||
| 929 | |||
| 930 | /* get Bksv from receiver */ | ||
| 931 | e = nvhdcp_i2c_read40(nvhdcp, 0x00, &nvhdcp->b_ksv); | ||
| 932 | if (e) { | ||
| 933 | nvhdcp_err("Bksv read failure\n"); | ||
| 934 | goto failure; | ||
| 935 | } | ||
| 936 | nvhdcp_vdbg("Bksv is 0x%016llx\n", nvhdcp->b_ksv); | ||
| 937 | if (verify_ksv(nvhdcp->b_ksv)) { | ||
| 938 | nvhdcp_err("Bksv verify failure!\n"); | ||
| 939 | goto failure; | ||
| 940 | } | ||
| 941 | |||
| 942 | nvhdcp_vdbg("read Bksv = 0x%010llx from device\n", nvhdcp->b_ksv); | ||
| 943 | |||
| 944 | set_bksv(hdmi, nvhdcp->b_ksv, (b_caps & BCAPS_REPEATER)); | ||
| 945 | |||
| 946 | nvhdcp_vdbg("loaded Bksv into controller\n"); | ||
| 947 | |||
| 948 | e = wait_hdcp_ctrl(hdmi, R0_VALID, NULL); | ||
| 949 | if (e) { | ||
| 950 | nvhdcp_err("R0 read failure!\n"); | ||
| 951 | goto failure; | ||
| 952 | } | ||
| 953 | |||
| 954 | nvhdcp_vdbg("R0 valid\n"); | ||
| 955 | |||
| 956 | msleep(100); /* can't read R0' within 100ms of writing Aksv */ | ||
| 957 | |||
| 958 | nvhdcp_vdbg("verifying links ...\n"); | ||
| 959 | |||
| 960 | e = verify_link(nvhdcp, false); | ||
| 961 | if (e) { | ||
| 962 | nvhdcp_err("link verification failed err %d\n", e); | ||
| 963 | goto failure; | ||
| 964 | } | ||
| 965 | |||
| 966 | /* if repeater then get repeater info */ | ||
| 967 | if (b_caps & BCAPS_REPEATER) { | ||
| 968 | e = get_repeater_info(nvhdcp); | ||
| 969 | if (e) { | ||
| 970 | nvhdcp_err("get repeater info failed\n"); | ||
| 971 | goto failure; | ||
| 972 | } | ||
| 973 | } | ||
| 974 | |||
| 975 | tmp = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_CTRL); | ||
| 976 | tmp |= CRYPT_ENABLED; | ||
| 977 | if (b_caps & BCAPS_11) /* HDCP 1.1 ? */ | ||
| 978 | tmp |= ONEONE_ENABLED; | ||
| 979 | tegra_hdmi_writel(hdmi, tmp, HDMI_NV_PDISP_RG_HDCP_CTRL); | ||
| 980 | |||
| 981 | nvhdcp_vdbg("CRYPT enabled\n"); | ||
| 982 | |||
| 983 | nvhdcp->state = STATE_LINK_VERIFY; | ||
| 984 | nvhdcp_info("link verified!\n"); | ||
| 985 | |||
| 986 | while (1) { | ||
| 987 | if (!nvhdcp_is_plugged(nvhdcp)) | ||
| 988 | goto lost_hdmi; | ||
| 989 | |||
| 990 | if (nvhdcp->state != STATE_LINK_VERIFY) | ||
| 991 | goto failure; | ||
| 992 | |||
| 993 | e = verify_link(nvhdcp, true); | ||
| 994 | if (e) { | ||
| 995 | nvhdcp_err("link verification failed err %d\n", e); | ||
| 996 | goto failure; | ||
| 997 | } | ||
| 998 | mutex_unlock(&nvhdcp->lock); | ||
| 999 | wait_event_interruptible_timeout(wq_worker, | ||
| 1000 | !nvhdcp_is_plugged(nvhdcp), msecs_to_jiffies(1500)); | ||
| 1001 | mutex_lock(&nvhdcp->lock); | ||
| 1002 | |||
| 1003 | } | ||
| 1004 | |||
| 1005 | failure: | ||
| 1006 | nvhdcp->fail_count++; | ||
| 1007 | if(nvhdcp->fail_count > 5) { | ||
| 1008 | nvhdcp_err("nvhdcp failure - too many failures, giving up!\n"); | ||
| 1009 | } else { | ||
| 1010 | nvhdcp_err("nvhdcp failure - renegotiating in 1 second\n"); | ||
| 1011 | if (!nvhdcp_is_plugged(nvhdcp)) | ||
| 1012 | goto lost_hdmi; | ||
| 1013 | queue_delayed_work(nvhdcp->downstream_wq, &nvhdcp->work, | ||
| 1014 | msecs_to_jiffies(1000)); | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | lost_hdmi: | ||
| 1018 | nvhdcp->state = STATE_UNAUTHENTICATED; | ||
| 1019 | hdcp_ctrl_run(hdmi, 0); | ||
| 1020 | |||
| 1021 | err: | ||
| 1022 | mutex_unlock(&nvhdcp->lock); | ||
| 1023 | return; | ||
| 1024 | disable: | ||
| 1025 | nvhdcp->state = STATE_OFF; | ||
| 1026 | nvhdcp_set_plugged(nvhdcp, false); | ||
| 1027 | mutex_unlock(&nvhdcp->lock); | ||
| 1028 | return; | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | static int tegra_nvhdcp_on(struct tegra_nvhdcp *nvhdcp) | ||
| 1032 | { | ||
| 1033 | nvhdcp->state = STATE_UNAUTHENTICATED; | ||
| 1034 | if (nvhdcp_is_plugged(nvhdcp)) { | ||
| 1035 | nvhdcp->fail_count = 0; | ||
| 1036 | queue_delayed_work(nvhdcp->downstream_wq, &nvhdcp->work, | ||
| 1037 | msecs_to_jiffies(100)); | ||
| 1038 | } | ||
| 1039 | return 0; | ||
| 1040 | } | ||
| 1041 | |||
| 1042 | static int tegra_nvhdcp_off(struct tegra_nvhdcp *nvhdcp) | ||
| 1043 | { | ||
| 1044 | mutex_lock(&nvhdcp->lock); | ||
| 1045 | nvhdcp->state = STATE_OFF; | ||
| 1046 | nvhdcp_set_plugged(nvhdcp, false); | ||
| 1047 | mutex_unlock(&nvhdcp->lock); | ||
| 1048 | wake_up_interruptible(&wq_worker); | ||
| 1049 | flush_workqueue(nvhdcp->downstream_wq); | ||
| 1050 | return 0; | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | void tegra_nvhdcp_set_plug(struct tegra_nvhdcp *nvhdcp, bool hpd) | ||
| 1054 | { | ||
| 1055 | nvhdcp_debug("hdmi hotplug detected (hpd = %d)\n", hpd); | ||
| 1056 | |||
| 1057 | if (hpd) { | ||
| 1058 | nvhdcp_set_plugged(nvhdcp, true); | ||
| 1059 | tegra_nvhdcp_on(nvhdcp); | ||
| 1060 | } else { | ||
| 1061 | tegra_nvhdcp_off(nvhdcp); | ||
| 1062 | } | ||
| 1063 | } | ||
| 1064 | |||
| 1065 | int tegra_nvhdcp_set_policy(struct tegra_nvhdcp *nvhdcp, int pol) | ||
| 1066 | { | ||
| 1067 | if (pol == TEGRA_NVHDCP_POLICY_ALWAYS_ON) { | ||
| 1068 | nvhdcp_info("using \"always on\" policy.\n"); | ||
| 1069 | if (atomic_xchg(&nvhdcp->policy, pol) != pol) { | ||
| 1070 | /* policy changed, start working */ | ||
| 1071 | tegra_nvhdcp_on(nvhdcp); | ||
| 1072 | } | ||
| 1073 | } else { | ||
| 1074 | /* unsupported policy */ | ||
| 1075 | return -EINVAL; | ||
| 1076 | } | ||
| 1077 | |||
| 1078 | return 0; | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | static int tegra_nvhdcp_renegotiate(struct tegra_nvhdcp *nvhdcp) | ||
| 1082 | { | ||
| 1083 | mutex_lock(&nvhdcp->lock); | ||
| 1084 | nvhdcp->state = STATE_RENEGOTIATE; | ||
| 1085 | mutex_unlock(&nvhdcp->lock); | ||
| 1086 | tegra_nvhdcp_on(nvhdcp); | ||
| 1087 | return 0; | ||
| 1088 | } | ||
| 1089 | |||
| 1090 | void tegra_nvhdcp_suspend(struct tegra_nvhdcp *nvhdcp) | ||
| 1091 | { | ||
| 1092 | if (!nvhdcp) return; | ||
| 1093 | tegra_nvhdcp_off(nvhdcp); | ||
| 1094 | } | ||
| 1095 | |||
| 1096 | void tegra_nvhdcp_resume(struct tegra_nvhdcp *nvhdcp) | ||
| 1097 | { | ||
| 1098 | if (!nvhdcp) return; | ||
| 1099 | tegra_nvhdcp_renegotiate(nvhdcp); | ||
| 1100 | } | ||
| 1101 | |||
| 1102 | static long nvhdcp_dev_ioctl(struct file *filp, | ||
| 1103 | unsigned int cmd, unsigned long arg) | ||
| 1104 | { | ||
| 1105 | struct tegra_nvhdcp *nvhdcp = filp->private_data; | ||
| 1106 | struct tegra_nvhdcp_packet *pkt; | ||
| 1107 | int e = -ENOTTY; | ||
| 1108 | |||
| 1109 | switch (cmd) { | ||
| 1110 | case TEGRAIO_NVHDCP_ON: | ||
| 1111 | return tegra_nvhdcp_on(nvhdcp); | ||
| 1112 | |||
| 1113 | case TEGRAIO_NVHDCP_OFF: | ||
| 1114 | return tegra_nvhdcp_off(nvhdcp); | ||
| 1115 | |||
| 1116 | case TEGRAIO_NVHDCP_SET_POLICY: | ||
| 1117 | return tegra_nvhdcp_set_policy(nvhdcp, arg); | ||
| 1118 | |||
| 1119 | case TEGRAIO_NVHDCP_READ_M: | ||
| 1120 | pkt = kmalloc(sizeof(*pkt), GFP_KERNEL); | ||
| 1121 | if (!pkt) | ||
| 1122 | return -ENOMEM; | ||
| 1123 | if (copy_from_user(pkt, (void __user *)arg, sizeof(*pkt))) { | ||
| 1124 | e = -EFAULT; | ||
| 1125 | goto kfree_pkt; | ||
| 1126 | } | ||
| 1127 | e = get_m_prime(nvhdcp, pkt); | ||
| 1128 | if (copy_to_user((void __user *)arg, pkt, sizeof(*pkt))) { | ||
| 1129 | e = -EFAULT; | ||
| 1130 | goto kfree_pkt; | ||
| 1131 | } | ||
| 1132 | kfree(pkt); | ||
| 1133 | return e; | ||
| 1134 | |||
| 1135 | case TEGRAIO_NVHDCP_READ_S: | ||
| 1136 | pkt = kmalloc(sizeof(*pkt), GFP_KERNEL); | ||
| 1137 | if (!pkt) | ||
| 1138 | return -ENOMEM; | ||
| 1139 | if (copy_from_user(pkt, (void __user *)arg, sizeof(*pkt))) { | ||
| 1140 | e = -EFAULT; | ||
| 1141 | goto kfree_pkt; | ||
| 1142 | } | ||
| 1143 | e = get_s_prime(nvhdcp, pkt); | ||
| 1144 | if (copy_to_user((void __user *)arg, pkt, sizeof(*pkt))) { | ||
| 1145 | e = -EFAULT; | ||
| 1146 | goto kfree_pkt; | ||
| 1147 | } | ||
| 1148 | kfree(pkt); | ||
| 1149 | return e; | ||
| 1150 | |||
| 1151 | case TEGRAIO_NVHDCP_RENEGOTIATE: | ||
| 1152 | e = tegra_nvhdcp_renegotiate(nvhdcp); | ||
| 1153 | break; | ||
| 1154 | } | ||
| 1155 | |||
| 1156 | return e; | ||
| 1157 | kfree_pkt: | ||
| 1158 | kfree(pkt); | ||
| 1159 | return e; | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | static int nvhdcp_dev_open(struct inode *inode, struct file *filp) | ||
| 1163 | { | ||
| 1164 | struct miscdevice *miscdev = filp->private_data; | ||
| 1165 | struct tegra_nvhdcp *nvhdcp = | ||
| 1166 | container_of(miscdev, struct tegra_nvhdcp, miscdev); | ||
| 1167 | filp->private_data = nvhdcp; | ||
| 1168 | return 0; | ||
| 1169 | } | ||
| 1170 | |||
| 1171 | static int nvhdcp_dev_release(struct inode *inode, struct file *filp) | ||
| 1172 | { | ||
| 1173 | filp->private_data = NULL; | ||
| 1174 | return 0; | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | static const struct file_operations nvhdcp_fops = { | ||
| 1178 | .owner = THIS_MODULE, | ||
| 1179 | .llseek = no_llseek, | ||
| 1180 | .unlocked_ioctl = nvhdcp_dev_ioctl, | ||
| 1181 | .open = nvhdcp_dev_open, | ||
| 1182 | .release = nvhdcp_dev_release, | ||
| 1183 | }; | ||
| 1184 | |||
| 1185 | /* we only support one AP right now, so should only call this once. */ | ||
| 1186 | struct tegra_nvhdcp *tegra_nvhdcp_create(struct tegra_dc_hdmi_data *hdmi, | ||
| 1187 | int id, int bus) | ||
| 1188 | { | ||
| 1189 | static struct tegra_nvhdcp *nvhdcp; /* prevent multiple calls */ | ||
| 1190 | struct i2c_adapter *adapter; | ||
| 1191 | int e; | ||
| 1192 | |||
| 1193 | if (nvhdcp) | ||
| 1194 | return ERR_PTR(-EMFILE); | ||
| 1195 | |||
| 1196 | nvhdcp = kzalloc(sizeof(*nvhdcp), GFP_KERNEL); | ||
| 1197 | if (!nvhdcp) | ||
| 1198 | return ERR_PTR(-ENOMEM); | ||
| 1199 | |||
| 1200 | nvhdcp->id = id; | ||
| 1201 | snprintf(nvhdcp->name, sizeof(nvhdcp->name), "nvhdcp%u", id); | ||
| 1202 | nvhdcp->hdmi = hdmi; | ||
| 1203 | mutex_init(&nvhdcp->lock); | ||
| 1204 | |||
| 1205 | strlcpy(nvhdcp->info.type, nvhdcp->name, sizeof(nvhdcp->info.type)); | ||
| 1206 | nvhdcp->bus = bus; | ||
| 1207 | nvhdcp->info.addr = 0x74 >> 1; | ||
| 1208 | nvhdcp->info.platform_data = nvhdcp; | ||
| 1209 | nvhdcp->fail_count = 0; | ||
| 1210 | |||
| 1211 | adapter = i2c_get_adapter(bus); | ||
| 1212 | if (!adapter) { | ||
| 1213 | nvhdcp_err("can't get adapter for bus %d\n", bus); | ||
| 1214 | e = -EBUSY; | ||
| 1215 | goto free_nvhdcp; | ||
| 1216 | } | ||
| 1217 | |||
| 1218 | nvhdcp->client = i2c_new_device(adapter, &nvhdcp->info); | ||
| 1219 | i2c_put_adapter(adapter); | ||
| 1220 | |||
| 1221 | if (!nvhdcp->client) { | ||
| 1222 | nvhdcp_err("can't create new device\n"); | ||
| 1223 | e = -EBUSY; | ||
| 1224 | goto free_nvhdcp; | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | nvhdcp->state = STATE_UNAUTHENTICATED; | ||
| 1228 | |||
| 1229 | nvhdcp->downstream_wq = create_singlethread_workqueue(nvhdcp->name); | ||
| 1230 | INIT_DELAYED_WORK(&nvhdcp->work, nvhdcp_downstream_worker); | ||
| 1231 | |||
| 1232 | nvhdcp->miscdev.minor = MISC_DYNAMIC_MINOR; | ||
| 1233 | nvhdcp->miscdev.name = nvhdcp->name; | ||
| 1234 | nvhdcp->miscdev.fops = &nvhdcp_fops; | ||
| 1235 | |||
| 1236 | e = misc_register(&nvhdcp->miscdev); | ||
| 1237 | if (e) | ||
| 1238 | goto free_workqueue; | ||
| 1239 | |||
| 1240 | nvhdcp_vdbg("%s(): created misc device %s\n", __func__, nvhdcp->name); | ||
| 1241 | |||
| 1242 | return nvhdcp; | ||
| 1243 | free_workqueue: | ||
| 1244 | destroy_workqueue(nvhdcp->downstream_wq); | ||
| 1245 | i2c_release_client(nvhdcp->client); | ||
| 1246 | free_nvhdcp: | ||
| 1247 | kfree(nvhdcp); | ||
| 1248 | nvhdcp_err("unable to create device.\n"); | ||
| 1249 | return ERR_PTR(e); | ||
| 1250 | } | ||
| 1251 | |||
| 1252 | void tegra_nvhdcp_destroy(struct tegra_nvhdcp *nvhdcp) | ||
| 1253 | { | ||
| 1254 | misc_deregister(&nvhdcp->miscdev); | ||
| 1255 | tegra_nvhdcp_off(nvhdcp); | ||
| 1256 | destroy_workqueue(nvhdcp->downstream_wq); | ||
| 1257 | i2c_release_client(nvhdcp->client); | ||
| 1258 | kfree(nvhdcp); | ||
| 1259 | } | ||
diff --git a/drivers/video/tegra/dc/nvhdcp.h b/drivers/video/tegra/dc/nvhdcp.h new file mode 100644 index 00000000000..90ea0be36d1 --- /dev/null +++ b/drivers/video/tegra/dc/nvhdcp.h | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/nvhdcp.h | ||
| 3 | * | ||
| 4 | * Copyright (c) 2010-2011, 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 | #ifndef __DRIVERS_VIDEO_TEGRA_DC_NVHDCP_H | ||
| 18 | #define __DRIVERS_VIDEO_TEGRA_DC_NVHDCP_H | ||
| 19 | #include <video/nvhdcp.h> | ||
| 20 | |||
| 21 | struct tegra_nvhdcp; | ||
| 22 | #ifdef CONFIG_TEGRA_NVHDCP | ||
| 23 | void tegra_nvhdcp_set_plug(struct tegra_nvhdcp *nvhdcp, bool hpd); | ||
| 24 | int tegra_nvhdcp_set_policy(struct tegra_nvhdcp *nvhdcp, int pol); | ||
| 25 | void tegra_nvhdcp_suspend(struct tegra_nvhdcp *nvhdcp); | ||
| 26 | void tegra_nvhdcp_resume(struct tegra_nvhdcp *nvhdcp); | ||
| 27 | struct tegra_nvhdcp *tegra_nvhdcp_create(struct tegra_dc_hdmi_data *hdmi, | ||
| 28 | int id, int bus); | ||
| 29 | void tegra_nvhdcp_destroy(struct tegra_nvhdcp *nvhdcp); | ||
| 30 | #else | ||
| 31 | inline void tegra_nvhdcp_set_plug(struct tegra_nvhdcp *nvhdcp, bool hpd) { } | ||
| 32 | inline int tegra_nvhdcp_set_policy(struct tegra_nvhdcp *nvhdcp, int pol) | ||
| 33 | { | ||
| 34 | return 0; | ||
| 35 | } | ||
| 36 | inline void tegra_nvhdcp_suspend(struct tegra_nvhdcp *nvhdcp) { } | ||
| 37 | inline void tegra_nvhdcp_resume(struct tegra_nvhdcp *nvhdcp) { } | ||
| 38 | inline struct tegra_nvhdcp *tegra_nvhdcp_create(struct tegra_dc_hdmi_data *hdmi, | ||
| 39 | int id, int bus) | ||
| 40 | { | ||
| 41 | return NULL; | ||
| 42 | } | ||
| 43 | inline void tegra_nvhdcp_destroy(struct tegra_nvhdcp *nvhdcp) { } | ||
| 44 | #endif | ||
| 45 | |||
| 46 | #endif | ||
diff --git a/drivers/video/tegra/dc/nvsd.c b/drivers/video/tegra/dc/nvsd.c new file mode 100644 index 00000000000..a2f3ece6ae7 --- /dev/null +++ b/drivers/video/tegra/dc/nvsd.c | |||
| @@ -0,0 +1,906 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/nvsd.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/kernel.h> | ||
| 18 | #include <mach/dc.h> | ||
| 19 | #include <linux/types.h> | ||
| 20 | #include <linux/string.h> | ||
| 21 | #include <linux/slab.h> | ||
| 22 | #include <linux/backlight.h> | ||
| 23 | #include <linux/platform_device.h> | ||
| 24 | |||
| 25 | #include "dc_reg.h" | ||
| 26 | #include "dc_priv.h" | ||
| 27 | #include "nvsd.h" | ||
| 28 | |||
| 29 | /* Elements for sysfs access */ | ||
| 30 | #define NVSD_ATTR(__name) static struct kobj_attribute nvsd_attr_##__name = \ | ||
| 31 | __ATTR(__name, S_IRUGO|S_IWUSR, nvsd_settings_show, nvsd_settings_store) | ||
| 32 | #define NVSD_ATTRS_ENTRY(__name) (&nvsd_attr_##__name.attr) | ||
| 33 | #define IS_NVSD_ATTR(__name) (attr == &nvsd_attr_##__name) | ||
| 34 | |||
| 35 | static ssize_t nvsd_settings_show(struct kobject *kobj, | ||
| 36 | struct kobj_attribute *attr, char *buf); | ||
| 37 | |||
| 38 | static ssize_t nvsd_settings_store(struct kobject *kobj, | ||
| 39 | struct kobj_attribute *attr, const char *buf, size_t count); | ||
| 40 | |||
| 41 | static ssize_t nvsd_registers_show(struct kobject *kobj, | ||
| 42 | struct kobj_attribute *attr, char *buf); | ||
| 43 | |||
| 44 | NVSD_ATTR(enable); | ||
| 45 | NVSD_ATTR(aggressiveness); | ||
| 46 | NVSD_ATTR(phase_in_settings); | ||
| 47 | NVSD_ATTR(phase_in_adjustments); | ||
| 48 | NVSD_ATTR(bin_width); | ||
| 49 | NVSD_ATTR(hw_update_delay); | ||
| 50 | NVSD_ATTR(use_vid_luma); | ||
| 51 | NVSD_ATTR(coeff); | ||
| 52 | NVSD_ATTR(blp_time_constant); | ||
| 53 | NVSD_ATTR(blp_step); | ||
| 54 | NVSD_ATTR(fc_time_limit); | ||
| 55 | NVSD_ATTR(fc_threshold); | ||
| 56 | NVSD_ATTR(lut); | ||
| 57 | NVSD_ATTR(bltf); | ||
| 58 | static struct kobj_attribute nvsd_attr_registers = | ||
| 59 | __ATTR(registers, S_IRUGO, nvsd_registers_show, NULL); | ||
| 60 | |||
| 61 | static struct attribute *nvsd_attrs[] = { | ||
| 62 | NVSD_ATTRS_ENTRY(enable), | ||
| 63 | NVSD_ATTRS_ENTRY(aggressiveness), | ||
| 64 | NVSD_ATTRS_ENTRY(phase_in_settings), | ||
| 65 | NVSD_ATTRS_ENTRY(phase_in_adjustments), | ||
| 66 | NVSD_ATTRS_ENTRY(bin_width), | ||
| 67 | NVSD_ATTRS_ENTRY(hw_update_delay), | ||
| 68 | NVSD_ATTRS_ENTRY(use_vid_luma), | ||
| 69 | NVSD_ATTRS_ENTRY(coeff), | ||
| 70 | NVSD_ATTRS_ENTRY(blp_time_constant), | ||
| 71 | NVSD_ATTRS_ENTRY(blp_step), | ||
| 72 | NVSD_ATTRS_ENTRY(fc_time_limit), | ||
| 73 | NVSD_ATTRS_ENTRY(fc_threshold), | ||
| 74 | NVSD_ATTRS_ENTRY(lut), | ||
| 75 | NVSD_ATTRS_ENTRY(bltf), | ||
| 76 | NVSD_ATTRS_ENTRY(registers), | ||
| 77 | NULL, | ||
| 78 | }; | ||
| 79 | |||
| 80 | static struct attribute_group nvsd_attr_group = { | ||
| 81 | .attrs = nvsd_attrs, | ||
| 82 | }; | ||
| 83 | |||
| 84 | static struct kobject *nvsd_kobj; | ||
| 85 | |||
| 86 | /* shared brightness variable */ | ||
| 87 | static atomic_t *sd_brightness = NULL; | ||
| 88 | /* shared boolean for manual K workaround */ | ||
| 89 | static atomic_t man_k_until_blank = ATOMIC_INIT(0); | ||
| 90 | |||
| 91 | static u8 nvsd_get_bw_idx(struct tegra_dc_sd_settings *settings) | ||
| 92 | { | ||
| 93 | u8 bw; | ||
| 94 | |||
| 95 | switch (settings->bin_width) { | ||
| 96 | default: | ||
| 97 | case -1: | ||
| 98 | /* A -1 bin-width indicates 'automatic' | ||
| 99 | based upon aggressiveness. */ | ||
| 100 | settings->bin_width = -1; | ||
| 101 | switch (settings->aggressiveness) { | ||
| 102 | default: | ||
| 103 | case 0: | ||
| 104 | case 1: | ||
| 105 | bw = SD_BIN_WIDTH_ONE; | ||
| 106 | break; | ||
| 107 | case 2: | ||
| 108 | case 3: | ||
| 109 | case 4: | ||
| 110 | bw = SD_BIN_WIDTH_TWO; | ||
| 111 | break; | ||
| 112 | case 5: | ||
| 113 | bw = SD_BIN_WIDTH_FOUR; | ||
| 114 | break; | ||
| 115 | } | ||
| 116 | break; | ||
| 117 | case 1: | ||
| 118 | bw = SD_BIN_WIDTH_ONE; | ||
| 119 | break; | ||
| 120 | case 2: | ||
| 121 | bw = SD_BIN_WIDTH_TWO; | ||
| 122 | break; | ||
| 123 | case 4: | ||
| 124 | bw = SD_BIN_WIDTH_FOUR; | ||
| 125 | break; | ||
| 126 | case 8: | ||
| 127 | bw = SD_BIN_WIDTH_EIGHT; | ||
| 128 | break; | ||
| 129 | } | ||
| 130 | return bw >> 3; | ||
| 131 | |||
| 132 | } | ||
| 133 | |||
| 134 | static bool nvsd_phase_in_adjustments(struct tegra_dc *dc, | ||
| 135 | struct tegra_dc_sd_settings *settings) | ||
| 136 | { | ||
| 137 | u8 step, cur_sd_brightness; | ||
| 138 | u16 target_k, cur_k; | ||
| 139 | u32 man_k, val; | ||
| 140 | |||
| 141 | cur_sd_brightness = atomic_read(sd_brightness); | ||
| 142 | |||
| 143 | target_k = tegra_dc_readl(dc, DC_DISP_SD_HW_K_VALUES); | ||
| 144 | target_k = SD_HW_K_R(target_k); | ||
| 145 | cur_k = tegra_dc_readl(dc, DC_DISP_SD_MAN_K_VALUES); | ||
| 146 | cur_k = SD_HW_K_R(cur_k); | ||
| 147 | |||
| 148 | /* read brightness value */ | ||
| 149 | val = tegra_dc_readl(dc, DC_DISP_SD_BL_CONTROL); | ||
| 150 | val = SD_BLC_BRIGHTNESS(val); | ||
| 151 | |||
| 152 | step = settings->phase_adj_step; | ||
| 153 | if (cur_sd_brightness != val || target_k != cur_k) { | ||
| 154 | if (!step) | ||
| 155 | step = ADJ_PHASE_STEP; | ||
| 156 | |||
| 157 | /* Phase in Backlight and Pixel K | ||
| 158 | every ADJ_PHASE_STEP frames*/ | ||
| 159 | if ((step-- & ADJ_PHASE_STEP) == ADJ_PHASE_STEP) { | ||
| 160 | |||
| 161 | if (val != cur_sd_brightness) | ||
| 162 | val > cur_sd_brightness ? | ||
| 163 | (cur_sd_brightness++) : | ||
| 164 | (cur_sd_brightness--); | ||
| 165 | |||
| 166 | if (target_k != cur_k) { | ||
| 167 | if (target_k > cur_k) | ||
| 168 | cur_k += K_STEP; | ||
| 169 | else | ||
| 170 | cur_k -= K_STEP; | ||
| 171 | } | ||
| 172 | |||
| 173 | /* Set manual k value */ | ||
| 174 | man_k = SD_MAN_K_R(cur_k) | | ||
| 175 | SD_MAN_K_G(cur_k) | SD_MAN_K_B(cur_k); | ||
| 176 | tegra_dc_writel(dc, man_k, DC_DISP_SD_MAN_K_VALUES); | ||
| 177 | /* Set manual brightness value */ | ||
| 178 | atomic_set(sd_brightness, cur_sd_brightness); | ||
| 179 | } | ||
| 180 | settings->phase_adj_step = step; | ||
| 181 | return true; | ||
| 182 | } else | ||
| 183 | return false; | ||
| 184 | } | ||
| 185 | |||
| 186 | /* phase in the luts based on the current and max step */ | ||
| 187 | static void nvsd_phase_in_luts(struct tegra_dc_sd_settings *settings, | ||
| 188 | struct tegra_dc *dc) | ||
| 189 | { | ||
| 190 | u32 val; | ||
| 191 | u8 bw_idx; | ||
| 192 | int i; | ||
| 193 | u16 phase_settings_step = settings->phase_settings_step; | ||
| 194 | u16 num_phase_in_steps = settings->num_phase_in_steps; | ||
| 195 | |||
| 196 | bw_idx = nvsd_get_bw_idx(settings); | ||
| 197 | |||
| 198 | /* Phase in Final LUT */ | ||
| 199 | for (i = 0; i < DC_DISP_SD_LUT_NUM; i++) { | ||
| 200 | val = SD_LUT_R((settings->lut[bw_idx][i].r * | ||
| 201 | phase_settings_step)/num_phase_in_steps) | | ||
| 202 | SD_LUT_G((settings->lut[bw_idx][i].g * | ||
| 203 | phase_settings_step)/num_phase_in_steps) | | ||
| 204 | SD_LUT_B((settings->lut[bw_idx][i].b * | ||
| 205 | phase_settings_step)/num_phase_in_steps); | ||
| 206 | |||
| 207 | tegra_dc_writel(dc, val, DC_DISP_SD_LUT(i)); | ||
| 208 | } | ||
| 209 | /* Phase in Final BLTF */ | ||
| 210 | for (i = 0; i < DC_DISP_SD_BL_TF_NUM; i++) { | ||
| 211 | val = SD_BL_TF_POINT_0(255-((255-settings->bltf[bw_idx][i][0]) | ||
| 212 | * phase_settings_step)/num_phase_in_steps) | | ||
| 213 | SD_BL_TF_POINT_1(255-((255-settings->bltf[bw_idx][i][1]) | ||
| 214 | * phase_settings_step)/num_phase_in_steps) | | ||
| 215 | SD_BL_TF_POINT_2(255-((255-settings->bltf[bw_idx][i][2]) | ||
| 216 | * phase_settings_step)/num_phase_in_steps) | | ||
| 217 | SD_BL_TF_POINT_3(255-((255-settings->bltf[bw_idx][i][3]) | ||
| 218 | * phase_settings_step)/num_phase_in_steps); | ||
| 219 | |||
| 220 | tegra_dc_writel(dc, val, DC_DISP_SD_BL_TF(i)); | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | /* handle the commands that may be invoked for phase_in_settings */ | ||
| 225 | static void nvsd_cmd_handler(struct tegra_dc_sd_settings *settings, | ||
| 226 | struct tegra_dc *dc) | ||
| 227 | { | ||
| 228 | u32 val; | ||
| 229 | u8 bw_idx, bw; | ||
| 230 | |||
| 231 | if (settings->cmd & ENABLE) { | ||
| 232 | settings->phase_settings_step++; | ||
| 233 | if (settings->phase_settings_step >= | ||
| 234 | settings->num_phase_in_steps) | ||
| 235 | settings->cmd &= ~ENABLE; | ||
| 236 | |||
| 237 | nvsd_phase_in_luts(settings, dc); | ||
| 238 | } | ||
| 239 | if (settings->cmd & DISABLE) { | ||
| 240 | settings->phase_settings_step--; | ||
| 241 | nvsd_phase_in_luts(settings, dc); | ||
| 242 | if (settings->phase_settings_step == 0) { | ||
| 243 | /* finish up aggressiveness phase in */ | ||
| 244 | if (settings->cmd & AGG_CHG) | ||
| 245 | settings->aggressiveness = settings->final_agg; | ||
| 246 | settings->cmd = NO_CMD; | ||
| 247 | settings->enable = 0; | ||
| 248 | nvsd_init(dc, settings); | ||
| 249 | } | ||
| 250 | } | ||
| 251 | if (settings->cmd & AGG_CHG) { | ||
| 252 | if (settings->aggressiveness == settings->final_agg) | ||
| 253 | settings->cmd &= ~AGG_CHG; | ||
| 254 | if ((settings->cur_agg_step++ & (STEPS_PER_AGG_CHG - 1)) == 0) { | ||
| 255 | settings->final_agg > settings->aggressiveness ? | ||
| 256 | settings->aggressiveness++ : | ||
| 257 | settings->aggressiveness--; | ||
| 258 | |||
| 259 | /* Update aggressiveness value in HW */ | ||
| 260 | val = tegra_dc_readl(dc, DC_DISP_SD_CONTROL); | ||
| 261 | val &= ~SD_AGGRESSIVENESS(0x7); | ||
| 262 | val |= SD_AGGRESSIVENESS(settings->aggressiveness); | ||
| 263 | |||
| 264 | /* Adjust bin_width for automatic setting */ | ||
| 265 | if (settings->bin_width == -1) { | ||
| 266 | bw_idx = nvsd_get_bw_idx(settings); | ||
| 267 | |||
| 268 | bw = bw_idx << 3; | ||
| 269 | |||
| 270 | val &= ~SD_BIN_WIDTH_MASK; | ||
| 271 | val |= bw; | ||
| 272 | } | ||
| 273 | tegra_dc_writel(dc, val, DC_DISP_SD_CONTROL); | ||
| 274 | |||
| 275 | nvsd_phase_in_luts(settings, dc); | ||
| 276 | } | ||
| 277 | } | ||
| 278 | } | ||
| 279 | |||
| 280 | static bool nvsd_update_enable(struct tegra_dc_sd_settings *settings, | ||
| 281 | int enable_val) | ||
| 282 | { | ||
| 283 | |||
| 284 | if (enable_val != 1 && enable_val != 0) | ||
| 285 | return false; | ||
| 286 | |||
| 287 | if (!settings->cmd && settings->enable != enable_val) { | ||
| 288 | settings->num_phase_in_steps = | ||
| 289 | STEPS_PER_AGG_LVL*settings->aggressiveness; | ||
| 290 | settings->phase_settings_step = enable_val ? | ||
| 291 | 0 : settings->num_phase_in_steps; | ||
| 292 | } | ||
| 293 | |||
| 294 | if (settings->enable != enable_val || settings->cmd & DISABLE) { | ||
| 295 | settings->cmd &= ~(ENABLE | DISABLE); | ||
| 296 | if (!settings->enable && enable_val) | ||
| 297 | settings->cmd |= PHASE_IN; | ||
| 298 | settings->cmd |= enable_val ? ENABLE : DISABLE; | ||
| 299 | return true; | ||
| 300 | } | ||
| 301 | |||
| 302 | return false; | ||
| 303 | } | ||
| 304 | |||
| 305 | static bool nvsd_update_agg(struct tegra_dc_sd_settings *settings, int agg_val) | ||
| 306 | { | ||
| 307 | int i; | ||
| 308 | int pri_lvl = SD_AGG_PRI_LVL(agg_val); | ||
| 309 | int agg_lvl = SD_GET_AGG(agg_val); | ||
| 310 | struct tegra_dc_sd_agg_priorities *sd_agg_priorities = | ||
| 311 | &settings->agg_priorities; | ||
| 312 | |||
| 313 | if (agg_lvl > 5 || agg_lvl < 0) | ||
| 314 | return false; | ||
| 315 | else if (agg_lvl == 0 && pri_lvl == 0) | ||
| 316 | return false; | ||
| 317 | |||
| 318 | if (pri_lvl >= 0 && pri_lvl < 4) | ||
| 319 | sd_agg_priorities->agg[pri_lvl] = agg_lvl; | ||
| 320 | |||
| 321 | for (i = NUM_AGG_PRI_LVLS - 1; i >= 0; i--) { | ||
| 322 | if (sd_agg_priorities->agg[i]) | ||
| 323 | break; | ||
| 324 | } | ||
| 325 | |||
| 326 | sd_agg_priorities->pri_lvl = i; | ||
| 327 | pri_lvl = i; | ||
| 328 | agg_lvl = sd_agg_priorities->agg[i]; | ||
| 329 | |||
| 330 | if (settings->phase_in_settings && settings->enable && | ||
| 331 | settings->aggressiveness != agg_lvl) { | ||
| 332 | |||
| 333 | settings->final_agg = agg_lvl; | ||
| 334 | settings->cmd |= AGG_CHG; | ||
| 335 | settings->cur_agg_step = 0; | ||
| 336 | return true; | ||
| 337 | } else if (settings->aggressiveness != agg_lvl) { | ||
| 338 | settings->aggressiveness = agg_lvl; | ||
| 339 | return true; | ||
| 340 | } | ||
| 341 | |||
| 342 | return false; | ||
| 343 | } | ||
| 344 | |||
| 345 | /* Functional initialization */ | ||
| 346 | void nvsd_init(struct tegra_dc *dc, struct tegra_dc_sd_settings *settings) | ||
| 347 | { | ||
| 348 | u32 i = 0; | ||
| 349 | u32 val = 0; | ||
| 350 | u32 bw_idx = 0; | ||
| 351 | /* TODO: check if HW says SD's available */ | ||
| 352 | |||
| 353 | /* If SD's not present or disabled, clear the register and return. */ | ||
| 354 | if (!settings || settings->enable == 0) { | ||
| 355 | /* clear the brightness val, too. */ | ||
| 356 | if (sd_brightness) | ||
| 357 | atomic_set(sd_brightness, 255); | ||
| 358 | |||
| 359 | sd_brightness = NULL; | ||
| 360 | |||
| 361 | if (settings) | ||
| 362 | settings->phase_settings_step = 0; | ||
| 363 | tegra_dc_writel(dc, 0, DC_DISP_SD_CONTROL); | ||
| 364 | return; | ||
| 365 | } | ||
| 366 | |||
| 367 | dev_dbg(&dc->ndev->dev, "NVSD Init:\n"); | ||
| 368 | |||
| 369 | /* init agg_priorities */ | ||
| 370 | if (!settings->agg_priorities.agg[0]) | ||
| 371 | settings->agg_priorities.agg[0] = settings->aggressiveness; | ||
| 372 | |||
| 373 | /* WAR: Settings will not be valid until the next flip. | ||
| 374 | * Thus, set manual K to either HW's current value (if | ||
| 375 | * we're already enabled) or a non-effective value (if | ||
| 376 | * we're about to enable). */ | ||
| 377 | val = tegra_dc_readl(dc, DC_DISP_SD_CONTROL); | ||
| 378 | |||
| 379 | if (val & SD_ENABLE_NORMAL) | ||
| 380 | i = tegra_dc_readl(dc, DC_DISP_SD_HW_K_VALUES); | ||
| 381 | else | ||
| 382 | i = 0; /* 0 values for RGB = 1.0, i.e. non-affected */ | ||
| 383 | |||
| 384 | tegra_dc_writel(dc, i, DC_DISP_SD_MAN_K_VALUES); | ||
| 385 | /* Enable manual correction mode here so that changing the | ||
| 386 | * settings won't immediately impact display dehavior. */ | ||
| 387 | val |= SD_CORRECTION_MODE_MAN; | ||
| 388 | tegra_dc_writel(dc, val, DC_DISP_SD_CONTROL); | ||
| 389 | |||
| 390 | bw_idx = nvsd_get_bw_idx(settings); | ||
| 391 | |||
| 392 | /* Write LUT */ | ||
| 393 | if (!settings->cmd) { | ||
| 394 | dev_dbg(&dc->ndev->dev, " LUT:\n"); | ||
| 395 | |||
| 396 | for (i = 0; i < DC_DISP_SD_LUT_NUM; i++) { | ||
| 397 | val = SD_LUT_R(settings->lut[bw_idx][i].r) | | ||
| 398 | SD_LUT_G(settings->lut[bw_idx][i].g) | | ||
| 399 | SD_LUT_B(settings->lut[bw_idx][i].b); | ||
| 400 | tegra_dc_writel(dc, val, DC_DISP_SD_LUT(i)); | ||
| 401 | |||
| 402 | dev_dbg(&dc->ndev->dev, " %d: 0x%08x\n", i, val); | ||
| 403 | } | ||
| 404 | } | ||
| 405 | |||
| 406 | /* Write BL TF */ | ||
| 407 | if (!settings->cmd) { | ||
| 408 | dev_dbg(&dc->ndev->dev, " BL_TF:\n"); | ||
| 409 | |||
| 410 | for (i = 0; i < DC_DISP_SD_BL_TF_NUM; i++) { | ||
| 411 | val = SD_BL_TF_POINT_0(settings->bltf[bw_idx][i][0]) | | ||
| 412 | SD_BL_TF_POINT_1(settings->bltf[bw_idx][i][1]) | | ||
| 413 | SD_BL_TF_POINT_2(settings->bltf[bw_idx][i][2]) | | ||
| 414 | SD_BL_TF_POINT_3(settings->bltf[bw_idx][i][3]); | ||
| 415 | |||
| 416 | tegra_dc_writel(dc, val, DC_DISP_SD_BL_TF(i)); | ||
| 417 | |||
| 418 | dev_dbg(&dc->ndev->dev, " %d: 0x%08x\n", i, val); | ||
| 419 | } | ||
| 420 | } else if ((settings->cmd & PHASE_IN)) { | ||
| 421 | settings->cmd &= ~PHASE_IN; | ||
| 422 | /* Write NO_OP values for BLTF */ | ||
| 423 | for (i = 0; i < DC_DISP_SD_BL_TF_NUM; i++) { | ||
| 424 | val = SD_BL_TF_POINT_0(0xFF) | | ||
| 425 | SD_BL_TF_POINT_1(0xFF) | | ||
| 426 | SD_BL_TF_POINT_2(0xFF) | | ||
| 427 | SD_BL_TF_POINT_3(0xFF); | ||
| 428 | |||
| 429 | tegra_dc_writel(dc, val, DC_DISP_SD_BL_TF(i)); | ||
| 430 | |||
| 431 | dev_dbg(&dc->ndev->dev, " %d: 0x%08x\n", i, val); | ||
| 432 | } | ||
| 433 | } | ||
| 434 | |||
| 435 | /* Set step correctly on init */ | ||
| 436 | if (!settings->cmd && settings->phase_in_settings) { | ||
| 437 | settings->num_phase_in_steps = STEPS_PER_AGG_LVL * | ||
| 438 | settings->aggressiveness; | ||
| 439 | settings->phase_settings_step = settings->enable ? | ||
| 440 | settings->num_phase_in_steps : 0; | ||
| 441 | } | ||
| 442 | |||
| 443 | /* Write Coeff */ | ||
| 444 | val = SD_CSC_COEFF_R(settings->coeff.r) | | ||
| 445 | SD_CSC_COEFF_G(settings->coeff.g) | | ||
| 446 | SD_CSC_COEFF_B(settings->coeff.b); | ||
| 447 | tegra_dc_writel(dc, val, DC_DISP_SD_CSC_COEFF); | ||
| 448 | dev_dbg(&dc->ndev->dev, " COEFF: 0x%08x\n", val); | ||
| 449 | |||
| 450 | /* Write BL Params */ | ||
| 451 | val = SD_BLP_TIME_CONSTANT(settings->blp.time_constant) | | ||
| 452 | SD_BLP_STEP(settings->blp.step); | ||
| 453 | tegra_dc_writel(dc, val, DC_DISP_SD_BL_PARAMETERS); | ||
| 454 | dev_dbg(&dc->ndev->dev, " BLP: 0x%08x\n", val); | ||
| 455 | |||
| 456 | /* Write Auto/Manual PWM */ | ||
| 457 | val = (settings->use_auto_pwm) ? SD_BLC_MODE_AUTO : SD_BLC_MODE_MAN; | ||
| 458 | tegra_dc_writel(dc, val, DC_DISP_SD_BL_CONTROL); | ||
| 459 | dev_dbg(&dc->ndev->dev, " BL_CONTROL: 0x%08x\n", val); | ||
| 460 | |||
| 461 | /* Write Flicker Control */ | ||
| 462 | val = SD_FC_TIME_LIMIT(settings->fc.time_limit) | | ||
| 463 | SD_FC_THRESHOLD(settings->fc.threshold); | ||
| 464 | tegra_dc_writel(dc, val, DC_DISP_SD_FLICKER_CONTROL); | ||
| 465 | dev_dbg(&dc->ndev->dev, " FLICKER_CONTROL: 0x%08x\n", val); | ||
| 466 | |||
| 467 | /* Manage SD Control */ | ||
| 468 | val = 0; | ||
| 469 | /* Stay in manual correction mode until the next flip. */ | ||
| 470 | val |= SD_CORRECTION_MODE_MAN; | ||
| 471 | /* Enable / One-Shot */ | ||
| 472 | val |= (settings->enable == 2) ? | ||
| 473 | (SD_ENABLE_ONESHOT | SD_ONESHOT_ENABLE) : | ||
| 474 | SD_ENABLE_NORMAL; | ||
| 475 | /* HW Update Delay */ | ||
| 476 | val |= SD_HW_UPDATE_DLY(settings->hw_update_delay); | ||
| 477 | /* Video Luma */ | ||
| 478 | val |= (settings->use_vid_luma) ? SD_USE_VID_LUMA : 0; | ||
| 479 | /* Aggressiveness */ | ||
| 480 | val |= SD_AGGRESSIVENESS(settings->aggressiveness); | ||
| 481 | /* Bin Width (value derived from bw_idx) */ | ||
| 482 | val |= bw_idx << 3; | ||
| 483 | /* Finally, Write SD Control */ | ||
| 484 | tegra_dc_writel(dc, val, DC_DISP_SD_CONTROL); | ||
| 485 | dev_dbg(&dc->ndev->dev, " SD_CONTROL: 0x%08x\n", val); | ||
| 486 | |||
| 487 | /* set the brightness pointer */ | ||
| 488 | sd_brightness = settings->sd_brightness; | ||
| 489 | |||
| 490 | /* note that we're in manual K until the next flip */ | ||
| 491 | atomic_set(&man_k_until_blank, 1); | ||
| 492 | } | ||
| 493 | |||
| 494 | /* Periodic update */ | ||
| 495 | bool nvsd_update_brightness(struct tegra_dc *dc) | ||
| 496 | { | ||
| 497 | u32 val = 0; | ||
| 498 | int cur_sd_brightness; | ||
| 499 | struct tegra_dc_sd_settings *settings = dc->out->sd_settings; | ||
| 500 | |||
| 501 | if (sd_brightness) { | ||
| 502 | if (atomic_read(&man_k_until_blank) && | ||
| 503 | !settings->phase_in_adjustments) { | ||
| 504 | val = tegra_dc_readl(dc, DC_DISP_SD_CONTROL); | ||
| 505 | val &= ~SD_CORRECTION_MODE_MAN; | ||
| 506 | tegra_dc_writel(dc, val, DC_DISP_SD_CONTROL); | ||
| 507 | atomic_set(&man_k_until_blank, 0); | ||
| 508 | } | ||
| 509 | |||
| 510 | if (settings->cmd) | ||
| 511 | nvsd_cmd_handler(settings, dc); | ||
| 512 | |||
| 513 | /* nvsd_cmd_handler may turn off didim */ | ||
| 514 | if (!settings->enable) | ||
| 515 | return true; | ||
| 516 | |||
| 517 | cur_sd_brightness = atomic_read(sd_brightness); | ||
| 518 | |||
| 519 | /* read brightness value */ | ||
| 520 | val = tegra_dc_readl(dc, DC_DISP_SD_BL_CONTROL); | ||
| 521 | val = SD_BLC_BRIGHTNESS(val); | ||
| 522 | |||
| 523 | if (settings->phase_in_adjustments) { | ||
| 524 | return nvsd_phase_in_adjustments(dc, settings); | ||
| 525 | } else if (val != (u32)cur_sd_brightness) { | ||
| 526 | /* set brightness value and note the update */ | ||
| 527 | atomic_set(sd_brightness, (int)val); | ||
| 528 | return true; | ||
| 529 | } | ||
| 530 | } | ||
| 531 | |||
| 532 | /* No update needed. */ | ||
| 533 | return false; | ||
| 534 | } | ||
| 535 | |||
| 536 | static ssize_t nvsd_lut_show(struct tegra_dc_sd_settings *sd_settings, | ||
| 537 | char *buf, ssize_t res) | ||
| 538 | { | ||
| 539 | u32 i; | ||
| 540 | u32 j; | ||
| 541 | |||
| 542 | for (i = 0; i < NUM_BIN_WIDTHS; i++) { | ||
| 543 | res += snprintf(buf + res, PAGE_SIZE - res, | ||
| 544 | "Bin Width: %d\n", 1 << i); | ||
| 545 | |||
| 546 | for (j = 0; j < DC_DISP_SD_LUT_NUM; j++) { | ||
| 547 | res += snprintf(buf + res, | ||
| 548 | PAGE_SIZE - res, | ||
| 549 | "%d: R: %3d / G: %3d / B: %3d\n", | ||
| 550 | j, | ||
| 551 | sd_settings->lut[i][j].r, | ||
| 552 | sd_settings->lut[i][j].g, | ||
| 553 | sd_settings->lut[i][j].b); | ||
| 554 | } | ||
| 555 | } | ||
| 556 | return res; | ||
| 557 | } | ||
| 558 | |||
| 559 | static ssize_t nvsd_bltf_show(struct tegra_dc_sd_settings *sd_settings, | ||
| 560 | char *buf, ssize_t res) | ||
| 561 | { | ||
| 562 | u32 i; | ||
| 563 | u32 j; | ||
| 564 | |||
| 565 | for (i = 0; i < NUM_BIN_WIDTHS; i++) { | ||
| 566 | res += snprintf(buf + res, PAGE_SIZE - res, | ||
| 567 | "Bin Width: %d\n", 1 << i); | ||
| 568 | |||
| 569 | for (j = 0; j < DC_DISP_SD_BL_TF_NUM; j++) { | ||
| 570 | res += snprintf(buf + res, | ||
| 571 | PAGE_SIZE - res, | ||
| 572 | "%d: 0: %3d / 1: %3d / 2: %3d / 3: %3d\n", | ||
| 573 | j, | ||
| 574 | sd_settings->bltf[i][j][0], | ||
| 575 | sd_settings->bltf[i][j][1], | ||
| 576 | sd_settings->bltf[i][j][2], | ||
| 577 | sd_settings->bltf[i][j][3]); | ||
| 578 | } | ||
| 579 | } | ||
| 580 | return res; | ||
| 581 | } | ||
| 582 | |||
| 583 | /* Sysfs accessors */ | ||
| 584 | static ssize_t nvsd_settings_show(struct kobject *kobj, | ||
| 585 | struct kobj_attribute *attr, char *buf) | ||
| 586 | { | ||
| 587 | struct device *dev = container_of((kobj->parent), struct device, kobj); | ||
| 588 | struct nvhost_device *ndev = to_nvhost_device(dev); | ||
| 589 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
| 590 | struct tegra_dc_sd_settings *sd_settings = dc->out->sd_settings; | ||
| 591 | ssize_t res = 0; | ||
| 592 | |||
| 593 | if (sd_settings) { | ||
| 594 | if (IS_NVSD_ATTR(enable)) | ||
| 595 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
| 596 | sd_settings->enable); | ||
| 597 | else if (IS_NVSD_ATTR(aggressiveness)) | ||
| 598 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
| 599 | sd_settings->aggressiveness); | ||
| 600 | else if (IS_NVSD_ATTR(phase_in_settings)) | ||
| 601 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
| 602 | sd_settings->phase_in_settings); | ||
| 603 | else if (IS_NVSD_ATTR(phase_in_adjustments)) | ||
| 604 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
| 605 | sd_settings->phase_in_adjustments); | ||
| 606 | else if (IS_NVSD_ATTR(bin_width)) | ||
| 607 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
| 608 | sd_settings->bin_width); | ||
| 609 | else if (IS_NVSD_ATTR(hw_update_delay)) | ||
| 610 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
| 611 | sd_settings->hw_update_delay); | ||
| 612 | else if (IS_NVSD_ATTR(use_vid_luma)) | ||
| 613 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
| 614 | sd_settings->use_vid_luma); | ||
| 615 | else if (IS_NVSD_ATTR(coeff)) | ||
| 616 | res = snprintf(buf, PAGE_SIZE, | ||
| 617 | "R: %d / G: %d / B: %d\n", | ||
| 618 | sd_settings->coeff.r, | ||
| 619 | sd_settings->coeff.g, | ||
| 620 | sd_settings->coeff.b); | ||
| 621 | else if (IS_NVSD_ATTR(blp_time_constant)) | ||
| 622 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
| 623 | sd_settings->blp.time_constant); | ||
| 624 | else if (IS_NVSD_ATTR(blp_step)) | ||
| 625 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
| 626 | sd_settings->blp.step); | ||
| 627 | else if (IS_NVSD_ATTR(fc_time_limit)) | ||
| 628 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
| 629 | sd_settings->fc.time_limit); | ||
| 630 | else if (IS_NVSD_ATTR(fc_threshold)) | ||
| 631 | res = snprintf(buf, PAGE_SIZE, "%d\n", | ||
| 632 | sd_settings->fc.threshold); | ||
| 633 | else if (IS_NVSD_ATTR(lut)) | ||
| 634 | res = nvsd_lut_show(sd_settings, buf, res); | ||
| 635 | else if (IS_NVSD_ATTR(bltf)) | ||
| 636 | res = nvsd_bltf_show(sd_settings, buf, res); | ||
| 637 | else | ||
| 638 | res = -EINVAL; | ||
| 639 | } else { | ||
| 640 | /* This shouldn't be reachable. But just in case... */ | ||
| 641 | res = -EINVAL; | ||
| 642 | } | ||
| 643 | |||
| 644 | return res; | ||
| 645 | } | ||
| 646 | |||
| 647 | #define nvsd_check_and_update(_min, _max, _varname) { \ | ||
| 648 | int val = simple_strtol(buf, NULL, 10); \ | ||
| 649 | if (val >= _min && val <= _max) { \ | ||
| 650 | sd_settings->_varname = val; \ | ||
| 651 | settings_updated = true; \ | ||
| 652 | } } | ||
| 653 | |||
| 654 | #define nvsd_get_multi(_ele, _num, _act, _min, _max) { \ | ||
| 655 | char *b, *c, *orig_b; \ | ||
| 656 | b = orig_b = kstrdup(buf, GFP_KERNEL); \ | ||
| 657 | for (_act = 0; _act < _num; _act++) { \ | ||
| 658 | if (!b) \ | ||
| 659 | break; \ | ||
| 660 | b = strim(b); \ | ||
| 661 | c = strsep(&b, " "); \ | ||
| 662 | if (!strlen(c)) \ | ||
| 663 | break; \ | ||
| 664 | _ele[_act] = simple_strtol(c, NULL, 10); \ | ||
| 665 | if (_ele[_act] < _min || _ele[_act] > _max) \ | ||
| 666 | break; \ | ||
| 667 | } \ | ||
| 668 | if (orig_b) \ | ||
| 669 | kfree(orig_b); \ | ||
| 670 | } | ||
| 671 | |||
| 672 | static int nvsd_lut_store(struct tegra_dc_sd_settings *sd_settings, | ||
| 673 | const char *buf) | ||
| 674 | { | ||
| 675 | int ele[3 * DC_DISP_SD_LUT_NUM * NUM_BIN_WIDTHS]; | ||
| 676 | int i = 0; | ||
| 677 | int j = 0; | ||
| 678 | int num = 3 * DC_DISP_SD_LUT_NUM * NUM_BIN_WIDTHS; | ||
| 679 | |||
| 680 | nvsd_get_multi(ele, num, i, 0, 255); | ||
| 681 | |||
| 682 | if (i != num) | ||
| 683 | return -EINVAL; | ||
| 684 | |||
| 685 | for (i = 0; i < NUM_BIN_WIDTHS; i++) { | ||
| 686 | for (j = 0; j < DC_DISP_SD_LUT_NUM; j++) { | ||
| 687 | sd_settings->lut[i][j].r = | ||
| 688 | ele[i * NUM_BIN_WIDTHS + j * 3 + 0]; | ||
| 689 | sd_settings->lut[i][j].g = | ||
| 690 | ele[i * NUM_BIN_WIDTHS + j * 3 + 1]; | ||
| 691 | sd_settings->lut[i][j].b = | ||
| 692 | ele[i * NUM_BIN_WIDTHS + j * 3 + 2]; | ||
| 693 | } | ||
| 694 | } | ||
| 695 | return 0; | ||
| 696 | } | ||
| 697 | |||
| 698 | static int nvsd_bltf_store(struct tegra_dc_sd_settings *sd_settings, | ||
| 699 | const char *buf) | ||
| 700 | { | ||
| 701 | int ele[4 * DC_DISP_SD_BL_TF_NUM * NUM_BIN_WIDTHS]; | ||
| 702 | int i = 0, j = 0, num = ARRAY_SIZE(ele); | ||
| 703 | |||
| 704 | nvsd_get_multi(ele, num, i, 0, 255); | ||
| 705 | |||
| 706 | if (i != num) | ||
| 707 | return -EINVAL; | ||
| 708 | |||
| 709 | for (i = 0; i < NUM_BIN_WIDTHS; i++) { | ||
| 710 | for (j = 0; j < DC_DISP_SD_BL_TF_NUM; j++) { | ||
| 711 | size_t base = (i * NUM_BIN_WIDTHS * | ||
| 712 | DC_DISP_SD_BL_TF_NUM) + (j * 4); | ||
| 713 | sd_settings->bltf[i][j][0] = ele[base + 0]; | ||
| 714 | sd_settings->bltf[i][j][1] = ele[base + 1]; | ||
| 715 | sd_settings->bltf[i][j][2] = ele[base + 2]; | ||
| 716 | sd_settings->bltf[i][j][3] = ele[base + 3]; | ||
| 717 | } | ||
| 718 | } | ||
| 719 | |||
| 720 | return 0; | ||
| 721 | } | ||
| 722 | |||
| 723 | static ssize_t nvsd_settings_store(struct kobject *kobj, | ||
| 724 | struct kobj_attribute *attr, const char *buf, size_t count) | ||
| 725 | { | ||
| 726 | struct device *dev = container_of((kobj->parent), struct device, kobj); | ||
| 727 | struct nvhost_device *ndev = to_nvhost_device(dev); | ||
| 728 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
| 729 | struct tegra_dc_sd_settings *sd_settings = dc->out->sd_settings; | ||
| 730 | ssize_t res = count; | ||
| 731 | bool settings_updated = false; | ||
| 732 | long int result; | ||
| 733 | int err; | ||
| 734 | |||
| 735 | if (sd_settings) { | ||
| 736 | if (IS_NVSD_ATTR(enable)) { | ||
| 737 | if (sd_settings->phase_in_settings) { | ||
| 738 | err = strict_strtol(buf, 10, &result); | ||
| 739 | if (err) | ||
| 740 | return err; | ||
| 741 | |||
| 742 | if (nvsd_update_enable(sd_settings, result)) | ||
| 743 | nvsd_check_and_update(1, 1, enable); | ||
| 744 | |||
| 745 | } else { | ||
| 746 | nvsd_check_and_update(0, 1, enable); | ||
| 747 | } | ||
| 748 | } else if (IS_NVSD_ATTR(aggressiveness)) { | ||
| 749 | err = strict_strtol(buf, 10, &result); | ||
| 750 | if (err) | ||
| 751 | return err; | ||
| 752 | |||
| 753 | if (nvsd_update_agg(sd_settings, result) | ||
| 754 | && !sd_settings->phase_in_settings) | ||
| 755 | settings_updated = true; | ||
| 756 | |||
| 757 | } else if (IS_NVSD_ATTR(phase_in_settings)) { | ||
| 758 | nvsd_check_and_update(0, 1, phase_in_settings); | ||
| 759 | } else if (IS_NVSD_ATTR(phase_in_adjustments)) { | ||
| 760 | nvsd_check_and_update(0, 1, phase_in_adjustments); | ||
| 761 | } else if (IS_NVSD_ATTR(bin_width)) { | ||
| 762 | nvsd_check_and_update(0, 8, bin_width); | ||
| 763 | } else if (IS_NVSD_ATTR(hw_update_delay)) { | ||
| 764 | nvsd_check_and_update(0, 2, hw_update_delay); | ||
| 765 | } else if (IS_NVSD_ATTR(use_vid_luma)) { | ||
| 766 | nvsd_check_and_update(0, 1, use_vid_luma); | ||
| 767 | } else if (IS_NVSD_ATTR(coeff)) { | ||
| 768 | int ele[3], i = 0, num = 3; | ||
| 769 | nvsd_get_multi(ele, num, i, 0, 15); | ||
| 770 | |||
| 771 | if (i == num) { | ||
| 772 | sd_settings->coeff.r = ele[0]; | ||
| 773 | sd_settings->coeff.g = ele[1]; | ||
| 774 | sd_settings->coeff.b = ele[2]; | ||
| 775 | settings_updated = true; | ||
| 776 | } else { | ||
| 777 | res = -EINVAL; | ||
| 778 | } | ||
| 779 | } else if (IS_NVSD_ATTR(blp_time_constant)) { | ||
| 780 | nvsd_check_and_update(0, 1024, blp.time_constant); | ||
| 781 | } else if (IS_NVSD_ATTR(blp_step)) { | ||
| 782 | nvsd_check_and_update(0, 255, blp.step); | ||
| 783 | } else if (IS_NVSD_ATTR(fc_time_limit)) { | ||
| 784 | nvsd_check_and_update(0, 255, fc.time_limit); | ||
| 785 | } else if (IS_NVSD_ATTR(fc_threshold)) { | ||
| 786 | nvsd_check_and_update(0, 255, fc.threshold); | ||
| 787 | } else if (IS_NVSD_ATTR(lut)) { | ||
| 788 | if (nvsd_lut_store(sd_settings, buf)) | ||
| 789 | res = -EINVAL; | ||
| 790 | else | ||
| 791 | settings_updated = true; | ||
| 792 | } else if (IS_NVSD_ATTR(bltf)) { | ||
| 793 | if (nvsd_bltf_store(sd_settings, buf)) | ||
| 794 | res = -EINVAL; | ||
| 795 | else | ||
| 796 | settings_updated = true; | ||
| 797 | } else { | ||
| 798 | res = -EINVAL; | ||
| 799 | } | ||
| 800 | |||
| 801 | /* Re-init if our settings were updated. */ | ||
| 802 | if (settings_updated) { | ||
| 803 | mutex_lock(&dc->lock); | ||
| 804 | if (!dc->enabled) { | ||
| 805 | mutex_unlock(&dc->lock); | ||
| 806 | return -ENODEV; | ||
| 807 | } | ||
| 808 | mutex_unlock(&dc->lock); | ||
| 809 | |||
| 810 | nvsd_init(dc, sd_settings); | ||
| 811 | |||
| 812 | /* Update backlight state IFF we're disabling! */ | ||
| 813 | if (!sd_settings->enable && sd_settings->bl_device) { | ||
| 814 | /* Do the actual brightness update outside of | ||
| 815 | * the mutex */ | ||
| 816 | struct platform_device *pdev = | ||
| 817 | sd_settings->bl_device; | ||
| 818 | struct backlight_device *bl = | ||
| 819 | platform_get_drvdata(pdev); | ||
| 820 | |||
| 821 | if (bl) | ||
| 822 | backlight_update_status(bl); | ||
| 823 | } | ||
| 824 | } | ||
| 825 | } else { | ||
| 826 | /* This shouldn't be reachable. But just in case... */ | ||
| 827 | res = -EINVAL; | ||
| 828 | } | ||
| 829 | |||
| 830 | return res; | ||
| 831 | } | ||
| 832 | |||
| 833 | #define NVSD_PRINT_REG(__name) { \ | ||
| 834 | u32 val = tegra_dc_readl(dc, __name); \ | ||
| 835 | res += snprintf(buf + res, PAGE_SIZE - res, #__name ": 0x%08x\n", \ | ||
| 836 | val); \ | ||
| 837 | } | ||
| 838 | |||
| 839 | #define NVSD_PRINT_REG_ARRAY(__name) { \ | ||
| 840 | u32 val = 0, i = 0; \ | ||
| 841 | res += snprintf(buf + res, PAGE_SIZE - res, #__name ":\n"); \ | ||
| 842 | for (i = 0; i < __name##_NUM; i++) { \ | ||
| 843 | val = tegra_dc_readl(dc, __name(i)); \ | ||
| 844 | res += snprintf(buf + res, PAGE_SIZE - res, " %d: 0x%08x\n", \ | ||
| 845 | i, val); \ | ||
| 846 | } \ | ||
| 847 | } | ||
| 848 | |||
| 849 | static ssize_t nvsd_registers_show(struct kobject *kobj, | ||
| 850 | struct kobj_attribute *attr, char *buf) | ||
| 851 | { | ||
| 852 | struct device *dev = container_of((kobj->parent), struct device, kobj); | ||
| 853 | struct nvhost_device *ndev = to_nvhost_device(dev); | ||
| 854 | struct tegra_dc *dc = nvhost_get_drvdata(ndev); | ||
| 855 | ssize_t res = 0; | ||
| 856 | |||
| 857 | mutex_lock(&dc->lock); | ||
| 858 | if (!dc->enabled) { | ||
| 859 | mutex_unlock(&dc->lock); | ||
| 860 | return -ENODEV; | ||
| 861 | } | ||
| 862 | |||
| 863 | mutex_unlock(&dc->lock); | ||
| 864 | NVSD_PRINT_REG(DC_DISP_SD_CONTROL); | ||
| 865 | NVSD_PRINT_REG(DC_DISP_SD_CSC_COEFF); | ||
| 866 | NVSD_PRINT_REG_ARRAY(DC_DISP_SD_LUT); | ||
| 867 | NVSD_PRINT_REG(DC_DISP_SD_FLICKER_CONTROL); | ||
| 868 | NVSD_PRINT_REG(DC_DISP_SD_PIXEL_COUNT); | ||
| 869 | NVSD_PRINT_REG_ARRAY(DC_DISP_SD_HISTOGRAM); | ||
| 870 | NVSD_PRINT_REG(DC_DISP_SD_BL_PARAMETERS); | ||
| 871 | NVSD_PRINT_REG_ARRAY(DC_DISP_SD_BL_TF); | ||
| 872 | NVSD_PRINT_REG(DC_DISP_SD_BL_CONTROL); | ||
| 873 | NVSD_PRINT_REG(DC_DISP_SD_HW_K_VALUES); | ||
| 874 | NVSD_PRINT_REG(DC_DISP_SD_MAN_K_VALUES); | ||
| 875 | |||
| 876 | return res; | ||
| 877 | } | ||
| 878 | |||
| 879 | /* Sysfs initializer */ | ||
| 880 | int nvsd_create_sysfs(struct device *dev) | ||
| 881 | { | ||
| 882 | int retval = 0; | ||
| 883 | |||
| 884 | nvsd_kobj = kobject_create_and_add("smartdimmer", &dev->kobj); | ||
| 885 | |||
| 886 | if (!nvsd_kobj) | ||
| 887 | return -ENOMEM; | ||
| 888 | |||
| 889 | retval = sysfs_create_group(nvsd_kobj, &nvsd_attr_group); | ||
| 890 | |||
| 891 | if (retval) { | ||
| 892 | kobject_put(nvsd_kobj); | ||
| 893 | dev_err(dev, "%s: failed to create attributes\n", __func__); | ||
| 894 | } | ||
| 895 | |||
| 896 | return retval; | ||
| 897 | } | ||
| 898 | |||
| 899 | /* Sysfs destructor */ | ||
| 900 | void __devexit nvsd_remove_sysfs(struct device *dev) | ||
| 901 | { | ||
| 902 | if (nvsd_kobj) { | ||
| 903 | sysfs_remove_group(nvsd_kobj, &nvsd_attr_group); | ||
| 904 | kobject_put(nvsd_kobj); | ||
| 905 | } | ||
| 906 | } | ||
diff --git a/drivers/video/tegra/dc/nvsd.h b/drivers/video/tegra/dc/nvsd.h new file mode 100644 index 00000000000..f7fc4a1ead6 --- /dev/null +++ b/drivers/video/tegra/dc/nvsd.h | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/nvsd.h | ||
| 3 | * | ||
| 4 | * Copyright (c) 2010-2011, 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 | #ifndef __DRIVERS_VIDEO_TEGRA_DC_NVSD_H | ||
| 18 | #define __DRIVERS_VIDEO_TEGRA_DC_NVSD_H | ||
| 19 | |||
| 20 | void nvsd_init(struct tegra_dc *dc, struct tegra_dc_sd_settings *settings); | ||
| 21 | bool nvsd_update_brightness(struct tegra_dc *dc); | ||
| 22 | int nvsd_create_sysfs(struct device *dev); | ||
| 23 | void __devexit nvsd_remove_sysfs(struct device *dev); | ||
| 24 | |||
| 25 | #endif | ||
diff --git a/drivers/video/tegra/dc/rgb.c b/drivers/video/tegra/dc/rgb.c new file mode 100644 index 00000000000..2112643058f --- /dev/null +++ b/drivers/video/tegra/dc/rgb.c | |||
| @@ -0,0 +1,160 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/dc/rgb.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2010 Google, Inc. | ||
| 5 | * Author: Erik Gilling <konkers@android.com> | ||
| 6 | * | ||
| 7 | * This software is licensed under the terms of the GNU General Public | ||
| 8 | * License version 2, as published by the Free Software Foundation, and | ||
| 9 | * may be copied, distributed, and modified under those terms. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <linux/kernel.h> | ||
| 19 | |||
| 20 | #include <mach/dc.h> | ||
| 21 | |||
| 22 | #include "dc_reg.h" | ||
| 23 | #include "dc_priv.h" | ||
| 24 | |||
| 25 | |||
| 26 | static const u32 tegra_dc_rgb_enable_partial_pintable[] = { | ||
| 27 | DC_COM_PIN_OUTPUT_ENABLE0, 0x00000000, | ||
| 28 | DC_COM_PIN_OUTPUT_ENABLE1, 0x00000000, | ||
| 29 | DC_COM_PIN_OUTPUT_ENABLE2, 0x00000000, | ||
| 30 | DC_COM_PIN_OUTPUT_ENABLE3, 0x00000000, | ||
| 31 | DC_COM_PIN_OUTPUT_POLARITY0, 0x00000000, | ||
| 32 | DC_COM_PIN_OUTPUT_POLARITY2, 0x00000000, | ||
| 33 | DC_COM_PIN_OUTPUT_DATA0, 0x00000000, | ||
| 34 | DC_COM_PIN_OUTPUT_DATA1, 0x00000000, | ||
| 35 | DC_COM_PIN_OUTPUT_DATA2, 0x00000000, | ||
| 36 | DC_COM_PIN_OUTPUT_DATA3, 0x00000000, | ||
| 37 | }; | ||
| 38 | |||
| 39 | static const u32 tegra_dc_rgb_enable_pintable[] = { | ||
| 40 | DC_COM_PIN_OUTPUT_ENABLE0, 0x00000000, | ||
| 41 | DC_COM_PIN_OUTPUT_ENABLE1, 0x00000000, | ||
| 42 | DC_COM_PIN_OUTPUT_ENABLE2, 0x00000000, | ||
| 43 | DC_COM_PIN_OUTPUT_ENABLE3, 0x00000000, | ||
| 44 | DC_COM_PIN_OUTPUT_POLARITY0, 0x00000000, | ||
| 45 | DC_COM_PIN_OUTPUT_POLARITY1, 0x01000000, | ||
| 46 | DC_COM_PIN_OUTPUT_POLARITY2, 0x00000000, | ||
| 47 | DC_COM_PIN_OUTPUT_POLARITY3, 0x00000000, | ||
| 48 | DC_COM_PIN_OUTPUT_DATA0, 0x00000000, | ||
| 49 | DC_COM_PIN_OUTPUT_DATA1, 0x00000000, | ||
| 50 | DC_COM_PIN_OUTPUT_DATA2, 0x00000000, | ||
| 51 | DC_COM_PIN_OUTPUT_DATA3, 0x00000000, | ||
| 52 | }; | ||
| 53 | |||
| 54 | static const u32 tegra_dc_rgb_enable_out_sel_pintable[] = { | ||
| 55 | DC_COM_PIN_OUTPUT_SELECT0, 0x00000000, | ||
| 56 | DC_COM_PIN_OUTPUT_SELECT1, 0x00000000, | ||
| 57 | DC_COM_PIN_OUTPUT_SELECT2, 0x00000000, | ||
| 58 | #ifdef CONFIG_TEGRA_SILICON_PLATFORM | ||
| 59 | DC_COM_PIN_OUTPUT_SELECT3, 0x00000000, | ||
| 60 | #else | ||
| 61 | /* The display panel sub-board used on FPGA platforms (panel 86) | ||
| 62 | is non-standard. It expects the Data Enable signal on the WR | ||
| 63 | pin instead of the DE pin. */ | ||
| 64 | DC_COM_PIN_OUTPUT_SELECT3, 0x00200000, | ||
| 65 | #endif | ||
| 66 | DC_COM_PIN_OUTPUT_SELECT4, 0x00210222, | ||
| 67 | DC_COM_PIN_OUTPUT_SELECT5, 0x00002200, | ||
| 68 | DC_COM_PIN_OUTPUT_SELECT6, 0x00020000, | ||
| 69 | }; | ||
| 70 | |||
| 71 | static const u32 tegra_dc_rgb_disable_pintable[] = { | ||
| 72 | DC_COM_PIN_OUTPUT_ENABLE0, 0x55555555, | ||
| 73 | DC_COM_PIN_OUTPUT_ENABLE1, 0x55150005, | ||
| 74 | DC_COM_PIN_OUTPUT_ENABLE2, 0x55555555, | ||
| 75 | DC_COM_PIN_OUTPUT_ENABLE3, 0x55555555, | ||
| 76 | DC_COM_PIN_OUTPUT_POLARITY0, 0x00000000, | ||
| 77 | DC_COM_PIN_OUTPUT_POLARITY1, 0x00000000, | ||
| 78 | DC_COM_PIN_OUTPUT_POLARITY2, 0x00000000, | ||
| 79 | DC_COM_PIN_OUTPUT_POLARITY3, 0x00000000, | ||
| 80 | DC_COM_PIN_OUTPUT_DATA0, 0xaaaaaaaa, | ||
| 81 | DC_COM_PIN_OUTPUT_DATA1, 0xaaaaaaaa, | ||
| 82 | DC_COM_PIN_OUTPUT_DATA2, 0xaaaaaaaa, | ||
| 83 | DC_COM_PIN_OUTPUT_DATA3, 0xaaaaaaaa, | ||
| 84 | DC_COM_PIN_OUTPUT_SELECT0, 0x00000000, | ||
| 85 | DC_COM_PIN_OUTPUT_SELECT1, 0x00000000, | ||
| 86 | DC_COM_PIN_OUTPUT_SELECT2, 0x00000000, | ||
| 87 | DC_COM_PIN_OUTPUT_SELECT3, 0x00000000, | ||
| 88 | DC_COM_PIN_OUTPUT_SELECT4, 0x00000000, | ||
| 89 | DC_COM_PIN_OUTPUT_SELECT5, 0x00000000, | ||
| 90 | DC_COM_PIN_OUTPUT_SELECT6, 0x00000000, | ||
| 91 | }; | ||
| 92 | |||
| 93 | void tegra_dc_rgb_enable(struct tegra_dc *dc) | ||
| 94 | { | ||
| 95 | int i; | ||
| 96 | u32 out_sel_pintable[ARRAY_SIZE(tegra_dc_rgb_enable_out_sel_pintable)]; | ||
| 97 | |||
| 98 | tegra_dc_writel(dc, PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | | ||
| 99 | PW4_ENABLE | PM0_ENABLE | PM1_ENABLE, | ||
| 100 | DC_CMD_DISPLAY_POWER_CONTROL); | ||
| 101 | |||
| 102 | tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, DC_CMD_DISPLAY_COMMAND); | ||
| 103 | |||
| 104 | if (dc->out->out_pins) { | ||
| 105 | tegra_dc_set_out_pin_polars(dc, dc->out->out_pins, | ||
| 106 | dc->out->n_out_pins); | ||
| 107 | tegra_dc_write_table(dc, tegra_dc_rgb_enable_partial_pintable); | ||
| 108 | } else { | ||
| 109 | tegra_dc_write_table(dc, tegra_dc_rgb_enable_pintable); | ||
| 110 | } | ||
| 111 | |||
| 112 | memcpy(out_sel_pintable, tegra_dc_rgb_enable_out_sel_pintable, | ||
| 113 | sizeof(tegra_dc_rgb_enable_out_sel_pintable)); | ||
| 114 | |||
| 115 | if (dc->out && dc->out->out_sel_configs) { | ||
| 116 | u8 *out_sels = dc->out->out_sel_configs; | ||
| 117 | for (i = 0; i < dc->out->n_out_sel_configs; i++) { | ||
| 118 | switch (out_sels[i]) { | ||
| 119 | case TEGRA_PIN_OUT_CONFIG_SEL_LM1_M1: | ||
| 120 | out_sel_pintable[5*2+1] = | ||
| 121 | (out_sel_pintable[5*2+1] & | ||
| 122 | ~PIN5_LM1_LCD_M1_OUTPUT_MASK) | | ||
| 123 | PIN5_LM1_LCD_M1_OUTPUT_M1; | ||
| 124 | break; | ||
| 125 | case TEGRA_PIN_OUT_CONFIG_SEL_LM1_LD21: | ||
| 126 | out_sel_pintable[5*2+1] = | ||
| 127 | (out_sel_pintable[5*2+1] & | ||
| 128 | ~PIN5_LM1_LCD_M1_OUTPUT_MASK) | | ||
| 129 | PIN5_LM1_LCD_M1_OUTPUT_LD21; | ||
| 130 | break; | ||
| 131 | case TEGRA_PIN_OUT_CONFIG_SEL_LM1_PM1: | ||
| 132 | out_sel_pintable[5*2+1] = | ||
| 133 | (out_sel_pintable[5*2+1] & | ||
| 134 | ~PIN5_LM1_LCD_M1_OUTPUT_MASK) | | ||
| 135 | PIN5_LM1_LCD_M1_OUTPUT_PM1; | ||
| 136 | break; | ||
| 137 | default: | ||
| 138 | dev_err(&dc->ndev->dev, | ||
| 139 | "Invalid pin config[%d]: %d\n", | ||
| 140 | i, out_sels[i]); | ||
| 141 | break; | ||
| 142 | } | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | tegra_dc_write_table(dc, out_sel_pintable); | ||
| 147 | } | ||
| 148 | |||
| 149 | void tegra_dc_rgb_disable(struct tegra_dc *dc) | ||
| 150 | { | ||
| 151 | tegra_dc_writel(dc, 0x00000000, DC_CMD_DISPLAY_POWER_CONTROL); | ||
| 152 | |||
| 153 | tegra_dc_write_table(dc, tegra_dc_rgb_disable_pintable); | ||
| 154 | } | ||
| 155 | |||
| 156 | struct tegra_dc_out_ops tegra_dc_rgb_ops = { | ||
| 157 | .enable = tegra_dc_rgb_enable, | ||
| 158 | .disable = tegra_dc_rgb_disable, | ||
| 159 | }; | ||
| 160 | |||
