tegra: dc: Fix div by zero in frame time computation.
[linux-2.6.git] / drivers / video / tegra / dc / mode.c
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 */
30 static 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
65 static 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
91 static 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
104 static 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 static s64 calc_frametime_ns(const struct tegra_dc_mode *m)
141 {
142         long h_total, v_total;
143         h_total = m->h_active + m->h_front_porch + m->h_back_porch +
144                 m->h_sync_width;
145         v_total = m->v_active + m->v_front_porch + m->v_back_porch +
146                 m->v_sync_width;
147         return (!m->pclk) ? 0 : (s64)(div_s64(((s64)h_total * v_total *
148                                         1000000000ULL), m->pclk));
149 }
150
151 /* return in 1000ths of a Hertz */
152 int tegra_dc_calc_refresh(const struct tegra_dc_mode *m)
153 {
154         long h_total, v_total, refresh;
155         h_total = m->h_active + m->h_front_porch + m->h_back_porch +
156                 m->h_sync_width;
157         v_total = m->v_active + m->v_front_porch + m->v_back_porch +
158                 m->v_sync_width;
159         refresh = m->pclk / h_total;
160         refresh *= 1000;
161         refresh /= v_total;
162         return refresh;
163 }
164
165 #ifdef DEBUG
166 static void print_mode(struct tegra_dc *dc,
167                         const struct tegra_dc_mode *mode, const char *note)
168 {
169         if (mode) {
170                 int refresh = tegra_dc_calc_refresh(mode);
171                 dev_info(&dc->ndev->dev, "%s():MODE:%dx%d@%d.%03uHz pclk=%d\n",
172                         note ? note : "",
173                         mode->h_active, mode->v_active,
174                         refresh / 1000, refresh % 1000,
175                         mode->pclk);
176         }
177 }
178 #else /* !DEBUG */
179 static inline void print_mode(struct tegra_dc *dc,
180                         const struct tegra_dc_mode *mode, const char *note) { }
181 #endif /* DEBUG */
182
183 int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode)
184 {
185         unsigned long val;
186         unsigned long rate;
187         unsigned long div;
188         unsigned long pclk;
189
190         print_mode(dc, mode, __func__);
191
192         /* use default EMC rate when switching modes */
193         dc->new_emc_clk_rate = tegra_dc_get_default_emc_clk_rate(dc);
194         tegra_dc_program_bandwidth(dc, true);
195
196         tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS);
197         tegra_dc_writel(dc, mode->h_ref_to_sync | (mode->v_ref_to_sync << 16),
198                         DC_DISP_REF_TO_SYNC);
199         tegra_dc_writel(dc, mode->h_sync_width | (mode->v_sync_width << 16),
200                         DC_DISP_SYNC_WIDTH);
201         tegra_dc_writel(dc, mode->h_back_porch | (mode->v_back_porch << 16),
202                         DC_DISP_BACK_PORCH);
203         tegra_dc_writel(dc, mode->h_active | (mode->v_active << 16),
204                         DC_DISP_DISP_ACTIVE);
205         tegra_dc_writel(dc, mode->h_front_porch | (mode->v_front_porch << 16),
206                         DC_DISP_FRONT_PORCH);
207
208         tegra_dc_writel(dc, DE_SELECT_ACTIVE | DE_CONTROL_NORMAL,
209                         DC_DISP_DATA_ENABLE_OPTIONS);
210
211         /* TODO: MIPI/CRT/HDMI clock cals */
212
213         val = DISP_DATA_FORMAT_DF1P1C;
214
215         if (dc->out->align == TEGRA_DC_ALIGN_MSB)
216                 val |= DISP_DATA_ALIGNMENT_MSB;
217         else
218                 val |= DISP_DATA_ALIGNMENT_LSB;
219
220         if (dc->out->order == TEGRA_DC_ORDER_RED_BLUE)
221                 val |= DISP_DATA_ORDER_RED_BLUE;
222         else
223                 val |= DISP_DATA_ORDER_BLUE_RED;
224
225         tegra_dc_writel(dc, val, DC_DISP_DISP_INTERFACE_CONTROL);
226
227         rate = tegra_dc_clk_get_rate(dc);
228
229         pclk = tegra_dc_pclk_round_rate(dc, mode->pclk);
230         trace_printk("%s:pclk=%ld\n", dc->ndev->name, pclk);
231         if (pclk < (mode->pclk / 100 * 99) ||
232             pclk > (mode->pclk / 100 * 109)) {
233                 dev_err(&dc->ndev->dev,
234                         "can't divide %ld clock to %d -1/+9%% %ld %d %d\n",
235                         rate, mode->pclk,
236                         pclk, (mode->pclk / 100 * 99),
237                         (mode->pclk / 100 * 109));
238                 return -EINVAL;
239         }
240
241         div = (rate * 2 / pclk) - 2;
242         trace_printk("%s:div=%ld\n", dc->ndev->name, div);
243
244         tegra_dc_writel(dc, 0x00010001,
245                         DC_DISP_SHIFT_CLOCK_OPTIONS);
246         tegra_dc_writel(dc, PIXEL_CLK_DIVIDER_PCD1 | SHIFT_CLK_DIVIDER(div),
247                         DC_DISP_DISP_CLOCK_CONTROL);
248
249 #ifdef CONFIG_SWITCH
250         switch_set_state(&dc->modeset_switch,
251                          (mode->h_active << 16) | mode->v_active);
252 #endif
253
254         tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL);
255         tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
256
257         print_mode_info(dc, dc->mode);
258         return 0;
259 }
260
261 static int panel_sync_rate;
262
263 int tegra_dc_get_panel_sync_rate(void)
264 {
265         return panel_sync_rate;
266 }
267 EXPORT_SYMBOL(tegra_dc_get_panel_sync_rate);
268
269 int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode)
270 {
271         memcpy(&dc->mode, mode, sizeof(dc->mode));
272
273         if (dc->out->type == TEGRA_DC_OUT_RGB)
274                 panel_sync_rate = tegra_dc_calc_refresh(mode);
275         else if (dc->out->type == TEGRA_DC_OUT_DSI)
276                 panel_sync_rate = dc->out->dsi->rated_refresh_rate * 1000;
277
278         print_mode(dc, mode, __func__);
279         dc->frametime_ns = calc_frametime_ns(mode);
280
281         return 0;
282 }
283 EXPORT_SYMBOL(tegra_dc_set_mode);
284
285 int tegra_dc_set_fb_mode(struct tegra_dc *dc,
286                 const struct fb_videomode *fbmode, bool stereo_mode)
287 {
288         struct tegra_dc_mode mode;
289
290         if (!fbmode->pixclock)
291                 return -EINVAL;
292
293         mode.pclk = PICOS2KHZ(fbmode->pixclock) * 1000;
294         mode.h_sync_width = fbmode->hsync_len;
295         mode.v_sync_width = fbmode->vsync_len;
296         mode.h_back_porch = fbmode->left_margin;
297         mode.v_back_porch = fbmode->upper_margin;
298         mode.h_active = fbmode->xres;
299         mode.v_active = fbmode->yres;
300         mode.h_front_porch = fbmode->right_margin;
301         mode.v_front_porch = fbmode->lower_margin;
302         mode.stereo_mode = stereo_mode;
303         if (dc->out->type == TEGRA_DC_OUT_HDMI) {
304                 /* HDMI controller requires h_ref=1, v_ref=1 */
305                 mode.h_ref_to_sync = 1;
306                 mode.v_ref_to_sync = 1;
307         } else {
308                 calc_ref_to_sync(&mode);
309         }
310         if (!check_ref_to_sync(&mode)) {
311                 dev_err(&dc->ndev->dev,
312                                 "Display timing doesn't meet restrictions.\n");
313                 return -EINVAL;
314         }
315         dev_info(&dc->ndev->dev, "Using mode %dx%d pclk=%d href=%d vref=%d\n",
316                 mode.h_active, mode.v_active, mode.pclk,
317                 mode.h_ref_to_sync, mode.v_ref_to_sync
318         );
319
320 #ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT
321         /* Double the pixel clock and update v_active only for
322          * frame packed mode */
323         if (mode.stereo_mode) {
324                 mode.pclk *= 2;
325                 /* total v_active = yres*2 + activespace */
326                 mode.v_active = fbmode->yres * 2 +
327                                 fbmode->vsync_len +
328                                 fbmode->upper_margin +
329                                 fbmode->lower_margin;
330         }
331 #endif
332
333         mode.flags = 0;
334
335         if (!(fbmode->sync & FB_SYNC_HOR_HIGH_ACT))
336                 mode.flags |= TEGRA_DC_MODE_FLAG_NEG_H_SYNC;
337
338         if (!(fbmode->sync & FB_SYNC_VERT_HIGH_ACT))
339                 mode.flags |= TEGRA_DC_MODE_FLAG_NEG_V_SYNC;
340
341         return tegra_dc_set_mode(dc, &mode);
342 }
343 EXPORT_SYMBOL(tegra_dc_set_fb_mode);