]> rtime.felk.cvut.cz Git - vajnamar/linux-xlnx.git/blob - drivers/gpu/drm/xilinx/xilinx_vtc.c
drm: xilinx: vtc: adding interlaced support
[vajnamar/linux-xlnx.git] / drivers / gpu / drm / xilinx / xilinx_vtc.c
1 /*
2  * Video Timing Controller support for Xilinx DRM KMS
3  *
4  *  Copyright (C) 2013 Xilinx, Inc.
5  *
6  *  Author: Hyun Woo Kwon <hyunk@xilinx.com>
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 <drm/drmP.h>
19
20 #include <linux/device.h>
21 #include <linux/err.h>
22 #include <linux/interrupt.h>
23 #include <linux/io.h>
24 #include <linux/of_address.h>
25 #include <linux/of_irq.h>
26 #include <linux/slab.h>
27
28 #include <video/videomode.h>
29
30 #include "xilinx_drm_drv.h"
31 #include "xilinx_vtc.h"
32
33 /* register offsets */
34 #define VTC_CTL         0x000   /* control */
35 #define VTC_STATS       0x004   /* status */
36 #define VTC_ERROR       0x008   /* error */
37
38 #define VTC_GASIZE      0x060   /* generator active size */
39 #define VTC_GENC        0x068   /* generator encoding */
40 #define VTC_GPOL        0x06c   /* generator polarity */
41 #define VTC_GHSIZE      0x070   /* generator frame horizontal size */
42 #define VTC_GVSIZE      0x074   /* generator frame vertical size */
43 #define VTC_GHSYNC      0x078   /* generator horizontal sync */
44 #define VTC_GVBHOFF_F0  0x07c   /* generator Field 0 vblank horizontal offset */
45 #define VTC_GVSYNC_F0   0x080   /* generator Field 0 vertical sync */
46 #define VTC_GVSHOFF_F0  0x084   /* generator Field 0 vsync horizontal offset */
47 #define VTC_GVBHOFF_F1  0x088   /* generator Field 1 vblank horizontal offset */
48 #define VTC_GVSYNC_F1   0x08C   /* generator Field 1 vertical sync */
49 #define VTC_GVSHOFF_F1  0x090   /* generator Field 1 vsync horizontal offset */
50
51 #define VTC_RESET       0x000   /* reset register */
52 #define VTC_ISR         0x004   /* interrupt status register */
53 #define VTC_IER         0x00c   /* interrupt enable register */
54
55 /* control register bit */
56 #define VTC_CTL_FIP     (1 << 6)        /* field id output polarity */
57 #define VTC_CTL_ACP     (1 << 5)        /* active chroma output polarity */
58 #define VTC_CTL_AVP     (1 << 4)        /* active video output polarity */
59 #define VTC_CTL_HSP     (1 << 3)        /* hori sync output polarity */
60 #define VTC_CTL_VSP     (1 << 2)        /* vert sync output polarity */
61 #define VTC_CTL_HBP     (1 << 1)        /* hori blank output polarity */
62 #define VTC_CTL_VBP     (1 << 0)        /* vert blank output polarity */
63
64 #define VTC_CTL_FIPSS   (1 << 26)       /* field id output polarity source */
65 #define VTC_CTL_ACPSS   (1 << 25)       /* active chroma out polarity source */
66 #define VTC_CTL_AVPSS   (1 << 24)       /* active video out polarity source */
67 #define VTC_CTL_HSPSS   (1 << 23)       /* hori sync out polarity source */
68 #define VTC_CTL_VSPSS   (1 << 22)       /* vert sync out polarity source */
69 #define VTC_CTL_HBPSS   (1 << 21)       /* hori blank out polarity source */
70 #define VTC_CTL_VBPSS   (1 << 20)       /* vert blank out polarity source */
71
72 #define VTC_CTL_VCSS    (1 << 18)       /* chroma source select */
73 #define VTC_CTL_VASS    (1 << 17)       /* vertical offset source select */
74 #define VTC_CTL_VBSS    (1 << 16)       /* vertical sync end source select */
75 #define VTC_CTL_VSSS    (1 << 15)       /* vertical sync start source select */
76 #define VTC_CTL_VFSS    (1 << 14)       /* vertical active size source select */
77 #define VTC_CTL_VTSS    (1 << 13)       /* vertical frame size source select */
78
79 #define VTC_CTL_HBSS    (1 << 11)       /* horiz sync end source select */
80 #define VTC_CTL_HSSS    (1 << 10)       /* horiz sync start source select */
81 #define VTC_CTL_HFSS    (1 << 9)        /* horiz active size source select */
82 #define VTC_CTL_HTSS    (1 << 8)        /* horiz frame size source select */
83
84 #define VTC_CTL_GE      (1 << 2)        /* vtc generator enable */
85 #define VTC_CTL_RU      (1 << 1)        /* vtc register update */
86
87 /* vtc generator horizontal 1 */
88 #define VTC_GH1_BPSTART_MASK   0x1fff0000       /* horiz back porch start */
89 #define VTC_GH1_BPSTART_SHIFT  16
90 #define VTC_GH1_SYNCSTART_MASK 0x00001fff
91
92 /* vtc generator vertical 1 (filed 0) */
93 #define VTC_GV1_BPSTART_MASK   0x1fff0000       /* vertical back porch start */
94 #define VTC_GV1_BPSTART_SHIFT  16
95 #define VTC_GV1_SYNCSTART_MASK 0x00001fff
96
97 /* vtc generator/detector vblank/vsync horizontal offset registers */
98 #define VTC_XVXHOX_HEND_MASK    0x1fff0000      /* horiz offset end */
99 #define VTC_XVXHOX_HEND_SHIFT   16              /* horiz offset end shift */
100 #define VTC_XVXHOX_HSTART_MASK  0x00001fff      /* horiz offset start */
101
102 /* reset register bit definition */
103 #define VTC_RESET_RESET         (1 << 31)       /* Software Reset */
104
105 /* interrupt status/enable register bit definition */
106 #define VTC_IXR_FSYNC15         (1 << 31)       /* frame sync interrupt 15 */
107 #define VTC_IXR_FSYNC14         (1 << 30)       /* frame sync interrupt 14 */
108 #define VTC_IXR_FSYNC13         (1 << 29)       /* frame sync interrupt 13 */
109 #define VTC_IXR_FSYNC12         (1 << 28)       /* frame sync interrupt 12 */
110 #define VTC_IXR_FSYNC11         (1 << 27)       /* frame sync interrupt 11 */
111 #define VTC_IXR_FSYNC10         (1 << 26)       /* frame sync interrupt 10 */
112 #define VTC_IXR_FSYNC09         (1 << 25)       /* frame sync interrupt 09 */
113 #define VTC_IXR_FSYNC08         (1 << 24)       /* frame sync interrupt 08 */
114 #define VTC_IXR_FSYNC07         (1 << 23)       /* frame sync interrupt 07 */
115 #define VTC_IXR_FSYNC06         (1 << 22)       /* frame sync interrupt 06 */
116 #define VTC_IXR_FSYNC05         (1 << 21)       /* frame sync interrupt 05 */
117 #define VTC_IXR_FSYNC04         (1 << 20)       /* frame sync interrupt 04 */
118 #define VTC_IXR_FSYNC03         (1 << 19)       /* frame sync interrupt 03 */
119 #define VTC_IXR_FSYNC02         (1 << 18)       /* frame sync interrupt 02 */
120 #define VTC_IXR_FSYNC01         (1 << 17)       /* frame sync interrupt 01 */
121 #define VTC_IXR_FSYNC00         (1 << 16)       /* frame sync interrupt 00 */
122 #define VTC_IXR_FSYNCALL_MASK   (VTC_IXR_FSYNC00 |      \
123                                 VTC_IXR_FSYNC01 |       \
124                                 VTC_IXR_FSYNC02 |       \
125                                 VTC_IXR_FSYNC03 |       \
126                                 VTC_IXR_FSYNC04 |       \
127                                 VTC_IXR_FSYNC05 |       \
128                                 VTC_IXR_FSYNC06 |       \
129                                 VTC_IXR_FSYNC07 |       \
130                                 VTC_IXR_FSYNC08 |       \
131                                 VTC_IXR_FSYNC09 |       \
132                                 VTC_IXR_FSYNC10 |       \
133                                 VTC_IXR_FSYNC11 |       \
134                                 VTC_IXR_FSYNC12 |       \
135                                 VTC_IXR_FSYNC13 |       \
136                                 VTC_IXR_FSYNC14 |       \
137                                 VTC_IXR_FSYNC15)
138
139 #define VTC_IXR_G_AV            (1 << 13)       /* generator actv video intr */
140 #define VTC_IXR_G_VBLANK        (1 << 12)       /* generator vblank interrupt */
141 #define VTC_IXR_G_ALL_MASK      (VTC_IXR_G_AV | \
142                                  VTC_IXR_G_VBLANK)      /* all generator intr */
143
144 #define VTC_IXR_D_AV            (1 << 11)       /* detector active video intr */
145 #define VTC_IXR_D_VBLANK        (1 << 10)       /* detector vblank interrupt */
146 #define VTC_IXR_D_ALL_MASK      (VTC_IXR_D_AV | \
147                                 VTC_IXR_D_VBLANK)       /* all detector intr */
148
149 #define VTC_IXR_LOL             (1 << 9)        /* lock loss */
150 #define VTC_IXR_LO              (1 << 8)        /* lock  */
151 #define VTC_IXR_LOCKALL_MASK    (VTC_IXR_LOL |  \
152                                 VTC_IXR_LO)     /* all signal lock intr */
153
154 #define VTC_IXR_ACL     (1 << 21)       /* active chroma signal lock */
155 #define VTC_IXR_AVL     (1 << 20)       /* active video signal lock */
156 #define VTC_IXR_HSL     (1 << 19)       /* horizontal sync signal lock */
157 #define VTC_IXR_VSL     (1 << 18)       /* vertical sync signal lock */
158 #define VTC_IXR_HBL     (1 << 17)       /* horizontal blank signal lock */
159 #define VTC_IXR_VBL     (1 << 16)       /* vertical blank signal lock */
160
161 #define VTC_GENC_INTERL BIT(6)          /* Interlaced bit in VTC_GENC */
162 /* mask for all interrupts */
163 #define VTC_IXR_ALLINTR_MASK    (VTC_IXR_FSYNCALL_MASK |        \
164                                 VTC_IXR_G_ALL_MASK |            \
165                                 VTC_IXR_D_ALL_MASK |            \
166                                 VTC_IXR_LOCKALL_MASK)
167 /**
168  * struct xilinx_vtc - Xilinx VTC object
169  *
170  * @base: base addr
171  * @irq: irq
172  * @vblank_fn: vblank handler func
173  * @vblank_data: vblank handler private data
174  */
175 struct xilinx_vtc {
176         void __iomem *base;
177         int irq;
178         void (*vblank_fn)(void *);
179         void *vblank_data;
180 };
181
182 /**
183  * struct xilinx_vtc_polarity - vtc polarity config
184  *
185  * @active_chroma: active chroma polarity
186  * @active_video: active video polarity
187  * @field_id: field ID polarity
188  * @vblank: vblank polarity
189  * @vsync: vsync polarity
190  * @hblank: hblank polarity
191  * @hsync: hsync polarity
192  */
193 struct xilinx_vtc_polarity {
194         u8 active_chroma;
195         u8 active_video;
196         u8 field_id;
197         u8 vblank;
198         u8 vsync;
199         u8 hblank;
200         u8 hsync;
201 };
202
203 /**
204  * struct xilinx_vtc_hori_offset - vtc horizontal offset config
205  *
206  * @v0blank_hori_start: vblank horizontal start (field 0)
207  * @v0blank_hori_end: vblank horizontal end (field 0)
208  * @v0sync_hori_start: vsync horizontal start (field 0)
209  * @v0sync_hori_end: vsync horizontal end (field 0)
210  * @v1blank_hori_start: vblank horizontal start (field 1)
211  * @v1blank_hori_end: vblank horizontal end (field 1)
212  * @v1sync_hori_start: vsync horizontal start (field 1)
213  * @v1sync_hori_end: vsync horizontal end (field 1)
214  */
215 struct xilinx_vtc_hori_offset {
216         u16 v0blank_hori_start;
217         u16 v0blank_hori_end;
218         u16 v0sync_hori_start;
219         u16 v0sync_hori_end;
220         u16 v1blank_hori_start;
221         u16 v1blank_hori_end;
222         u16 v1sync_hori_start;
223         u16 v1sync_hori_end;
224 };
225
226 /**
227  * struct xilinx_vtc_src_config - vtc source config
228  *
229  * @field_id_pol: filed id polarity source
230  * @active_chroma_pol: active chroma polarity source
231  * @active_video_pol: active video polarity source
232  * @hsync_pol: hsync polarity source
233  * @vsync_pol: vsync polarity source
234  * @hblank_pol: hblnak polarity source
235  * @vblank_pol: vblank polarity source
236  * @vchroma: vchroma polarity start source
237  * @vactive: vactive size source
238  * @vbackporch: vbackporch start source
239  * @vsync: vsync start source
240  * @vfrontporch: vfrontporch start source
241  * @vtotal: vtotal size source
242  * @hactive: hactive start source
243  * @hbackporch: hbackporch start source
244  * @hsync: hsync start source
245  * @hfrontporch: hfrontporch start source
246  * @htotal: htotal size source
247  */
248 struct xilinx_vtc_src_config {
249         u8 field_id_pol;
250         u8 active_chroma_pol;
251         u8 active_video_pol;
252         u8 hsync_pol;
253         u8 vsync_pol;
254         u8 hblank_pol;
255         u8 vblank_pol;
256
257         u8 vchroma;
258         u8 vactive;
259         u8 vbackporch;
260         u8 vsync;
261         u8 vfrontporch;
262         u8 vtotal;
263
264         u8 hactive;
265         u8 hbackporch;
266         u8 hsync;
267         u8 hfrontporch;
268         u8 htotal;
269 };
270
271 /* configure polarity of signals */
272 static void xilinx_vtc_config_polarity(struct xilinx_vtc *vtc,
273                                        struct xilinx_vtc_polarity *polarity)
274 {
275         u32 reg;
276
277         reg = xilinx_drm_readl(vtc->base, VTC_GPOL);
278
279         if (polarity->active_chroma)
280                 reg |= VTC_CTL_ACP;
281         if (polarity->active_video)
282                 reg |= VTC_CTL_AVP;
283         if (polarity->field_id)
284                 reg |= VTC_CTL_FIP;
285         if (polarity->vblank)
286                 reg |= VTC_CTL_VBP;
287         if (polarity->vsync)
288                 reg |= VTC_CTL_VSP;
289         if (polarity->hblank)
290                 reg |= VTC_CTL_HBP;
291         if (polarity->hsync)
292                 reg |= VTC_CTL_HSP;
293
294         xilinx_drm_writel(vtc->base, VTC_GPOL, reg);
295 }
296
297 /* configure horizontal offset */
298 static void
299 xilinx_vtc_config_hori_offset(struct xilinx_vtc *vtc,
300                               struct xilinx_vtc_hori_offset *hori_offset)
301 {
302         u32 reg;
303
304         /* Calculate and update Generator VBlank Hori field 0 */
305         reg = hori_offset->v0blank_hori_start & VTC_XVXHOX_HSTART_MASK;
306         reg |= (hori_offset->v0blank_hori_end << VTC_XVXHOX_HEND_SHIFT) &
307                 VTC_XVXHOX_HEND_MASK;
308         xilinx_drm_writel(vtc->base, VTC_GVBHOFF_F0, reg);
309
310         /* Calculate and update Generator VSync Hori field 0 */
311         reg = hori_offset->v0sync_hori_start & VTC_XVXHOX_HSTART_MASK;
312         reg |= (hori_offset->v0sync_hori_end << VTC_XVXHOX_HEND_SHIFT) &
313                 VTC_XVXHOX_HEND_MASK;
314         xilinx_drm_writel(vtc->base, VTC_GVSHOFF_F0, reg);
315
316         /* Calculate and update Generator VBlank Hori field 1 */
317         reg = hori_offset->v1blank_hori_start & VTC_XVXHOX_HSTART_MASK;
318         reg |= (hori_offset->v1blank_hori_end << VTC_XVXHOX_HEND_SHIFT) &
319                 VTC_XVXHOX_HEND_MASK;
320         xilinx_drm_writel(vtc->base, VTC_GVBHOFF_F1, reg);
321
322         /* Calculate and update Generator VBlank Hori field 1 */
323         reg =  hori_offset->v1sync_hori_start & VTC_XVXHOX_HSTART_MASK;
324         reg |= (hori_offset->v1sync_hori_end << VTC_XVXHOX_HEND_SHIFT) &
325                 VTC_XVXHOX_HEND_MASK;
326         xilinx_drm_writel(vtc->base, VTC_GVSHOFF_F1, reg);
327
328 }
329
330 /* configure source */
331 static void xilinx_vtc_config_src(struct xilinx_vtc *vtc,
332                                   struct xilinx_vtc_src_config *src_config)
333 {
334         u32 reg;
335
336         reg = xilinx_drm_readl(vtc->base, VTC_CTL);
337
338         if (src_config->field_id_pol)
339                 reg |= VTC_CTL_FIPSS;
340         if (src_config->active_chroma_pol)
341                 reg |= VTC_CTL_ACPSS;
342         if (src_config->active_video_pol)
343                 reg |= VTC_CTL_AVPSS;
344         if (src_config->hsync_pol)
345                 reg |= VTC_CTL_HSPSS;
346         if (src_config->vsync_pol)
347                 reg |= VTC_CTL_VSPSS;
348         if (src_config->hblank_pol)
349                 reg |= VTC_CTL_HBPSS;
350         if (src_config->vblank_pol)
351                 reg |= VTC_CTL_VBPSS;
352
353         if (src_config->vchroma)
354                 reg |= VTC_CTL_VCSS;
355         if (src_config->vactive)
356                 reg |= VTC_CTL_VASS;
357         if (src_config->vbackporch)
358                 reg |= VTC_CTL_VBSS;
359         if (src_config->vsync)
360                 reg |= VTC_CTL_VSSS;
361         if (src_config->vfrontporch)
362                 reg |= VTC_CTL_VFSS;
363         if (src_config->vtotal)
364                 reg |= VTC_CTL_VTSS;
365
366         if (src_config->hbackporch)
367                 reg |= VTC_CTL_HBSS;
368         if (src_config->hsync)
369                 reg |= VTC_CTL_HSSS;
370         if (src_config->hfrontporch)
371                 reg |= VTC_CTL_HFSS;
372         if (src_config->htotal)
373                 reg |= VTC_CTL_HTSS;
374
375         xilinx_drm_writel(vtc->base, VTC_CTL, reg);
376 }
377
378 /* enable vtc */
379 void xilinx_vtc_enable(struct xilinx_vtc *vtc)
380 {
381         u32 reg;
382
383         /* enable a generator only for now */
384         reg = xilinx_drm_readl(vtc->base, VTC_CTL);
385         xilinx_drm_writel(vtc->base, VTC_CTL, reg | VTC_CTL_GE);
386 }
387
388 /* disable vtc */
389 void xilinx_vtc_disable(struct xilinx_vtc *vtc)
390 {
391         u32 reg;
392
393         /* disable a generator only for now */
394         reg = xilinx_drm_readl(vtc->base, VTC_CTL);
395         xilinx_drm_writel(vtc->base, VTC_CTL, reg & ~VTC_CTL_GE);
396 }
397
398 /* configure vtc signals */
399 void xilinx_vtc_config_sig(struct xilinx_vtc *vtc,
400                            struct videomode *vm)
401 {
402         u32 reg;
403         u32 htotal, hactive, hsync_start, hbackporch_start;
404         u32 vtotal, vactive, vsync_start, vbackporch_start;
405         struct xilinx_vtc_hori_offset hori_offset;
406         struct xilinx_vtc_polarity polarity;
407         struct xilinx_vtc_src_config src;
408
409         reg = xilinx_drm_readl(vtc->base, VTC_CTL);
410         xilinx_drm_writel(vtc->base, VTC_CTL, reg & ~VTC_CTL_RU);
411
412         htotal = vm->hactive + vm->hfront_porch + vm->hsync_len +
413                  vm->hback_porch;
414         vtotal = vm->vactive + vm->vfront_porch + vm->vsync_len +
415                  vm->vback_porch;
416
417         hactive = vm->hactive;
418         vactive = vm->vactive;
419
420         hsync_start = vm->hactive + vm->hfront_porch;
421         vsync_start = vm->vactive + vm->vfront_porch;
422
423         hbackporch_start = hsync_start + vm->hsync_len;
424         vbackporch_start = vsync_start + vm->vsync_len;
425
426         reg = htotal & 0x1fff;
427         xilinx_drm_writel(vtc->base, VTC_GHSIZE, reg);
428
429         reg = vtotal & 0x1fff;
430         reg |= reg << VTC_GV1_BPSTART_SHIFT;
431         xilinx_drm_writel(vtc->base, VTC_GVSIZE, reg);
432
433         DRM_DEBUG_DRIVER("ht: %d, vt: %d\n", htotal, vtotal);
434
435         reg = hactive & 0x1fff;
436         reg |= (vactive & 0x1fff) << 16;
437         xilinx_drm_writel(vtc->base, VTC_GASIZE, reg);
438
439         DRM_DEBUG_DRIVER("ha: %d, va: %d\n", hactive, vactive);
440
441         reg = hsync_start & VTC_GH1_SYNCSTART_MASK;
442         reg |= (hbackporch_start << VTC_GH1_BPSTART_SHIFT) &
443                VTC_GH1_BPSTART_MASK;
444         xilinx_drm_writel(vtc->base, VTC_GHSYNC, reg);
445
446         DRM_DEBUG_DRIVER("hs: %d, hb: %d\n", hsync_start, hbackporch_start);
447
448         reg = vsync_start & VTC_GV1_SYNCSTART_MASK;
449         reg |= (vbackporch_start << VTC_GV1_BPSTART_SHIFT) &
450                VTC_GV1_BPSTART_MASK;
451         xilinx_drm_writel(vtc->base, VTC_GVSYNC_F0, reg);
452         DRM_DEBUG_DRIVER("vs: %d, vb: %d\n", vsync_start, vbackporch_start);
453
454         hori_offset.v0blank_hori_start = hactive;
455         hori_offset.v0blank_hori_end = hactive;
456         hori_offset.v0sync_hori_start = hsync_start;
457         hori_offset.v0sync_hori_end = hsync_start;
458
459         hori_offset.v1blank_hori_start = hactive;
460         hori_offset.v1blank_hori_end = hactive;
461
462         if (vm->flags & DISPLAY_FLAGS_INTERLACED) {
463                 hori_offset.v1sync_hori_start = hsync_start - (htotal / 2);
464                 hori_offset.v1sync_hori_end = hsync_start - (htotal / 2);
465                 xilinx_drm_writel(vtc->base, VTC_GVSYNC_F1, reg);
466                 reg = xilinx_drm_readl(vtc->base, VTC_GENC) | VTC_GENC_INTERL;
467                 xilinx_drm_writel(vtc->base, VTC_GENC, reg);
468         } else {
469                 hori_offset.v1sync_hori_start = hsync_start;
470                 hori_offset.v1sync_hori_end = hsync_start;
471                 reg = xilinx_drm_readl(vtc->base, VTC_GENC) & ~VTC_GENC_INTERL;
472                 xilinx_drm_writel(vtc->base, VTC_GENC, reg);
473         }
474
475         xilinx_vtc_config_hori_offset(vtc, &hori_offset);
476         /* set up polarity */
477         memset(&polarity, 0x0, sizeof(polarity));
478         polarity.hsync = 1;
479         polarity.vsync = 1;
480         polarity.hblank = 1;
481         polarity.vblank = 1;
482         polarity.active_video = 1;
483         polarity.active_chroma = 1;
484         polarity.field_id = 1;
485         xilinx_vtc_config_polarity(vtc, &polarity);
486
487         /* set up src config */
488         memset(&src, 0x0, sizeof(src));
489         src.vchroma = 1;
490         src.vactive = 1;
491         src.vbackporch = 1;
492         src.vsync = 1;
493         src.vfrontporch = 1;
494         src.vtotal = 1;
495         src.hactive = 1;
496         src.hbackporch = 1;
497         src.hsync = 1;
498         src.hfrontporch = 1;
499         src.htotal = 1;
500         xilinx_vtc_config_src(vtc, &src);
501
502         reg = xilinx_drm_readl(vtc->base, VTC_CTL);
503         xilinx_drm_writel(vtc->base, VTC_CTL, reg | VTC_CTL_RU);
504 }
505
506 /* reset vtc */
507 void xilinx_vtc_reset(struct xilinx_vtc *vtc)
508 {
509         u32 reg;
510
511         xilinx_drm_writel(vtc->base, VTC_RESET, VTC_RESET_RESET);
512
513         /* enable register update */
514         reg = xilinx_drm_readl(vtc->base, VTC_CTL);
515         xilinx_drm_writel(vtc->base, VTC_CTL, reg | VTC_CTL_RU);
516 }
517
518 /* enable interrupt */
519 static inline void xilinx_vtc_intr_enable(struct xilinx_vtc *vtc, u32 intr)
520 {
521         xilinx_drm_writel(vtc->base, VTC_IER, (intr & VTC_IXR_ALLINTR_MASK) |
522                           xilinx_drm_readl(vtc->base, VTC_IER));
523 }
524
525 /* disable interrupt */
526 static inline void xilinx_vtc_intr_disable(struct xilinx_vtc *vtc, u32 intr)
527 {
528         xilinx_drm_writel(vtc->base, VTC_IER, ~(intr & VTC_IXR_ALLINTR_MASK) &
529                           xilinx_drm_readl(vtc->base, VTC_IER));
530 }
531
532 /* get interrupt */
533 static inline u32 xilinx_vtc_intr_get(struct xilinx_vtc *vtc)
534 {
535         return xilinx_drm_readl(vtc->base, VTC_IER) &
536                xilinx_drm_readl(vtc->base, VTC_ISR) & VTC_IXR_ALLINTR_MASK;
537 }
538
539 /* clear interrupt */
540 static inline void xilinx_vtc_intr_clear(struct xilinx_vtc *vtc, u32 intr)
541 {
542         xilinx_drm_writel(vtc->base, VTC_ISR, intr & VTC_IXR_ALLINTR_MASK);
543 }
544
545 /* interrupt handler */
546 static irqreturn_t xilinx_vtc_intr_handler(int irq, void *data)
547 {
548         struct xilinx_vtc *vtc = data;
549
550         u32 intr = xilinx_vtc_intr_get(vtc);
551
552         if (!intr)
553                 return IRQ_NONE;
554
555         if ((intr & VTC_IXR_G_VBLANK) && (vtc->vblank_fn))
556                 vtc->vblank_fn(vtc->vblank_data);
557
558         xilinx_vtc_intr_clear(vtc, intr);
559
560         return IRQ_HANDLED;
561 }
562
563 /* enable vblank interrupt */
564 void xilinx_vtc_enable_vblank_intr(struct xilinx_vtc *vtc,
565                                    void (*vblank_fn)(void *),
566                                    void *vblank_priv)
567 {
568         vtc->vblank_fn = vblank_fn;
569         vtc->vblank_data = vblank_priv;
570         xilinx_vtc_intr_enable(vtc, VTC_IXR_G_VBLANK);
571 }
572
573 /* disable vblank interrupt */
574 void xilinx_vtc_disable_vblank_intr(struct xilinx_vtc *vtc)
575 {
576         xilinx_vtc_intr_disable(vtc, VTC_IXR_G_VBLANK);
577         vtc->vblank_data = NULL;
578         vtc->vblank_fn = NULL;
579 }
580
581 static const struct of_device_id xilinx_vtc_of_match[] = {
582         { .compatible = "xlnx,v-tc-5.01.a" },
583         { /* end of table */ },
584 };
585
586 /* probe vtc */
587 struct xilinx_vtc *xilinx_vtc_probe(struct device *dev,
588                                     struct device_node *node)
589 {
590         struct xilinx_vtc *vtc;
591         const struct of_device_id *match;
592         struct resource res;
593         int ret;
594
595         match = of_match_node(xilinx_vtc_of_match, node);
596         if (!match) {
597                 dev_err(dev, "failed to match the device node\n");
598                 return ERR_PTR(-ENODEV);
599         }
600
601         vtc = devm_kzalloc(dev, sizeof(*vtc), GFP_KERNEL);
602         if (!vtc)
603                 return ERR_PTR(-ENOMEM);
604
605         ret = of_address_to_resource(node, 0, &res);
606         if (ret) {
607                 dev_err(dev, "failed to of_address_to_resource\n");
608                 return ERR_PTR(ret);
609         }
610
611         vtc->base = devm_ioremap_resource(dev, &res);
612         if (IS_ERR(vtc->base))
613                 return ERR_CAST(vtc->base);
614
615         xilinx_vtc_intr_disable(vtc, VTC_IXR_ALLINTR_MASK);
616         vtc->irq = irq_of_parse_and_map(node, 0);
617         if (vtc->irq > 0) {
618                 ret = devm_request_irq(dev, vtc->irq, xilinx_vtc_intr_handler,
619                                        IRQF_SHARED, "xilinx_vtc", vtc);
620                 if (ret) {
621                         dev_warn(dev, "failed to requet_irq() for vtc\n");
622                         return ERR_PTR(ret);
623                 }
624         }
625
626         xilinx_vtc_reset(vtc);
627
628         return vtc;
629 }