.. | .. |
---|
9 | 9 | #include <linux/clk.h> |
---|
10 | 10 | #include <linux/component.h> |
---|
11 | 11 | #include <linux/crc-ccitt.h> |
---|
| 12 | +#include <linux/module.h> |
---|
12 | 13 | #include <linux/of_address.h> |
---|
13 | | -#include <linux/pm_runtime.h> |
---|
| 14 | +#include <linux/phy/phy-mipi-dphy.h> |
---|
| 15 | +#include <linux/phy/phy.h> |
---|
| 16 | +#include <linux/platform_device.h> |
---|
14 | 17 | #include <linux/regmap.h> |
---|
| 18 | +#include <linux/regulator/consumer.h> |
---|
15 | 19 | #include <linux/reset.h> |
---|
16 | 20 | #include <linux/slab.h> |
---|
17 | 21 | |
---|
18 | | -#include <linux/phy/phy.h> |
---|
19 | | - |
---|
20 | | -#include <drm/drmP.h> |
---|
21 | 22 | #include <drm/drm_atomic_helper.h> |
---|
22 | | -#include <drm/drm_crtc_helper.h> |
---|
23 | 23 | #include <drm/drm_mipi_dsi.h> |
---|
24 | 24 | #include <drm/drm_panel.h> |
---|
| 25 | +#include <drm/drm_print.h> |
---|
| 26 | +#include <drm/drm_probe_helper.h> |
---|
| 27 | +#include <drm/drm_simple_kms_helper.h> |
---|
25 | 28 | |
---|
26 | | -#include "sun4i_drv.h" |
---|
| 29 | +#include "sun4i_crtc.h" |
---|
| 30 | +#include "sun4i_tcon.h" |
---|
27 | 31 | #include "sun6i_mipi_dsi.h" |
---|
28 | 32 | |
---|
29 | 33 | #include <video/mipi_display.h> |
---|
.. | .. |
---|
32 | 36 | #define SUN6I_DSI_CTL_EN BIT(0) |
---|
33 | 37 | |
---|
34 | 38 | #define SUN6I_DSI_BASIC_CTL_REG 0x00c |
---|
| 39 | +#define SUN6I_DSI_BASIC_CTL_TRAIL_INV(n) (((n) & 0xf) << 4) |
---|
| 40 | +#define SUN6I_DSI_BASIC_CTL_TRAIL_FILL BIT(3) |
---|
35 | 41 | #define SUN6I_DSI_BASIC_CTL_HBP_DIS BIT(2) |
---|
36 | 42 | #define SUN6I_DSI_BASIC_CTL_HSA_HSE_DIS BIT(1) |
---|
37 | 43 | #define SUN6I_DSI_BASIC_CTL_VIDEO_BURST BIT(0) |
---|
.. | .. |
---|
151 | 157 | #define SUN6I_DSI_DEBUG_DATA_REG 0x2f8 |
---|
152 | 158 | |
---|
153 | 159 | #define SUN6I_DSI_CMD_TX_REG(n) (0x300 + (n) * 0x04) |
---|
| 160 | + |
---|
| 161 | +#define SUN6I_DSI_SYNC_POINT 40 |
---|
154 | 162 | |
---|
155 | 163 | enum sun6i_dsi_start_inst { |
---|
156 | 164 | DSI_START_LPRX, |
---|
.. | .. |
---|
357 | 365 | static u16 sun6i_dsi_get_video_start_delay(struct sun6i_dsi *dsi, |
---|
358 | 366 | struct drm_display_mode *mode) |
---|
359 | 367 | { |
---|
360 | | - u16 delay = mode->vtotal - (mode->vsync_end - mode->vdisplay) + 1; |
---|
| 368 | + u16 delay = mode->vtotal - (mode->vsync_start - mode->vdisplay) + 1; |
---|
361 | 369 | |
---|
362 | 370 | if (delay > mode->vtotal) |
---|
363 | 371 | delay = delay % mode->vtotal; |
---|
364 | 372 | |
---|
365 | 373 | return max_t(u16, delay, 1); |
---|
| 374 | +} |
---|
| 375 | + |
---|
| 376 | +static u16 sun6i_dsi_get_line_num(struct sun6i_dsi *dsi, |
---|
| 377 | + struct drm_display_mode *mode) |
---|
| 378 | +{ |
---|
| 379 | + struct mipi_dsi_device *device = dsi->device; |
---|
| 380 | + unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8; |
---|
| 381 | + |
---|
| 382 | + return mode->htotal * Bpp / device->lanes; |
---|
| 383 | +} |
---|
| 384 | + |
---|
| 385 | +static u16 sun6i_dsi_get_drq_edge0(struct sun6i_dsi *dsi, |
---|
| 386 | + struct drm_display_mode *mode, |
---|
| 387 | + u16 line_num, u16 edge1) |
---|
| 388 | +{ |
---|
| 389 | + u16 edge0 = edge1; |
---|
| 390 | + |
---|
| 391 | + edge0 += (mode->hdisplay + 40) * SUN6I_DSI_TCON_DIV / 8; |
---|
| 392 | + |
---|
| 393 | + if (edge0 > line_num) |
---|
| 394 | + return edge0 - line_num; |
---|
| 395 | + |
---|
| 396 | + return 1; |
---|
| 397 | +} |
---|
| 398 | + |
---|
| 399 | +static u16 sun6i_dsi_get_drq_edge1(struct sun6i_dsi *dsi, |
---|
| 400 | + struct drm_display_mode *mode, |
---|
| 401 | + u16 line_num) |
---|
| 402 | +{ |
---|
| 403 | + struct mipi_dsi_device *device = dsi->device; |
---|
| 404 | + unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8; |
---|
| 405 | + unsigned int hbp = mode->htotal - mode->hsync_end; |
---|
| 406 | + u16 edge1; |
---|
| 407 | + |
---|
| 408 | + edge1 = SUN6I_DSI_SYNC_POINT; |
---|
| 409 | + edge1 += (mode->hdisplay + hbp + 20) * Bpp / device->lanes; |
---|
| 410 | + |
---|
| 411 | + if (edge1 > line_num) |
---|
| 412 | + return line_num; |
---|
| 413 | + |
---|
| 414 | + return edge1; |
---|
366 | 415 | } |
---|
367 | 416 | |
---|
368 | 417 | static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi, |
---|
.. | .. |
---|
371 | 420 | struct mipi_dsi_device *device = dsi->device; |
---|
372 | 421 | u32 val = 0; |
---|
373 | 422 | |
---|
374 | | - if ((mode->hsync_end - mode->hdisplay) > 20) { |
---|
| 423 | + if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) { |
---|
| 424 | + u16 line_num = sun6i_dsi_get_line_num(dsi, mode); |
---|
| 425 | + u16 edge0, edge1; |
---|
| 426 | + |
---|
| 427 | + edge1 = sun6i_dsi_get_drq_edge1(dsi, mode, line_num); |
---|
| 428 | + edge0 = sun6i_dsi_get_drq_edge0(dsi, mode, line_num, edge1); |
---|
| 429 | + |
---|
| 430 | + regmap_write(dsi->regs, SUN6I_DSI_BURST_DRQ_REG, |
---|
| 431 | + SUN6I_DSI_BURST_DRQ_EDGE0(edge0) | |
---|
| 432 | + SUN6I_DSI_BURST_DRQ_EDGE1(edge1)); |
---|
| 433 | + |
---|
| 434 | + regmap_write(dsi->regs, SUN6I_DSI_BURST_LINE_REG, |
---|
| 435 | + SUN6I_DSI_BURST_LINE_NUM(line_num) | |
---|
| 436 | + SUN6I_DSI_BURST_LINE_SYNC_POINT(SUN6I_DSI_SYNC_POINT)); |
---|
| 437 | + |
---|
| 438 | + val = SUN6I_DSI_TCON_DRQ_ENABLE_MODE; |
---|
| 439 | + } else if ((mode->hsync_start - mode->hdisplay) > 20) { |
---|
375 | 440 | /* Maaaaaagic */ |
---|
376 | | - u16 drq = (mode->hsync_end - mode->hdisplay) - 20; |
---|
| 441 | + u16 drq = (mode->hsync_start - mode->hdisplay) - 20; |
---|
377 | 442 | |
---|
378 | 443 | drq *= mipi_dsi_pixel_format_to_bpp(device->format); |
---|
379 | 444 | drq /= 32; |
---|
.. | .. |
---|
388 | 453 | static void sun6i_dsi_setup_inst_loop(struct sun6i_dsi *dsi, |
---|
389 | 454 | struct drm_display_mode *mode) |
---|
390 | 455 | { |
---|
| 456 | + struct mipi_dsi_device *device = dsi->device; |
---|
391 | 457 | u16 delay = 50 - 1; |
---|
| 458 | + |
---|
| 459 | + if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) { |
---|
| 460 | + u32 hsync_porch = (mode->htotal - mode->hdisplay) * 150; |
---|
| 461 | + |
---|
| 462 | + delay = (hsync_porch / ((mode->clock / 1000) * 8)); |
---|
| 463 | + delay -= 50; |
---|
| 464 | + } |
---|
| 465 | + |
---|
| 466 | + regmap_write(dsi->regs, SUN6I_DSI_INST_LOOP_SEL_REG, |
---|
| 467 | + 2 << (4 * DSI_INST_ID_LP11) | |
---|
| 468 | + 3 << (4 * DSI_INST_ID_DLY)); |
---|
392 | 469 | |
---|
393 | 470 | regmap_write(dsi->regs, SUN6I_DSI_INST_LOOP_NUM_REG(0), |
---|
394 | 471 | SUN6I_DSI_INST_LOOP_NUM_N0(50 - 1) | |
---|
.. | .. |
---|
454 | 531 | struct drm_display_mode *mode) |
---|
455 | 532 | { |
---|
456 | 533 | struct mipi_dsi_device *device = dsi->device; |
---|
457 | | - unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8; |
---|
458 | | - u16 hbp, hfp, hsa, hblk, vblk; |
---|
| 534 | + int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8; |
---|
| 535 | + u16 hbp = 0, hfp = 0, hsa = 0, hblk = 0, vblk = 0; |
---|
| 536 | + u32 basic_ctl = 0; |
---|
459 | 537 | size_t bytes; |
---|
460 | 538 | u8 *buffer; |
---|
461 | 539 | |
---|
462 | 540 | /* Do all timing calculations up front to allocate buffer space */ |
---|
463 | 541 | |
---|
464 | | - /* |
---|
465 | | - * A sync period is composed of a blanking packet (4 bytes + |
---|
466 | | - * payload + 2 bytes) and a sync event packet (4 bytes). Its |
---|
467 | | - * minimal size is therefore 10 bytes |
---|
468 | | - */ |
---|
| 542 | + if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) { |
---|
| 543 | + hblk = mode->hdisplay * Bpp; |
---|
| 544 | + basic_ctl = SUN6I_DSI_BASIC_CTL_VIDEO_BURST | |
---|
| 545 | + SUN6I_DSI_BASIC_CTL_HSA_HSE_DIS | |
---|
| 546 | + SUN6I_DSI_BASIC_CTL_HBP_DIS; |
---|
| 547 | + |
---|
| 548 | + if (device->lanes == 4) |
---|
| 549 | + basic_ctl |= SUN6I_DSI_BASIC_CTL_TRAIL_FILL | |
---|
| 550 | + SUN6I_DSI_BASIC_CTL_TRAIL_INV(0xc); |
---|
| 551 | + } else { |
---|
| 552 | + /* |
---|
| 553 | + * A sync period is composed of a blanking packet (4 |
---|
| 554 | + * bytes + payload + 2 bytes) and a sync event packet |
---|
| 555 | + * (4 bytes). Its minimal size is therefore 10 bytes |
---|
| 556 | + */ |
---|
469 | 557 | #define HSA_PACKET_OVERHEAD 10 |
---|
470 | | - hsa = max((unsigned int)HSA_PACKET_OVERHEAD, |
---|
471 | | - (mode->hsync_end - mode->hsync_start) * Bpp - HSA_PACKET_OVERHEAD); |
---|
| 558 | + hsa = max(HSA_PACKET_OVERHEAD, |
---|
| 559 | + (mode->hsync_end - mode->hsync_start) * Bpp - HSA_PACKET_OVERHEAD); |
---|
472 | 560 | |
---|
473 | | - /* |
---|
474 | | - * The backporch is set using a blanking packet (4 bytes + |
---|
475 | | - * payload + 2 bytes). Its minimal size is therefore 6 bytes |
---|
476 | | - */ |
---|
| 561 | + /* |
---|
| 562 | + * The backporch is set using a blanking packet (4 |
---|
| 563 | + * bytes + payload + 2 bytes). Its minimal size is |
---|
| 564 | + * therefore 6 bytes |
---|
| 565 | + */ |
---|
477 | 566 | #define HBP_PACKET_OVERHEAD 6 |
---|
478 | | - hbp = max((unsigned int)HBP_PACKET_OVERHEAD, |
---|
479 | | - (mode->hsync_start - mode->hdisplay) * Bpp - HBP_PACKET_OVERHEAD); |
---|
| 567 | + hbp = max(HBP_PACKET_OVERHEAD, |
---|
| 568 | + (mode->htotal - mode->hsync_end) * Bpp - HBP_PACKET_OVERHEAD); |
---|
480 | 569 | |
---|
481 | | - /* |
---|
482 | | - * The frontporch is set using a blanking packet (4 bytes + |
---|
483 | | - * payload + 2 bytes). Its minimal size is therefore 6 bytes |
---|
484 | | - */ |
---|
485 | | -#define HFP_PACKET_OVERHEAD 6 |
---|
486 | | - hfp = max((unsigned int)HFP_PACKET_OVERHEAD, |
---|
487 | | - (mode->htotal - mode->hsync_end) * Bpp - HFP_PACKET_OVERHEAD); |
---|
| 570 | + /* |
---|
| 571 | + * The frontporch is set using a sync event (4 bytes) |
---|
| 572 | + * and two blanking packets (each one is 4 bytes + |
---|
| 573 | + * payload + 2 bytes). Its minimal size is therefore |
---|
| 574 | + * 16 bytes |
---|
| 575 | + */ |
---|
| 576 | +#define HFP_PACKET_OVERHEAD 16 |
---|
| 577 | + hfp = max(HFP_PACKET_OVERHEAD, |
---|
| 578 | + (mode->hsync_start - mode->hdisplay) * Bpp - HFP_PACKET_OVERHEAD); |
---|
488 | 579 | |
---|
489 | | - /* |
---|
490 | | - * hblk seems to be the line + porches length. |
---|
491 | | - */ |
---|
492 | | - hblk = mode->htotal * Bpp - hsa; |
---|
| 580 | + /* |
---|
| 581 | + * The blanking is set using a sync event (4 bytes) |
---|
| 582 | + * and a blanking packet (4 bytes + payload + 2 |
---|
| 583 | + * bytes). Its minimal size is therefore 10 bytes. |
---|
| 584 | + */ |
---|
| 585 | +#define HBLK_PACKET_OVERHEAD 10 |
---|
| 586 | + hblk = max(HBLK_PACKET_OVERHEAD, |
---|
| 587 | + (mode->htotal - (mode->hsync_end - mode->hsync_start)) * Bpp - |
---|
| 588 | + HBLK_PACKET_OVERHEAD); |
---|
493 | 589 | |
---|
494 | | - /* |
---|
495 | | - * And I'm not entirely sure what vblk is about. The driver in |
---|
496 | | - * Allwinner BSP is using a rather convoluted calculation |
---|
497 | | - * there only for 4 lanes. However, using 0 (the !4 lanes |
---|
498 | | - * case) even with a 4 lanes screen seems to work... |
---|
499 | | - */ |
---|
500 | | - vblk = 0; |
---|
| 590 | + /* |
---|
| 591 | + * And I'm not entirely sure what vblk is about. The driver in |
---|
| 592 | + * Allwinner BSP is using a rather convoluted calculation |
---|
| 593 | + * there only for 4 lanes. However, using 0 (the !4 lanes |
---|
| 594 | + * case) even with a 4 lanes screen seems to work... |
---|
| 595 | + */ |
---|
| 596 | + vblk = 0; |
---|
| 597 | + } |
---|
501 | 598 | |
---|
502 | 599 | /* How many bytes do we need to send all payloads? */ |
---|
503 | 600 | bytes = max_t(size_t, max(max(hfp, hblk), max(hsa, hbp)), vblk); |
---|
.. | .. |
---|
505 | 602 | if (WARN_ON(!buffer)) |
---|
506 | 603 | return; |
---|
507 | 604 | |
---|
508 | | - regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, 0); |
---|
| 605 | + regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, basic_ctl); |
---|
509 | 606 | |
---|
510 | 607 | regmap_write(dsi->regs, SUN6I_DSI_SYNC_HSS_REG, |
---|
511 | 608 | sun6i_dsi_build_sync_pkt(MIPI_DSI_H_SYNC_START, |
---|
.. | .. |
---|
530 | 627 | regmap_write(dsi->regs, SUN6I_DSI_BASIC_SIZE0_REG, |
---|
531 | 628 | SUN6I_DSI_BASIC_SIZE0_VSA(mode->vsync_end - |
---|
532 | 629 | mode->vsync_start) | |
---|
533 | | - SUN6I_DSI_BASIC_SIZE0_VBP(mode->vsync_start - |
---|
534 | | - mode->vdisplay)); |
---|
| 630 | + SUN6I_DSI_BASIC_SIZE0_VBP(mode->vtotal - |
---|
| 631 | + mode->vsync_end)); |
---|
535 | 632 | |
---|
536 | 633 | regmap_write(dsi->regs, SUN6I_DSI_BASIC_SIZE1_REG, |
---|
537 | 634 | SUN6I_DSI_BASIC_SIZE1_VACT(mode->vdisplay) | |
---|
.. | .. |
---|
621 | 718 | struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; |
---|
622 | 719 | struct sun6i_dsi *dsi = encoder_to_sun6i_dsi(encoder); |
---|
623 | 720 | struct mipi_dsi_device *device = dsi->device; |
---|
| 721 | + union phy_configure_opts opts = { }; |
---|
| 722 | + struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy; |
---|
624 | 723 | u16 delay; |
---|
| 724 | + int err; |
---|
625 | 725 | |
---|
626 | 726 | DRM_DEBUG_DRIVER("Enabling DSI output\n"); |
---|
627 | 727 | |
---|
628 | | - pm_runtime_get_sync(dsi->dev); |
---|
| 728 | + err = regulator_enable(dsi->regulator); |
---|
| 729 | + if (err) |
---|
| 730 | + dev_warn(dsi->dev, "failed to enable VCC-DSI supply: %d\n", err); |
---|
| 731 | + |
---|
| 732 | + reset_control_deassert(dsi->reset); |
---|
| 733 | + clk_prepare_enable(dsi->mod_clk); |
---|
| 734 | + |
---|
| 735 | + /* |
---|
| 736 | + * Enable the DSI block. |
---|
| 737 | + */ |
---|
| 738 | + regmap_write(dsi->regs, SUN6I_DSI_CTL_REG, SUN6I_DSI_CTL_EN); |
---|
| 739 | + |
---|
| 740 | + regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL0_REG, |
---|
| 741 | + SUN6I_DSI_BASIC_CTL0_ECC_EN | SUN6I_DSI_BASIC_CTL0_CRC_EN); |
---|
| 742 | + |
---|
| 743 | + regmap_write(dsi->regs, SUN6I_DSI_TRANS_START_REG, 10); |
---|
| 744 | + regmap_write(dsi->regs, SUN6I_DSI_TRANS_ZERO_REG, 0); |
---|
| 745 | + |
---|
| 746 | + sun6i_dsi_inst_init(dsi, dsi->device); |
---|
| 747 | + |
---|
| 748 | + regmap_write(dsi->regs, SUN6I_DSI_DEBUG_DATA_REG, 0xff); |
---|
629 | 749 | |
---|
630 | 750 | delay = sun6i_dsi_get_video_start_delay(dsi, mode); |
---|
631 | 751 | regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL1_REG, |
---|
.. | .. |
---|
639 | 759 | sun6i_dsi_setup_format(dsi, mode); |
---|
640 | 760 | sun6i_dsi_setup_timings(dsi, mode); |
---|
641 | 761 | |
---|
642 | | - sun6i_dphy_init(dsi->dphy, device->lanes); |
---|
643 | | - sun6i_dphy_power_on(dsi->dphy, device->lanes); |
---|
| 762 | + phy_init(dsi->dphy); |
---|
644 | 763 | |
---|
645 | | - if (!IS_ERR(dsi->panel)) |
---|
| 764 | + phy_mipi_dphy_get_default_config(mode->clock * 1000, |
---|
| 765 | + mipi_dsi_pixel_format_to_bpp(device->format), |
---|
| 766 | + device->lanes, cfg); |
---|
| 767 | + |
---|
| 768 | + phy_set_mode(dsi->dphy, PHY_MODE_MIPI_DPHY); |
---|
| 769 | + phy_configure(dsi->dphy, &opts); |
---|
| 770 | + phy_power_on(dsi->dphy); |
---|
| 771 | + |
---|
| 772 | + if (dsi->panel) |
---|
646 | 773 | drm_panel_prepare(dsi->panel); |
---|
647 | 774 | |
---|
648 | 775 | /* |
---|
.. | .. |
---|
657 | 784 | * ordering on the panels I've tested it with, so I guess this |
---|
658 | 785 | * will do for now, until that IP is better understood. |
---|
659 | 786 | */ |
---|
660 | | - if (!IS_ERR(dsi->panel)) |
---|
| 787 | + if (dsi->panel) |
---|
661 | 788 | drm_panel_enable(dsi->panel); |
---|
662 | 789 | |
---|
663 | 790 | sun6i_dsi_start(dsi, DSI_START_HSC); |
---|
.. | .. |
---|
673 | 800 | |
---|
674 | 801 | DRM_DEBUG_DRIVER("Disabling DSI output\n"); |
---|
675 | 802 | |
---|
676 | | - if (!IS_ERR(dsi->panel)) { |
---|
| 803 | + if (dsi->panel) { |
---|
677 | 804 | drm_panel_disable(dsi->panel); |
---|
678 | 805 | drm_panel_unprepare(dsi->panel); |
---|
679 | 806 | } |
---|
680 | 807 | |
---|
681 | | - sun6i_dphy_power_off(dsi->dphy); |
---|
682 | | - sun6i_dphy_exit(dsi->dphy); |
---|
| 808 | + phy_power_off(dsi->dphy); |
---|
| 809 | + phy_exit(dsi->dphy); |
---|
683 | 810 | |
---|
684 | | - pm_runtime_put(dsi->dev); |
---|
| 811 | + clk_disable_unprepare(dsi->mod_clk); |
---|
| 812 | + reset_control_assert(dsi->reset); |
---|
| 813 | + regulator_disable(dsi->regulator); |
---|
685 | 814 | } |
---|
686 | 815 | |
---|
687 | 816 | static int sun6i_dsi_get_modes(struct drm_connector *connector) |
---|
688 | 817 | { |
---|
689 | 818 | struct sun6i_dsi *dsi = connector_to_sun6i_dsi(connector); |
---|
690 | 819 | |
---|
691 | | - return drm_panel_get_modes(dsi->panel); |
---|
| 820 | + return drm_panel_get_modes(dsi->panel, connector); |
---|
692 | 821 | } |
---|
693 | 822 | |
---|
694 | | -static struct drm_connector_helper_funcs sun6i_dsi_connector_helper_funcs = { |
---|
| 823 | +static const struct drm_connector_helper_funcs sun6i_dsi_connector_helper_funcs = { |
---|
695 | 824 | .get_modes = sun6i_dsi_get_modes, |
---|
696 | 825 | }; |
---|
697 | 826 | |
---|
698 | 827 | static enum drm_connector_status |
---|
699 | 828 | sun6i_dsi_connector_detect(struct drm_connector *connector, bool force) |
---|
700 | 829 | { |
---|
701 | | - return connector_status_connected; |
---|
| 830 | + struct sun6i_dsi *dsi = connector_to_sun6i_dsi(connector); |
---|
| 831 | + |
---|
| 832 | + return dsi->panel ? connector_status_connected : |
---|
| 833 | + connector_status_disconnected; |
---|
702 | 834 | } |
---|
703 | 835 | |
---|
704 | 836 | static const struct drm_connector_funcs sun6i_dsi_connector_funcs = { |
---|
.. | .. |
---|
715 | 847 | .enable = sun6i_dsi_encoder_enable, |
---|
716 | 848 | }; |
---|
717 | 849 | |
---|
718 | | -static const struct drm_encoder_funcs sun6i_dsi_enc_funcs = { |
---|
719 | | - .destroy = drm_encoder_cleanup, |
---|
720 | | -}; |
---|
721 | | - |
---|
722 | 850 | static u32 sun6i_dsi_dcs_build_pkt_hdr(struct sun6i_dsi *dsi, |
---|
723 | 851 | const struct mipi_dsi_msg *msg) |
---|
724 | 852 | { |
---|
725 | 853 | u32 pkt = msg->type; |
---|
726 | 854 | |
---|
727 | 855 | if (msg->type == MIPI_DSI_DCS_LONG_WRITE) { |
---|
728 | | - pkt |= ((msg->tx_len + 1) & 0xffff) << 8; |
---|
729 | | - pkt |= (((msg->tx_len + 1) >> 8) & 0xffff) << 16; |
---|
| 856 | + pkt |= ((msg->tx_len) & 0xffff) << 8; |
---|
| 857 | + pkt |= (((msg->tx_len) >> 8) & 0xffff) << 16; |
---|
730 | 858 | } else { |
---|
731 | 859 | pkt |= (((u8 *)msg->tx_buf)[0] << 8); |
---|
732 | 860 | if (msg->tx_len > 1) |
---|
.. | .. |
---|
835 | 963 | struct mipi_dsi_device *device) |
---|
836 | 964 | { |
---|
837 | 965 | struct sun6i_dsi *dsi = host_to_sun6i_dsi(host); |
---|
| 966 | + struct drm_panel *panel = of_drm_find_panel(device->dev.of_node); |
---|
838 | 967 | |
---|
| 968 | + if (IS_ERR(panel)) |
---|
| 969 | + return PTR_ERR(panel); |
---|
| 970 | + if (!dsi->drm || !dsi->drm->registered) |
---|
| 971 | + return -EPROBE_DEFER; |
---|
| 972 | + |
---|
| 973 | + dsi->panel = panel; |
---|
839 | 974 | dsi->device = device; |
---|
840 | | - dsi->panel = of_drm_find_panel(device->dev.of_node); |
---|
841 | | - if (IS_ERR(dsi->panel)) |
---|
842 | | - return PTR_ERR(dsi->panel); |
---|
| 975 | + |
---|
| 976 | + drm_kms_helper_hotplug_event(dsi->drm); |
---|
843 | 977 | |
---|
844 | 978 | dev_info(host->dev, "Attached device %s\n", device->name); |
---|
845 | 979 | |
---|
.. | .. |
---|
853 | 987 | |
---|
854 | 988 | dsi->panel = NULL; |
---|
855 | 989 | dsi->device = NULL; |
---|
| 990 | + |
---|
| 991 | + drm_kms_helper_hotplug_event(dsi->drm); |
---|
856 | 992 | |
---|
857 | 993 | return 0; |
---|
858 | 994 | } |
---|
.. | .. |
---|
875 | 1011 | switch (msg->type) { |
---|
876 | 1012 | case MIPI_DSI_DCS_SHORT_WRITE: |
---|
877 | 1013 | case MIPI_DSI_DCS_SHORT_WRITE_PARAM: |
---|
| 1014 | + case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM: |
---|
878 | 1015 | ret = sun6i_dsi_dcs_write_short(dsi, msg); |
---|
879 | 1016 | break; |
---|
880 | 1017 | |
---|
.. | .. |
---|
887 | 1024 | ret = sun6i_dsi_dcs_read(dsi, msg); |
---|
888 | 1025 | break; |
---|
889 | 1026 | } |
---|
| 1027 | + fallthrough; |
---|
890 | 1028 | |
---|
891 | 1029 | default: |
---|
892 | 1030 | ret = -EINVAL; |
---|
.. | .. |
---|
913 | 1051 | void *data) |
---|
914 | 1052 | { |
---|
915 | 1053 | struct drm_device *drm = data; |
---|
916 | | - struct sun4i_drv *drv = drm->dev_private; |
---|
917 | 1054 | struct sun6i_dsi *dsi = dev_get_drvdata(dev); |
---|
918 | 1055 | int ret; |
---|
919 | 1056 | |
---|
920 | | - if (!dsi->panel) |
---|
921 | | - return -EPROBE_DEFER; |
---|
922 | | - |
---|
923 | | - dsi->drv = drv; |
---|
924 | | - |
---|
925 | 1057 | drm_encoder_helper_add(&dsi->encoder, |
---|
926 | 1058 | &sun6i_dsi_enc_helper_funcs); |
---|
927 | | - ret = drm_encoder_init(drm, |
---|
928 | | - &dsi->encoder, |
---|
929 | | - &sun6i_dsi_enc_funcs, |
---|
930 | | - DRM_MODE_ENCODER_DSI, |
---|
931 | | - NULL); |
---|
| 1059 | + ret = drm_simple_encoder_init(drm, &dsi->encoder, |
---|
| 1060 | + DRM_MODE_ENCODER_DSI); |
---|
932 | 1061 | if (ret) { |
---|
933 | 1062 | dev_err(dsi->dev, "Couldn't initialise the DSI encoder\n"); |
---|
934 | 1063 | return ret; |
---|
.. | .. |
---|
947 | 1076 | } |
---|
948 | 1077 | |
---|
949 | 1078 | drm_connector_attach_encoder(&dsi->connector, &dsi->encoder); |
---|
950 | | - drm_panel_attach(dsi->panel, &dsi->connector); |
---|
| 1079 | + |
---|
| 1080 | + dsi->drm = drm; |
---|
951 | 1081 | |
---|
952 | 1082 | return 0; |
---|
953 | 1083 | |
---|
.. | .. |
---|
961 | 1091 | { |
---|
962 | 1092 | struct sun6i_dsi *dsi = dev_get_drvdata(dev); |
---|
963 | 1093 | |
---|
964 | | - drm_panel_detach(dsi->panel); |
---|
| 1094 | + dsi->drm = NULL; |
---|
965 | 1095 | } |
---|
966 | 1096 | |
---|
967 | 1097 | static const struct component_ops sun6i_dsi_ops = { |
---|
.. | .. |
---|
972 | 1102 | static int sun6i_dsi_probe(struct platform_device *pdev) |
---|
973 | 1103 | { |
---|
974 | 1104 | struct device *dev = &pdev->dev; |
---|
975 | | - struct device_node *dphy_node; |
---|
| 1105 | + const char *bus_clk_name = NULL; |
---|
976 | 1106 | struct sun6i_dsi *dsi; |
---|
977 | 1107 | struct resource *res; |
---|
978 | 1108 | void __iomem *base; |
---|
.. | .. |
---|
986 | 1116 | dsi->host.ops = &sun6i_dsi_host_ops; |
---|
987 | 1117 | dsi->host.dev = dev; |
---|
988 | 1118 | |
---|
| 1119 | + if (of_device_is_compatible(dev->of_node, |
---|
| 1120 | + "allwinner,sun6i-a31-mipi-dsi")) |
---|
| 1121 | + bus_clk_name = "bus"; |
---|
| 1122 | + |
---|
989 | 1123 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
990 | 1124 | base = devm_ioremap_resource(dev, res); |
---|
991 | 1125 | if (IS_ERR(base)) { |
---|
.. | .. |
---|
993 | 1127 | return PTR_ERR(base); |
---|
994 | 1128 | } |
---|
995 | 1129 | |
---|
996 | | - dsi->regs = devm_regmap_init_mmio_clk(dev, "bus", base, |
---|
997 | | - &sun6i_dsi_regmap_config); |
---|
998 | | - if (IS_ERR(dsi->regs)) { |
---|
999 | | - dev_err(dev, "Couldn't create the DSI encoder regmap\n"); |
---|
1000 | | - return PTR_ERR(dsi->regs); |
---|
| 1130 | + dsi->regulator = devm_regulator_get(dev, "vcc-dsi"); |
---|
| 1131 | + if (IS_ERR(dsi->regulator)) { |
---|
| 1132 | + dev_err(dev, "Couldn't get VCC-DSI supply\n"); |
---|
| 1133 | + return PTR_ERR(dsi->regulator); |
---|
1001 | 1134 | } |
---|
1002 | 1135 | |
---|
1003 | 1136 | dsi->reset = devm_reset_control_get_shared(dev, NULL); |
---|
.. | .. |
---|
1006 | 1139 | return PTR_ERR(dsi->reset); |
---|
1007 | 1140 | } |
---|
1008 | 1141 | |
---|
1009 | | - dsi->mod_clk = devm_clk_get(dev, "mod"); |
---|
1010 | | - if (IS_ERR(dsi->mod_clk)) { |
---|
1011 | | - dev_err(dev, "Couldn't get the DSI mod clock\n"); |
---|
1012 | | - return PTR_ERR(dsi->mod_clk); |
---|
| 1142 | + dsi->regs = devm_regmap_init_mmio(dev, base, &sun6i_dsi_regmap_config); |
---|
| 1143 | + if (IS_ERR(dsi->regs)) { |
---|
| 1144 | + dev_err(dev, "Couldn't init regmap\n"); |
---|
| 1145 | + return PTR_ERR(dsi->regs); |
---|
| 1146 | + } |
---|
| 1147 | + |
---|
| 1148 | + dsi->bus_clk = devm_clk_get(dev, bus_clk_name); |
---|
| 1149 | + if (IS_ERR(dsi->bus_clk)) { |
---|
| 1150 | + dev_err(dev, "Couldn't get the DSI bus clock\n"); |
---|
| 1151 | + return PTR_ERR(dsi->bus_clk); |
---|
| 1152 | + } |
---|
| 1153 | + |
---|
| 1154 | + ret = regmap_mmio_attach_clk(dsi->regs, dsi->bus_clk); |
---|
| 1155 | + if (ret) |
---|
| 1156 | + return ret; |
---|
| 1157 | + |
---|
| 1158 | + if (of_device_is_compatible(dev->of_node, |
---|
| 1159 | + "allwinner,sun6i-a31-mipi-dsi")) { |
---|
| 1160 | + dsi->mod_clk = devm_clk_get(dev, "mod"); |
---|
| 1161 | + if (IS_ERR(dsi->mod_clk)) { |
---|
| 1162 | + dev_err(dev, "Couldn't get the DSI mod clock\n"); |
---|
| 1163 | + ret = PTR_ERR(dsi->mod_clk); |
---|
| 1164 | + goto err_attach_clk; |
---|
| 1165 | + } |
---|
1013 | 1166 | } |
---|
1014 | 1167 | |
---|
1015 | 1168 | /* |
---|
.. | .. |
---|
1018 | 1171 | */ |
---|
1019 | 1172 | clk_set_rate_exclusive(dsi->mod_clk, 297000000); |
---|
1020 | 1173 | |
---|
1021 | | - dphy_node = of_parse_phandle(dev->of_node, "phys", 0); |
---|
1022 | | - ret = sun6i_dphy_probe(dsi, dphy_node); |
---|
1023 | | - of_node_put(dphy_node); |
---|
1024 | | - if (ret) { |
---|
| 1174 | + dsi->dphy = devm_phy_get(dev, "dphy"); |
---|
| 1175 | + if (IS_ERR(dsi->dphy)) { |
---|
1025 | 1176 | dev_err(dev, "Couldn't get the MIPI D-PHY\n"); |
---|
| 1177 | + ret = PTR_ERR(dsi->dphy); |
---|
1026 | 1178 | goto err_unprotect_clk; |
---|
1027 | 1179 | } |
---|
1028 | | - |
---|
1029 | | - pm_runtime_enable(dev); |
---|
1030 | 1180 | |
---|
1031 | 1181 | ret = mipi_dsi_host_register(&dsi->host); |
---|
1032 | 1182 | if (ret) { |
---|
1033 | 1183 | dev_err(dev, "Couldn't register MIPI-DSI host\n"); |
---|
1034 | | - goto err_remove_phy; |
---|
| 1184 | + goto err_unprotect_clk; |
---|
1035 | 1185 | } |
---|
1036 | 1186 | |
---|
1037 | 1187 | ret = component_add(&pdev->dev, &sun6i_dsi_ops); |
---|
.. | .. |
---|
1044 | 1194 | |
---|
1045 | 1195 | err_remove_dsi_host: |
---|
1046 | 1196 | mipi_dsi_host_unregister(&dsi->host); |
---|
1047 | | -err_remove_phy: |
---|
1048 | | - pm_runtime_disable(dev); |
---|
1049 | | - sun6i_dphy_remove(dsi); |
---|
1050 | 1197 | err_unprotect_clk: |
---|
1051 | 1198 | clk_rate_exclusive_put(dsi->mod_clk); |
---|
| 1199 | +err_attach_clk: |
---|
| 1200 | + if (!IS_ERR(dsi->bus_clk)) |
---|
| 1201 | + regmap_mmio_detach_clk(dsi->regs); |
---|
1052 | 1202 | return ret; |
---|
1053 | 1203 | } |
---|
1054 | 1204 | |
---|
.. | .. |
---|
1059 | 1209 | |
---|
1060 | 1210 | component_del(&pdev->dev, &sun6i_dsi_ops); |
---|
1061 | 1211 | mipi_dsi_host_unregister(&dsi->host); |
---|
1062 | | - pm_runtime_disable(dev); |
---|
1063 | | - sun6i_dphy_remove(dsi); |
---|
1064 | 1212 | clk_rate_exclusive_put(dsi->mod_clk); |
---|
1065 | 1213 | |
---|
1066 | | - return 0; |
---|
1067 | | -} |
---|
1068 | | - |
---|
1069 | | -static int __maybe_unused sun6i_dsi_runtime_resume(struct device *dev) |
---|
1070 | | -{ |
---|
1071 | | - struct sun6i_dsi *dsi = dev_get_drvdata(dev); |
---|
1072 | | - |
---|
1073 | | - reset_control_deassert(dsi->reset); |
---|
1074 | | - clk_prepare_enable(dsi->mod_clk); |
---|
1075 | | - |
---|
1076 | | - /* |
---|
1077 | | - * Enable the DSI block. |
---|
1078 | | - * |
---|
1079 | | - * Some part of it can only be done once we get a number of |
---|
1080 | | - * lanes, see sun6i_dsi_inst_init |
---|
1081 | | - */ |
---|
1082 | | - regmap_write(dsi->regs, SUN6I_DSI_CTL_REG, SUN6I_DSI_CTL_EN); |
---|
1083 | | - |
---|
1084 | | - regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL0_REG, |
---|
1085 | | - SUN6I_DSI_BASIC_CTL0_ECC_EN | SUN6I_DSI_BASIC_CTL0_CRC_EN); |
---|
1086 | | - |
---|
1087 | | - regmap_write(dsi->regs, SUN6I_DSI_TRANS_START_REG, 10); |
---|
1088 | | - regmap_write(dsi->regs, SUN6I_DSI_TRANS_ZERO_REG, 0); |
---|
1089 | | - |
---|
1090 | | - if (dsi->device) |
---|
1091 | | - sun6i_dsi_inst_init(dsi, dsi->device); |
---|
1092 | | - |
---|
1093 | | - regmap_write(dsi->regs, SUN6I_DSI_DEBUG_DATA_REG, 0xff); |
---|
| 1214 | + if (!IS_ERR(dsi->bus_clk)) |
---|
| 1215 | + regmap_mmio_detach_clk(dsi->regs); |
---|
1094 | 1216 | |
---|
1095 | 1217 | return 0; |
---|
1096 | 1218 | } |
---|
1097 | | - |
---|
1098 | | -static int __maybe_unused sun6i_dsi_runtime_suspend(struct device *dev) |
---|
1099 | | -{ |
---|
1100 | | - struct sun6i_dsi *dsi = dev_get_drvdata(dev); |
---|
1101 | | - |
---|
1102 | | - clk_disable_unprepare(dsi->mod_clk); |
---|
1103 | | - reset_control_assert(dsi->reset); |
---|
1104 | | - |
---|
1105 | | - return 0; |
---|
1106 | | -} |
---|
1107 | | - |
---|
1108 | | -static const struct dev_pm_ops sun6i_dsi_pm_ops = { |
---|
1109 | | - SET_RUNTIME_PM_OPS(sun6i_dsi_runtime_suspend, |
---|
1110 | | - sun6i_dsi_runtime_resume, |
---|
1111 | | - NULL) |
---|
1112 | | -}; |
---|
1113 | 1219 | |
---|
1114 | 1220 | static const struct of_device_id sun6i_dsi_of_table[] = { |
---|
1115 | 1221 | { .compatible = "allwinner,sun6i-a31-mipi-dsi" }, |
---|
| 1222 | + { .compatible = "allwinner,sun50i-a64-mipi-dsi" }, |
---|
1116 | 1223 | { } |
---|
1117 | 1224 | }; |
---|
1118 | 1225 | MODULE_DEVICE_TABLE(of, sun6i_dsi_of_table); |
---|
.. | .. |
---|
1123 | 1230 | .driver = { |
---|
1124 | 1231 | .name = "sun6i-mipi-dsi", |
---|
1125 | 1232 | .of_match_table = sun6i_dsi_of_table, |
---|
1126 | | - .pm = &sun6i_dsi_pm_ops, |
---|
1127 | 1233 | }, |
---|
1128 | 1234 | }; |
---|
1129 | 1235 | module_platform_driver(sun6i_dsi_platform_driver); |
---|