1f2cb3148SBenjamin Gaignard /* 2f2cb3148SBenjamin Gaignard * Copyright (C) STMicroelectronics SA 2014 3f2cb3148SBenjamin Gaignard * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> 4f2cb3148SBenjamin Gaignard * Fabien Dessenne <fabien.dessenne@st.com> 5f2cb3148SBenjamin Gaignard * Vincent Abriou <vincent.abriou@st.com> 6f2cb3148SBenjamin Gaignard * for STMicroelectronics. 7f2cb3148SBenjamin Gaignard * License terms: GNU General Public License (GPL), version 2 8f2cb3148SBenjamin Gaignard */ 9f2cb3148SBenjamin Gaignard 10f2cb3148SBenjamin Gaignard #include <linux/module.h> 11f2cb3148SBenjamin Gaignard #include <linux/notifier.h> 12f2cb3148SBenjamin Gaignard #include <linux/platform_device.h> 13f2cb3148SBenjamin Gaignard 14f2cb3148SBenjamin Gaignard #include <drm/drmP.h> 15f2cb3148SBenjamin Gaignard 16bdfd36efSVille Syrjälä #include "sti_drv.h" 17f2cb3148SBenjamin Gaignard #include "sti_vtg.h" 18f2cb3148SBenjamin Gaignard 19503290ceSVincent Abriou #define VTG_MODE_MASTER 0 20503290ceSVincent Abriou #define VTG_MODE_SLAVE_BY_EXT0 1 21f2cb3148SBenjamin Gaignard 22f2cb3148SBenjamin Gaignard /* registers offset */ 23f2cb3148SBenjamin Gaignard #define VTG_MODE 0x0000 24f2cb3148SBenjamin Gaignard #define VTG_CLKLN 0x0008 25f2cb3148SBenjamin Gaignard #define VTG_HLFLN 0x000C 26f2cb3148SBenjamin Gaignard #define VTG_DRST_AUTOC 0x0010 27f2cb3148SBenjamin Gaignard #define VTG_VID_TFO 0x0040 28f2cb3148SBenjamin Gaignard #define VTG_VID_TFS 0x0044 29f2cb3148SBenjamin Gaignard #define VTG_VID_BFO 0x0048 30f2cb3148SBenjamin Gaignard #define VTG_VID_BFS 0x004C 31f2cb3148SBenjamin Gaignard 32f2cb3148SBenjamin Gaignard #define VTG_HOST_ITS 0x0078 33f2cb3148SBenjamin Gaignard #define VTG_HOST_ITS_BCLR 0x007C 34f2cb3148SBenjamin Gaignard #define VTG_HOST_ITM_BCLR 0x0088 35f2cb3148SBenjamin Gaignard #define VTG_HOST_ITM_BSET 0x008C 36f2cb3148SBenjamin Gaignard 37f2cb3148SBenjamin Gaignard #define VTG_H_HD_1 0x00C0 38f2cb3148SBenjamin Gaignard #define VTG_TOP_V_VD_1 0x00C4 39f2cb3148SBenjamin Gaignard #define VTG_BOT_V_VD_1 0x00C8 40f2cb3148SBenjamin Gaignard #define VTG_TOP_V_HD_1 0x00CC 41f2cb3148SBenjamin Gaignard #define VTG_BOT_V_HD_1 0x00D0 42f2cb3148SBenjamin Gaignard 43f2cb3148SBenjamin Gaignard #define VTG_H_HD_2 0x00E0 44f2cb3148SBenjamin Gaignard #define VTG_TOP_V_VD_2 0x00E4 45f2cb3148SBenjamin Gaignard #define VTG_BOT_V_VD_2 0x00E8 46f2cb3148SBenjamin Gaignard #define VTG_TOP_V_HD_2 0x00EC 47f2cb3148SBenjamin Gaignard #define VTG_BOT_V_HD_2 0x00F0 48f2cb3148SBenjamin Gaignard 49f2cb3148SBenjamin Gaignard #define VTG_H_HD_3 0x0100 50f2cb3148SBenjamin Gaignard #define VTG_TOP_V_VD_3 0x0104 51f2cb3148SBenjamin Gaignard #define VTG_BOT_V_VD_3 0x0108 52f2cb3148SBenjamin Gaignard #define VTG_TOP_V_HD_3 0x010C 53f2cb3148SBenjamin Gaignard #define VTG_BOT_V_HD_3 0x0110 54f2cb3148SBenjamin Gaignard 557f2d479cSBenjamin Gaignard #define VTG_H_HD_4 0x0120 567f2d479cSBenjamin Gaignard #define VTG_TOP_V_VD_4 0x0124 577f2d479cSBenjamin Gaignard #define VTG_BOT_V_VD_4 0x0128 587f2d479cSBenjamin Gaignard #define VTG_TOP_V_HD_4 0x012c 597f2d479cSBenjamin Gaignard #define VTG_BOT_V_HD_4 0x0130 607f2d479cSBenjamin Gaignard 61f2cb3148SBenjamin Gaignard #define VTG_IRQ_BOTTOM BIT(0) 62f2cb3148SBenjamin Gaignard #define VTG_IRQ_TOP BIT(1) 63f2cb3148SBenjamin Gaignard #define VTG_IRQ_MASK (VTG_IRQ_TOP | VTG_IRQ_BOTTOM) 64f2cb3148SBenjamin Gaignard 657f2d479cSBenjamin Gaignard /* Delay introduced by the HDMI in nb of pixel */ 668eba2703SVincent Abriou #define HDMI_DELAY (5) 677f2d479cSBenjamin Gaignard 689a024948SBich Hemon /* Delay introduced by the DVO in nb of pixel */ 694d703770SBich Hemon #define DVO_DELAY (7) 709a024948SBich Hemon 71f2cb3148SBenjamin Gaignard /* delay introduced by the Arbitrary Waveform Generator in nb of pixels */ 72f2cb3148SBenjamin Gaignard #define AWG_DELAY_HD (-9) 73f2cb3148SBenjamin Gaignard #define AWG_DELAY_ED (-8) 74f2cb3148SBenjamin Gaignard #define AWG_DELAY_SD (-7) 75f2cb3148SBenjamin Gaignard 76bdfd36efSVille Syrjälä static LIST_HEAD(vtg_lookup); 77f2cb3148SBenjamin Gaignard 78503290ceSVincent Abriou /* 79503290ceSVincent Abriou * STI VTG register offset structure 80503290ceSVincent Abriou * 81503290ceSVincent Abriou *@h_hd: stores the VTG_H_HD_x register offset 82503290ceSVincent Abriou *@top_v_vd: stores the VTG_TOP_V_VD_x register offset 83503290ceSVincent Abriou *@bot_v_vd: stores the VTG_BOT_V_VD_x register offset 84503290ceSVincent Abriou *@top_v_hd: stores the VTG_TOP_V_HD_x register offset 85503290ceSVincent Abriou *@bot_v_hd: stores the VTG_BOT_V_HD_x register offset 86503290ceSVincent Abriou */ 87503290ceSVincent Abriou struct sti_vtg_regs_offs { 88503290ceSVincent Abriou u32 h_hd; 89503290ceSVincent Abriou u32 top_v_vd; 90503290ceSVincent Abriou u32 bot_v_vd; 91503290ceSVincent Abriou u32 top_v_hd; 92503290ceSVincent Abriou u32 bot_v_hd; 93503290ceSVincent Abriou }; 94503290ceSVincent Abriou 95503290ceSVincent Abriou #define VTG_MAX_SYNC_OUTPUT 4 96503290ceSVincent Abriou static const struct sti_vtg_regs_offs vtg_regs_offs[VTG_MAX_SYNC_OUTPUT] = { 97503290ceSVincent Abriou { VTG_H_HD_1, 98503290ceSVincent Abriou VTG_TOP_V_VD_1, VTG_BOT_V_VD_1, VTG_TOP_V_HD_1, VTG_BOT_V_HD_1 }, 99503290ceSVincent Abriou { VTG_H_HD_2, 100503290ceSVincent Abriou VTG_TOP_V_VD_2, VTG_BOT_V_VD_2, VTG_TOP_V_HD_2, VTG_BOT_V_HD_2 }, 101503290ceSVincent Abriou { VTG_H_HD_3, 102503290ceSVincent Abriou VTG_TOP_V_VD_3, VTG_BOT_V_VD_3, VTG_TOP_V_HD_3, VTG_BOT_V_HD_3 }, 103503290ceSVincent Abriou { VTG_H_HD_4, 104503290ceSVincent Abriou VTG_TOP_V_VD_4, VTG_BOT_V_VD_4, VTG_TOP_V_HD_4, VTG_BOT_V_HD_4 } 105503290ceSVincent Abriou }; 106503290ceSVincent Abriou 107503290ceSVincent Abriou /* 108503290ceSVincent Abriou * STI VTG synchronisation parameters structure 109503290ceSVincent Abriou * 110503290ceSVincent Abriou *@hsync: sample number falling and rising edge 111503290ceSVincent Abriou *@vsync_line_top: vertical top field line number falling and rising edge 112503290ceSVincent Abriou *@vsync_line_bot: vertical bottom field line number falling and rising edge 113503290ceSVincent Abriou *@vsync_off_top: vertical top field sample number rising and falling edge 114503290ceSVincent Abriou *@vsync_off_bot: vertical bottom field sample number rising and falling edge 115503290ceSVincent Abriou */ 116503290ceSVincent Abriou struct sti_vtg_sync_params { 117503290ceSVincent Abriou u32 hsync; 118503290ceSVincent Abriou u32 vsync_line_top; 119503290ceSVincent Abriou u32 vsync_line_bot; 120503290ceSVincent Abriou u32 vsync_off_top; 121503290ceSVincent Abriou u32 vsync_off_bot; 122503290ceSVincent Abriou }; 123503290ceSVincent Abriou 124f2cb3148SBenjamin Gaignard /** 125f2cb3148SBenjamin Gaignard * STI VTG structure 126f2cb3148SBenjamin Gaignard * 127f2cb3148SBenjamin Gaignard * @dev: pointer to device driver 128503290ceSVincent Abriou * @np: device node 129503290ceSVincent Abriou * @regs: register mapping 130503290ceSVincent Abriou * @sync_params: synchronisation parameters used to generate timings 131f2cb3148SBenjamin Gaignard * @irq: VTG irq 132503290ceSVincent Abriou * @irq_status: store the IRQ status value 133f2cb3148SBenjamin Gaignard * @notifier_list: notifier callback 1342388693eSThierry Reding * @crtc: the CRTC for vblank event 135f2cb3148SBenjamin Gaignard * @slave: slave vtg 136f2cb3148SBenjamin Gaignard * @link: List node to link the structure in lookup list 137f2cb3148SBenjamin Gaignard */ 138f2cb3148SBenjamin Gaignard struct sti_vtg { 139f2cb3148SBenjamin Gaignard struct device *dev; 140f2cb3148SBenjamin Gaignard struct device_node *np; 141f2cb3148SBenjamin Gaignard void __iomem *regs; 142503290ceSVincent Abriou struct sti_vtg_sync_params sync_params[VTG_MAX_SYNC_OUTPUT]; 143f2cb3148SBenjamin Gaignard int irq; 144f2cb3148SBenjamin Gaignard u32 irq_status; 145f2cb3148SBenjamin Gaignard struct raw_notifier_head notifier_list; 1462388693eSThierry Reding struct drm_crtc *crtc; 147f2cb3148SBenjamin Gaignard struct sti_vtg *slave; 148f2cb3148SBenjamin Gaignard struct list_head link; 149f2cb3148SBenjamin Gaignard }; 150f2cb3148SBenjamin Gaignard 151f2cb3148SBenjamin Gaignard static void vtg_register(struct sti_vtg *vtg) 152f2cb3148SBenjamin Gaignard { 153f2cb3148SBenjamin Gaignard list_add_tail(&vtg->link, &vtg_lookup); 154f2cb3148SBenjamin Gaignard } 155f2cb3148SBenjamin Gaignard 156f2cb3148SBenjamin Gaignard struct sti_vtg *of_vtg_find(struct device_node *np) 157f2cb3148SBenjamin Gaignard { 158f2cb3148SBenjamin Gaignard struct sti_vtg *vtg; 159f2cb3148SBenjamin Gaignard 160f2cb3148SBenjamin Gaignard list_for_each_entry(vtg, &vtg_lookup, link) { 161f2cb3148SBenjamin Gaignard if (vtg->np == np) 162f2cb3148SBenjamin Gaignard return vtg; 163f2cb3148SBenjamin Gaignard } 164f2cb3148SBenjamin Gaignard return NULL; 165f2cb3148SBenjamin Gaignard } 166f2cb3148SBenjamin Gaignard 167f2cb3148SBenjamin Gaignard static void vtg_reset(struct sti_vtg *vtg) 168f2cb3148SBenjamin Gaignard { 169f2cb3148SBenjamin Gaignard /* reset slave and then master */ 170f2cb3148SBenjamin Gaignard if (vtg->slave) 171f2cb3148SBenjamin Gaignard vtg_reset(vtg->slave); 172f2cb3148SBenjamin Gaignard 173f2cb3148SBenjamin Gaignard writel(1, vtg->regs + VTG_DRST_AUTOC); 174f2cb3148SBenjamin Gaignard } 175f2cb3148SBenjamin Gaignard 1768eba2703SVincent Abriou static void vtg_set_output_window(void __iomem *regs, 1778eba2703SVincent Abriou const struct drm_display_mode *mode) 1788eba2703SVincent Abriou { 1798eba2703SVincent Abriou u32 video_top_field_start; 1808eba2703SVincent Abriou u32 video_top_field_stop; 1818eba2703SVincent Abriou u32 video_bottom_field_start; 1828eba2703SVincent Abriou u32 video_bottom_field_stop; 1838eba2703SVincent Abriou u32 xstart = sti_vtg_get_pixel_number(*mode, 0); 1848eba2703SVincent Abriou u32 ystart = sti_vtg_get_line_number(*mode, 0); 1858eba2703SVincent Abriou u32 xstop = sti_vtg_get_pixel_number(*mode, mode->hdisplay - 1); 1868eba2703SVincent Abriou u32 ystop = sti_vtg_get_line_number(*mode, mode->vdisplay - 1); 1878eba2703SVincent Abriou 1888eba2703SVincent Abriou /* Set output window to fit the display mode selected */ 1898eba2703SVincent Abriou video_top_field_start = (ystart << 16) | xstart; 1908eba2703SVincent Abriou video_top_field_stop = (ystop << 16) | xstop; 1918eba2703SVincent Abriou 1928eba2703SVincent Abriou /* Only progressive supported for now */ 1938eba2703SVincent Abriou video_bottom_field_start = video_top_field_start; 1948eba2703SVincent Abriou video_bottom_field_stop = video_top_field_stop; 1958eba2703SVincent Abriou 1968eba2703SVincent Abriou writel(video_top_field_start, regs + VTG_VID_TFO); 1978eba2703SVincent Abriou writel(video_top_field_stop, regs + VTG_VID_TFS); 1988eba2703SVincent Abriou writel(video_bottom_field_start, regs + VTG_VID_BFO); 1998eba2703SVincent Abriou writel(video_bottom_field_stop, regs + VTG_VID_BFS); 2008eba2703SVincent Abriou } 2018eba2703SVincent Abriou 202503290ceSVincent Abriou static void vtg_set_hsync_vsync_pos(struct sti_vtg_sync_params *sync, 203503290ceSVincent Abriou int delay, 204503290ceSVincent Abriou const struct drm_display_mode *mode) 205f2cb3148SBenjamin Gaignard { 206503290ceSVincent Abriou long clocksperline, start, stop; 207503290ceSVincent Abriou u32 risesync_top, fallsync_top; 208503290ceSVincent Abriou u32 risesync_offs_top, fallsync_offs_top; 209503290ceSVincent Abriou 210503290ceSVincent Abriou clocksperline = mode->htotal; 211503290ceSVincent Abriou 212503290ceSVincent Abriou /* Get the hsync position */ 213503290ceSVincent Abriou start = 0; 214503290ceSVincent Abriou stop = mode->hsync_end - mode->hsync_start; 215503290ceSVincent Abriou 216503290ceSVincent Abriou start += delay; 217503290ceSVincent Abriou stop += delay; 218503290ceSVincent Abriou 219503290ceSVincent Abriou if (start < 0) 220503290ceSVincent Abriou start += clocksperline; 221503290ceSVincent Abriou else if (start >= clocksperline) 222503290ceSVincent Abriou start -= clocksperline; 223503290ceSVincent Abriou 224503290ceSVincent Abriou if (stop < 0) 225503290ceSVincent Abriou stop += clocksperline; 226503290ceSVincent Abriou else if (stop >= clocksperline) 227503290ceSVincent Abriou stop -= clocksperline; 228503290ceSVincent Abriou 229503290ceSVincent Abriou sync->hsync = (stop << 16) | start; 230503290ceSVincent Abriou 231503290ceSVincent Abriou /* Get the vsync position */ 232503290ceSVincent Abriou if (delay >= 0) { 233503290ceSVincent Abriou risesync_top = 1; 234503290ceSVincent Abriou fallsync_top = risesync_top; 235503290ceSVincent Abriou fallsync_top += mode->vsync_end - mode->vsync_start; 236503290ceSVincent Abriou 237503290ceSVincent Abriou fallsync_offs_top = (u32)delay; 238503290ceSVincent Abriou risesync_offs_top = (u32)delay; 239503290ceSVincent Abriou } else { 240503290ceSVincent Abriou risesync_top = mode->vtotal; 241503290ceSVincent Abriou fallsync_top = mode->vsync_end - mode->vsync_start; 242503290ceSVincent Abriou 243503290ceSVincent Abriou fallsync_offs_top = clocksperline + delay; 244503290ceSVincent Abriou risesync_offs_top = clocksperline + delay; 245503290ceSVincent Abriou } 246503290ceSVincent Abriou 247503290ceSVincent Abriou sync->vsync_line_top = (fallsync_top << 16) | risesync_top; 248503290ceSVincent Abriou sync->vsync_off_top = (fallsync_offs_top << 16) | risesync_offs_top; 249503290ceSVincent Abriou 250503290ceSVincent Abriou /* Only progressive supported for now */ 251503290ceSVincent Abriou sync->vsync_line_bot = sync->vsync_line_top; 252503290ceSVincent Abriou sync->vsync_off_bot = sync->vsync_off_top; 253503290ceSVincent Abriou } 254503290ceSVincent Abriou 255503290ceSVincent Abriou static void vtg_set_mode(struct sti_vtg *vtg, 256503290ceSVincent Abriou int type, 257503290ceSVincent Abriou struct sti_vtg_sync_params *sync, 258503290ceSVincent Abriou const struct drm_display_mode *mode) 259503290ceSVincent Abriou { 260503290ceSVincent Abriou unsigned int i; 261f2cb3148SBenjamin Gaignard 262f2cb3148SBenjamin Gaignard if (vtg->slave) 263503290ceSVincent Abriou vtg_set_mode(vtg->slave, VTG_MODE_SLAVE_BY_EXT0, 264503290ceSVincent Abriou vtg->sync_params, mode); 265f2cb3148SBenjamin Gaignard 2668eba2703SVincent Abriou /* Set the number of clock cycles per line */ 267f2cb3148SBenjamin Gaignard writel(mode->htotal, vtg->regs + VTG_CLKLN); 2688eba2703SVincent Abriou 2698eba2703SVincent Abriou /* Set Half Line Per Field (only progressive supported for now) */ 270f2cb3148SBenjamin Gaignard writel(mode->vtotal * 2, vtg->regs + VTG_HLFLN); 271f2cb3148SBenjamin Gaignard 2728eba2703SVincent Abriou /* Program output window */ 2738eba2703SVincent Abriou vtg_set_output_window(vtg->regs, mode); 274f2cb3148SBenjamin Gaignard 275503290ceSVincent Abriou /* Set hsync and vsync position for HDMI */ 276503290ceSVincent Abriou vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_HDMI - 1], HDMI_DELAY, mode); 277f2cb3148SBenjamin Gaignard 278503290ceSVincent Abriou /* Set hsync and vsync position for HD DCS */ 279503290ceSVincent Abriou vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_HDDCS - 1], 0, mode); 280c58d6d1bSVincent Abriou 281503290ceSVincent Abriou /* Set hsync and vsync position for HDF */ 282503290ceSVincent Abriou vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_HDF - 1], AWG_DELAY_HD, mode); 283f2cb3148SBenjamin Gaignard 284503290ceSVincent Abriou /* Set hsync and vsync position for DVO */ 2859a024948SBich Hemon vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_DVO - 1], DVO_DELAY, mode); 2867f2d479cSBenjamin Gaignard 287503290ceSVincent Abriou /* Progam the syncs outputs */ 288503290ceSVincent Abriou for (i = 0; i < VTG_MAX_SYNC_OUTPUT ; i++) { 289503290ceSVincent Abriou writel(sync[i].hsync, 290503290ceSVincent Abriou vtg->regs + vtg_regs_offs[i].h_hd); 291503290ceSVincent Abriou writel(sync[i].vsync_line_top, 292503290ceSVincent Abriou vtg->regs + vtg_regs_offs[i].top_v_vd); 293503290ceSVincent Abriou writel(sync[i].vsync_line_bot, 294503290ceSVincent Abriou vtg->regs + vtg_regs_offs[i].bot_v_vd); 295503290ceSVincent Abriou writel(sync[i].vsync_off_top, 296503290ceSVincent Abriou vtg->regs + vtg_regs_offs[i].top_v_hd); 297503290ceSVincent Abriou writel(sync[i].vsync_off_bot, 298503290ceSVincent Abriou vtg->regs + vtg_regs_offs[i].bot_v_hd); 299503290ceSVincent Abriou } 3007f2d479cSBenjamin Gaignard 301f2cb3148SBenjamin Gaignard /* mode */ 302f2cb3148SBenjamin Gaignard writel(type, vtg->regs + VTG_MODE); 303f2cb3148SBenjamin Gaignard } 304f2cb3148SBenjamin Gaignard 305f2cb3148SBenjamin Gaignard static void vtg_enable_irq(struct sti_vtg *vtg) 306f2cb3148SBenjamin Gaignard { 307f2cb3148SBenjamin Gaignard /* clear interrupt status and mask */ 308f2cb3148SBenjamin Gaignard writel(0xFFFF, vtg->regs + VTG_HOST_ITS_BCLR); 309f2cb3148SBenjamin Gaignard writel(0xFFFF, vtg->regs + VTG_HOST_ITM_BCLR); 310f2cb3148SBenjamin Gaignard writel(VTG_IRQ_MASK, vtg->regs + VTG_HOST_ITM_BSET); 311f2cb3148SBenjamin Gaignard } 312f2cb3148SBenjamin Gaignard 313f2cb3148SBenjamin Gaignard void sti_vtg_set_config(struct sti_vtg *vtg, 314f2cb3148SBenjamin Gaignard const struct drm_display_mode *mode) 315f2cb3148SBenjamin Gaignard { 316f2cb3148SBenjamin Gaignard /* write configuration */ 317503290ceSVincent Abriou vtg_set_mode(vtg, VTG_MODE_MASTER, vtg->sync_params, mode); 318f2cb3148SBenjamin Gaignard 319f2cb3148SBenjamin Gaignard vtg_reset(vtg); 320f2cb3148SBenjamin Gaignard 321f2cb3148SBenjamin Gaignard /* enable irq for the vtg vblank synchro */ 322f2cb3148SBenjamin Gaignard if (vtg->slave) 323f2cb3148SBenjamin Gaignard vtg_enable_irq(vtg->slave); 324f2cb3148SBenjamin Gaignard else 325f2cb3148SBenjamin Gaignard vtg_enable_irq(vtg); 326f2cb3148SBenjamin Gaignard } 327f2cb3148SBenjamin Gaignard 328f2cb3148SBenjamin Gaignard /** 329f2cb3148SBenjamin Gaignard * sti_vtg_get_line_number 330f2cb3148SBenjamin Gaignard * 331f2cb3148SBenjamin Gaignard * @mode: display mode to be used 332f2cb3148SBenjamin Gaignard * @y: line 333f2cb3148SBenjamin Gaignard * 334f2cb3148SBenjamin Gaignard * Return the line number according to the display mode taking 335f2cb3148SBenjamin Gaignard * into account the Sync and Back Porch information. 336f2cb3148SBenjamin Gaignard * Video frame line numbers start at 1, y starts at 0. 337f2cb3148SBenjamin Gaignard * In interlaced modes the start line is the field line number of the odd 338f2cb3148SBenjamin Gaignard * field, but y is still defined as a progressive frame. 339f2cb3148SBenjamin Gaignard */ 340f2cb3148SBenjamin Gaignard u32 sti_vtg_get_line_number(struct drm_display_mode mode, int y) 341f2cb3148SBenjamin Gaignard { 342f2cb3148SBenjamin Gaignard u32 start_line = mode.vtotal - mode.vsync_start + 1; 343f2cb3148SBenjamin Gaignard 344f2cb3148SBenjamin Gaignard if (mode.flags & DRM_MODE_FLAG_INTERLACE) 345f2cb3148SBenjamin Gaignard start_line *= 2; 346f2cb3148SBenjamin Gaignard 347f2cb3148SBenjamin Gaignard return start_line + y; 348f2cb3148SBenjamin Gaignard } 349f2cb3148SBenjamin Gaignard 350f2cb3148SBenjamin Gaignard /** 351f2cb3148SBenjamin Gaignard * sti_vtg_get_pixel_number 352f2cb3148SBenjamin Gaignard * 353f2cb3148SBenjamin Gaignard * @mode: display mode to be used 354f2cb3148SBenjamin Gaignard * @x: row 355f2cb3148SBenjamin Gaignard * 356f2cb3148SBenjamin Gaignard * Return the pixel number according to the display mode taking 357f2cb3148SBenjamin Gaignard * into account the Sync and Back Porch information. 358f2cb3148SBenjamin Gaignard * Pixels are counted from 0. 359f2cb3148SBenjamin Gaignard */ 360f2cb3148SBenjamin Gaignard u32 sti_vtg_get_pixel_number(struct drm_display_mode mode, int x) 361f2cb3148SBenjamin Gaignard { 362f2cb3148SBenjamin Gaignard return mode.htotal - mode.hsync_start + x; 363f2cb3148SBenjamin Gaignard } 364f2cb3148SBenjamin Gaignard 3652388693eSThierry Reding int sti_vtg_register_client(struct sti_vtg *vtg, struct notifier_block *nb, 3662388693eSThierry Reding struct drm_crtc *crtc) 367f2cb3148SBenjamin Gaignard { 368f2cb3148SBenjamin Gaignard if (vtg->slave) 3692388693eSThierry Reding return sti_vtg_register_client(vtg->slave, nb, crtc); 370f2cb3148SBenjamin Gaignard 3712388693eSThierry Reding vtg->crtc = crtc; 372f2cb3148SBenjamin Gaignard return raw_notifier_chain_register(&vtg->notifier_list, nb); 373f2cb3148SBenjamin Gaignard } 374f2cb3148SBenjamin Gaignard 375f2cb3148SBenjamin Gaignard int sti_vtg_unregister_client(struct sti_vtg *vtg, struct notifier_block *nb) 376f2cb3148SBenjamin Gaignard { 377f2cb3148SBenjamin Gaignard if (vtg->slave) 378f2cb3148SBenjamin Gaignard return sti_vtg_unregister_client(vtg->slave, nb); 379f2cb3148SBenjamin Gaignard 380f2cb3148SBenjamin Gaignard return raw_notifier_chain_unregister(&vtg->notifier_list, nb); 381f2cb3148SBenjamin Gaignard } 382f2cb3148SBenjamin Gaignard 383f2cb3148SBenjamin Gaignard static irqreturn_t vtg_irq_thread(int irq, void *arg) 384f2cb3148SBenjamin Gaignard { 385f2cb3148SBenjamin Gaignard struct sti_vtg *vtg = arg; 386f2cb3148SBenjamin Gaignard u32 event; 387f2cb3148SBenjamin Gaignard 388f2cb3148SBenjamin Gaignard event = (vtg->irq_status & VTG_IRQ_TOP) ? 389f2cb3148SBenjamin Gaignard VTG_TOP_FIELD_EVENT : VTG_BOTTOM_FIELD_EVENT; 390f2cb3148SBenjamin Gaignard 3912388693eSThierry Reding raw_notifier_call_chain(&vtg->notifier_list, event, vtg->crtc); 392f2cb3148SBenjamin Gaignard 393f2cb3148SBenjamin Gaignard return IRQ_HANDLED; 394f2cb3148SBenjamin Gaignard } 395f2cb3148SBenjamin Gaignard 396f2cb3148SBenjamin Gaignard static irqreturn_t vtg_irq(int irq, void *arg) 397f2cb3148SBenjamin Gaignard { 398f2cb3148SBenjamin Gaignard struct sti_vtg *vtg = arg; 399f2cb3148SBenjamin Gaignard 400f2cb3148SBenjamin Gaignard vtg->irq_status = readl(vtg->regs + VTG_HOST_ITS); 401f2cb3148SBenjamin Gaignard 402f2cb3148SBenjamin Gaignard writel(vtg->irq_status, vtg->regs + VTG_HOST_ITS_BCLR); 403f2cb3148SBenjamin Gaignard 404f2cb3148SBenjamin Gaignard /* force sync bus write */ 405f2cb3148SBenjamin Gaignard readl(vtg->regs + VTG_HOST_ITS); 406f2cb3148SBenjamin Gaignard 407f2cb3148SBenjamin Gaignard return IRQ_WAKE_THREAD; 408f2cb3148SBenjamin Gaignard } 409f2cb3148SBenjamin Gaignard 410f2cb3148SBenjamin Gaignard static int vtg_probe(struct platform_device *pdev) 411f2cb3148SBenjamin Gaignard { 412f2cb3148SBenjamin Gaignard struct device *dev = &pdev->dev; 413f2cb3148SBenjamin Gaignard struct device_node *np; 414f2cb3148SBenjamin Gaignard struct sti_vtg *vtg; 415f2cb3148SBenjamin Gaignard struct resource *res; 416f2cb3148SBenjamin Gaignard int ret; 417f2cb3148SBenjamin Gaignard 418f2cb3148SBenjamin Gaignard vtg = devm_kzalloc(dev, sizeof(*vtg), GFP_KERNEL); 419f2cb3148SBenjamin Gaignard if (!vtg) 420f2cb3148SBenjamin Gaignard return -ENOMEM; 421f2cb3148SBenjamin Gaignard 422f2cb3148SBenjamin Gaignard vtg->dev = dev; 423f2cb3148SBenjamin Gaignard vtg->np = pdev->dev.of_node; 424f2cb3148SBenjamin Gaignard 425f2cb3148SBenjamin Gaignard /* Get Memory ressources */ 426f2cb3148SBenjamin Gaignard res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 427f2cb3148SBenjamin Gaignard if (!res) { 428f2cb3148SBenjamin Gaignard DRM_ERROR("Get memory resource failed\n"); 429f2cb3148SBenjamin Gaignard return -ENOMEM; 430f2cb3148SBenjamin Gaignard } 431f2cb3148SBenjamin Gaignard vtg->regs = devm_ioremap_nocache(dev, res->start, resource_size(res)); 432*1ae0d5afSArvind Yadav if (!vtg->regs) { 433*1ae0d5afSArvind Yadav DRM_ERROR("failed to remap I/O memory\n"); 434*1ae0d5afSArvind Yadav return -ENOMEM; 435*1ae0d5afSArvind Yadav } 436f2cb3148SBenjamin Gaignard 437f2cb3148SBenjamin Gaignard np = of_parse_phandle(pdev->dev.of_node, "st,slave", 0); 438f2cb3148SBenjamin Gaignard if (np) { 439f2cb3148SBenjamin Gaignard vtg->slave = of_vtg_find(np); 440e8ef1b69SPeter Chen of_node_put(np); 441f2cb3148SBenjamin Gaignard 442f2cb3148SBenjamin Gaignard if (!vtg->slave) 443f2cb3148SBenjamin Gaignard return -EPROBE_DEFER; 444f2cb3148SBenjamin Gaignard } else { 445f2cb3148SBenjamin Gaignard vtg->irq = platform_get_irq(pdev, 0); 446287980e4SArnd Bergmann if (vtg->irq < 0) { 447f2cb3148SBenjamin Gaignard DRM_ERROR("Failed to get VTG interrupt\n"); 448f2cb3148SBenjamin Gaignard return vtg->irq; 449f2cb3148SBenjamin Gaignard } 450f2cb3148SBenjamin Gaignard 451f2cb3148SBenjamin Gaignard RAW_INIT_NOTIFIER_HEAD(&vtg->notifier_list); 452f2cb3148SBenjamin Gaignard 453f2cb3148SBenjamin Gaignard ret = devm_request_threaded_irq(dev, vtg->irq, vtg_irq, 4548b0a99ceSVincent Abriou vtg_irq_thread, IRQF_ONESHOT, 4558b0a99ceSVincent Abriou dev_name(dev), vtg); 456287980e4SArnd Bergmann if (ret < 0) { 457f2cb3148SBenjamin Gaignard DRM_ERROR("Failed to register VTG interrupt\n"); 458f2cb3148SBenjamin Gaignard return ret; 459f2cb3148SBenjamin Gaignard } 460f2cb3148SBenjamin Gaignard } 461f2cb3148SBenjamin Gaignard 462f2cb3148SBenjamin Gaignard vtg_register(vtg); 463f2cb3148SBenjamin Gaignard platform_set_drvdata(pdev, vtg); 464f2cb3148SBenjamin Gaignard 465f2cb3148SBenjamin Gaignard DRM_INFO("%s %s\n", __func__, dev_name(vtg->dev)); 466f2cb3148SBenjamin Gaignard 467f2cb3148SBenjamin Gaignard return 0; 468f2cb3148SBenjamin Gaignard } 469f2cb3148SBenjamin Gaignard 470f2cb3148SBenjamin Gaignard static int vtg_remove(struct platform_device *pdev) 471f2cb3148SBenjamin Gaignard { 472f2cb3148SBenjamin Gaignard return 0; 473f2cb3148SBenjamin Gaignard } 474f2cb3148SBenjamin Gaignard 475f2cb3148SBenjamin Gaignard static const struct of_device_id vtg_of_match[] = { 476f2cb3148SBenjamin Gaignard { .compatible = "st,vtg", }, 477f2cb3148SBenjamin Gaignard { /* sentinel */ } 478f2cb3148SBenjamin Gaignard }; 479f2cb3148SBenjamin Gaignard MODULE_DEVICE_TABLE(of, vtg_of_match); 480f2cb3148SBenjamin Gaignard 481f2cb3148SBenjamin Gaignard struct platform_driver sti_vtg_driver = { 482f2cb3148SBenjamin Gaignard .driver = { 483f2cb3148SBenjamin Gaignard .name = "sti-vtg", 484f2cb3148SBenjamin Gaignard .owner = THIS_MODULE, 485f2cb3148SBenjamin Gaignard .of_match_table = vtg_of_match, 486f2cb3148SBenjamin Gaignard }, 487f2cb3148SBenjamin Gaignard .probe = vtg_probe, 488f2cb3148SBenjamin Gaignard .remove = vtg_remove, 489f2cb3148SBenjamin Gaignard }; 490f2cb3148SBenjamin Gaignard 491f2cb3148SBenjamin Gaignard MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>"); 492f2cb3148SBenjamin Gaignard MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver"); 493f2cb3148SBenjamin Gaignard MODULE_LICENSE("GPL"); 494