sti_vtg.c (11839B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) STMicroelectronics SA 2014 4 * 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 0 23 24/* registers offset */ 25#define VTG_MODE 0x0000 26#define VTG_CLKLN 0x0008 27#define VTG_HLFLN 0x000C 28#define VTG_DRST_AUTOC 0x0010 29#define VTG_VID_TFO 0x0040 30#define VTG_VID_TFS 0x0044 31#define VTG_VID_BFO 0x0048 32#define VTG_VID_BFS 0x004C 33 34#define VTG_HOST_ITS 0x0078 35#define VTG_HOST_ITS_BCLR 0x007C 36#define VTG_HOST_ITM_BCLR 0x0088 37#define VTG_HOST_ITM_BSET 0x008C 38 39#define VTG_H_HD_1 0x00C0 40#define VTG_TOP_V_VD_1 0x00C4 41#define VTG_BOT_V_VD_1 0x00C8 42#define VTG_TOP_V_HD_1 0x00CC 43#define VTG_BOT_V_HD_1 0x00D0 44 45#define VTG_H_HD_2 0x00E0 46#define VTG_TOP_V_VD_2 0x00E4 47#define VTG_BOT_V_VD_2 0x00E8 48#define VTG_TOP_V_HD_2 0x00EC 49#define VTG_BOT_V_HD_2 0x00F0 50 51#define VTG_H_HD_3 0x0100 52#define VTG_TOP_V_VD_3 0x0104 53#define VTG_BOT_V_VD_3 0x0108 54#define VTG_TOP_V_HD_3 0x010C 55#define VTG_BOT_V_HD_3 0x0110 56 57#define VTG_H_HD_4 0x0120 58#define VTG_TOP_V_VD_4 0x0124 59#define VTG_BOT_V_VD_4 0x0128 60#define VTG_TOP_V_HD_4 0x012c 61#define VTG_BOT_V_HD_4 0x0130 62 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 structure 80 * 81 *@h_hd: stores the VTG_H_HD_x register offset 82 *@top_v_vd: stores the VTG_TOP_V_VD_x register offset 83 *@bot_v_vd: stores the VTG_BOT_V_VD_x register offset 84 *@top_v_hd: stores the VTG_TOP_V_HD_x register offset 85 *@bot_v_hd: stores the VTG_BOT_V_HD_x register offset 86 */ 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 4 96static 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 structure 109 * 110 *@hsync: sample number falling and rising edge 111 *@vsync_line_top: vertical top field line number falling and rising edge 112 *@vsync_line_bot: vertical bottom field line number falling and rising edge 113 *@vsync_off_top: vertical top field sample number rising and falling edge 114 *@vsync_off_bot: vertical bottom field sample number rising and falling edge 115 */ 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 structure 126 * 127 * @regs: register mapping 128 * @sync_params: synchronisation parameters used to generate timings 129 * @irq: VTG irq 130 * @irq_status: store the IRQ status value 131 * @notifier_list: notifier callback 132 * @crtc: the CRTC for vblank event 133 */ 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_number 305 * 306 * @mode: display mode to be used 307 * @y: line 308 * 309 * Return the line number according to the display mode taking 310 * 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 odd 313 * 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_number 327 * 328 * @mode: display mode to be used 329 * @x: row 330 * 331 * Return the pixel number according to the display mode taking 332 * 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 .owner = THIS_MODULE, 435 .of_match_table = vtg_of_match, 436 }, 437 .probe = vtg_probe, 438}; 439 440MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>"); 441MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver"); 442MODULE_LICENSE("GPL");