PageRenderTime 56ms CodeModel.GetById 37ms app.highlight 17ms RepoModel.GetById 0ms app.codeStats 0ms

/drivers/video/tegra/dc/mode.c

https://bitbucket.org/cyanogenmod/android_kernel_asus_tf300t
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);