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:
Dave Airlie 2018-02-16 09:29:27 +10:00
commit 76ea0f334e
123 changed files with 3258 additions and 834 deletions

View file

@ -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:

View file

@ -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>;
};
};
};
};

View file

@ -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";

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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;

View file

@ -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(&gtt->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(&gtt->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

View file

@ -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) {

View file

@ -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) {

View file

@ -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) {

View file

@ -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)

View file

@ -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"

View file

@ -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;
}

View file

@ -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,

View file

@ -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)

View file

@ -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)

View file

@ -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 = {

View file

@ -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,

View file

@ -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);

View file

@ -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);

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;

View file

@ -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;

View file

@ -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,
};

View file

@ -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);

View file

@ -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;
}

View file

@ -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,
};

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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,

View file

@ -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);

View file

@ -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,

View file

@ -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 ||

View file

@ -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);

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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,

View file

@ -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 {

View file

@ -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;

View file

@ -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) {

View file

@ -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;
}

View file

@ -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)

View file

@ -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;

View file

@ -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;

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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)

View file

@ -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);

View file

@ -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);

View file

@ -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

View file

@ -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

View 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");

View file

@ -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)

View file

@ -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.

View file

@ -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);

View file

@ -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;
};

View file

@ -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,

View file

@ -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:

View file

@ -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;

View file

@ -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 */

View file

@ -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(&gtt->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(&gtt->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

View file

@ -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,

View file

@ -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) |

View file

@ -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,

View file

@ -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;
}

View file

@ -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, &ltdc_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;

View file

@ -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 {

View file

@ -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

View file

@ -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))

View file

@ -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_ */

View file

@ -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,

View file

@ -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);

View file

@ -19,6 +19,7 @@
struct sun4i_drv {
struct list_head engine_list;
struct list_head frontend_list;
struct list_head tcon_list;
};

View file

@ -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);
}

View 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");

View 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_ */

View file

@ -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, &copy->state);
copy->uses_frontend = orig->uses_frontend;
return &copy->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),

View file

@ -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);

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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);
};
/**

View file

@ -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,

View file

@ -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);

View file

@ -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)

View file

@ -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);

View file

@ -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)
/**

View file

@ -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)

View file

@ -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 = {

View file

@ -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 \

View file

@ -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