/drivers/video/tegra/dc/mode.c
C | 318 lines | 217 code | 57 blank | 44 comment | 38 complexity | 18f7472e71468303ee73fdf8e041c38f MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
1/* 2 * drivers/video/tegra/dc/mode.c 3 * 4 * Copyright (C) 2010 Google, Inc. 5 * 6 * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. 7 * 8 * This software is licensed under the terms of the GNU General Public 9 * License version 2, as published by the Free Software Foundation, and 10 * may be copied, distributed, and modified under those terms. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 */ 18 19#include <linux/err.h> 20#include <linux/types.h> 21#include <linux/clk.h> 22 23#include <mach/clk.h> 24#include <mach/dc.h> 25 26#include "dc_reg.h" 27#include "dc_priv.h" 28 29/* return non-zero if constraint is violated */ 30static int calc_h_ref_to_sync(const struct tegra_dc_mode *mode, int *href) 31{ 32 long a, b; 33 34 /* Constraint 5: H_REF_TO_SYNC >= 0 */ 35 a = 0; 36 37 /* Constraint 6: H_FRONT_PORT >= (H_REF_TO_SYNC + 1) */ 38 b = mode->h_front_porch - 1; 39 40 /* Constraint 1: H_REF_TO_SYNC + H_SYNC_WIDTH + H_BACK_PORCH > 11 */ 41 if (a + mode->h_sync_width + mode->h_back_porch <= 11) 42 a = 1 + 11 - mode->h_sync_width - mode->h_back_porch; 43 /* check Constraint 1 and 6 */ 44 if (a > b) 45 return 1; 46 47 /* Constraint 4: H_SYNC_WIDTH >= 1 */ 48 if (mode->h_sync_width < 1) 49 return 4; 50 51 /* Constraint 7: H_DISP_ACTIVE >= 16 */ 52 if (mode->h_active < 16) 53 return 7; 54 55 if (href) { 56 if (b > a && a % 2) 57 *href = a + 1; /* use smallest even value */ 58 else 59 *href = a; /* even or only possible value */ 60 } 61 62 return 0; 63} 64 65static int calc_v_ref_to_sync(const struct tegra_dc_mode *mode, int *vref) 66{ 67 long a; 68 a = 1; /* Constraint 5: V_REF_TO_SYNC >= 1 */ 69 70 /* Constraint 2: V_REF_TO_SYNC + V_SYNC_WIDTH + V_BACK_PORCH > 1 */ 71 if (a + mode->v_sync_width + mode->v_back_porch <= 1) 72 a = 1 + 1 - mode->v_sync_width - mode->v_back_porch; 73 74 /* Constraint 6 */ 75 if (mode->v_front_porch < a + 1) 76 a = mode->v_front_porch - 1; 77 78 /* Constraint 4: V_SYNC_WIDTH >= 1 */ 79 if (mode->v_sync_width < 1) 80 return 4; 81 82 /* Constraint 7: V_DISP_ACTIVE >= 16 */ 83 if (mode->v_active < 16) 84 return 7; 85 86 if (vref) 87 *vref = a; 88 return 0; 89} 90 91static int calc_ref_to_sync(struct tegra_dc_mode *mode) 92{ 93 int ret; 94 ret = calc_h_ref_to_sync(mode, &mode->h_ref_to_sync); 95 if (ret) 96 return ret; 97 ret = calc_v_ref_to_sync(mode, &mode->v_ref_to_sync); 98 if (ret) 99 return ret; 100 101 return 0; 102} 103 104static bool check_ref_to_sync(struct tegra_dc_mode *mode) 105{ 106 /* Constraint 1: H_REF_TO_SYNC + H_SYNC_WIDTH + H_BACK_PORCH > 11. */ 107 if (mode->h_ref_to_sync + mode->h_sync_width + mode->h_back_porch <= 11) 108 return false; 109 110 /* Constraint 2: V_REF_TO_SYNC + V_SYNC_WIDTH + V_BACK_PORCH > 1. */ 111 if (mode->v_ref_to_sync + mode->v_sync_width + mode->v_back_porch <= 1) 112 return false; 113 114 /* Constraint 3: V_FRONT_PORCH + V_SYNC_WIDTH + V_BACK_PORCH > 1 115 * (vertical blank). */ 116 if (mode->v_front_porch + mode->v_sync_width + mode->v_back_porch <= 1) 117 return false; 118 119 /* Constraint 4: V_SYNC_WIDTH >= 1; H_SYNC_WIDTH >= 1. */ 120 if (mode->v_sync_width < 1 || mode->h_sync_width < 1) 121 return false; 122 123 /* Constraint 5: V_REF_TO_SYNC >= 1; H_REF_TO_SYNC >= 0. */ 124 if (mode->v_ref_to_sync < 1 || mode->h_ref_to_sync < 0) 125 return false; 126 127 /* Constraint 6: V_FRONT_PORT >= (V_REF_TO_SYNC + 1); 128 * H_FRONT_PORT >= (H_REF_TO_SYNC + 1). */ 129 if (mode->v_front_porch < mode->v_ref_to_sync + 1 || 130 mode->h_front_porch < mode->h_ref_to_sync + 1) 131 return false; 132 133 /* Constraint 7: H_DISP_ACTIVE >= 16; V_DISP_ACTIVE >= 16. */ 134 if (mode->h_active < 16 || mode->v_active < 16) 135 return false; 136 137 return true; 138} 139 140/* return in 1000ths of a Hertz */ 141int tegra_dc_calc_refresh(const struct tegra_dc_mode *m) 142{ 143 long h_total, v_total, refresh; 144 h_total = m->h_active + m->h_front_porch + m->h_back_porch + 145 m->h_sync_width; 146 v_total = m->v_active + m->v_front_porch + m->v_back_porch + 147 m->v_sync_width; 148 refresh = m->pclk / h_total; 149 refresh *= 1000; 150 refresh /= v_total; 151 return refresh; 152} 153 154#ifdef DEBUG 155static void print_mode(struct tegra_dc *dc, 156 const struct tegra_dc_mode *mode, const char *note) 157{ 158 if (mode) { 159 int refresh = tegra_dc_calc_refresh(mode); 160 dev_info(&dc->ndev->dev, "%s():MODE:%dx%d@%d.%03uHz pclk=%d\n", 161 note ? note : "", 162 mode->h_active, mode->v_active, 163 refresh / 1000, refresh % 1000, 164 mode->pclk); 165 } 166} 167#else /* !DEBUG */ 168static inline void print_mode(struct tegra_dc *dc, 169 const struct tegra_dc_mode *mode, const char *note) { } 170#endif /* DEBUG */ 171 172int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode) 173{ 174 unsigned long val; 175 unsigned long rate; 176 unsigned long div; 177 unsigned long pclk; 178 179 print_mode(dc, mode, __func__); 180 181 /* use default EMC rate when switching modes */ 182 dc->new_emc_clk_rate = tegra_dc_get_default_emc_clk_rate(dc); 183 tegra_dc_program_bandwidth(dc, true); 184 185 tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS); 186 tegra_dc_writel(dc, mode->h_ref_to_sync | (mode->v_ref_to_sync << 16), 187 DC_DISP_REF_TO_SYNC); 188 tegra_dc_writel(dc, mode->h_sync_width | (mode->v_sync_width << 16), 189 DC_DISP_SYNC_WIDTH); 190 tegra_dc_writel(dc, mode->h_back_porch | (mode->v_back_porch << 16), 191 DC_DISP_BACK_PORCH); 192 tegra_dc_writel(dc, mode->h_active | (mode->v_active << 16), 193 DC_DISP_DISP_ACTIVE); 194 tegra_dc_writel(dc, mode->h_front_porch | (mode->v_front_porch << 16), 195 DC_DISP_FRONT_PORCH); 196 197 tegra_dc_writel(dc, DE_SELECT_ACTIVE | DE_CONTROL_NORMAL, 198 DC_DISP_DATA_ENABLE_OPTIONS); 199 200 /* TODO: MIPI/CRT/HDMI clock cals */ 201 202 val = DISP_DATA_FORMAT_DF1P1C; 203 204 if (dc->out->align == TEGRA_DC_ALIGN_MSB) 205 val |= DISP_DATA_ALIGNMENT_MSB; 206 else 207 val |= DISP_DATA_ALIGNMENT_LSB; 208 209 if (dc->out->order == TEGRA_DC_ORDER_RED_BLUE) 210 val |= DISP_DATA_ORDER_RED_BLUE; 211 else 212 val |= DISP_DATA_ORDER_BLUE_RED; 213 214 tegra_dc_writel(dc, val, DC_DISP_DISP_INTERFACE_CONTROL); 215 216 rate = tegra_dc_clk_get_rate(dc); 217 218 pclk = tegra_dc_pclk_round_rate(dc, mode->pclk); 219 trace_printk("%s:pclk=%ld\n", dc->ndev->name, pclk); 220 if (pclk < (mode->pclk / 100 * 99) || 221 pclk > (mode->pclk / 100 * 109)) { 222 dev_err(&dc->ndev->dev, 223 "can't divide %ld clock to %d -1/+9%% %ld %d %d\n", 224 rate, mode->pclk, 225 pclk, (mode->pclk / 100 * 99), 226 (mode->pclk / 100 * 109)); 227 return -EINVAL; 228 } 229 230 div = (rate * 2 / pclk) - 2; 231 trace_printk("%s:div=%ld\n", dc->ndev->name, div); 232 233 tegra_dc_writel(dc, 0x00010001, 234 DC_DISP_SHIFT_CLOCK_OPTIONS); 235 tegra_dc_writel(dc, PIXEL_CLK_DIVIDER_PCD1 | SHIFT_CLK_DIVIDER(div), 236 DC_DISP_DISP_CLOCK_CONTROL); 237 238#ifdef CONFIG_SWITCH 239 switch_set_state(&dc->modeset_switch, 240 (mode->h_active << 16) | mode->v_active); 241#endif 242 243 tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL); 244 tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); 245 246 print_mode_info(dc, dc->mode); 247 return 0; 248} 249 250int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode) 251{ 252 memcpy(&dc->mode, mode, sizeof(dc->mode)); 253 254 print_mode(dc, mode, __func__); 255 256 return 0; 257} 258EXPORT_SYMBOL(tegra_dc_set_mode); 259 260int tegra_dc_set_fb_mode(struct tegra_dc *dc, 261 const struct fb_videomode *fbmode, bool stereo_mode) 262{ 263 struct tegra_dc_mode mode; 264 265 if (!fbmode->pixclock) 266 return -EINVAL; 267 268 mode.pclk = PICOS2KHZ(fbmode->pixclock) * 1000; 269 mode.h_sync_width = fbmode->hsync_len; 270 mode.v_sync_width = fbmode->vsync_len; 271 mode.h_back_porch = fbmode->left_margin; 272 mode.v_back_porch = fbmode->upper_margin; 273 mode.h_active = fbmode->xres; 274 mode.v_active = fbmode->yres; 275 mode.h_front_porch = fbmode->right_margin; 276 mode.v_front_porch = fbmode->lower_margin; 277 mode.stereo_mode = stereo_mode; 278 if (dc->out->type == TEGRA_DC_OUT_HDMI) { 279 /* HDMI controller requires h_ref=1, v_ref=1 */ 280 mode.h_ref_to_sync = 1; 281 mode.v_ref_to_sync = 1; 282 } else { 283 calc_ref_to_sync(&mode); 284 } 285 if (!check_ref_to_sync(&mode)) { 286 dev_err(&dc->ndev->dev, 287 "Display timing doesn't meet restrictions.\n"); 288 return -EINVAL; 289 } 290 dev_info(&dc->ndev->dev, "Using mode %dx%d pclk=%d href=%d vref=%d\n", 291 mode.h_active, mode.v_active, mode.pclk, 292 mode.h_ref_to_sync, mode.v_ref_to_sync 293 ); 294 295#ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT 296 /* Double the pixel clock and update v_active only for 297 * frame packed mode */ 298 if (mode.stereo_mode) { 299 mode.pclk *= 2; 300 /* total v_active = yres*2 + activespace */ 301 mode.v_active = fbmode->yres * 2 + 302 fbmode->vsync_len + 303 fbmode->upper_margin + 304 fbmode->lower_margin; 305 } 306#endif 307 308 mode.flags = 0; 309 310 if (!(fbmode->sync & FB_SYNC_HOR_HIGH_ACT)) 311 mode.flags |= TEGRA_DC_MODE_FLAG_NEG_H_SYNC; 312 313 if (!(fbmode->sync & FB_SYNC_VERT_HIGH_ACT)) 314 mode.flags |= TEGRA_DC_MODE_FLAG_NEG_V_SYNC; 315 316 return tegra_dc_set_mode(dc, &mode); 317} 318EXPORT_SYMBOL(tegra_dc_set_fb_mode);