1 /* 2 * Copyright (C) 2015 Free Electrons 3 * Copyright (C) 2015 NextThing Co 4 * 5 * Maxime Ripard <maxime.ripard@free-electrons.com> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation; either version 2 of 10 * the License, or (at your option) any later version. 11 */ 12 13 #include <drm/drmP.h> 14 #include <drm/drm_atomic_helper.h> 15 #include <drm/drm_crtc.h> 16 #include <drm/drm_crtc_helper.h> 17 #include <drm/drm_modes.h> 18 #include <drm/drm_panel.h> 19 20 #include <linux/component.h> 21 #include <linux/ioport.h> 22 #include <linux/of_address.h> 23 #include <linux/of_graph.h> 24 #include <linux/of_irq.h> 25 #include <linux/regmap.h> 26 #include <linux/reset.h> 27 28 #include "sun4i_crtc.h" 29 #include "sun4i_dotclock.h" 30 #include "sun4i_drv.h" 31 #include "sun4i_rgb.h" 32 #include "sun4i_tcon.h" 33 34 void sun4i_tcon_disable(struct sun4i_tcon *tcon) 35 { 36 DRM_DEBUG_DRIVER("Disabling TCON\n"); 37 38 /* Disable the TCON */ 39 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, 40 SUN4I_TCON_GCTL_TCON_ENABLE, 0); 41 } 42 EXPORT_SYMBOL(sun4i_tcon_disable); 43 44 void sun4i_tcon_enable(struct sun4i_tcon *tcon) 45 { 46 DRM_DEBUG_DRIVER("Enabling TCON\n"); 47 48 /* Enable the TCON */ 49 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, 50 SUN4I_TCON_GCTL_TCON_ENABLE, 51 SUN4I_TCON_GCTL_TCON_ENABLE); 52 } 53 EXPORT_SYMBOL(sun4i_tcon_enable); 54 55 void sun4i_tcon_channel_disable(struct sun4i_tcon *tcon, int channel) 56 { 57 /* Disable the TCON's channel */ 58 if (channel == 0) { 59 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, 60 SUN4I_TCON0_CTL_TCON_ENABLE, 0); 61 clk_disable_unprepare(tcon->dclk); 62 } else if (channel == 1) { 63 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, 64 SUN4I_TCON1_CTL_TCON_ENABLE, 0); 65 clk_disable_unprepare(tcon->sclk1); 66 } 67 } 68 EXPORT_SYMBOL(sun4i_tcon_channel_disable); 69 70 void sun4i_tcon_channel_enable(struct sun4i_tcon *tcon, int channel) 71 { 72 /* Enable the TCON's channel */ 73 if (channel == 0) { 74 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, 75 SUN4I_TCON0_CTL_TCON_ENABLE, 76 SUN4I_TCON0_CTL_TCON_ENABLE); 77 clk_prepare_enable(tcon->dclk); 78 } else if (channel == 1) { 79 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, 80 SUN4I_TCON1_CTL_TCON_ENABLE, 81 SUN4I_TCON1_CTL_TCON_ENABLE); 82 clk_prepare_enable(tcon->sclk1); 83 } 84 } 85 EXPORT_SYMBOL(sun4i_tcon_channel_enable); 86 87 void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable) 88 { 89 u32 mask, val = 0; 90 91 DRM_DEBUG_DRIVER("%sabling VBLANK interrupt\n", enable ? "En" : "Dis"); 92 93 mask = SUN4I_TCON_GINT0_VBLANK_ENABLE(0) | 94 SUN4I_TCON_GINT0_VBLANK_ENABLE(1); 95 96 if (enable) 97 val = mask; 98 99 regmap_update_bits(tcon->regs, SUN4I_TCON_GINT0_REG, mask, val); 100 } 101 EXPORT_SYMBOL(sun4i_tcon_enable_vblank); 102 103 static int sun4i_tcon_get_clk_delay(struct drm_display_mode *mode, 104 int channel) 105 { 106 int delay = mode->vtotal - mode->vdisplay; 107 108 if (mode->flags & DRM_MODE_FLAG_INTERLACE) 109 delay /= 2; 110 111 if (channel == 1) 112 delay -= 2; 113 114 delay = min(delay, 30); 115 116 DRM_DEBUG_DRIVER("TCON %d clock delay %u\n", channel, delay); 117 118 return delay; 119 } 120 121 void sun4i_tcon0_mode_set(struct sun4i_tcon *tcon, 122 struct drm_display_mode *mode) 123 { 124 unsigned int bp, hsync, vsync; 125 u8 clk_delay; 126 u32 val = 0; 127 128 /* Adjust clock delay */ 129 clk_delay = sun4i_tcon_get_clk_delay(mode, 0); 130 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, 131 SUN4I_TCON0_CTL_CLK_DELAY_MASK, 132 SUN4I_TCON0_CTL_CLK_DELAY(clk_delay)); 133 134 /* Set the resolution */ 135 regmap_write(tcon->regs, SUN4I_TCON0_BASIC0_REG, 136 SUN4I_TCON0_BASIC0_X(mode->crtc_hdisplay) | 137 SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay)); 138 139 /* 140 * This is called a backporch in the register documentation, 141 * but it really is the front porch + hsync 142 */ 143 bp = mode->crtc_htotal - mode->crtc_hsync_start; 144 DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n", 145 mode->crtc_htotal, bp); 146 147 /* Set horizontal display timings */ 148 regmap_write(tcon->regs, SUN4I_TCON0_BASIC1_REG, 149 SUN4I_TCON0_BASIC1_H_TOTAL(mode->crtc_htotal) | 150 SUN4I_TCON0_BASIC1_H_BACKPORCH(bp)); 151 152 /* 153 * This is called a backporch in the register documentation, 154 * but it really is the front porch + hsync 155 */ 156 bp = mode->crtc_vtotal - mode->crtc_vsync_start; 157 DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n", 158 mode->crtc_vtotal, bp); 159 160 /* Set vertical display timings */ 161 regmap_write(tcon->regs, SUN4I_TCON0_BASIC2_REG, 162 SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal) | 163 SUN4I_TCON0_BASIC2_V_BACKPORCH(bp)); 164 165 /* Set Hsync and Vsync length */ 166 hsync = mode->crtc_hsync_end - mode->crtc_hsync_start; 167 vsync = mode->crtc_vsync_end - mode->crtc_vsync_start; 168 DRM_DEBUG_DRIVER("Setting HSYNC %d, VSYNC %d\n", hsync, vsync); 169 regmap_write(tcon->regs, SUN4I_TCON0_BASIC3_REG, 170 SUN4I_TCON0_BASIC3_V_SYNC(vsync) | 171 SUN4I_TCON0_BASIC3_H_SYNC(hsync)); 172 173 /* Setup the polarity of the various signals */ 174 if (!(mode->flags & DRM_MODE_FLAG_PHSYNC)) 175 val |= SUN4I_TCON0_IO_POL_HSYNC_POSITIVE; 176 177 if (!(mode->flags & DRM_MODE_FLAG_PVSYNC)) 178 val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE; 179 180 regmap_update_bits(tcon->regs, SUN4I_TCON0_IO_POL_REG, 181 SUN4I_TCON0_IO_POL_HSYNC_POSITIVE | SUN4I_TCON0_IO_POL_VSYNC_POSITIVE, 182 val); 183 184 /* Map output pins to channel 0 */ 185 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, 186 SUN4I_TCON_GCTL_IOMAP_MASK, 187 SUN4I_TCON_GCTL_IOMAP_TCON0); 188 189 /* Enable the output on the pins */ 190 regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, 0); 191 } 192 EXPORT_SYMBOL(sun4i_tcon0_mode_set); 193 194 void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon, 195 struct drm_display_mode *mode) 196 { 197 unsigned int bp, hsync, vsync; 198 u8 clk_delay; 199 u32 val; 200 201 /* Adjust clock delay */ 202 clk_delay = sun4i_tcon_get_clk_delay(mode, 1); 203 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, 204 SUN4I_TCON1_CTL_CLK_DELAY_MASK, 205 SUN4I_TCON1_CTL_CLK_DELAY(clk_delay)); 206 207 /* Set interlaced mode */ 208 if (mode->flags & DRM_MODE_FLAG_INTERLACE) 209 val = SUN4I_TCON1_CTL_INTERLACE_ENABLE; 210 else 211 val = 0; 212 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, 213 SUN4I_TCON1_CTL_INTERLACE_ENABLE, 214 val); 215 216 /* Set the input resolution */ 217 regmap_write(tcon->regs, SUN4I_TCON1_BASIC0_REG, 218 SUN4I_TCON1_BASIC0_X(mode->crtc_hdisplay) | 219 SUN4I_TCON1_BASIC0_Y(mode->crtc_vdisplay)); 220 221 /* Set the upscaling resolution */ 222 regmap_write(tcon->regs, SUN4I_TCON1_BASIC1_REG, 223 SUN4I_TCON1_BASIC1_X(mode->crtc_hdisplay) | 224 SUN4I_TCON1_BASIC1_Y(mode->crtc_vdisplay)); 225 226 /* Set the output resolution */ 227 regmap_write(tcon->regs, SUN4I_TCON1_BASIC2_REG, 228 SUN4I_TCON1_BASIC2_X(mode->crtc_hdisplay) | 229 SUN4I_TCON1_BASIC2_Y(mode->crtc_vdisplay)); 230 231 /* Set horizontal display timings */ 232 bp = mode->crtc_htotal - mode->crtc_hsync_end; 233 DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n", 234 mode->htotal, bp); 235 regmap_write(tcon->regs, SUN4I_TCON1_BASIC3_REG, 236 SUN4I_TCON1_BASIC3_H_TOTAL(mode->crtc_htotal) | 237 SUN4I_TCON1_BASIC3_H_BACKPORCH(bp)); 238 239 /* Set vertical display timings */ 240 bp = mode->crtc_vtotal - mode->crtc_vsync_end; 241 DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n", 242 mode->vtotal, bp); 243 regmap_write(tcon->regs, SUN4I_TCON1_BASIC4_REG, 244 SUN4I_TCON1_BASIC4_V_TOTAL(mode->vtotal) | 245 SUN4I_TCON1_BASIC4_V_BACKPORCH(bp)); 246 247 /* Set Hsync and Vsync length */ 248 hsync = mode->crtc_hsync_end - mode->crtc_hsync_start; 249 vsync = mode->crtc_vsync_end - mode->crtc_vsync_start; 250 DRM_DEBUG_DRIVER("Setting HSYNC %d, VSYNC %d\n", hsync, vsync); 251 regmap_write(tcon->regs, SUN4I_TCON1_BASIC5_REG, 252 SUN4I_TCON1_BASIC5_V_SYNC(vsync) | 253 SUN4I_TCON1_BASIC5_H_SYNC(hsync)); 254 255 /* Map output pins to channel 1 */ 256 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, 257 SUN4I_TCON_GCTL_IOMAP_MASK, 258 SUN4I_TCON_GCTL_IOMAP_TCON1); 259 260 /* 261 * FIXME: Undocumented bits 262 */ 263 if (tcon->has_mux) 264 regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, 1); 265 } 266 EXPORT_SYMBOL(sun4i_tcon1_mode_set); 267 268 static void sun4i_tcon_finish_page_flip(struct drm_device *dev, 269 struct sun4i_crtc *scrtc) 270 { 271 unsigned long flags; 272 273 spin_lock_irqsave(&dev->event_lock, flags); 274 if (scrtc->event) { 275 drm_crtc_send_vblank_event(&scrtc->crtc, scrtc->event); 276 drm_crtc_vblank_put(&scrtc->crtc); 277 scrtc->event = NULL; 278 } 279 spin_unlock_irqrestore(&dev->event_lock, flags); 280 } 281 282 static irqreturn_t sun4i_tcon_handler(int irq, void *private) 283 { 284 struct sun4i_tcon *tcon = private; 285 struct drm_device *drm = tcon->drm; 286 struct sun4i_drv *drv = drm->dev_private; 287 struct sun4i_crtc *scrtc = drv->crtc; 288 unsigned int status; 289 290 regmap_read(tcon->regs, SUN4I_TCON_GINT0_REG, &status); 291 292 if (!(status & (SUN4I_TCON_GINT0_VBLANK_INT(0) | 293 SUN4I_TCON_GINT0_VBLANK_INT(1)))) 294 return IRQ_NONE; 295 296 drm_crtc_handle_vblank(&scrtc->crtc); 297 sun4i_tcon_finish_page_flip(drm, scrtc); 298 299 /* Acknowledge the interrupt */ 300 regmap_update_bits(tcon->regs, SUN4I_TCON_GINT0_REG, 301 SUN4I_TCON_GINT0_VBLANK_INT(0) | 302 SUN4I_TCON_GINT0_VBLANK_INT(1), 303 0); 304 305 return IRQ_HANDLED; 306 } 307 308 static int sun4i_tcon_init_clocks(struct device *dev, 309 struct sun4i_tcon *tcon) 310 { 311 tcon->clk = devm_clk_get(dev, "ahb"); 312 if (IS_ERR(tcon->clk)) { 313 dev_err(dev, "Couldn't get the TCON bus clock\n"); 314 return PTR_ERR(tcon->clk); 315 } 316 clk_prepare_enable(tcon->clk); 317 318 tcon->sclk0 = devm_clk_get(dev, "tcon-ch0"); 319 if (IS_ERR(tcon->sclk0)) { 320 dev_err(dev, "Couldn't get the TCON channel 0 clock\n"); 321 return PTR_ERR(tcon->sclk0); 322 } 323 324 tcon->sclk1 = devm_clk_get(dev, "tcon-ch1"); 325 if (IS_ERR(tcon->sclk1)) { 326 dev_err(dev, "Couldn't get the TCON channel 1 clock\n"); 327 return PTR_ERR(tcon->sclk1); 328 } 329 330 return sun4i_dclk_create(dev, tcon); 331 } 332 333 static void sun4i_tcon_free_clocks(struct sun4i_tcon *tcon) 334 { 335 sun4i_dclk_free(tcon); 336 clk_disable_unprepare(tcon->clk); 337 } 338 339 static int sun4i_tcon_init_irq(struct device *dev, 340 struct sun4i_tcon *tcon) 341 { 342 struct platform_device *pdev = to_platform_device(dev); 343 int irq, ret; 344 345 irq = platform_get_irq(pdev, 0); 346 if (irq < 0) { 347 dev_err(dev, "Couldn't retrieve the TCON interrupt\n"); 348 return irq; 349 } 350 351 ret = devm_request_irq(dev, irq, sun4i_tcon_handler, 0, 352 dev_name(dev), tcon); 353 if (ret) { 354 dev_err(dev, "Couldn't request the IRQ\n"); 355 return ret; 356 } 357 358 return 0; 359 } 360 361 static struct regmap_config sun4i_tcon_regmap_config = { 362 .reg_bits = 32, 363 .val_bits = 32, 364 .reg_stride = 4, 365 .max_register = 0x800, 366 }; 367 368 static int sun4i_tcon_init_regmap(struct device *dev, 369 struct sun4i_tcon *tcon) 370 { 371 struct platform_device *pdev = to_platform_device(dev); 372 struct resource *res; 373 void __iomem *regs; 374 375 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 376 regs = devm_ioremap_resource(dev, res); 377 if (IS_ERR(regs)) { 378 dev_err(dev, "Couldn't map the TCON registers\n"); 379 return PTR_ERR(regs); 380 } 381 382 tcon->regs = devm_regmap_init_mmio(dev, regs, 383 &sun4i_tcon_regmap_config); 384 if (IS_ERR(tcon->regs)) { 385 dev_err(dev, "Couldn't create the TCON regmap\n"); 386 return PTR_ERR(tcon->regs); 387 } 388 389 /* Make sure the TCON is disabled and all IRQs are off */ 390 regmap_write(tcon->regs, SUN4I_TCON_GCTL_REG, 0); 391 regmap_write(tcon->regs, SUN4I_TCON_GINT0_REG, 0); 392 regmap_write(tcon->regs, SUN4I_TCON_GINT1_REG, 0); 393 394 /* Disable IO lines and set them to tristate */ 395 regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, ~0); 396 regmap_write(tcon->regs, SUN4I_TCON1_IO_TRI_REG, ~0); 397 398 return 0; 399 } 400 401 static struct drm_panel *sun4i_tcon_find_panel(struct device_node *node) 402 { 403 struct device_node *port, *remote, *child; 404 struct device_node *end_node = NULL; 405 406 /* Inputs are listed first, then outputs */ 407 port = of_graph_get_port_by_id(node, 1); 408 409 /* 410 * Our first output is the RGB interface where the panel will 411 * be connected. 412 */ 413 for_each_child_of_node(port, child) { 414 u32 reg; 415 416 of_property_read_u32(child, "reg", ®); 417 if (reg == 0) 418 end_node = child; 419 } 420 421 if (!end_node) { 422 DRM_DEBUG_DRIVER("Missing panel endpoint\n"); 423 return ERR_PTR(-ENODEV); 424 } 425 426 remote = of_graph_get_remote_port_parent(end_node); 427 if (!remote) { 428 DRM_DEBUG_DRIVER("Unable to parse remote node\n"); 429 return ERR_PTR(-EINVAL); 430 } 431 432 return of_drm_find_panel(remote) ?: ERR_PTR(-EPROBE_DEFER); 433 } 434 435 static int sun4i_tcon_bind(struct device *dev, struct device *master, 436 void *data) 437 { 438 struct drm_device *drm = data; 439 struct sun4i_drv *drv = drm->dev_private; 440 struct sun4i_tcon *tcon; 441 int ret; 442 443 tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL); 444 if (!tcon) 445 return -ENOMEM; 446 dev_set_drvdata(dev, tcon); 447 drv->tcon = tcon; 448 tcon->drm = drm; 449 450 if (of_device_is_compatible(dev->of_node, "allwinner,sun5i-a13-tcon")) 451 tcon->has_mux = true; 452 453 tcon->lcd_rst = devm_reset_control_get(dev, "lcd"); 454 if (IS_ERR(tcon->lcd_rst)) { 455 dev_err(dev, "Couldn't get our reset line\n"); 456 return PTR_ERR(tcon->lcd_rst); 457 } 458 459 /* Make sure our TCON is reset */ 460 if (!reset_control_status(tcon->lcd_rst)) 461 reset_control_assert(tcon->lcd_rst); 462 463 ret = reset_control_deassert(tcon->lcd_rst); 464 if (ret) { 465 dev_err(dev, "Couldn't deassert our reset line\n"); 466 return ret; 467 } 468 469 ret = sun4i_tcon_init_regmap(dev, tcon); 470 if (ret) { 471 dev_err(dev, "Couldn't init our TCON regmap\n"); 472 goto err_assert_reset; 473 } 474 475 ret = sun4i_tcon_init_clocks(dev, tcon); 476 if (ret) { 477 dev_err(dev, "Couldn't init our TCON clocks\n"); 478 goto err_assert_reset; 479 } 480 481 ret = sun4i_tcon_init_irq(dev, tcon); 482 if (ret) { 483 dev_err(dev, "Couldn't init our TCON interrupts\n"); 484 goto err_free_clocks; 485 } 486 487 tcon->panel = sun4i_tcon_find_panel(dev->of_node); 488 if (IS_ERR(tcon->panel)) { 489 dev_info(dev, "No panel found... RGB output disabled\n"); 490 return 0; 491 } 492 493 ret = sun4i_rgb_init(drm); 494 if (ret < 0) 495 goto err_free_clocks; 496 497 return 0; 498 499 err_free_clocks: 500 sun4i_tcon_free_clocks(tcon); 501 err_assert_reset: 502 reset_control_assert(tcon->lcd_rst); 503 return ret; 504 } 505 506 static void sun4i_tcon_unbind(struct device *dev, struct device *master, 507 void *data) 508 { 509 struct sun4i_tcon *tcon = dev_get_drvdata(dev); 510 511 sun4i_tcon_free_clocks(tcon); 512 } 513 514 static struct component_ops sun4i_tcon_ops = { 515 .bind = sun4i_tcon_bind, 516 .unbind = sun4i_tcon_unbind, 517 }; 518 519 static int sun4i_tcon_probe(struct platform_device *pdev) 520 { 521 struct device_node *node = pdev->dev.of_node; 522 struct drm_panel *panel; 523 524 /* 525 * The panel is not ready. 526 * Defer the probe. 527 */ 528 panel = sun4i_tcon_find_panel(node); 529 530 /* 531 * If we don't have a panel endpoint, just go on 532 */ 533 if (PTR_ERR(panel) == -EPROBE_DEFER) { 534 DRM_DEBUG_DRIVER("Still waiting for our panel. Deferring...\n"); 535 return -EPROBE_DEFER; 536 } 537 538 return component_add(&pdev->dev, &sun4i_tcon_ops); 539 } 540 541 static int sun4i_tcon_remove(struct platform_device *pdev) 542 { 543 component_del(&pdev->dev, &sun4i_tcon_ops); 544 545 return 0; 546 } 547 548 static const struct of_device_id sun4i_tcon_of_table[] = { 549 { .compatible = "allwinner,sun5i-a13-tcon" }, 550 { } 551 }; 552 MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table); 553 554 static struct platform_driver sun4i_tcon_platform_driver = { 555 .probe = sun4i_tcon_probe, 556 .remove = sun4i_tcon_remove, 557 .driver = { 558 .name = "sun4i-tcon", 559 .of_match_table = sun4i_tcon_of_table, 560 }, 561 }; 562 module_platform_driver(sun4i_tcon_platform_driver); 563 564 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); 565 MODULE_DESCRIPTION("Allwinner A10 Timing Controller Driver"); 566 MODULE_LICENSE("GPL"); 567