442 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) STMicroelectronics SA 20144 * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>5 * Fabien Dessenne <fabien.dessenne@st.com>6 * Vincent Abriou <vincent.abriou@st.com>7 * for STMicroelectronics.8 */9 10#include <linux/module.h>11#include <linux/io.h>12#include <linux/notifier.h>13#include <linux/of_platform.h>14#include <linux/platform_device.h>15 16#include <drm/drm_modes.h>17#include <drm/drm_print.h>18 19#include "sti_drv.h"20#include "sti_vtg.h"21 22#define VTG_MODE_MASTER 023 24/* registers offset */25#define VTG_MODE 0x000026#define VTG_CLKLN 0x000827#define VTG_HLFLN 0x000C28#define VTG_DRST_AUTOC 0x001029#define VTG_VID_TFO 0x004030#define VTG_VID_TFS 0x004431#define VTG_VID_BFO 0x004832#define VTG_VID_BFS 0x004C33 34#define VTG_HOST_ITS 0x007835#define VTG_HOST_ITS_BCLR 0x007C36#define VTG_HOST_ITM_BCLR 0x008837#define VTG_HOST_ITM_BSET 0x008C38 39#define VTG_H_HD_1 0x00C040#define VTG_TOP_V_VD_1 0x00C441#define VTG_BOT_V_VD_1 0x00C842#define VTG_TOP_V_HD_1 0x00CC43#define VTG_BOT_V_HD_1 0x00D044 45#define VTG_H_HD_2 0x00E046#define VTG_TOP_V_VD_2 0x00E447#define VTG_BOT_V_VD_2 0x00E848#define VTG_TOP_V_HD_2 0x00EC49#define VTG_BOT_V_HD_2 0x00F050 51#define VTG_H_HD_3 0x010052#define VTG_TOP_V_VD_3 0x010453#define VTG_BOT_V_VD_3 0x010854#define VTG_TOP_V_HD_3 0x010C55#define VTG_BOT_V_HD_3 0x011056 57#define VTG_H_HD_4 0x012058#define VTG_TOP_V_VD_4 0x012459#define VTG_BOT_V_VD_4 0x012860#define VTG_TOP_V_HD_4 0x012c61#define VTG_BOT_V_HD_4 0x013062 63#define VTG_IRQ_BOTTOM BIT(0)64#define VTG_IRQ_TOP BIT(1)65#define VTG_IRQ_MASK (VTG_IRQ_TOP | VTG_IRQ_BOTTOM)66 67/* Delay introduced by the HDMI in nb of pixel */68#define HDMI_DELAY (5)69 70/* Delay introduced by the DVO in nb of pixel */71#define DVO_DELAY (7)72 73/* delay introduced by the Arbitrary Waveform Generator in nb of pixels */74#define AWG_DELAY_HD (-9)75#define AWG_DELAY_ED (-8)76#define AWG_DELAY_SD (-7)77 78/*79 * STI VTG register offset structure80 *81 *@h_hd: stores the VTG_H_HD_x register offset82 *@top_v_vd: stores the VTG_TOP_V_VD_x register offset83 *@bot_v_vd: stores the VTG_BOT_V_VD_x register offset84 *@top_v_hd: stores the VTG_TOP_V_HD_x register offset85 *@bot_v_hd: stores the VTG_BOT_V_HD_x register offset86 */87struct sti_vtg_regs_offs {88 u32 h_hd;89 u32 top_v_vd;90 u32 bot_v_vd;91 u32 top_v_hd;92 u32 bot_v_hd;93};94 95#define VTG_MAX_SYNC_OUTPUT 496static const struct sti_vtg_regs_offs vtg_regs_offs[VTG_MAX_SYNC_OUTPUT] = {97 { VTG_H_HD_1,98 VTG_TOP_V_VD_1, VTG_BOT_V_VD_1, VTG_TOP_V_HD_1, VTG_BOT_V_HD_1 },99 { VTG_H_HD_2,100 VTG_TOP_V_VD_2, VTG_BOT_V_VD_2, VTG_TOP_V_HD_2, VTG_BOT_V_HD_2 },101 { VTG_H_HD_3,102 VTG_TOP_V_VD_3, VTG_BOT_V_VD_3, VTG_TOP_V_HD_3, VTG_BOT_V_HD_3 },103 { VTG_H_HD_4,104 VTG_TOP_V_VD_4, VTG_BOT_V_VD_4, VTG_TOP_V_HD_4, VTG_BOT_V_HD_4 }105};106 107/*108 * STI VTG synchronisation parameters structure109 *110 *@hsync: sample number falling and rising edge111 *@vsync_line_top: vertical top field line number falling and rising edge112 *@vsync_line_bot: vertical bottom field line number falling and rising edge113 *@vsync_off_top: vertical top field sample number rising and falling edge114 *@vsync_off_bot: vertical bottom field sample number rising and falling edge115 */116struct sti_vtg_sync_params {117 u32 hsync;118 u32 vsync_line_top;119 u32 vsync_line_bot;120 u32 vsync_off_top;121 u32 vsync_off_bot;122};123 124/*125 * STI VTG structure126 *127 * @regs: register mapping128 * @sync_params: synchronisation parameters used to generate timings129 * @irq: VTG irq130 * @irq_status: store the IRQ status value131 * @notifier_list: notifier callback132 * @crtc: the CRTC for vblank event133 */134struct sti_vtg {135 void __iomem *regs;136 struct sti_vtg_sync_params sync_params[VTG_MAX_SYNC_OUTPUT];137 int irq;138 u32 irq_status;139 struct raw_notifier_head notifier_list;140 struct drm_crtc *crtc;141};142 143struct sti_vtg *of_vtg_find(struct device_node *np)144{145 struct platform_device *pdev;146 147 pdev = of_find_device_by_node(np);148 if (!pdev)149 return NULL;150 151 return (struct sti_vtg *)platform_get_drvdata(pdev);152}153 154static void vtg_reset(struct sti_vtg *vtg)155{156 writel(1, vtg->regs + VTG_DRST_AUTOC);157}158 159static void vtg_set_output_window(void __iomem *regs,160 const struct drm_display_mode *mode)161{162 u32 video_top_field_start;163 u32 video_top_field_stop;164 u32 video_bottom_field_start;165 u32 video_bottom_field_stop;166 u32 xstart = sti_vtg_get_pixel_number(*mode, 0);167 u32 ystart = sti_vtg_get_line_number(*mode, 0);168 u32 xstop = sti_vtg_get_pixel_number(*mode, mode->hdisplay - 1);169 u32 ystop = sti_vtg_get_line_number(*mode, mode->vdisplay - 1);170 171 /* Set output window to fit the display mode selected */172 video_top_field_start = (ystart << 16) | xstart;173 video_top_field_stop = (ystop << 16) | xstop;174 175 /* Only progressive supported for now */176 video_bottom_field_start = video_top_field_start;177 video_bottom_field_stop = video_top_field_stop;178 179 writel(video_top_field_start, regs + VTG_VID_TFO);180 writel(video_top_field_stop, regs + VTG_VID_TFS);181 writel(video_bottom_field_start, regs + VTG_VID_BFO);182 writel(video_bottom_field_stop, regs + VTG_VID_BFS);183}184 185static void vtg_set_hsync_vsync_pos(struct sti_vtg_sync_params *sync,186 int delay,187 const struct drm_display_mode *mode)188{189 long clocksperline, start, stop;190 u32 risesync_top, fallsync_top;191 u32 risesync_offs_top, fallsync_offs_top;192 193 clocksperline = mode->htotal;194 195 /* Get the hsync position */196 start = 0;197 stop = mode->hsync_end - mode->hsync_start;198 199 start += delay;200 stop += delay;201 202 if (start < 0)203 start += clocksperline;204 else if (start >= clocksperline)205 start -= clocksperline;206 207 if (stop < 0)208 stop += clocksperline;209 else if (stop >= clocksperline)210 stop -= clocksperline;211 212 sync->hsync = (stop << 16) | start;213 214 /* Get the vsync position */215 if (delay >= 0) {216 risesync_top = 1;217 fallsync_top = risesync_top;218 fallsync_top += mode->vsync_end - mode->vsync_start;219 220 fallsync_offs_top = (u32)delay;221 risesync_offs_top = (u32)delay;222 } else {223 risesync_top = mode->vtotal;224 fallsync_top = mode->vsync_end - mode->vsync_start;225 226 fallsync_offs_top = clocksperline + delay;227 risesync_offs_top = clocksperline + delay;228 }229 230 sync->vsync_line_top = (fallsync_top << 16) | risesync_top;231 sync->vsync_off_top = (fallsync_offs_top << 16) | risesync_offs_top;232 233 /* Only progressive supported for now */234 sync->vsync_line_bot = sync->vsync_line_top;235 sync->vsync_off_bot = sync->vsync_off_top;236}237 238static void vtg_set_mode(struct sti_vtg *vtg,239 int type,240 struct sti_vtg_sync_params *sync,241 const struct drm_display_mode *mode)242{243 unsigned int i;244 245 /* Set the number of clock cycles per line */246 writel(mode->htotal, vtg->regs + VTG_CLKLN);247 248 /* Set Half Line Per Field (only progressive supported for now) */249 writel(mode->vtotal * 2, vtg->regs + VTG_HLFLN);250 251 /* Program output window */252 vtg_set_output_window(vtg->regs, mode);253 254 /* Set hsync and vsync position for HDMI */255 vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_HDMI - 1], HDMI_DELAY, mode);256 257 /* Set hsync and vsync position for HD DCS */258 vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_HDDCS - 1], 0, mode);259 260 /* Set hsync and vsync position for HDF */261 vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_HDF - 1], AWG_DELAY_HD, mode);262 263 /* Set hsync and vsync position for DVO */264 vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_DVO - 1], DVO_DELAY, mode);265 266 /* Progam the syncs outputs */267 for (i = 0; i < VTG_MAX_SYNC_OUTPUT ; i++) {268 writel(sync[i].hsync,269 vtg->regs + vtg_regs_offs[i].h_hd);270 writel(sync[i].vsync_line_top,271 vtg->regs + vtg_regs_offs[i].top_v_vd);272 writel(sync[i].vsync_line_bot,273 vtg->regs + vtg_regs_offs[i].bot_v_vd);274 writel(sync[i].vsync_off_top,275 vtg->regs + vtg_regs_offs[i].top_v_hd);276 writel(sync[i].vsync_off_bot,277 vtg->regs + vtg_regs_offs[i].bot_v_hd);278 }279 280 /* mode */281 writel(type, vtg->regs + VTG_MODE);282}283 284static void vtg_enable_irq(struct sti_vtg *vtg)285{286 /* clear interrupt status and mask */287 writel(0xFFFF, vtg->regs + VTG_HOST_ITS_BCLR);288 writel(0xFFFF, vtg->regs + VTG_HOST_ITM_BCLR);289 writel(VTG_IRQ_MASK, vtg->regs + VTG_HOST_ITM_BSET);290}291 292void sti_vtg_set_config(struct sti_vtg *vtg,293 const struct drm_display_mode *mode)294{295 /* write configuration */296 vtg_set_mode(vtg, VTG_MODE_MASTER, vtg->sync_params, mode);297 298 vtg_reset(vtg);299 300 vtg_enable_irq(vtg);301}302 303/**304 * sti_vtg_get_line_number305 *306 * @mode: display mode to be used307 * @y: line308 *309 * Return the line number according to the display mode taking310 * into account the Sync and Back Porch information.311 * Video frame line numbers start at 1, y starts at 0.312 * In interlaced modes the start line is the field line number of the odd313 * field, but y is still defined as a progressive frame.314 */315u32 sti_vtg_get_line_number(struct drm_display_mode mode, int y)316{317 u32 start_line = mode.vtotal - mode.vsync_start + 1;318 319 if (mode.flags & DRM_MODE_FLAG_INTERLACE)320 start_line *= 2;321 322 return start_line + y;323}324 325/**326 * sti_vtg_get_pixel_number327 *328 * @mode: display mode to be used329 * @x: row330 *331 * Return the pixel number according to the display mode taking332 * into account the Sync and Back Porch information.333 * Pixels are counted from 0.334 */335u32 sti_vtg_get_pixel_number(struct drm_display_mode mode, int x)336{337 return mode.htotal - mode.hsync_start + x;338}339 340int sti_vtg_register_client(struct sti_vtg *vtg, struct notifier_block *nb,341 struct drm_crtc *crtc)342{343 vtg->crtc = crtc;344 return raw_notifier_chain_register(&vtg->notifier_list, nb);345}346 347int sti_vtg_unregister_client(struct sti_vtg *vtg, struct notifier_block *nb)348{349 return raw_notifier_chain_unregister(&vtg->notifier_list, nb);350}351 352static irqreturn_t vtg_irq_thread(int irq, void *arg)353{354 struct sti_vtg *vtg = arg;355 u32 event;356 357 event = (vtg->irq_status & VTG_IRQ_TOP) ?358 VTG_TOP_FIELD_EVENT : VTG_BOTTOM_FIELD_EVENT;359 360 raw_notifier_call_chain(&vtg->notifier_list, event, vtg->crtc);361 362 return IRQ_HANDLED;363}364 365static irqreturn_t vtg_irq(int irq, void *arg)366{367 struct sti_vtg *vtg = arg;368 369 vtg->irq_status = readl(vtg->regs + VTG_HOST_ITS);370 371 writel(vtg->irq_status, vtg->regs + VTG_HOST_ITS_BCLR);372 373 /* force sync bus write */374 readl(vtg->regs + VTG_HOST_ITS);375 376 return IRQ_WAKE_THREAD;377}378 379static int vtg_probe(struct platform_device *pdev)380{381 struct device *dev = &pdev->dev;382 struct sti_vtg *vtg;383 struct resource *res;384 int ret;385 386 vtg = devm_kzalloc(dev, sizeof(*vtg), GFP_KERNEL);387 if (!vtg)388 return -ENOMEM;389 390 /* Get Memory ressources */391 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);392 if (!res) {393 DRM_ERROR("Get memory resource failed\n");394 return -ENOMEM;395 }396 vtg->regs = devm_ioremap(dev, res->start, resource_size(res));397 if (!vtg->regs) {398 DRM_ERROR("failed to remap I/O memory\n");399 return -ENOMEM;400 }401 402 vtg->irq = platform_get_irq(pdev, 0);403 if (vtg->irq < 0) {404 DRM_ERROR("Failed to get VTG interrupt\n");405 return vtg->irq;406 }407 408 RAW_INIT_NOTIFIER_HEAD(&vtg->notifier_list);409 410 ret = devm_request_threaded_irq(dev, vtg->irq, vtg_irq,411 vtg_irq_thread, IRQF_ONESHOT,412 dev_name(dev), vtg);413 if (ret < 0) {414 DRM_ERROR("Failed to register VTG interrupt\n");415 return ret;416 }417 418 platform_set_drvdata(pdev, vtg);419 420 DRM_INFO("%s %s\n", __func__, dev_name(dev));421 422 return 0;423}424 425static const struct of_device_id vtg_of_match[] = {426 { .compatible = "st,vtg", },427 { /* sentinel */ }428};429MODULE_DEVICE_TABLE(of, vtg_of_match);430 431struct platform_driver sti_vtg_driver = {432 .driver = {433 .name = "sti-vtg",434 .of_match_table = vtg_of_match,435 },436 .probe = vtg_probe,437};438 439MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");440MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");441MODULE_LICENSE("GPL");442