2 * Video Timing Controller support for Xilinx DRM KMS
4 * Copyright (C) 2013 Xilinx, Inc.
6 * Author: Hyun Woo Kwon <hyunk@xilinx.com>
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.
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.
20 #include <linux/device.h>
21 #include <linux/err.h>
22 #include <linux/interrupt.h>
24 #include <linux/of_address.h>
25 #include <linux/of_irq.h>
26 #include <linux/slab.h>
28 #include <video/videomode.h>
30 #include "xilinx_drm_drv.h"
31 #include "xilinx_vtc.h"
33 /* register offsets */
34 #define VTC_CTL 0x000 /* control */
35 #define VTC_STATS 0x004 /* status */
36 #define VTC_ERROR 0x008 /* error */
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 */
51 #define VTC_RESET 0x000 /* reset register */
52 #define VTC_ISR 0x004 /* interrupt status register */
53 #define VTC_IER 0x00c /* interrupt enable register */
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 */
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 */
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 */
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 */
84 #define VTC_CTL_GE (1 << 2) /* vtc generator enable */
85 #define VTC_CTL_RU (1 << 1) /* vtc register update */
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
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
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 */
102 /* reset register bit definition */
103 #define VTC_RESET_RESET (1 << 31) /* Software Reset */
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 | \
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 */
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 */
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 */
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 */
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)
168 * struct xilinx_vtc - Xilinx VTC object
172 * @vblank_fn: vblank handler func
173 * @vblank_data: vblank handler private data
178 void (*vblank_fn)(void *);
183 * struct xilinx_vtc_polarity - vtc polarity config
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
193 struct xilinx_vtc_polarity {
204 * struct xilinx_vtc_hori_offset - vtc horizontal offset config
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)
215 struct xilinx_vtc_hori_offset {
216 u16 v0blank_hori_start;
217 u16 v0blank_hori_end;
218 u16 v0sync_hori_start;
220 u16 v1blank_hori_start;
221 u16 v1blank_hori_end;
222 u16 v1sync_hori_start;
227 * struct xilinx_vtc_src_config - vtc source config
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
248 struct xilinx_vtc_src_config {
250 u8 active_chroma_pol;
271 /* configure polarity of signals */
272 static void xilinx_vtc_config_polarity(struct xilinx_vtc *vtc,
273 struct xilinx_vtc_polarity *polarity)
277 reg = xilinx_drm_readl(vtc->base, VTC_GPOL);
279 if (polarity->active_chroma)
281 if (polarity->active_video)
283 if (polarity->field_id)
285 if (polarity->vblank)
289 if (polarity->hblank)
294 xilinx_drm_writel(vtc->base, VTC_GPOL, reg);
297 /* configure horizontal offset */
299 xilinx_vtc_config_hori_offset(struct xilinx_vtc *vtc,
300 struct xilinx_vtc_hori_offset *hori_offset)
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);
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);
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);
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);
330 /* configure source */
331 static void xilinx_vtc_config_src(struct xilinx_vtc *vtc,
332 struct xilinx_vtc_src_config *src_config)
336 reg = xilinx_drm_readl(vtc->base, VTC_CTL);
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;
353 if (src_config->vchroma)
355 if (src_config->vactive)
357 if (src_config->vbackporch)
359 if (src_config->vsync)
361 if (src_config->vfrontporch)
363 if (src_config->vtotal)
366 if (src_config->hbackporch)
368 if (src_config->hsync)
370 if (src_config->hfrontporch)
372 if (src_config->htotal)
375 xilinx_drm_writel(vtc->base, VTC_CTL, reg);
379 void xilinx_vtc_enable(struct xilinx_vtc *vtc)
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);
389 void xilinx_vtc_disable(struct xilinx_vtc *vtc)
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);
398 /* configure vtc signals */
399 void xilinx_vtc_config_sig(struct xilinx_vtc *vtc,
400 struct videomode *vm)
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;
409 reg = xilinx_drm_readl(vtc->base, VTC_CTL);
410 xilinx_drm_writel(vtc->base, VTC_CTL, reg & ~VTC_CTL_RU);
412 htotal = vm->hactive + vm->hfront_porch + vm->hsync_len +
414 vtotal = vm->vactive + vm->vfront_porch + vm->vsync_len +
417 hactive = vm->hactive;
418 vactive = vm->vactive;
420 hsync_start = vm->hactive + vm->hfront_porch;
421 vsync_start = vm->vactive + vm->vfront_porch;
423 hbackporch_start = hsync_start + vm->hsync_len;
424 vbackporch_start = vsync_start + vm->vsync_len;
426 reg = htotal & 0x1fff;
427 xilinx_drm_writel(vtc->base, VTC_GHSIZE, reg);
429 reg = vtotal & 0x1fff;
430 reg |= reg << VTC_GV1_BPSTART_SHIFT;
431 xilinx_drm_writel(vtc->base, VTC_GVSIZE, reg);
433 DRM_DEBUG_DRIVER("ht: %d, vt: %d\n", htotal, vtotal);
435 reg = hactive & 0x1fff;
436 reg |= (vactive & 0x1fff) << 16;
437 xilinx_drm_writel(vtc->base, VTC_GASIZE, reg);
439 DRM_DEBUG_DRIVER("ha: %d, va: %d\n", hactive, vactive);
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);
446 DRM_DEBUG_DRIVER("hs: %d, hb: %d\n", hsync_start, hbackporch_start);
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);
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;
459 hori_offset.v1blank_hori_start = hactive;
460 hori_offset.v1blank_hori_end = hactive;
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);
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);
475 xilinx_vtc_config_hori_offset(vtc, &hori_offset);
476 /* set up polarity */
477 memset(&polarity, 0x0, sizeof(polarity));
482 polarity.active_video = 1;
483 polarity.active_chroma = 1;
484 polarity.field_id = 1;
485 xilinx_vtc_config_polarity(vtc, &polarity);
487 /* set up src config */
488 memset(&src, 0x0, sizeof(src));
500 xilinx_vtc_config_src(vtc, &src);
502 reg = xilinx_drm_readl(vtc->base, VTC_CTL);
503 xilinx_drm_writel(vtc->base, VTC_CTL, reg | VTC_CTL_RU);
507 void xilinx_vtc_reset(struct xilinx_vtc *vtc)
511 xilinx_drm_writel(vtc->base, VTC_RESET, VTC_RESET_RESET);
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);
518 /* enable interrupt */
519 static inline void xilinx_vtc_intr_enable(struct xilinx_vtc *vtc, u32 intr)
521 xilinx_drm_writel(vtc->base, VTC_IER, (intr & VTC_IXR_ALLINTR_MASK) |
522 xilinx_drm_readl(vtc->base, VTC_IER));
525 /* disable interrupt */
526 static inline void xilinx_vtc_intr_disable(struct xilinx_vtc *vtc, u32 intr)
528 xilinx_drm_writel(vtc->base, VTC_IER, ~(intr & VTC_IXR_ALLINTR_MASK) &
529 xilinx_drm_readl(vtc->base, VTC_IER));
533 static inline u32 xilinx_vtc_intr_get(struct xilinx_vtc *vtc)
535 return xilinx_drm_readl(vtc->base, VTC_IER) &
536 xilinx_drm_readl(vtc->base, VTC_ISR) & VTC_IXR_ALLINTR_MASK;
539 /* clear interrupt */
540 static inline void xilinx_vtc_intr_clear(struct xilinx_vtc *vtc, u32 intr)
542 xilinx_drm_writel(vtc->base, VTC_ISR, intr & VTC_IXR_ALLINTR_MASK);
545 /* interrupt handler */
546 static irqreturn_t xilinx_vtc_intr_handler(int irq, void *data)
548 struct xilinx_vtc *vtc = data;
550 u32 intr = xilinx_vtc_intr_get(vtc);
555 if ((intr & VTC_IXR_G_VBLANK) && (vtc->vblank_fn))
556 vtc->vblank_fn(vtc->vblank_data);
558 xilinx_vtc_intr_clear(vtc, intr);
563 /* enable vblank interrupt */
564 void xilinx_vtc_enable_vblank_intr(struct xilinx_vtc *vtc,
565 void (*vblank_fn)(void *),
568 vtc->vblank_fn = vblank_fn;
569 vtc->vblank_data = vblank_priv;
570 xilinx_vtc_intr_enable(vtc, VTC_IXR_G_VBLANK);
573 /* disable vblank interrupt */
574 void xilinx_vtc_disable_vblank_intr(struct xilinx_vtc *vtc)
576 xilinx_vtc_intr_disable(vtc, VTC_IXR_G_VBLANK);
577 vtc->vblank_data = NULL;
578 vtc->vblank_fn = NULL;
581 static const struct of_device_id xilinx_vtc_of_match[] = {
582 { .compatible = "xlnx,v-tc-5.01.a" },
583 { /* end of table */ },
587 struct xilinx_vtc *xilinx_vtc_probe(struct device *dev,
588 struct device_node *node)
590 struct xilinx_vtc *vtc;
591 const struct of_device_id *match;
595 match = of_match_node(xilinx_vtc_of_match, node);
597 dev_err(dev, "failed to match the device node\n");
598 return ERR_PTR(-ENODEV);
601 vtc = devm_kzalloc(dev, sizeof(*vtc), GFP_KERNEL);
603 return ERR_PTR(-ENOMEM);
605 ret = of_address_to_resource(node, 0, &res);
607 dev_err(dev, "failed to of_address_to_resource\n");
611 vtc->base = devm_ioremap_resource(dev, &res);
612 if (IS_ERR(vtc->base))
613 return ERR_CAST(vtc->base);
615 xilinx_vtc_intr_disable(vtc, VTC_IXR_ALLINTR_MASK);
616 vtc->irq = irq_of_parse_and_map(node, 0);
618 ret = devm_request_irq(dev, vtc->irq, xilinx_vtc_intr_handler,
619 IRQF_SHARED, "xilinx_vtc", vtc);
621 dev_warn(dev, "failed to requet_irq() for vtc\n");
626 xilinx_vtc_reset(vtc);