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 16f2cb3148SBenjamin Gaignard #include "sti_vtg.h" 17f2cb3148SBenjamin Gaignard 18f2cb3148SBenjamin Gaignard #define VTG_TYPE_MASTER 0 19f2cb3148SBenjamin Gaignard #define VTG_TYPE_SLAVE_BY_EXT0 1 20f2cb3148SBenjamin Gaignard 21f2cb3148SBenjamin Gaignard /* registers offset */ 22f2cb3148SBenjamin Gaignard #define VTG_MODE 0x0000 23f2cb3148SBenjamin Gaignard #define VTG_CLKLN 0x0008 24f2cb3148SBenjamin Gaignard #define VTG_HLFLN 0x000C 25f2cb3148SBenjamin Gaignard #define VTG_DRST_AUTOC 0x0010 26f2cb3148SBenjamin Gaignard #define VTG_VID_TFO 0x0040 27f2cb3148SBenjamin Gaignard #define VTG_VID_TFS 0x0044 28f2cb3148SBenjamin Gaignard #define VTG_VID_BFO 0x0048 29f2cb3148SBenjamin Gaignard #define VTG_VID_BFS 0x004C 30f2cb3148SBenjamin Gaignard 31f2cb3148SBenjamin Gaignard #define VTG_HOST_ITS 0x0078 32f2cb3148SBenjamin Gaignard #define VTG_HOST_ITS_BCLR 0x007C 33f2cb3148SBenjamin Gaignard #define VTG_HOST_ITM_BCLR 0x0088 34f2cb3148SBenjamin Gaignard #define VTG_HOST_ITM_BSET 0x008C 35f2cb3148SBenjamin Gaignard 36f2cb3148SBenjamin Gaignard #define VTG_H_HD_1 0x00C0 37f2cb3148SBenjamin Gaignard #define VTG_TOP_V_VD_1 0x00C4 38f2cb3148SBenjamin Gaignard #define VTG_BOT_V_VD_1 0x00C8 39f2cb3148SBenjamin Gaignard #define VTG_TOP_V_HD_1 0x00CC 40f2cb3148SBenjamin Gaignard #define VTG_BOT_V_HD_1 0x00D0 41f2cb3148SBenjamin Gaignard 42f2cb3148SBenjamin Gaignard #define VTG_H_HD_2 0x00E0 43f2cb3148SBenjamin Gaignard #define VTG_TOP_V_VD_2 0x00E4 44f2cb3148SBenjamin Gaignard #define VTG_BOT_V_VD_2 0x00E8 45f2cb3148SBenjamin Gaignard #define VTG_TOP_V_HD_2 0x00EC 46f2cb3148SBenjamin Gaignard #define VTG_BOT_V_HD_2 0x00F0 47f2cb3148SBenjamin Gaignard 48f2cb3148SBenjamin Gaignard #define VTG_H_HD_3 0x0100 49f2cb3148SBenjamin Gaignard #define VTG_TOP_V_VD_3 0x0104 50f2cb3148SBenjamin Gaignard #define VTG_BOT_V_VD_3 0x0108 51f2cb3148SBenjamin Gaignard #define VTG_TOP_V_HD_3 0x010C 52f2cb3148SBenjamin Gaignard #define VTG_BOT_V_HD_3 0x0110 53f2cb3148SBenjamin Gaignard 547f2d479cSBenjamin Gaignard #define VTG_H_HD_4 0x0120 557f2d479cSBenjamin Gaignard #define VTG_TOP_V_VD_4 0x0124 567f2d479cSBenjamin Gaignard #define VTG_BOT_V_VD_4 0x0128 577f2d479cSBenjamin Gaignard #define VTG_TOP_V_HD_4 0x012c 587f2d479cSBenjamin Gaignard #define VTG_BOT_V_HD_4 0x0130 597f2d479cSBenjamin Gaignard 60f2cb3148SBenjamin Gaignard #define VTG_IRQ_BOTTOM BIT(0) 61f2cb3148SBenjamin Gaignard #define VTG_IRQ_TOP BIT(1) 62f2cb3148SBenjamin Gaignard #define VTG_IRQ_MASK (VTG_IRQ_TOP | VTG_IRQ_BOTTOM) 63f2cb3148SBenjamin Gaignard 647f2d479cSBenjamin Gaignard /* Delay introduced by the HDMI in nb of pixel */ 65*8eba2703SVincent Abriou #define HDMI_DELAY (5) 667f2d479cSBenjamin Gaignard 67f2cb3148SBenjamin Gaignard /* delay introduced by the Arbitrary Waveform Generator in nb of pixels */ 68f2cb3148SBenjamin Gaignard #define AWG_DELAY_HD (-9) 69f2cb3148SBenjamin Gaignard #define AWG_DELAY_ED (-8) 70f2cb3148SBenjamin Gaignard #define AWG_DELAY_SD (-7) 71f2cb3148SBenjamin Gaignard 72f2cb3148SBenjamin Gaignard LIST_HEAD(vtg_lookup); 73f2cb3148SBenjamin Gaignard 74f2cb3148SBenjamin Gaignard /** 75f2cb3148SBenjamin Gaignard * STI VTG structure 76f2cb3148SBenjamin Gaignard * 77f2cb3148SBenjamin Gaignard * @dev: pointer to device driver 78f2cb3148SBenjamin Gaignard * @data: data associated to the device 79f2cb3148SBenjamin Gaignard * @irq: VTG irq 80f2cb3148SBenjamin Gaignard * @type: VTG type (main or aux) 81f2cb3148SBenjamin Gaignard * @notifier_list: notifier callback 82f2cb3148SBenjamin Gaignard * @crtc_id: the crtc id for vblank event 83f2cb3148SBenjamin Gaignard * @slave: slave vtg 84f2cb3148SBenjamin Gaignard * @link: List node to link the structure in lookup list 85f2cb3148SBenjamin Gaignard */ 86f2cb3148SBenjamin Gaignard struct sti_vtg { 87f2cb3148SBenjamin Gaignard struct device *dev; 88f2cb3148SBenjamin Gaignard struct device_node *np; 89f2cb3148SBenjamin Gaignard void __iomem *regs; 90f2cb3148SBenjamin Gaignard int irq; 91f2cb3148SBenjamin Gaignard u32 irq_status; 92f2cb3148SBenjamin Gaignard struct raw_notifier_head notifier_list; 93f2cb3148SBenjamin Gaignard int crtc_id; 94f2cb3148SBenjamin Gaignard struct sti_vtg *slave; 95f2cb3148SBenjamin Gaignard struct list_head link; 96f2cb3148SBenjamin Gaignard }; 97f2cb3148SBenjamin Gaignard 98f2cb3148SBenjamin Gaignard static void vtg_register(struct sti_vtg *vtg) 99f2cb3148SBenjamin Gaignard { 100f2cb3148SBenjamin Gaignard list_add_tail(&vtg->link, &vtg_lookup); 101f2cb3148SBenjamin Gaignard } 102f2cb3148SBenjamin Gaignard 103f2cb3148SBenjamin Gaignard struct sti_vtg *of_vtg_find(struct device_node *np) 104f2cb3148SBenjamin Gaignard { 105f2cb3148SBenjamin Gaignard struct sti_vtg *vtg; 106f2cb3148SBenjamin Gaignard 107f2cb3148SBenjamin Gaignard list_for_each_entry(vtg, &vtg_lookup, link) { 108f2cb3148SBenjamin Gaignard if (vtg->np == np) 109f2cb3148SBenjamin Gaignard return vtg; 110f2cb3148SBenjamin Gaignard } 111f2cb3148SBenjamin Gaignard return NULL; 112f2cb3148SBenjamin Gaignard } 113f2cb3148SBenjamin Gaignard EXPORT_SYMBOL(of_vtg_find); 114f2cb3148SBenjamin Gaignard 115f2cb3148SBenjamin Gaignard static void vtg_reset(struct sti_vtg *vtg) 116f2cb3148SBenjamin Gaignard { 117f2cb3148SBenjamin Gaignard /* reset slave and then master */ 118f2cb3148SBenjamin Gaignard if (vtg->slave) 119f2cb3148SBenjamin Gaignard vtg_reset(vtg->slave); 120f2cb3148SBenjamin Gaignard 121f2cb3148SBenjamin Gaignard writel(1, vtg->regs + VTG_DRST_AUTOC); 122f2cb3148SBenjamin Gaignard } 123f2cb3148SBenjamin Gaignard 124*8eba2703SVincent Abriou static void vtg_set_output_window(void __iomem *regs, 125*8eba2703SVincent Abriou const struct drm_display_mode *mode) 126*8eba2703SVincent Abriou { 127*8eba2703SVincent Abriou u32 video_top_field_start; 128*8eba2703SVincent Abriou u32 video_top_field_stop; 129*8eba2703SVincent Abriou u32 video_bottom_field_start; 130*8eba2703SVincent Abriou u32 video_bottom_field_stop; 131*8eba2703SVincent Abriou u32 xstart = sti_vtg_get_pixel_number(*mode, 0); 132*8eba2703SVincent Abriou u32 ystart = sti_vtg_get_line_number(*mode, 0); 133*8eba2703SVincent Abriou u32 xstop = sti_vtg_get_pixel_number(*mode, mode->hdisplay - 1); 134*8eba2703SVincent Abriou u32 ystop = sti_vtg_get_line_number(*mode, mode->vdisplay - 1); 135*8eba2703SVincent Abriou 136*8eba2703SVincent Abriou /* Set output window to fit the display mode selected */ 137*8eba2703SVincent Abriou video_top_field_start = (ystart << 16) | xstart; 138*8eba2703SVincent Abriou video_top_field_stop = (ystop << 16) | xstop; 139*8eba2703SVincent Abriou 140*8eba2703SVincent Abriou /* Only progressive supported for now */ 141*8eba2703SVincent Abriou video_bottom_field_start = video_top_field_start; 142*8eba2703SVincent Abriou video_bottom_field_stop = video_top_field_stop; 143*8eba2703SVincent Abriou 144*8eba2703SVincent Abriou writel(video_top_field_start, regs + VTG_VID_TFO); 145*8eba2703SVincent Abriou writel(video_top_field_stop, regs + VTG_VID_TFS); 146*8eba2703SVincent Abriou writel(video_bottom_field_start, regs + VTG_VID_BFO); 147*8eba2703SVincent Abriou writel(video_bottom_field_stop, regs + VTG_VID_BFS); 148*8eba2703SVincent Abriou } 149*8eba2703SVincent Abriou 150f2cb3148SBenjamin Gaignard static void vtg_set_mode(struct sti_vtg *vtg, 151f2cb3148SBenjamin Gaignard int type, const struct drm_display_mode *mode) 152f2cb3148SBenjamin Gaignard { 153f2cb3148SBenjamin Gaignard u32 tmp; 154f2cb3148SBenjamin Gaignard 155f2cb3148SBenjamin Gaignard if (vtg->slave) 156f2cb3148SBenjamin Gaignard vtg_set_mode(vtg->slave, VTG_TYPE_SLAVE_BY_EXT0, mode); 157f2cb3148SBenjamin Gaignard 158*8eba2703SVincent Abriou /* Set the number of clock cycles per line */ 159f2cb3148SBenjamin Gaignard writel(mode->htotal, vtg->regs + VTG_CLKLN); 160*8eba2703SVincent Abriou 161*8eba2703SVincent Abriou /* Set Half Line Per Field (only progressive supported for now) */ 162f2cb3148SBenjamin Gaignard writel(mode->vtotal * 2, vtg->regs + VTG_HLFLN); 163f2cb3148SBenjamin Gaignard 164*8eba2703SVincent Abriou /* Program output window */ 165*8eba2703SVincent Abriou vtg_set_output_window(vtg->regs, mode); 166f2cb3148SBenjamin Gaignard 1677f2d479cSBenjamin Gaignard /* prepare VTG set 1 for HDMI */ 1687f2d479cSBenjamin Gaignard tmp = (mode->hsync_end - mode->hsync_start + HDMI_DELAY) << 16; 1697f2d479cSBenjamin Gaignard tmp |= HDMI_DELAY; 170f2cb3148SBenjamin Gaignard writel(tmp, vtg->regs + VTG_H_HD_1); 171f2cb3148SBenjamin Gaignard 172f2cb3148SBenjamin Gaignard tmp = (mode->vsync_end - mode->vsync_start + 1) << 16; 173f2cb3148SBenjamin Gaignard tmp |= 1; 174f2cb3148SBenjamin Gaignard writel(tmp, vtg->regs + VTG_TOP_V_VD_1); 175f2cb3148SBenjamin Gaignard writel(tmp, vtg->regs + VTG_BOT_V_VD_1); 176f2cb3148SBenjamin Gaignard writel(0, vtg->regs + VTG_TOP_V_HD_1); 177f2cb3148SBenjamin Gaignard writel(0, vtg->regs + VTG_BOT_V_HD_1); 178f2cb3148SBenjamin Gaignard 179f2cb3148SBenjamin Gaignard /* prepare VTG set 2 for for HD DCS */ 1807f2d479cSBenjamin Gaignard tmp = (mode->hsync_end - mode->hsync_start) << 16; 1817f2d479cSBenjamin Gaignard writel(tmp, vtg->regs + VTG_H_HD_2); 1827f2d479cSBenjamin Gaignard 1837f2d479cSBenjamin Gaignard tmp = (mode->vsync_end - mode->vsync_start + 1) << 16; 1847f2d479cSBenjamin Gaignard tmp |= 1; 185f2cb3148SBenjamin Gaignard writel(tmp, vtg->regs + VTG_TOP_V_VD_2); 186f2cb3148SBenjamin Gaignard writel(tmp, vtg->regs + VTG_BOT_V_VD_2); 187f2cb3148SBenjamin Gaignard writel(0, vtg->regs + VTG_TOP_V_HD_2); 188f2cb3148SBenjamin Gaignard writel(0, vtg->regs + VTG_BOT_V_HD_2); 189f2cb3148SBenjamin Gaignard 190f2cb3148SBenjamin Gaignard /* prepare VTG set 3 for HD Analog in HD mode */ 191f2cb3148SBenjamin Gaignard tmp = (mode->hsync_end - mode->hsync_start + AWG_DELAY_HD) << 16; 192f2cb3148SBenjamin Gaignard tmp |= mode->htotal + AWG_DELAY_HD; 193f2cb3148SBenjamin Gaignard writel(tmp, vtg->regs + VTG_H_HD_3); 194f2cb3148SBenjamin Gaignard 195f2cb3148SBenjamin Gaignard tmp = (mode->vsync_end - mode->vsync_start) << 16; 196f2cb3148SBenjamin Gaignard tmp |= mode->vtotal; 197f2cb3148SBenjamin Gaignard writel(tmp, vtg->regs + VTG_TOP_V_VD_3); 198f2cb3148SBenjamin Gaignard writel(tmp, vtg->regs + VTG_BOT_V_VD_3); 199f2cb3148SBenjamin Gaignard 200f2cb3148SBenjamin Gaignard tmp = (mode->htotal + AWG_DELAY_HD) << 16; 201f2cb3148SBenjamin Gaignard tmp |= mode->htotal + AWG_DELAY_HD; 202f2cb3148SBenjamin Gaignard writel(tmp, vtg->regs + VTG_TOP_V_HD_3); 203f2cb3148SBenjamin Gaignard writel(tmp, vtg->regs + VTG_BOT_V_HD_3); 204f2cb3148SBenjamin Gaignard 2057f2d479cSBenjamin Gaignard /* Prepare VTG set 4 for DVO */ 2067f2d479cSBenjamin Gaignard tmp = (mode->hsync_end - mode->hsync_start) << 16; 2077f2d479cSBenjamin Gaignard writel(tmp, vtg->regs + VTG_H_HD_4); 2087f2d479cSBenjamin Gaignard 2097f2d479cSBenjamin Gaignard tmp = (mode->vsync_end - mode->vsync_start + 1) << 16; 2107f2d479cSBenjamin Gaignard tmp |= 1; 2117f2d479cSBenjamin Gaignard writel(tmp, vtg->regs + VTG_TOP_V_VD_4); 2127f2d479cSBenjamin Gaignard writel(tmp, vtg->regs + VTG_BOT_V_VD_4); 2137f2d479cSBenjamin Gaignard writel(0, vtg->regs + VTG_TOP_V_HD_4); 2147f2d479cSBenjamin Gaignard writel(0, vtg->regs + VTG_BOT_V_HD_4); 2157f2d479cSBenjamin Gaignard 216f2cb3148SBenjamin Gaignard /* mode */ 217f2cb3148SBenjamin Gaignard writel(type, vtg->regs + VTG_MODE); 218f2cb3148SBenjamin Gaignard } 219f2cb3148SBenjamin Gaignard 220f2cb3148SBenjamin Gaignard static void vtg_enable_irq(struct sti_vtg *vtg) 221f2cb3148SBenjamin Gaignard { 222f2cb3148SBenjamin Gaignard /* clear interrupt status and mask */ 223f2cb3148SBenjamin Gaignard writel(0xFFFF, vtg->regs + VTG_HOST_ITS_BCLR); 224f2cb3148SBenjamin Gaignard writel(0xFFFF, vtg->regs + VTG_HOST_ITM_BCLR); 225f2cb3148SBenjamin Gaignard writel(VTG_IRQ_MASK, vtg->regs + VTG_HOST_ITM_BSET); 226f2cb3148SBenjamin Gaignard } 227f2cb3148SBenjamin Gaignard 228f2cb3148SBenjamin Gaignard void sti_vtg_set_config(struct sti_vtg *vtg, 229f2cb3148SBenjamin Gaignard const struct drm_display_mode *mode) 230f2cb3148SBenjamin Gaignard { 231f2cb3148SBenjamin Gaignard /* write configuration */ 232f2cb3148SBenjamin Gaignard vtg_set_mode(vtg, VTG_TYPE_MASTER, mode); 233f2cb3148SBenjamin Gaignard 234f2cb3148SBenjamin Gaignard vtg_reset(vtg); 235f2cb3148SBenjamin Gaignard 236f2cb3148SBenjamin Gaignard /* enable irq for the vtg vblank synchro */ 237f2cb3148SBenjamin Gaignard if (vtg->slave) 238f2cb3148SBenjamin Gaignard vtg_enable_irq(vtg->slave); 239f2cb3148SBenjamin Gaignard else 240f2cb3148SBenjamin Gaignard vtg_enable_irq(vtg); 241f2cb3148SBenjamin Gaignard } 242f2cb3148SBenjamin Gaignard EXPORT_SYMBOL(sti_vtg_set_config); 243f2cb3148SBenjamin Gaignard 244f2cb3148SBenjamin Gaignard /** 245f2cb3148SBenjamin Gaignard * sti_vtg_get_line_number 246f2cb3148SBenjamin Gaignard * 247f2cb3148SBenjamin Gaignard * @mode: display mode to be used 248f2cb3148SBenjamin Gaignard * @y: line 249f2cb3148SBenjamin Gaignard * 250f2cb3148SBenjamin Gaignard * Return the line number according to the display mode taking 251f2cb3148SBenjamin Gaignard * into account the Sync and Back Porch information. 252f2cb3148SBenjamin Gaignard * Video frame line numbers start at 1, y starts at 0. 253f2cb3148SBenjamin Gaignard * In interlaced modes the start line is the field line number of the odd 254f2cb3148SBenjamin Gaignard * field, but y is still defined as a progressive frame. 255f2cb3148SBenjamin Gaignard */ 256f2cb3148SBenjamin Gaignard u32 sti_vtg_get_line_number(struct drm_display_mode mode, int y) 257f2cb3148SBenjamin Gaignard { 258f2cb3148SBenjamin Gaignard u32 start_line = mode.vtotal - mode.vsync_start + 1; 259f2cb3148SBenjamin Gaignard 260f2cb3148SBenjamin Gaignard if (mode.flags & DRM_MODE_FLAG_INTERLACE) 261f2cb3148SBenjamin Gaignard start_line *= 2; 262f2cb3148SBenjamin Gaignard 263f2cb3148SBenjamin Gaignard return start_line + y; 264f2cb3148SBenjamin Gaignard } 265f2cb3148SBenjamin Gaignard EXPORT_SYMBOL(sti_vtg_get_line_number); 266f2cb3148SBenjamin Gaignard 267f2cb3148SBenjamin Gaignard /** 268f2cb3148SBenjamin Gaignard * sti_vtg_get_pixel_number 269f2cb3148SBenjamin Gaignard * 270f2cb3148SBenjamin Gaignard * @mode: display mode to be used 271f2cb3148SBenjamin Gaignard * @x: row 272f2cb3148SBenjamin Gaignard * 273f2cb3148SBenjamin Gaignard * Return the pixel number according to the display mode taking 274f2cb3148SBenjamin Gaignard * into account the Sync and Back Porch information. 275f2cb3148SBenjamin Gaignard * Pixels are counted from 0. 276f2cb3148SBenjamin Gaignard */ 277f2cb3148SBenjamin Gaignard u32 sti_vtg_get_pixel_number(struct drm_display_mode mode, int x) 278f2cb3148SBenjamin Gaignard { 279f2cb3148SBenjamin Gaignard return mode.htotal - mode.hsync_start + x; 280f2cb3148SBenjamin Gaignard } 281f2cb3148SBenjamin Gaignard EXPORT_SYMBOL(sti_vtg_get_pixel_number); 282f2cb3148SBenjamin Gaignard 283f2cb3148SBenjamin Gaignard int sti_vtg_register_client(struct sti_vtg *vtg, 284f2cb3148SBenjamin Gaignard struct notifier_block *nb, int crtc_id) 285f2cb3148SBenjamin Gaignard { 286f2cb3148SBenjamin Gaignard if (vtg->slave) 287f2cb3148SBenjamin Gaignard return sti_vtg_register_client(vtg->slave, nb, crtc_id); 288f2cb3148SBenjamin Gaignard 289f2cb3148SBenjamin Gaignard vtg->crtc_id = crtc_id; 290f2cb3148SBenjamin Gaignard return raw_notifier_chain_register(&vtg->notifier_list, nb); 291f2cb3148SBenjamin Gaignard } 292f2cb3148SBenjamin Gaignard EXPORT_SYMBOL(sti_vtg_register_client); 293f2cb3148SBenjamin Gaignard 294f2cb3148SBenjamin Gaignard int sti_vtg_unregister_client(struct sti_vtg *vtg, struct notifier_block *nb) 295f2cb3148SBenjamin Gaignard { 296f2cb3148SBenjamin Gaignard if (vtg->slave) 297f2cb3148SBenjamin Gaignard return sti_vtg_unregister_client(vtg->slave, nb); 298f2cb3148SBenjamin Gaignard 299f2cb3148SBenjamin Gaignard return raw_notifier_chain_unregister(&vtg->notifier_list, nb); 300f2cb3148SBenjamin Gaignard } 301f2cb3148SBenjamin Gaignard EXPORT_SYMBOL(sti_vtg_unregister_client); 302f2cb3148SBenjamin Gaignard 303f2cb3148SBenjamin Gaignard static irqreturn_t vtg_irq_thread(int irq, void *arg) 304f2cb3148SBenjamin Gaignard { 305f2cb3148SBenjamin Gaignard struct sti_vtg *vtg = arg; 306f2cb3148SBenjamin Gaignard u32 event; 307f2cb3148SBenjamin Gaignard 308f2cb3148SBenjamin Gaignard event = (vtg->irq_status & VTG_IRQ_TOP) ? 309f2cb3148SBenjamin Gaignard VTG_TOP_FIELD_EVENT : VTG_BOTTOM_FIELD_EVENT; 310f2cb3148SBenjamin Gaignard 311f2cb3148SBenjamin Gaignard raw_notifier_call_chain(&vtg->notifier_list, event, &vtg->crtc_id); 312f2cb3148SBenjamin Gaignard 313f2cb3148SBenjamin Gaignard return IRQ_HANDLED; 314f2cb3148SBenjamin Gaignard } 315f2cb3148SBenjamin Gaignard 316f2cb3148SBenjamin Gaignard static irqreturn_t vtg_irq(int irq, void *arg) 317f2cb3148SBenjamin Gaignard { 318f2cb3148SBenjamin Gaignard struct sti_vtg *vtg = arg; 319f2cb3148SBenjamin Gaignard 320f2cb3148SBenjamin Gaignard vtg->irq_status = readl(vtg->regs + VTG_HOST_ITS); 321f2cb3148SBenjamin Gaignard 322f2cb3148SBenjamin Gaignard writel(vtg->irq_status, vtg->regs + VTG_HOST_ITS_BCLR); 323f2cb3148SBenjamin Gaignard 324f2cb3148SBenjamin Gaignard /* force sync bus write */ 325f2cb3148SBenjamin Gaignard readl(vtg->regs + VTG_HOST_ITS); 326f2cb3148SBenjamin Gaignard 327f2cb3148SBenjamin Gaignard return IRQ_WAKE_THREAD; 328f2cb3148SBenjamin Gaignard } 329f2cb3148SBenjamin Gaignard 330f2cb3148SBenjamin Gaignard static int vtg_probe(struct platform_device *pdev) 331f2cb3148SBenjamin Gaignard { 332f2cb3148SBenjamin Gaignard struct device *dev = &pdev->dev; 333f2cb3148SBenjamin Gaignard struct device_node *np; 334f2cb3148SBenjamin Gaignard struct sti_vtg *vtg; 335f2cb3148SBenjamin Gaignard struct resource *res; 336f2cb3148SBenjamin Gaignard char irq_name[32]; 337f2cb3148SBenjamin Gaignard int ret; 338f2cb3148SBenjamin Gaignard 339f2cb3148SBenjamin Gaignard vtg = devm_kzalloc(dev, sizeof(*vtg), GFP_KERNEL); 340f2cb3148SBenjamin Gaignard if (!vtg) 341f2cb3148SBenjamin Gaignard return -ENOMEM; 342f2cb3148SBenjamin Gaignard 343f2cb3148SBenjamin Gaignard vtg->dev = dev; 344f2cb3148SBenjamin Gaignard vtg->np = pdev->dev.of_node; 345f2cb3148SBenjamin Gaignard 346f2cb3148SBenjamin Gaignard /* Get Memory ressources */ 347f2cb3148SBenjamin Gaignard res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 348f2cb3148SBenjamin Gaignard if (!res) { 349f2cb3148SBenjamin Gaignard DRM_ERROR("Get memory resource failed\n"); 350f2cb3148SBenjamin Gaignard return -ENOMEM; 351f2cb3148SBenjamin Gaignard } 352f2cb3148SBenjamin Gaignard vtg->regs = devm_ioremap_nocache(dev, res->start, resource_size(res)); 353f2cb3148SBenjamin Gaignard 354f2cb3148SBenjamin Gaignard np = of_parse_phandle(pdev->dev.of_node, "st,slave", 0); 355f2cb3148SBenjamin Gaignard if (np) { 356f2cb3148SBenjamin Gaignard vtg->slave = of_vtg_find(np); 357f2cb3148SBenjamin Gaignard 358f2cb3148SBenjamin Gaignard if (!vtg->slave) 359f2cb3148SBenjamin Gaignard return -EPROBE_DEFER; 360f2cb3148SBenjamin Gaignard } else { 361f2cb3148SBenjamin Gaignard vtg->irq = platform_get_irq(pdev, 0); 362f2cb3148SBenjamin Gaignard if (IS_ERR_VALUE(vtg->irq)) { 363f2cb3148SBenjamin Gaignard DRM_ERROR("Failed to get VTG interrupt\n"); 364f2cb3148SBenjamin Gaignard return vtg->irq; 365f2cb3148SBenjamin Gaignard } 366f2cb3148SBenjamin Gaignard 367f2cb3148SBenjamin Gaignard snprintf(irq_name, sizeof(irq_name), "vsync-%s", 368f2cb3148SBenjamin Gaignard dev_name(vtg->dev)); 369f2cb3148SBenjamin Gaignard 370f2cb3148SBenjamin Gaignard RAW_INIT_NOTIFIER_HEAD(&vtg->notifier_list); 371f2cb3148SBenjamin Gaignard 372f2cb3148SBenjamin Gaignard ret = devm_request_threaded_irq(dev, vtg->irq, vtg_irq, 373f2cb3148SBenjamin Gaignard vtg_irq_thread, IRQF_ONESHOT, irq_name, vtg); 374f2cb3148SBenjamin Gaignard if (IS_ERR_VALUE(ret)) { 375f2cb3148SBenjamin Gaignard DRM_ERROR("Failed to register VTG interrupt\n"); 376f2cb3148SBenjamin Gaignard return ret; 377f2cb3148SBenjamin Gaignard } 378f2cb3148SBenjamin Gaignard } 379f2cb3148SBenjamin Gaignard 380f2cb3148SBenjamin Gaignard vtg_register(vtg); 381f2cb3148SBenjamin Gaignard platform_set_drvdata(pdev, vtg); 382f2cb3148SBenjamin Gaignard 383f2cb3148SBenjamin Gaignard DRM_INFO("%s %s\n", __func__, dev_name(vtg->dev)); 384f2cb3148SBenjamin Gaignard 385f2cb3148SBenjamin Gaignard return 0; 386f2cb3148SBenjamin Gaignard } 387f2cb3148SBenjamin Gaignard 388f2cb3148SBenjamin Gaignard static int vtg_remove(struct platform_device *pdev) 389f2cb3148SBenjamin Gaignard { 390f2cb3148SBenjamin Gaignard return 0; 391f2cb3148SBenjamin Gaignard } 392f2cb3148SBenjamin Gaignard 393f2cb3148SBenjamin Gaignard static const struct of_device_id vtg_of_match[] = { 394f2cb3148SBenjamin Gaignard { .compatible = "st,vtg", }, 395f2cb3148SBenjamin Gaignard { /* sentinel */ } 396f2cb3148SBenjamin Gaignard }; 397f2cb3148SBenjamin Gaignard MODULE_DEVICE_TABLE(of, vtg_of_match); 398f2cb3148SBenjamin Gaignard 399f2cb3148SBenjamin Gaignard struct platform_driver sti_vtg_driver = { 400f2cb3148SBenjamin Gaignard .driver = { 401f2cb3148SBenjamin Gaignard .name = "sti-vtg", 402f2cb3148SBenjamin Gaignard .owner = THIS_MODULE, 403f2cb3148SBenjamin Gaignard .of_match_table = vtg_of_match, 404f2cb3148SBenjamin Gaignard }, 405f2cb3148SBenjamin Gaignard .probe = vtg_probe, 406f2cb3148SBenjamin Gaignard .remove = vtg_remove, 407f2cb3148SBenjamin Gaignard }; 408f2cb3148SBenjamin Gaignard 409f2cb3148SBenjamin Gaignard module_platform_driver(sti_vtg_driver); 410f2cb3148SBenjamin Gaignard 411f2cb3148SBenjamin Gaignard MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>"); 412f2cb3148SBenjamin Gaignard MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver"); 413f2cb3148SBenjamin Gaignard MODULE_LICENSE("GPL"); 414