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 #include "sunxi_engine.h" 34 35 void sun4i_tcon_disable(struct sun4i_tcon *tcon) 36 { 37 DRM_DEBUG_DRIVER("Disabling TCON\n"); 38 39 /* Disable the TCON */ 40 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, 41 SUN4I_TCON_GCTL_TCON_ENABLE, 0); 42 } 43 EXPORT_SYMBOL(sun4i_tcon_disable); 44 45 void sun4i_tcon_enable(struct sun4i_tcon *tcon) 46 { 47 DRM_DEBUG_DRIVER("Enabling TCON\n"); 48 49 /* Enable the TCON */ 50 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, 51 SUN4I_TCON_GCTL_TCON_ENABLE, 52 SUN4I_TCON_GCTL_TCON_ENABLE); 53 } 54 EXPORT_SYMBOL(sun4i_tcon_enable); 55 56 void sun4i_tcon_channel_disable(struct sun4i_tcon *tcon, int channel) 57 { 58 DRM_DEBUG_DRIVER("Disabling TCON channel %d\n", channel); 59 60 /* Disable the TCON's channel */ 61 if (channel == 0) { 62 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, 63 SUN4I_TCON0_CTL_TCON_ENABLE, 0); 64 clk_disable_unprepare(tcon->dclk); 65 return; 66 } 67 68 WARN_ON(!tcon->quirks->has_channel_1); 69 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, 70 SUN4I_TCON1_CTL_TCON_ENABLE, 0); 71 clk_disable_unprepare(tcon->sclk1); 72 } 73 EXPORT_SYMBOL(sun4i_tcon_channel_disable); 74 75 void sun4i_tcon_channel_enable(struct sun4i_tcon *tcon, int channel) 76 { 77 DRM_DEBUG_DRIVER("Enabling TCON channel %d\n", channel); 78 79 /* Enable the TCON's channel */ 80 if (channel == 0) { 81 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, 82 SUN4I_TCON0_CTL_TCON_ENABLE, 83 SUN4I_TCON0_CTL_TCON_ENABLE); 84 clk_prepare_enable(tcon->dclk); 85 return; 86 } 87 88 WARN_ON(!tcon->quirks->has_channel_1); 89 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, 90 SUN4I_TCON1_CTL_TCON_ENABLE, 91 SUN4I_TCON1_CTL_TCON_ENABLE); 92 clk_prepare_enable(tcon->sclk1); 93 } 94 EXPORT_SYMBOL(sun4i_tcon_channel_enable); 95 96 void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable) 97 { 98 u32 mask, val = 0; 99 100 DRM_DEBUG_DRIVER("%sabling VBLANK interrupt\n", enable ? "En" : "Dis"); 101 102 mask = SUN4I_TCON_GINT0_VBLANK_ENABLE(0) | 103 SUN4I_TCON_GINT0_VBLANK_ENABLE(1); 104 105 if (enable) 106 val = mask; 107 108 regmap_update_bits(tcon->regs, SUN4I_TCON_GINT0_REG, mask, val); 109 } 110 EXPORT_SYMBOL(sun4i_tcon_enable_vblank); 111 112 void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel, 113 struct drm_encoder *encoder) 114 { 115 u32 val; 116 117 if (!tcon->quirks->has_unknown_mux) 118 return; 119 120 if (channel != 1) 121 return; 122 123 if (encoder->encoder_type == DRM_MODE_ENCODER_TVDAC) 124 val = 1; 125 else 126 val = 0; 127 128 /* 129 * FIXME: Undocumented bits 130 */ 131 regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, val); 132 } 133 EXPORT_SYMBOL(sun4i_tcon_set_mux); 134 135 static int sun4i_tcon_get_clk_delay(struct drm_display_mode *mode, 136 int channel) 137 { 138 int delay = mode->vtotal - mode->vdisplay; 139 140 if (mode->flags & DRM_MODE_FLAG_INTERLACE) 141 delay /= 2; 142 143 if (channel == 1) 144 delay -= 2; 145 146 delay = min(delay, 30); 147 148 DRM_DEBUG_DRIVER("TCON %d clock delay %u\n", channel, delay); 149 150 return delay; 151 } 152 153 void sun4i_tcon0_mode_set(struct sun4i_tcon *tcon, 154 struct drm_display_mode *mode) 155 { 156 unsigned int bp, hsync, vsync; 157 u8 clk_delay; 158 u32 val = 0; 159 160 /* Configure the dot clock */ 161 clk_set_rate(tcon->dclk, mode->crtc_clock * 1000); 162 163 /* Adjust clock delay */ 164 clk_delay = sun4i_tcon_get_clk_delay(mode, 0); 165 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, 166 SUN4I_TCON0_CTL_CLK_DELAY_MASK, 167 SUN4I_TCON0_CTL_CLK_DELAY(clk_delay)); 168 169 /* Set the resolution */ 170 regmap_write(tcon->regs, SUN4I_TCON0_BASIC0_REG, 171 SUN4I_TCON0_BASIC0_X(mode->crtc_hdisplay) | 172 SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay)); 173 174 /* 175 * This is called a backporch in the register documentation, 176 * but it really is the back porch + hsync 177 */ 178 bp = mode->crtc_htotal - mode->crtc_hsync_start; 179 DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n", 180 mode->crtc_htotal, bp); 181 182 /* Set horizontal display timings */ 183 regmap_write(tcon->regs, SUN4I_TCON0_BASIC1_REG, 184 SUN4I_TCON0_BASIC1_H_TOTAL(mode->crtc_htotal) | 185 SUN4I_TCON0_BASIC1_H_BACKPORCH(bp)); 186 187 /* 188 * This is called a backporch in the register documentation, 189 * but it really is the back porch + hsync 190 */ 191 bp = mode->crtc_vtotal - mode->crtc_vsync_start; 192 DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n", 193 mode->crtc_vtotal, bp); 194 195 /* Set vertical display timings */ 196 regmap_write(tcon->regs, SUN4I_TCON0_BASIC2_REG, 197 SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal * 2) | 198 SUN4I_TCON0_BASIC2_V_BACKPORCH(bp)); 199 200 /* Set Hsync and Vsync length */ 201 hsync = mode->crtc_hsync_end - mode->crtc_hsync_start; 202 vsync = mode->crtc_vsync_end - mode->crtc_vsync_start; 203 DRM_DEBUG_DRIVER("Setting HSYNC %d, VSYNC %d\n", hsync, vsync); 204 regmap_write(tcon->regs, SUN4I_TCON0_BASIC3_REG, 205 SUN4I_TCON0_BASIC3_V_SYNC(vsync) | 206 SUN4I_TCON0_BASIC3_H_SYNC(hsync)); 207 208 /* Setup the polarity of the various signals */ 209 if (!(mode->flags & DRM_MODE_FLAG_PHSYNC)) 210 val |= SUN4I_TCON0_IO_POL_HSYNC_POSITIVE; 211 212 if (!(mode->flags & DRM_MODE_FLAG_PVSYNC)) 213 val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE; 214 215 regmap_update_bits(tcon->regs, SUN4I_TCON0_IO_POL_REG, 216 SUN4I_TCON0_IO_POL_HSYNC_POSITIVE | SUN4I_TCON0_IO_POL_VSYNC_POSITIVE, 217 val); 218 219 /* Map output pins to channel 0 */ 220 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, 221 SUN4I_TCON_GCTL_IOMAP_MASK, 222 SUN4I_TCON_GCTL_IOMAP_TCON0); 223 224 /* Enable the output on the pins */ 225 regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, 0); 226 } 227 EXPORT_SYMBOL(sun4i_tcon0_mode_set); 228 229 void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon, 230 struct drm_display_mode *mode) 231 { 232 unsigned int bp, hsync, vsync, vtotal; 233 u8 clk_delay; 234 u32 val; 235 236 WARN_ON(!tcon->quirks->has_channel_1); 237 238 /* Configure the dot clock */ 239 clk_set_rate(tcon->sclk1, mode->crtc_clock * 1000); 240 241 /* Adjust clock delay */ 242 clk_delay = sun4i_tcon_get_clk_delay(mode, 1); 243 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, 244 SUN4I_TCON1_CTL_CLK_DELAY_MASK, 245 SUN4I_TCON1_CTL_CLK_DELAY(clk_delay)); 246 247 /* Set interlaced mode */ 248 if (mode->flags & DRM_MODE_FLAG_INTERLACE) 249 val = SUN4I_TCON1_CTL_INTERLACE_ENABLE; 250 else 251 val = 0; 252 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, 253 SUN4I_TCON1_CTL_INTERLACE_ENABLE, 254 val); 255 256 /* Set the input resolution */ 257 regmap_write(tcon->regs, SUN4I_TCON1_BASIC0_REG, 258 SUN4I_TCON1_BASIC0_X(mode->crtc_hdisplay) | 259 SUN4I_TCON1_BASIC0_Y(mode->crtc_vdisplay)); 260 261 /* Set the upscaling resolution */ 262 regmap_write(tcon->regs, SUN4I_TCON1_BASIC1_REG, 263 SUN4I_TCON1_BASIC1_X(mode->crtc_hdisplay) | 264 SUN4I_TCON1_BASIC1_Y(mode->crtc_vdisplay)); 265 266 /* Set the output resolution */ 267 regmap_write(tcon->regs, SUN4I_TCON1_BASIC2_REG, 268 SUN4I_TCON1_BASIC2_X(mode->crtc_hdisplay) | 269 SUN4I_TCON1_BASIC2_Y(mode->crtc_vdisplay)); 270 271 /* Set horizontal display timings */ 272 bp = mode->crtc_htotal - mode->crtc_hsync_start; 273 DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n", 274 mode->htotal, bp); 275 regmap_write(tcon->regs, SUN4I_TCON1_BASIC3_REG, 276 SUN4I_TCON1_BASIC3_H_TOTAL(mode->crtc_htotal) | 277 SUN4I_TCON1_BASIC3_H_BACKPORCH(bp)); 278 279 bp = mode->crtc_vtotal - mode->crtc_vsync_start; 280 DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n", 281 mode->crtc_vtotal, bp); 282 283 /* 284 * The vertical resolution needs to be doubled in all 285 * cases. We could use crtc_vtotal and always multiply by two, 286 * but that leads to a rounding error in interlace when vtotal 287 * is odd. 288 * 289 * This happens with TV's PAL for example, where vtotal will 290 * be 625, crtc_vtotal 312, and thus crtc_vtotal * 2 will be 291 * 624, which apparently confuses the hardware. 292 * 293 * To work around this, we will always use vtotal, and 294 * multiply by two only if we're not in interlace. 295 */ 296 vtotal = mode->vtotal; 297 if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) 298 vtotal = vtotal * 2; 299 300 /* Set vertical display timings */ 301 regmap_write(tcon->regs, SUN4I_TCON1_BASIC4_REG, 302 SUN4I_TCON1_BASIC4_V_TOTAL(vtotal) | 303 SUN4I_TCON1_BASIC4_V_BACKPORCH(bp)); 304 305 /* Set Hsync and Vsync length */ 306 hsync = mode->crtc_hsync_end - mode->crtc_hsync_start; 307 vsync = mode->crtc_vsync_end - mode->crtc_vsync_start; 308 DRM_DEBUG_DRIVER("Setting HSYNC %d, VSYNC %d\n", hsync, vsync); 309 regmap_write(tcon->regs, SUN4I_TCON1_BASIC5_REG, 310 SUN4I_TCON1_BASIC5_V_SYNC(vsync) | 311 SUN4I_TCON1_BASIC5_H_SYNC(hsync)); 312 313 /* Map output pins to channel 1 */ 314 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, 315 SUN4I_TCON_GCTL_IOMAP_MASK, 316 SUN4I_TCON_GCTL_IOMAP_TCON1); 317 } 318 EXPORT_SYMBOL(sun4i_tcon1_mode_set); 319 320 static void sun4i_tcon_finish_page_flip(struct drm_device *dev, 321 struct sun4i_crtc *scrtc) 322 { 323 unsigned long flags; 324 325 spin_lock_irqsave(&dev->event_lock, flags); 326 if (scrtc->event) { 327 drm_crtc_send_vblank_event(&scrtc->crtc, scrtc->event); 328 drm_crtc_vblank_put(&scrtc->crtc); 329 scrtc->event = NULL; 330 } 331 spin_unlock_irqrestore(&dev->event_lock, flags); 332 } 333 334 static irqreturn_t sun4i_tcon_handler(int irq, void *private) 335 { 336 struct sun4i_tcon *tcon = private; 337 struct drm_device *drm = tcon->drm; 338 struct sun4i_crtc *scrtc = tcon->crtc; 339 unsigned int status; 340 341 regmap_read(tcon->regs, SUN4I_TCON_GINT0_REG, &status); 342 343 if (!(status & (SUN4I_TCON_GINT0_VBLANK_INT(0) | 344 SUN4I_TCON_GINT0_VBLANK_INT(1)))) 345 return IRQ_NONE; 346 347 drm_crtc_handle_vblank(&scrtc->crtc); 348 sun4i_tcon_finish_page_flip(drm, scrtc); 349 350 /* Acknowledge the interrupt */ 351 regmap_update_bits(tcon->regs, SUN4I_TCON_GINT0_REG, 352 SUN4I_TCON_GINT0_VBLANK_INT(0) | 353 SUN4I_TCON_GINT0_VBLANK_INT(1), 354 0); 355 356 return IRQ_HANDLED; 357 } 358 359 static int sun4i_tcon_init_clocks(struct device *dev, 360 struct sun4i_tcon *tcon) 361 { 362 tcon->clk = devm_clk_get(dev, "ahb"); 363 if (IS_ERR(tcon->clk)) { 364 dev_err(dev, "Couldn't get the TCON bus clock\n"); 365 return PTR_ERR(tcon->clk); 366 } 367 clk_prepare_enable(tcon->clk); 368 369 tcon->sclk0 = devm_clk_get(dev, "tcon-ch0"); 370 if (IS_ERR(tcon->sclk0)) { 371 dev_err(dev, "Couldn't get the TCON channel 0 clock\n"); 372 return PTR_ERR(tcon->sclk0); 373 } 374 375 if (tcon->quirks->has_channel_1) { 376 tcon->sclk1 = devm_clk_get(dev, "tcon-ch1"); 377 if (IS_ERR(tcon->sclk1)) { 378 dev_err(dev, "Couldn't get the TCON channel 1 clock\n"); 379 return PTR_ERR(tcon->sclk1); 380 } 381 } 382 383 return 0; 384 } 385 386 static void sun4i_tcon_free_clocks(struct sun4i_tcon *tcon) 387 { 388 clk_disable_unprepare(tcon->clk); 389 } 390 391 static int sun4i_tcon_init_irq(struct device *dev, 392 struct sun4i_tcon *tcon) 393 { 394 struct platform_device *pdev = to_platform_device(dev); 395 int irq, ret; 396 397 irq = platform_get_irq(pdev, 0); 398 if (irq < 0) { 399 dev_err(dev, "Couldn't retrieve the TCON interrupt\n"); 400 return irq; 401 } 402 403 ret = devm_request_irq(dev, irq, sun4i_tcon_handler, 0, 404 dev_name(dev), tcon); 405 if (ret) { 406 dev_err(dev, "Couldn't request the IRQ\n"); 407 return ret; 408 } 409 410 return 0; 411 } 412 413 static struct regmap_config sun4i_tcon_regmap_config = { 414 .reg_bits = 32, 415 .val_bits = 32, 416 .reg_stride = 4, 417 .max_register = 0x800, 418 }; 419 420 static int sun4i_tcon_init_regmap(struct device *dev, 421 struct sun4i_tcon *tcon) 422 { 423 struct platform_device *pdev = to_platform_device(dev); 424 struct resource *res; 425 void __iomem *regs; 426 427 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 428 regs = devm_ioremap_resource(dev, res); 429 if (IS_ERR(regs)) 430 return PTR_ERR(regs); 431 432 tcon->regs = devm_regmap_init_mmio(dev, regs, 433 &sun4i_tcon_regmap_config); 434 if (IS_ERR(tcon->regs)) { 435 dev_err(dev, "Couldn't create the TCON regmap\n"); 436 return PTR_ERR(tcon->regs); 437 } 438 439 /* Make sure the TCON is disabled and all IRQs are off */ 440 regmap_write(tcon->regs, SUN4I_TCON_GCTL_REG, 0); 441 regmap_write(tcon->regs, SUN4I_TCON_GINT0_REG, 0); 442 regmap_write(tcon->regs, SUN4I_TCON_GINT1_REG, 0); 443 444 /* Disable IO lines and set them to tristate */ 445 regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, ~0); 446 regmap_write(tcon->regs, SUN4I_TCON1_IO_TRI_REG, ~0); 447 448 return 0; 449 } 450 451 /* 452 * On SoCs with the old display pipeline design (Display Engine 1.0), 453 * the TCON is always tied to just one backend. Hence we can traverse 454 * the of_graph upwards to find the backend our tcon is connected to, 455 * and take its ID as our own. 456 * 457 * We can either identify backends from their compatible strings, which 458 * means maintaining a large list of them. Or, since the backend is 459 * registered and binded before the TCON, we can just go through the 460 * list of registered backends and compare the device node. 461 * 462 * As the structures now store engines instead of backends, here this 463 * function in fact searches the corresponding engine, and the ID is 464 * requested via the get_id function of the engine. 465 */ 466 static struct sunxi_engine *sun4i_tcon_find_engine(struct sun4i_drv *drv, 467 struct device_node *node) 468 { 469 struct device_node *port, *ep, *remote; 470 struct sunxi_engine *engine; 471 472 port = of_graph_get_port_by_id(node, 0); 473 if (!port) 474 return ERR_PTR(-EINVAL); 475 476 for_each_available_child_of_node(port, ep) { 477 remote = of_graph_get_remote_port_parent(ep); 478 if (!remote) 479 continue; 480 481 /* does this node match any registered engines? */ 482 list_for_each_entry(engine, &drv->engine_list, list) { 483 if (remote == engine->node) { 484 of_node_put(remote); 485 of_node_put(port); 486 return engine; 487 } 488 } 489 490 /* keep looking through upstream ports */ 491 engine = sun4i_tcon_find_engine(drv, remote); 492 if (!IS_ERR(engine)) { 493 of_node_put(remote); 494 of_node_put(port); 495 return engine; 496 } 497 } 498 499 return ERR_PTR(-EINVAL); 500 } 501 502 static int sun4i_tcon_bind(struct device *dev, struct device *master, 503 void *data) 504 { 505 struct drm_device *drm = data; 506 struct sun4i_drv *drv = drm->dev_private; 507 struct sunxi_engine *engine; 508 struct sun4i_tcon *tcon; 509 int ret; 510 511 engine = sun4i_tcon_find_engine(drv, dev->of_node); 512 if (IS_ERR(engine)) { 513 dev_err(dev, "Couldn't find matching engine\n"); 514 return -EPROBE_DEFER; 515 } 516 517 tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL); 518 if (!tcon) 519 return -ENOMEM; 520 dev_set_drvdata(dev, tcon); 521 tcon->drm = drm; 522 tcon->dev = dev; 523 tcon->id = engine->id; 524 tcon->quirks = of_device_get_match_data(dev); 525 526 tcon->lcd_rst = devm_reset_control_get(dev, "lcd"); 527 if (IS_ERR(tcon->lcd_rst)) { 528 dev_err(dev, "Couldn't get our reset line\n"); 529 return PTR_ERR(tcon->lcd_rst); 530 } 531 532 /* Make sure our TCON is reset */ 533 if (!reset_control_status(tcon->lcd_rst)) 534 reset_control_assert(tcon->lcd_rst); 535 536 ret = reset_control_deassert(tcon->lcd_rst); 537 if (ret) { 538 dev_err(dev, "Couldn't deassert our reset line\n"); 539 return ret; 540 } 541 542 ret = sun4i_tcon_init_clocks(dev, tcon); 543 if (ret) { 544 dev_err(dev, "Couldn't init our TCON clocks\n"); 545 goto err_assert_reset; 546 } 547 548 ret = sun4i_tcon_init_regmap(dev, tcon); 549 if (ret) { 550 dev_err(dev, "Couldn't init our TCON regmap\n"); 551 goto err_free_clocks; 552 } 553 554 ret = sun4i_dclk_create(dev, tcon); 555 if (ret) { 556 dev_err(dev, "Couldn't create our TCON dot clock\n"); 557 goto err_free_clocks; 558 } 559 560 ret = sun4i_tcon_init_irq(dev, tcon); 561 if (ret) { 562 dev_err(dev, "Couldn't init our TCON interrupts\n"); 563 goto err_free_dotclock; 564 } 565 566 tcon->crtc = sun4i_crtc_init(drm, engine, tcon); 567 if (IS_ERR(tcon->crtc)) { 568 dev_err(dev, "Couldn't create our CRTC\n"); 569 ret = PTR_ERR(tcon->crtc); 570 goto err_free_clocks; 571 } 572 573 ret = sun4i_rgb_init(drm, tcon); 574 if (ret < 0) 575 goto err_free_clocks; 576 577 list_add_tail(&tcon->list, &drv->tcon_list); 578 579 return 0; 580 581 err_free_dotclock: 582 sun4i_dclk_free(tcon); 583 err_free_clocks: 584 sun4i_tcon_free_clocks(tcon); 585 err_assert_reset: 586 reset_control_assert(tcon->lcd_rst); 587 return ret; 588 } 589 590 static void sun4i_tcon_unbind(struct device *dev, struct device *master, 591 void *data) 592 { 593 struct sun4i_tcon *tcon = dev_get_drvdata(dev); 594 595 list_del(&tcon->list); 596 sun4i_dclk_free(tcon); 597 sun4i_tcon_free_clocks(tcon); 598 } 599 600 static const struct component_ops sun4i_tcon_ops = { 601 .bind = sun4i_tcon_bind, 602 .unbind = sun4i_tcon_unbind, 603 }; 604 605 static int sun4i_tcon_probe(struct platform_device *pdev) 606 { 607 struct device_node *node = pdev->dev.of_node; 608 struct drm_bridge *bridge; 609 struct drm_panel *panel; 610 int ret; 611 612 ret = drm_of_find_panel_or_bridge(node, 1, 0, &panel, &bridge); 613 if (ret == -EPROBE_DEFER) 614 return ret; 615 616 return component_add(&pdev->dev, &sun4i_tcon_ops); 617 } 618 619 static int sun4i_tcon_remove(struct platform_device *pdev) 620 { 621 component_del(&pdev->dev, &sun4i_tcon_ops); 622 623 return 0; 624 } 625 626 static const struct sun4i_tcon_quirks sun5i_a13_quirks = { 627 .has_unknown_mux = true, 628 .has_channel_1 = true, 629 }; 630 631 static const struct sun4i_tcon_quirks sun6i_a31_quirks = { 632 .has_channel_1 = true, 633 }; 634 635 static const struct sun4i_tcon_quirks sun6i_a31s_quirks = { 636 .has_channel_1 = true, 637 }; 638 639 static const struct sun4i_tcon_quirks sun8i_a33_quirks = { 640 /* nothing is supported */ 641 }; 642 643 static const struct sun4i_tcon_quirks sun8i_v3s_quirks = { 644 /* nothing is supported */ 645 }; 646 647 static const struct of_device_id sun4i_tcon_of_table[] = { 648 { .compatible = "allwinner,sun5i-a13-tcon", .data = &sun5i_a13_quirks }, 649 { .compatible = "allwinner,sun6i-a31-tcon", .data = &sun6i_a31_quirks }, 650 { .compatible = "allwinner,sun6i-a31s-tcon", .data = &sun6i_a31s_quirks }, 651 { .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks }, 652 { .compatible = "allwinner,sun8i-v3s-tcon", .data = &sun8i_v3s_quirks }, 653 { } 654 }; 655 MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table); 656 657 static struct platform_driver sun4i_tcon_platform_driver = { 658 .probe = sun4i_tcon_probe, 659 .remove = sun4i_tcon_remove, 660 .driver = { 661 .name = "sun4i-tcon", 662 .of_match_table = sun4i_tcon_of_table, 663 }, 664 }; 665 module_platform_driver(sun4i_tcon_platform_driver); 666 667 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); 668 MODULE_DESCRIPTION("Allwinner A10 Timing Controller Driver"); 669 MODULE_LICENSE("GPL"); 670