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