video: tegra: dc: Add support to SCAN_COLUMN.
[linux-3.10.git] / drivers / video / tegra / dc / window.c
1 /*
2  * drivers/video/tegra/dc/window.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 #include <linux/err.h>
19 #include <linux/types.h>
20 #include <linux/moduleparam.h>
21 #include <mach/dc.h>
22
23 #include "dc_reg.h"
24 #include "dc_config.h"
25 #include "dc_priv.h"
26
27 static int no_vsync;
28 static atomic_t frame_end_ref = ATOMIC_INIT(0);
29
30 module_param_named(no_vsync, no_vsync, int, S_IRUGO | S_IWUSR);
31
32 static bool tegra_dc_windows_are_clean(struct tegra_dc_win *windows[],
33                                              int n)
34 {
35         int i;
36
37         for (i = 0; i < n; i++) {
38                 if (windows[i]->dirty)
39                         return false;
40         }
41
42         return true;
43 }
44
45 int tegra_dc_config_frame_end_intr(struct tegra_dc *dc, bool enable)
46 {
47         tegra_dc_writel(dc, FRAME_END_INT, DC_CMD_INT_STATUS);
48         if (enable) {
49                 atomic_inc(&frame_end_ref);
50                 tegra_dc_unmask_interrupt(dc, FRAME_END_INT);
51         } else if (!atomic_dec_return(&frame_end_ref))
52                 tegra_dc_mask_interrupt(dc, FRAME_END_INT);
53         return 0;
54 }
55
56 static int get_topmost_window(u32 *depths, unsigned long *wins, int win_num)
57 {
58         int idx, best = -1;
59
60         for_each_set_bit(idx, wins, win_num) {
61                 if (best == -1 || depths[idx] < depths[best])
62                         best = idx;
63         }
64         clear_bit(best, wins);
65         return best;
66 }
67
68 static u32 blend_topwin(u32 flags)
69 {
70         if (flags & TEGRA_WIN_FLAG_BLEND_COVERAGE)
71                 return BLEND(NOKEY, ALPHA, 0xff, 0xff);
72         else if (flags & TEGRA_WIN_FLAG_BLEND_PREMULT)
73                 return BLEND(NOKEY, PREMULT, 0xff, 0xff);
74         else
75                 return BLEND(NOKEY, FIX, 0xff, 0xff);
76 }
77
78 static u32 blend_2win(int idx, unsigned long behind_mask,
79                                                 u32* flags, int xy, int win_num)
80 {
81         int other;
82
83         for (other = 0; other < win_num; other++) {
84                 if (other != idx && (xy-- == 0))
85                         break;
86         }
87         if (BIT(other) & behind_mask)
88                 return blend_topwin(flags[idx]);
89         else if (flags[other])
90                 return BLEND(NOKEY, DEPENDANT, 0x00, 0x00);
91         else
92                 return BLEND(NOKEY, FIX, 0x00, 0x00);
93 }
94
95 static u32 blend_3win(int idx, unsigned long behind_mask,
96                                                 u32* flags, int win_num)
97 {
98         unsigned long infront_mask;
99         int first;
100
101         infront_mask = ~(behind_mask | BIT(idx));
102         infront_mask &= (BIT(win_num) - 1);
103         first = ffs(infront_mask) - 1;
104
105         if (!infront_mask)
106                 return blend_topwin(flags[idx]);
107         else if (behind_mask && first != -1 && flags[first])
108                 return BLEND(NOKEY, DEPENDANT, 0x00, 0x00);
109         else
110                 return BLEND(NOKEY, FIX, 0x0, 0x0);
111 }
112
113 static void tegra_dc_blend_parallel(struct tegra_dc *dc,
114                                 struct tegra_dc_blend *blend)
115 {
116         int win_num = dc->gen1_blend_num;
117         unsigned long mask = BIT(win_num) - 1;
118
119         while (mask) {
120                 int idx = get_topmost_window(blend->z, &mask, win_num);
121
122                 tegra_dc_writel(dc, WINDOW_A_SELECT << idx,
123                                 DC_CMD_DISPLAY_WINDOW_HEADER);
124                 tegra_dc_writel(dc, BLEND(NOKEY, FIX, 0xff, 0xff),
125                                 DC_WIN_BLEND_NOKEY);
126                 tegra_dc_writel(dc, BLEND(NOKEY, FIX, 0xff, 0xff),
127                                 DC_WIN_BLEND_1WIN);
128                 tegra_dc_writel(dc, blend_2win(idx, mask, blend->flags, 0,
129                                 win_num), DC_WIN_BLEND_2WIN_X);
130                 tegra_dc_writel(dc, blend_2win(idx, mask, blend->flags, 1,
131                                 win_num), DC_WIN_BLEND_2WIN_Y);
132                 tegra_dc_writel(dc, blend_3win(idx, mask, blend->flags,
133                                 win_num), DC_WIN_BLEND_3WIN_XY);
134         }
135 }
136
137 static void tegra_dc_blend_sequential(struct tegra_dc *dc,
138                                 struct tegra_dc_blend *blend)
139 {
140         int i;
141
142         for (i = 0; i < DC_N_WINDOWS; i++) {
143                 if (!tegra_dc_feature_is_gen2_blender(dc, i))
144                         continue;
145
146                 tegra_dc_writel(dc, WINDOW_A_SELECT << i,
147                                 DC_CMD_DISPLAY_WINDOW_HEADER);
148
149                 if (blend->flags[i] & TEGRA_WIN_FLAG_BLEND_COVERAGE) {
150                         tegra_dc_writel(dc,
151                                         WIN_K1(0xff) |
152                                         WIN_K2(0xff) |
153                                         WIN_BLEND_ENABLE,
154                                         DC_WINBUF_BLEND_LAYER_CONTROL);
155
156                         tegra_dc_writel(dc,
157                         WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_K1_TIMES_SRC |
158                         WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_NEG_K1_TIMES_SRC |
159                         WIN_BLEND_FACT_SRC_ALPHA_MATCH_SEL_K2 |
160                         WIN_BLEND_FACT_DST_ALPHA_MATCH_SEL_ZERO,
161                         DC_WINBUF_BLEND_MATCH_SELECT);
162
163                         tegra_dc_writel(dc,
164                                         WIN_ALPHA_1BIT_WEIGHT0(0) |
165                                         WIN_ALPHA_1BIT_WEIGHT1(0xff),
166                                         DC_WINBUF_BLEND_ALPHA_1BIT);
167                 } else if (blend->flags[i] & TEGRA_WIN_FLAG_BLEND_PREMULT) {
168                         tegra_dc_writel(dc,
169                                         WIN_K1(0xff) |
170                                         WIN_K2(0xff) |
171                                         WIN_BLEND_ENABLE,
172                                         DC_WINBUF_BLEND_LAYER_CONTROL);
173
174                         tegra_dc_writel(dc,
175                         WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_K1 |
176                         WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_NEG_K1 |
177                         WIN_BLEND_FACT_SRC_ALPHA_MATCH_SEL_K2 |
178                         WIN_BLEND_FACT_DST_ALPHA_MATCH_SEL_ZERO,
179                         DC_WINBUF_BLEND_MATCH_SELECT);
180
181                         tegra_dc_writel(dc,
182                                         WIN_ALPHA_1BIT_WEIGHT0(0) |
183                                         WIN_ALPHA_1BIT_WEIGHT1(0xff),
184                                         DC_WINBUF_BLEND_ALPHA_1BIT);
185                 } else {
186                         tegra_dc_writel(dc,
187                                         WIN_BLEND_BYPASS,
188                                         DC_WINBUF_BLEND_LAYER_CONTROL);
189                 }
190         }
191 }
192
193 /* does not support syncing windows on multiple dcs in one call */
194 int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n)
195 {
196         int ret;
197         if (n < 1 || n > DC_N_WINDOWS)
198                 return -EINVAL;
199
200         if (!windows[0]->dc->enabled)
201                 return -EFAULT;
202
203 #ifdef CONFIG_TEGRA_SIMULATION_PLATFORM
204         /* Don't want to timeout on simulator */
205         ret = wait_event_interruptible(windows[0]->dc->wq,
206                 tegra_dc_windows_are_clean(windows, n));
207 #else
208         trace_printk("%s:Before wait_event_interruptible_timeout\n",
209                 windows[0]->dc->ndev->name);
210         ret = wait_event_interruptible_timeout(windows[0]->dc->wq,
211                 tegra_dc_windows_are_clean(windows, n),
212                 HZ);
213         trace_printk("%s:After wait_event_interruptible_timeout\n",
214                 windows[0]->dc->ndev->name);
215 #endif
216         return ret;
217 }
218 EXPORT_SYMBOL(tegra_dc_sync_windows);
219
220 static inline u32 compute_dda_inc(fixed20_12 in, unsigned out_int,
221                                   bool v, unsigned Bpp)
222 {
223         /*
224          * min(round((prescaled_size_in_pixels - 1) * 0x1000 /
225          *           (post_scaled_size_in_pixels - 1)), MAX)
226          * Where the value of MAX is as follows:
227          * For V_DDA_INCREMENT: 15.0 (0xF000)
228          * For H_DDA_INCREMENT:  4.0 (0x4000) for 4 Bytes/pix formats.
229          *                       8.0 (0x8000) for 2 Bytes/pix formats.
230          */
231
232         fixed20_12 out = dfixed_init(out_int);
233         u32 dda_inc;
234         int max;
235
236         if (v) {
237                 max = 15;
238         } else {
239                 switch (Bpp) {
240                 default:
241                         WARN_ON_ONCE(1);
242                         /* fallthrough */
243                 case 4:
244                         max = 4;
245                         break;
246                 case 2:
247                         max = 8;
248                         break;
249                 }
250         }
251
252         out.full = max_t(u32, out.full - dfixed_const(1), dfixed_const(1));
253         in.full -= dfixed_const(1);
254
255         dda_inc = dfixed_div(in, out);
256
257         dda_inc = min_t(u32, dda_inc, dfixed_const(max));
258
259         return dda_inc;
260 }
261
262 static inline u32 compute_initial_dda(fixed20_12 in)
263 {
264         return dfixed_frac(in);
265 }
266
267 static inline void tegra_dc_update_scaling(struct tegra_dc *dc,
268                                 struct tegra_dc_win *win, unsigned Bpp,
269                                 unsigned Bpp_bw, bool scan_column)
270 {
271         u32 h_dda, h_dda_init;
272         u32 v_dda, v_dda_init;
273
274         h_dda_init = compute_initial_dda(win->x);
275         v_dda_init = compute_initial_dda(win->y);
276
277         if (scan_column) {
278                 tegra_dc_writel(dc,
279                         V_PRESCALED_SIZE(dfixed_trunc(win->w)) |
280                         H_PRESCALED_SIZE(dfixed_trunc(win->h) * Bpp),
281                         DC_WIN_PRESCALED_SIZE);
282
283                 tegra_dc_writel(dc, v_dda_init, DC_WIN_H_INITIAL_DDA);
284                 tegra_dc_writel(dc, h_dda_init, DC_WIN_V_INITIAL_DDA);
285                 h_dda = compute_dda_inc(win->h, win->out_h,
286                                 false, Bpp_bw);
287                 v_dda = compute_dda_inc(win->w, win->out_w,
288                                 true, Bpp_bw);
289         } else {
290                 tegra_dc_writel(dc,
291                         V_PRESCALED_SIZE(dfixed_trunc(win->h)) |
292                         H_PRESCALED_SIZE(dfixed_trunc(win->w) * Bpp),
293                         DC_WIN_PRESCALED_SIZE);
294
295                 tegra_dc_writel(dc, h_dda_init, DC_WIN_H_INITIAL_DDA);
296                 tegra_dc_writel(dc, v_dda_init, DC_WIN_V_INITIAL_DDA);
297                 h_dda = compute_dda_inc(win->w, win->out_w,
298                                 false, Bpp_bw);
299                 v_dda = compute_dda_inc(win->h, win->out_h,
300                                 true, Bpp_bw);
301         }
302         tegra_dc_writel(dc, V_DDA_INC(v_dda) |
303                 H_DDA_INC(h_dda), DC_WIN_DDA_INCREMENT);
304 }
305
306 /* does not support updating windows on multiple dcs in one call */
307 int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
308 {
309         struct tegra_dc *dc;
310         unsigned long update_mask = GENERAL_ACT_REQ;
311         unsigned long val;
312         unsigned long win_options;
313         bool update_blend_par = false;
314         bool update_blend_seq = false;
315         int i;
316
317         dc = windows[0]->dc;
318
319         if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) {
320                 /* Acquire one_shot_lock to avoid race condition between
321                  * cancellation of old delayed work and schedule of new
322                  * delayed work. */
323                 mutex_lock(&dc->one_shot_lock);
324                 cancel_delayed_work_sync(&dc->one_shot_work);
325         }
326         mutex_lock(&dc->lock);
327
328         if (!dc->enabled) {
329                 mutex_unlock(&dc->lock);
330                 if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
331                         mutex_unlock(&dc->one_shot_lock);
332                 return -EFAULT;
333         }
334
335         tegra_dc_hold_dc_out(dc);
336
337         if (no_vsync)
338                 tegra_dc_writel(dc, WRITE_MUX_ACTIVE | READ_MUX_ACTIVE,
339                         DC_CMD_STATE_ACCESS);
340         else
341                 tegra_dc_writel(dc, WRITE_MUX_ASSEMBLY | READ_MUX_ASSEMBLY,
342                         DC_CMD_STATE_ACCESS);
343
344         for (i = 0; i < n; i++) {
345                 struct tegra_dc_win *win = windows[i];
346                 bool scan_column = 0;
347                 fixed20_12 h_offset, v_offset;
348                 bool invert_h = (win->flags & TEGRA_WIN_FLAG_INVERT_H) != 0;
349                 bool invert_v = (win->flags & TEGRA_WIN_FLAG_INVERT_V) != 0;
350                 bool yuv = tegra_dc_is_yuv(win->fmt);
351                 bool yuvp = tegra_dc_is_yuv_planar(win->fmt);
352                 unsigned Bpp = tegra_dc_fmt_bpp(win->fmt) / 8;
353                 /* Bytes per pixel of bandwidth, used for dda_inc calculation */
354                 unsigned Bpp_bw = Bpp * (yuvp ? 2 : 1);
355                 const bool filter_h = win_use_h_filter(dc, win);
356                 const bool filter_v = win_use_v_filter(dc, win);
357 #if defined(CONFIG_TEGRA_DC_SCAN_COLUMN)
358                 scan_column = (win->flags & TEGRA_WIN_FLAG_SCAN_COLUMN);
359 #endif
360
361                 /* Update blender */
362                 if ((win->z != dc->blend.z[win->idx]) ||
363                         ((win->flags & TEGRA_WIN_BLEND_FLAGS_MASK) !=
364                         dc->blend.flags[win->idx])) {
365                         dc->blend.z[win->idx] = win->z;
366                         dc->blend.flags[win->idx] =
367                                 win->flags & TEGRA_WIN_BLEND_FLAGS_MASK;
368                         if (tegra_dc_feature_is_gen2_blender(dc, win->idx))
369                                 update_blend_seq = true;
370                         else
371                                 update_blend_par = true;
372                 }
373
374                 tegra_dc_writel(dc, WINDOW_A_SELECT << win->idx,
375                                 DC_CMD_DISPLAY_WINDOW_HEADER);
376
377                 if (!no_vsync)
378                         update_mask |= WIN_A_ACT_REQ << win->idx;
379
380                 if (!WIN_IS_ENABLED(win)) {
381                         dc->windows[i].dirty = 1;
382                         tegra_dc_writel(dc, 0, DC_WIN_WIN_OPTIONS);
383                         continue;
384                 }
385
386                 tegra_dc_writel(dc, win->fmt & 0x1f, DC_WIN_COLOR_DEPTH);
387                 tegra_dc_writel(dc, win->fmt >> 6, DC_WIN_BYTE_SWAP);
388
389                 tegra_dc_writel(dc,
390                         V_POSITION(win->out_y) | H_POSITION(win->out_x),
391                         DC_WIN_POSITION);
392
393                 /* Check scan_column flag to set window size and scaling. */
394                 win_options = WIN_ENABLE;
395                 if (scan_column) {
396                         win_options |= WIN_SCAN_COLUMN;
397                         win_options |= H_FILTER_ENABLE(filter_v);
398                         win_options |= V_FILTER_ENABLE(filter_h);
399                         val = V_SIZE(win->out_w) | H_SIZE(win->out_h);
400                 } else {
401                         win_options |= H_FILTER_ENABLE(filter_h);
402                         win_options |= V_FILTER_ENABLE(filter_v);
403                         val = V_SIZE(win->out_h) | H_SIZE(win->out_w);
404                 }
405                 tegra_dc_writel(dc, val, DC_WIN_SIZE);
406
407                 /* Update scaling registers if window supports scaling. */
408                 if (likely(tegra_dc_feature_has_scaling(dc, win->idx)))
409                         tegra_dc_update_scaling(dc, win, Bpp, Bpp_bw,
410                                                                 scan_column);
411
412 #if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
413                 tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE);
414                 tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE);
415 #endif
416                 tegra_dc_writel(dc, (unsigned long)win->phys_addr,
417                         DC_WINBUF_START_ADDR);
418
419                 if (!yuvp) {
420                         tegra_dc_writel(dc, win->stride, DC_WIN_LINE_STRIDE);
421                 } else {
422                         tegra_dc_writel(dc,
423                                 (unsigned long)win->phys_addr_u,
424                                 DC_WINBUF_START_ADDR_U);
425                         tegra_dc_writel(dc,
426                                 (unsigned long)win->phys_addr_v,
427                                 DC_WINBUF_START_ADDR_V);
428                         tegra_dc_writel(dc,
429                                 LINE_STRIDE(win->stride) |
430                                 UV_LINE_STRIDE(win->stride_uv),
431                                 DC_WIN_LINE_STRIDE);
432                 }
433
434                 h_offset = win->x;
435                 if (invert_h) {
436                         h_offset.full += win->w.full - dfixed_const(1);
437                 }
438
439                 v_offset = win->y;
440                 if (invert_v) {
441                         v_offset.full += win->h.full - dfixed_const(1);
442                 }
443
444                 tegra_dc_writel(dc, dfixed_trunc(h_offset) * Bpp,
445                                 DC_WINBUF_ADDR_H_OFFSET);
446                 tegra_dc_writel(dc, dfixed_trunc(v_offset),
447                                 DC_WINBUF_ADDR_V_OFFSET);
448
449                 if (tegra_dc_feature_has_tiling(dc, win->idx)) {
450                         if (WIN_IS_TILED(win))
451                                 tegra_dc_writel(dc,
452                                         DC_WIN_BUFFER_ADDR_MODE_TILE |
453                                         DC_WIN_BUFFER_ADDR_MODE_TILE_UV,
454                                         DC_WIN_BUFFER_ADDR_MODE);
455                         else
456                                 tegra_dc_writel(dc,
457                                         DC_WIN_BUFFER_ADDR_MODE_LINEAR |
458                                         DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV,
459                                         DC_WIN_BUFFER_ADDR_MODE);
460                 }
461
462                 if (yuv)
463                         win_options |= CSC_ENABLE;
464                 else if (tegra_dc_fmt_bpp(win->fmt) < 24)
465                         win_options |= COLOR_EXPAND;
466
467                 if (win->ppflags & TEGRA_WIN_PPFLAG_CP_ENABLE)
468                         win_options |= CP_ENABLE;
469
470                 win_options |= H_DIRECTION_DECREMENT(invert_h);
471                 win_options |= V_DIRECTION_DECREMENT(invert_v);
472
473                 tegra_dc_writel(dc, win_options, DC_WIN_WIN_OPTIONS);
474
475 #ifdef CONFIG_ARCH_TEGRA_3x_SOC
476                 if (win->global_alpha == 255)
477                         tegra_dc_writel(dc, 0, DC_WIN_GLOBAL_ALPHA);
478                 else
479                         tegra_dc_writel(dc, GLOBAL_ALPHA_ENABLE |
480                                 win->global_alpha, DC_WIN_GLOBAL_ALPHA);
481 #endif
482
483                 win->dirty = no_vsync ? 0 : 1;
484
485                 dev_dbg(&dc->ndev->dev, "%s():idx=%d z=%d x=%d y=%d w=%d h=%d "
486                         "out_x=%u out_y=%u out_w=%u out_h=%u "
487                         "fmt=%d yuvp=%d Bpp=%u filter_h=%d filter_v=%d",
488                         __func__, win->idx, win->z,
489                         dfixed_trunc(win->x), dfixed_trunc(win->y),
490                         dfixed_trunc(win->w), dfixed_trunc(win->h),
491                         win->out_x, win->out_y, win->out_w, win->out_h,
492                         win->fmt, yuvp, Bpp, filter_h, filter_v);
493                 trace_printk("%s:win%u in:%ux%u out:%ux%u fmt=%d\n",
494                         dc->ndev->name, win->idx, dfixed_trunc(win->w),
495                         dfixed_trunc(win->h), win->out_w, win->out_h, win->fmt);
496         }
497
498         if (update_blend_par || update_blend_seq) {
499                 if (update_blend_par)
500                         tegra_dc_blend_parallel(dc, &dc->blend);
501                 if (update_blend_seq)
502                         tegra_dc_blend_sequential(dc, &dc->blend);
503                 for (i = 0; i < DC_N_WINDOWS; i++) {
504                         if (!no_vsync)
505                                 dc->windows[i].dirty = 1;
506                         update_mask |= WIN_A_ACT_REQ << i;
507                 }
508         }
509
510         tegra_dc_set_dynamic_emc(windows, n);
511
512         tegra_dc_writel(dc, update_mask << 8, DC_CMD_STATE_CONTROL);
513
514         tegra_dc_writel(dc, FRAME_END_INT | V_BLANK_INT, DC_CMD_INT_STATUS);
515         if (!no_vsync) {
516                 set_bit(V_BLANK_FLIP, &dc->vblank_ref_count);
517                 tegra_dc_unmask_interrupt(dc,
518                         FRAME_END_INT | V_BLANK_INT | ALL_UF_INT);
519         } else {
520                 clear_bit(V_BLANK_FLIP, &dc->vblank_ref_count);
521                 tegra_dc_mask_interrupt(dc, V_BLANK_INT | ALL_UF_INT);
522                 if (!atomic_read(&frame_end_ref))
523                         tegra_dc_mask_interrupt(dc, FRAME_END_INT);
524         }
525
526         if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
527                 schedule_delayed_work(&dc->one_shot_work,
528                                 msecs_to_jiffies(dc->one_shot_delay_ms));
529
530         /* update EMC clock if calculated bandwidth has changed */
531         tegra_dc_program_bandwidth(dc, false);
532
533         if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
534                 update_mask |= NC_HOST_TRIG;
535
536         tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL);
537         trace_printk("%s:update_mask=%#lx\n", dc->ndev->name, update_mask);
538
539         tegra_dc_release_dc_out(dc);
540         mutex_unlock(&dc->lock);
541         if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
542                 mutex_unlock(&dc->one_shot_lock);
543
544         return 0;
545 }
546 EXPORT_SYMBOL(tegra_dc_update_windows);
547
548 void tegra_dc_trigger_windows(struct tegra_dc *dc)
549 {
550         u32 val, i;
551         u32 completed = 0;
552         u32 dirty = 0;
553
554         val = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
555         for (i = 0; i < DC_N_WINDOWS; i++) {
556 #ifdef CONFIG_TEGRA_SIMULATION_PLATFORM
557                 /* FIXME: this is not needed when the simulator
558                    clears WIN_x_UPDATE bits as in HW */
559                 dc->windows[i].dirty = 0;
560                 completed = 1;
561 #else
562                 if (!(val & (WIN_A_ACT_REQ << i))) {
563                         dc->windows[i].dirty = 0;
564                         completed = 1;
565                 } else {
566                         dirty = 1;
567                 }
568 #endif
569         }
570
571         if (!dirty) {
572                 if (!(dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
573                         && !atomic_read(&frame_end_ref))
574                         tegra_dc_mask_interrupt(dc, FRAME_END_INT);
575         }
576
577         if (completed)
578                 wake_up(&dc->wq);
579 }
580