Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors

This patch adds ARGB hardware cursor support to the DRM driver for the
Marvell Armada SoCs. ARGB cursors are supported at either 32x64 or
64x32 resolutions.

Tested-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Reviewed-by: Rob Clark <robdclark@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

+256 -6
+1
drivers/gpu/drm/armada/armada_510.c
··· 80 80 81 81 const struct armada_variant armada510_ops = { 82 82 .has_spu_adv_reg = true, 83 + .spu_adv_reg = ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND, 83 84 .init = armada510_init, 84 85 .crtc_init = armada510_crtc_init, 85 86 .crtc_compute_clock = armada510_crtc_compute_clock,
+241 -4
drivers/gpu/drm/armada/armada_crtc.c
··· 381 381 val = readl_relaxed(base + LCD_SPU_ADV_REG); 382 382 val &= ~(ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | ADV_VSYNCOFFEN); 383 383 val |= dcrtc->v[i].spu_adv_reg; 384 - writel_relaxed(val, dcrtc->base + LCD_SPU_ADV_REG); 384 + writel_relaxed(val, base + LCD_SPU_ADV_REG); 385 385 } 386 + 387 + if (stat & DUMB_FRAMEDONE && dcrtc->cursor_update) { 388 + writel_relaxed(dcrtc->cursor_hw_pos, 389 + base + LCD_SPU_HWC_OVSA_HPXL_VLN); 390 + writel_relaxed(dcrtc->cursor_hw_sz, 391 + base + LCD_SPU_HWC_HPXL_VLN); 392 + armada_updatel(CFG_HWC_ENA, 393 + CFG_HWC_ENA | CFG_HWC_1BITMOD | CFG_HWC_1BITENA, 394 + base + LCD_SPU_DMA_CTRL0); 395 + dcrtc->cursor_update = false; 396 + armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA); 397 + } 398 + 386 399 spin_unlock(&dcrtc->irq_lock); 387 400 388 401 if (stat & GRA_FRAME_IRQ) { ··· 535 522 adj->crtc_htotal; 536 523 dcrtc->v[1].spu_v_porch = tm << 16 | bm; 537 524 val = adj->crtc_hsync_start; 538 - dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN; 525 + dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN | 526 + priv->variant->spu_adv_reg; 539 527 540 528 if (interlaced) { 541 529 /* Odd interlaced frame */ ··· 544 530 (1 << 16); 545 531 dcrtc->v[0].spu_v_porch = dcrtc->v[1].spu_v_porch + 1; 546 532 val = adj->crtc_hsync_start - adj->crtc_htotal / 2; 547 - dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN; 533 + dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN | 534 + priv->variant->spu_adv_reg; 548 535 } else { 549 536 dcrtc->v[0] = dcrtc->v[1]; 550 537 } ··· 560 545 armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total, 561 546 LCD_SPUT_V_H_TOTAL); 562 547 563 - if (priv->variant->has_spu_adv_reg) 548 + if (priv->variant->has_spu_adv_reg) { 564 549 armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg, 565 550 ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | 566 551 ADV_VSYNCOFFEN, LCD_SPU_ADV_REG); 552 + } 567 553 568 554 val = CFG_GRA_ENA | CFG_GRA_HSMOOTH; 569 555 val |= CFG_GRA_FMT(drm_fb_to_armada_fb(dcrtc->crtc.fb)->fmt); ··· 656 640 .disable = armada_drm_crtc_disable, 657 641 }; 658 642 643 + static void armada_load_cursor_argb(void __iomem *base, uint32_t *pix, 644 + unsigned stride, unsigned width, unsigned height) 645 + { 646 + uint32_t addr; 647 + unsigned y; 648 + 649 + addr = SRAM_HWC32_RAM1; 650 + for (y = 0; y < height; y++) { 651 + uint32_t *p = &pix[y * stride]; 652 + unsigned x; 653 + 654 + for (x = 0; x < width; x++, p++) { 655 + uint32_t val = *p; 656 + 657 + val = (val & 0xff00ff00) | 658 + (val & 0x000000ff) << 16 | 659 + (val & 0x00ff0000) >> 16; 660 + 661 + writel_relaxed(val, 662 + base + LCD_SPU_SRAM_WRDAT); 663 + writel_relaxed(addr | SRAM_WRITE, 664 + base + LCD_SPU_SRAM_CTRL); 665 + addr += 1; 666 + if ((addr & 0x00ff) == 0) 667 + addr += 0xf00; 668 + if ((addr & 0x30ff) == 0) 669 + addr = SRAM_HWC32_RAM2; 670 + } 671 + } 672 + } 673 + 674 + static void armada_drm_crtc_cursor_tran(void __iomem *base) 675 + { 676 + unsigned addr; 677 + 678 + for (addr = 0; addr < 256; addr++) { 679 + /* write the default value */ 680 + writel_relaxed(0x55555555, base + LCD_SPU_SRAM_WRDAT); 681 + writel_relaxed(addr | SRAM_WRITE | SRAM_HWC32_TRAN, 682 + base + LCD_SPU_SRAM_CTRL); 683 + } 684 + } 685 + 686 + static int armada_drm_crtc_cursor_update(struct armada_crtc *dcrtc, bool reload) 687 + { 688 + uint32_t xoff, xscr, w = dcrtc->cursor_w, s; 689 + uint32_t yoff, yscr, h = dcrtc->cursor_h; 690 + uint32_t para1; 691 + 692 + /* 693 + * Calculate the visible width and height of the cursor, 694 + * screen position, and the position in the cursor bitmap. 695 + */ 696 + if (dcrtc->cursor_x < 0) { 697 + xoff = -dcrtc->cursor_x; 698 + xscr = 0; 699 + w -= min(xoff, w); 700 + } else if (dcrtc->cursor_x + w > dcrtc->crtc.mode.hdisplay) { 701 + xoff = 0; 702 + xscr = dcrtc->cursor_x; 703 + w = max_t(int, dcrtc->crtc.mode.hdisplay - dcrtc->cursor_x, 0); 704 + } else { 705 + xoff = 0; 706 + xscr = dcrtc->cursor_x; 707 + } 708 + 709 + if (dcrtc->cursor_y < 0) { 710 + yoff = -dcrtc->cursor_y; 711 + yscr = 0; 712 + h -= min(yoff, h); 713 + } else if (dcrtc->cursor_y + h > dcrtc->crtc.mode.vdisplay) { 714 + yoff = 0; 715 + yscr = dcrtc->cursor_y; 716 + h = max_t(int, dcrtc->crtc.mode.vdisplay - dcrtc->cursor_y, 0); 717 + } else { 718 + yoff = 0; 719 + yscr = dcrtc->cursor_y; 720 + } 721 + 722 + /* On interlaced modes, the vertical cursor size must be halved */ 723 + s = dcrtc->cursor_w; 724 + if (dcrtc->interlaced) { 725 + s *= 2; 726 + yscr /= 2; 727 + h /= 2; 728 + } 729 + 730 + if (!dcrtc->cursor_obj || !h || !w) { 731 + spin_lock_irq(&dcrtc->irq_lock); 732 + armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA); 733 + dcrtc->cursor_update = false; 734 + armada_updatel(0, CFG_HWC_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0); 735 + spin_unlock_irq(&dcrtc->irq_lock); 736 + return 0; 737 + } 738 + 739 + para1 = readl_relaxed(dcrtc->base + LCD_SPU_SRAM_PARA1); 740 + armada_updatel(CFG_CSB_256x32, CFG_CSB_256x32 | CFG_PDWN256x32, 741 + dcrtc->base + LCD_SPU_SRAM_PARA1); 742 + 743 + /* 744 + * Initialize the transparency if the SRAM was powered down. 745 + * We must also reload the cursor data as well. 746 + */ 747 + if (!(para1 & CFG_CSB_256x32)) { 748 + armada_drm_crtc_cursor_tran(dcrtc->base); 749 + reload = true; 750 + } 751 + 752 + if (dcrtc->cursor_hw_sz != (h << 16 | w)) { 753 + spin_lock_irq(&dcrtc->irq_lock); 754 + armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA); 755 + dcrtc->cursor_update = false; 756 + armada_updatel(0, CFG_HWC_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0); 757 + spin_unlock_irq(&dcrtc->irq_lock); 758 + reload = true; 759 + } 760 + if (reload) { 761 + struct armada_gem_object *obj = dcrtc->cursor_obj; 762 + uint32_t *pix; 763 + /* Set the top-left corner of the cursor image */ 764 + pix = obj->addr; 765 + pix += yoff * s + xoff; 766 + armada_load_cursor_argb(dcrtc->base, pix, s, w, h); 767 + } 768 + 769 + /* Reload the cursor position, size and enable in the IRQ handler */ 770 + spin_lock_irq(&dcrtc->irq_lock); 771 + dcrtc->cursor_hw_pos = yscr << 16 | xscr; 772 + dcrtc->cursor_hw_sz = h << 16 | w; 773 + dcrtc->cursor_update = true; 774 + armada_drm_crtc_enable_irq(dcrtc, DUMB_FRAMEDONE_ENA); 775 + spin_unlock_irq(&dcrtc->irq_lock); 776 + 777 + return 0; 778 + } 779 + 780 + static void cursor_update(void *data) 781 + { 782 + armada_drm_crtc_cursor_update(data, true); 783 + } 784 + 785 + static int armada_drm_crtc_cursor_set(struct drm_crtc *crtc, 786 + struct drm_file *file, uint32_t handle, uint32_t w, uint32_t h) 787 + { 788 + struct drm_device *dev = crtc->dev; 789 + struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); 790 + struct armada_private *priv = crtc->dev->dev_private; 791 + struct armada_gem_object *obj = NULL; 792 + int ret; 793 + 794 + /* If no cursor support, replicate drm's return value */ 795 + if (!priv->variant->has_spu_adv_reg) 796 + return -ENXIO; 797 + 798 + if (handle && w > 0 && h > 0) { 799 + /* maximum size is 64x32 or 32x64 */ 800 + if (w > 64 || h > 64 || (w > 32 && h > 32)) 801 + return -ENOMEM; 802 + 803 + obj = armada_gem_object_lookup(dev, file, handle); 804 + if (!obj) 805 + return -ENOENT; 806 + 807 + /* Must be a kernel-mapped object */ 808 + if (!obj->addr) { 809 + drm_gem_object_unreference_unlocked(&obj->obj); 810 + return -EINVAL; 811 + } 812 + 813 + if (obj->obj.size < w * h * 4) { 814 + DRM_ERROR("buffer is too small\n"); 815 + drm_gem_object_unreference_unlocked(&obj->obj); 816 + return -ENOMEM; 817 + } 818 + } 819 + 820 + mutex_lock(&dev->struct_mutex); 821 + if (dcrtc->cursor_obj) { 822 + dcrtc->cursor_obj->update = NULL; 823 + dcrtc->cursor_obj->update_data = NULL; 824 + drm_gem_object_unreference(&dcrtc->cursor_obj->obj); 825 + } 826 + dcrtc->cursor_obj = obj; 827 + dcrtc->cursor_w = w; 828 + dcrtc->cursor_h = h; 829 + ret = armada_drm_crtc_cursor_update(dcrtc, true); 830 + if (obj) { 831 + obj->update_data = dcrtc; 832 + obj->update = cursor_update; 833 + } 834 + mutex_unlock(&dev->struct_mutex); 835 + 836 + return ret; 837 + } 838 + 839 + static int armada_drm_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) 840 + { 841 + struct drm_device *dev = crtc->dev; 842 + struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); 843 + struct armada_private *priv = crtc->dev->dev_private; 844 + int ret; 845 + 846 + /* If no cursor support, replicate drm's return value */ 847 + if (!priv->variant->has_spu_adv_reg) 848 + return -EFAULT; 849 + 850 + mutex_lock(&dev->struct_mutex); 851 + dcrtc->cursor_x = x; 852 + dcrtc->cursor_y = y; 853 + ret = armada_drm_crtc_cursor_update(dcrtc, false); 854 + mutex_unlock(&dev->struct_mutex); 855 + 856 + return ret; 857 + } 858 + 659 859 static void armada_drm_crtc_destroy(struct drm_crtc *crtc) 660 860 { 661 861 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); 662 862 struct armada_private *priv = crtc->dev->dev_private; 863 + 864 + if (dcrtc->cursor_obj) 865 + drm_gem_object_unreference(&dcrtc->cursor_obj->obj); 663 866 664 867 priv->dcrtc[dcrtc->num] = NULL; 665 868 drm_crtc_cleanup(&dcrtc->crtc); ··· 985 750 } 986 751 987 752 static struct drm_crtc_funcs armada_crtc_funcs = { 753 + .cursor_set = armada_drm_crtc_cursor_set, 754 + .cursor_move = armada_drm_crtc_cursor_move, 988 755 .destroy = armada_drm_crtc_destroy, 989 756 .set_config = drm_crtc_helper_set_config, 990 757 .page_flip = armada_drm_crtc_page_flip,
+9
drivers/gpu/drm/armada/armada_crtc.h
··· 44 44 uint32_t spu_adv_reg; 45 45 } v[2]; 46 46 bool interlaced; 47 + bool cursor_update; 47 48 uint8_t csc_yuv_mode; 48 49 uint8_t csc_rgb_mode; 49 50 50 51 struct drm_plane *plane; 52 + 53 + struct armada_gem_object *cursor_obj; 54 + int cursor_x; 55 + int cursor_y; 56 + uint32_t cursor_hw_pos; 57 + uint32_t cursor_hw_sz; 58 + uint32_t cursor_w; 59 + uint32_t cursor_h; 51 60 52 61 int dpms; 53 62 uint32_t cfg_dumb_ctrl;
+1
drivers/gpu/drm/armada/armada_drm.h
··· 60 60 61 61 struct armada_variant { 62 62 bool has_spu_adv_reg; 63 + uint32_t spu_adv_reg; 63 64 int (*init)(struct armada_private *, struct device *); 64 65 int (*crtc_init)(struct armada_crtc *); 65 66 int (*crtc_compute_clock)(struct armada_crtc *,
+4 -2
drivers/gpu/drm/armada/armada_hw.h
··· 168 168 SRAM_READ = 0 << 14, 169 169 SRAM_WRITE = 2 << 14, 170 170 SRAM_INIT = 3 << 14, 171 - SRAM_HWC32_RAMR = 0xc << 8, 172 - SRAM_HWC32_RAMG = 0xd << 8, 171 + SRAM_HWC32_RAM1 = 0xc << 8, 172 + SRAM_HWC32_RAM2 = 0xd << 8, 173 + SRAM_HWC32_RAMR = SRAM_HWC32_RAM1, 174 + SRAM_HWC32_RAMG = SRAM_HWC32_RAM2, 173 175 SRAM_HWC32_RAMB = 0xe << 8, 174 176 SRAM_HWC32_TRAN = 0xf << 8, 175 177 SRAM_HWC = 0xf << 8,