diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-08-30 23:34:34 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-08-30 23:34:34 +0300 |
commit | 461f35f014466c4e26dca6be0f431f57297df3f2 (patch) | |
tree | 0bd2fded69ba0752ca16c304d3e1880d5f1eb30b /drivers/gpu/drm/loongson/lsdc_plane.c | |
parent | 53ea7f624fb91074c2f9458832ed74975ee5d64c (diff) | |
parent | 3698a75f5a98d0a6599e2878ab25d30a82dd836a (diff) | |
download | linux-461f35f014466c4e26dca6be0f431f57297df3f2.tar.xz |
Merge tag 'drm-next-2023-08-30' of git://anongit.freedesktop.org/drm/drm
Pull drm updates from Dave Airlie:
"The drm core grew a new generic gpu virtual address manager, and new
execution locking helpers. These are used by nouveau now to provide
uAPI support for the userspace Vulkan driver. AMD had a bunch of new
IP core support, loads of refactoring around fbdev, but mostly just
the usual amount of stuff across the board.
core:
- fix gfp flags in drmm_kmalloc
gpuva:
- add new generic GPU VA manager (for nouveau initially)
syncobj:
- add new DRM_IOCTL_SYNCOBJ_EVENTFD ioctl
dma-buf:
- acquire resv lock for mmap() in exporters
- support dma-buf self import automatically
- docs fixes
backlight:
- fix fbdev interactions
atomic:
- improve logging
prime:
- remove struct gem_prim_mmap plus driver updates
gem:
- drm_exec: add locking over multiple GEM objects
- fix lockdep checking
fbdev:
- make fbdev userspace interfaces optional
- use linux device instead of fbdev device
- use deferred i/o helper macros in various drivers
- Make FB core selectable without drivers
- Remove obsolete flags FBINFO_DEFAULT and FBINFO_FLAG_DEFAULT
- Add helper macros and Kconfig tokens for DMA-allocated framebuffer
ttm:
- support init_on_free
- swapout fixes
panel:
- panel-edp: Support AUO B116XAB01.4
- Support Visionox R66451 plus DT bindings
- ld9040:
- Backlight support
- magic improved
- Kconfig fix
- Convert to of_device_get_match_data()
- Fix Kconfig dependencies
- simple:
- Set bpc value to fix warning
- Set connector type for AUO T215HVN01
- Support Innolux G156HCE-L01 plus DT bindings
- ili9881: Support TDO TL050HDV35 LCD panel plus DT bindings
- startek: Support KD070FHFID015 MIPI-DSI panel plus DT bindings
- sitronix-st7789v:
- Support Inanbo T28CP45TN89 plus DT bindings
- Support EDT ET028013DMA plus DT bindings
- Various cleanups
- edp: Add timings for N140HCA-EAC
- Allow panels and touchscreens to power sequence together
- Fix Innolux G156HCE-L01 LVDS clock
bridge:
- debugfs for chains support
- dw-hdmi:
- Improve support for YUV420 bus format
- CEC suspend/resume
- update EDID on HDMI detect
- dw-mipi-dsi: Fix enable/disable of DSI controller
- lt9611uxc: Use MODULE_FIRMWARE()
- ps8640: Remove broken EDID code
- samsung-dsim: Fix command transfer
- tc358764:
- Handle HS/VS polarity
- Use BIT() macro
- Various cleanups
- adv7511: Fix low refresh rate
- anx7625:
- Switch to macros instead of hardcoded values
- locking fixes
- tc358767: fix hardware delays
- sitronix-st7789v:
- Support panel orientation
- Support rotation property
- Add support for Jasonic JT240MHQS-HWT-EK-E3 plus DT bindings
amdgpu:
- SDMA 6.1.0 support
- HDP 6.1 support
- SMUIO 14.0 support
- PSP 14.0 support
- IH 6.1 support
- Lots of checkpatch cleanups
- GFX 9.4.3 updates
- Add USB PD and IFWI flashing documentation
- GPUVM updates
- RAS fixes
- DRR fixes
- FAMS fixes
- Virtual display fixes
- Soft IH fixes
- SMU13 fixes
- Rework PSP firmware loading for other IPs
- Kernel doc fixes
- DCN 3.0.1 fixes
- LTTPR fixes
- DP MST fixes
- DCN 3.1.6 fixes
- SMU 13.x fixes
- PSP 13.x fixes
- SubVP fixes
- GC 9.4.3 fixes
- Display bandwidth calculation fixes
- VCN4 secure submission fixes
- Allow building DC on RISC-V
- Add visible FB info to bo_print_info
- HBR3 fixes
- GFX9 MCBP fix
- GMC10 vmhub index fix
- GMC11 vmhub index fix
- Create a new doorbell manager
- SR-IOV fixes
- initial freesync panel replay support
- revert zpos properly until igt regression is fixeed
- use TTM to manage doorbell BAR
- Expose both current and average power via hwmon if supported
amdkfd:
- Cleanup CRIU dma-buf handling
- Use KIQ to unmap HIQ
- GFX 9.4.3 debugger updates
- GFX 9.4.2 debugger fixes
- Enable cooperative groups fof gfx11
- SVM fixes
- Convert older APUs to use dGPU path like newer APUs
- Drop IOMMUv2 path as it is no longer used
- TBA fix for aldebaran
i915:
- ICL+ DSI modeset sequence
- HDCP improvements
- MTL display fixes and cleanups
- HSW/BDW PSR1 restored
- Init DDI ports in VBT order
- General display refactors
- Start using plane scale factor for relative data rate
- Use shmem for dpt objects
- Expose RPS thresholds in sysfs
- Apply GuC SLPC min frequency softlimit correctly
- Extend Wa_14015795083 to TGL, RKL, DG1 and ADL
- Fix a VMA UAF for multi-gt platform
- Do not use stolen on MTL due to HW bug
- Check HuC and GuC version compatibility on MTL
- avoid infinite GPU waits due to premature release of request memory
- Fixes and updates for GSC memory allocation
- Display SDVO fixes
- Take stolen handling out of FBC code
- Make i915_coherent_map_type GT-centric
- Simplify shmem_create_from_object map_type
msm:
- SM6125 MDSS support
- DPU: SM6125 DPU support
- DSI: runtime PM support, burst mode support
- DSI PHY: SM6125 support in 14nm DSI PHY driver
- GPU: prepare for a7xx
- fix a690 firmware
- disable relocs on a6xx and newer
radeon:
- Lots of checkpatch cleanups
ast:
- improve device-model detection
- Represent BMV as virtual connector
- Report DP connection status
nouveau:
- add new exec/bind interface to support Vulkan
- document some getparam ioctls
- improve VRAM detection
- various fixes/cleanups
- workraound DPCD issues
ivpu:
- MMU updates
- debugfs support
- Support vpu4
virtio:
- add sync object support
atmel-hlcdc:
- Support inverted pixclock polarity
etnaviv:
- runtime PM cleanups
- hang handling fixes
exynos:
- use fbdev DMA helpers
- fix possible NULL ptr dereference
komeda:
- always attach encoder
omapdrm:
- use fbdev DMA helpers
ingenic:
- kconfig regmap fixes
loongson:
- support display controller
mediatek:
- Small mtk-dpi cleanups
- DisplayPort: support eDP and aux-bus
- Fix coverity issues
- Fix potential memory leak if vmap() fail
mgag200:
- minor fixes
mxsfb:
- support disabling overlay planes
panfrost:
- fix sync in IRQ handling
ssd130x:
- Support per-controller default resolution plus DT bindings
- Reduce memory-allocation overhead
- Improve intermediate buffer size computation
- Fix allocation of temporary buffers
- Fix pitch computation
- Fix shadow plane allocation
tegra:
- use fbdev DMA helpers
- Convert to devm_platform_ioremap_resource()
- support bridge/connector
- enable PM
tidss:
- Support TI AM625 plus DT bindings
- Implement new connector model plus driver updates
vkms:
- improve write back support
- docs fixes
- support gamma LUT
zynqmp-dpsub:
- misc fixes"
* tag 'drm-next-2023-08-30' of git://anongit.freedesktop.org/drm/drm: (1327 commits)
drm/gpuva_mgr: remove unused prev pointer in __drm_gpuva_sm_map()
drm/tests/drm_kunit_helpers: Place correct function name in the comment header
drm/nouveau: uapi: don't pass NO_PREFETCH flag implicitly
drm/nouveau: uvmm: fix unset region pointer on remap
drm/nouveau: sched: avoid job races between entities
drm/i915: Fix HPD polling, reenabling the output poll work as needed
drm: Add an HPD poll helper to reschedule the poll work
drm/i915: Fix TLB-Invalidation seqno store
drm/ttm/tests: Fix type conversion in ttm_pool_test
drm/msm/a6xx: Bail out early if setting GPU OOB fails
drm/msm/a6xx: Move LLC accessors to the common header
drm/msm/a6xx: Introduce a6xx_llc_read
drm/ttm/tests: Require MMU when testing
drm/panel: simple: Fix Innolux G156HCE-L01 LVDS clock
Revert "Revert "drm/amdgpu/display: change pipe policy for DCN 2.0""
drm/amdgpu: Add memory vendor information
drm/amd: flush any delayed gfxoff on suspend entry
drm/amdgpu: skip fence GFX interrupts disable/enable for S0ix
drm/amdgpu: Remove gfxoff check in GFX v9.4.3
drm/amd/pm: Update pci link speed for smu v13.0.6
...
Diffstat (limited to 'drivers/gpu/drm/loongson/lsdc_plane.c')
-rw-r--r-- | drivers/gpu/drm/loongson/lsdc_plane.c | 793 |
1 files changed, 793 insertions, 0 deletions
diff --git a/drivers/gpu/drm/loongson/lsdc_plane.c b/drivers/gpu/drm/loongson/lsdc_plane.c new file mode 100644 index 000000000000..0d5094633222 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_plane.c @@ -0,0 +1,793 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include <linux/delay.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_gem_atomic_helper.h> +#include <drm/drm_plane_helper.h> + +#include "lsdc_drv.h" +#include "lsdc_regs.h" +#include "lsdc_ttm.h" + +static const u32 lsdc_primary_formats[] = { + DRM_FORMAT_XRGB8888, +}; + +static const u32 lsdc_cursor_formats[] = { + DRM_FORMAT_ARGB8888, +}; + +static const u64 lsdc_fb_format_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +static unsigned int lsdc_get_fb_offset(struct drm_framebuffer *fb, + struct drm_plane_state *state) +{ + unsigned int offset = fb->offsets[0]; + + offset += fb->format->cpp[0] * (state->src_x >> 16); + offset += fb->pitches[0] * (state->src_y >> 16); + + return offset; +} + +static u64 lsdc_fb_base_addr(struct drm_framebuffer *fb) +{ + struct lsdc_device *ldev = to_lsdc(fb->dev); + struct lsdc_bo *lbo = gem_to_lsdc_bo(fb->obj[0]); + + return lsdc_bo_gpu_offset(lbo) + ldev->vram_base; +} + +static int lsdc_primary_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_crtc *crtc = new_plane_state->crtc; + struct drm_crtc_state *new_crtc_state; + + if (!crtc) + return 0; + + new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + + return drm_atomic_helper_check_plane_state(new_plane_state, + new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, true); +} + +static void lsdc_primary_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct lsdc_primary *primary = to_lsdc_primary(plane); + const struct lsdc_primary_plane_ops *ops = primary->ops; + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_framebuffer *new_fb = new_plane_state->fb; + struct drm_framebuffer *old_fb = old_plane_state->fb; + u64 fb_addr = lsdc_fb_base_addr(new_fb); + + fb_addr += lsdc_get_fb_offset(new_fb, new_plane_state); + + ops->update_fb_addr(primary, fb_addr); + ops->update_fb_stride(primary, new_fb->pitches[0]); + + if (!old_fb || old_fb->format != new_fb->format) + ops->update_fb_format(primary, new_fb->format); +} + +static void lsdc_primary_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + /* + * Do nothing, just prevent call into atomic_update(). + * Writing the format as LSDC_PF_NONE can disable the primary, + * But it seems not necessary... + */ + drm_dbg(plane->dev, "%s disabled\n", plane->name); +} + +static int lsdc_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + struct drm_framebuffer *fb = new_state->fb; + struct lsdc_bo *lbo; + u64 gpu_vaddr; + int ret; + + if (!fb) + return 0; + + lbo = gem_to_lsdc_bo(fb->obj[0]); + + ret = lsdc_bo_reserve(lbo); + if (unlikely(ret)) { + drm_err(plane->dev, "bo %p reserve failed\n", lbo); + return ret; + } + + ret = lsdc_bo_pin(lbo, LSDC_GEM_DOMAIN_VRAM, &gpu_vaddr); + + lsdc_bo_unreserve(lbo); + + if (unlikely(ret)) { + drm_err(plane->dev, "bo %p pin failed\n", lbo); + return ret; + } + + lsdc_bo_ref(lbo); + + if (plane->type != DRM_PLANE_TYPE_CURSOR) + drm_dbg(plane->dev, + "%s[%p] pin at 0x%llx, bo size: %zu\n", + plane->name, lbo, gpu_vaddr, lsdc_bo_size(lbo)); + + return drm_gem_plane_helper_prepare_fb(plane, new_state); +} + +static void lsdc_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_framebuffer *fb = old_state->fb; + struct lsdc_bo *lbo; + int ret; + + if (!fb) + return; + + lbo = gem_to_lsdc_bo(fb->obj[0]); + + ret = lsdc_bo_reserve(lbo); + if (unlikely(ret)) { + drm_err(plane->dev, "%p reserve failed\n", lbo); + return; + } + + lsdc_bo_unpin(lbo); + + lsdc_bo_unreserve(lbo); + + lsdc_bo_unref(lbo); + + if (plane->type != DRM_PLANE_TYPE_CURSOR) + drm_dbg(plane->dev, "%s unpin\n", plane->name); +} + +static const struct drm_plane_helper_funcs lsdc_primary_helper_funcs = { + .prepare_fb = lsdc_plane_prepare_fb, + .cleanup_fb = lsdc_plane_cleanup_fb, + .atomic_check = lsdc_primary_atomic_check, + .atomic_update = lsdc_primary_atomic_update, + .atomic_disable = lsdc_primary_atomic_disable, +}; + +static int lsdc_cursor_plane_atomic_async_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_state; + struct drm_crtc_state *crtc_state; + + new_state = drm_atomic_get_new_plane_state(state, plane); + + if (!plane->state || !plane->state->fb) { + drm_dbg(plane->dev, "%s: state is NULL\n", plane->name); + return -EINVAL; + } + + if (new_state->crtc_w != new_state->crtc_h) { + drm_dbg(plane->dev, "unsupported cursor size: %ux%u\n", + new_state->crtc_w, new_state->crtc_h); + return -EINVAL; + } + + if (new_state->crtc_w != 64 && new_state->crtc_w != 32) { + drm_dbg(plane->dev, "unsupported cursor size: %ux%u\n", + new_state->crtc_w, new_state->crtc_h); + return -EINVAL; + } + + crtc_state = drm_atomic_get_existing_crtc_state(state, new_state->crtc); + if (!crtc_state->active) + return -EINVAL; + + if (plane->state->crtc != new_state->crtc || + plane->state->src_w != new_state->src_w || + plane->state->src_h != new_state->src_h || + plane->state->crtc_w != new_state->crtc_w || + plane->state->crtc_h != new_state->crtc_h) + return -EINVAL; + + if (new_state->visible != plane->state->visible) + return -EINVAL; + + return drm_atomic_helper_check_plane_state(plane->state, + crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + true, true); +} + +static void lsdc_cursor_plane_atomic_async_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct lsdc_cursor *cursor = to_lsdc_cursor(plane); + const struct lsdc_cursor_plane_ops *ops = cursor->ops; + struct drm_framebuffer *old_fb = plane->state->fb; + struct drm_framebuffer *new_fb; + struct drm_plane_state *new_state; + + new_state = drm_atomic_get_new_plane_state(state, plane); + + new_fb = plane->state->fb; + + plane->state->crtc_x = new_state->crtc_x; + plane->state->crtc_y = new_state->crtc_y; + plane->state->crtc_h = new_state->crtc_h; + plane->state->crtc_w = new_state->crtc_w; + plane->state->src_x = new_state->src_x; + plane->state->src_y = new_state->src_y; + plane->state->src_h = new_state->src_h; + plane->state->src_w = new_state->src_w; + swap(plane->state->fb, new_state->fb); + + if (new_state->visible) { + enum lsdc_cursor_size cursor_size; + + switch (new_state->crtc_w) { + case 64: + cursor_size = CURSOR_SIZE_64X64; + break; + case 32: + cursor_size = CURSOR_SIZE_32X32; + break; + default: + cursor_size = CURSOR_SIZE_32X32; + break; + } + + ops->update_position(cursor, new_state->crtc_x, new_state->crtc_y); + + ops->update_cfg(cursor, cursor_size, CURSOR_FORMAT_ARGB8888); + + if (!old_fb || old_fb != new_fb) + ops->update_bo_addr(cursor, lsdc_fb_base_addr(new_fb)); + } +} + +/* ls7a1000 cursor plane helpers */ + +static int ls7a1000_cursor_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state; + struct drm_crtc_state *new_crtc_state; + struct drm_crtc *crtc; + + new_plane_state = drm_atomic_get_new_plane_state(state, plane); + + crtc = new_plane_state->crtc; + if (!crtc) { + drm_dbg(plane->dev, "%s is not bind to a crtc\n", plane->name); + return 0; + } + + if (new_plane_state->crtc_w != 32 || new_plane_state->crtc_h != 32) { + drm_dbg(plane->dev, "unsupported cursor size: %ux%u\n", + new_plane_state->crtc_w, new_plane_state->crtc_h); + return -EINVAL; + } + + new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + + return drm_atomic_helper_check_plane_state(new_plane_state, + new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + true, true); +} + +static void ls7a1000_cursor_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct lsdc_cursor *cursor = to_lsdc_cursor(plane); + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_framebuffer *new_fb = new_plane_state->fb; + struct drm_framebuffer *old_fb = old_plane_state->fb; + const struct lsdc_cursor_plane_ops *ops = cursor->ops; + u64 addr = lsdc_fb_base_addr(new_fb); + + if (!new_plane_state->visible) + return; + + ops->update_position(cursor, new_plane_state->crtc_x, new_plane_state->crtc_y); + + if (!old_fb || old_fb != new_fb) + ops->update_bo_addr(cursor, addr); + + ops->update_cfg(cursor, CURSOR_SIZE_32X32, CURSOR_FORMAT_ARGB8888); +} + +static void ls7a1000_cursor_plane_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct lsdc_cursor *cursor = to_lsdc_cursor(plane); + const struct lsdc_cursor_plane_ops *ops = cursor->ops; + + ops->update_cfg(cursor, CURSOR_SIZE_32X32, CURSOR_FORMAT_DISABLE); +} + +static const struct drm_plane_helper_funcs ls7a1000_cursor_plane_helper_funcs = { + .prepare_fb = lsdc_plane_prepare_fb, + .cleanup_fb = lsdc_plane_cleanup_fb, + .atomic_check = ls7a1000_cursor_plane_atomic_check, + .atomic_update = ls7a1000_cursor_plane_atomic_update, + .atomic_disable = ls7a1000_cursor_plane_atomic_disable, + .atomic_async_check = lsdc_cursor_plane_atomic_async_check, + .atomic_async_update = lsdc_cursor_plane_atomic_async_update, +}; + +/* ls7a2000 cursor plane helpers */ + +static int ls7a2000_cursor_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state; + struct drm_crtc_state *new_crtc_state; + struct drm_crtc *crtc; + + new_plane_state = drm_atomic_get_new_plane_state(state, plane); + + crtc = new_plane_state->crtc; + if (!crtc) { + drm_dbg(plane->dev, "%s is not bind to a crtc\n", plane->name); + return 0; + } + + if (new_plane_state->crtc_w != new_plane_state->crtc_h) { + drm_dbg(plane->dev, "unsupported cursor size: %ux%u\n", + new_plane_state->crtc_w, new_plane_state->crtc_h); + return -EINVAL; + } + + if (new_plane_state->crtc_w != 64 && new_plane_state->crtc_w != 32) { + drm_dbg(plane->dev, "unsupported cursor size: %ux%u\n", + new_plane_state->crtc_w, new_plane_state->crtc_h); + return -EINVAL; + } + + new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + + return drm_atomic_helper_check_plane_state(new_plane_state, + new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + true, true); +} + +/* Update the format, size and location of the cursor */ + +static void ls7a2000_cursor_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct lsdc_cursor *cursor = to_lsdc_cursor(plane); + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_framebuffer *new_fb = new_plane_state->fb; + struct drm_framebuffer *old_fb = old_plane_state->fb; + const struct lsdc_cursor_plane_ops *ops = cursor->ops; + enum lsdc_cursor_size cursor_size; + + if (!new_plane_state->visible) + return; + + ops->update_position(cursor, new_plane_state->crtc_x, new_plane_state->crtc_y); + + if (!old_fb || new_fb != old_fb) { + u64 addr = lsdc_fb_base_addr(new_fb); + + ops->update_bo_addr(cursor, addr); + } + + switch (new_plane_state->crtc_w) { + case 64: + cursor_size = CURSOR_SIZE_64X64; + break; + case 32: + cursor_size = CURSOR_SIZE_32X32; + break; + default: + cursor_size = CURSOR_SIZE_64X64; + break; + } + + ops->update_cfg(cursor, cursor_size, CURSOR_FORMAT_ARGB8888); +} + +static void ls7a2000_cursor_plane_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct lsdc_cursor *cursor = to_lsdc_cursor(plane); + const struct lsdc_cursor_plane_ops *hw_ops = cursor->ops; + + hw_ops->update_cfg(cursor, CURSOR_SIZE_64X64, CURSOR_FORMAT_DISABLE); +} + +static const struct drm_plane_helper_funcs ls7a2000_cursor_plane_helper_funcs = { + .prepare_fb = lsdc_plane_prepare_fb, + .cleanup_fb = lsdc_plane_cleanup_fb, + .atomic_check = ls7a2000_cursor_plane_atomic_check, + .atomic_update = ls7a2000_cursor_plane_atomic_update, + .atomic_disable = ls7a2000_cursor_plane_atomic_disable, + .atomic_async_check = lsdc_cursor_plane_atomic_async_check, + .atomic_async_update = lsdc_cursor_plane_atomic_async_update, +}; + +static void lsdc_plane_atomic_print_state(struct drm_printer *p, + const struct drm_plane_state *state) +{ + struct drm_framebuffer *fb = state->fb; + u64 addr; + + if (!fb) + return; + + addr = lsdc_fb_base_addr(fb); + + drm_printf(p, "\tdma addr=%llx\n", addr); +} + +static const struct drm_plane_funcs lsdc_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .atomic_print_state = lsdc_plane_atomic_print_state, +}; + +/* Primary plane 0 hardware related ops */ + +static void lsdc_primary0_update_fb_addr(struct lsdc_primary *primary, u64 addr) +{ + struct lsdc_device *ldev = primary->ldev; + u32 status; + u32 lo, hi; + + /* 40-bit width physical address bus */ + lo = addr & 0xFFFFFFFF; + hi = (addr >> 32) & 0xFF; + + status = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG); + if (status & FB_REG_IN_USING) { + lsdc_wreg32(ldev, LSDC_CRTC0_FB1_ADDR_LO_REG, lo); + lsdc_wreg32(ldev, LSDC_CRTC0_FB1_ADDR_HI_REG, hi); + } else { + lsdc_wreg32(ldev, LSDC_CRTC0_FB0_ADDR_LO_REG, lo); + lsdc_wreg32(ldev, LSDC_CRTC0_FB0_ADDR_HI_REG, hi); + } +} + +static void lsdc_primary0_update_fb_stride(struct lsdc_primary *primary, u32 stride) +{ + struct lsdc_device *ldev = primary->ldev; + + lsdc_wreg32(ldev, LSDC_CRTC0_STRIDE_REG, stride); +} + +static void lsdc_primary0_update_fb_format(struct lsdc_primary *primary, + const struct drm_format_info *format) +{ + struct lsdc_device *ldev = primary->ldev; + u32 status; + + status = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG); + + /* + * TODO: add RGB565 support, only support XRBG8888 at present + */ + status &= ~CFG_PIX_FMT_MASK; + status |= LSDC_PF_XRGB8888; + + lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, status); +} + +/* Primary plane 1 hardware related ops */ + +static void lsdc_primary1_update_fb_addr(struct lsdc_primary *primary, u64 addr) +{ + struct lsdc_device *ldev = primary->ldev; + u32 status; + u32 lo, hi; + + /* 40-bit width physical address bus */ + lo = addr & 0xFFFFFFFF; + hi = (addr >> 32) & 0xFF; + + status = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG); + if (status & FB_REG_IN_USING) { + lsdc_wreg32(ldev, LSDC_CRTC1_FB1_ADDR_LO_REG, lo); + lsdc_wreg32(ldev, LSDC_CRTC1_FB1_ADDR_HI_REG, hi); + } else { + lsdc_wreg32(ldev, LSDC_CRTC1_FB0_ADDR_LO_REG, lo); + lsdc_wreg32(ldev, LSDC_CRTC1_FB0_ADDR_HI_REG, hi); + } +} + +static void lsdc_primary1_update_fb_stride(struct lsdc_primary *primary, u32 stride) +{ + struct lsdc_device *ldev = primary->ldev; + + lsdc_wreg32(ldev, LSDC_CRTC1_STRIDE_REG, stride); +} + +static void lsdc_primary1_update_fb_format(struct lsdc_primary *primary, + const struct drm_format_info *format) +{ + struct lsdc_device *ldev = primary->ldev; + u32 status; + + status = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG); + + /* + * TODO: add RGB565 support, only support XRBG8888 at present + */ + status &= ~CFG_PIX_FMT_MASK; + status |= LSDC_PF_XRGB8888; + + lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, status); +} + +static const struct lsdc_primary_plane_ops lsdc_primary_plane_hw_ops[2] = { + { + .update_fb_addr = lsdc_primary0_update_fb_addr, + .update_fb_stride = lsdc_primary0_update_fb_stride, + .update_fb_format = lsdc_primary0_update_fb_format, + }, + { + .update_fb_addr = lsdc_primary1_update_fb_addr, + .update_fb_stride = lsdc_primary1_update_fb_stride, + .update_fb_format = lsdc_primary1_update_fb_format, + }, +}; + +/* + * Update location, format, enable and disable state of the cursor, + * For those who have two hardware cursor, let cursor 0 is attach to CRTC-0, + * cursor 1 is attach to CRTC-1. Compositing the primary plane and cursor + * plane is automatically done by hardware, the cursor is alway on the top of + * the primary plane. In other word, z-order is fixed in hardware and cannot + * be changed. For those old DC who has only one hardware cursor, we made it + * shared by the two screen, this works on extend screen mode. + */ + +/* cursor plane 0 (for pipe 0) related hardware ops */ + +static void lsdc_cursor0_update_bo_addr(struct lsdc_cursor *cursor, u64 addr) +{ + struct lsdc_device *ldev = cursor->ldev; + + /* 40-bit width physical address bus */ + lsdc_wreg32(ldev, LSDC_CURSOR0_ADDR_HI_REG, (addr >> 32) & 0xFF); + lsdc_wreg32(ldev, LSDC_CURSOR0_ADDR_LO_REG, addr); +} + +static void lsdc_cursor0_update_position(struct lsdc_cursor *cursor, int x, int y) +{ + struct lsdc_device *ldev = cursor->ldev; + + if (x < 0) + x = 0; + + if (y < 0) + y = 0; + + lsdc_wreg32(ldev, LSDC_CURSOR0_POSITION_REG, (y << 16) | x); +} + +static void lsdc_cursor0_update_cfg(struct lsdc_cursor *cursor, + enum lsdc_cursor_size cursor_size, + enum lsdc_cursor_format fmt) +{ + struct lsdc_device *ldev = cursor->ldev; + u32 cfg; + + cfg = CURSOR_ON_CRTC0 << CURSOR_LOCATION_SHIFT | + cursor_size << CURSOR_SIZE_SHIFT | + fmt << CURSOR_FORMAT_SHIFT; + + lsdc_wreg32(ldev, LSDC_CURSOR0_CFG_REG, cfg); +} + +/* cursor plane 1 (for pipe 1) related hardware ops */ + +static void lsdc_cursor1_update_bo_addr(struct lsdc_cursor *cursor, u64 addr) +{ + struct lsdc_device *ldev = cursor->ldev; + + /* 40-bit width physical address bus */ + lsdc_wreg32(ldev, LSDC_CURSOR1_ADDR_HI_REG, (addr >> 32) & 0xFF); + lsdc_wreg32(ldev, LSDC_CURSOR1_ADDR_LO_REG, addr); +} + +static void lsdc_cursor1_update_position(struct lsdc_cursor *cursor, int x, int y) +{ + struct lsdc_device *ldev = cursor->ldev; + + if (x < 0) + x = 0; + + if (y < 0) + y = 0; + + lsdc_wreg32(ldev, LSDC_CURSOR1_POSITION_REG, (y << 16) | x); +} + +static void lsdc_cursor1_update_cfg(struct lsdc_cursor *cursor, + enum lsdc_cursor_size cursor_size, + enum lsdc_cursor_format fmt) +{ + struct lsdc_device *ldev = cursor->ldev; + u32 cfg; + + cfg = CURSOR_ON_CRTC1 << CURSOR_LOCATION_SHIFT | + cursor_size << CURSOR_SIZE_SHIFT | + fmt << CURSOR_FORMAT_SHIFT; + + lsdc_wreg32(ldev, LSDC_CURSOR1_CFG_REG, cfg); +} + +/* The hardware cursors become normal since ls7a2000/ls2k2000 */ + +static const struct lsdc_cursor_plane_ops ls7a2000_cursor_hw_ops[2] = { + { + .update_bo_addr = lsdc_cursor0_update_bo_addr, + .update_cfg = lsdc_cursor0_update_cfg, + .update_position = lsdc_cursor0_update_position, + }, + { + .update_bo_addr = lsdc_cursor1_update_bo_addr, + .update_cfg = lsdc_cursor1_update_cfg, + .update_position = lsdc_cursor1_update_position, + }, +}; + +/* Quirks for cursor 1, only for old loongson display controller */ + +static void lsdc_cursor1_update_bo_addr_quirk(struct lsdc_cursor *cursor, u64 addr) +{ + struct lsdc_device *ldev = cursor->ldev; + + /* 40-bit width physical address bus */ + lsdc_wreg32(ldev, LSDC_CURSOR0_ADDR_HI_REG, (addr >> 32) & 0xFF); + lsdc_wreg32(ldev, LSDC_CURSOR0_ADDR_LO_REG, addr); +} + +static void lsdc_cursor1_update_position_quirk(struct lsdc_cursor *cursor, int x, int y) +{ + struct lsdc_device *ldev = cursor->ldev; + + if (x < 0) + x = 0; + + if (y < 0) + y = 0; + + lsdc_wreg32(ldev, LSDC_CURSOR0_POSITION_REG, (y << 16) | x); +} + +static void lsdc_cursor1_update_cfg_quirk(struct lsdc_cursor *cursor, + enum lsdc_cursor_size cursor_size, + enum lsdc_cursor_format fmt) +{ + struct lsdc_device *ldev = cursor->ldev; + u32 cfg; + + cfg = CURSOR_ON_CRTC1 << CURSOR_LOCATION_SHIFT | + cursor_size << CURSOR_SIZE_SHIFT | + fmt << CURSOR_FORMAT_SHIFT; + + lsdc_wreg32(ldev, LSDC_CURSOR0_CFG_REG, cfg); +} + +/* + * The unforgiving LS7A1000/LS2K1000 has only one hardware cursors plane + */ +static const struct lsdc_cursor_plane_ops ls7a1000_cursor_hw_ops[2] = { + { + .update_bo_addr = lsdc_cursor0_update_bo_addr, + .update_cfg = lsdc_cursor0_update_cfg, + .update_position = lsdc_cursor0_update_position, + }, + { + .update_bo_addr = lsdc_cursor1_update_bo_addr_quirk, + .update_cfg = lsdc_cursor1_update_cfg_quirk, + .update_position = lsdc_cursor1_update_position_quirk, + }, +}; + +int lsdc_primary_plane_init(struct drm_device *ddev, + struct drm_plane *plane, + unsigned int index) +{ + struct lsdc_primary *primary = to_lsdc_primary(plane); + int ret; + + ret = drm_universal_plane_init(ddev, plane, 1 << index, + &lsdc_plane_funcs, + lsdc_primary_formats, + ARRAY_SIZE(lsdc_primary_formats), + lsdc_fb_format_modifiers, + DRM_PLANE_TYPE_PRIMARY, + "ls-primary-plane-%u", index); + if (ret) + return ret; + + drm_plane_helper_add(plane, &lsdc_primary_helper_funcs); + + primary->ldev = to_lsdc(ddev); + primary->ops = &lsdc_primary_plane_hw_ops[index]; + + return 0; +} + +int ls7a1000_cursor_plane_init(struct drm_device *ddev, + struct drm_plane *plane, + unsigned int index) +{ + struct lsdc_cursor *cursor = to_lsdc_cursor(plane); + int ret; + + ret = drm_universal_plane_init(ddev, plane, 1 << index, + &lsdc_plane_funcs, + lsdc_cursor_formats, + ARRAY_SIZE(lsdc_cursor_formats), + lsdc_fb_format_modifiers, + DRM_PLANE_TYPE_CURSOR, + "ls-cursor-plane-%u", index); + if (ret) + return ret; + + cursor->ldev = to_lsdc(ddev); + cursor->ops = &ls7a1000_cursor_hw_ops[index]; + + drm_plane_helper_add(plane, &ls7a1000_cursor_plane_helper_funcs); + + return 0; +} + +int ls7a2000_cursor_plane_init(struct drm_device *ddev, + struct drm_plane *plane, + unsigned int index) +{ + struct lsdc_cursor *cursor = to_lsdc_cursor(plane); + int ret; + + ret = drm_universal_plane_init(ddev, plane, 1 << index, + &lsdc_plane_funcs, + lsdc_cursor_formats, + ARRAY_SIZE(lsdc_cursor_formats), + lsdc_fb_format_modifiers, + DRM_PLANE_TYPE_CURSOR, + "ls-cursor-plane-%u", index); + if (ret) + return ret; + + cursor->ldev = to_lsdc(ddev); + cursor->ops = &ls7a2000_cursor_hw_ops[index]; + + drm_plane_helper_add(plane, &ls7a2000_cursor_plane_helper_funcs); + + return 0; +} |