| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2014 Free Electrons |
|---|
| 3 | 4 | * Copyright (C) 2014 Atmel |
|---|
| 4 | 5 | * |
|---|
| 5 | 6 | * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 8 | | - * under the terms of the GNU General Public License version 2 as published by |
|---|
| 9 | | - * the Free Software Foundation. |
|---|
| 10 | | - * |
|---|
| 11 | | - * This program is distributed in the hope that it will be useful, but WITHOUT |
|---|
| 12 | | - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|---|
| 13 | | - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
|---|
| 14 | | - * more details. |
|---|
| 15 | | - * |
|---|
| 16 | | - * You should have received a copy of the GNU General Public License along with |
|---|
| 17 | | - * this program. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 18 | 7 | */ |
|---|
| 8 | + |
|---|
| 9 | +#include <linux/dmapool.h> |
|---|
| 10 | +#include <linux/mfd/atmel-hlcdc.h> |
|---|
| 11 | + |
|---|
| 12 | +#include <drm/drm_atomic.h> |
|---|
| 13 | +#include <drm/drm_atomic_helper.h> |
|---|
| 14 | +#include <drm/drm_fb_cma_helper.h> |
|---|
| 15 | +#include <drm/drm_fourcc.h> |
|---|
| 16 | +#include <drm/drm_gem_cma_helper.h> |
|---|
| 17 | +#include <drm/drm_plane_helper.h> |
|---|
| 19 | 18 | |
|---|
| 20 | 19 | #include "atmel_hlcdc_dc.h" |
|---|
| 21 | 20 | |
|---|
| .. | .. |
|---|
| 372 | 371 | atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_HLCDC_LAYER_DMA_CFG, |
|---|
| 373 | 372 | cfg); |
|---|
| 374 | 373 | |
|---|
| 375 | | - cfg = ATMEL_HLCDC_LAYER_DMA; |
|---|
| 374 | + cfg = ATMEL_HLCDC_LAYER_DMA | ATMEL_HLCDC_LAYER_REP; |
|---|
| 376 | 375 | |
|---|
| 377 | 376 | if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) { |
|---|
| 378 | 377 | cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL | |
|---|
| .. | .. |
|---|
| 549 | 548 | |
|---|
| 550 | 549 | ovl_state = drm_plane_state_to_atmel_hlcdc_plane_state(ovl_s); |
|---|
| 551 | 550 | |
|---|
| 552 | | - if (!ovl_s->fb || |
|---|
| 551 | + if (!ovl_s->visible || |
|---|
| 552 | + !ovl_s->fb || |
|---|
| 553 | 553 | ovl_s->fb->format->has_alpha || |
|---|
| 554 | 554 | ovl_s->alpha != DRM_BLEND_ALPHA_OPAQUE) |
|---|
| 555 | 555 | continue; |
|---|
| .. | .. |
|---|
| 601 | 601 | struct drm_framebuffer *fb = state->base.fb; |
|---|
| 602 | 602 | const struct drm_display_mode *mode; |
|---|
| 603 | 603 | struct drm_crtc_state *crtc_state; |
|---|
| 604 | | - unsigned int patched_crtc_w; |
|---|
| 605 | | - unsigned int patched_crtc_h; |
|---|
| 606 | | - unsigned int patched_src_w; |
|---|
| 607 | | - unsigned int patched_src_h; |
|---|
| 608 | | - unsigned int tmp; |
|---|
| 609 | | - int x_offset = 0; |
|---|
| 610 | | - int y_offset = 0; |
|---|
| 611 | | - int hsub = 1; |
|---|
| 612 | | - int vsub = 1; |
|---|
| 604 | + int ret; |
|---|
| 613 | 605 | int i; |
|---|
| 614 | 606 | |
|---|
| 615 | | - if (!state->base.crtc || !fb) |
|---|
| 607 | + if (!state->base.crtc || WARN_ON(!fb)) |
|---|
| 616 | 608 | return 0; |
|---|
| 617 | 609 | |
|---|
| 618 | 610 | crtc_state = drm_atomic_get_existing_crtc_state(s->state, s->crtc); |
|---|
| 619 | 611 | mode = &crtc_state->adjusted_mode; |
|---|
| 620 | 612 | |
|---|
| 621 | | - state->src_x = s->src_x; |
|---|
| 622 | | - state->src_y = s->src_y; |
|---|
| 623 | | - state->src_h = s->src_h; |
|---|
| 624 | | - state->src_w = s->src_w; |
|---|
| 625 | | - state->crtc_x = s->crtc_x; |
|---|
| 626 | | - state->crtc_y = s->crtc_y; |
|---|
| 627 | | - state->crtc_h = s->crtc_h; |
|---|
| 628 | | - state->crtc_w = s->crtc_w; |
|---|
| 613 | + ret = drm_atomic_helper_check_plane_state(s, crtc_state, |
|---|
| 614 | + (1 << 16) / 2048, |
|---|
| 615 | + INT_MAX, true, true); |
|---|
| 616 | + if (ret || !s->visible) |
|---|
| 617 | + return ret; |
|---|
| 618 | + |
|---|
| 619 | + state->src_x = s->src.x1; |
|---|
| 620 | + state->src_y = s->src.y1; |
|---|
| 621 | + state->src_w = drm_rect_width(&s->src); |
|---|
| 622 | + state->src_h = drm_rect_height(&s->src); |
|---|
| 623 | + state->crtc_x = s->dst.x1; |
|---|
| 624 | + state->crtc_y = s->dst.y1; |
|---|
| 625 | + state->crtc_w = drm_rect_width(&s->dst); |
|---|
| 626 | + state->crtc_h = drm_rect_height(&s->dst); |
|---|
| 627 | + |
|---|
| 629 | 628 | if ((state->src_x | state->src_y | state->src_w | state->src_h) & |
|---|
| 630 | 629 | SUBPIXEL_MASK) |
|---|
| 631 | 630 | return -EINVAL; |
|---|
| .. | .. |
|---|
| 639 | 638 | if (state->nplanes > ATMEL_HLCDC_LAYER_MAX_PLANES) |
|---|
| 640 | 639 | return -EINVAL; |
|---|
| 641 | 640 | |
|---|
| 642 | | - /* |
|---|
| 643 | | - * Swap width and size in case of 90 or 270 degrees rotation |
|---|
| 644 | | - */ |
|---|
| 645 | | - if (drm_rotation_90_or_270(state->base.rotation)) { |
|---|
| 646 | | - tmp = state->crtc_w; |
|---|
| 647 | | - state->crtc_w = state->crtc_h; |
|---|
| 648 | | - state->crtc_h = tmp; |
|---|
| 649 | | - tmp = state->src_w; |
|---|
| 650 | | - state->src_w = state->src_h; |
|---|
| 651 | | - state->src_h = tmp; |
|---|
| 652 | | - } |
|---|
| 653 | | - |
|---|
| 654 | | - if (state->crtc_x + state->crtc_w > mode->hdisplay) |
|---|
| 655 | | - patched_crtc_w = mode->hdisplay - state->crtc_x; |
|---|
| 656 | | - else |
|---|
| 657 | | - patched_crtc_w = state->crtc_w; |
|---|
| 658 | | - |
|---|
| 659 | | - if (state->crtc_x < 0) { |
|---|
| 660 | | - patched_crtc_w += state->crtc_x; |
|---|
| 661 | | - x_offset = -state->crtc_x; |
|---|
| 662 | | - state->crtc_x = 0; |
|---|
| 663 | | - } |
|---|
| 664 | | - |
|---|
| 665 | | - if (state->crtc_y + state->crtc_h > mode->vdisplay) |
|---|
| 666 | | - patched_crtc_h = mode->vdisplay - state->crtc_y; |
|---|
| 667 | | - else |
|---|
| 668 | | - patched_crtc_h = state->crtc_h; |
|---|
| 669 | | - |
|---|
| 670 | | - if (state->crtc_y < 0) { |
|---|
| 671 | | - patched_crtc_h += state->crtc_y; |
|---|
| 672 | | - y_offset = -state->crtc_y; |
|---|
| 673 | | - state->crtc_y = 0; |
|---|
| 674 | | - } |
|---|
| 675 | | - |
|---|
| 676 | | - patched_src_w = DIV_ROUND_CLOSEST(patched_crtc_w * state->src_w, |
|---|
| 677 | | - state->crtc_w); |
|---|
| 678 | | - patched_src_h = DIV_ROUND_CLOSEST(patched_crtc_h * state->src_h, |
|---|
| 679 | | - state->crtc_h); |
|---|
| 680 | | - |
|---|
| 681 | | - hsub = drm_format_horz_chroma_subsampling(fb->format->format); |
|---|
| 682 | | - vsub = drm_format_vert_chroma_subsampling(fb->format->format); |
|---|
| 683 | | - |
|---|
| 684 | 641 | for (i = 0; i < state->nplanes; i++) { |
|---|
| 685 | 642 | unsigned int offset = 0; |
|---|
| 686 | | - int xdiv = i ? hsub : 1; |
|---|
| 687 | | - int ydiv = i ? vsub : 1; |
|---|
| 643 | + int xdiv = i ? fb->format->hsub : 1; |
|---|
| 644 | + int ydiv = i ? fb->format->vsub : 1; |
|---|
| 688 | 645 | |
|---|
| 689 | 646 | state->bpp[i] = fb->format->cpp[i]; |
|---|
| 690 | 647 | if (!state->bpp[i]) |
|---|
| .. | .. |
|---|
| 692 | 649 | |
|---|
| 693 | 650 | switch (state->base.rotation & DRM_MODE_ROTATE_MASK) { |
|---|
| 694 | 651 | case DRM_MODE_ROTATE_90: |
|---|
| 695 | | - offset = ((y_offset + state->src_y + patched_src_w - 1) / |
|---|
| 696 | | - ydiv) * fb->pitches[i]; |
|---|
| 697 | | - offset += ((x_offset + state->src_x) / xdiv) * |
|---|
| 698 | | - state->bpp[i]; |
|---|
| 699 | | - state->xstride[i] = ((patched_src_w - 1) / ydiv) * |
|---|
| 700 | | - fb->pitches[i]; |
|---|
| 701 | | - state->pstride[i] = -fb->pitches[i] - state->bpp[i]; |
|---|
| 702 | | - break; |
|---|
| 703 | | - case DRM_MODE_ROTATE_180: |
|---|
| 704 | | - offset = ((y_offset + state->src_y + patched_src_h - 1) / |
|---|
| 705 | | - ydiv) * fb->pitches[i]; |
|---|
| 706 | | - offset += ((x_offset + state->src_x + patched_src_w - 1) / |
|---|
| 707 | | - xdiv) * state->bpp[i]; |
|---|
| 708 | | - state->xstride[i] = ((((patched_src_w - 1) / xdiv) - 1) * |
|---|
| 709 | | - state->bpp[i]) - fb->pitches[i]; |
|---|
| 710 | | - state->pstride[i] = -2 * state->bpp[i]; |
|---|
| 711 | | - break; |
|---|
| 712 | | - case DRM_MODE_ROTATE_270: |
|---|
| 713 | | - offset = ((y_offset + state->src_y) / ydiv) * |
|---|
| 652 | + offset = (state->src_y / ydiv) * |
|---|
| 714 | 653 | fb->pitches[i]; |
|---|
| 715 | | - offset += ((x_offset + state->src_x + patched_src_h - 1) / |
|---|
| 654 | + offset += ((state->src_x + state->src_w - 1) / |
|---|
| 716 | 655 | xdiv) * state->bpp[i]; |
|---|
| 717 | | - state->xstride[i] = -(((patched_src_w - 1) / ydiv) * |
|---|
| 656 | + state->xstride[i] = -(((state->src_h - 1) / ydiv) * |
|---|
| 718 | 657 | fb->pitches[i]) - |
|---|
| 719 | 658 | (2 * state->bpp[i]); |
|---|
| 720 | 659 | state->pstride[i] = fb->pitches[i] - state->bpp[i]; |
|---|
| 721 | 660 | break; |
|---|
| 661 | + case DRM_MODE_ROTATE_180: |
|---|
| 662 | + offset = ((state->src_y + state->src_h - 1) / |
|---|
| 663 | + ydiv) * fb->pitches[i]; |
|---|
| 664 | + offset += ((state->src_x + state->src_w - 1) / |
|---|
| 665 | + xdiv) * state->bpp[i]; |
|---|
| 666 | + state->xstride[i] = ((((state->src_w - 1) / xdiv) - 1) * |
|---|
| 667 | + state->bpp[i]) - fb->pitches[i]; |
|---|
| 668 | + state->pstride[i] = -2 * state->bpp[i]; |
|---|
| 669 | + break; |
|---|
| 670 | + case DRM_MODE_ROTATE_270: |
|---|
| 671 | + offset = ((state->src_y + state->src_h - 1) / |
|---|
| 672 | + ydiv) * fb->pitches[i]; |
|---|
| 673 | + offset += (state->src_x / xdiv) * state->bpp[i]; |
|---|
| 674 | + state->xstride[i] = ((state->src_h - 1) / ydiv) * |
|---|
| 675 | + fb->pitches[i]; |
|---|
| 676 | + state->pstride[i] = -fb->pitches[i] - state->bpp[i]; |
|---|
| 677 | + break; |
|---|
| 722 | 678 | case DRM_MODE_ROTATE_0: |
|---|
| 723 | 679 | default: |
|---|
| 724 | | - offset = ((y_offset + state->src_y) / ydiv) * |
|---|
| 725 | | - fb->pitches[i]; |
|---|
| 726 | | - offset += ((x_offset + state->src_x) / xdiv) * |
|---|
| 727 | | - state->bpp[i]; |
|---|
| 680 | + offset = (state->src_y / ydiv) * fb->pitches[i]; |
|---|
| 681 | + offset += (state->src_x / xdiv) * state->bpp[i]; |
|---|
| 728 | 682 | state->xstride[i] = fb->pitches[i] - |
|---|
| 729 | | - ((patched_src_w / xdiv) * |
|---|
| 683 | + ((state->src_w / xdiv) * |
|---|
| 730 | 684 | state->bpp[i]); |
|---|
| 731 | 685 | state->pstride[i] = 0; |
|---|
| 732 | 686 | break; |
|---|
| .. | .. |
|---|
| 735 | 689 | state->offsets[i] = offset + fb->offsets[i]; |
|---|
| 736 | 690 | } |
|---|
| 737 | 691 | |
|---|
| 738 | | - state->src_w = patched_src_w; |
|---|
| 739 | | - state->src_h = patched_src_h; |
|---|
| 740 | | - state->crtc_w = patched_crtc_w; |
|---|
| 741 | | - state->crtc_h = patched_crtc_h; |
|---|
| 692 | + /* |
|---|
| 693 | + * Swap width and size in case of 90 or 270 degrees rotation |
|---|
| 694 | + */ |
|---|
| 695 | + if (drm_rotation_90_or_270(state->base.rotation)) { |
|---|
| 696 | + swap(state->src_w, state->src_h); |
|---|
| 697 | + } |
|---|
| 742 | 698 | |
|---|
| 743 | 699 | if (!desc->layout.size && |
|---|
| 744 | 700 | (mode->hdisplay != state->crtc_w || |
|---|
| 745 | 701 | mode->vdisplay != state->crtc_h)) |
|---|
| 746 | | - return -EINVAL; |
|---|
| 747 | | - |
|---|
| 748 | | - if (desc->max_height && state->crtc_h > desc->max_height) |
|---|
| 749 | | - return -EINVAL; |
|---|
| 750 | | - |
|---|
| 751 | | - if (desc->max_width && state->crtc_w > desc->max_width) |
|---|
| 752 | 702 | return -EINVAL; |
|---|
| 753 | 703 | |
|---|
| 754 | 704 | if ((state->crtc_h != state->src_h || state->crtc_w != state->src_w) && |
|---|
| .. | .. |
|---|
| 756 | 706 | state->base.fb->format->has_alpha)) |
|---|
| 757 | 707 | return -EINVAL; |
|---|
| 758 | 708 | |
|---|
| 759 | | - if (state->crtc_x < 0 || state->crtc_y < 0) |
|---|
| 760 | | - return -EINVAL; |
|---|
| 761 | | - |
|---|
| 762 | | - if (state->crtc_w + state->crtc_x > mode->hdisplay || |
|---|
| 763 | | - state->crtc_h + state->crtc_y > mode->vdisplay) |
|---|
| 764 | | - return -EINVAL; |
|---|
| 765 | | - |
|---|
| 766 | 709 | return 0; |
|---|
| 710 | +} |
|---|
| 711 | + |
|---|
| 712 | +static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p, |
|---|
| 713 | + struct drm_plane_state *old_state) |
|---|
| 714 | +{ |
|---|
| 715 | + struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); |
|---|
| 716 | + |
|---|
| 717 | + /* Disable interrupts */ |
|---|
| 718 | + atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IDR, |
|---|
| 719 | + 0xffffffff); |
|---|
| 720 | + |
|---|
| 721 | + /* Disable the layer */ |
|---|
| 722 | + atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHDR, |
|---|
| 723 | + ATMEL_HLCDC_LAYER_RST | |
|---|
| 724 | + ATMEL_HLCDC_LAYER_A2Q | |
|---|
| 725 | + ATMEL_HLCDC_LAYER_UPDATE); |
|---|
| 726 | + |
|---|
| 727 | + /* Clear all pending interrupts */ |
|---|
| 728 | + atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR); |
|---|
| 767 | 729 | } |
|---|
| 768 | 730 | |
|---|
| 769 | 731 | static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p, |
|---|
| .. | .. |
|---|
| 776 | 738 | |
|---|
| 777 | 739 | if (!p->state->crtc || !p->state->fb) |
|---|
| 778 | 740 | return; |
|---|
| 741 | + |
|---|
| 742 | + if (!state->base.visible) { |
|---|
| 743 | + atmel_hlcdc_plane_atomic_disable(p, old_s); |
|---|
| 744 | + return; |
|---|
| 745 | + } |
|---|
| 779 | 746 | |
|---|
| 780 | 747 | atmel_hlcdc_plane_update_pos_and_size(plane, state); |
|---|
| 781 | 748 | atmel_hlcdc_plane_update_general_settings(plane, state); |
|---|
| .. | .. |
|---|
| 796 | 763 | ATMEL_HLCDC_LAYER_UPDATE | |
|---|
| 797 | 764 | (sr & ATMEL_HLCDC_LAYER_EN ? |
|---|
| 798 | 765 | ATMEL_HLCDC_LAYER_A2Q : ATMEL_HLCDC_LAYER_EN)); |
|---|
| 799 | | -} |
|---|
| 800 | | - |
|---|
| 801 | | -static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p, |
|---|
| 802 | | - struct drm_plane_state *old_state) |
|---|
| 803 | | -{ |
|---|
| 804 | | - struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); |
|---|
| 805 | | - |
|---|
| 806 | | - /* Disable interrupts */ |
|---|
| 807 | | - atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IDR, |
|---|
| 808 | | - 0xffffffff); |
|---|
| 809 | | - |
|---|
| 810 | | - /* Disable the layer */ |
|---|
| 811 | | - atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHDR, |
|---|
| 812 | | - ATMEL_HLCDC_LAYER_RST | |
|---|
| 813 | | - ATMEL_HLCDC_LAYER_A2Q | |
|---|
| 814 | | - ATMEL_HLCDC_LAYER_UPDATE); |
|---|
| 815 | | - |
|---|
| 816 | | - /* Clear all pending interrupts */ |
|---|
| 817 | | - atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR); |
|---|
| 818 | 766 | } |
|---|
| 819 | 767 | |
|---|
| 820 | 768 | static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane) |
|---|
| .. | .. |
|---|
| 942 | 890 | "Failed to allocate initial plane state\n"); |
|---|
| 943 | 891 | return; |
|---|
| 944 | 892 | } |
|---|
| 945 | | - |
|---|
| 946 | | - p->state = &state->base; |
|---|
| 947 | | - p->state->alpha = DRM_BLEND_ALPHA_OPAQUE; |
|---|
| 948 | | - p->state->plane = p; |
|---|
| 893 | + __drm_atomic_helper_plane_reset(p, &state->base); |
|---|
| 949 | 894 | } |
|---|
| 950 | 895 | } |
|---|
| 951 | 896 | |
|---|