drm-misc-next for 4.17:
UAPI Changes: - drm/vc4: Expose performance counters to userspace (Boris) Cross-subsystem Changes: - MAINTAINERS: Linus to maintain panel-arm-versatile in -misc (Linus) Core Changes: - Only use swiotlb when necessary (Chunming) Driver Changes: - drm/panel: Add support for ARM Versatile panels (Linus) - pl111: Improvements around versatile panel support (Linus) ---------------------------------------- Tagged on 2018-02-06: drm-misc-next for 4.17: UAPI Changes: - Validate mode flags + type (Ville) - Deprecate unused mode flags PIXMUX, BCAST (Ville) - Deprecate unused mode types BUILTIN, CRTC_C, CLOCK_C, DEFAULT (Ville) Cross-subsystem Changes: - MAINTAINERS: s/Daniel/Maarten/ for drm-misc (Daniel) Core Changes: - gem: Export gem functions for drivers to use (Samuel) - bridge: Introduce bridge timings in drm_bridge (Linus) - dma-buf: Allow exclusive fence to be bundled in fence array when calling reservation_object_get_fences_rcu (Christian) - dp: Add training pattern 4 and HBR3 support to dp helpers (Manasi) - fourcc: Add alpha bit to formats to avoid driver format LUTs (Maxime) - mode: Various cleanups + add new device-wide .mode_valid hook (Ville) - atomic: Fix state leak when non-blocking commits fail (Leo) NOTE: IIRC, this was cross-picked to -fixes so it might fall out - crc: Allow polling on the data fd (Maarten) Driver Changes: - bridge/vga-dac: Add THS8134* support (Linus) - tinydrm: Various MIPI DBI improvements/cleanups (Noralf) - bridge/dw-mipi-dsi: Cleanups + use create_packet helper (Brian) - drm/sun4i: Add Display Engine frontend support (Maxime) - drm/sun4i: Add zpos support + increase num planes from 2 to 4 (Maxime) - various: Use drm_mode_get_hv_timing() to fill plane clip rectangle (Ville) - stm: Add 8-bit clut support, add dsi phy v1.31 support, +fixes (Phillipe) Cc: Boris Brezillon <boris.brezillon@free-electrons.com> Cc: Chunming Zhou <david1.zhou@amd.com> Cc: Samuel Li <Samuel.Li@amd.com> Cc: Linus Walleij <linus.walleij@linaro.org> Cc: Noralf Trønnes <noralf@tronnes.org> Cc: Brian Norris <briannorris@chromium.org> Cc: Maxime Ripard <maxime.ripard@free-electrons.com> Cc: Ville Syrjala <ville.syrjala@linux.intel.com> Cc: Christian König <christian.koenig@amd.com> Cc: Manasi Navare <manasi.d.navare@intel.com> Cc: Philippe Cornu <philippe.cornu@st.com> Cc: Leo (Sunpeng) Li <sunpeng.li@amd.com> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEfxcpfMSgdnQMs+QqlvcN/ahKBwoFAlqDUjcACgkQlvcN/ahK Bwp83Af8D805yWeS/rJ9pLaiaWmfuymMtZVl/IOEssElxFO4d+ya/S4HrcWJQwX6 KinIQiaytKKe3+TXhVRVVwg7nLBFXzaPey6SvFPCgSEa+Bm+b3v0Uk1ll59UfOzs /IhwIYIvUSCqTVAccpoT8X/85JUQ3iP41RbdfbBwUtpvL+XLl/y29Pw1dtV71ZCH I3/5zAk3odLd9LuPkyhtg+V1e8pluKlPVGZ0ElOiZ7HlKzc3ekOLNNj563LBHq8y b2txi2+h/bfBx7PU5JfP6aOKGGameE79/3UExwW0dJJAHqU728KKs/7OaDu7w0vr UlH0cY/Zc461ejPldXXx8hoKIxJ3bw== =5EUv -----END PGP SIGNATURE----- Merge tag 'drm-misc-next-2018-02-13' of git://anongit.freedesktop.org/drm/drm-misc into drm-next drm-misc-next for 4.17: UAPI Changes: - drm/vc4: Expose performance counters to userspace (Boris) Cross-subsystem Changes: - MAINTAINERS: Linus to maintain panel-arm-versatile in -misc (Linus) Core Changes: - Only use swiotlb when necessary (Chunming) Driver Changes: - drm/panel: Add support for ARM Versatile panels (Linus) - pl111: Improvements around versatile panel support (Linus) ---------------------------------------- Tagged on 2018-02-06: drm-misc-next for 4.17: UAPI Changes: - Validate mode flags + type (Ville) - Deprecate unused mode flags PIXMUX, BCAST (Ville) - Deprecate unused mode types BUILTIN, CRTC_C, CLOCK_C, DEFAULT (Ville) Cross-subsystem Changes: - MAINTAINERS: s/Daniel/Maarten/ for drm-misc (Daniel) Core Changes: - gem: Export gem functions for drivers to use (Samuel) - bridge: Introduce bridge timings in drm_bridge (Linus) - dma-buf: Allow exclusive fence to be bundled in fence array when calling reservation_object_get_fences_rcu (Christian) - dp: Add training pattern 4 and HBR3 support to dp helpers (Manasi) - fourcc: Add alpha bit to formats to avoid driver format LUTs (Maxime) - mode: Various cleanups + add new device-wide .mode_valid hook (Ville) - atomic: Fix state leak when non-blocking commits fail (Leo) NOTE: IIRC, this was cross-picked to -fixes so it might fall out - crc: Allow polling on the data fd (Maarten) Driver Changes: - bridge/vga-dac: Add THS8134* support (Linus) - tinydrm: Various MIPI DBI improvements/cleanups (Noralf) - bridge/dw-mipi-dsi: Cleanups + use create_packet helper (Brian) - drm/sun4i: Add Display Engine frontend support (Maxime) - drm/sun4i: Add zpos support + increase num planes from 2 to 4 (Maxime) - various: Use drm_mode_get_hv_timing() to fill plane clip rectangle (Ville) - stm: Add 8-bit clut support, add dsi phy v1.31 support, +fixes (Phillipe) Cc: Boris Brezillon <boris.brezillon@free-electrons.com> Cc: Chunming Zhou <david1.zhou@amd.com> Cc: Samuel Li <Samuel.Li@amd.com> Cc: Linus Walleij <linus.walleij@linaro.org> Cc: Noralf Trønnes <noralf@tronnes.org> Cc: Brian Norris <briannorris@chromium.org> Cc: Maxime Ripard <maxime.ripard@free-electrons.com> Cc: Ville Syrjala <ville.syrjala@linux.intel.com> Cc: Christian König <christian.koenig@amd.com> Cc: Manasi Navare <manasi.d.navare@intel.com> Cc: Philippe Cornu <philippe.cornu@st.com> Cc: Leo (Sunpeng) Li <sunpeng.li@amd.com> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> * tag 'drm-misc-next-2018-02-13' of git://anongit.freedesktop.org/drm/drm-misc: (115 commits) drm/radeon: only enable swiotlb path when need v2 drm/amdgpu: only enable swiotlb alloc when need v2 drm: add func to get max iomem address v2 drm/vc4: Expose performance counters to userspace drm: Print the pid when debug logging an ioctl error. drm/stm: ltdc: remove non-alpha color formats on layer 2 for older hw drm/stm: ltdc: add non-alpha color formats drm/bridge/synopsys: dsi: Add 1.31 version support drm/bridge/synopsys: dsi: Add read feature drm/pl111: Support multiple endpoints on the CLCD drm/pl111: Support variants with broken VBLANK drm/pl111: Support variants with broken clock divider drm/pl111: Handle the Versatile RGB/BGR565 mode drm/pl111: Properly detect the ARM PL110 variants drm/panel: Add support for ARM Versatile panels drm/panel: Device tree bindings for ARM Versatile panels drm/bridge: Rename argument from crtc to bridge drm/crc: Add support for polling on the data fd. drm/sun4i: Use drm_mode_get_hv_timing() to populate plane clip rectangle drm/rcar-du: Use drm_mode_get_hv_timing() to populate plane clip rectangle ...
This commit is contained in:
commit
76ea0f334e
123 changed files with 3258 additions and 834 deletions
|
@ -1,11 +1,16 @@
|
|||
THS8135 Video DAC
|
||||
-----------------
|
||||
THS8134 and THS8135 Video DAC
|
||||
-----------------------------
|
||||
|
||||
This is the binding for Texas Instruments THS8135 Video DAC bridge.
|
||||
This is the binding for Texas Instruments THS8134, THS8134A, THS8134B and
|
||||
THS8135 Video DAC bridges.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Must be "ti,ths8135"
|
||||
- compatible: Must be one of
|
||||
"ti,ths8134"
|
||||
"ti,ths8134a," "ti,ths8134"
|
||||
"ti,ths8134b", "ti,ths8134"
|
||||
"ti,ths8135"
|
||||
|
||||
Required nodes:
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
ARM Versatile TFT Panels
|
||||
|
||||
These panels are connected to the daughterboards found on the
|
||||
ARM Versatile reference designs.
|
||||
|
||||
This device node must appear as a child to a "syscon"-compatible
|
||||
node.
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "arm,versatile-tft-panel"
|
||||
|
||||
Required subnodes:
|
||||
- port: see display/panel/panel-common.txt, graph.txt
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
sysreg@0 {
|
||||
compatible = "arm,versatile-sysreg", "syscon", "simple-mfd";
|
||||
reg = <0x00000 0x1000>;
|
||||
|
||||
panel: display@0 {
|
||||
compatible = "arm,versatile-tft-panel";
|
||||
|
||||
port {
|
||||
panel_in: endpoint {
|
||||
remote-endpoint = <&foo>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
|
@ -98,7 +98,7 @@ Example 2: DSI panel
|
|||
compatible = "st,stm32-dsi";
|
||||
reg = <0x40016c00 0x800>;
|
||||
clocks = <&rcc 1 CLK_F469_DSI>, <&clk_hse>;
|
||||
clock-names = "ref", "pclk";
|
||||
clock-names = "pclk", "ref";
|
||||
resets = <&rcc STM32F4_APB2_RESET(DSI)>;
|
||||
reset-names = "apb";
|
||||
|
||||
|
|
|
@ -4456,6 +4456,13 @@ T: git git://anongit.freedesktop.org/drm/drm-misc
|
|||
S: Supported
|
||||
F: drivers/gpu/drm/pl111/
|
||||
|
||||
DRM DRIVER FOR ARM VERSATILE TFT PANELS
|
||||
M: Linus Walleij <linus.walleij@linaro.org>
|
||||
T: git git://anongit.freedesktop.org/drm/drm-misc
|
||||
S: Maintained
|
||||
F: drivers/gpu/drm/panel/panel-arm-versatile.c
|
||||
F: Documentation/devicetree/bindings/display/panel/arm,versatile-tft-panel.txt
|
||||
|
||||
DRM DRIVER FOR AST SERVER GRAPHICS CHIPS
|
||||
M: Dave Airlie <airlied@redhat.com>
|
||||
S: Odd Fixes
|
||||
|
@ -4610,8 +4617,8 @@ F: include/uapi/drm/
|
|||
F: include/linux/vga*
|
||||
|
||||
DRM DRIVERS AND MISC GPU PATCHES
|
||||
M: Daniel Vetter <daniel.vetter@intel.com>
|
||||
M: Gustavo Padovan <gustavo@padovan.org>
|
||||
M: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
|
||||
M: Sean Paul <seanpaul@chromium.org>
|
||||
W: https://01.org/linuxgraphics/gfx-docs/maintainer-tools/drm-misc.html
|
||||
S: Maintained
|
||||
|
|
|
@ -374,8 +374,9 @@ EXPORT_SYMBOL(reservation_object_copy_fences);
|
|||
* @pshared: the array of shared fence ptrs returned (array is krealloc'd to
|
||||
* the required size, and must be freed by caller)
|
||||
*
|
||||
* RETURNS
|
||||
* Zero or -errno
|
||||
* Retrieve all fences from the reservation object. If the pointer for the
|
||||
* exclusive fence is not specified the fence is put into the array of the
|
||||
* shared fences as well. Returns either zero or -ENOMEM.
|
||||
*/
|
||||
int reservation_object_get_fences_rcu(struct reservation_object *obj,
|
||||
struct dma_fence **pfence_excl,
|
||||
|
@ -389,8 +390,8 @@ int reservation_object_get_fences_rcu(struct reservation_object *obj,
|
|||
|
||||
do {
|
||||
struct reservation_object_list *fobj;
|
||||
unsigned seq;
|
||||
unsigned int i;
|
||||
unsigned int i, seq;
|
||||
size_t sz = 0;
|
||||
|
||||
shared_count = i = 0;
|
||||
|
||||
|
@ -402,9 +403,14 @@ int reservation_object_get_fences_rcu(struct reservation_object *obj,
|
|||
goto unlock;
|
||||
|
||||
fobj = rcu_dereference(obj->fence);
|
||||
if (fobj) {
|
||||
if (fobj)
|
||||
sz += sizeof(*shared) * fobj->shared_max;
|
||||
|
||||
if (!pfence_excl && fence_excl)
|
||||
sz += sizeof(*shared);
|
||||
|
||||
if (sz) {
|
||||
struct dma_fence **nshared;
|
||||
size_t sz = sizeof(*shared) * fobj->shared_max;
|
||||
|
||||
nshared = krealloc(shared, sz,
|
||||
GFP_NOWAIT | __GFP_NOWARN);
|
||||
|
@ -420,13 +426,19 @@ int reservation_object_get_fences_rcu(struct reservation_object *obj,
|
|||
break;
|
||||
}
|
||||
shared = nshared;
|
||||
shared_count = fobj->shared_count;
|
||||
|
||||
shared_count = fobj ? fobj->shared_count : 0;
|
||||
for (i = 0; i < shared_count; ++i) {
|
||||
shared[i] = rcu_dereference(fobj->shared[i]);
|
||||
if (!dma_fence_get_rcu(shared[i]))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!pfence_excl && fence_excl) {
|
||||
shared[i] = fence_excl;
|
||||
fence_excl = NULL;
|
||||
++i;
|
||||
++shared_count;
|
||||
}
|
||||
}
|
||||
|
||||
if (i != shared_count || read_seqcount_retry(&obj->seq, seq)) {
|
||||
|
@ -448,7 +460,8 @@ int reservation_object_get_fences_rcu(struct reservation_object *obj,
|
|||
|
||||
*pshared_count = shared_count;
|
||||
*pshared = shared;
|
||||
*pfence_excl = fence_excl;
|
||||
if (pfence_excl)
|
||||
*pfence_excl = fence_excl;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -43,14 +43,14 @@
|
|||
* timelines.
|
||||
*
|
||||
* Fences can be created with SW_SYNC_IOC_CREATE_FENCE ioctl with struct
|
||||
* sw_sync_ioctl_create_fence as parameter.
|
||||
* sw_sync_create_fence_data as parameter.
|
||||
*
|
||||
* To increment the timeline counter, SW_SYNC_IOC_INC ioctl should be used
|
||||
* with the increment as u32. This will update the last signaled value
|
||||
* from the timeline and signal any fence that has a seqno smaller or equal
|
||||
* to it.
|
||||
*
|
||||
* struct sw_sync_ioctl_create_fence
|
||||
* struct sw_sync_create_fence_data
|
||||
* @value: the seqno to initialise the fence with
|
||||
* @name: the name of the new sync point
|
||||
* @fence: return the fd of the new sync_file with the created fence
|
||||
|
|
|
@ -1504,6 +1504,7 @@ struct amdgpu_device {
|
|||
const struct amdgpu_asic_funcs *asic_funcs;
|
||||
bool shutdown;
|
||||
bool need_dma32;
|
||||
bool need_swiotlb;
|
||||
bool accel_working;
|
||||
struct work_struct reset_work;
|
||||
struct notifier_block acpi_nb;
|
||||
|
|
|
@ -1018,7 +1018,7 @@ static int amdgpu_ttm_tt_populate(struct ttm_tt *ttm,
|
|||
}
|
||||
|
||||
#ifdef CONFIG_SWIOTLB
|
||||
if (swiotlb_nr_tbl()) {
|
||||
if (adev->need_swiotlb && swiotlb_nr_tbl()) {
|
||||
return ttm_dma_populate(>t->ttm, adev->dev, ctx);
|
||||
}
|
||||
#endif
|
||||
|
@ -1045,7 +1045,7 @@ static void amdgpu_ttm_tt_unpopulate(struct ttm_tt *ttm)
|
|||
adev = amdgpu_ttm_adev(ttm->bdev);
|
||||
|
||||
#ifdef CONFIG_SWIOTLB
|
||||
if (swiotlb_nr_tbl()) {
|
||||
if (adev->need_swiotlb && swiotlb_nr_tbl()) {
|
||||
ttm_dma_unpopulate(>t->ttm, adev->dev);
|
||||
return;
|
||||
}
|
||||
|
@ -2010,7 +2010,7 @@ static int amdgpu_ttm_debugfs_init(struct amdgpu_device *adev)
|
|||
count = ARRAY_SIZE(amdgpu_ttm_debugfs_list);
|
||||
|
||||
#ifdef CONFIG_SWIOTLB
|
||||
if (!swiotlb_nr_tbl())
|
||||
if (!(adev->need_swiotlb && swiotlb_nr_tbl()))
|
||||
--count;
|
||||
#endif
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
*/
|
||||
#include <linux/firmware.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_cache.h>
|
||||
#include "amdgpu.h"
|
||||
#include "gmc_v6_0.h"
|
||||
#include "amdgpu_ucode.h"
|
||||
|
@ -851,6 +852,7 @@ static int gmc_v6_0_sw_init(void *handle)
|
|||
pci_set_consistent_dma_mask(adev->pdev, DMA_BIT_MASK(32));
|
||||
dev_warn(adev->dev, "amdgpu: No coherent DMA available.\n");
|
||||
}
|
||||
adev->need_swiotlb = drm_get_max_iomem() > ((u64)1 << dma_bits);
|
||||
|
||||
r = gmc_v6_0_init_microcode(adev);
|
||||
if (r) {
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
*/
|
||||
#include <linux/firmware.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_cache.h>
|
||||
#include "amdgpu.h"
|
||||
#include "cikd.h"
|
||||
#include "cik.h"
|
||||
|
@ -999,6 +1000,7 @@ static int gmc_v7_0_sw_init(void *handle)
|
|||
pci_set_consistent_dma_mask(adev->pdev, DMA_BIT_MASK(32));
|
||||
pr_warn("amdgpu: No coherent DMA available\n");
|
||||
}
|
||||
adev->need_swiotlb = drm_get_max_iomem() > ((u64)1 << dma_bits);
|
||||
|
||||
r = gmc_v7_0_init_microcode(adev);
|
||||
if (r) {
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
*/
|
||||
#include <linux/firmware.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_cache.h>
|
||||
#include "amdgpu.h"
|
||||
#include "gmc_v8_0.h"
|
||||
#include "amdgpu_ucode.h"
|
||||
|
@ -1085,6 +1086,7 @@ static int gmc_v8_0_sw_init(void *handle)
|
|||
*/
|
||||
adev->need_dma32 = false;
|
||||
dma_bits = adev->need_dma32 ? 32 : 40;
|
||||
adev->need_swiotlb = drm_get_max_iomem() > ((u64)1 << dma_bits);
|
||||
r = pci_set_dma_mask(adev->pdev, DMA_BIT_MASK(dma_bits));
|
||||
if (r) {
|
||||
adev->need_dma32 = true;
|
||||
|
@ -1096,6 +1098,7 @@ static int gmc_v8_0_sw_init(void *handle)
|
|||
pci_set_consistent_dma_mask(adev->pdev, DMA_BIT_MASK(32));
|
||||
pr_warn("amdgpu: No coherent DMA available\n");
|
||||
}
|
||||
adev->need_swiotlb = drm_get_max_iomem() > ((u64)1 << dma_bits);
|
||||
|
||||
r = gmc_v8_0_init_microcode(adev);
|
||||
if (r) {
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
*
|
||||
*/
|
||||
#include <linux/firmware.h>
|
||||
#include <drm/drm_cache.h>
|
||||
#include "amdgpu.h"
|
||||
#include "gmc_v9_0.h"
|
||||
#include "amdgpu_atomfirmware.h"
|
||||
|
@ -852,6 +853,7 @@ static int gmc_v9_0_sw_init(void *handle)
|
|||
pci_set_consistent_dma_mask(adev->pdev, DMA_BIT_MASK(32));
|
||||
printk(KERN_WARNING "amdgpu: No coherent DMA available.\n");
|
||||
}
|
||||
adev->need_swiotlb = drm_get_max_iomem() > ((u64)1 << dma_bits);
|
||||
|
||||
r = gmc_v9_0_mc_init(adev);
|
||||
if (r)
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
*/
|
||||
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_encoder_slave.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_device.h>
|
||||
|
||||
#include "arcpgu.h"
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_encoder_slave.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
|
||||
#include "arcpgu.h"
|
||||
|
@ -29,7 +28,6 @@
|
|||
|
||||
struct arcpgu_drm_connector {
|
||||
struct drm_connector connector;
|
||||
struct drm_encoder_slave *encoder_slave;
|
||||
};
|
||||
|
||||
static int arcpgu_drm_connector_get_modes(struct drm_connector *connector)
|
||||
|
@ -68,7 +66,7 @@ static struct drm_encoder_funcs arcpgu_drm_encoder_funcs = {
|
|||
int arcpgu_drm_sim_init(struct drm_device *drm, struct device_node *np)
|
||||
{
|
||||
struct arcpgu_drm_connector *arcpgu_connector;
|
||||
struct drm_encoder_slave *encoder;
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_connector *connector;
|
||||
int ret;
|
||||
|
||||
|
@ -76,10 +74,10 @@ int arcpgu_drm_sim_init(struct drm_device *drm, struct device_node *np)
|
|||
if (encoder == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
encoder->base.possible_crtcs = 1;
|
||||
encoder->base.possible_clones = 0;
|
||||
encoder->possible_crtcs = 1;
|
||||
encoder->possible_clones = 0;
|
||||
|
||||
ret = drm_encoder_init(drm, &encoder->base, &arcpgu_drm_encoder_funcs,
|
||||
ret = drm_encoder_init(drm, encoder, &arcpgu_drm_encoder_funcs,
|
||||
DRM_MODE_ENCODER_VIRTUAL, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -101,21 +99,19 @@ int arcpgu_drm_sim_init(struct drm_device *drm, struct device_node *np)
|
|||
goto error_encoder_cleanup;
|
||||
}
|
||||
|
||||
ret = drm_mode_connector_attach_encoder(connector, &encoder->base);
|
||||
ret = drm_mode_connector_attach_encoder(connector, encoder);
|
||||
if (ret < 0) {
|
||||
dev_err(drm->dev, "could not attach connector to encoder\n");
|
||||
drm_connector_unregister(connector);
|
||||
goto error_connector_cleanup;
|
||||
}
|
||||
|
||||
arcpgu_connector->encoder_slave = encoder;
|
||||
|
||||
return 0;
|
||||
|
||||
error_connector_cleanup:
|
||||
drm_connector_cleanup(connector);
|
||||
|
||||
error_encoder_cleanup:
|
||||
drm_encoder_cleanup(&encoder->base);
|
||||
drm_encoder_cleanup(encoder);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -249,8 +249,9 @@ static int hdlcd_plane_atomic_check(struct drm_plane *plane,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
clip.x2 = crtc_state->adjusted_mode.hdisplay;
|
||||
clip.y2 = crtc_state->adjusted_mode.vdisplay;
|
||||
if (crtc_state->enable)
|
||||
drm_mode_get_hv_timing(&crtc_state->mode,
|
||||
&clip.x2, &clip.y2);
|
||||
|
||||
return drm_atomic_helper_check_plane_state(state, crtc_state, &clip,
|
||||
DRM_PLANE_HELPER_NO_SCALING,
|
||||
|
|
|
@ -148,8 +148,10 @@ static int malidp_se_check_scaling(struct malidp_plane *mp,
|
|||
if (!crtc_state)
|
||||
return -EINVAL;
|
||||
|
||||
clip.x2 = crtc_state->adjusted_mode.hdisplay;
|
||||
clip.y2 = crtc_state->adjusted_mode.vdisplay;
|
||||
if (crtc_state->enable)
|
||||
drm_mode_get_hv_timing(&crtc_state->mode,
|
||||
&clip.x2, &clip.y2);
|
||||
|
||||
ret = drm_atomic_helper_check_plane_state(state, crtc_state, &clip,
|
||||
0, INT_MAX, true, true);
|
||||
if (ret)
|
||||
|
|
|
@ -194,20 +194,6 @@ static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool atmel_hlcdc_format_embeds_alpha(u32 format)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sizeof(format); i++) {
|
||||
char tmp = (format >> (8 * i)) & 0xff;
|
||||
|
||||
if (tmp == 'A')
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static u32 heo_downscaling_xcoef[] = {
|
||||
0x11343311,
|
||||
0x000000f7,
|
||||
|
@ -377,13 +363,13 @@ atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
|
|||
{
|
||||
unsigned int cfg = ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id;
|
||||
const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
|
||||
u32 format = state->base.fb->format->format;
|
||||
const struct drm_format_info *format = state->base.fb->format;
|
||||
|
||||
/*
|
||||
* Rotation optimization is not working on RGB888 (rotation is still
|
||||
* working but without any optimization).
|
||||
*/
|
||||
if (format == DRM_FORMAT_RGB888)
|
||||
if (format->format == DRM_FORMAT_RGB888)
|
||||
cfg |= ATMEL_HLCDC_LAYER_DMA_ROTDIS;
|
||||
|
||||
atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_HLCDC_LAYER_DMA_CFG,
|
||||
|
@ -395,7 +381,7 @@ atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
|
|||
cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |
|
||||
ATMEL_HLCDC_LAYER_ITER;
|
||||
|
||||
if (atmel_hlcdc_format_embeds_alpha(format))
|
||||
if (format->has_alpha)
|
||||
cfg |= ATMEL_HLCDC_LAYER_LAEN;
|
||||
else
|
||||
cfg |= ATMEL_HLCDC_LAYER_GAEN |
|
||||
|
@ -566,7 +552,7 @@ atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state)
|
|||
ovl_state = drm_plane_state_to_atmel_hlcdc_plane_state(ovl_s);
|
||||
|
||||
if (!ovl_s->fb ||
|
||||
atmel_hlcdc_format_embeds_alpha(ovl_s->fb->format->format) ||
|
||||
ovl_s->fb->format->has_alpha ||
|
||||
ovl_state->alpha != 255)
|
||||
continue;
|
||||
|
||||
|
@ -769,7 +755,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
|
|||
|
||||
if ((state->crtc_h != state->src_h || state->crtc_w != state->src_w) &&
|
||||
(!desc->layout.memsize ||
|
||||
atmel_hlcdc_format_embeds_alpha(state->base.fb->format->format)))
|
||||
state->base.fb->format->has_alpha))
|
||||
return -EINVAL;
|
||||
|
||||
if (state->crtc_x < 0 || state->crtc_y < 0)
|
||||
|
|
|
@ -1301,8 +1301,7 @@ static void unregister_i2c_dummy_clients(struct anx78xx *anx78xx)
|
|||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(anx78xx->i2c_dummy); i++)
|
||||
if (anx78xx->i2c_dummy[i])
|
||||
i2c_unregister_device(anx78xx->i2c_dummy[i]);
|
||||
i2c_unregister_device(anx78xx->i2c_dummy[i]);
|
||||
}
|
||||
|
||||
static const struct regmap_config anx78xx_regmap_config = {
|
||||
|
|
|
@ -727,7 +727,6 @@ static int analogix_dp_set_link_train(struct analogix_dp_device *dp,
|
|||
|
||||
static int analogix_dp_config_video(struct analogix_dp_device *dp)
|
||||
{
|
||||
int retval = 0;
|
||||
int timeout_loop = 0;
|
||||
int done_count = 0;
|
||||
|
||||
|
@ -783,10 +782,7 @@ static int analogix_dp_config_video(struct analogix_dp_device *dp)
|
|||
usleep_range(1000, 1001);
|
||||
}
|
||||
|
||||
if (retval != 0)
|
||||
dev_err(dp->dev, "Video stream is not detected!\n");
|
||||
|
||||
return retval;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void analogix_dp_enable_scramble(struct analogix_dp_device *dp,
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
|
@ -204,6 +205,7 @@ static int dumb_vga_probe(struct platform_device *pdev)
|
|||
|
||||
vga->bridge.funcs = &dumb_vga_bridge_funcs;
|
||||
vga->bridge.of_node = pdev->dev.of_node;
|
||||
vga->bridge.timings = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
drm_bridge_add(&vga->bridge);
|
||||
|
||||
|
@ -222,10 +224,61 @@ static int dumb_vga_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We assume the ADV7123 DAC is the "default" for historical reasons
|
||||
* Information taken from the ADV7123 datasheet, revision D.
|
||||
* NOTE: the ADV7123EP seems to have other timings and need a new timings
|
||||
* set if used.
|
||||
*/
|
||||
static const struct drm_bridge_timings default_dac_timings = {
|
||||
/* Timing specifications, datasheet page 7 */
|
||||
.sampling_edge = DRM_BUS_FLAG_PIXDATA_POSEDGE,
|
||||
.setup_time_ps = 500,
|
||||
.hold_time_ps = 1500,
|
||||
};
|
||||
|
||||
/*
|
||||
* Information taken from the THS8134, THS8134A, THS8134B datasheet named
|
||||
* "SLVS205D", dated May 1990, revised March 2000.
|
||||
*/
|
||||
static const struct drm_bridge_timings ti_ths8134_dac_timings = {
|
||||
/* From timing diagram, datasheet page 9 */
|
||||
.sampling_edge = DRM_BUS_FLAG_PIXDATA_POSEDGE,
|
||||
/* From datasheet, page 12 */
|
||||
.setup_time_ps = 3000,
|
||||
/* I guess this means latched input */
|
||||
.hold_time_ps = 0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Information taken from the THS8135 datasheet named "SLAS343B", dated
|
||||
* May 2001, revised April 2013.
|
||||
*/
|
||||
static const struct drm_bridge_timings ti_ths8135_dac_timings = {
|
||||
/* From timing diagram, datasheet page 14 */
|
||||
.sampling_edge = DRM_BUS_FLAG_PIXDATA_POSEDGE,
|
||||
/* From datasheet, page 16 */
|
||||
.setup_time_ps = 2000,
|
||||
.hold_time_ps = 500,
|
||||
};
|
||||
|
||||
static const struct of_device_id dumb_vga_match[] = {
|
||||
{ .compatible = "dumb-vga-dac" },
|
||||
{ .compatible = "adi,adv7123" },
|
||||
{ .compatible = "ti,ths8135" },
|
||||
{
|
||||
.compatible = "dumb-vga-dac",
|
||||
.data = NULL,
|
||||
},
|
||||
{
|
||||
.compatible = "adi,adv7123",
|
||||
.data = &default_dac_timings,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,ths8135",
|
||||
.data = &ti_ths8135_dac_timings,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,ths8134",
|
||||
.data = &ti_ths8134_dac_timings,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dumb_vga_match);
|
||||
|
|
|
@ -29,7 +29,10 @@
|
|||
#include <drm/bridge/dw_mipi_dsi.h>
|
||||
#include <video/mipi_display.h>
|
||||
|
||||
#define HWVER_131 0x31333100 /* IP version 1.31 */
|
||||
|
||||
#define DSI_VERSION 0x00
|
||||
#define VERSION GENMASK(31, 8)
|
||||
|
||||
#define DSI_PWR_UP 0x04
|
||||
#define RESET 0
|
||||
|
@ -136,10 +139,6 @@
|
|||
GEN_SW_0P_TX_LP)
|
||||
|
||||
#define DSI_GEN_HDR 0x6c
|
||||
/* TODO These 2 defines will be reworked thanks to mipi_dsi_create_packet() */
|
||||
#define GEN_HDATA(data) (((data) & 0xffff) << 8)
|
||||
#define GEN_HTYPE(type) (((type) & 0xff) << 0)
|
||||
|
||||
#define DSI_GEN_PLD_DATA 0x70
|
||||
|
||||
#define DSI_CMD_PKT_STATUS 0x74
|
||||
|
@ -169,11 +168,12 @@
|
|||
#define PHY_CLKHS2LP_TIME(lbcc) (((lbcc) & 0x3ff) << 16)
|
||||
#define PHY_CLKLP2HS_TIME(lbcc) ((lbcc) & 0x3ff)
|
||||
|
||||
/* TODO Next register is slightly different between 1.30 & 1.31 IP version */
|
||||
#define DSI_PHY_TMR_CFG 0x9c
|
||||
#define PHY_HS2LP_TIME(lbcc) (((lbcc) & 0xff) << 24)
|
||||
#define PHY_LP2HS_TIME(lbcc) (((lbcc) & 0xff) << 16)
|
||||
#define MAX_RD_TIME(lbcc) ((lbcc) & 0x7fff)
|
||||
#define PHY_HS2LP_TIME_V131(lbcc) (((lbcc) & 0x3ff) << 16)
|
||||
#define PHY_LP2HS_TIME_V131(lbcc) ((lbcc) & 0x3ff)
|
||||
|
||||
#define DSI_PHY_RSTZ 0xa0
|
||||
#define PHY_DISFORCEPLL 0
|
||||
|
@ -212,7 +212,9 @@
|
|||
#define DSI_INT_ST1 0xc0
|
||||
#define DSI_INT_MSK0 0xc4
|
||||
#define DSI_INT_MSK1 0xc8
|
||||
|
||||
#define DSI_PHY_TMR_RD_CFG 0xf4
|
||||
#define MAX_RD_TIME_V131(lbcc) ((lbcc) & 0x7fff)
|
||||
|
||||
#define PHY_STATUS_TIMEOUT_US 10000
|
||||
#define CMD_PKT_STATUS_TIMEOUT_US 20000
|
||||
|
@ -359,52 +361,23 @@ static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 hdr_val)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dw_mipi_dsi_dcs_short_write(struct dw_mipi_dsi *dsi,
|
||||
const struct mipi_dsi_msg *msg)
|
||||
static int dw_mipi_dsi_write(struct dw_mipi_dsi *dsi,
|
||||
const struct mipi_dsi_packet *packet)
|
||||
{
|
||||
const u8 *tx_buf = msg->tx_buf;
|
||||
u16 data = 0;
|
||||
const u8 *tx_buf = packet->payload;
|
||||
int len = packet->payload_length, pld_data_bytes = sizeof(u32), ret;
|
||||
__le32 word;
|
||||
u32 val;
|
||||
|
||||
if (msg->tx_len > 0)
|
||||
data |= tx_buf[0];
|
||||
if (msg->tx_len > 1)
|
||||
data |= tx_buf[1] << 8;
|
||||
|
||||
if (msg->tx_len > 2) {
|
||||
dev_err(dsi->dev, "too long tx buf length %zu for short write\n",
|
||||
msg->tx_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
val = GEN_HDATA(data) | GEN_HTYPE(msg->type);
|
||||
return dw_mipi_dsi_gen_pkt_hdr_write(dsi, val);
|
||||
}
|
||||
|
||||
static int dw_mipi_dsi_dcs_long_write(struct dw_mipi_dsi *dsi,
|
||||
const struct mipi_dsi_msg *msg)
|
||||
{
|
||||
const u8 *tx_buf = msg->tx_buf;
|
||||
int len = msg->tx_len, pld_data_bytes = sizeof(u32), ret;
|
||||
u32 hdr_val = GEN_HDATA(msg->tx_len) | GEN_HTYPE(msg->type);
|
||||
u32 remainder;
|
||||
u32 val;
|
||||
|
||||
if (msg->tx_len < 3) {
|
||||
dev_err(dsi->dev, "wrong tx buf length %zu for long write\n",
|
||||
msg->tx_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
while (DIV_ROUND_UP(len, pld_data_bytes)) {
|
||||
while (len) {
|
||||
if (len < pld_data_bytes) {
|
||||
remainder = 0;
|
||||
memcpy(&remainder, tx_buf, len);
|
||||
dsi_write(dsi, DSI_GEN_PLD_DATA, remainder);
|
||||
word = 0;
|
||||
memcpy(&word, tx_buf, len);
|
||||
dsi_write(dsi, DSI_GEN_PLD_DATA, le32_to_cpu(word));
|
||||
len = 0;
|
||||
} else {
|
||||
memcpy(&remainder, tx_buf, pld_data_bytes);
|
||||
dsi_write(dsi, DSI_GEN_PLD_DATA, remainder);
|
||||
memcpy(&word, tx_buf, pld_data_bytes);
|
||||
dsi_write(dsi, DSI_GEN_PLD_DATA, le32_to_cpu(word));
|
||||
tx_buf += pld_data_bytes;
|
||||
len -= pld_data_bytes;
|
||||
}
|
||||
|
@ -419,40 +392,74 @@ static int dw_mipi_dsi_dcs_long_write(struct dw_mipi_dsi *dsi,
|
|||
}
|
||||
}
|
||||
|
||||
return dw_mipi_dsi_gen_pkt_hdr_write(dsi, hdr_val);
|
||||
word = 0;
|
||||
memcpy(&word, packet->header, sizeof(packet->header));
|
||||
return dw_mipi_dsi_gen_pkt_hdr_write(dsi, le32_to_cpu(word));
|
||||
}
|
||||
|
||||
static int dw_mipi_dsi_read(struct dw_mipi_dsi *dsi,
|
||||
const struct mipi_dsi_msg *msg)
|
||||
{
|
||||
int i, j, ret, len = msg->rx_len;
|
||||
u8 *buf = msg->rx_buf;
|
||||
u32 val;
|
||||
|
||||
/* Wait end of the read operation */
|
||||
ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
|
||||
val, !(val & GEN_RD_CMD_BUSY),
|
||||
1000, CMD_PKT_STATUS_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(dsi->dev, "Timeout during read operation\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i += 4) {
|
||||
/* Read fifo must not be empty before all bytes are read */
|
||||
ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
|
||||
val, !(val & GEN_PLD_R_EMPTY),
|
||||
1000, CMD_PKT_STATUS_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(dsi->dev, "Read payload FIFO is empty\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
val = dsi_read(dsi, DSI_GEN_PLD_DATA);
|
||||
for (j = 0; j < 4 && j + i < len; j++)
|
||||
buf[i + j] = val >> (8 * j);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host,
|
||||
const struct mipi_dsi_msg *msg)
|
||||
{
|
||||
struct dw_mipi_dsi *dsi = host_to_dsi(host);
|
||||
int ret;
|
||||
struct mipi_dsi_packet packet;
|
||||
int ret, nb_bytes;
|
||||
|
||||
/*
|
||||
* TODO dw drv improvements
|
||||
* use mipi_dsi_create_packet() instead of all following
|
||||
* functions and code (no switch cases, no
|
||||
* dw_mipi_dsi_dcs_short_write(), only the loop in long_write...)
|
||||
* and use packet.header...
|
||||
*/
|
||||
dw_mipi_message_config(dsi, msg);
|
||||
|
||||
switch (msg->type) {
|
||||
case MIPI_DSI_DCS_SHORT_WRITE:
|
||||
case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
|
||||
case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
|
||||
ret = dw_mipi_dsi_dcs_short_write(dsi, msg);
|
||||
break;
|
||||
case MIPI_DSI_DCS_LONG_WRITE:
|
||||
ret = dw_mipi_dsi_dcs_long_write(dsi, msg);
|
||||
break;
|
||||
default:
|
||||
dev_err(dsi->dev, "unsupported message type 0x%02x\n",
|
||||
msg->type);
|
||||
ret = -EINVAL;
|
||||
ret = mipi_dsi_create_packet(&packet, msg);
|
||||
if (ret) {
|
||||
dev_err(dsi->dev, "failed to create packet: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
dw_mipi_message_config(dsi, msg);
|
||||
|
||||
ret = dw_mipi_dsi_write(dsi, &packet);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (msg->rx_buf && msg->rx_len) {
|
||||
ret = dw_mipi_dsi_read(dsi, msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
nb_bytes = msg->rx_len;
|
||||
} else {
|
||||
nb_bytes = packet.size;
|
||||
}
|
||||
|
||||
return nb_bytes;
|
||||
}
|
||||
|
||||
static const struct mipi_dsi_host_ops dw_mipi_dsi_host_ops = {
|
||||
|
@ -658,6 +665,8 @@ static void dw_mipi_dsi_vertical_timing_config(struct dw_mipi_dsi *dsi,
|
|||
|
||||
static void dw_mipi_dsi_dphy_timing_config(struct dw_mipi_dsi *dsi)
|
||||
{
|
||||
u32 hw_version;
|
||||
|
||||
/*
|
||||
* TODO dw drv improvements
|
||||
* data & clock lane timers should be computed according to panel
|
||||
|
@ -665,8 +674,17 @@ static void dw_mipi_dsi_dphy_timing_config(struct dw_mipi_dsi *dsi)
|
|||
* note: DSI_PHY_TMR_CFG.MAX_RD_TIME should be in line with
|
||||
* DSI_CMD_MODE_CFG.MAX_RD_PKT_SIZE_LP (see CMD_MODE_ALL_LP)
|
||||
*/
|
||||
dsi_write(dsi, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME(0x40)
|
||||
| PHY_LP2HS_TIME(0x40) | MAX_RD_TIME(10000));
|
||||
|
||||
hw_version = dsi_read(dsi, DSI_VERSION) & VERSION;
|
||||
|
||||
if (hw_version >= HWVER_131) {
|
||||
dsi_write(dsi, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME_V131(0x40) |
|
||||
PHY_LP2HS_TIME_V131(0x40));
|
||||
dsi_write(dsi, DSI_PHY_TMR_RD_CFG, MAX_RD_TIME_V131(10000));
|
||||
} else {
|
||||
dsi_write(dsi, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME(0x40) |
|
||||
PHY_LP2HS_TIME(0x40) | MAX_RD_TIME(10000));
|
||||
}
|
||||
|
||||
dsi_write(dsi, DSI_PHY_TMR_LPCLK_CFG, PHY_CLKHS2LP_TIME(0x40)
|
||||
| PHY_CLKLP2HS_TIME(0x40));
|
||||
|
@ -746,9 +764,9 @@ static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge)
|
|||
pm_runtime_put(dsi->dev);
|
||||
}
|
||||
|
||||
void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
|
||||
const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops;
|
||||
|
@ -922,8 +940,6 @@ __dw_mipi_dsi_probe(struct platform_device *pdev,
|
|||
dsi->bridge.of_node = pdev->dev.of_node;
|
||||
#endif
|
||||
|
||||
dev_set_drvdata(dev, dsi);
|
||||
|
||||
return dsi;
|
||||
}
|
||||
|
||||
|
@ -935,23 +951,16 @@ static void __dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi)
|
|||
/*
|
||||
* Probe/remove API, used from platforms based on the DRM bridge API.
|
||||
*/
|
||||
int dw_mipi_dsi_probe(struct platform_device *pdev,
|
||||
const struct dw_mipi_dsi_plat_data *plat_data)
|
||||
struct dw_mipi_dsi *
|
||||
dw_mipi_dsi_probe(struct platform_device *pdev,
|
||||
const struct dw_mipi_dsi_plat_data *plat_data)
|
||||
{
|
||||
struct dw_mipi_dsi *dsi;
|
||||
|
||||
dsi = __dw_mipi_dsi_probe(pdev, plat_data);
|
||||
if (IS_ERR(dsi))
|
||||
return PTR_ERR(dsi);
|
||||
|
||||
return 0;
|
||||
return __dw_mipi_dsi_probe(pdev, plat_data);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_mipi_dsi_probe);
|
||||
|
||||
void dw_mipi_dsi_remove(struct platform_device *pdev)
|
||||
void dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi)
|
||||
{
|
||||
struct dw_mipi_dsi *dsi = platform_get_drvdata(pdev);
|
||||
|
||||
mipi_dsi_host_unregister(&dsi->dsi_host);
|
||||
|
||||
__dw_mipi_dsi_remove(dsi);
|
||||
|
@ -961,31 +970,30 @@ EXPORT_SYMBOL_GPL(dw_mipi_dsi_remove);
|
|||
/*
|
||||
* Bind/unbind API, used from platforms based on the component framework.
|
||||
*/
|
||||
int dw_mipi_dsi_bind(struct platform_device *pdev, struct drm_encoder *encoder,
|
||||
const struct dw_mipi_dsi_plat_data *plat_data)
|
||||
struct dw_mipi_dsi *
|
||||
dw_mipi_dsi_bind(struct platform_device *pdev, struct drm_encoder *encoder,
|
||||
const struct dw_mipi_dsi_plat_data *plat_data)
|
||||
{
|
||||
struct dw_mipi_dsi *dsi;
|
||||
int ret;
|
||||
|
||||
dsi = __dw_mipi_dsi_probe(pdev, plat_data);
|
||||
if (IS_ERR(dsi))
|
||||
return PTR_ERR(dsi);
|
||||
return dsi;
|
||||
|
||||
ret = drm_bridge_attach(encoder, &dsi->bridge, NULL);
|
||||
if (ret) {
|
||||
dw_mipi_dsi_remove(pdev);
|
||||
dw_mipi_dsi_remove(dsi);
|
||||
DRM_ERROR("Failed to initialize bridge with drm\n");
|
||||
return ret;
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return dsi;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_mipi_dsi_bind);
|
||||
|
||||
void dw_mipi_dsi_unbind(struct device *dev)
|
||||
void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi)
|
||||
{
|
||||
struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
|
||||
|
||||
__dw_mipi_dsi_remove(dsi);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_mipi_dsi_unbind);
|
||||
|
|
|
@ -390,7 +390,7 @@ int drm_atomic_set_mode_prop_for_crtc(struct drm_crtc_state *state,
|
|||
|
||||
if (blob) {
|
||||
if (blob->length != sizeof(struct drm_mode_modeinfo) ||
|
||||
drm_mode_convert_umode(&state->mode,
|
||||
drm_mode_convert_umode(state->crtc->dev, &state->mode,
|
||||
(const struct drm_mode_modeinfo *)
|
||||
blob->data))
|
||||
return -EINVAL;
|
||||
|
@ -863,10 +863,10 @@ static int drm_atomic_plane_check(struct drm_plane *plane,
|
|||
int ret;
|
||||
|
||||
/* either *both* CRTC and FB must be set, or neither */
|
||||
if (WARN_ON(state->crtc && !state->fb)) {
|
||||
if (state->crtc && !state->fb) {
|
||||
DRM_DEBUG_ATOMIC("CRTC set but no FB\n");
|
||||
return -EINVAL;
|
||||
} else if (WARN_ON(state->fb && !state->crtc)) {
|
||||
} else if (state->fb && !state->crtc) {
|
||||
DRM_DEBUG_ATOMIC("FB set but no CRTC\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -1878,6 +1878,8 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
|
|||
new_crtc_state->event->base.completion = &commit->flip_done;
|
||||
new_crtc_state->event->base.completion_release = release_crtc_commit;
|
||||
drm_crtc_commit_get(commit);
|
||||
|
||||
commit->abort_completion = true;
|
||||
}
|
||||
|
||||
for_each_oldnew_connector_in_state(state, conn, old_conn_state, new_conn_state, i) {
|
||||
|
@ -3421,8 +3423,21 @@ EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state);
|
|||
void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state)
|
||||
{
|
||||
if (state->commit) {
|
||||
/*
|
||||
* In the event that a non-blocking commit returns
|
||||
* -ERESTARTSYS before the commit_tail work is queued, we will
|
||||
* have an extra reference to the commit object. Release it, if
|
||||
* the event has not been consumed by the worker.
|
||||
*
|
||||
* state->event may be freed, so we can't directly look at
|
||||
* state->event->base.completion.
|
||||
*/
|
||||
if (state->event && state->commit->abort_completion)
|
||||
drm_crtc_commit_put(state->commit);
|
||||
|
||||
kfree(state->commit->event);
|
||||
state->commit->event = NULL;
|
||||
|
||||
drm_crtc_commit_put(state->commit);
|
||||
}
|
||||
|
||||
|
|
|
@ -205,9 +205,14 @@ int drm_connector_init(struct drm_device *dev,
|
|||
connector->dev = dev;
|
||||
connector->funcs = funcs;
|
||||
|
||||
ret = ida_simple_get(&config->connector_ida, 0, 0, GFP_KERNEL);
|
||||
if (ret < 0)
|
||||
/* connector index is used with 32bit bitmasks */
|
||||
ret = ida_simple_get(&config->connector_ida, 0, 32, GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
DRM_DEBUG_KMS("Failed to allocate %s connector index: %d\n",
|
||||
drm_connector_enum_list[connector_type].name,
|
||||
ret);
|
||||
goto out_put;
|
||||
}
|
||||
connector->index = ret;
|
||||
ret = 0;
|
||||
|
||||
|
|
|
@ -282,6 +282,10 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
|
|||
WARN_ON(primary && primary->type != DRM_PLANE_TYPE_PRIMARY);
|
||||
WARN_ON(cursor && cursor->type != DRM_PLANE_TYPE_CURSOR);
|
||||
|
||||
/* crtc index is used with 32bit bitmasks */
|
||||
if (WARN_ON(config->num_crtc >= 32))
|
||||
return -EINVAL;
|
||||
|
||||
crtc->dev = dev;
|
||||
crtc->funcs = funcs;
|
||||
|
||||
|
@ -610,7 +614,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
|
|||
goto out;
|
||||
}
|
||||
|
||||
ret = drm_mode_convert_umode(mode, &crtc_req->mode);
|
||||
ret = drm_mode_convert_umode(dev, mode, &crtc_req->mode);
|
||||
if (ret) {
|
||||
DRM_DEBUG_KMS("Invalid mode\n");
|
||||
goto out;
|
||||
|
|
|
@ -307,10 +307,29 @@ static ssize_t crtc_crc_read(struct file *filep, char __user *user_buf,
|
|||
return LINE_LEN(crc->values_cnt);
|
||||
}
|
||||
|
||||
static unsigned int crtc_crc_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct drm_crtc *crtc = file->f_inode->i_private;
|
||||
struct drm_crtc_crc *crc = &crtc->crc;
|
||||
unsigned ret;
|
||||
|
||||
poll_wait(file, &crc->wq, wait);
|
||||
|
||||
spin_lock_irq(&crc->lock);
|
||||
if (crc->source && crtc_crc_data_count(crc))
|
||||
ret = POLLIN | POLLRDNORM;
|
||||
else
|
||||
ret = 0;
|
||||
spin_unlock_irq(&crc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations drm_crtc_crc_data_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = crtc_crc_open,
|
||||
.read = crtc_crc_read,
|
||||
.poll = crtc_crc_poll,
|
||||
.release = crtc_crc_release,
|
||||
};
|
||||
|
||||
|
|
|
@ -146,6 +146,8 @@ u8 drm_dp_link_rate_to_bw_code(int link_rate)
|
|||
return DP_LINK_BW_2_7;
|
||||
case 540000:
|
||||
return DP_LINK_BW_5_4;
|
||||
case 810000:
|
||||
return DP_LINK_BW_8_1;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_link_rate_to_bw_code);
|
||||
|
@ -161,6 +163,8 @@ int drm_dp_bw_code_to_link_rate(u8 link_bw)
|
|||
return 270000;
|
||||
case DP_LINK_BW_5_4:
|
||||
return 540000;
|
||||
case DP_LINK_BW_8_1:
|
||||
return 810000;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate);
|
||||
|
|
|
@ -2087,6 +2087,9 @@ static bool drm_dp_get_vc_payload_bw(int dp_link_bw,
|
|||
case DP_LINK_BW_5_4:
|
||||
*out = 10 * dp_link_count;
|
||||
break;
|
||||
case DP_LINK_BW_8_1:
|
||||
*out = 15 * dp_link_count;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -2767,7 +2767,7 @@ do_detailed_mode(struct detailed_timing *timing, void *c)
|
|||
|
||||
drm_mode_probed_add(closure->connector, newmode);
|
||||
closure->modes++;
|
||||
closure->preferred = 0;
|
||||
closure->preferred = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2784,7 +2784,7 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid,
|
|||
struct detailed_mode_closure closure = {
|
||||
.connector = connector,
|
||||
.edid = edid,
|
||||
.preferred = 1,
|
||||
.preferred = true,
|
||||
.quirks = quirks,
|
||||
};
|
||||
|
||||
|
|
|
@ -110,6 +110,10 @@ int drm_encoder_init(struct drm_device *dev,
|
|||
{
|
||||
int ret;
|
||||
|
||||
/* encoder index is used with 32bit bitmasks */
|
||||
if (WARN_ON(dev->mode_config.num_encoder >= 32))
|
||||
return -EINVAL;
|
||||
|
||||
ret = drm_mode_object_add(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
@ -112,18 +112,18 @@ const struct drm_format_info *__drm_format_info(u32 format)
|
|||
{ .format = DRM_FORMAT_XBGR4444, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_RGBX4444, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_BGRX4444, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_ARGB4444, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_ABGR4444, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_RGBA4444, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_BGRA4444, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_ARGB4444, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_ABGR4444, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_RGBA4444, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_BGRA4444, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_XRGB1555, .depth = 15, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_XBGR1555, .depth = 15, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_RGBX5551, .depth = 15, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_BGRX5551, .depth = 15, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_ARGB1555, .depth = 15, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_ABGR1555, .depth = 15, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_RGBA5551, .depth = 15, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_BGRA5551, .depth = 15, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_ARGB1555, .depth = 15, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_ABGR1555, .depth = 15, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_RGBA5551, .depth = 15, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_BGRA5551, .depth = 15, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_RGB565, .depth = 16, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_BGR565, .depth = 16, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_RGB888, .depth = 24, .num_planes = 1, .cpp = { 3, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
|
@ -132,26 +132,26 @@ const struct drm_format_info *__drm_format_info(u32 format)
|
|||
{ .format = DRM_FORMAT_XBGR8888, .depth = 24, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_RGBX8888, .depth = 24, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_BGRX8888, .depth = 24, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_RGB565_A8, .depth = 24, .num_planes = 2, .cpp = { 2, 1, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_BGR565_A8, .depth = 24, .num_planes = 2, .cpp = { 2, 1, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_RGB565_A8, .depth = 24, .num_planes = 2, .cpp = { 2, 1, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_BGR565_A8, .depth = 24, .num_planes = 2, .cpp = { 2, 1, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_XRGB2101010, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_XBGR2101010, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_RGBX1010102, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_BGRX1010102, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_ARGB2101010, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_ABGR2101010, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_RGBA1010102, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_BGRA1010102, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_ARGB8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_ABGR8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_RGBA8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_BGRA8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_RGB888_A8, .depth = 32, .num_planes = 2, .cpp = { 3, 1, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_BGR888_A8, .depth = 32, .num_planes = 2, .cpp = { 3, 1, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_XRGB8888_A8, .depth = 32, .num_planes = 2, .cpp = { 4, 1, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_XBGR8888_A8, .depth = 32, .num_planes = 2, .cpp = { 4, 1, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_RGBX8888_A8, .depth = 32, .num_planes = 2, .cpp = { 4, 1, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_BGRX8888_A8, .depth = 32, .num_planes = 2, .cpp = { 4, 1, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_ARGB2101010, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_ABGR2101010, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_RGBA1010102, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_BGRA1010102, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_ARGB8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_ABGR8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_RGBA8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_BGRA8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_RGB888_A8, .depth = 32, .num_planes = 2, .cpp = { 3, 1, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_BGR888_A8, .depth = 32, .num_planes = 2, .cpp = { 3, 1, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_XRGB8888_A8, .depth = 32, .num_planes = 2, .cpp = { 4, 1, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_XBGR8888_A8, .depth = 32, .num_planes = 2, .cpp = { 4, 1, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_RGBX8888_A8, .depth = 32, .num_planes = 2, .cpp = { 4, 1, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_BGRX8888_A8, .depth = 32, .num_planes = 2, .cpp = { 4, 1, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_YUV410, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 4, .vsub = 4 },
|
||||
{ .format = DRM_FORMAT_YVU410, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 4, .vsub = 4 },
|
||||
{ .format = DRM_FORMAT_YUV411, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 4, .vsub = 1 },
|
||||
|
@ -172,7 +172,7 @@ const struct drm_format_info *__drm_format_info(u32 format)
|
|||
{ .format = DRM_FORMAT_YVYU, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_UYVY, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_VYUY, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_AYUV, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_AYUV, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
};
|
||||
|
||||
unsigned int i;
|
||||
|
|
|
@ -509,7 +509,7 @@ int drm_ioctl_permit(u32 flags, struct drm_file *file_priv)
|
|||
return -EACCES;
|
||||
|
||||
/* MASTER is only for master or control clients */
|
||||
if (unlikely((flags & DRM_MASTER) &&
|
||||
if (unlikely((flags & DRM_MASTER) &&
|
||||
!drm_is_current_master(file_priv) &&
|
||||
!drm_is_control_client(file_priv)))
|
||||
return -EACCES;
|
||||
|
@ -704,7 +704,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
|
|||
*
|
||||
* ##define DRM_IOCTL_MY_DRIVER_OPERATION \
|
||||
* DRM_IOW(DRM_COMMAND_BASE, struct my_driver_operation)
|
||||
*
|
||||
*
|
||||
* DRM driver private IOCTL must be in the range from DRM_COMMAND_BASE to
|
||||
* DRM_COMMAND_END. Finally you need an array of &struct drm_ioctl_desc to wire
|
||||
* up the handlers and set the access rights::
|
||||
|
@ -848,7 +848,7 @@ long drm_ioctl(struct file *filp,
|
|||
if (kdata != stack_kdata)
|
||||
kfree(kdata);
|
||||
if (retcode)
|
||||
DRM_DEBUG("ret = %d\n", retcode);
|
||||
DRM_DEBUG("pid=%d, ret = %d\n", task_pid_nr(current), retcode);
|
||||
return retcode;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_ioctl);
|
||||
|
|
|
@ -149,3 +149,16 @@ void drm_legacy_ioremapfree(struct drm_local_map *map, struct drm_device *dev)
|
|||
iounmap(map->handle);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_legacy_ioremapfree);
|
||||
|
||||
u64 drm_get_max_iomem(void)
|
||||
{
|
||||
struct resource *tmp;
|
||||
u64 max_iomem = 0;
|
||||
|
||||
for (tmp = iomem_resource.child; tmp; tmp = tmp->sibling) {
|
||||
max_iomem = max(max_iomem, tmp->end);
|
||||
}
|
||||
|
||||
return max_iomem;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_get_max_iomem);
|
||||
|
|
|
@ -498,8 +498,9 @@ int mipi_dsi_shutdown_peripheral(struct mipi_dsi_device *dsi)
|
|||
.tx_buf = (u8 [2]) { 0, 0 },
|
||||
.tx_len = 2,
|
||||
};
|
||||
int ret = mipi_dsi_device_transfer(dsi, &msg);
|
||||
|
||||
return mipi_dsi_device_transfer(dsi, &msg);
|
||||
return (ret < 0) ? ret : 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_dsi_shutdown_peripheral);
|
||||
|
||||
|
@ -517,8 +518,9 @@ int mipi_dsi_turn_on_peripheral(struct mipi_dsi_device *dsi)
|
|||
.tx_buf = (u8 [2]) { 0, 0 },
|
||||
.tx_len = 2,
|
||||
};
|
||||
int ret = mipi_dsi_device_transfer(dsi, &msg);
|
||||
|
||||
return mipi_dsi_device_transfer(dsi, &msg);
|
||||
return (ret < 0) ? ret : 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_dsi_turn_on_peripheral);
|
||||
|
||||
|
@ -541,8 +543,9 @@ int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
|
|||
.tx_len = sizeof(tx),
|
||||
.tx_buf = tx,
|
||||
};
|
||||
int ret = mipi_dsi_device_transfer(dsi, &msg);
|
||||
|
||||
return mipi_dsi_device_transfer(dsi, &msg);
|
||||
return (ret < 0) ? ret : 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size);
|
||||
|
||||
|
|
|
@ -833,7 +833,7 @@ EXPORT_SYMBOL(drm_mode_get_hv_timing);
|
|||
*/
|
||||
void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
|
||||
{
|
||||
if ((p == NULL) || ((p->type & DRM_MODE_TYPE_CRTC_C) == DRM_MODE_TYPE_BUILTIN))
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
p->crtc_clock = p->clock;
|
||||
|
@ -1023,19 +1023,18 @@ bool drm_mode_equal_no_clocks_no_stereo(const struct drm_display_mode *mode1,
|
|||
}
|
||||
EXPORT_SYMBOL(drm_mode_equal_no_clocks_no_stereo);
|
||||
|
||||
/**
|
||||
* drm_mode_validate_basic - make sure the mode is somewhat sane
|
||||
* @mode: mode to check
|
||||
*
|
||||
* Check that the mode timings are at least somewhat reasonable.
|
||||
* Any hardware specific limits are left up for each driver to check.
|
||||
*
|
||||
* Returns:
|
||||
* The mode status
|
||||
*/
|
||||
enum drm_mode_status
|
||||
static enum drm_mode_status
|
||||
drm_mode_validate_basic(const struct drm_display_mode *mode)
|
||||
{
|
||||
if (mode->type & ~DRM_MODE_TYPE_ALL)
|
||||
return MODE_BAD;
|
||||
|
||||
if (mode->flags & ~DRM_MODE_FLAG_ALL)
|
||||
return MODE_BAD;
|
||||
|
||||
if ((mode->flags & DRM_MODE_FLAG_3D_MASK) > DRM_MODE_FLAG_3D_MAX)
|
||||
return MODE_BAD;
|
||||
|
||||
if (mode->clock == 0)
|
||||
return MODE_CLOCK_LOW;
|
||||
|
||||
|
@ -1053,7 +1052,35 @@ drm_mode_validate_basic(const struct drm_display_mode *mode)
|
|||
|
||||
return MODE_OK;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_mode_validate_basic);
|
||||
|
||||
/**
|
||||
* drm_mode_validate_driver - make sure the mode is somewhat sane
|
||||
* @dev: drm device
|
||||
* @mode: mode to check
|
||||
*
|
||||
* First do basic validation on the mode, and then allow the driver
|
||||
* to check for device/driver specific limitations via the optional
|
||||
* &drm_mode_config_helper_funcs.mode_valid hook.
|
||||
*
|
||||
* Returns:
|
||||
* The mode status
|
||||
*/
|
||||
enum drm_mode_status
|
||||
drm_mode_validate_driver(struct drm_device *dev,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
enum drm_mode_status status;
|
||||
|
||||
status = drm_mode_validate_basic(mode);
|
||||
if (status != MODE_OK)
|
||||
return status;
|
||||
|
||||
if (dev->mode_config.funcs->mode_valid)
|
||||
return dev->mode_config.funcs->mode_valid(dev, mode);
|
||||
else
|
||||
return MODE_OK;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_mode_validate_driver);
|
||||
|
||||
/**
|
||||
* drm_mode_validate_size - make sure modes adhere to size constraints
|
||||
|
@ -1555,6 +1582,7 @@ void drm_mode_convert_to_umode(struct drm_mode_modeinfo *out,
|
|||
|
||||
/**
|
||||
* drm_crtc_convert_umode - convert a modeinfo into a drm_display_mode
|
||||
* @dev: drm device
|
||||
* @out: drm_display_mode to return to the user
|
||||
* @in: drm_mode_modeinfo to use
|
||||
*
|
||||
|
@ -1564,7 +1592,8 @@ void drm_mode_convert_to_umode(struct drm_mode_modeinfo *out,
|
|||
* Returns:
|
||||
* Zero on success, negative errno on failure.
|
||||
*/
|
||||
int drm_mode_convert_umode(struct drm_display_mode *out,
|
||||
int drm_mode_convert_umode(struct drm_device *dev,
|
||||
struct drm_display_mode *out,
|
||||
const struct drm_mode_modeinfo *in)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
|
@ -1574,9 +1603,6 @@ int drm_mode_convert_umode(struct drm_display_mode *out,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if ((in->flags & DRM_MODE_FLAG_3D_MASK) > DRM_MODE_FLAG_3D_MAX)
|
||||
goto out;
|
||||
|
||||
out->clock = in->clock;
|
||||
out->hdisplay = in->hdisplay;
|
||||
out->hsync_start = in->hsync_start;
|
||||
|
@ -1594,7 +1620,7 @@ int drm_mode_convert_umode(struct drm_display_mode *out,
|
|||
strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN);
|
||||
out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
|
||||
|
||||
out->status = drm_mode_validate_basic(out);
|
||||
out->status = drm_mode_validate_driver(dev, out);
|
||||
if (out->status != MODE_OK)
|
||||
goto out;
|
||||
|
||||
|
|
|
@ -173,6 +173,10 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
|
|||
unsigned int format_modifier_count = 0;
|
||||
int ret;
|
||||
|
||||
/* plane index is used with 32bit bitmasks */
|
||||
if (WARN_ON(config->num_total_plane >= 32))
|
||||
return -EINVAL;
|
||||
|
||||
ret = drm_mode_object_add(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
@ -73,6 +73,9 @@
|
|||
* Drivers should detect this situation and return back the gem object
|
||||
* from the dma-buf private. Prime will do this automatically for drivers that
|
||||
* use the drm_gem_prime_{import,export} helpers.
|
||||
*
|
||||
* GEM struct &dma_buf_ops symbols are now exported. They can be resued by
|
||||
* drivers which implement GEM interface.
|
||||
*/
|
||||
|
||||
struct drm_prime_member {
|
||||
|
@ -180,9 +183,20 @@ static int drm_prime_lookup_buf_handle(struct drm_prime_file_private *prime_fpri
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int drm_gem_map_attach(struct dma_buf *dma_buf,
|
||||
struct device *target_dev,
|
||||
struct dma_buf_attachment *attach)
|
||||
/**
|
||||
* drm_gem_map_attach - dma_buf attach implementation for GEM
|
||||
* @dma_buf: buffer to attach device to
|
||||
* @target_dev: not used
|
||||
* @attach: buffer attachment data
|
||||
*
|
||||
* Allocates &drm_prime_attachment and calls &drm_driver.gem_prime_pin for
|
||||
* device specific attachment. This can be used as the &dma_buf_ops.attach
|
||||
* callback.
|
||||
*
|
||||
* Returns 0 on success, negative error code on failure.
|
||||
*/
|
||||
int drm_gem_map_attach(struct dma_buf *dma_buf, struct device *target_dev,
|
||||
struct dma_buf_attachment *attach)
|
||||
{
|
||||
struct drm_prime_attachment *prime_attach;
|
||||
struct drm_gem_object *obj = dma_buf->priv;
|
||||
|
@ -200,9 +214,18 @@ static int drm_gem_map_attach(struct dma_buf *dma_buf,
|
|||
|
||||
return dev->driver->gem_prime_pin(obj);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_map_attach);
|
||||
|
||||
static void drm_gem_map_detach(struct dma_buf *dma_buf,
|
||||
struct dma_buf_attachment *attach)
|
||||
/**
|
||||
* drm_gem_map_detach - dma_buf detach implementation for GEM
|
||||
* @dma_buf: buffer to detach from
|
||||
* @attach: attachment to be detached
|
||||
*
|
||||
* Cleans up &dma_buf_attachment. This can be used as the &dma_buf_ops.detach
|
||||
* callback.
|
||||
*/
|
||||
void drm_gem_map_detach(struct dma_buf *dma_buf,
|
||||
struct dma_buf_attachment *attach)
|
||||
{
|
||||
struct drm_prime_attachment *prime_attach = attach->priv;
|
||||
struct drm_gem_object *obj = dma_buf->priv;
|
||||
|
@ -228,6 +251,7 @@ static void drm_gem_map_detach(struct dma_buf *dma_buf,
|
|||
kfree(prime_attach);
|
||||
attach->priv = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_map_detach);
|
||||
|
||||
void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpriv,
|
||||
struct dma_buf *dma_buf)
|
||||
|
@ -254,8 +278,20 @@ void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpr
|
|||
}
|
||||
}
|
||||
|
||||
static struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach,
|
||||
enum dma_data_direction dir)
|
||||
/**
|
||||
* drm_gem_map_dma_buf - map_dma_buf implementation for GEM
|
||||
* @attach: attachment whose scatterlist is to be returned
|
||||
* @dir: direction of DMA transfer
|
||||
*
|
||||
* Calls &drm_driver.gem_prime_get_sg_table and then maps the scatterlist. This
|
||||
* can be used as the &dma_buf_ops.map_dma_buf callback.
|
||||
*
|
||||
* Returns sg_table containing the scatterlist to be returned; returns ERR_PTR
|
||||
* on error. May return -EINTR if it is interrupted by a signal.
|
||||
*/
|
||||
|
||||
struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
struct drm_prime_attachment *prime_attach = attach->priv;
|
||||
struct drm_gem_object *obj = attach->dmabuf->priv;
|
||||
|
@ -291,13 +327,21 @@ static struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach,
|
|||
|
||||
return sgt;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_map_dma_buf);
|
||||
|
||||
static void drm_gem_unmap_dma_buf(struct dma_buf_attachment *attach,
|
||||
struct sg_table *sgt,
|
||||
enum dma_data_direction dir)
|
||||
/**
|
||||
* drm_gem_unmap_dma_buf - unmap_dma_buf implementation for GEM
|
||||
*
|
||||
* Not implemented. The unmap is done at drm_gem_map_detach(). This can be
|
||||
* used as the &dma_buf_ops.unmap_dma_buf callback.
|
||||
*/
|
||||
void drm_gem_unmap_dma_buf(struct dma_buf_attachment *attach,
|
||||
struct sg_table *sgt,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
/* nothing to be done here */
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_unmap_dma_buf);
|
||||
|
||||
/**
|
||||
* drm_gem_dmabuf_export - dma_buf export implementation for GEM
|
||||
|
@ -348,47 +392,99 @@ void drm_gem_dmabuf_release(struct dma_buf *dma_buf)
|
|||
}
|
||||
EXPORT_SYMBOL(drm_gem_dmabuf_release);
|
||||
|
||||
static void *drm_gem_dmabuf_vmap(struct dma_buf *dma_buf)
|
||||
/**
|
||||
* drm_gem_dmabuf_vmap - dma_buf vmap implementation for GEM
|
||||
* @dma_buf: buffer to be mapped
|
||||
*
|
||||
* Sets up a kernel virtual mapping. This can be used as the &dma_buf_ops.vmap
|
||||
* callback.
|
||||
*
|
||||
* Returns the kernel virtual address.
|
||||
*/
|
||||
void *drm_gem_dmabuf_vmap(struct dma_buf *dma_buf)
|
||||
{
|
||||
struct drm_gem_object *obj = dma_buf->priv;
|
||||
struct drm_device *dev = obj->dev;
|
||||
|
||||
return dev->driver->gem_prime_vmap(obj);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_dmabuf_vmap);
|
||||
|
||||
static void drm_gem_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr)
|
||||
/**
|
||||
* drm_gem_dmabuf_vunmap - dma_buf vunmap implementation for GEM
|
||||
* @dma_buf: buffer to be unmapped
|
||||
* @vaddr: the virtual address of the buffer
|
||||
*
|
||||
* Releases a kernel virtual mapping. This can be used as the
|
||||
* &dma_buf_ops.vunmap callback.
|
||||
*/
|
||||
void drm_gem_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr)
|
||||
{
|
||||
struct drm_gem_object *obj = dma_buf->priv;
|
||||
struct drm_device *dev = obj->dev;
|
||||
|
||||
dev->driver->gem_prime_vunmap(obj, vaddr);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_dmabuf_vunmap);
|
||||
|
||||
static void *drm_gem_dmabuf_kmap_atomic(struct dma_buf *dma_buf,
|
||||
unsigned long page_num)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void drm_gem_dmabuf_kunmap_atomic(struct dma_buf *dma_buf,
|
||||
unsigned long page_num, void *addr)
|
||||
{
|
||||
|
||||
}
|
||||
static void *drm_gem_dmabuf_kmap(struct dma_buf *dma_buf,
|
||||
/**
|
||||
* drm_gem_dmabuf_kmap_atomic - map_atomic implementation for GEM
|
||||
*
|
||||
* Not implemented. This can be used as the &dma_buf_ops.map_atomic callback.
|
||||
*/
|
||||
void *drm_gem_dmabuf_kmap_atomic(struct dma_buf *dma_buf,
|
||||
unsigned long page_num)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_dmabuf_kmap_atomic);
|
||||
|
||||
static void drm_gem_dmabuf_kunmap(struct dma_buf *dma_buf,
|
||||
/**
|
||||
* drm_gem_dmabuf_kunmap_atomic - unmap_atomic implementation for GEM
|
||||
*
|
||||
* Not implemented. This can be used as the &dma_buf_ops.unmap_atomic callback.
|
||||
*/
|
||||
void drm_gem_dmabuf_kunmap_atomic(struct dma_buf *dma_buf,
|
||||
unsigned long page_num, void *addr)
|
||||
{
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_dmabuf_kunmap_atomic);
|
||||
|
||||
static int drm_gem_dmabuf_mmap(struct dma_buf *dma_buf,
|
||||
struct vm_area_struct *vma)
|
||||
/**
|
||||
* drm_gem_dmabuf_kmap - map implementation for GEM
|
||||
*
|
||||
* Not implemented. This can be used as the &dma_buf_ops.map callback.
|
||||
*/
|
||||
void *drm_gem_dmabuf_kmap(struct dma_buf *dma_buf, unsigned long page_num)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_dmabuf_kmap);
|
||||
|
||||
/**
|
||||
* drm_gem_dmabuf_kunmap - unmap implementation for GEM
|
||||
*
|
||||
* Not implemented. This can be used as the &dma_buf_ops.unmap callback.
|
||||
*/
|
||||
void drm_gem_dmabuf_kunmap(struct dma_buf *dma_buf, unsigned long page_num,
|
||||
void *addr)
|
||||
{
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_dmabuf_kunmap);
|
||||
|
||||
/**
|
||||
* drm_gem_dmabuf_mmap - dma_buf mmap implementation for GEM
|
||||
* @dma_buf: buffer to be mapped
|
||||
* @vma: virtual address range
|
||||
*
|
||||
* Provides memory mapping for the buffer. This can be used as the
|
||||
* &dma_buf_ops.mmap callback.
|
||||
*
|
||||
* Returns 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int drm_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma)
|
||||
{
|
||||
struct drm_gem_object *obj = dma_buf->priv;
|
||||
struct drm_device *dev = obj->dev;
|
||||
|
@ -398,6 +494,7 @@ static int drm_gem_dmabuf_mmap(struct dma_buf *dma_buf,
|
|||
|
||||
return dev->driver->gem_prime_mmap(obj, vma);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_dmabuf_mmap);
|
||||
|
||||
static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = {
|
||||
.attach = drm_gem_map_attach,
|
||||
|
|
|
@ -499,7 +499,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
|
|||
|
||||
list_for_each_entry(mode, &connector->modes, head) {
|
||||
if (mode->status == MODE_OK)
|
||||
mode->status = drm_mode_validate_basic(mode);
|
||||
mode->status = drm_mode_validate_driver(dev, mode);
|
||||
|
||||
if (mode->status == MODE_OK)
|
||||
mode->status = drm_mode_validate_size(mode, maxX, maxY);
|
||||
|
|
|
@ -100,8 +100,9 @@ static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane,
|
|||
if (!crtc_state->enable)
|
||||
return 0; /* nothing to check when disabling or disabled */
|
||||
|
||||
clip.x2 = crtc_state->adjusted_mode.hdisplay;
|
||||
clip.y2 = crtc_state->adjusted_mode.vdisplay;
|
||||
if (crtc_state->enable)
|
||||
drm_mode_get_hv_timing(&crtc_state->mode,
|
||||
&clip.x2, &clip.y2);
|
||||
|
||||
ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
|
||||
&clip,
|
||||
|
|
|
@ -179,18 +179,6 @@ static const u8 filter_cr_horiz_tap4[] = {
|
|||
70, 59, 48, 37, 27, 19, 11, 5,
|
||||
};
|
||||
|
||||
static inline bool is_alpha_format(unsigned int pixel_format)
|
||||
{
|
||||
switch (pixel_format) {
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
case DRM_FORMAT_ARGB1555:
|
||||
case DRM_FORMAT_ARGB4444:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u32 vp_reg_read(struct mixer_context *ctx, u32 reg_id)
|
||||
{
|
||||
return readl(ctx->vp_regs + reg_id);
|
||||
|
@ -625,7 +613,7 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
|
|||
mixer_reg_write(ctx, MXR_GRAPHIC_BASE(win), dma_addr);
|
||||
|
||||
mixer_cfg_layer(ctx, win, priority, true);
|
||||
mixer_cfg_gfx_blend(ctx, win, is_alpha_format(fb->format->format));
|
||||
mixer_cfg_gfx_blend(ctx, win, fb->format->has_alpha);
|
||||
|
||||
/* layer update mandatory for mixer 16.0.33.0 */
|
||||
if (ctx->mxr_ver == MXR_VER_16_0_33_0 ||
|
||||
|
|
|
@ -326,8 +326,7 @@ sil164_encoder_destroy(struct drm_encoder *encoder)
|
|||
{
|
||||
struct sil164_priv *priv = to_sil164_priv(encoder);
|
||||
|
||||
if (priv->duallink_slave)
|
||||
i2c_unregister_device(priv->duallink_slave);
|
||||
i2c_unregister_device(priv->duallink_slave);
|
||||
|
||||
kfree(priv);
|
||||
drm_i2c_encoder_destroy(encoder);
|
||||
|
|
|
@ -1600,8 +1600,7 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
|
|||
/* if encoder_init fails, the encoder slave is never registered,
|
||||
* so cleanup here:
|
||||
*/
|
||||
if (priv->cec)
|
||||
i2c_unregister_device(priv->cec);
|
||||
i2c_unregister_device(priv->cec);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
|
|
|
@ -128,14 +128,6 @@ int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_
|
|||
if (!intel_state->base.crtc && !old_plane_state->base.crtc)
|
||||
return 0;
|
||||
|
||||
/* Clip all planes to CRTC size, or 0x0 if CRTC is disabled */
|
||||
intel_state->clip.x1 = 0;
|
||||
intel_state->clip.y1 = 0;
|
||||
intel_state->clip.x2 =
|
||||
crtc_state->base.enable ? crtc_state->pipe_src_w : 0;
|
||||
intel_state->clip.y2 =
|
||||
crtc_state->base.enable ? crtc_state->pipe_src_h : 0;
|
||||
|
||||
if (state->fb && drm_rotation_90_or_270(state->rotation)) {
|
||||
struct drm_format_name_buf format_name;
|
||||
|
||||
|
|
|
@ -304,9 +304,6 @@ intel_crt_mode_valid(struct drm_connector *connector,
|
|||
int max_dotclk = dev_priv->max_dotclk_freq;
|
||||
int max_clock;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
||||
return MODE_NO_DBLESCAN;
|
||||
|
||||
if (mode->clock < 25000)
|
||||
return MODE_CLOCK_LOW;
|
||||
|
||||
|
|
|
@ -6404,9 +6404,18 @@ static int intel_crtc_compute_config(struct intel_crtc *crtc,
|
|||
* - LVDS dual channel mode
|
||||
* - Double wide pipe
|
||||
*/
|
||||
if ((intel_crtc_has_type(pipe_config, INTEL_OUTPUT_LVDS) &&
|
||||
intel_is_dual_link_lvds(dev)) || pipe_config->double_wide)
|
||||
pipe_config->pipe_src_w &= ~1;
|
||||
if (pipe_config->pipe_src_w & 1) {
|
||||
if (pipe_config->double_wide) {
|
||||
DRM_DEBUG_KMS("Odd pipe source width not supported with double wide pipe\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (intel_crtc_has_type(pipe_config, INTEL_OUTPUT_LVDS) &&
|
||||
intel_is_dual_link_lvds(dev)) {
|
||||
DRM_DEBUG_KMS("Odd pipe source width not supported with dual link LVDS\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cantiga+ cannot handle modes with a hsync front porch of 0.
|
||||
* WaPruneModeWithIncorrectHsyncOffset:ctg,elk,ilk,snb,ivb,vlv,hsw.
|
||||
|
@ -9343,13 +9352,18 @@ static int intel_check_cursor(struct intel_crtc_state *crtc_state,
|
|||
struct intel_plane_state *plane_state)
|
||||
{
|
||||
const struct drm_framebuffer *fb = plane_state->base.fb;
|
||||
struct drm_rect clip = {};
|
||||
int src_x, src_y;
|
||||
u32 offset;
|
||||
int ret;
|
||||
|
||||
if (crtc_state->base.enable)
|
||||
drm_mode_get_hv_timing(&crtc_state->base.mode,
|
||||
&clip.x2, &clip.y2);
|
||||
|
||||
ret = drm_atomic_helper_check_plane_state(&plane_state->base,
|
||||
&crtc_state->base,
|
||||
&plane_state->clip,
|
||||
&clip,
|
||||
DRM_PLANE_HELPER_NO_SCALING,
|
||||
DRM_PLANE_HELPER_NO_SCALING,
|
||||
true, true);
|
||||
|
@ -12784,6 +12798,7 @@ intel_check_primary_plane(struct intel_plane *plane,
|
|||
int min_scale = DRM_PLANE_HELPER_NO_SCALING;
|
||||
int max_scale = DRM_PLANE_HELPER_NO_SCALING;
|
||||
bool can_position = false;
|
||||
struct drm_rect clip = {};
|
||||
int ret;
|
||||
|
||||
if (INTEL_GEN(dev_priv) >= 9) {
|
||||
|
@ -12795,9 +12810,13 @@ intel_check_primary_plane(struct intel_plane *plane,
|
|||
can_position = true;
|
||||
}
|
||||
|
||||
if (crtc_state->base.enable)
|
||||
drm_mode_get_hv_timing(&crtc_state->base.mode,
|
||||
&clip.x2, &clip.y2);
|
||||
|
||||
ret = drm_atomic_helper_check_plane_state(&state->base,
|
||||
&crtc_state->base,
|
||||
&state->clip,
|
||||
&clip,
|
||||
min_scale, max_scale,
|
||||
can_position, true);
|
||||
if (ret)
|
||||
|
@ -14097,10 +14116,37 @@ static void intel_atomic_state_free(struct drm_atomic_state *state)
|
|||
kfree(state);
|
||||
}
|
||||
|
||||
static enum drm_mode_status
|
||||
intel_mode_valid(struct drm_device *dev,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
if (mode->vscan > 1)
|
||||
return MODE_NO_VSCAN;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
||||
return MODE_NO_DBLESCAN;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_HSKEW)
|
||||
return MODE_H_ILLEGAL;
|
||||
|
||||
if (mode->flags & (DRM_MODE_FLAG_CSYNC |
|
||||
DRM_MODE_FLAG_NCSYNC |
|
||||
DRM_MODE_FLAG_PCSYNC))
|
||||
return MODE_HSYNC;
|
||||
|
||||
if (mode->flags & (DRM_MODE_FLAG_BCAST |
|
||||
DRM_MODE_FLAG_PIXMUX |
|
||||
DRM_MODE_FLAG_CLKDIV2))
|
||||
return MODE_BAD;
|
||||
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_funcs intel_mode_funcs = {
|
||||
.fb_create = intel_user_framebuffer_create,
|
||||
.get_format_info = intel_get_format_info,
|
||||
.output_poll_changed = intel_fbdev_output_poll_changed,
|
||||
.mode_valid = intel_mode_valid,
|
||||
.atomic_check = intel_atomic_check,
|
||||
.atomic_commit = intel_atomic_commit,
|
||||
.atomic_state_alloc = intel_atomic_state_alloc,
|
||||
|
|
|
@ -406,7 +406,6 @@ struct intel_atomic_state {
|
|||
|
||||
struct intel_plane_state {
|
||||
struct drm_plane_state base;
|
||||
struct drm_rect clip;
|
||||
struct i915_vma *vma;
|
||||
|
||||
struct {
|
||||
|
|
|
@ -1266,11 +1266,6 @@ intel_dsi_mode_valid(struct drm_connector *connector,
|
|||
|
||||
DRM_DEBUG_KMS("\n");
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_DBLSCAN) {
|
||||
DRM_DEBUG_KMS("MODE_NO_DBLESCAN\n");
|
||||
return MODE_NO_DBLESCAN;
|
||||
}
|
||||
|
||||
if (fixed_mode) {
|
||||
if (mode->hdisplay > fixed_mode->hdisplay)
|
||||
return MODE_PANEL;
|
||||
|
|
|
@ -219,9 +219,6 @@ intel_dvo_mode_valid(struct drm_connector *connector,
|
|||
int max_dotclk = to_i915(connector->dev)->max_dotclk_freq;
|
||||
int target_clock = mode->clock;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
||||
return MODE_NO_DBLESCAN;
|
||||
|
||||
/* XXX: Validate clock range */
|
||||
|
||||
if (fixed_mode) {
|
||||
|
|
|
@ -799,8 +799,7 @@ static bool intel_fbc_can_activate(struct intel_crtc *crtc)
|
|||
return false;
|
||||
}
|
||||
|
||||
if ((cache->crtc.mode_flags & DRM_MODE_FLAG_INTERLACE) ||
|
||||
(cache->crtc.mode_flags & DRM_MODE_FLAG_DBLSCAN)) {
|
||||
if (cache->crtc.mode_flags & DRM_MODE_FLAG_INTERLACE) {
|
||||
fbc->no_fbc_reason = "incompatible mode";
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1314,9 +1314,6 @@ intel_hdmi_mode_valid(struct drm_connector *connector,
|
|||
bool force_dvi =
|
||||
READ_ONCE(to_intel_digital_connector_state(connector->state)->force_audio) == HDMI_AUDIO_OFF_DVI;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
||||
return MODE_NO_DBLESCAN;
|
||||
|
||||
clock = mode->clock;
|
||||
|
||||
if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING)
|
||||
|
|
|
@ -1607,9 +1607,6 @@ intel_sdvo_mode_valid(struct drm_connector *connector,
|
|||
struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
|
||||
int max_dotclk = to_i915(connector->dev)->max_dotclk_freq;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
||||
return MODE_NO_DBLESCAN;
|
||||
|
||||
if (intel_sdvo->pixel_clock_min > mode->clock)
|
||||
return MODE_CLOCK_LOW;
|
||||
|
||||
|
|
|
@ -864,7 +864,7 @@ intel_check_sprite_plane(struct intel_plane *plane,
|
|||
uint32_t src_x, src_y, src_w, src_h;
|
||||
struct drm_rect *src = &state->base.src;
|
||||
struct drm_rect *dst = &state->base.dst;
|
||||
const struct drm_rect *clip = &state->clip;
|
||||
struct drm_rect clip = {};
|
||||
int max_stride = INTEL_GEN(dev_priv) >= 9 ? 32768 : 16384;
|
||||
int hscale, vscale;
|
||||
int max_scale, min_scale;
|
||||
|
@ -923,7 +923,11 @@ intel_check_sprite_plane(struct intel_plane *plane,
|
|||
vscale = drm_rect_calc_vscale_relaxed(src, dst, min_scale, max_scale);
|
||||
BUG_ON(vscale < 0);
|
||||
|
||||
state->base.visible = drm_rect_clip_scaled(src, dst, clip, hscale, vscale);
|
||||
if (crtc_state->base.enable)
|
||||
drm_mode_get_hv_timing(&crtc_state->base.mode,
|
||||
&clip.x2, &clip.y2);
|
||||
|
||||
state->base.visible = drm_rect_clip_scaled(src, dst, &clip, hscale, vscale);
|
||||
|
||||
crtc_x = dst->x1;
|
||||
crtc_y = dst->y1;
|
||||
|
|
|
@ -351,7 +351,7 @@ static int ipu_plane_atomic_check(struct drm_plane *plane,
|
|||
struct drm_framebuffer *old_fb = old_state->fb;
|
||||
unsigned long eba, ubo, vbo, old_ubo, old_vbo, alpha_eba;
|
||||
bool can_position = (plane->type == DRM_PLANE_TYPE_OVERLAY);
|
||||
struct drm_rect clip;
|
||||
struct drm_rect clip = {};
|
||||
int hsub, vsub;
|
||||
int ret;
|
||||
|
||||
|
@ -367,10 +367,10 @@ static int ipu_plane_atomic_check(struct drm_plane *plane,
|
|||
if (WARN_ON(!crtc_state))
|
||||
return -EINVAL;
|
||||
|
||||
clip.x1 = 0;
|
||||
clip.y1 = 0;
|
||||
clip.x2 = crtc_state->adjusted_mode.hdisplay;
|
||||
clip.y2 = crtc_state->adjusted_mode.vdisplay;
|
||||
if (crtc_state->enable)
|
||||
drm_mode_get_hv_timing(&crtc_state->mode,
|
||||
&clip.x2, &clip.y2);
|
||||
|
||||
ret = drm_atomic_helper_check_plane_state(state, crtc_state, &clip,
|
||||
DRM_PLANE_HELPER_NO_SCALING,
|
||||
DRM_PLANE_HELPER_NO_SCALING,
|
||||
|
|
|
@ -108,8 +108,9 @@ static int mtk_plane_atomic_check(struct drm_plane *plane,
|
|||
if (IS_ERR(crtc_state))
|
||||
return PTR_ERR(crtc_state);
|
||||
|
||||
clip.x2 = crtc_state->mode.hdisplay;
|
||||
clip.y2 = crtc_state->mode.vdisplay;
|
||||
if (crtc_state->enable)
|
||||
drm_mode_get_hv_timing(&crtc_state->mode,
|
||||
&clip.x2, &clip.y2);
|
||||
|
||||
return drm_atomic_helper_check_plane_state(state, crtc_state, &clip,
|
||||
DRM_PLANE_HELPER_NO_SCALING,
|
||||
|
|
|
@ -58,8 +58,9 @@ static int meson_plane_atomic_check(struct drm_plane *plane,
|
|||
if (IS_ERR(crtc_state))
|
||||
return PTR_ERR(crtc_state);
|
||||
|
||||
clip.x2 = crtc_state->mode.hdisplay;
|
||||
clip.y2 = crtc_state->mode.vdisplay;
|
||||
if (crtc_state->enable)
|
||||
drm_mode_get_hv_timing(&crtc_state->mode,
|
||||
&clip.x2, &clip.y2);
|
||||
|
||||
return drm_atomic_helper_check_plane_state(state, crtc_state, &clip,
|
||||
DRM_PLANE_HELPER_NO_SCALING,
|
||||
|
|
|
@ -1620,8 +1620,8 @@ static int mga_vga_mode_valid(struct drm_connector *connector,
|
|||
return MODE_VIRTUAL_X;
|
||||
if (mode->vdisplay > 1024)
|
||||
return MODE_VIRTUAL_Y;
|
||||
if (mga_vga_calculate_mode_bandwidth(mode,
|
||||
bpp > (31877 * 1024)))
|
||||
if (mga_vga_calculate_mode_bandwidth(mode, bpp) >
|
||||
(31877 * 1024))
|
||||
return MODE_BANDWIDTH;
|
||||
} else if (mdev->type == G200_EV &&
|
||||
(mga_vga_calculate_mode_bandwidth(mode, bpp)
|
||||
|
|
|
@ -286,7 +286,7 @@ static int mdp5_plane_atomic_check_with_state(struct drm_crtc_state *crtc_state,
|
|||
uint32_t max_width, max_height;
|
||||
bool out_of_bounds = false;
|
||||
uint32_t caps = 0;
|
||||
struct drm_rect clip;
|
||||
struct drm_rect clip = {};
|
||||
int min_scale, max_scale;
|
||||
int ret;
|
||||
|
||||
|
@ -320,13 +320,13 @@ static int mdp5_plane_atomic_check_with_state(struct drm_crtc_state *crtc_state,
|
|||
return -ERANGE;
|
||||
}
|
||||
|
||||
clip.x1 = 0;
|
||||
clip.y1 = 0;
|
||||
clip.x2 = crtc_state->adjusted_mode.hdisplay;
|
||||
clip.y2 = crtc_state->adjusted_mode.vdisplay;
|
||||
min_scale = FRAC_16_16(1, 8);
|
||||
max_scale = FRAC_16_16(8, 1);
|
||||
|
||||
if (crtc_state->enable)
|
||||
drm_mode_get_hv_timing(&crtc_state->mode,
|
||||
&clip.x2, &clip.y2);
|
||||
|
||||
ret = drm_atomic_helper_check_plane_state(state, crtc_state, &clip,
|
||||
min_scale, max_scale,
|
||||
true, true);
|
||||
|
@ -471,7 +471,7 @@ static int mdp5_plane_atomic_async_check(struct drm_plane *plane,
|
|||
{
|
||||
struct mdp5_plane_state *mdp5_state = to_mdp5_plane_state(state);
|
||||
struct drm_crtc_state *crtc_state;
|
||||
struct drm_rect clip;
|
||||
struct drm_rect clip = {};
|
||||
int min_scale, max_scale;
|
||||
int ret;
|
||||
|
||||
|
@ -499,13 +499,13 @@ static int mdp5_plane_atomic_async_check(struct drm_plane *plane,
|
|||
plane->state->fb != state->fb)
|
||||
return -EINVAL;
|
||||
|
||||
clip.x1 = 0;
|
||||
clip.y1 = 0;
|
||||
clip.x2 = crtc_state->adjusted_mode.hdisplay;
|
||||
clip.y2 = crtc_state->adjusted_mode.vdisplay;
|
||||
min_scale = FRAC_16_16(1, 8);
|
||||
max_scale = FRAC_16_16(8, 1);
|
||||
|
||||
if (crtc_state->enable)
|
||||
drm_mode_get_hv_timing(&crtc_state->mode,
|
||||
&clip.x2, &clip.y2);
|
||||
|
||||
ret = drm_atomic_helper_check_plane_state(state, crtc_state, &clip,
|
||||
min_scale, max_scale,
|
||||
true, true);
|
||||
|
|
|
@ -232,8 +232,6 @@ struct nv50_wndw_atom {
|
|||
struct drm_plane_state state;
|
||||
u8 interval;
|
||||
|
||||
struct drm_rect clip;
|
||||
|
||||
struct {
|
||||
u32 handle;
|
||||
u16 offset:12;
|
||||
|
@ -848,10 +846,6 @@ nv50_wndw_atomic_check_acquire(struct nv50_wndw *wndw,
|
|||
int ret;
|
||||
|
||||
NV_ATOMIC(drm, "%s acquire\n", wndw->plane.name);
|
||||
asyw->clip.x1 = 0;
|
||||
asyw->clip.y1 = 0;
|
||||
asyw->clip.x2 = asyh->state.mode.hdisplay;
|
||||
asyw->clip.y2 = asyh->state.mode.vdisplay;
|
||||
|
||||
asyw->image.w = fb->base.width;
|
||||
asyw->image.h = fb->base.height;
|
||||
|
@ -1149,10 +1143,15 @@ static int
|
|||
nv50_curs_acquire(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw,
|
||||
struct nv50_head_atom *asyh)
|
||||
{
|
||||
struct drm_rect clip = {};
|
||||
int ret;
|
||||
|
||||
if (asyh->state.enable)
|
||||
drm_mode_get_hv_timing(&asyh->state.mode,
|
||||
&clip.x2, &clip.y2);
|
||||
|
||||
ret = drm_atomic_helper_check_plane_state(&asyw->state, &asyh->state,
|
||||
&asyw->clip,
|
||||
&clip,
|
||||
DRM_PLANE_HELPER_NO_SCALING,
|
||||
DRM_PLANE_HELPER_NO_SCALING,
|
||||
true, true);
|
||||
|
@ -1436,13 +1435,18 @@ nv50_base_acquire(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw,
|
|||
struct nv50_head_atom *asyh)
|
||||
{
|
||||
const struct drm_framebuffer *fb = asyw->state.fb;
|
||||
struct drm_rect clip = {};
|
||||
int ret;
|
||||
|
||||
if (!fb->format->depth)
|
||||
return -EINVAL;
|
||||
|
||||
if (asyh->state.enable)
|
||||
drm_mode_get_hv_timing(&asyh->state.mode,
|
||||
&clip.x2, &clip.y2);
|
||||
|
||||
ret = drm_atomic_helper_check_plane_state(&asyw->state, &asyh->state,
|
||||
&asyw->clip,
|
||||
&clip,
|
||||
DRM_PLANE_HELPER_NO_SCALING,
|
||||
DRM_PLANE_HELPER_NO_SCALING,
|
||||
false, true);
|
||||
|
|
|
@ -7,6 +7,16 @@ config DRM_PANEL
|
|||
menu "Display Panels"
|
||||
depends on DRM && DRM_PANEL
|
||||
|
||||
config DRM_PANEL_ARM_VERSATILE
|
||||
tristate "ARM Versatile panel driver"
|
||||
depends on OF
|
||||
depends on MFD_SYSCON
|
||||
select VIDEOMODE_HELPERS
|
||||
help
|
||||
This driver supports the ARM Versatile panels connected to ARM
|
||||
reference designs. The panel is detected using special registers
|
||||
in the Versatile family syscon registers.
|
||||
|
||||
config DRM_PANEL_LVDS
|
||||
tristate "Generic LVDS panel driver"
|
||||
depends on OF
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_DRM_PANEL_ARM_VERSATILE) += panel-arm-versatile.o
|
||||
obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o
|
||||
obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
|
||||
obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o
|
||||
|
|
377
drivers/gpu/drm/panel/panel-arm-versatile.c
Normal file
377
drivers/gpu/drm/panel/panel-arm-versatile.c
Normal file
|
@ -0,0 +1,377 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Panel driver for the ARM Versatile family reference designs from
|
||||
* ARM Limited.
|
||||
*
|
||||
* Author:
|
||||
* Linus Walleij <linus.wallei@linaro.org>
|
||||
*
|
||||
* On the Versatile AB, these panels come mounted on daughterboards
|
||||
* named "IB1" or "IB2" (Interface Board 1 & 2 respectively.) They
|
||||
* are documented in ARM DUI 0225D Appendix C and D. These daughter
|
||||
* boards support TFT display panels.
|
||||
*
|
||||
* - The IB1 is a passive board where the display connector defines a
|
||||
* few wires for encoding the display type for autodetection,
|
||||
* suitable display settings can then be looked up from this setting.
|
||||
* The magic bits can be read out from the system controller.
|
||||
*
|
||||
* - The IB2 is a more complex board intended for GSM phone development
|
||||
* with some logic and a control register, which needs to be accessed
|
||||
* and the board display needs to be turned on explicitly.
|
||||
*
|
||||
* On the Versatile PB, a special CLCD adaptor board is available
|
||||
* supporting the same displays as the Versatile AB, plus one more
|
||||
* Epson QCIF display.
|
||||
*
|
||||
*/
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_panel.h>
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <video/of_videomode.h>
|
||||
#include <video/videomode.h>
|
||||
|
||||
/*
|
||||
* This configuration register in the Versatile and RealView
|
||||
* family is uniformly present but appears more and more
|
||||
* unutilized starting with the RealView series.
|
||||
*/
|
||||
#define SYS_CLCD 0x50
|
||||
|
||||
/* The Versatile can detect the connected panel type */
|
||||
#define SYS_CLCD_CLCDID_MASK (BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12))
|
||||
#define SYS_CLCD_ID_SANYO_3_8 (0x00 << 8)
|
||||
#define SYS_CLCD_ID_SHARP_8_4 (0x01 << 8)
|
||||
#define SYS_CLCD_ID_EPSON_2_2 (0x02 << 8)
|
||||
#define SYS_CLCD_ID_SANYO_2_5 (0x07 << 8)
|
||||
#define SYS_CLCD_ID_VGA (0x1f << 8)
|
||||
|
||||
/* IB2 control register for the Versatile daughterboard */
|
||||
#define IB2_CTRL 0x00
|
||||
#define IB2_CTRL_LCD_SD BIT(1) /* 1 = shut down LCD */
|
||||
#define IB2_CTRL_LCD_BL_ON BIT(0)
|
||||
#define IB2_CTRL_LCD_MASK (BIT(0)|BIT(1))
|
||||
|
||||
/**
|
||||
* struct versatile_panel_type - lookup struct for the supported panels
|
||||
*/
|
||||
struct versatile_panel_type {
|
||||
/**
|
||||
* @name: the name of this panel
|
||||
*/
|
||||
const char *name;
|
||||
/**
|
||||
* @magic: the magic value from the detection register
|
||||
*/
|
||||
u32 magic;
|
||||
/**
|
||||
* @mode: the DRM display mode for this panel
|
||||
*/
|
||||
struct drm_display_mode mode;
|
||||
/**
|
||||
* @bus_flags: the DRM bus flags for this panel e.g. inverted clock
|
||||
*/
|
||||
u32 bus_flags;
|
||||
/**
|
||||
* @width_mm: the panel width in mm
|
||||
*/
|
||||
u32 width_mm;
|
||||
/**
|
||||
* @height_mm: the panel height in mm
|
||||
*/
|
||||
u32 height_mm;
|
||||
/**
|
||||
* @ib2: the panel may be connected on an IB2 daughterboard
|
||||
*/
|
||||
bool ib2;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct versatile_panel - state container for the Versatile panels
|
||||
*/
|
||||
struct versatile_panel {
|
||||
/**
|
||||
* @dev: the container device
|
||||
*/
|
||||
struct device *dev;
|
||||
/**
|
||||
* @panel: the DRM panel instance for this device
|
||||
*/
|
||||
struct drm_panel panel;
|
||||
/**
|
||||
* @panel_type: the Versatile panel type as detected
|
||||
*/
|
||||
const struct versatile_panel_type *panel_type;
|
||||
/**
|
||||
* @map: map to the parent syscon where the main register reside
|
||||
*/
|
||||
struct regmap *map;
|
||||
/**
|
||||
* @ib2_map: map to the IB2 syscon, if applicable
|
||||
*/
|
||||
struct regmap *ib2_map;
|
||||
};
|
||||
|
||||
static const struct versatile_panel_type versatile_panels[] = {
|
||||
/*
|
||||
* Sanyo TM38QV67A02A - 3.8 inch QVGA (320x240) Color TFT
|
||||
* found on the Versatile AB IB1 connector or the Versatile
|
||||
* PB adaptor board connector.
|
||||
*/
|
||||
{
|
||||
.name = "Sanyo TM38QV67A02A",
|
||||
.magic = SYS_CLCD_ID_SANYO_3_8,
|
||||
.width_mm = 79,
|
||||
.height_mm = 54,
|
||||
.mode = {
|
||||
.clock = 10000000,
|
||||
.hdisplay = 320,
|
||||
.hsync_start = 320 + 6,
|
||||
.hsync_end = 320 + 6 + 6,
|
||||
.htotal = 320 + 6 + 6 + 6,
|
||||
.vdisplay = 240,
|
||||
.vsync_start = 240 + 5,
|
||||
.vsync_end = 240 + 5 + 6,
|
||||
.vtotal = 240 + 5 + 6 + 5,
|
||||
.vrefresh = 116,
|
||||
.flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
|
||||
},
|
||||
},
|
||||
/*
|
||||
* Sharp LQ084V1DG21 640x480 VGA Color TFT module
|
||||
* found on the Versatile AB IB1 connector or the Versatile
|
||||
* PB adaptor board connector.
|
||||
*/
|
||||
{
|
||||
.name = "Sharp LQ084V1DG21",
|
||||
.magic = SYS_CLCD_ID_SHARP_8_4,
|
||||
.width_mm = 171,
|
||||
.height_mm = 130,
|
||||
.mode = {
|
||||
.clock = 25000000,
|
||||
.hdisplay = 640,
|
||||
.hsync_start = 640 + 24,
|
||||
.hsync_end = 640 + 24 + 96,
|
||||
.htotal = 640 + 24 + 96 + 24,
|
||||
.vdisplay = 480,
|
||||
.vsync_start = 480 + 11,
|
||||
.vsync_end = 480 + 11 + 2,
|
||||
.vtotal = 480 + 11 + 2 + 32,
|
||||
.vrefresh = 60,
|
||||
.flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
|
||||
},
|
||||
},
|
||||
/*
|
||||
* Epson L2F50113T00 - 2.2 inch QCIF 176x220 Color TFT
|
||||
* found on the Versatile PB adaptor board connector.
|
||||
*/
|
||||
{
|
||||
.name = "Epson L2F50113T00",
|
||||
.magic = SYS_CLCD_ID_EPSON_2_2,
|
||||
.width_mm = 34,
|
||||
.height_mm = 45,
|
||||
.mode = {
|
||||
.clock = 625000000,
|
||||
.hdisplay = 176,
|
||||
.hsync_start = 176 + 2,
|
||||
.hsync_end = 176 + 2 + 3,
|
||||
.htotal = 176 + 2 + 3 + 3,
|
||||
.vdisplay = 220,
|
||||
.vsync_start = 220 + 0,
|
||||
.vsync_end = 220 + 0 + 2,
|
||||
.vtotal = 220 + 0 + 2 + 1,
|
||||
.vrefresh = 390,
|
||||
.flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
|
||||
},
|
||||
.bus_flags = DRM_BUS_FLAG_PIXDATA_NEGEDGE,
|
||||
},
|
||||
/*
|
||||
* Sanyo ALR252RGT 240x320 portrait display found on the
|
||||
* Versatile AB IB2 daughterboard for GSM prototyping.
|
||||
*/
|
||||
{
|
||||
.name = "Sanyo ALR252RGT",
|
||||
.magic = SYS_CLCD_ID_SANYO_2_5,
|
||||
.width_mm = 37,
|
||||
.height_mm = 50,
|
||||
.mode = {
|
||||
.clock = 5400000,
|
||||
.hdisplay = 240,
|
||||
.hsync_start = 240 + 10,
|
||||
.hsync_end = 240 + 10 + 10,
|
||||
.htotal = 240 + 10 + 10 + 20,
|
||||
.vdisplay = 320,
|
||||
.vsync_start = 320 + 2,
|
||||
.vsync_end = 320 + 2 + 2,
|
||||
.vtotal = 320 + 2 + 2 + 2,
|
||||
.vrefresh = 116,
|
||||
.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
|
||||
},
|
||||
.bus_flags = DRM_BUS_FLAG_PIXDATA_NEGEDGE,
|
||||
.ib2 = true,
|
||||
},
|
||||
};
|
||||
|
||||
static inline struct versatile_panel *
|
||||
to_versatile_panel(struct drm_panel *panel)
|
||||
{
|
||||
return container_of(panel, struct versatile_panel, panel);
|
||||
}
|
||||
|
||||
static int versatile_panel_disable(struct drm_panel *panel)
|
||||
{
|
||||
struct versatile_panel *vpanel = to_versatile_panel(panel);
|
||||
|
||||
/* If we're on an IB2 daughterboard, turn off display */
|
||||
if (vpanel->ib2_map) {
|
||||
dev_dbg(vpanel->dev, "disable IB2 display\n");
|
||||
regmap_update_bits(vpanel->ib2_map,
|
||||
IB2_CTRL,
|
||||
IB2_CTRL_LCD_MASK,
|
||||
IB2_CTRL_LCD_SD);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int versatile_panel_enable(struct drm_panel *panel)
|
||||
{
|
||||
struct versatile_panel *vpanel = to_versatile_panel(panel);
|
||||
|
||||
/* If we're on an IB2 daughterboard, turn on display */
|
||||
if (vpanel->ib2_map) {
|
||||
dev_dbg(vpanel->dev, "enable IB2 display\n");
|
||||
regmap_update_bits(vpanel->ib2_map,
|
||||
IB2_CTRL,
|
||||
IB2_CTRL_LCD_MASK,
|
||||
IB2_CTRL_LCD_BL_ON);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int versatile_panel_get_modes(struct drm_panel *panel)
|
||||
{
|
||||
struct drm_connector *connector = panel->connector;
|
||||
struct versatile_panel *vpanel = to_versatile_panel(panel);
|
||||
struct drm_display_mode *mode;
|
||||
|
||||
strncpy(connector->display_info.name, vpanel->panel_type->name,
|
||||
DRM_DISPLAY_INFO_LEN);
|
||||
connector->display_info.width_mm = vpanel->panel_type->width_mm;
|
||||
connector->display_info.height_mm = vpanel->panel_type->height_mm;
|
||||
connector->display_info.bus_flags = vpanel->panel_type->bus_flags;
|
||||
|
||||
mode = drm_mode_duplicate(panel->drm, &vpanel->panel_type->mode);
|
||||
drm_mode_set_name(mode);
|
||||
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
|
||||
|
||||
mode->width_mm = vpanel->panel_type->width_mm;
|
||||
mode->height_mm = vpanel->panel_type->height_mm;
|
||||
drm_mode_probed_add(connector, mode);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct drm_panel_funcs versatile_panel_drm_funcs = {
|
||||
.disable = versatile_panel_disable,
|
||||
.enable = versatile_panel_enable,
|
||||
.get_modes = versatile_panel_get_modes,
|
||||
};
|
||||
|
||||
static int versatile_panel_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct versatile_panel *vpanel;
|
||||
struct device *parent;
|
||||
struct regmap *map;
|
||||
int ret;
|
||||
u32 val;
|
||||
int i;
|
||||
|
||||
parent = dev->parent;
|
||||
if (!parent) {
|
||||
dev_err(dev, "no parent for versatile panel\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
map = syscon_node_to_regmap(parent->of_node);
|
||||
if (IS_ERR(map)) {
|
||||
dev_err(dev, "no regmap for versatile panel parent\n");
|
||||
return PTR_ERR(map);
|
||||
}
|
||||
|
||||
vpanel = devm_kzalloc(dev, sizeof(*vpanel), GFP_KERNEL);
|
||||
if (!vpanel)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = regmap_read(map, SYS_CLCD, &val);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot access syscon regs\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
val &= SYS_CLCD_CLCDID_MASK;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(versatile_panels); i++) {
|
||||
const struct versatile_panel_type *pt;
|
||||
|
||||
pt = &versatile_panels[i];
|
||||
if (pt->magic == val) {
|
||||
vpanel->panel_type = pt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* No panel detected or VGA, let's leave this show */
|
||||
if (i == ARRAY_SIZE(versatile_panels)) {
|
||||
dev_info(dev, "no panel detected\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev_info(dev, "detected: %s\n", vpanel->panel_type->name);
|
||||
vpanel->dev = dev;
|
||||
vpanel->map = map;
|
||||
|
||||
/* Check if the panel is mounted on an IB2 daughterboard */
|
||||
if (vpanel->panel_type->ib2) {
|
||||
vpanel->ib2_map = syscon_regmap_lookup_by_compatible(
|
||||
"arm,versatile-ib2-syscon");
|
||||
if (IS_ERR(vpanel->ib2_map))
|
||||
vpanel->ib2_map = NULL;
|
||||
else
|
||||
dev_info(dev, "panel mounted on IB2 daughterboard\n");
|
||||
}
|
||||
|
||||
drm_panel_init(&vpanel->panel);
|
||||
vpanel->panel.dev = dev;
|
||||
vpanel->panel.funcs = &versatile_panel_drm_funcs;
|
||||
|
||||
return drm_panel_add(&vpanel->panel);
|
||||
}
|
||||
|
||||
static const struct of_device_id versatile_panel_match[] = {
|
||||
{ .compatible = "arm,versatile-tft-panel", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, versatile_panel_match);
|
||||
|
||||
static struct platform_driver versatile_panel_driver = {
|
||||
.probe = versatile_panel_probe,
|
||||
.driver = {
|
||||
.name = "versatile-tft-panel",
|
||||
.of_match_table = versatile_panel_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(versatile_panel_driver);
|
||||
|
||||
MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
|
||||
MODULE_DESCRIPTION("ARM Versatile panel driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -59,34 +59,28 @@ static inline struct wuxga_nt_panel *to_wuxga_nt_panel(struct drm_panel *panel)
|
|||
|
||||
static int wuxga_nt_panel_on(struct wuxga_nt_panel *wuxga_nt)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = wuxga_nt->dsi;
|
||||
int ret;
|
||||
|
||||
ret = mipi_dsi_turn_on_peripheral(dsi);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
return mipi_dsi_turn_on_peripheral(wuxga_nt->dsi);
|
||||
}
|
||||
|
||||
static int wuxga_nt_panel_disable(struct drm_panel *panel)
|
||||
{
|
||||
struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
|
||||
int mipi_ret, bl_ret = 0;
|
||||
|
||||
if (!wuxga_nt->enabled)
|
||||
return 0;
|
||||
|
||||
mipi_dsi_shutdown_peripheral(wuxga_nt->dsi);
|
||||
mipi_ret = mipi_dsi_shutdown_peripheral(wuxga_nt->dsi);
|
||||
|
||||
if (wuxga_nt->backlight) {
|
||||
wuxga_nt->backlight->props.power = FB_BLANK_POWERDOWN;
|
||||
wuxga_nt->backlight->props.state |= BL_CORE_FBBLANK;
|
||||
backlight_update_status(wuxga_nt->backlight);
|
||||
bl_ret = backlight_update_status(wuxga_nt->backlight);
|
||||
}
|
||||
|
||||
wuxga_nt->enabled = false;
|
||||
|
||||
return 0;
|
||||
return mipi_ret ? mipi_ret : bl_ret;
|
||||
}
|
||||
|
||||
static int wuxga_nt_panel_unprepare(struct drm_panel *panel)
|
||||
|
|
|
@ -8,6 +8,7 @@ config DRM_PL111
|
|||
select DRM_GEM_CMA_HELPER
|
||||
select DRM_BRIDGE
|
||||
select DRM_PANEL_BRIDGE
|
||||
select DRM_DUMB_VGA_DAC
|
||||
select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
|
||||
help
|
||||
Choose this option for DRM support for the PL111 CLCD controller.
|
||||
|
|
|
@ -94,6 +94,7 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
|
|||
const struct drm_display_mode *mode = &cstate->mode;
|
||||
struct drm_framebuffer *fb = plane->state->fb;
|
||||
struct drm_connector *connector = priv->connector;
|
||||
struct drm_bridge *bridge = priv->bridge;
|
||||
u32 cntl;
|
||||
u32 ppl, hsw, hfp, hbp;
|
||||
u32 lpp, vsw, vfp, vbp;
|
||||
|
@ -137,17 +138,46 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
|
|||
tim2 = readl(priv->regs + CLCD_TIM2);
|
||||
tim2 &= (TIM2_BCD | TIM2_PCD_LO_MASK | TIM2_PCD_HI_MASK);
|
||||
|
||||
if (priv->variant->broken_clockdivider)
|
||||
tim2 |= TIM2_BCD;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
|
||||
tim2 |= TIM2_IHS;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
|
||||
tim2 |= TIM2_IVS;
|
||||
|
||||
if (connector->display_info.bus_flags & DRM_BUS_FLAG_DE_LOW)
|
||||
tim2 |= TIM2_IOE;
|
||||
if (connector) {
|
||||
if (connector->display_info.bus_flags & DRM_BUS_FLAG_DE_LOW)
|
||||
tim2 |= TIM2_IOE;
|
||||
|
||||
if (connector->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE)
|
||||
tim2 |= TIM2_IPC;
|
||||
if (connector->display_info.bus_flags &
|
||||
DRM_BUS_FLAG_PIXDATA_NEGEDGE)
|
||||
tim2 |= TIM2_IPC;
|
||||
}
|
||||
|
||||
if (bridge) {
|
||||
const struct drm_bridge_timings *btimings = bridge->timings;
|
||||
|
||||
/*
|
||||
* Here is when things get really fun. Sometimes the bridge
|
||||
* timings are such that the signal out from PL11x is not
|
||||
* stable before the receiving bridge (such as a dumb VGA DAC
|
||||
* or similar) samples it. If that happens, we compensate by
|
||||
* the only method we have: output the data on the opposite
|
||||
* edge of the clock so it is for sure stable when it gets
|
||||
* sampled.
|
||||
*
|
||||
* The PL111 manual does not contain proper timining diagrams
|
||||
* or data for these details, but we know from experiments
|
||||
* that the setup time is more than 3000 picoseconds (3 ns).
|
||||
* If we have a bridge that requires the signal to be stable
|
||||
* earlier than 3000 ps before the clock pulse, we have to
|
||||
* output the data on the opposite edge to avoid flicker.
|
||||
*/
|
||||
if (btimings && btimings->setup_time_ps >= 3000)
|
||||
tim2 ^= TIM2_IPC;
|
||||
}
|
||||
|
||||
tim2 |= cpl << 16;
|
||||
writel(tim2, priv->regs + CLCD_TIM2);
|
||||
|
@ -172,10 +202,17 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
|
|||
cntl |= CNTL_LCDBPP24 | CNTL_BGR;
|
||||
break;
|
||||
case DRM_FORMAT_BGR565:
|
||||
cntl |= CNTL_LCDBPP16_565;
|
||||
if (priv->variant->is_pl110)
|
||||
cntl |= CNTL_LCDBPP16;
|
||||
else
|
||||
cntl |= CNTL_LCDBPP16_565;
|
||||
break;
|
||||
case DRM_FORMAT_RGB565:
|
||||
cntl |= CNTL_LCDBPP16_565 | CNTL_BGR;
|
||||
if (priv->variant->is_pl110)
|
||||
cntl |= CNTL_LCDBPP16;
|
||||
else
|
||||
cntl |= CNTL_LCDBPP16_565;
|
||||
cntl |= CNTL_BGR;
|
||||
break;
|
||||
case DRM_FORMAT_ABGR1555:
|
||||
case DRM_FORMAT_XBGR1555:
|
||||
|
@ -199,6 +236,10 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
|
|||
break;
|
||||
}
|
||||
|
||||
/* The PL110 in Integrator/Versatile does the BGR routing externally */
|
||||
if (priv->variant->external_bgr)
|
||||
cntl &= ~CNTL_BGR;
|
||||
|
||||
/* Power sequence: first enable and chill */
|
||||
writel(cntl, priv->regs + priv->ctrl);
|
||||
|
||||
|
@ -215,7 +256,8 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
|
|||
cntl |= CNTL_LCDPWR;
|
||||
writel(cntl, priv->regs + priv->ctrl);
|
||||
|
||||
drm_crtc_vblank_on(crtc);
|
||||
if (!priv->variant->broken_vblank)
|
||||
drm_crtc_vblank_on(crtc);
|
||||
}
|
||||
|
||||
void pl111_display_disable(struct drm_simple_display_pipe *pipe)
|
||||
|
@ -225,7 +267,8 @@ void pl111_display_disable(struct drm_simple_display_pipe *pipe)
|
|||
struct pl111_drm_dev_private *priv = drm->dev_private;
|
||||
u32 cntl;
|
||||
|
||||
drm_crtc_vblank_off(crtc);
|
||||
if (!priv->variant->broken_vblank)
|
||||
drm_crtc_vblank_off(crtc);
|
||||
|
||||
/* Power Down */
|
||||
cntl = readl(priv->regs + priv->ctrl);
|
||||
|
@ -417,6 +460,11 @@ pl111_init_clock_divider(struct drm_device *drm)
|
|||
dev_err(drm->dev, "CLCD: unable to get clcdclk.\n");
|
||||
return PTR_ERR(parent);
|
||||
}
|
||||
/* If the clock divider is broken, use the parent directly */
|
||||
if (priv->variant->broken_clockdivider) {
|
||||
priv->clk = parent;
|
||||
return 0;
|
||||
}
|
||||
parent_name = __clk_get_name(parent);
|
||||
|
||||
spin_lock_init(&priv->tim2_lock);
|
||||
|
|
|
@ -36,12 +36,20 @@ struct drm_minor;
|
|||
* struct pl111_variant_data - encodes IP differences
|
||||
* @name: the name of this variant
|
||||
* @is_pl110: this is the early PL110 variant
|
||||
* @external_bgr: this is the Versatile Pl110 variant with external
|
||||
* BGR/RGB routing
|
||||
* @broken_clockdivider: the clock divider is broken and we need to
|
||||
* use the supplied clock directly
|
||||
* @broken_vblank: the vblank IRQ is broken on this variant
|
||||
* @formats: array of supported pixel formats on this variant
|
||||
* @nformats: the length of the array of supported pixel formats
|
||||
*/
|
||||
struct pl111_variant_data {
|
||||
const char *name;
|
||||
bool is_pl110;
|
||||
bool external_bgr;
|
||||
bool broken_clockdivider;
|
||||
bool broken_vblank;
|
||||
const u32 *formats;
|
||||
unsigned int nformats;
|
||||
};
|
||||
|
|
|
@ -58,6 +58,8 @@
|
|||
#include <linux/dma-buf.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
|
@ -85,9 +87,13 @@ static int pl111_modeset_init(struct drm_device *dev)
|
|||
{
|
||||
struct drm_mode_config *mode_config;
|
||||
struct pl111_drm_dev_private *priv = dev->dev_private;
|
||||
struct drm_panel *panel;
|
||||
struct drm_bridge *bridge;
|
||||
struct device_node *np = dev->dev->of_node;
|
||||
struct device_node *remote;
|
||||
struct drm_panel *panel = NULL;
|
||||
struct drm_bridge *bridge = NULL;
|
||||
bool defer = false;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
drm_mode_config_init(dev);
|
||||
mode_config = &dev->mode_config;
|
||||
|
@ -97,10 +103,54 @@ static int pl111_modeset_init(struct drm_device *dev)
|
|||
mode_config->min_height = 1;
|
||||
mode_config->max_height = 768;
|
||||
|
||||
ret = drm_of_find_panel_or_bridge(dev->dev->of_node,
|
||||
0, 0, &panel, &bridge);
|
||||
if (ret && ret != -ENODEV)
|
||||
return ret;
|
||||
i = 0;
|
||||
for_each_endpoint_of_node(np, remote) {
|
||||
struct drm_panel *tmp_panel;
|
||||
struct drm_bridge *tmp_bridge;
|
||||
|
||||
dev_dbg(dev->dev, "checking endpoint %d\n", i);
|
||||
|
||||
ret = drm_of_find_panel_or_bridge(dev->dev->of_node,
|
||||
0, i,
|
||||
&tmp_panel,
|
||||
&tmp_bridge);
|
||||
if (ret) {
|
||||
if (ret == -EPROBE_DEFER) {
|
||||
/*
|
||||
* Something deferred, but that is often just
|
||||
* another way of saying -ENODEV, but let's
|
||||
* cast a vote for later deferral.
|
||||
*/
|
||||
defer = true;
|
||||
} else if (ret != -ENODEV) {
|
||||
/* Continue, maybe something else is working */
|
||||
dev_err(dev->dev,
|
||||
"endpoint %d returns %d\n", i, ret);
|
||||
}
|
||||
}
|
||||
|
||||
if (tmp_panel) {
|
||||
dev_info(dev->dev,
|
||||
"found panel on endpoint %d\n", i);
|
||||
panel = tmp_panel;
|
||||
}
|
||||
if (tmp_bridge) {
|
||||
dev_info(dev->dev,
|
||||
"found bridge on endpoint %d\n", i);
|
||||
bridge = tmp_bridge;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we can't find neither panel nor bridge on any of the
|
||||
* endpoints, and any of them retured -EPROBE_DEFER, then
|
||||
* let's defer this driver too.
|
||||
*/
|
||||
if ((!panel && !bridge) && defer)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (panel) {
|
||||
bridge = drm_panel_bridge_add(panel,
|
||||
DRM_MODE_CONNECTOR_Unknown);
|
||||
|
@ -108,11 +158,17 @@ static int pl111_modeset_init(struct drm_device *dev)
|
|||
ret = PTR_ERR(bridge);
|
||||
goto out_config;
|
||||
}
|
||||
/*
|
||||
* TODO: when we are using a different bridge than a panel
|
||||
* (such as a dumb VGA connector) we need to devise a different
|
||||
* method to get the connector out of the bridge.
|
||||
*/
|
||||
} else if (bridge) {
|
||||
dev_info(dev->dev, "Using non-panel bridge\n");
|
||||
} else {
|
||||
dev_err(dev->dev, "No bridge, exiting\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
priv->bridge = bridge;
|
||||
if (panel) {
|
||||
priv->panel = panel;
|
||||
priv->connector = panel->connector;
|
||||
}
|
||||
|
||||
ret = pl111_display_init(dev);
|
||||
|
@ -126,14 +182,12 @@ static int pl111_modeset_init(struct drm_device *dev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->bridge = bridge;
|
||||
priv->panel = panel;
|
||||
priv->connector = panel->connector;
|
||||
|
||||
ret = drm_vblank_init(dev, 1);
|
||||
if (ret != 0) {
|
||||
dev_err(dev->dev, "Failed to init vblank\n");
|
||||
goto out_bridge;
|
||||
if (!priv->variant->broken_vblank) {
|
||||
ret = drm_vblank_init(dev, 1);
|
||||
if (ret != 0) {
|
||||
dev_err(dev->dev, "Failed to init vblank\n");
|
||||
goto out_bridge;
|
||||
}
|
||||
}
|
||||
|
||||
drm_mode_config_reset(dev);
|
||||
|
@ -170,10 +224,6 @@ static struct drm_driver pl111_drm_driver = {
|
|||
.dumb_create = drm_gem_cma_dumb_create,
|
||||
.gem_free_object_unlocked = drm_gem_cma_free_object,
|
||||
.gem_vm_ops = &drm_gem_cma_vm_ops,
|
||||
|
||||
.enable_vblank = pl111_enable_vblank,
|
||||
.disable_vblank = pl111_disable_vblank,
|
||||
|
||||
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
|
||||
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
|
||||
.gem_prime_import = drm_gem_prime_import,
|
||||
|
@ -191,7 +241,7 @@ static int pl111_amba_probe(struct amba_device *amba_dev,
|
|||
{
|
||||
struct device *dev = &amba_dev->dev;
|
||||
struct pl111_drm_dev_private *priv;
|
||||
struct pl111_variant_data *variant = id->data;
|
||||
const struct pl111_variant_data *variant = id->data;
|
||||
struct drm_device *drm;
|
||||
int ret;
|
||||
|
||||
|
@ -199,6 +249,11 @@ static int pl111_amba_probe(struct amba_device *amba_dev,
|
|||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!variant->broken_vblank) {
|
||||
pl111_drm_driver.enable_vblank = pl111_enable_vblank;
|
||||
pl111_drm_driver.disable_vblank = pl111_disable_vblank;
|
||||
}
|
||||
|
||||
drm = drm_dev_alloc(&pl111_drm_driver, dev);
|
||||
if (IS_ERR(drm))
|
||||
return PTR_ERR(drm);
|
||||
|
@ -207,27 +262,10 @@ static int pl111_amba_probe(struct amba_device *amba_dev,
|
|||
drm->dev_private = priv;
|
||||
priv->variant = variant;
|
||||
|
||||
/*
|
||||
* The PL110 and PL111 variants have two registers
|
||||
* swapped: interrupt enable and control. For this reason
|
||||
* we use offsets that we can change per variant.
|
||||
*/
|
||||
/* The two variants swap this register */
|
||||
if (variant->is_pl110) {
|
||||
/*
|
||||
* The ARM Versatile boards are even more special:
|
||||
* their PrimeCell ID say they are PL110 but the
|
||||
* control and interrupt enable registers are anyway
|
||||
* swapped to the PL111 order so they are not following
|
||||
* the PL110 datasheet.
|
||||
*/
|
||||
if (of_machine_is_compatible("arm,versatile-ab") ||
|
||||
of_machine_is_compatible("arm,versatile-pb")) {
|
||||
priv->ienb = CLCD_PL111_IENB;
|
||||
priv->ctrl = CLCD_PL111_CNTL;
|
||||
} else {
|
||||
priv->ienb = CLCD_PL110_IENB;
|
||||
priv->ctrl = CLCD_PL110_CNTL;
|
||||
}
|
||||
priv->ienb = CLCD_PL110_IENB;
|
||||
priv->ctrl = CLCD_PL110_CNTL;
|
||||
} else {
|
||||
priv->ienb = CLCD_PL111_IENB;
|
||||
priv->ctrl = CLCD_PL111_CNTL;
|
||||
|
@ -239,6 +277,11 @@ static int pl111_amba_probe(struct amba_device *amba_dev,
|
|||
return PTR_ERR(priv->regs);
|
||||
}
|
||||
|
||||
/* This may override some variant settings */
|
||||
ret = pl111_versatile_init(dev, priv);
|
||||
if (ret)
|
||||
goto dev_unref;
|
||||
|
||||
/* turn off interrupts before requesting the irq */
|
||||
writel(0, priv->regs + priv->ienb);
|
||||
|
||||
|
@ -249,10 +292,6 @@ static int pl111_amba_probe(struct amba_device *amba_dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = pl111_versatile_init(dev, priv);
|
||||
if (ret)
|
||||
goto dev_unref;
|
||||
|
||||
ret = pl111_modeset_init(drm);
|
||||
if (ret != 0)
|
||||
goto dev_unref;
|
||||
|
@ -284,8 +323,7 @@ static int pl111_amba_remove(struct amba_device *amba_dev)
|
|||
}
|
||||
|
||||
/*
|
||||
* This variant exist in early versions like the ARM Integrator
|
||||
* and this version lacks the 565 and 444 pixel formats.
|
||||
* This early variant lacks the 565 and 444 pixel formats.
|
||||
*/
|
||||
static const u32 pl110_pixel_formats[] = {
|
||||
DRM_FORMAT_ABGR8888,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include <linux/amba/clcd-regs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
|
@ -64,10 +65,8 @@ static const struct of_device_id versatile_clcd_of_match[] = {
|
|||
#define INTEGRATOR_CLCD_LCDBIASEN BIT(8)
|
||||
#define INTEGRATOR_CLCD_LCDBIASUP BIT(9)
|
||||
#define INTEGRATOR_CLCD_LCDBIASDN BIT(10)
|
||||
/* Bits 11,12,13 controls the LCD type */
|
||||
#define INTEGRATOR_CLCD_LCDMUX_MASK (BIT(11)|BIT(12)|BIT(13))
|
||||
/* Bits 11,12,13 controls the LCD or VGA bridge type */
|
||||
#define INTEGRATOR_CLCD_LCDMUX_LCD24 BIT(11)
|
||||
#define INTEGRATOR_CLCD_LCDMUX_VGA565 BIT(12)
|
||||
#define INTEGRATOR_CLCD_LCDMUX_SHARP (BIT(11)|BIT(12))
|
||||
#define INTEGRATOR_CLCD_LCDMUX_VGA555 BIT(13)
|
||||
#define INTEGRATOR_CLCD_LCDMUX_VGA24 (BIT(11)|BIT(12)|BIT(13))
|
||||
|
@ -82,16 +81,7 @@ static const struct of_device_id versatile_clcd_of_match[] = {
|
|||
/* 0 = 24bit VGA, 1 = 18bit VGA */
|
||||
#define INTEGRATOR_CLCD_LCD_N24BITEN BIT(19)
|
||||
|
||||
#define INTEGRATOR_CLCD_MASK (INTEGRATOR_CLCD_LCDBIASEN | \
|
||||
INTEGRATOR_CLCD_LCDBIASUP | \
|
||||
INTEGRATOR_CLCD_LCDBIASDN | \
|
||||
INTEGRATOR_CLCD_LCDMUX_MASK | \
|
||||
INTEGRATOR_CLCD_LCD0_EN | \
|
||||
INTEGRATOR_CLCD_LCD1_EN | \
|
||||
INTEGRATOR_CLCD_LCD_STATIC1 | \
|
||||
INTEGRATOR_CLCD_LCD_STATIC2 | \
|
||||
INTEGRATOR_CLCD_LCD_STATIC | \
|
||||
INTEGRATOR_CLCD_LCD_N24BITEN)
|
||||
#define INTEGRATOR_CLCD_MASK GENMASK(19, 8)
|
||||
|
||||
static void pl111_integrator_enable(struct drm_device *drm, u32 format)
|
||||
{
|
||||
|
@ -106,11 +96,8 @@ static void pl111_integrator_enable(struct drm_device *drm, u32 format)
|
|||
switch (format) {
|
||||
case DRM_FORMAT_XBGR8888:
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
break;
|
||||
case DRM_FORMAT_BGR565:
|
||||
case DRM_FORMAT_RGB565:
|
||||
/* truecolor RGB565 */
|
||||
val |= INTEGRATOR_CLCD_LCDMUX_VGA565;
|
||||
/* 24bit formats */
|
||||
val |= INTEGRATOR_CLCD_LCDMUX_VGA24;
|
||||
break;
|
||||
case DRM_FORMAT_XBGR1555:
|
||||
case DRM_FORMAT_XRGB1555:
|
||||
|
@ -217,6 +204,57 @@ static void pl111_realview_clcd_enable(struct drm_device *drm, u32 format)
|
|||
SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH);
|
||||
}
|
||||
|
||||
/* PL110 pixel formats for Integrator, vanilla PL110 */
|
||||
static const u32 pl110_integrator_pixel_formats[] = {
|
||||
DRM_FORMAT_ABGR8888,
|
||||
DRM_FORMAT_XBGR8888,
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_ABGR1555,
|
||||
DRM_FORMAT_XBGR1555,
|
||||
DRM_FORMAT_ARGB1555,
|
||||
DRM_FORMAT_XRGB1555,
|
||||
};
|
||||
|
||||
/* Extended PL110 pixel formats for Integrator and Versatile */
|
||||
static const u32 pl110_versatile_pixel_formats[] = {
|
||||
DRM_FORMAT_ABGR8888,
|
||||
DRM_FORMAT_XBGR8888,
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_BGR565, /* Uses external PLD */
|
||||
DRM_FORMAT_RGB565, /* Uses external PLD */
|
||||
DRM_FORMAT_ABGR1555,
|
||||
DRM_FORMAT_XBGR1555,
|
||||
DRM_FORMAT_ARGB1555,
|
||||
DRM_FORMAT_XRGB1555,
|
||||
};
|
||||
|
||||
/*
|
||||
* The Integrator variant is a PL110 with a bunch of broken, or not
|
||||
* yet implemented features
|
||||
*/
|
||||
static const struct pl111_variant_data pl110_integrator = {
|
||||
.name = "PL110 Integrator",
|
||||
.is_pl110 = true,
|
||||
.broken_clockdivider = true,
|
||||
.broken_vblank = true,
|
||||
.formats = pl110_integrator_pixel_formats,
|
||||
.nformats = ARRAY_SIZE(pl110_integrator_pixel_formats),
|
||||
};
|
||||
|
||||
/*
|
||||
* This is the in-between PL110 variant found in the ARM Versatile,
|
||||
* supporting RGB565/BGR565
|
||||
*/
|
||||
static const struct pl111_variant_data pl110_versatile = {
|
||||
.name = "PL110 Versatile",
|
||||
.is_pl110 = true,
|
||||
.external_bgr = true,
|
||||
.formats = pl110_versatile_pixel_formats,
|
||||
.nformats = ARRAY_SIZE(pl110_versatile_pixel_formats),
|
||||
};
|
||||
|
||||
int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv)
|
||||
{
|
||||
const struct of_device_id *clcd_id;
|
||||
|
@ -241,14 +279,24 @@ int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv)
|
|||
switch (versatile_clcd_type) {
|
||||
case INTEGRATOR_CLCD_CM:
|
||||
versatile_syscon_map = map;
|
||||
priv->variant = &pl110_integrator;
|
||||
priv->variant_display_enable = pl111_integrator_enable;
|
||||
dev_info(dev, "set up callbacks for Integrator PL110\n");
|
||||
break;
|
||||
case VERSATILE_CLCD:
|
||||
versatile_syscon_map = map;
|
||||
/* This can do RGB565 with external PLD */
|
||||
priv->variant = &pl110_versatile;
|
||||
priv->variant_display_enable = pl111_versatile_enable;
|
||||
priv->variant_display_disable = pl111_versatile_disable;
|
||||
dev_info(dev, "set up callbacks for Versatile PL110+\n");
|
||||
/*
|
||||
* The Versatile has a variant halfway between PL110
|
||||
* and PL111 where these two registers have already been
|
||||
* swapped.
|
||||
*/
|
||||
priv->ienb = CLCD_PL111_IENB;
|
||||
priv->ctrl = CLCD_PL111_CNTL;
|
||||
dev_info(dev, "set up callbacks for Versatile PL110\n");
|
||||
break;
|
||||
case REALVIEW_CLCD_EB:
|
||||
case REALVIEW_CLCD_PB1176:
|
||||
|
|
|
@ -2387,6 +2387,7 @@ struct radeon_device {
|
|||
struct radeon_dummy_page dummy_page;
|
||||
bool shutdown;
|
||||
bool need_dma32;
|
||||
bool need_swiotlb;
|
||||
bool accel_working;
|
||||
bool fastfb_working; /* IGP feature*/
|
||||
bool needs_reset, in_reset;
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_cache.h>
|
||||
#include <drm/radeon_drm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/vgaarb.h>
|
||||
|
@ -1378,6 +1379,7 @@ int radeon_device_init(struct radeon_device *rdev,
|
|||
pci_set_consistent_dma_mask(rdev->pdev, DMA_BIT_MASK(32));
|
||||
pr_warn("radeon: No coherent DMA available\n");
|
||||
}
|
||||
rdev->need_swiotlb = drm_get_max_iomem() > ((u64)1 << dma_bits);
|
||||
|
||||
/* Registers mapping */
|
||||
/* TODO: block userspace mapping of io register */
|
||||
|
|
|
@ -756,7 +756,7 @@ static int radeon_ttm_tt_populate(struct ttm_tt *ttm,
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_SWIOTLB
|
||||
if (swiotlb_nr_tbl()) {
|
||||
if (rdev->need_swiotlb && swiotlb_nr_tbl()) {
|
||||
return ttm_dma_populate(>t->ttm, rdev->dev, ctx);
|
||||
}
|
||||
#endif
|
||||
|
@ -788,7 +788,7 @@ static void radeon_ttm_tt_unpopulate(struct ttm_tt *ttm)
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_SWIOTLB
|
||||
if (swiotlb_nr_tbl()) {
|
||||
if (rdev->need_swiotlb && swiotlb_nr_tbl()) {
|
||||
ttm_dma_unpopulate(>t->ttm, rdev->dev);
|
||||
return;
|
||||
}
|
||||
|
@ -1155,7 +1155,7 @@ static int radeon_ttm_debugfs_init(struct radeon_device *rdev)
|
|||
count = ARRAY_SIZE(radeon_ttm_debugfs_list);
|
||||
|
||||
#ifdef CONFIG_SWIOTLB
|
||||
if (!swiotlb_nr_tbl())
|
||||
if (!(rdev->need_swiotlb && swiotlb_nr_tbl()))
|
||||
--count;
|
||||
#endif
|
||||
|
||||
|
|
|
@ -572,7 +572,7 @@ int __rcar_du_plane_atomic_check(struct drm_plane *plane,
|
|||
{
|
||||
struct drm_device *dev = plane->dev;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
struct drm_rect clip;
|
||||
struct drm_rect clip = {};
|
||||
int ret;
|
||||
|
||||
if (!state->crtc) {
|
||||
|
@ -589,10 +589,9 @@ int __rcar_du_plane_atomic_check(struct drm_plane *plane,
|
|||
if (IS_ERR(crtc_state))
|
||||
return PTR_ERR(crtc_state);
|
||||
|
||||
clip.x1 = 0;
|
||||
clip.y1 = 0;
|
||||
clip.x2 = crtc_state->mode.hdisplay;
|
||||
clip.y2 = crtc_state->mode.vdisplay;
|
||||
if (crtc_state->enable)
|
||||
drm_mode_get_hv_timing(&crtc_state->mode,
|
||||
&clip.x2, &clip.y2);
|
||||
|
||||
ret = drm_atomic_helper_check_plane_state(state, crtc_state, &clip,
|
||||
DRM_PLANE_HELPER_NO_SCALING,
|
||||
|
|
|
@ -253,17 +253,6 @@ static bool is_yuv_support(uint32_t format)
|
|||
}
|
||||
}
|
||||
|
||||
static bool is_alpha_support(uint32_t format)
|
||||
{
|
||||
switch (format) {
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
case DRM_FORMAT_ABGR8888:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src,
|
||||
uint32_t dst, bool is_horizontal,
|
||||
int vsu_mode, int *vskiplines)
|
||||
|
@ -641,7 +630,7 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
|
|||
struct vop_win *vop_win = to_vop_win(plane);
|
||||
const struct vop_win_data *win = vop_win->data;
|
||||
int ret;
|
||||
struct drm_rect clip;
|
||||
struct drm_rect clip = {};
|
||||
int min_scale = win->phy->scl ? FRAC_16_16(1, 8) :
|
||||
DRM_PLANE_HELPER_NO_SCALING;
|
||||
int max_scale = win->phy->scl ? FRAC_16_16(8, 1) :
|
||||
|
@ -654,10 +643,9 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
|
|||
if (WARN_ON(!crtc_state))
|
||||
return -EINVAL;
|
||||
|
||||
clip.x1 = 0;
|
||||
clip.y1 = 0;
|
||||
clip.x2 = crtc_state->adjusted_mode.hdisplay;
|
||||
clip.y2 = crtc_state->adjusted_mode.vdisplay;
|
||||
if (crtc_state->enable)
|
||||
drm_mode_get_hv_timing(&crtc_state->mode,
|
||||
&clip.x2, &clip.y2);
|
||||
|
||||
ret = drm_atomic_helper_check_plane_state(state, crtc_state, &clip,
|
||||
min_scale, max_scale,
|
||||
|
@ -790,7 +778,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
|
|||
rb_swap = has_rb_swapped(fb->format->format);
|
||||
VOP_WIN_SET(vop, win, rb_swap, rb_swap);
|
||||
|
||||
if (is_alpha_support(fb->format->format)) {
|
||||
if (fb->format->has_alpha) {
|
||||
VOP_WIN_SET(vop, win, dst_alpha_ctl,
|
||||
DST_FACTOR_M0(ALPHA_SRC_INVERSE));
|
||||
val = SRC_ALPHA_EN(1) | SRC_COLOR_M0(ALPHA_SRC_PRE_MUL) |
|
||||
|
|
|
@ -31,6 +31,24 @@ static const struct drm_mode_config_funcs drv_mode_config_funcs = {
|
|||
.atomic_commit = drm_atomic_helper_commit,
|
||||
};
|
||||
|
||||
static int stm_gem_cma_dumb_create(struct drm_file *file,
|
||||
struct drm_device *dev,
|
||||
struct drm_mode_create_dumb *args)
|
||||
{
|
||||
#ifdef CONFIG_MMU
|
||||
unsigned int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
|
||||
|
||||
/*
|
||||
* in order to optimize data transfer, pitch is aligned on
|
||||
* 128 bytes, height is aligned on 4 bytes
|
||||
*/
|
||||
args->pitch = roundup(min_pitch, 128);
|
||||
args->height = roundup(args->height, 4);
|
||||
#endif
|
||||
|
||||
return drm_gem_cma_dumb_create_internal(file, dev, args);
|
||||
}
|
||||
|
||||
DEFINE_DRM_GEM_CMA_FOPS(drv_driver_fops);
|
||||
|
||||
static struct drm_driver drv_driver = {
|
||||
|
@ -44,7 +62,7 @@ static struct drm_driver drv_driver = {
|
|||
.minor = 0,
|
||||
.patchlevel = 0,
|
||||
.fops = &drv_driver_fops,
|
||||
.dumb_create = drm_gem_cma_dumb_create,
|
||||
.dumb_create = stm_gem_cma_dumb_create,
|
||||
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
|
||||
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
|
||||
.gem_free_object_unlocked = drm_gem_cma_free_object,
|
||||
|
|
|
@ -14,7 +14,14 @@
|
|||
#include <drm/bridge/dw_mipi_dsi.h>
|
||||
#include <video/mipi_display.h>
|
||||
|
||||
/* DSI wrapper register & bit definitions */
|
||||
#define HWVER_130 0x31333000 /* IP version 1.30 */
|
||||
#define HWVER_131 0x31333100 /* IP version 1.31 */
|
||||
|
||||
/* DSI digital registers & bit definitions */
|
||||
#define DSI_VERSION 0x00
|
||||
#define VERSION GENMASK(31, 8)
|
||||
|
||||
/* DSI wrapper registers & bit definitions */
|
||||
/* Note: registers are named as in the Reference Manual */
|
||||
#define DSI_WCFGR 0x0400 /* Wrapper ConFiGuration Reg */
|
||||
#define WCFGR_DSIM BIT(0) /* DSI Mode */
|
||||
|
@ -65,6 +72,10 @@ enum dsi_color {
|
|||
struct dw_mipi_dsi_stm {
|
||||
void __iomem *base;
|
||||
struct clk *pllref_clk;
|
||||
struct dw_mipi_dsi *dsi;
|
||||
u32 hw_version;
|
||||
int lane_min_kbps;
|
||||
int lane_max_kbps;
|
||||
};
|
||||
|
||||
static inline void dsi_write(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 val)
|
||||
|
@ -121,7 +132,8 @@ static int dsi_pll_get_clkout_khz(int clkin_khz, int idf, int ndiv, int odf)
|
|||
return DIV_ROUND_CLOSEST(clkin_khz * ndiv, divisor);
|
||||
}
|
||||
|
||||
static int dsi_pll_get_params(int clkin_khz, int clkout_khz,
|
||||
static int dsi_pll_get_params(struct dw_mipi_dsi_stm *dsi,
|
||||
int clkin_khz, int clkout_khz,
|
||||
int *idf, int *ndiv, int *odf)
|
||||
{
|
||||
int i, o, n, n_min, n_max;
|
||||
|
@ -131,8 +143,8 @@ static int dsi_pll_get_params(int clkin_khz, int clkout_khz,
|
|||
if (clkin_khz <= 0 || clkout_khz <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
fvco_min = LANE_MIN_KBPS * 2 * ODF_MAX;
|
||||
fvco_max = LANE_MAX_KBPS * 2 * ODF_MIN;
|
||||
fvco_min = dsi->lane_min_kbps * 2 * ODF_MAX;
|
||||
fvco_max = dsi->lane_max_kbps * 2 * ODF_MIN;
|
||||
|
||||
best_delta = 1000000; /* big started value (1000000khz) */
|
||||
|
||||
|
@ -212,6 +224,15 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, struct drm_display_mode *mode,
|
|||
int ret, bpp;
|
||||
u32 val;
|
||||
|
||||
/* Update lane capabilities according to hw version */
|
||||
dsi->hw_version = dsi_read(dsi, DSI_VERSION) & VERSION;
|
||||
dsi->lane_min_kbps = LANE_MIN_KBPS;
|
||||
dsi->lane_max_kbps = LANE_MAX_KBPS;
|
||||
if (dsi->hw_version == HWVER_131) {
|
||||
dsi->lane_min_kbps *= 2;
|
||||
dsi->lane_max_kbps *= 2;
|
||||
}
|
||||
|
||||
pll_in_khz = (unsigned int)(clk_get_rate(dsi->pllref_clk) / 1000);
|
||||
|
||||
/* Compute requested pll out */
|
||||
|
@ -219,12 +240,12 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, struct drm_display_mode *mode,
|
|||
pll_out_khz = mode->clock * bpp / lanes;
|
||||
/* Add 20% to pll out to be higher than pixel bw (burst mode only) */
|
||||
pll_out_khz = (pll_out_khz * 12) / 10;
|
||||
if (pll_out_khz > LANE_MAX_KBPS) {
|
||||
pll_out_khz = LANE_MAX_KBPS;
|
||||
if (pll_out_khz > dsi->lane_max_kbps) {
|
||||
pll_out_khz = dsi->lane_max_kbps;
|
||||
DRM_WARN("Warning max phy mbps is used\n");
|
||||
}
|
||||
if (pll_out_khz < LANE_MIN_KBPS) {
|
||||
pll_out_khz = LANE_MIN_KBPS;
|
||||
if (pll_out_khz < dsi->lane_min_kbps) {
|
||||
pll_out_khz = dsi->lane_min_kbps;
|
||||
DRM_WARN("Warning min phy mbps is used\n");
|
||||
}
|
||||
|
||||
|
@ -232,7 +253,8 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, struct drm_display_mode *mode,
|
|||
idf = 0;
|
||||
ndiv = 0;
|
||||
odf = 0;
|
||||
ret = dsi_pll_get_params(pll_in_khz, pll_out_khz, &idf, &ndiv, &odf);
|
||||
ret = dsi_pll_get_params(dsi, pll_in_khz, pll_out_khz,
|
||||
&idf, &ndiv, &odf);
|
||||
if (ret)
|
||||
DRM_WARN("Warning dsi_pll_get_params(): bad params\n");
|
||||
|
||||
|
@ -312,21 +334,24 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev)
|
|||
dw_mipi_dsi_stm_plat_data.base = dsi->base;
|
||||
dw_mipi_dsi_stm_plat_data.priv_data = dsi;
|
||||
|
||||
ret = dw_mipi_dsi_probe(pdev, &dw_mipi_dsi_stm_plat_data);
|
||||
if (ret) {
|
||||
platform_set_drvdata(pdev, dsi);
|
||||
|
||||
dsi->dsi = dw_mipi_dsi_probe(pdev, &dw_mipi_dsi_stm_plat_data);
|
||||
if (IS_ERR(dsi->dsi)) {
|
||||
DRM_ERROR("Failed to initialize mipi dsi host\n");
|
||||
clk_disable_unprepare(dsi->pllref_clk);
|
||||
return PTR_ERR(dsi->dsi);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_mipi_dsi_stm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dw_mipi_dsi_stm *dsi = dw_mipi_dsi_stm_plat_data.priv_data;
|
||||
struct dw_mipi_dsi_stm *dsi = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(dsi->pllref_clk);
|
||||
dw_mipi_dsi_remove(pdev);
|
||||
dw_mipi_dsi_remove(dsi->dsi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -175,6 +175,8 @@
|
|||
|
||||
#define LXCFBLNR_CFBLN GENMASK(10, 0) /* Color Frame Buffer Line Number */
|
||||
|
||||
#define CLUT_SIZE 256
|
||||
|
||||
#define CONSTA_MAX 0xFF /* CONSTant Alpha MAX= 1.0 */
|
||||
#define BF1_PAXCA 0x600 /* Pixel Alpha x Constant Alpha */
|
||||
#define BF1_CA 0x400 /* Constant Alpha */
|
||||
|
@ -326,6 +328,26 @@ static inline u32 to_drm_pixelformat(enum ltdc_pix_fmt pf)
|
|||
}
|
||||
}
|
||||
|
||||
static inline u32 get_pixelformat_without_alpha(u32 drm)
|
||||
{
|
||||
switch (drm) {
|
||||
case DRM_FORMAT_ARGB4444:
|
||||
return DRM_FORMAT_XRGB4444;
|
||||
case DRM_FORMAT_RGBA4444:
|
||||
return DRM_FORMAT_RGBX4444;
|
||||
case DRM_FORMAT_ARGB1555:
|
||||
return DRM_FORMAT_XRGB1555;
|
||||
case DRM_FORMAT_RGBA5551:
|
||||
return DRM_FORMAT_RGBX5551;
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
return DRM_FORMAT_XRGB8888;
|
||||
case DRM_FORMAT_RGBA8888:
|
||||
return DRM_FORMAT_RGBX8888;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t ltdc_irq_thread(int irq, void *arg)
|
||||
{
|
||||
struct drm_device *ddev = arg;
|
||||
|
@ -363,6 +385,28 @@ static irqreturn_t ltdc_irq(int irq, void *arg)
|
|||
* DRM_CRTC
|
||||
*/
|
||||
|
||||
static void ltdc_crtc_update_clut(struct drm_crtc *crtc)
|
||||
{
|
||||
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
|
||||
struct drm_color_lut *lut;
|
||||
u32 val;
|
||||
int i;
|
||||
|
||||
if (!crtc || !crtc->state)
|
||||
return;
|
||||
|
||||
if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut)
|
||||
return;
|
||||
|
||||
lut = (struct drm_color_lut *)crtc->state->gamma_lut->data;
|
||||
|
||||
for (i = 0; i < CLUT_SIZE; i++, lut++) {
|
||||
val = ((lut->red << 8) & 0xff0000) | (lut->green & 0xff00) |
|
||||
(lut->blue >> 8) | (i << 24);
|
||||
reg_write(ldev->regs, LTDC_L1CLUTWR, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void ltdc_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_state)
|
||||
{
|
||||
|
@ -404,12 +448,35 @@ static void ltdc_crtc_atomic_disable(struct drm_crtc *crtc,
|
|||
reg_set(ldev->regs, LTDC_SRCR, SRCR_IMR);
|
||||
}
|
||||
|
||||
static bool ltdc_crtc_mode_fixup(struct drm_crtc *crtc,
|
||||
const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
|
||||
int rate = mode->clock * 1000;
|
||||
|
||||
/*
|
||||
* TODO clk_round_rate() does not work yet. When ready, it can
|
||||
* be used instead of clk_set_rate() then clk_get_rate().
|
||||
*/
|
||||
|
||||
clk_disable(ldev->pixel_clk);
|
||||
if (clk_set_rate(ldev->pixel_clk, rate) < 0) {
|
||||
DRM_ERROR("Cannot set rate (%dHz) for pixel clk\n", rate);
|
||||
return false;
|
||||
}
|
||||
clk_enable(ldev->pixel_clk);
|
||||
|
||||
adjusted_mode->clock = clk_get_rate(ldev->pixel_clk) / 1000;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
|
||||
{
|
||||
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
|
||||
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
|
||||
struct videomode vm;
|
||||
int rate = mode->clock * 1000;
|
||||
u32 hsync, vsync, accum_hbp, accum_vbp, accum_act_w, accum_act_h;
|
||||
u32 total_width, total_height;
|
||||
u32 val;
|
||||
|
@ -432,15 +499,6 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
|
|||
total_width = accum_act_w + vm.hfront_porch;
|
||||
total_height = accum_act_h + vm.vfront_porch;
|
||||
|
||||
clk_disable(ldev->pixel_clk);
|
||||
|
||||
if (clk_set_rate(ldev->pixel_clk, rate) < 0) {
|
||||
DRM_ERROR("Cannot set rate (%dHz) for pixel clk\n", rate);
|
||||
return;
|
||||
}
|
||||
|
||||
clk_enable(ldev->pixel_clk);
|
||||
|
||||
/* Configures the HS, VS, DE and PC polarities. Default Active Low */
|
||||
val = 0;
|
||||
|
||||
|
@ -486,6 +544,8 @@ static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc,
|
|||
|
||||
DRM_DEBUG_ATOMIC("\n");
|
||||
|
||||
ltdc_crtc_update_clut(crtc);
|
||||
|
||||
/* Commit shadow registers = update planes at next vblank */
|
||||
reg_set(ldev->regs, LTDC_SRCR, SRCR_VBR);
|
||||
|
||||
|
@ -502,6 +562,7 @@ static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc,
|
|||
}
|
||||
|
||||
static const struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = {
|
||||
.mode_fixup = ltdc_crtc_mode_fixup,
|
||||
.mode_set_nofb = ltdc_crtc_mode_set_nofb,
|
||||
.atomic_flush = ltdc_crtc_atomic_flush,
|
||||
.atomic_enable = ltdc_crtc_atomic_enable,
|
||||
|
@ -533,6 +594,7 @@ static const struct drm_crtc_funcs ltdc_crtc_funcs = {
|
|||
.reset = drm_atomic_helper_crtc_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
|
||||
.gamma_set = drm_atomic_helper_legacy_gamma_set,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -638,6 +700,14 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
|
|||
|
||||
/* Specifies the blending factors */
|
||||
val = BF1_PAXCA | BF2_1PAXCA;
|
||||
if (!fb->format->has_alpha)
|
||||
val = BF1_CA | BF2_1CA;
|
||||
|
||||
/* Manage hw-specific capabilities */
|
||||
if (ldev->caps.non_alpha_only_l1 &&
|
||||
plane->type != DRM_PLANE_TYPE_PRIMARY)
|
||||
val = BF1_PAXCA | BF2_1PAXCA;
|
||||
|
||||
reg_update_bits(ldev->regs, LTDC_L1BFCR + lofs,
|
||||
LXBFCR_BF2 | LXBFCR_BF1, val);
|
||||
|
||||
|
@ -705,8 +775,8 @@ static struct drm_plane *ltdc_plane_create(struct drm_device *ddev,
|
|||
struct device *dev = ddev->dev;
|
||||
struct drm_plane *plane;
|
||||
unsigned int i, nb_fmt = 0;
|
||||
u32 formats[NB_PF];
|
||||
u32 drm_fmt;
|
||||
u32 formats[NB_PF * 2];
|
||||
u32 drm_fmt, drm_fmt_no_alpha;
|
||||
int ret;
|
||||
|
||||
/* Get supported pixel formats */
|
||||
|
@ -715,6 +785,18 @@ static struct drm_plane *ltdc_plane_create(struct drm_device *ddev,
|
|||
if (!drm_fmt)
|
||||
continue;
|
||||
formats[nb_fmt++] = drm_fmt;
|
||||
|
||||
/* Add the no-alpha related format if any & supported */
|
||||
drm_fmt_no_alpha = get_pixelformat_without_alpha(drm_fmt);
|
||||
if (!drm_fmt_no_alpha)
|
||||
continue;
|
||||
|
||||
/* Manage hw-specific capabilities */
|
||||
if (ldev->caps.non_alpha_only_l1 &&
|
||||
type != DRM_PLANE_TYPE_PRIMARY)
|
||||
continue;
|
||||
|
||||
formats[nb_fmt++] = drm_fmt_no_alpha;
|
||||
}
|
||||
|
||||
plane = devm_kzalloc(dev, sizeof(*plane), GFP_KERNEL);
|
||||
|
@ -765,6 +847,9 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
|
|||
|
||||
drm_crtc_helper_add(crtc, <dc_crtc_helper_funcs);
|
||||
|
||||
drm_mode_crtc_set_gamma_size(crtc, CLUT_SIZE);
|
||||
drm_crtc_enable_color_mgmt(crtc, 0, false, CLUT_SIZE);
|
||||
|
||||
DRM_DEBUG_DRIVER("CRTC:%d created\n", crtc->base.id);
|
||||
|
||||
/* Add planes. Note : the first layer is used by primary plane */
|
||||
|
@ -839,10 +924,19 @@ static int ltdc_get_caps(struct drm_device *ddev)
|
|||
case HWVER_10300:
|
||||
ldev->caps.reg_ofs = REG_OFS_NONE;
|
||||
ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a0;
|
||||
/*
|
||||
* Hw older versions support non-alpha color formats derived
|
||||
* from native alpha color formats only on the primary layer.
|
||||
* For instance, RG16 native format without alpha works fine
|
||||
* on 2nd layer but XR24 (derived color format from AR24)
|
||||
* does not work on 2nd layer.
|
||||
*/
|
||||
ldev->caps.non_alpha_only_l1 = true;
|
||||
break;
|
||||
case HWVER_20101:
|
||||
ldev->caps.reg_ofs = REG_OFS_4;
|
||||
ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a1;
|
||||
ldev->caps.non_alpha_only_l1 = false;
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
|
|
|
@ -17,6 +17,7 @@ struct ltdc_caps {
|
|||
u32 reg_ofs; /* register offset for applicable regs */
|
||||
u32 bus_width; /* bus width (32 or 64 bits) */
|
||||
const u32 *pix_fmt_hw; /* supported pixel formats */
|
||||
bool non_alpha_only_l1; /* non-native no-alpha formats on layer 1 */
|
||||
};
|
||||
|
||||
struct ltdc_device {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
sun4i-backend-y += sun4i_backend.o sun4i_layer.o
|
||||
sun4i-frontend-y += sun4i_frontend.o
|
||||
|
||||
sun4i-drm-y += sun4i_drv.o
|
||||
sun4i-drm-y += sun4i_framebuffer.o
|
||||
|
@ -24,6 +25,6 @@ obj-$(CONFIG_DRM_SUN4I) += sun4i-tcon.o
|
|||
obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o
|
||||
obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o
|
||||
|
||||
obj-$(CONFIG_DRM_SUN4I_BACKEND) += sun4i-backend.o
|
||||
obj-$(CONFIG_DRM_SUN4I_BACKEND) += sun4i-backend.o sun4i-frontend.o
|
||||
obj-$(CONFIG_DRM_SUN4I_HDMI) += sun4i-drm-hdmi.o
|
||||
obj-$(CONFIG_DRM_SUN8I_MIXER) += sun8i-mixer.o
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
|
@ -26,6 +27,7 @@
|
|||
|
||||
#include "sun4i_backend.h"
|
||||
#include "sun4i_drv.h"
|
||||
#include "sun4i_frontend.h"
|
||||
#include "sun4i_layer.h"
|
||||
#include "sunxi_engine.h"
|
||||
|
||||
|
@ -93,7 +95,7 @@ void sun4i_backend_layer_enable(struct sun4i_backend *backend,
|
|||
static int sun4i_backend_drm_format_to_layer(struct drm_plane *plane,
|
||||
u32 format, u32 *mode)
|
||||
{
|
||||
if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
|
||||
if (plane && (plane->type == DRM_PLANE_TYPE_PRIMARY) &&
|
||||
(format == DRM_FORMAT_ARGB8888))
|
||||
format = DRM_FORMAT_XRGB8888;
|
||||
|
||||
|
@ -141,7 +143,6 @@ int sun4i_backend_update_layer_coord(struct sun4i_backend *backend,
|
|||
int layer, struct drm_plane *plane)
|
||||
{
|
||||
struct drm_plane_state *state = plane->state;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
|
||||
DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
|
||||
|
||||
|
@ -153,12 +154,6 @@ int sun4i_backend_update_layer_coord(struct sun4i_backend *backend,
|
|||
state->crtc_h));
|
||||
}
|
||||
|
||||
/* Set the line width */
|
||||
DRM_DEBUG_DRIVER("Layer line width: %d bits\n", fb->pitches[0] * 8);
|
||||
regmap_write(backend->engine.regs,
|
||||
SUN4I_BACKEND_LAYLINEWIDTH_REG(layer),
|
||||
fb->pitches[0] * 8);
|
||||
|
||||
/* Set height and width */
|
||||
DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
|
||||
state->crtc_w, state->crtc_h);
|
||||
|
@ -210,6 +205,30 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int sun4i_backend_update_layer_frontend(struct sun4i_backend *backend,
|
||||
int layer, uint32_t fmt)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = sun4i_backend_drm_format_to_layer(NULL, fmt, &val);
|
||||
if (ret) {
|
||||
DRM_DEBUG_DRIVER("Invalid format\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
regmap_update_bits(backend->engine.regs,
|
||||
SUN4I_BACKEND_ATTCTL_REG0(layer),
|
||||
SUN4I_BACKEND_ATTCTL_REG0_LAY_VDOEN,
|
||||
SUN4I_BACKEND_ATTCTL_REG0_LAY_VDOEN);
|
||||
|
||||
regmap_update_bits(backend->engine.regs,
|
||||
SUN4I_BACKEND_ATTCTL_REG1(layer),
|
||||
SUN4I_BACKEND_ATTCTL_REG1_LAY_FBFMT, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
|
||||
int layer, struct drm_plane *plane)
|
||||
{
|
||||
|
@ -218,6 +237,12 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
|
|||
u32 lo_paddr, hi_paddr;
|
||||
dma_addr_t paddr;
|
||||
|
||||
/* Set the line width */
|
||||
DRM_DEBUG_DRIVER("Layer line width: %d bits\n", fb->pitches[0] * 8);
|
||||
regmap_write(backend->engine.regs,
|
||||
SUN4I_BACKEND_LAYLINEWIDTH_REG(layer),
|
||||
fb->pitches[0] * 8);
|
||||
|
||||
/* Get the start of the displayed memory */
|
||||
paddr = drm_fb_cma_get_gem_addr(fb, state, 0);
|
||||
DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
|
||||
|
@ -246,6 +271,176 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int sun4i_backend_update_layer_zpos(struct sun4i_backend *backend, int layer,
|
||||
struct drm_plane *plane)
|
||||
{
|
||||
struct drm_plane_state *state = plane->state;
|
||||
unsigned int priority = state->normalized_zpos;
|
||||
|
||||
DRM_DEBUG_DRIVER("Setting layer %d's priority to %d\n", layer, priority);
|
||||
|
||||
regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_ATTCTL_REG0(layer),
|
||||
SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL_MASK,
|
||||
SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL(priority));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool sun4i_backend_plane_uses_scaler(struct drm_plane_state *state)
|
||||
{
|
||||
u16 src_h = state->src_h >> 16;
|
||||
u16 src_w = state->src_w >> 16;
|
||||
|
||||
DRM_DEBUG_DRIVER("Input size %dx%d, output size %dx%d\n",
|
||||
src_w, src_h, state->crtc_w, state->crtc_h);
|
||||
|
||||
if ((state->crtc_h != src_h) || (state->crtc_w != src_w))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool sun4i_backend_plane_uses_frontend(struct drm_plane_state *state)
|
||||
{
|
||||
struct sun4i_layer *layer = plane_to_sun4i_layer(state->plane);
|
||||
struct sun4i_backend *backend = layer->backend;
|
||||
|
||||
if (IS_ERR(backend->frontend))
|
||||
return false;
|
||||
|
||||
return sun4i_backend_plane_uses_scaler(state);
|
||||
}
|
||||
|
||||
static void sun4i_backend_atomic_begin(struct sunxi_engine *engine,
|
||||
struct drm_crtc_state *old_state)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
WARN_ON(regmap_read_poll_timeout(engine->regs,
|
||||
SUN4I_BACKEND_REGBUFFCTL_REG,
|
||||
val, !(val & SUN4I_BACKEND_REGBUFFCTL_LOADCTL),
|
||||
100, 50000));
|
||||
}
|
||||
|
||||
static int sun4i_backend_atomic_check(struct sunxi_engine *engine,
|
||||
struct drm_crtc_state *crtc_state)
|
||||
{
|
||||
struct drm_atomic_state *state = crtc_state->state;
|
||||
struct drm_device *drm = state->dev;
|
||||
struct drm_plane *plane;
|
||||
unsigned int num_planes = 0;
|
||||
unsigned int num_alpha_planes = 0;
|
||||
unsigned int num_frontend_planes = 0;
|
||||
|
||||
DRM_DEBUG_DRIVER("Starting checking our planes\n");
|
||||
|
||||
if (!crtc_state->planes_changed)
|
||||
return 0;
|
||||
|
||||
drm_for_each_plane_mask(plane, drm, crtc_state->plane_mask) {
|
||||
struct drm_plane_state *plane_state =
|
||||
drm_atomic_get_plane_state(state, plane);
|
||||
struct sun4i_layer_state *layer_state =
|
||||
state_to_sun4i_layer_state(plane_state);
|
||||
struct drm_framebuffer *fb = plane_state->fb;
|
||||
struct drm_format_name_buf format_name;
|
||||
|
||||
if (sun4i_backend_plane_uses_frontend(plane_state)) {
|
||||
DRM_DEBUG_DRIVER("Using the frontend for plane %d\n",
|
||||
plane->index);
|
||||
|
||||
layer_state->uses_frontend = true;
|
||||
num_frontend_planes++;
|
||||
} else {
|
||||
layer_state->uses_frontend = false;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DRIVER("Plane FB format is %s\n",
|
||||
drm_get_format_name(fb->format->format,
|
||||
&format_name));
|
||||
if (fb->format->has_alpha)
|
||||
num_alpha_planes++;
|
||||
|
||||
num_planes++;
|
||||
}
|
||||
|
||||
/*
|
||||
* The hardware is a bit unusual here.
|
||||
*
|
||||
* Even though it supports 4 layers, it does the composition
|
||||
* in two separate steps.
|
||||
*
|
||||
* The first one is assigning a layer to one of its two
|
||||
* pipes. If more that 1 layer is assigned to the same pipe,
|
||||
* and if pixels overlaps, the pipe will take the pixel from
|
||||
* the layer with the highest priority.
|
||||
*
|
||||
* The second step is the actual alpha blending, that takes
|
||||
* the two pipes as input, and uses the eventual alpha
|
||||
* component to do the transparency between the two.
|
||||
*
|
||||
* This two steps scenario makes us unable to guarantee a
|
||||
* robust alpha blending between the 4 layers in all
|
||||
* situations, since this means that we need to have one layer
|
||||
* with alpha at the lowest position of our two pipes.
|
||||
*
|
||||
* However, we cannot even do that, since the hardware has a
|
||||
* bug where the lowest plane of the lowest pipe (pipe 0,
|
||||
* priority 0), if it has any alpha, will discard the pixel
|
||||
* entirely and just display the pixels in the background
|
||||
* color (black by default).
|
||||
*
|
||||
* This means that we effectively have only three valid
|
||||
* configurations with alpha, all of them with the alpha being
|
||||
* on pipe1 with the lowest position, which can be 1, 2 or 3
|
||||
* depending on the number of planes and their zpos.
|
||||
*/
|
||||
if (num_alpha_planes > SUN4I_BACKEND_NUM_ALPHA_LAYERS) {
|
||||
DRM_DEBUG_DRIVER("Too many planes with alpha, rejecting...\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (num_frontend_planes > SUN4I_BACKEND_NUM_FRONTEND_LAYERS) {
|
||||
DRM_DEBUG_DRIVER("Too many planes going through the frontend, rejecting\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DRIVER("State valid with %u planes, %u alpha, %u video\n",
|
||||
num_planes, num_alpha_planes, num_frontend_planes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sun4i_backend_vblank_quirk(struct sunxi_engine *engine)
|
||||
{
|
||||
struct sun4i_backend *backend = engine_to_sun4i_backend(engine);
|
||||
struct sun4i_frontend *frontend = backend->frontend;
|
||||
|
||||
if (!frontend)
|
||||
return;
|
||||
|
||||
/*
|
||||
* In a teardown scenario with the frontend involved, we have
|
||||
* to keep the frontend enabled until the next vblank, and
|
||||
* only then disable it.
|
||||
*
|
||||
* This is due to the fact that the backend will not take into
|
||||
* account the new configuration (with the plane that used to
|
||||
* be fed by the frontend now disabled) until we write to the
|
||||
* commit bit and the hardware fetches the new configuration
|
||||
* during the next vblank.
|
||||
*
|
||||
* So we keep the frontend around in order to prevent any
|
||||
* visual artifacts.
|
||||
*/
|
||||
spin_lock(&backend->frontend_lock);
|
||||
if (backend->frontend_teardown) {
|
||||
sun4i_frontend_exit(frontend);
|
||||
backend->frontend_teardown = false;
|
||||
}
|
||||
spin_unlock(&backend->frontend_lock);
|
||||
};
|
||||
|
||||
static int sun4i_backend_init_sat(struct device *dev) {
|
||||
struct sun4i_backend *backend = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
@ -330,11 +525,43 @@ static int sun4i_backend_of_get_id(struct device_node *node)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* TODO: This needs to take multiple pipelines into account */
|
||||
static struct sun4i_frontend *sun4i_backend_find_frontend(struct sun4i_drv *drv,
|
||||
struct device_node *node)
|
||||
{
|
||||
struct device_node *port, *ep, *remote;
|
||||
struct sun4i_frontend *frontend;
|
||||
|
||||
port = of_graph_get_port_by_id(node, 0);
|
||||
if (!port)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
for_each_available_child_of_node(port, ep) {
|
||||
remote = of_graph_get_remote_port_parent(ep);
|
||||
if (!remote)
|
||||
continue;
|
||||
|
||||
/* does this node match any registered engines? */
|
||||
list_for_each_entry(frontend, &drv->frontend_list, list) {
|
||||
if (remote == frontend->node) {
|
||||
of_node_put(remote);
|
||||
of_node_put(port);
|
||||
return frontend;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static const struct sunxi_engine_ops sun4i_backend_engine_ops = {
|
||||
.atomic_begin = sun4i_backend_atomic_begin,
|
||||
.atomic_check = sun4i_backend_atomic_check,
|
||||
.commit = sun4i_backend_commit,
|
||||
.layers_init = sun4i_layers_init,
|
||||
.apply_color_correction = sun4i_backend_apply_color_correction,
|
||||
.disable_color_correction = sun4i_backend_disable_color_correction,
|
||||
.vblank_quirk = sun4i_backend_vblank_quirk,
|
||||
};
|
||||
|
||||
static struct regmap_config sun4i_backend_regmap_config = {
|
||||
|
@ -360,6 +587,7 @@ static int sun4i_backend_bind(struct device *dev, struct device *master,
|
|||
if (!backend)
|
||||
return -ENOMEM;
|
||||
dev_set_drvdata(dev, backend);
|
||||
spin_lock_init(&backend->frontend_lock);
|
||||
|
||||
backend->engine.node = dev->of_node;
|
||||
backend->engine.ops = &sun4i_backend_engine_ops;
|
||||
|
@ -367,6 +595,10 @@ static int sun4i_backend_bind(struct device *dev, struct device *master,
|
|||
if (backend->engine.id < 0)
|
||||
return backend->engine.id;
|
||||
|
||||
backend->frontend = sun4i_backend_find_frontend(drv, dev->of_node);
|
||||
if (IS_ERR(backend->frontend))
|
||||
dev_warn(dev, "Couldn't find matching frontend, frontend features disabled\n");
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(regs))
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
#define SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(x) ((x) << 15)
|
||||
#define SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL_MASK GENMASK(11, 10)
|
||||
#define SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL(x) ((x) << 10)
|
||||
#define SUN4I_BACKEND_ATTCTL_REG0_LAY_VDOEN BIT(1)
|
||||
|
||||
#define SUN4I_BACKEND_ATTCTL_REG1(l) (0x8a0 + (0x4 * (l)))
|
||||
#define SUN4I_BACKEND_ATTCTL_REG1_LAY_HSCAFCT GENMASK(15, 14)
|
||||
|
@ -111,7 +112,9 @@
|
|||
#define SUN4I_BACKEND_SPRALPHACTL_REG 0x90c
|
||||
#define SUN4I_BACKEND_IYUVCTL_REG 0x920
|
||||
#define SUN4I_BACKEND_IYUVADD_REG(c) (0x930 + (0x4 * (c)))
|
||||
#define SUN4I_BACKEND_IYUVLINEWITDTH_REG(c) (0x940 + (0x4 * (c)))
|
||||
|
||||
#define SUN4I_BACKEND_IYUVLINEWIDTH_REG(c) (0x940 + (0x4 * (c)))
|
||||
|
||||
#define SUN4I_BACKEND_YGCOEF_REG(c) (0x950 + (0x4 * (c)))
|
||||
#define SUN4I_BACKEND_YGCONS_REG 0x95c
|
||||
#define SUN4I_BACKEND_URCOEF_REG(c) (0x960 + (0x4 * (c)))
|
||||
|
@ -143,8 +146,13 @@
|
|||
#define SUN4I_BACKEND_HWCCOLORTAB_OFF 0x4c00
|
||||
#define SUN4I_BACKEND_PIPE_OFF(p) (0x5000 + (0x400 * (p)))
|
||||
|
||||
#define SUN4I_BACKEND_NUM_LAYERS 4
|
||||
#define SUN4I_BACKEND_NUM_ALPHA_LAYERS 1
|
||||
#define SUN4I_BACKEND_NUM_FRONTEND_LAYERS 1
|
||||
|
||||
struct sun4i_backend {
|
||||
struct sunxi_engine engine;
|
||||
struct sun4i_frontend *frontend;
|
||||
|
||||
struct reset_control *reset;
|
||||
|
||||
|
@ -154,6 +162,10 @@ struct sun4i_backend {
|
|||
|
||||
struct clk *sat_clk;
|
||||
struct reset_control *sat_reset;
|
||||
|
||||
/* Protects against races in the frontend teardown */
|
||||
spinlock_t frontend_lock;
|
||||
bool frontend_teardown;
|
||||
};
|
||||
|
||||
static inline struct sun4i_backend *
|
||||
|
@ -170,5 +182,9 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend,
|
|||
int layer, struct drm_plane *plane);
|
||||
int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
|
||||
int layer, struct drm_plane *plane);
|
||||
int sun4i_backend_update_layer_frontend(struct sun4i_backend *backend,
|
||||
int layer, uint32_t in_fmt);
|
||||
int sun4i_backend_update_layer_zpos(struct sun4i_backend *backend,
|
||||
int layer, struct drm_plane *plane);
|
||||
|
||||
#endif /* _SUN4I_BACKEND_H_ */
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
#include <video/videomode.h>
|
||||
|
||||
#include "sun4i_backend.h"
|
||||
#include "sun4i_crtc.h"
|
||||
#include "sun4i_drv.h"
|
||||
#include "sunxi_engine.h"
|
||||
|
@ -46,11 +47,25 @@ static struct drm_encoder *sun4i_crtc_get_encoder(struct drm_crtc *crtc)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int sun4i_crtc_atomic_check(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *state)
|
||||
{
|
||||
struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
|
||||
struct sunxi_engine *engine = scrtc->engine;
|
||||
int ret = 0;
|
||||
|
||||
if (engine && engine->ops && engine->ops->atomic_check)
|
||||
ret = engine->ops->atomic_check(engine, state);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_state)
|
||||
{
|
||||
struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct sunxi_engine *engine = scrtc->engine;
|
||||
unsigned long flags;
|
||||
|
||||
if (crtc->state->event) {
|
||||
|
@ -60,7 +75,10 @@ static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc,
|
|||
scrtc->event = crtc->state->event;
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
crtc->state->event = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (engine->ops->atomic_begin)
|
||||
engine->ops->atomic_begin(engine, old_state);
|
||||
}
|
||||
|
||||
static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
|
||||
|
@ -125,6 +143,7 @@ static void sun4i_crtc_mode_set_nofb(struct drm_crtc *crtc)
|
|||
}
|
||||
|
||||
static const struct drm_crtc_helper_funcs sun4i_crtc_helper_funcs = {
|
||||
.atomic_check = sun4i_crtc_atomic_check,
|
||||
.atomic_begin = sun4i_crtc_atomic_begin,
|
||||
.atomic_flush = sun4i_crtc_atomic_flush,
|
||||
.atomic_enable = sun4i_crtc_atomic_enable,
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <drm/drm_of.h>
|
||||
|
||||
#include "sun4i_drv.h"
|
||||
#include "sun4i_frontend.h"
|
||||
#include "sun4i_framebuffer.h"
|
||||
#include "sun4i_tcon.h"
|
||||
|
||||
|
@ -91,6 +92,7 @@ static int sun4i_drv_bind(struct device *dev)
|
|||
goto free_drm;
|
||||
}
|
||||
drm->dev_private = drv;
|
||||
INIT_LIST_HEAD(&drv->frontend_list);
|
||||
INIT_LIST_HEAD(&drv->engine_list);
|
||||
INIT_LIST_HEAD(&drv->tcon_list);
|
||||
|
||||
|
@ -177,6 +179,14 @@ static bool sun4i_drv_node_is_frontend(struct device_node *node)
|
|||
of_device_is_compatible(node, "allwinner,sun8i-a33-display-frontend");
|
||||
}
|
||||
|
||||
static bool sun4i_drv_node_is_supported_frontend(struct device_node *node)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_DRM_SUN4I_BACKEND))
|
||||
return !!of_match_node(sun4i_frontend_of_table, node);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool sun4i_drv_node_is_tcon(struct device_node *node)
|
||||
{
|
||||
return !!of_match_node(sun4i_tcon_of_table, node);
|
||||
|
@ -225,9 +235,11 @@ static int sun4i_drv_add_endpoints(struct device *dev,
|
|||
int count = 0;
|
||||
|
||||
/*
|
||||
* We don't support the frontend for now, so we will never
|
||||
* have a device bound. Just skip over it, but we still want
|
||||
* the rest our pipeline to be added.
|
||||
* The frontend has been disabled in some of our old device
|
||||
* trees. If we find a node that is the frontend and is
|
||||
* disabled, we should just follow through and parse its
|
||||
* child, but without adding it to the component list.
|
||||
* Otherwise, we obviously want to add it to the list.
|
||||
*/
|
||||
if (!sun4i_drv_node_is_frontend(node) &&
|
||||
!of_device_is_available(node))
|
||||
|
@ -240,7 +252,14 @@ static int sun4i_drv_add_endpoints(struct device *dev,
|
|||
if (sun4i_drv_node_is_connector(node))
|
||||
return 0;
|
||||
|
||||
if (!sun4i_drv_node_is_frontend(node)) {
|
||||
/*
|
||||
* If the device is either just a regular device, or an
|
||||
* enabled frontend supported by the driver, we add it to our
|
||||
* component list.
|
||||
*/
|
||||
if (!sun4i_drv_node_is_frontend(node) ||
|
||||
(sun4i_drv_node_is_supported_frontend(node) &&
|
||||
of_device_is_available(node))) {
|
||||
/* Add current component */
|
||||
DRM_DEBUG_DRIVER("Adding component %pOF\n", node);
|
||||
drm_of_component_match_add(dev, match, compare_of, node);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
struct sun4i_drv {
|
||||
struct list_head engine_list;
|
||||
struct list_head frontend_list;
|
||||
struct list_head tcon_list;
|
||||
};
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
* the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
|
@ -19,13 +20,33 @@
|
|||
#include "sun4i_drv.h"
|
||||
#include "sun4i_framebuffer.h"
|
||||
|
||||
static int sun4i_de_atomic_check(struct drm_device *dev,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = drm_atomic_helper_check_modeset(dev, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = drm_atomic_normalize_zpos(dev, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return drm_atomic_helper_check_planes(dev, state);
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_funcs sun4i_de_mode_config_funcs = {
|
||||
.output_poll_changed = drm_fb_helper_output_poll_changed,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_check = sun4i_de_atomic_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
.fb_create = drm_gem_fb_create,
|
||||
};
|
||||
|
||||
static struct drm_mode_config_helper_funcs sun4i_de_mode_config_helpers = {
|
||||
.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
|
||||
};
|
||||
|
||||
int sun4i_framebuffer_init(struct drm_device *drm)
|
||||
{
|
||||
drm_mode_config_reset(drm);
|
||||
|
@ -34,6 +55,7 @@ int sun4i_framebuffer_init(struct drm_device *drm)
|
|||
drm->mode_config.max_height = 8192;
|
||||
|
||||
drm->mode_config.funcs = &sun4i_de_mode_config_funcs;
|
||||
drm->mode_config.helper_private = &sun4i_de_mode_config_helpers;
|
||||
|
||||
return drm_fb_cma_fbdev_init(drm, 32, 0);
|
||||
}
|
||||
|
|
389
drivers/gpu/drm/sun4i/sun4i_frontend.c
Normal file
389
drivers/gpu/drm/sun4i/sun4i_frontend.c
Normal file
|
@ -0,0 +1,389 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2017 Free Electrons
|
||||
* Maxime Ripard <maxime.ripard@free-electrons.com>
|
||||
*/
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include "sun4i_drv.h"
|
||||
#include "sun4i_frontend.h"
|
||||
|
||||
static const u32 sun4i_frontend_vert_coef[32] = {
|
||||
0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd,
|
||||
0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb,
|
||||
0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb,
|
||||
0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc,
|
||||
0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd,
|
||||
0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff,
|
||||
0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff,
|
||||
0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100,
|
||||
};
|
||||
|
||||
static const u32 sun4i_frontend_horz_coef[64] = {
|
||||
0x40000000, 0x00000000, 0x40fe0000, 0x0000ff03,
|
||||
0x3ffd0000, 0x0000ff05, 0x3ffc0000, 0x0000ff06,
|
||||
0x3efb0000, 0x0000ff08, 0x3dfb0000, 0x0000ff09,
|
||||
0x3bfa0000, 0x0000fe0d, 0x39fa0000, 0x0000fe0f,
|
||||
0x38fa0000, 0x0000fe10, 0x36fa0000, 0x0000fe12,
|
||||
0x33fa0000, 0x0000fd16, 0x31fa0000, 0x0000fd18,
|
||||
0x2ffa0000, 0x0000fd1a, 0x2cfa0000, 0x0000fc1e,
|
||||
0x29fa0000, 0x0000fc21, 0x27fb0000, 0x0000fb23,
|
||||
0x24fb0000, 0x0000fb26, 0x21fb0000, 0x0000fb29,
|
||||
0x1ffc0000, 0x0000fa2b, 0x1cfc0000, 0x0000fa2e,
|
||||
0x19fd0000, 0x0000fa30, 0x16fd0000, 0x0000fa33,
|
||||
0x14fd0000, 0x0000fa35, 0x11fe0000, 0x0000fa37,
|
||||
0x0ffe0000, 0x0000fa39, 0x0dfe0000, 0x0000fa3b,
|
||||
0x0afe0000, 0x0000fa3e, 0x08ff0000, 0x0000fb3e,
|
||||
0x06ff0000, 0x0000fb40, 0x05ff0000, 0x0000fc40,
|
||||
0x03ff0000, 0x0000fd41, 0x01ff0000, 0x0000fe42,
|
||||
};
|
||||
|
||||
static void sun4i_frontend_scaler_init(struct sun4i_frontend *frontend)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZCOEF0_REG(i),
|
||||
sun4i_frontend_horz_coef[2 * i]);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZCOEF0_REG(i),
|
||||
sun4i_frontend_horz_coef[2 * i]);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZCOEF1_REG(i),
|
||||
sun4i_frontend_horz_coef[2 * i + 1]);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZCOEF1_REG(i),
|
||||
sun4i_frontend_horz_coef[2 * i + 1]);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTCOEF_REG(i),
|
||||
sun4i_frontend_vert_coef[i]);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTCOEF_REG(i),
|
||||
sun4i_frontend_vert_coef[i]);
|
||||
}
|
||||
|
||||
regmap_update_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG,
|
||||
SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL,
|
||||
SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL);
|
||||
}
|
||||
|
||||
int sun4i_frontend_init(struct sun4i_frontend *frontend)
|
||||
{
|
||||
return pm_runtime_get_sync(frontend->dev);
|
||||
}
|
||||
EXPORT_SYMBOL(sun4i_frontend_init);
|
||||
|
||||
void sun4i_frontend_exit(struct sun4i_frontend *frontend)
|
||||
{
|
||||
pm_runtime_put(frontend->dev);
|
||||
}
|
||||
EXPORT_SYMBOL(sun4i_frontend_exit);
|
||||
|
||||
void sun4i_frontend_update_buffer(struct sun4i_frontend *frontend,
|
||||
struct drm_plane *plane)
|
||||
{
|
||||
struct drm_plane_state *state = plane->state;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
dma_addr_t paddr;
|
||||
|
||||
/* Set the line width */
|
||||
DRM_DEBUG_DRIVER("Frontend stride: %d bytes\n", fb->pitches[0]);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_LINESTRD0_REG,
|
||||
fb->pitches[0]);
|
||||
|
||||
/* Set the physical address of the buffer in memory */
|
||||
paddr = drm_fb_cma_get_gem_addr(fb, state, 0);
|
||||
paddr -= PHYS_OFFSET;
|
||||
DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR0_REG, paddr);
|
||||
}
|
||||
EXPORT_SYMBOL(sun4i_frontend_update_buffer);
|
||||
|
||||
static int sun4i_frontend_drm_format_to_input_fmt(uint32_t fmt, u32 *val)
|
||||
{
|
||||
switch (fmt) {
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
*val = 5;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int sun4i_frontend_drm_format_to_output_fmt(uint32_t fmt, u32 *val)
|
||||
{
|
||||
switch (fmt) {
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
*val = 2;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int sun4i_frontend_update_formats(struct sun4i_frontend *frontend,
|
||||
struct drm_plane *plane, uint32_t out_fmt)
|
||||
{
|
||||
struct drm_plane_state *state = plane->state;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
u32 out_fmt_val;
|
||||
u32 in_fmt_val;
|
||||
int ret;
|
||||
|
||||
ret = sun4i_frontend_drm_format_to_input_fmt(fb->format->format,
|
||||
&in_fmt_val);
|
||||
if (ret) {
|
||||
DRM_DEBUG_DRIVER("Invalid input format\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sun4i_frontend_drm_format_to_output_fmt(out_fmt, &out_fmt_val);
|
||||
if (ret) {
|
||||
DRM_DEBUG_DRIVER("Invalid output format\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* I have no idea what this does exactly, but it seems to be
|
||||
* related to the scaler FIR filter phase parameters.
|
||||
*/
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZPHASE_REG, 0x400);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZPHASE_REG, 0x400);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE0_REG, 0x400);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE0_REG, 0x400);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE1_REG, 0x400);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE1_REG, 0x400);
|
||||
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_INPUT_FMT_REG,
|
||||
SUN4I_FRONTEND_INPUT_FMT_DATA_MOD(1) |
|
||||
SUN4I_FRONTEND_INPUT_FMT_DATA_FMT(in_fmt_val) |
|
||||
SUN4I_FRONTEND_INPUT_FMT_PS(1));
|
||||
|
||||
/*
|
||||
* TODO: It look like the A31 and A80 at least will need the
|
||||
* bit 7 (ALPHA_EN) enabled when using a format with alpha (so
|
||||
* ARGB8888).
|
||||
*/
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_OUTPUT_FMT_REG,
|
||||
SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT(out_fmt_val));
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(sun4i_frontend_update_formats);
|
||||
|
||||
void sun4i_frontend_update_coord(struct sun4i_frontend *frontend,
|
||||
struct drm_plane *plane)
|
||||
{
|
||||
struct drm_plane_state *state = plane->state;
|
||||
|
||||
/* Set height and width */
|
||||
DRM_DEBUG_DRIVER("Frontend size W: %u H: %u\n",
|
||||
state->crtc_w, state->crtc_h);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_INSIZE_REG,
|
||||
SUN4I_FRONTEND_INSIZE(state->src_h >> 16,
|
||||
state->src_w >> 16));
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_INSIZE_REG,
|
||||
SUN4I_FRONTEND_INSIZE(state->src_h >> 16,
|
||||
state->src_w >> 16));
|
||||
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_OUTSIZE_REG,
|
||||
SUN4I_FRONTEND_OUTSIZE(state->crtc_h, state->crtc_w));
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_OUTSIZE_REG,
|
||||
SUN4I_FRONTEND_OUTSIZE(state->crtc_h, state->crtc_w));
|
||||
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZFACT_REG,
|
||||
state->src_w / state->crtc_w);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZFACT_REG,
|
||||
state->src_w / state->crtc_w);
|
||||
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTFACT_REG,
|
||||
state->src_h / state->crtc_h);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTFACT_REG,
|
||||
state->src_h / state->crtc_h);
|
||||
|
||||
regmap_write_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG,
|
||||
SUN4I_FRONTEND_FRM_CTRL_REG_RDY,
|
||||
SUN4I_FRONTEND_FRM_CTRL_REG_RDY);
|
||||
}
|
||||
EXPORT_SYMBOL(sun4i_frontend_update_coord);
|
||||
|
||||
int sun4i_frontend_enable(struct sun4i_frontend *frontend)
|
||||
{
|
||||
regmap_write_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG,
|
||||
SUN4I_FRONTEND_FRM_CTRL_FRM_START,
|
||||
SUN4I_FRONTEND_FRM_CTRL_FRM_START);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(sun4i_frontend_enable);
|
||||
|
||||
static struct regmap_config sun4i_frontend_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = 0x0a14,
|
||||
};
|
||||
|
||||
static int sun4i_frontend_bind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct sun4i_frontend *frontend;
|
||||
struct drm_device *drm = data;
|
||||
struct sun4i_drv *drv = drm->dev_private;
|
||||
struct resource *res;
|
||||
void __iomem *regs;
|
||||
|
||||
frontend = devm_kzalloc(dev, sizeof(*frontend), GFP_KERNEL);
|
||||
if (!frontend)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, frontend);
|
||||
frontend->dev = dev;
|
||||
frontend->node = dev->of_node;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
||||
frontend->regs = devm_regmap_init_mmio(dev, regs,
|
||||
&sun4i_frontend_regmap_config);
|
||||
if (IS_ERR(frontend->regs)) {
|
||||
dev_err(dev, "Couldn't create the frontend regmap\n");
|
||||
return PTR_ERR(frontend->regs);
|
||||
}
|
||||
|
||||
frontend->reset = devm_reset_control_get(dev, NULL);
|
||||
if (IS_ERR(frontend->reset)) {
|
||||
dev_err(dev, "Couldn't get our reset line\n");
|
||||
return PTR_ERR(frontend->reset);
|
||||
}
|
||||
|
||||
frontend->bus_clk = devm_clk_get(dev, "ahb");
|
||||
if (IS_ERR(frontend->bus_clk)) {
|
||||
dev_err(dev, "Couldn't get our bus clock\n");
|
||||
return PTR_ERR(frontend->bus_clk);
|
||||
}
|
||||
|
||||
frontend->mod_clk = devm_clk_get(dev, "mod");
|
||||
if (IS_ERR(frontend->mod_clk)) {
|
||||
dev_err(dev, "Couldn't get our mod clock\n");
|
||||
return PTR_ERR(frontend->mod_clk);
|
||||
}
|
||||
|
||||
frontend->ram_clk = devm_clk_get(dev, "ram");
|
||||
if (IS_ERR(frontend->ram_clk)) {
|
||||
dev_err(dev, "Couldn't get our ram clock\n");
|
||||
return PTR_ERR(frontend->ram_clk);
|
||||
}
|
||||
|
||||
list_add_tail(&frontend->list, &drv->frontend_list);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sun4i_frontend_unbind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct sun4i_frontend *frontend = dev_get_drvdata(dev);
|
||||
|
||||
list_del(&frontend->list);
|
||||
pm_runtime_force_suspend(dev);
|
||||
}
|
||||
|
||||
static const struct component_ops sun4i_frontend_ops = {
|
||||
.bind = sun4i_frontend_bind,
|
||||
.unbind = sun4i_frontend_unbind,
|
||||
};
|
||||
|
||||
static int sun4i_frontend_probe(struct platform_device *pdev)
|
||||
{
|
||||
return component_add(&pdev->dev, &sun4i_frontend_ops);
|
||||
}
|
||||
|
||||
static int sun4i_frontend_remove(struct platform_device *pdev)
|
||||
{
|
||||
component_del(&pdev->dev, &sun4i_frontend_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun4i_frontend_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct sun4i_frontend *frontend = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
clk_set_rate(frontend->mod_clk, 300000000);
|
||||
|
||||
clk_prepare_enable(frontend->bus_clk);
|
||||
clk_prepare_enable(frontend->mod_clk);
|
||||
clk_prepare_enable(frontend->ram_clk);
|
||||
|
||||
ret = reset_control_reset(frontend->reset);
|
||||
if (ret) {
|
||||
dev_err(dev, "Couldn't reset our device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
regmap_update_bits(frontend->regs, SUN4I_FRONTEND_EN_REG,
|
||||
SUN4I_FRONTEND_EN_EN,
|
||||
SUN4I_FRONTEND_EN_EN);
|
||||
|
||||
regmap_update_bits(frontend->regs, SUN4I_FRONTEND_BYPASS_REG,
|
||||
SUN4I_FRONTEND_BYPASS_CSC_EN,
|
||||
SUN4I_FRONTEND_BYPASS_CSC_EN);
|
||||
|
||||
sun4i_frontend_scaler_init(frontend);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun4i_frontend_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct sun4i_frontend *frontend = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(frontend->ram_clk);
|
||||
clk_disable_unprepare(frontend->mod_clk);
|
||||
clk_disable_unprepare(frontend->bus_clk);
|
||||
|
||||
reset_control_assert(frontend->reset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops sun4i_frontend_pm_ops = {
|
||||
.runtime_resume = sun4i_frontend_runtime_resume,
|
||||
.runtime_suspend = sun4i_frontend_runtime_suspend,
|
||||
};
|
||||
|
||||
const struct of_device_id sun4i_frontend_of_table[] = {
|
||||
{ .compatible = "allwinner,sun8i-a33-display-frontend" },
|
||||
{ }
|
||||
};
|
||||
EXPORT_SYMBOL(sun4i_frontend_of_table);
|
||||
MODULE_DEVICE_TABLE(of, sun4i_frontend_of_table);
|
||||
|
||||
static struct platform_driver sun4i_frontend_driver = {
|
||||
.probe = sun4i_frontend_probe,
|
||||
.remove = sun4i_frontend_remove,
|
||||
.driver = {
|
||||
.name = "sun4i-frontend",
|
||||
.of_match_table = sun4i_frontend_of_table,
|
||||
.pm = &sun4i_frontend_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(sun4i_frontend_driver);
|
||||
|
||||
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
|
||||
MODULE_DESCRIPTION("Allwinner A10 Display Engine Frontend Driver");
|
||||
MODULE_LICENSE("GPL");
|
99
drivers/gpu/drm/sun4i/sun4i_frontend.h
Normal file
99
drivers/gpu/drm/sun4i/sun4i_frontend.h
Normal file
|
@ -0,0 +1,99 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2017 Free Electrons
|
||||
* Maxime Ripard <maxime.ripard@free-electrons.com>
|
||||
*/
|
||||
|
||||
#ifndef _SUN4I_FRONTEND_H_
|
||||
#define _SUN4I_FRONTEND_H_
|
||||
|
||||
#include <linux/list.h>
|
||||
|
||||
#define SUN4I_FRONTEND_EN_REG 0x000
|
||||
#define SUN4I_FRONTEND_EN_EN BIT(0)
|
||||
|
||||
#define SUN4I_FRONTEND_FRM_CTRL_REG 0x004
|
||||
#define SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL BIT(23)
|
||||
#define SUN4I_FRONTEND_FRM_CTRL_FRM_START BIT(16)
|
||||
#define SUN4I_FRONTEND_FRM_CTRL_COEF_RDY BIT(1)
|
||||
#define SUN4I_FRONTEND_FRM_CTRL_REG_RDY BIT(0)
|
||||
|
||||
#define SUN4I_FRONTEND_BYPASS_REG 0x008
|
||||
#define SUN4I_FRONTEND_BYPASS_CSC_EN BIT(1)
|
||||
|
||||
#define SUN4I_FRONTEND_BUF_ADDR0_REG 0x020
|
||||
|
||||
#define SUN4I_FRONTEND_LINESTRD0_REG 0x040
|
||||
|
||||
#define SUN4I_FRONTEND_INPUT_FMT_REG 0x04c
|
||||
#define SUN4I_FRONTEND_INPUT_FMT_DATA_MOD(mod) ((mod) << 8)
|
||||
#define SUN4I_FRONTEND_INPUT_FMT_DATA_FMT(fmt) ((fmt) << 4)
|
||||
#define SUN4I_FRONTEND_INPUT_FMT_PS(ps) (ps)
|
||||
|
||||
#define SUN4I_FRONTEND_OUTPUT_FMT_REG 0x05c
|
||||
#define SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT(fmt) (fmt)
|
||||
|
||||
#define SUN4I_FRONTEND_CH0_INSIZE_REG 0x100
|
||||
#define SUN4I_FRONTEND_INSIZE(h, w) ((((h) - 1) << 16) | (((w) - 1)))
|
||||
|
||||
#define SUN4I_FRONTEND_CH0_OUTSIZE_REG 0x104
|
||||
#define SUN4I_FRONTEND_OUTSIZE(h, w) ((((h) - 1) << 16) | (((w) - 1)))
|
||||
|
||||
#define SUN4I_FRONTEND_CH0_HORZFACT_REG 0x108
|
||||
#define SUN4I_FRONTEND_HORZFACT(i, f) (((i) << 16) | (f))
|
||||
|
||||
#define SUN4I_FRONTEND_CH0_VERTFACT_REG 0x10c
|
||||
#define SUN4I_FRONTEND_VERTFACT(i, f) (((i) << 16) | (f))
|
||||
|
||||
#define SUN4I_FRONTEND_CH0_HORZPHASE_REG 0x110
|
||||
#define SUN4I_FRONTEND_CH0_VERTPHASE0_REG 0x114
|
||||
#define SUN4I_FRONTEND_CH0_VERTPHASE1_REG 0x118
|
||||
|
||||
#define SUN4I_FRONTEND_CH1_INSIZE_REG 0x200
|
||||
#define SUN4I_FRONTEND_CH1_OUTSIZE_REG 0x204
|
||||
#define SUN4I_FRONTEND_CH1_HORZFACT_REG 0x208
|
||||
#define SUN4I_FRONTEND_CH1_VERTFACT_REG 0x20c
|
||||
|
||||
#define SUN4I_FRONTEND_CH1_HORZPHASE_REG 0x210
|
||||
#define SUN4I_FRONTEND_CH1_VERTPHASE0_REG 0x214
|
||||
#define SUN4I_FRONTEND_CH1_VERTPHASE1_REG 0x218
|
||||
|
||||
#define SUN4I_FRONTEND_CH0_HORZCOEF0_REG(i) (0x400 + i * 4)
|
||||
#define SUN4I_FRONTEND_CH0_HORZCOEF1_REG(i) (0x480 + i * 4)
|
||||
#define SUN4I_FRONTEND_CH0_VERTCOEF_REG(i) (0x500 + i * 4)
|
||||
#define SUN4I_FRONTEND_CH1_HORZCOEF0_REG(i) (0x600 + i * 4)
|
||||
#define SUN4I_FRONTEND_CH1_HORZCOEF1_REG(i) (0x680 + i * 4)
|
||||
#define SUN4I_FRONTEND_CH1_VERTCOEF_REG(i) (0x700 + i * 4)
|
||||
|
||||
struct clk;
|
||||
struct device_node;
|
||||
struct drm_plane;
|
||||
struct regmap;
|
||||
struct reset_control;
|
||||
|
||||
struct sun4i_frontend {
|
||||
struct list_head list;
|
||||
struct device *dev;
|
||||
struct device_node *node;
|
||||
|
||||
struct clk *bus_clk;
|
||||
struct clk *mod_clk;
|
||||
struct clk *ram_clk;
|
||||
struct regmap *regs;
|
||||
struct reset_control *reset;
|
||||
};
|
||||
|
||||
extern const struct of_device_id sun4i_frontend_of_table[];
|
||||
|
||||
int sun4i_frontend_init(struct sun4i_frontend *frontend);
|
||||
void sun4i_frontend_exit(struct sun4i_frontend *frontend);
|
||||
int sun4i_frontend_enable(struct sun4i_frontend *frontend);
|
||||
|
||||
void sun4i_frontend_update_buffer(struct sun4i_frontend *frontend,
|
||||
struct drm_plane *plane);
|
||||
void sun4i_frontend_update_coord(struct sun4i_frontend *frontend,
|
||||
struct drm_plane *plane);
|
||||
int sun4i_frontend_update_formats(struct sun4i_frontend *frontend,
|
||||
struct drm_plane *plane, uint32_t out_fmt);
|
||||
|
||||
#endif /* _SUN4I_FRONTEND_H_ */
|
|
@ -15,34 +15,107 @@
|
|||
#include <drm/drmP.h>
|
||||
|
||||
#include "sun4i_backend.h"
|
||||
#include "sun4i_frontend.h"
|
||||
#include "sun4i_layer.h"
|
||||
#include "sunxi_engine.h"
|
||||
|
||||
struct sun4i_plane_desc {
|
||||
enum drm_plane_type type;
|
||||
u8 pipe;
|
||||
const uint32_t *formats;
|
||||
uint32_t nformats;
|
||||
enum drm_plane_type type;
|
||||
u8 pipe;
|
||||
const uint32_t *formats;
|
||||
uint32_t nformats;
|
||||
};
|
||||
|
||||
static void sun4i_backend_layer_reset(struct drm_plane *plane)
|
||||
{
|
||||
struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
|
||||
struct sun4i_layer_state *state;
|
||||
|
||||
if (plane->state) {
|
||||
state = state_to_sun4i_layer_state(plane->state);
|
||||
|
||||
__drm_atomic_helper_plane_destroy_state(&state->state);
|
||||
|
||||
kfree(state);
|
||||
plane->state = NULL;
|
||||
}
|
||||
|
||||
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
||||
if (state) {
|
||||
plane->state = &state->state;
|
||||
plane->state->plane = plane;
|
||||
plane->state->zpos = layer->id;
|
||||
}
|
||||
}
|
||||
|
||||
static struct drm_plane_state *
|
||||
sun4i_backend_layer_duplicate_state(struct drm_plane *plane)
|
||||
{
|
||||
struct sun4i_layer_state *orig = state_to_sun4i_layer_state(plane->state);
|
||||
struct sun4i_layer_state *copy;
|
||||
|
||||
copy = kzalloc(sizeof(*copy), GFP_KERNEL);
|
||||
if (!copy)
|
||||
return NULL;
|
||||
|
||||
__drm_atomic_helper_plane_duplicate_state(plane, ©->state);
|
||||
copy->uses_frontend = orig->uses_frontend;
|
||||
|
||||
return ©->state;
|
||||
}
|
||||
|
||||
static void sun4i_backend_layer_destroy_state(struct drm_plane *plane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct sun4i_layer_state *s_state = state_to_sun4i_layer_state(state);
|
||||
|
||||
__drm_atomic_helper_plane_destroy_state(state);
|
||||
|
||||
kfree(s_state);
|
||||
}
|
||||
|
||||
static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
|
||||
struct drm_plane_state *old_state)
|
||||
{
|
||||
struct sun4i_layer_state *layer_state = state_to_sun4i_layer_state(old_state);
|
||||
struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
|
||||
struct sun4i_backend *backend = layer->backend;
|
||||
|
||||
sun4i_backend_layer_enable(backend, layer->id, false);
|
||||
|
||||
if (layer_state->uses_frontend) {
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&backend->frontend_lock, flags);
|
||||
backend->frontend_teardown = true;
|
||||
spin_unlock_irqrestore(&backend->frontend_lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
|
||||
struct drm_plane_state *old_state)
|
||||
{
|
||||
struct sun4i_layer_state *layer_state = state_to_sun4i_layer_state(plane->state);
|
||||
struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
|
||||
struct sun4i_backend *backend = layer->backend;
|
||||
struct sun4i_frontend *frontend = backend->frontend;
|
||||
|
||||
if (layer_state->uses_frontend) {
|
||||
sun4i_frontend_init(frontend);
|
||||
sun4i_frontend_update_coord(frontend, plane);
|
||||
sun4i_frontend_update_buffer(frontend, plane);
|
||||
sun4i_frontend_update_formats(frontend, plane,
|
||||
DRM_FORMAT_ARGB8888);
|
||||
sun4i_backend_update_layer_frontend(backend, layer->id,
|
||||
DRM_FORMAT_ARGB8888);
|
||||
sun4i_frontend_enable(frontend);
|
||||
} else {
|
||||
sun4i_backend_update_layer_formats(backend, layer->id, plane);
|
||||
sun4i_backend_update_layer_buffer(backend, layer->id, plane);
|
||||
}
|
||||
|
||||
sun4i_backend_update_layer_coord(backend, layer->id, plane);
|
||||
sun4i_backend_update_layer_formats(backend, layer->id, plane);
|
||||
sun4i_backend_update_layer_buffer(backend, layer->id, plane);
|
||||
sun4i_backend_update_layer_zpos(backend, layer->id, plane);
|
||||
sun4i_backend_layer_enable(backend, layer->id, true);
|
||||
}
|
||||
|
||||
|
@ -52,11 +125,11 @@ static const struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
|
|||
};
|
||||
|
||||
static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
|
||||
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
|
||||
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
|
||||
.atomic_destroy_state = sun4i_backend_layer_destroy_state,
|
||||
.atomic_duplicate_state = sun4i_backend_layer_duplicate_state,
|
||||
.destroy = drm_plane_cleanup,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
.reset = drm_atomic_helper_plane_reset,
|
||||
.reset = sun4i_backend_layer_reset,
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
};
|
||||
|
||||
|
@ -128,32 +201,12 @@ struct drm_plane **sun4i_layers_init(struct drm_device *drm,
|
|||
struct sun4i_backend *backend = engine_to_sun4i_backend(engine);
|
||||
int i;
|
||||
|
||||
planes = devm_kcalloc(drm->dev, ARRAY_SIZE(sun4i_backend_planes) + 1,
|
||||
/* We need to have a sentinel at the need, hence the overallocation */
|
||||
planes = devm_kcalloc(drm->dev, SUN4I_BACKEND_NUM_LAYERS + 1,
|
||||
sizeof(*planes), GFP_KERNEL);
|
||||
if (!planes)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/*
|
||||
* The hardware is a bit unusual here.
|
||||
*
|
||||
* Even though it supports 4 layers, it does the composition
|
||||
* in two separate steps.
|
||||
*
|
||||
* The first one is assigning a layer to one of its two
|
||||
* pipes. If more that 1 layer is assigned to the same pipe,
|
||||
* and if pixels overlaps, the pipe will take the pixel from
|
||||
* the layer with the highest priority.
|
||||
*
|
||||
* The second step is the actual alpha blending, that takes
|
||||
* the two pipes as input, and uses the eventual alpha
|
||||
* component to do the transparency between the two.
|
||||
*
|
||||
* This two steps scenario makes us unable to guarantee a
|
||||
* robust alpha blending between the 4 layers in all
|
||||
* situations. So we just expose two layers, one per pipe. On
|
||||
* SoCs that support it, sprites could fill the need for more
|
||||
* layers.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(sun4i_backend_planes); i++) {
|
||||
const struct sun4i_plane_desc *plane = &sun4i_backend_planes[i];
|
||||
struct sun4i_layer *layer;
|
||||
|
@ -165,6 +218,8 @@ struct drm_plane **sun4i_layers_init(struct drm_device *drm,
|
|||
return ERR_CAST(layer);
|
||||
};
|
||||
|
||||
drm_plane_create_zpos_immutable_property(&layer->plane, i);
|
||||
|
||||
DRM_DEBUG_DRIVER("Assigning %s plane to pipe %d\n",
|
||||
i ? "overlay" : "primary", plane->pipe);
|
||||
regmap_update_bits(engine->regs, SUN4I_BACKEND_ATTCTL_REG0(i),
|
||||
|
|
|
@ -22,12 +22,23 @@ struct sun4i_layer {
|
|||
int id;
|
||||
};
|
||||
|
||||
struct sun4i_layer_state {
|
||||
struct drm_plane_state state;
|
||||
bool uses_frontend;
|
||||
};
|
||||
|
||||
static inline struct sun4i_layer *
|
||||
plane_to_sun4i_layer(struct drm_plane *plane)
|
||||
{
|
||||
return container_of(plane, struct sun4i_layer, plane);
|
||||
}
|
||||
|
||||
static inline struct sun4i_layer_state *
|
||||
state_to_sun4i_layer_state(struct drm_plane_state *state)
|
||||
{
|
||||
return container_of(state, struct sun4i_layer_state, state);
|
||||
}
|
||||
|
||||
struct drm_plane **sun4i_layers_init(struct drm_device *drm,
|
||||
struct sunxi_engine *engine);
|
||||
|
||||
|
|
|
@ -540,6 +540,7 @@ static irqreturn_t sun4i_tcon_handler(int irq, void *private)
|
|||
struct sun4i_tcon *tcon = private;
|
||||
struct drm_device *drm = tcon->drm;
|
||||
struct sun4i_crtc *scrtc = tcon->crtc;
|
||||
struct sunxi_engine *engine = scrtc->engine;
|
||||
unsigned int status;
|
||||
|
||||
regmap_read(tcon->regs, SUN4I_TCON_GINT0_REG, &status);
|
||||
|
@ -557,6 +558,9 @@ static irqreturn_t sun4i_tcon_handler(int irq, void *private)
|
|||
SUN4I_TCON_GINT0_VBLANK_INT(1),
|
||||
0);
|
||||
|
||||
if (engine->ops->vblank_quirk)
|
||||
engine->ops->vblank_quirk(engine);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
|
|
@ -211,7 +211,7 @@ static int sun8i_ui_layer_atomic_check(struct drm_plane *plane,
|
|||
struct drm_crtc *crtc = state->crtc;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
int min_scale, max_scale;
|
||||
struct drm_rect clip;
|
||||
struct drm_rect clip = {};
|
||||
|
||||
if (!crtc)
|
||||
return 0;
|
||||
|
@ -220,10 +220,9 @@ static int sun8i_ui_layer_atomic_check(struct drm_plane *plane,
|
|||
if (WARN_ON(!crtc_state))
|
||||
return -EINVAL;
|
||||
|
||||
clip.x1 = 0;
|
||||
clip.y1 = 0;
|
||||
clip.x2 = crtc_state->adjusted_mode.hdisplay;
|
||||
clip.y2 = crtc_state->adjusted_mode.vdisplay;
|
||||
if (crtc_state->enable)
|
||||
drm_mode_get_hv_timing(&crtc_state->mode,
|
||||
&clip.x2, &clip.y2);
|
||||
|
||||
min_scale = DRM_PLANE_HELPER_NO_SCALING;
|
||||
max_scale = DRM_PLANE_HELPER_NO_SCALING;
|
||||
|
|
|
@ -239,7 +239,7 @@ static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
|
|||
struct drm_crtc *crtc = state->crtc;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
int min_scale, max_scale;
|
||||
struct drm_rect clip;
|
||||
struct drm_rect clip = {};
|
||||
|
||||
if (!crtc)
|
||||
return 0;
|
||||
|
@ -248,10 +248,9 @@ static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
|
|||
if (WARN_ON(!crtc_state))
|
||||
return -EINVAL;
|
||||
|
||||
clip.x1 = 0;
|
||||
clip.y1 = 0;
|
||||
clip.x2 = crtc_state->adjusted_mode.hdisplay;
|
||||
clip.y2 = crtc_state->adjusted_mode.vdisplay;
|
||||
if (crtc_state->enable)
|
||||
drm_mode_get_hv_timing(&crtc_state->mode,
|
||||
&clip.x2, &clip.y2);
|
||||
|
||||
min_scale = DRM_PLANE_HELPER_NO_SCALING;
|
||||
max_scale = DRM_PLANE_HELPER_NO_SCALING;
|
||||
|
|
|
@ -12,16 +12,106 @@
|
|||
|
||||
struct drm_plane;
|
||||
struct drm_device;
|
||||
struct drm_crtc_state;
|
||||
|
||||
struct sunxi_engine;
|
||||
|
||||
/**
|
||||
* struct sunxi_engine_ops - helper operations for sunXi engines
|
||||
*
|
||||
* These hooks are used by the common part of the DRM driver to
|
||||
* implement the proper behaviour.
|
||||
*/
|
||||
struct sunxi_engine_ops {
|
||||
/**
|
||||
* @atomic_begin:
|
||||
*
|
||||
* This callback allows to prepare our engine for an atomic
|
||||
* update. This is mirroring the
|
||||
* &drm_crtc_helper_funcs.atomic_begin callback, so any
|
||||
* documentation there applies.
|
||||
*
|
||||
* This function is optional.
|
||||
*/
|
||||
void (*atomic_begin)(struct sunxi_engine *engine,
|
||||
struct drm_crtc_state *old_state);
|
||||
|
||||
/**
|
||||
* @atomic_check:
|
||||
*
|
||||
* This callback allows to validate plane-update related CRTC
|
||||
* constraints specific to engines. This is mirroring the
|
||||
* &drm_crtc_helper_funcs.atomic_check callback, so any
|
||||
* documentation there applies.
|
||||
*
|
||||
* This function is optional.
|
||||
*
|
||||
* RETURNS:
|
||||
*
|
||||
* 0 on success or a negative error code.
|
||||
*/
|
||||
int (*atomic_check)(struct sunxi_engine *engine,
|
||||
struct drm_crtc_state *state);
|
||||
|
||||
/**
|
||||
* @commit:
|
||||
*
|
||||
* This callback will trigger the hardware switch to commit
|
||||
* the new configuration that has been setup during the next
|
||||
* vblank period.
|
||||
*
|
||||
* This function is optional.
|
||||
*/
|
||||
void (*commit)(struct sunxi_engine *engine);
|
||||
|
||||
/**
|
||||
* @layers_init:
|
||||
*
|
||||
* This callback is used to allocate, initialize and register
|
||||
* the layers supported by that engine.
|
||||
*
|
||||
* This function is mandatory.
|
||||
*
|
||||
* RETURNS:
|
||||
*
|
||||
* The array of struct drm_plane backing the layers, or an
|
||||
* error pointer on failure.
|
||||
*/
|
||||
struct drm_plane **(*layers_init)(struct drm_device *drm,
|
||||
struct sunxi_engine *engine);
|
||||
|
||||
/**
|
||||
* @apply_color_correction:
|
||||
*
|
||||
* This callback will enable the color correction in the
|
||||
* engine. This is useful only for the composite output.
|
||||
*
|
||||
* This function is optional.
|
||||
*/
|
||||
void (*apply_color_correction)(struct sunxi_engine *engine);
|
||||
|
||||
/**
|
||||
* @disable_color_correction:
|
||||
*
|
||||
* This callback will stop the color correction in the
|
||||
* engine. This is useful only for the composite output.
|
||||
*
|
||||
* This function is optional.
|
||||
*/
|
||||
void (*disable_color_correction)(struct sunxi_engine *engine);
|
||||
|
||||
/**
|
||||
* @vblank_quirk:
|
||||
*
|
||||
* This callback is used to implement engine-specific
|
||||
* behaviour part of the VBLANK event. It is run with all the
|
||||
* constraints of an interrupt (can't sleep, all local
|
||||
* interrupts disabled) and therefore should be as fast as
|
||||
* possible.
|
||||
*
|
||||
* This function is optional.
|
||||
*/
|
||||
void (*vblank_quirk)(struct sunxi_engine *engine);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -82,7 +82,7 @@ int tegra_plane_state_add(struct tegra_plane *plane,
|
|||
{
|
||||
struct drm_crtc_state *crtc_state;
|
||||
struct tegra_dc_state *tegra;
|
||||
struct drm_rect clip;
|
||||
struct drm_rect clip = {};
|
||||
int err;
|
||||
|
||||
/* Propagate errors from allocation or locking failures. */
|
||||
|
@ -90,10 +90,9 @@ int tegra_plane_state_add(struct tegra_plane *plane,
|
|||
if (IS_ERR(crtc_state))
|
||||
return PTR_ERR(crtc_state);
|
||||
|
||||
clip.x1 = 0;
|
||||
clip.y1 = 0;
|
||||
clip.x2 = crtc_state->mode.hdisplay;
|
||||
clip.y2 = crtc_state->mode.vdisplay;
|
||||
if (crtc_state->enable)
|
||||
drm_mode_get_hv_timing(&crtc_state->mode,
|
||||
&clip.x2, &clip.y2);
|
||||
|
||||
/* Check plane state for visibility and calculate clipping bounds */
|
||||
err = drm_atomic_helper_check_plane_state(state, crtc_state, &clip,
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
struct tinydrm_connector {
|
||||
struct drm_connector base;
|
||||
const struct drm_display_mode *mode;
|
||||
struct drm_display_mode mode;
|
||||
};
|
||||
|
||||
static inline struct tinydrm_connector *
|
||||
|
@ -29,7 +29,7 @@ static int tinydrm_connector_get_modes(struct drm_connector *connector)
|
|||
struct tinydrm_connector *tconn = to_tinydrm_connector(connector);
|
||||
struct drm_display_mode *mode;
|
||||
|
||||
mode = drm_mode_duplicate(connector->dev, tconn->mode);
|
||||
mode = drm_mode_duplicate(connector->dev, &tconn->mode);
|
||||
if (!mode) {
|
||||
DRM_ERROR("Failed to duplicate mode\n");
|
||||
return 0;
|
||||
|
@ -92,7 +92,7 @@ tinydrm_connector_create(struct drm_device *drm,
|
|||
if (!tconn)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
tconn->mode = mode;
|
||||
drm_mode_copy(&tconn->mode, mode);
|
||||
connector = &tconn->base;
|
||||
|
||||
drm_connector_helper_add(connector, &tinydrm_connector_hfuncs);
|
||||
|
@ -199,35 +199,27 @@ tinydrm_display_pipe_init(struct tinydrm_device *tdev,
|
|||
unsigned int rotation)
|
||||
{
|
||||
struct drm_device *drm = tdev->drm;
|
||||
struct drm_display_mode *mode_copy;
|
||||
struct drm_display_mode mode_copy;
|
||||
struct drm_connector *connector;
|
||||
int ret;
|
||||
|
||||
mode_copy = devm_kmalloc(drm->dev, sizeof(*mode_copy), GFP_KERNEL);
|
||||
if (!mode_copy)
|
||||
return -ENOMEM;
|
||||
|
||||
*mode_copy = *mode;
|
||||
ret = tinydrm_rotate_mode(mode_copy, rotation);
|
||||
drm_mode_copy(&mode_copy, mode);
|
||||
ret = tinydrm_rotate_mode(&mode_copy, rotation);
|
||||
if (ret) {
|
||||
DRM_ERROR("Illegal rotation value %u\n", rotation);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
drm->mode_config.min_width = mode_copy->hdisplay;
|
||||
drm->mode_config.max_width = mode_copy->hdisplay;
|
||||
drm->mode_config.min_height = mode_copy->vdisplay;
|
||||
drm->mode_config.max_height = mode_copy->vdisplay;
|
||||
drm->mode_config.min_width = mode_copy.hdisplay;
|
||||
drm->mode_config.max_width = mode_copy.hdisplay;
|
||||
drm->mode_config.min_height = mode_copy.vdisplay;
|
||||
drm->mode_config.max_height = mode_copy.vdisplay;
|
||||
|
||||
connector = tinydrm_connector_create(drm, mode_copy, connector_type);
|
||||
connector = tinydrm_connector_create(drm, &mode_copy, connector_type);
|
||||
if (IS_ERR(connector))
|
||||
return PTR_ERR(connector);
|
||||
|
||||
ret = drm_simple_display_pipe_init(drm, &tdev->pipe, funcs, formats,
|
||||
format_count, NULL, connector);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
return drm_simple_display_pipe_init(drm, &tdev->pipe, funcs, formats,
|
||||
format_count, NULL, connector);
|
||||
}
|
||||
EXPORT_SYMBOL(tinydrm_display_pipe_init);
|
||||
|
|
|
@ -180,7 +180,6 @@ static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
|
|||
{
|
||||
struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
|
||||
struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
|
||||
struct drm_framebuffer *fb = pipe->plane.fb;
|
||||
struct device *dev = tdev->drm->dev;
|
||||
int ret;
|
||||
u8 am_id;
|
||||
|
@ -269,10 +268,7 @@ static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
|
|||
|
||||
ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x1017);
|
||||
|
||||
mipi->enabled = true;
|
||||
|
||||
if (fb)
|
||||
fb->funcs->dirty(fb, NULL, 0, 0, NULL, 0);
|
||||
mipi_dbi_enable_flush(mipi);
|
||||
}
|
||||
|
||||
static void ili9225_pipe_disable(struct drm_simple_display_pipe *pipe)
|
||||
|
|
|
@ -9,47 +9,59 @@
|
|||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_modeset_helper.h>
|
||||
#include <drm/tinydrm/ili9341.h>
|
||||
#include <drm/tinydrm/mipi-dbi.h>
|
||||
#include <drm/tinydrm/tinydrm-helpers.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_modeset_helper.h>
|
||||
#include <drm/tinydrm/mipi-dbi.h>
|
||||
#include <drm/tinydrm/tinydrm-helpers.h>
|
||||
#include <video/mipi_display.h>
|
||||
|
||||
static int mi0283qt_init(struct mipi_dbi *mipi)
|
||||
#define ILI9341_FRMCTR1 0xb1
|
||||
#define ILI9341_DISCTRL 0xb6
|
||||
#define ILI9341_ETMOD 0xb7
|
||||
|
||||
#define ILI9341_PWCTRL1 0xc0
|
||||
#define ILI9341_PWCTRL2 0xc1
|
||||
#define ILI9341_VMCTRL1 0xc5
|
||||
#define ILI9341_VMCTRL2 0xc7
|
||||
#define ILI9341_PWCTRLA 0xcb
|
||||
#define ILI9341_PWCTRLB 0xcf
|
||||
|
||||
#define ILI9341_PGAMCTRL 0xe0
|
||||
#define ILI9341_NGAMCTRL 0xe1
|
||||
#define ILI9341_DTCTRLA 0xe8
|
||||
#define ILI9341_DTCTRLB 0xea
|
||||
#define ILI9341_PWRSEQ 0xed
|
||||
|
||||
#define ILI9341_EN3GAM 0xf2
|
||||
#define ILI9341_PUMPCTRL 0xf7
|
||||
|
||||
#define ILI9341_MADCTL_BGR BIT(3)
|
||||
#define ILI9341_MADCTL_MV BIT(5)
|
||||
#define ILI9341_MADCTL_MX BIT(6)
|
||||
#define ILI9341_MADCTL_MY BIT(7)
|
||||
|
||||
static void mi0283qt_enable(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_crtc_state *crtc_state)
|
||||
{
|
||||
struct tinydrm_device *tdev = &mipi->tinydrm;
|
||||
struct device *dev = tdev->drm->dev;
|
||||
struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
|
||||
struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
|
||||
u8 addr_mode;
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG_KMS("\n");
|
||||
|
||||
ret = regulator_enable(mipi->regulator);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "Failed to enable regulator %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Avoid flicker by skipping setup if the bootloader has done it */
|
||||
if (mipi_dbi_display_is_on(mipi))
|
||||
return 0;
|
||||
|
||||
mipi_dbi_hw_reset(mipi);
|
||||
ret = mipi_dbi_command(mipi, MIPI_DCS_SOFT_RESET);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "Error sending command %d\n", ret);
|
||||
regulator_disable(mipi->regulator);
|
||||
return ret;
|
||||
}
|
||||
|
||||
msleep(20);
|
||||
ret = mipi_dbi_poweron_conditional_reset(mipi);
|
||||
if (ret < 0)
|
||||
return;
|
||||
if (ret == 1)
|
||||
goto out_enable;
|
||||
|
||||
mipi_dbi_command(mipi, MIPI_DCS_SET_DISPLAY_OFF);
|
||||
|
||||
|
@ -68,7 +80,7 @@ static int mi0283qt_init(struct mipi_dbi *mipi)
|
|||
mipi_dbi_command(mipi, ILI9341_VMCTRL2, 0xbe);
|
||||
|
||||
/* Memory Access Control */
|
||||
mipi_dbi_command(mipi, MIPI_DCS_SET_PIXEL_FORMAT, 0x55);
|
||||
mipi_dbi_command(mipi, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT);
|
||||
|
||||
switch (mipi->rotation) {
|
||||
default:
|
||||
|
@ -112,19 +124,12 @@ static int mi0283qt_init(struct mipi_dbi *mipi)
|
|||
mipi_dbi_command(mipi, MIPI_DCS_SET_DISPLAY_ON);
|
||||
msleep(100);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mi0283qt_fini(void *data)
|
||||
{
|
||||
struct mipi_dbi *mipi = data;
|
||||
|
||||
DRM_DEBUG_KMS("\n");
|
||||
regulator_disable(mipi->regulator);
|
||||
out_enable:
|
||||
mipi_dbi_enable_flush(mipi);
|
||||
}
|
||||
|
||||
static const struct drm_simple_display_pipe_funcs mi0283qt_pipe_funcs = {
|
||||
.enable = mipi_dbi_pipe_enable,
|
||||
.enable = mi0283qt_enable,
|
||||
.disable = mipi_dbi_pipe_disable,
|
||||
.update = tinydrm_display_pipe_update,
|
||||
.prepare_fb = tinydrm_display_pipe_prepare_fb,
|
||||
|
@ -205,17 +210,6 @@ static int mi0283qt_probe(struct spi_device *spi)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mi0283qt_init(mipi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* use devres to fini after drm unregister (drv->remove is before) */
|
||||
ret = devm_add_action(dev, mi0283qt_fini, mipi);
|
||||
if (ret) {
|
||||
mi0283qt_fini(mipi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, mipi);
|
||||
|
||||
return devm_tinydrm_register(&mipi->tinydrm);
|
||||
|
@ -231,25 +225,13 @@ static void mi0283qt_shutdown(struct spi_device *spi)
|
|||
static int __maybe_unused mi0283qt_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct mipi_dbi *mipi = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = drm_mode_config_helper_suspend(mipi->tinydrm.drm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mi0283qt_fini(mipi);
|
||||
|
||||
return 0;
|
||||
return drm_mode_config_helper_suspend(mipi->tinydrm.drm);
|
||||
}
|
||||
|
||||
static int __maybe_unused mi0283qt_pm_resume(struct device *dev)
|
||||
{
|
||||
struct mipi_dbi *mipi = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = mi0283qt_init(mipi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drm_mode_config_helper_resume(mipi->tinydrm.drm);
|
||||
|
||||
|
|
|
@ -271,21 +271,16 @@ static const struct drm_framebuffer_funcs mipi_dbi_fb_funcs = {
|
|||
};
|
||||
|
||||
/**
|
||||
* mipi_dbi_pipe_enable - MIPI DBI pipe enable helper
|
||||
* @pipe: Display pipe
|
||||
* @crtc_state: CRTC state
|
||||
* mipi_dbi_enable_flush - MIPI DBI enable helper
|
||||
* @mipi: MIPI DBI structure
|
||||
*
|
||||
* This function enables backlight. Drivers can use this as their
|
||||
* This function sets &mipi_dbi->enabled, flushes the whole framebuffer and
|
||||
* enables the backlight. Drivers can use this in their
|
||||
* &drm_simple_display_pipe_funcs->enable callback.
|
||||
*/
|
||||
void mipi_dbi_pipe_enable(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_crtc_state *crtc_state)
|
||||
void mipi_dbi_enable_flush(struct mipi_dbi *mipi)
|
||||
{
|
||||
struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
|
||||
struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
|
||||
struct drm_framebuffer *fb = pipe->plane.fb;
|
||||
|
||||
DRM_DEBUG_KMS("\n");
|
||||
struct drm_framebuffer *fb = mipi->tinydrm.pipe.plane.fb;
|
||||
|
||||
mipi->enabled = true;
|
||||
if (fb)
|
||||
|
@ -293,7 +288,7 @@ void mipi_dbi_pipe_enable(struct drm_simple_display_pipe *pipe,
|
|||
|
||||
tinydrm_enable_backlight(mipi->backlight);
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_dbi_pipe_enable);
|
||||
EXPORT_SYMBOL(mipi_dbi_enable_flush);
|
||||
|
||||
static void mipi_dbi_blank(struct mipi_dbi *mipi)
|
||||
{
|
||||
|
@ -316,8 +311,8 @@ static void mipi_dbi_blank(struct mipi_dbi *mipi)
|
|||
* mipi_dbi_pipe_disable - MIPI DBI pipe disable helper
|
||||
* @pipe: Display pipe
|
||||
*
|
||||
* This function disables backlight if present or if not the
|
||||
* display memory is blanked. Drivers can use this as their
|
||||
* This function disables backlight if present, if not the display memory is
|
||||
* blanked. The regulator is disabled if in use. Drivers can use this as their
|
||||
* &drm_simple_display_pipe_funcs->disable callback.
|
||||
*/
|
||||
void mipi_dbi_pipe_disable(struct drm_simple_display_pipe *pipe)
|
||||
|
@ -333,6 +328,9 @@ void mipi_dbi_pipe_disable(struct drm_simple_display_pipe *pipe)
|
|||
tinydrm_disable_backlight(mipi->backlight);
|
||||
else
|
||||
mipi_dbi_blank(mipi);
|
||||
|
||||
if (mipi->regulator)
|
||||
regulator_disable(mipi->regulator);
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_dbi_pipe_disable);
|
||||
|
||||
|
@ -416,7 +414,7 @@ void mipi_dbi_hw_reset(struct mipi_dbi *mipi)
|
|||
return;
|
||||
|
||||
gpiod_set_value_cansleep(mipi->reset, 0);
|
||||
msleep(20);
|
||||
usleep_range(20, 1000);
|
||||
gpiod_set_value_cansleep(mipi->reset, 1);
|
||||
msleep(120);
|
||||
}
|
||||
|
@ -443,6 +441,7 @@ bool mipi_dbi_display_is_on(struct mipi_dbi *mipi)
|
|||
|
||||
val &= ~DCS_POWER_MODE_RESERVED_MASK;
|
||||
|
||||
/* The poweron/reset value is 08h DCS_POWER_MODE_DISPLAY_NORMAL_MODE */
|
||||
if (val != (DCS_POWER_MODE_DISPLAY |
|
||||
DCS_POWER_MODE_DISPLAY_NORMAL_MODE | DCS_POWER_MODE_SLEEP_MODE))
|
||||
return false;
|
||||
|
@ -453,6 +452,78 @@ bool mipi_dbi_display_is_on(struct mipi_dbi *mipi)
|
|||
}
|
||||
EXPORT_SYMBOL(mipi_dbi_display_is_on);
|
||||
|
||||
static int mipi_dbi_poweron_reset_conditional(struct mipi_dbi *mipi, bool cond)
|
||||
{
|
||||
struct device *dev = mipi->tinydrm.drm->dev;
|
||||
int ret;
|
||||
|
||||
if (mipi->regulator) {
|
||||
ret = regulator_enable(mipi->regulator);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "Failed to enable regulator (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (cond && mipi_dbi_display_is_on(mipi))
|
||||
return 1;
|
||||
|
||||
mipi_dbi_hw_reset(mipi);
|
||||
ret = mipi_dbi_command(mipi, MIPI_DCS_SOFT_RESET);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "Failed to send reset command (%d)\n", ret);
|
||||
if (mipi->regulator)
|
||||
regulator_disable(mipi->regulator);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we did a hw reset, we know the controller is in Sleep mode and
|
||||
* per MIPI DSC spec should wait 5ms after soft reset. If we didn't,
|
||||
* we assume worst case and wait 120ms.
|
||||
*/
|
||||
if (mipi->reset)
|
||||
usleep_range(5000, 20000);
|
||||
else
|
||||
msleep(120);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mipi_dbi_poweron_reset - MIPI DBI poweron and reset
|
||||
* @mipi: MIPI DBI structure
|
||||
*
|
||||
* This function enables the regulator if used and does a hardware and software
|
||||
* reset.
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, or a negative error code.
|
||||
*/
|
||||
int mipi_dbi_poweron_reset(struct mipi_dbi *mipi)
|
||||
{
|
||||
return mipi_dbi_poweron_reset_conditional(mipi, false);
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_dbi_poweron_reset);
|
||||
|
||||
/**
|
||||
* mipi_dbi_poweron_conditional_reset - MIPI DBI poweron and conditional reset
|
||||
* @mipi: MIPI DBI structure
|
||||
*
|
||||
* This function enables the regulator if used and if the display is off, it
|
||||
* does a hardware and software reset. If mipi_dbi_display_is_on() determines
|
||||
* that the display is on, no reset is performed.
|
||||
*
|
||||
* Returns:
|
||||
* Zero if the controller was reset, 1 if the display was already on, or a
|
||||
* negative error code.
|
||||
*/
|
||||
int mipi_dbi_poweron_conditional_reset(struct mipi_dbi *mipi)
|
||||
{
|
||||
return mipi_dbi_poweron_reset_conditional(mipi, true);
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_dbi_poweron_conditional_reset);
|
||||
|
||||
#if IS_ENABLED(CONFIG_SPI)
|
||||
|
||||
/**
|
||||
|
|
|
@ -179,20 +179,16 @@ static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe,
|
|||
{
|
||||
struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
|
||||
struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
|
||||
struct drm_framebuffer *fb = pipe->plane.fb;
|
||||
struct device *dev = tdev->drm->dev;
|
||||
int ret;
|
||||
u8 addr_mode;
|
||||
|
||||
DRM_DEBUG_KMS("\n");
|
||||
|
||||
mipi_dbi_hw_reset(mipi);
|
||||
ret = mipi_dbi_command(mipi, ST7586_AUTO_READ_CTRL, 0x9f);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "Error sending command %d\n", ret);
|
||||
ret = mipi_dbi_poweron_reset(mipi);
|
||||
if (ret)
|
||||
return;
|
||||
}
|
||||
|
||||
mipi_dbi_command(mipi, ST7586_AUTO_READ_CTRL, 0x9f);
|
||||
mipi_dbi_command(mipi, ST7586_OTP_RW_CTRL, 0x00);
|
||||
|
||||
msleep(10);
|
||||
|
@ -241,10 +237,7 @@ static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe,
|
|||
|
||||
mipi_dbi_command(mipi, MIPI_DCS_SET_DISPLAY_ON);
|
||||
|
||||
mipi->enabled = true;
|
||||
|
||||
if (fb)
|
||||
fb->funcs->dirty(fb, NULL, 0, 0, NULL, 0);
|
||||
mipi_dbi_enable_flush(mipi);
|
||||
}
|
||||
|
||||
static void st7586_pipe_disable(struct drm_simple_display_pipe *pipe)
|
||||
|
|
|
@ -40,19 +40,14 @@ static void jd_t18003_t01_pipe_enable(struct drm_simple_display_pipe *pipe,
|
|||
{
|
||||
struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
|
||||
struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
|
||||
struct device *dev = tdev->drm->dev;
|
||||
int ret;
|
||||
u8 addr_mode;
|
||||
|
||||
DRM_DEBUG_KMS("\n");
|
||||
|
||||
mipi_dbi_hw_reset(mipi);
|
||||
|
||||
ret = mipi_dbi_command(mipi, MIPI_DCS_SOFT_RESET);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "Error sending command %d\n", ret);
|
||||
ret = mipi_dbi_poweron_reset(mipi);
|
||||
if (ret)
|
||||
return;
|
||||
}
|
||||
|
||||
msleep(150);
|
||||
|
||||
|
@ -102,7 +97,7 @@ static void jd_t18003_t01_pipe_enable(struct drm_simple_display_pipe *pipe,
|
|||
|
||||
msleep(20);
|
||||
|
||||
mipi_dbi_pipe_enable(pipe, crtc_state);
|
||||
mipi_dbi_enable_flush(mipi);
|
||||
}
|
||||
|
||||
static const struct drm_simple_display_pipe_funcs jd_t18003_t01_pipe_funcs = {
|
||||
|
|
|
@ -15,6 +15,7 @@ vc4-y := \
|
|||
vc4_vec.o \
|
||||
vc4_hvs.o \
|
||||
vc4_irq.o \
|
||||
vc4_perfmon.o \
|
||||
vc4_plane.o \
|
||||
vc4_render_cl.o \
|
||||
vc4_trace_points.o \
|
||||
|
|
|
@ -101,6 +101,7 @@ static int vc4_get_param_ioctl(struct drm_device *dev, void *data,
|
|||
case DRM_VC4_PARAM_SUPPORTS_THREADED_FS:
|
||||
case DRM_VC4_PARAM_SUPPORTS_FIXED_RCL_ORDER:
|
||||
case DRM_VC4_PARAM_SUPPORTS_MADVISE:
|
||||
case DRM_VC4_PARAM_SUPPORTS_PERFMON:
|
||||
args->value = true;
|
||||
break;
|
||||
default:
|
||||
|
@ -111,6 +112,26 @@ static int vc4_get_param_ioctl(struct drm_device *dev, void *data,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int vc4_open(struct drm_device *dev, struct drm_file *file)
|
||||
{
|
||||
struct vc4_file *vc4file;
|
||||
|
||||
vc4file = kzalloc(sizeof(*vc4file), GFP_KERNEL);
|
||||
if (!vc4file)
|
||||
return -ENOMEM;
|
||||
|
||||
vc4_perfmon_open_file(vc4file);
|
||||
file->driver_priv = vc4file;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vc4_close(struct drm_device *dev, struct drm_file *file)
|
||||
{
|
||||
struct vc4_file *vc4file = file->driver_priv;
|
||||
|
||||
vc4_perfmon_close_file(vc4file);
|
||||
}
|
||||
|
||||
static const struct vm_operations_struct vc4_vm_ops = {
|
||||
.fault = vc4_fault,
|
||||
.open = drm_gem_vm_open,
|
||||
|
@ -143,6 +164,9 @@ static const struct drm_ioctl_desc vc4_drm_ioctls[] = {
|
|||
DRM_IOCTL_DEF_DRV(VC4_GET_TILING, vc4_get_tiling_ioctl, DRM_RENDER_ALLOW),
|
||||
DRM_IOCTL_DEF_DRV(VC4_LABEL_BO, vc4_label_bo_ioctl, DRM_RENDER_ALLOW),
|
||||
DRM_IOCTL_DEF_DRV(VC4_GEM_MADVISE, vc4_gem_madvise_ioctl, DRM_RENDER_ALLOW),
|
||||
DRM_IOCTL_DEF_DRV(VC4_PERFMON_CREATE, vc4_perfmon_create_ioctl, DRM_RENDER_ALLOW),
|
||||
DRM_IOCTL_DEF_DRV(VC4_PERFMON_DESTROY, vc4_perfmon_destroy_ioctl, DRM_RENDER_ALLOW),
|
||||
DRM_IOCTL_DEF_DRV(VC4_PERFMON_GET_VALUES, vc4_perfmon_get_values_ioctl, DRM_RENDER_ALLOW),
|
||||
};
|
||||
|
||||
static struct drm_driver vc4_drm_driver = {
|
||||
|
@ -153,6 +177,8 @@ static struct drm_driver vc4_drm_driver = {
|
|||
DRIVER_RENDER |
|
||||
DRIVER_PRIME),
|
||||
.lastclose = drm_fb_helper_lastclose,
|
||||
.open = vc4_open,
|
||||
.postclose = vc4_close,
|
||||
.irq_handler = vc4_irq,
|
||||
.irq_preinstall = vc4_irq_preinstall,
|
||||
.irq_postinstall = vc4_irq_postinstall,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue