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