162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) STMicroelectronics SA 2014 462306a36Sopenharmony_ci * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> 562306a36Sopenharmony_ci * Fabien Dessenne <fabien.dessenne@st.com> 662306a36Sopenharmony_ci * Vincent Abriou <vincent.abriou@st.com> 762306a36Sopenharmony_ci * for STMicroelectronics. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/notifier.h> 1362306a36Sopenharmony_ci#include <linux/of_platform.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <drm/drm_modes.h> 1762306a36Sopenharmony_ci#include <drm/drm_print.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "sti_drv.h" 2062306a36Sopenharmony_ci#include "sti_vtg.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define VTG_MODE_MASTER 0 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* registers offset */ 2562306a36Sopenharmony_ci#define VTG_MODE 0x0000 2662306a36Sopenharmony_ci#define VTG_CLKLN 0x0008 2762306a36Sopenharmony_ci#define VTG_HLFLN 0x000C 2862306a36Sopenharmony_ci#define VTG_DRST_AUTOC 0x0010 2962306a36Sopenharmony_ci#define VTG_VID_TFO 0x0040 3062306a36Sopenharmony_ci#define VTG_VID_TFS 0x0044 3162306a36Sopenharmony_ci#define VTG_VID_BFO 0x0048 3262306a36Sopenharmony_ci#define VTG_VID_BFS 0x004C 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define VTG_HOST_ITS 0x0078 3562306a36Sopenharmony_ci#define VTG_HOST_ITS_BCLR 0x007C 3662306a36Sopenharmony_ci#define VTG_HOST_ITM_BCLR 0x0088 3762306a36Sopenharmony_ci#define VTG_HOST_ITM_BSET 0x008C 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define VTG_H_HD_1 0x00C0 4062306a36Sopenharmony_ci#define VTG_TOP_V_VD_1 0x00C4 4162306a36Sopenharmony_ci#define VTG_BOT_V_VD_1 0x00C8 4262306a36Sopenharmony_ci#define VTG_TOP_V_HD_1 0x00CC 4362306a36Sopenharmony_ci#define VTG_BOT_V_HD_1 0x00D0 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define VTG_H_HD_2 0x00E0 4662306a36Sopenharmony_ci#define VTG_TOP_V_VD_2 0x00E4 4762306a36Sopenharmony_ci#define VTG_BOT_V_VD_2 0x00E8 4862306a36Sopenharmony_ci#define VTG_TOP_V_HD_2 0x00EC 4962306a36Sopenharmony_ci#define VTG_BOT_V_HD_2 0x00F0 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define VTG_H_HD_3 0x0100 5262306a36Sopenharmony_ci#define VTG_TOP_V_VD_3 0x0104 5362306a36Sopenharmony_ci#define VTG_BOT_V_VD_3 0x0108 5462306a36Sopenharmony_ci#define VTG_TOP_V_HD_3 0x010C 5562306a36Sopenharmony_ci#define VTG_BOT_V_HD_3 0x0110 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define VTG_H_HD_4 0x0120 5862306a36Sopenharmony_ci#define VTG_TOP_V_VD_4 0x0124 5962306a36Sopenharmony_ci#define VTG_BOT_V_VD_4 0x0128 6062306a36Sopenharmony_ci#define VTG_TOP_V_HD_4 0x012c 6162306a36Sopenharmony_ci#define VTG_BOT_V_HD_4 0x0130 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define VTG_IRQ_BOTTOM BIT(0) 6462306a36Sopenharmony_ci#define VTG_IRQ_TOP BIT(1) 6562306a36Sopenharmony_ci#define VTG_IRQ_MASK (VTG_IRQ_TOP | VTG_IRQ_BOTTOM) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* Delay introduced by the HDMI in nb of pixel */ 6862306a36Sopenharmony_ci#define HDMI_DELAY (5) 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* Delay introduced by the DVO in nb of pixel */ 7162306a36Sopenharmony_ci#define DVO_DELAY (7) 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* delay introduced by the Arbitrary Waveform Generator in nb of pixels */ 7462306a36Sopenharmony_ci#define AWG_DELAY_HD (-9) 7562306a36Sopenharmony_ci#define AWG_DELAY_ED (-8) 7662306a36Sopenharmony_ci#define AWG_DELAY_SD (-7) 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* 7962306a36Sopenharmony_ci * STI VTG register offset structure 8062306a36Sopenharmony_ci * 8162306a36Sopenharmony_ci *@h_hd: stores the VTG_H_HD_x register offset 8262306a36Sopenharmony_ci *@top_v_vd: stores the VTG_TOP_V_VD_x register offset 8362306a36Sopenharmony_ci *@bot_v_vd: stores the VTG_BOT_V_VD_x register offset 8462306a36Sopenharmony_ci *@top_v_hd: stores the VTG_TOP_V_HD_x register offset 8562306a36Sopenharmony_ci *@bot_v_hd: stores the VTG_BOT_V_HD_x register offset 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_cistruct sti_vtg_regs_offs { 8862306a36Sopenharmony_ci u32 h_hd; 8962306a36Sopenharmony_ci u32 top_v_vd; 9062306a36Sopenharmony_ci u32 bot_v_vd; 9162306a36Sopenharmony_ci u32 top_v_hd; 9262306a36Sopenharmony_ci u32 bot_v_hd; 9362306a36Sopenharmony_ci}; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define VTG_MAX_SYNC_OUTPUT 4 9662306a36Sopenharmony_cistatic const struct sti_vtg_regs_offs vtg_regs_offs[VTG_MAX_SYNC_OUTPUT] = { 9762306a36Sopenharmony_ci { VTG_H_HD_1, 9862306a36Sopenharmony_ci VTG_TOP_V_VD_1, VTG_BOT_V_VD_1, VTG_TOP_V_HD_1, VTG_BOT_V_HD_1 }, 9962306a36Sopenharmony_ci { VTG_H_HD_2, 10062306a36Sopenharmony_ci VTG_TOP_V_VD_2, VTG_BOT_V_VD_2, VTG_TOP_V_HD_2, VTG_BOT_V_HD_2 }, 10162306a36Sopenharmony_ci { VTG_H_HD_3, 10262306a36Sopenharmony_ci VTG_TOP_V_VD_3, VTG_BOT_V_VD_3, VTG_TOP_V_HD_3, VTG_BOT_V_HD_3 }, 10362306a36Sopenharmony_ci { VTG_H_HD_4, 10462306a36Sopenharmony_ci VTG_TOP_V_VD_4, VTG_BOT_V_VD_4, VTG_TOP_V_HD_4, VTG_BOT_V_HD_4 } 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* 10862306a36Sopenharmony_ci * STI VTG synchronisation parameters structure 10962306a36Sopenharmony_ci * 11062306a36Sopenharmony_ci *@hsync: sample number falling and rising edge 11162306a36Sopenharmony_ci *@vsync_line_top: vertical top field line number falling and rising edge 11262306a36Sopenharmony_ci *@vsync_line_bot: vertical bottom field line number falling and rising edge 11362306a36Sopenharmony_ci *@vsync_off_top: vertical top field sample number rising and falling edge 11462306a36Sopenharmony_ci *@vsync_off_bot: vertical bottom field sample number rising and falling edge 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_cistruct sti_vtg_sync_params { 11762306a36Sopenharmony_ci u32 hsync; 11862306a36Sopenharmony_ci u32 vsync_line_top; 11962306a36Sopenharmony_ci u32 vsync_line_bot; 12062306a36Sopenharmony_ci u32 vsync_off_top; 12162306a36Sopenharmony_ci u32 vsync_off_bot; 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* 12562306a36Sopenharmony_ci * STI VTG structure 12662306a36Sopenharmony_ci * 12762306a36Sopenharmony_ci * @regs: register mapping 12862306a36Sopenharmony_ci * @sync_params: synchronisation parameters used to generate timings 12962306a36Sopenharmony_ci * @irq: VTG irq 13062306a36Sopenharmony_ci * @irq_status: store the IRQ status value 13162306a36Sopenharmony_ci * @notifier_list: notifier callback 13262306a36Sopenharmony_ci * @crtc: the CRTC for vblank event 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_cistruct sti_vtg { 13562306a36Sopenharmony_ci void __iomem *regs; 13662306a36Sopenharmony_ci struct sti_vtg_sync_params sync_params[VTG_MAX_SYNC_OUTPUT]; 13762306a36Sopenharmony_ci int irq; 13862306a36Sopenharmony_ci u32 irq_status; 13962306a36Sopenharmony_ci struct raw_notifier_head notifier_list; 14062306a36Sopenharmony_ci struct drm_crtc *crtc; 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistruct sti_vtg *of_vtg_find(struct device_node *np) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct platform_device *pdev; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci pdev = of_find_device_by_node(np); 14862306a36Sopenharmony_ci if (!pdev) 14962306a36Sopenharmony_ci return NULL; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return (struct sti_vtg *)platform_get_drvdata(pdev); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void vtg_reset(struct sti_vtg *vtg) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci writel(1, vtg->regs + VTG_DRST_AUTOC); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic void vtg_set_output_window(void __iomem *regs, 16062306a36Sopenharmony_ci const struct drm_display_mode *mode) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci u32 video_top_field_start; 16362306a36Sopenharmony_ci u32 video_top_field_stop; 16462306a36Sopenharmony_ci u32 video_bottom_field_start; 16562306a36Sopenharmony_ci u32 video_bottom_field_stop; 16662306a36Sopenharmony_ci u32 xstart = sti_vtg_get_pixel_number(*mode, 0); 16762306a36Sopenharmony_ci u32 ystart = sti_vtg_get_line_number(*mode, 0); 16862306a36Sopenharmony_ci u32 xstop = sti_vtg_get_pixel_number(*mode, mode->hdisplay - 1); 16962306a36Sopenharmony_ci u32 ystop = sti_vtg_get_line_number(*mode, mode->vdisplay - 1); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* Set output window to fit the display mode selected */ 17262306a36Sopenharmony_ci video_top_field_start = (ystart << 16) | xstart; 17362306a36Sopenharmony_ci video_top_field_stop = (ystop << 16) | xstop; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* Only progressive supported for now */ 17662306a36Sopenharmony_ci video_bottom_field_start = video_top_field_start; 17762306a36Sopenharmony_ci video_bottom_field_stop = video_top_field_stop; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci writel(video_top_field_start, regs + VTG_VID_TFO); 18062306a36Sopenharmony_ci writel(video_top_field_stop, regs + VTG_VID_TFS); 18162306a36Sopenharmony_ci writel(video_bottom_field_start, regs + VTG_VID_BFO); 18262306a36Sopenharmony_ci writel(video_bottom_field_stop, regs + VTG_VID_BFS); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic void vtg_set_hsync_vsync_pos(struct sti_vtg_sync_params *sync, 18662306a36Sopenharmony_ci int delay, 18762306a36Sopenharmony_ci const struct drm_display_mode *mode) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci long clocksperline, start, stop; 19062306a36Sopenharmony_ci u32 risesync_top, fallsync_top; 19162306a36Sopenharmony_ci u32 risesync_offs_top, fallsync_offs_top; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci clocksperline = mode->htotal; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* Get the hsync position */ 19662306a36Sopenharmony_ci start = 0; 19762306a36Sopenharmony_ci stop = mode->hsync_end - mode->hsync_start; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci start += delay; 20062306a36Sopenharmony_ci stop += delay; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (start < 0) 20362306a36Sopenharmony_ci start += clocksperline; 20462306a36Sopenharmony_ci else if (start >= clocksperline) 20562306a36Sopenharmony_ci start -= clocksperline; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (stop < 0) 20862306a36Sopenharmony_ci stop += clocksperline; 20962306a36Sopenharmony_ci else if (stop >= clocksperline) 21062306a36Sopenharmony_ci stop -= clocksperline; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci sync->hsync = (stop << 16) | start; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* Get the vsync position */ 21562306a36Sopenharmony_ci if (delay >= 0) { 21662306a36Sopenharmony_ci risesync_top = 1; 21762306a36Sopenharmony_ci fallsync_top = risesync_top; 21862306a36Sopenharmony_ci fallsync_top += mode->vsync_end - mode->vsync_start; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci fallsync_offs_top = (u32)delay; 22162306a36Sopenharmony_ci risesync_offs_top = (u32)delay; 22262306a36Sopenharmony_ci } else { 22362306a36Sopenharmony_ci risesync_top = mode->vtotal; 22462306a36Sopenharmony_ci fallsync_top = mode->vsync_end - mode->vsync_start; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci fallsync_offs_top = clocksperline + delay; 22762306a36Sopenharmony_ci risesync_offs_top = clocksperline + delay; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci sync->vsync_line_top = (fallsync_top << 16) | risesync_top; 23162306a36Sopenharmony_ci sync->vsync_off_top = (fallsync_offs_top << 16) | risesync_offs_top; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* Only progressive supported for now */ 23462306a36Sopenharmony_ci sync->vsync_line_bot = sync->vsync_line_top; 23562306a36Sopenharmony_ci sync->vsync_off_bot = sync->vsync_off_top; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic void vtg_set_mode(struct sti_vtg *vtg, 23962306a36Sopenharmony_ci int type, 24062306a36Sopenharmony_ci struct sti_vtg_sync_params *sync, 24162306a36Sopenharmony_ci const struct drm_display_mode *mode) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci unsigned int i; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* Set the number of clock cycles per line */ 24662306a36Sopenharmony_ci writel(mode->htotal, vtg->regs + VTG_CLKLN); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* Set Half Line Per Field (only progressive supported for now) */ 24962306a36Sopenharmony_ci writel(mode->vtotal * 2, vtg->regs + VTG_HLFLN); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* Program output window */ 25262306a36Sopenharmony_ci vtg_set_output_window(vtg->regs, mode); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* Set hsync and vsync position for HDMI */ 25562306a36Sopenharmony_ci vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_HDMI - 1], HDMI_DELAY, mode); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* Set hsync and vsync position for HD DCS */ 25862306a36Sopenharmony_ci vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_HDDCS - 1], 0, mode); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* Set hsync and vsync position for HDF */ 26162306a36Sopenharmony_ci vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_HDF - 1], AWG_DELAY_HD, mode); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* Set hsync and vsync position for DVO */ 26462306a36Sopenharmony_ci vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_DVO - 1], DVO_DELAY, mode); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* Progam the syncs outputs */ 26762306a36Sopenharmony_ci for (i = 0; i < VTG_MAX_SYNC_OUTPUT ; i++) { 26862306a36Sopenharmony_ci writel(sync[i].hsync, 26962306a36Sopenharmony_ci vtg->regs + vtg_regs_offs[i].h_hd); 27062306a36Sopenharmony_ci writel(sync[i].vsync_line_top, 27162306a36Sopenharmony_ci vtg->regs + vtg_regs_offs[i].top_v_vd); 27262306a36Sopenharmony_ci writel(sync[i].vsync_line_bot, 27362306a36Sopenharmony_ci vtg->regs + vtg_regs_offs[i].bot_v_vd); 27462306a36Sopenharmony_ci writel(sync[i].vsync_off_top, 27562306a36Sopenharmony_ci vtg->regs + vtg_regs_offs[i].top_v_hd); 27662306a36Sopenharmony_ci writel(sync[i].vsync_off_bot, 27762306a36Sopenharmony_ci vtg->regs + vtg_regs_offs[i].bot_v_hd); 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* mode */ 28162306a36Sopenharmony_ci writel(type, vtg->regs + VTG_MODE); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic void vtg_enable_irq(struct sti_vtg *vtg) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci /* clear interrupt status and mask */ 28762306a36Sopenharmony_ci writel(0xFFFF, vtg->regs + VTG_HOST_ITS_BCLR); 28862306a36Sopenharmony_ci writel(0xFFFF, vtg->regs + VTG_HOST_ITM_BCLR); 28962306a36Sopenharmony_ci writel(VTG_IRQ_MASK, vtg->regs + VTG_HOST_ITM_BSET); 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_civoid sti_vtg_set_config(struct sti_vtg *vtg, 29362306a36Sopenharmony_ci const struct drm_display_mode *mode) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci /* write configuration */ 29662306a36Sopenharmony_ci vtg_set_mode(vtg, VTG_MODE_MASTER, vtg->sync_params, mode); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci vtg_reset(vtg); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci vtg_enable_irq(vtg); 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci/** 30462306a36Sopenharmony_ci * sti_vtg_get_line_number 30562306a36Sopenharmony_ci * 30662306a36Sopenharmony_ci * @mode: display mode to be used 30762306a36Sopenharmony_ci * @y: line 30862306a36Sopenharmony_ci * 30962306a36Sopenharmony_ci * Return the line number according to the display mode taking 31062306a36Sopenharmony_ci * into account the Sync and Back Porch information. 31162306a36Sopenharmony_ci * Video frame line numbers start at 1, y starts at 0. 31262306a36Sopenharmony_ci * In interlaced modes the start line is the field line number of the odd 31362306a36Sopenharmony_ci * field, but y is still defined as a progressive frame. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ciu32 sti_vtg_get_line_number(struct drm_display_mode mode, int y) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci u32 start_line = mode.vtotal - mode.vsync_start + 1; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (mode.flags & DRM_MODE_FLAG_INTERLACE) 32062306a36Sopenharmony_ci start_line *= 2; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return start_line + y; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci/** 32662306a36Sopenharmony_ci * sti_vtg_get_pixel_number 32762306a36Sopenharmony_ci * 32862306a36Sopenharmony_ci * @mode: display mode to be used 32962306a36Sopenharmony_ci * @x: row 33062306a36Sopenharmony_ci * 33162306a36Sopenharmony_ci * Return the pixel number according to the display mode taking 33262306a36Sopenharmony_ci * into account the Sync and Back Porch information. 33362306a36Sopenharmony_ci * Pixels are counted from 0. 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_ciu32 sti_vtg_get_pixel_number(struct drm_display_mode mode, int x) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci return mode.htotal - mode.hsync_start + x; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ciint sti_vtg_register_client(struct sti_vtg *vtg, struct notifier_block *nb, 34162306a36Sopenharmony_ci struct drm_crtc *crtc) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci vtg->crtc = crtc; 34462306a36Sopenharmony_ci return raw_notifier_chain_register(&vtg->notifier_list, nb); 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ciint sti_vtg_unregister_client(struct sti_vtg *vtg, struct notifier_block *nb) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci return raw_notifier_chain_unregister(&vtg->notifier_list, nb); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic irqreturn_t vtg_irq_thread(int irq, void *arg) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct sti_vtg *vtg = arg; 35562306a36Sopenharmony_ci u32 event; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci event = (vtg->irq_status & VTG_IRQ_TOP) ? 35862306a36Sopenharmony_ci VTG_TOP_FIELD_EVENT : VTG_BOTTOM_FIELD_EVENT; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci raw_notifier_call_chain(&vtg->notifier_list, event, vtg->crtc); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci return IRQ_HANDLED; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic irqreturn_t vtg_irq(int irq, void *arg) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci struct sti_vtg *vtg = arg; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci vtg->irq_status = readl(vtg->regs + VTG_HOST_ITS); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci writel(vtg->irq_status, vtg->regs + VTG_HOST_ITS_BCLR); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* force sync bus write */ 37462306a36Sopenharmony_ci readl(vtg->regs + VTG_HOST_ITS); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic int vtg_probe(struct platform_device *pdev) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 38262306a36Sopenharmony_ci struct sti_vtg *vtg; 38362306a36Sopenharmony_ci struct resource *res; 38462306a36Sopenharmony_ci int ret; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci vtg = devm_kzalloc(dev, sizeof(*vtg), GFP_KERNEL); 38762306a36Sopenharmony_ci if (!vtg) 38862306a36Sopenharmony_ci return -ENOMEM; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* Get Memory ressources */ 39162306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 39262306a36Sopenharmony_ci if (!res) { 39362306a36Sopenharmony_ci DRM_ERROR("Get memory resource failed\n"); 39462306a36Sopenharmony_ci return -ENOMEM; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci vtg->regs = devm_ioremap(dev, res->start, resource_size(res)); 39762306a36Sopenharmony_ci if (!vtg->regs) { 39862306a36Sopenharmony_ci DRM_ERROR("failed to remap I/O memory\n"); 39962306a36Sopenharmony_ci return -ENOMEM; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci vtg->irq = platform_get_irq(pdev, 0); 40362306a36Sopenharmony_ci if (vtg->irq < 0) { 40462306a36Sopenharmony_ci DRM_ERROR("Failed to get VTG interrupt\n"); 40562306a36Sopenharmony_ci return vtg->irq; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci RAW_INIT_NOTIFIER_HEAD(&vtg->notifier_list); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci ret = devm_request_threaded_irq(dev, vtg->irq, vtg_irq, 41162306a36Sopenharmony_ci vtg_irq_thread, IRQF_ONESHOT, 41262306a36Sopenharmony_ci dev_name(dev), vtg); 41362306a36Sopenharmony_ci if (ret < 0) { 41462306a36Sopenharmony_ci DRM_ERROR("Failed to register VTG interrupt\n"); 41562306a36Sopenharmony_ci return ret; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci platform_set_drvdata(pdev, vtg); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci DRM_INFO("%s %s\n", __func__, dev_name(dev)); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci return 0; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic const struct of_device_id vtg_of_match[] = { 42662306a36Sopenharmony_ci { .compatible = "st,vtg", }, 42762306a36Sopenharmony_ci { /* sentinel */ } 42862306a36Sopenharmony_ci}; 42962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, vtg_of_match); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistruct platform_driver sti_vtg_driver = { 43262306a36Sopenharmony_ci .driver = { 43362306a36Sopenharmony_ci .name = "sti-vtg", 43462306a36Sopenharmony_ci .owner = THIS_MODULE, 43562306a36Sopenharmony_ci .of_match_table = vtg_of_match, 43662306a36Sopenharmony_ci }, 43762306a36Sopenharmony_ci .probe = vtg_probe, 43862306a36Sopenharmony_ci}; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ciMODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>"); 44162306a36Sopenharmony_ciMODULE_DESCRIPTION("STMicroelectronics SoC DRM driver"); 44262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 443