mirror of
https://github.com/torvalds/linux.git
synced 2024-12-29 14:21:47 +00:00
Merge tag 'drm-misc-next-2017-05-16' of git://anongit.freedesktop.org/git/drm-misc into drm-next
UAPI Changes: - Return -ENODEV instead of -ENXIO when creating cma fb w/o valid gem (Daniel) - Add aspect ratio and custom scaling propertis to connector state (Maarten) Cross-subsystem Changes: - None Core Changes: - Add Laurent as bridge reviewer and Andrzej as bridge maintainer (Archit) - Maintain new STM driver through -misc (Yannick) - Misc doc improvements (as is tradition) (Daniel) - Add driver-private objects to atomic state (Dhinakaran) - Deprecate preclose hook in modern drivers (use postclose) (Daniel) - Add hwmode to vblank struct. This fixes mode access in irq context and reduced a bunch of boilerplate (Daniel) Driver Changes: - vc4: Add out-fence support to vc4 V3D rendering (Eric) - stm: Add stm32f429 display hw and am-480272h3tmqw-t01h panel support (Yannick) - vc4: Remove 256MB cma limit from vc4 (Eric) - dw-hdmi: Disable audio when inactive, instead of always enabled (Romain) - zte: Add support for VGA to the ZTE driver (Shawn) - i915: Track DP MST bandwidth and check it in atomic_check (Dhinakaran) - vgem: Enable gem dmabuf import iface to facilitate ion testing (Laura) - vc4: Add support for Cygnus (new dt compat string + couple bug fixes) (Eric) - pl111: Add driver for pl111 CLCD display controller (Eric/Tom) - vgem: Subclass drm_device instead of standalone platform device (Chris) Cc: Archit Taneja <architt@codeaurora.org> Cc: Eric Anholt <eric@anholt.net> Cc: Yannick Fertre <yannick.fertre@st.com> Cc: Romain Perier <romain.perier@collabora.com> Cc: Navare, Manasi D <manasi.d.navare@intel.com> Cc: Shawn Guo <shawn.guo@linaro.org> Cc: Dhinakaran Pandiyan <dhinakaran.pandiyan@intel.com> Cc: Laura Abbott <labbott@redhat.com> Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Cc: Tom Cooksey <tom.cooksey@arm.com> Cc: Daniel Vetter <daniel.vetter@intel.com> Cc: Chris Wilson <chris@chris-wilson.co.uk> * tag 'drm-misc-next-2017-05-16' of git://anongit.freedesktop.org/git/drm-misc: (72 commits) drm: add missing declaration to drm_blend.h drm/dp: Wait up all outstanding tx waiters drm/dp: Read the tx msg state once after checking for an event drm/prime: Forward declare struct device drm/vblank: Lock down vblank->hwmode more drm/vblank: drop the mode argument from drm_calc_vbltimestamp_from_scanoutpos drm/vblank: Add FIXME comments about moving the vblank ts hooks drm/vblank: Switch to bool in_vblank_irq in get_vblank_timestamp drm/vblank: Switch drm_driver->get_vblank_timestamp to return a bool drm/vgem: Convert to a struct drm_device subclass gpu: drm: gma500: remove dead code drm/sti: Adjust two checks for null pointers in sti_hqvdp_probe() drm/sti: Fix typos in a comment line drm/sti: Fix a typo in a comment line drm/sti: Replace 17 seq_puts() calls by seq_putc() drm/sti: Reduce function calls for sequence output at five places drm/sti: use seq_puts to display a string drm: Nerf the preclose callback for modern drivers drm/exynos: Merge pre/postclose hooks drm/tegra: switch to postclose ...
This commit is contained in:
commit
e98c58e55f
@ -5,7 +5,7 @@ with HDMI output and the HVS (Hardware Video Scaler) for compositing
|
||||
display planes.
|
||||
|
||||
Required properties for VC4:
|
||||
- compatible: Should be "brcm,bcm2835-vc4"
|
||||
- compatible: Should be "brcm,bcm2835-vc4" or "brcm,cygnus-vc4"
|
||||
|
||||
Required properties for Pixel Valve:
|
||||
- compatible: Should be one of "brcm,bcm2835-pixelvalve0",
|
||||
@ -54,11 +54,14 @@ Required properties for VEC:
|
||||
See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
|
||||
|
||||
Required properties for V3D:
|
||||
- compatible: Should be "brcm,bcm2835-v3d"
|
||||
- compatible: Should be "brcm,bcm2835-v3d" or "brcm,cygnus-v3d"
|
||||
- reg: Physical base address and length of the V3D's registers
|
||||
- interrupts: The interrupt number
|
||||
See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
|
||||
|
||||
Optional properties for V3D:
|
||||
- clocks: The clock the unit runs on
|
||||
|
||||
Required properties for DSI:
|
||||
- compatible: Should be "brcm,bcm2835-dsi0" or "brcm,bcm2835-dsi1"
|
||||
- reg: Physical base address and length of the DSI block's registers
|
||||
|
36
Documentation/devicetree/bindings/display/st,stm32-ltdc.txt
Normal file
36
Documentation/devicetree/bindings/display/st,stm32-ltdc.txt
Normal file
@ -0,0 +1,36 @@
|
||||
* STMicroelectronics STM32 lcd-tft display controller
|
||||
|
||||
- ltdc: lcd-tft display controller host
|
||||
must be a sub-node of st-display-subsystem
|
||||
Required properties:
|
||||
- compatible: "st,stm32-ltdc"
|
||||
- reg: Physical base address of the IP registers and length of memory mapped region.
|
||||
- clocks: A list of phandle + clock-specifier pairs, one for each
|
||||
entry in 'clock-names'.
|
||||
- clock-names: A list of clock names. For ltdc it should contain:
|
||||
- "lcd" for the clock feeding the output pixel clock & IP clock.
|
||||
- resets: reset to be used by the device (defined by use of RCC macro).
|
||||
Required nodes:
|
||||
- Video port for RGB output.
|
||||
|
||||
Example:
|
||||
|
||||
/ {
|
||||
...
|
||||
soc {
|
||||
...
|
||||
ltdc: display-controller@40016800 {
|
||||
compatible = "st,stm32-ltdc";
|
||||
reg = <0x40016800 0x200>;
|
||||
interrupts = <88>, <89>;
|
||||
resets = <&rcc STM32F4_APB2_RESET(LTDC)>;
|
||||
clocks = <&rcc 1 CLK_LCD>;
|
||||
clock-names = "lcd";
|
||||
|
||||
port {
|
||||
ltdc_out_rgb: endpoint {
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
@ -58,6 +58,18 @@ Required properties:
|
||||
integer cells. The first cell is the offset of SYSCTRL register used
|
||||
to control TV Encoder DAC power, and the second cell is the bit mask.
|
||||
|
||||
* VGA output device
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "zte,zx296718-vga"
|
||||
- reg: Physical base address and length of the VGA device IO region
|
||||
- interrupts : VGA interrupt number to CPU
|
||||
- clocks: Phandle with clock-specifier pointing to VGA I2C clock.
|
||||
- clock-names: Must be "i2c_wclk".
|
||||
- zte,vga-power-control: the phandle to SYSCTRL block followed by two
|
||||
integer cells. The first cell is the offset of SYSCTRL register used
|
||||
to control VGA DAC power, and the second cell is the bit mask.
|
||||
|
||||
Example:
|
||||
|
||||
vou: vou@1440000 {
|
||||
@ -81,6 +93,15 @@ vou: vou@1440000 {
|
||||
"main_wclk", "aux_wclk";
|
||||
};
|
||||
|
||||
vga: vga@8000 {
|
||||
compatible = "zte,zx296718-vga";
|
||||
reg = <0x8000 0x1000>;
|
||||
interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&topcrm VGA_I2C_WCLK>;
|
||||
clock-names = "i2c_wclk";
|
||||
zte,vga-power-control = <&sysctrl 0x170 0xe0>;
|
||||
};
|
||||
|
||||
hdmi: hdmi@c000 {
|
||||
compatible = "zte,zx296718-hdmi";
|
||||
reg = <0xc000 0x4000>;
|
||||
|
@ -12,6 +12,7 @@ Linux GPU Driver Developer's Guide
|
||||
drm-uapi
|
||||
i915
|
||||
meson
|
||||
pl111
|
||||
tinydrm
|
||||
vc4
|
||||
vga-switcheroo
|
||||
|
6
Documentation/gpu/pl111.rst
Normal file
6
Documentation/gpu/pl111.rst
Normal file
@ -0,0 +1,6 @@
|
||||
==========================================
|
||||
drm/pl111 ARM PrimeCell PL111 CLCD Driver
|
||||
==========================================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/pl111/pl111_drv.c
|
||||
:doc: ARM PrimeCell PL111 CLCD Driver
|
17
MAINTAINERS
17
MAINTAINERS
@ -4228,6 +4228,12 @@ F: include/drm/drm*
|
||||
F: include/uapi/drm/drm*
|
||||
F: include/linux/vga*
|
||||
|
||||
DRM DRIVER FOR ARM PL111 CLCD
|
||||
M: Eric Anholt <eric@anholt.net>
|
||||
T: git git://anongit.freedesktop.org/drm/drm-misc
|
||||
S: Supported
|
||||
F: drivers/gpu/drm/pl111/
|
||||
|
||||
DRM DRIVER FOR AST SERVER GRAPHICS CHIPS
|
||||
M: Dave Airlie <airlied@redhat.com>
|
||||
S: Odd Fixes
|
||||
@ -4235,6 +4241,8 @@ F: drivers/gpu/drm/ast/
|
||||
|
||||
DRM DRIVERS FOR BRIDGE CHIPS
|
||||
M: Archit Taneja <architt@codeaurora.org>
|
||||
M: Andrzej Hajda <a.hajda@samsung.com>
|
||||
R: Laurent Pinchart <Laurent.pinchart@ideasonboard.com>
|
||||
S: Maintained
|
||||
T: git git://anongit.freedesktop.org/drm/drm-misc
|
||||
F: drivers/gpu/drm/bridge/
|
||||
@ -4491,6 +4499,15 @@ S: Maintained
|
||||
F: drivers/gpu/drm/sti
|
||||
F: Documentation/devicetree/bindings/display/st,stih4xx.txt
|
||||
|
||||
DRM DRIVERS FOR STM
|
||||
M: Yannick Fertre <yannick.fertre@st.com>
|
||||
M: Philippe Cornu <philippe.cornu@st.com>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
T: git git://anongit.freedesktop.org/drm/drm-misc
|
||||
S: Maintained
|
||||
F: drivers/gpu/drm/stm
|
||||
F: Documentation/devicetree/bindings/display/st,stm32-ltdc.txt
|
||||
|
||||
DRM DRIVER FOR TDFX VIDEO CARDS
|
||||
S: Orphan / Obsolete
|
||||
F: drivers/gpu/drm/tdfx/
|
||||
|
@ -558,8 +558,8 @@ struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,
|
||||
if (WARN_ON(!dmabuf || !dev))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
attach = kzalloc(sizeof(struct dma_buf_attachment), GFP_KERNEL);
|
||||
if (attach == NULL)
|
||||
attach = kzalloc(sizeof(*attach), GFP_KERNEL);
|
||||
if (!attach)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
attach->dev = dev;
|
||||
@ -1122,9 +1122,7 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused)
|
||||
attach_count = 0;
|
||||
|
||||
list_for_each_entry(attach_obj, &buf_obj->attachments, node) {
|
||||
seq_puts(s, "\t");
|
||||
|
||||
seq_printf(s, "%s\n", dev_name(attach_obj->dev));
|
||||
seq_printf(s, "\t%s\n", dev_name(attach_obj->dev));
|
||||
attach_count++;
|
||||
}
|
||||
|
||||
|
@ -402,6 +402,11 @@ dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)
|
||||
}
|
||||
}
|
||||
|
||||
if (!timeout) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cb.base.func = dma_fence_default_wait_cb;
|
||||
cb.task = current;
|
||||
list_add(&cb.base.node, &fence->cb_list);
|
||||
|
@ -110,7 +110,7 @@ static void sync_print_fence(struct seq_file *s,
|
||||
}
|
||||
}
|
||||
|
||||
seq_puts(s, "\n");
|
||||
seq_putc(s, '\n');
|
||||
}
|
||||
|
||||
static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj)
|
||||
@ -161,7 +161,7 @@ static int sync_debugfs_show(struct seq_file *s, void *unused)
|
||||
sync_timeline_list);
|
||||
|
||||
sync_print_obj(s, obj);
|
||||
seq_puts(s, "\n");
|
||||
seq_putc(s, '\n');
|
||||
}
|
||||
spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
|
||||
|
||||
@ -173,7 +173,7 @@ static int sync_debugfs_show(struct seq_file *s, void *unused)
|
||||
container_of(pos, struct sync_file, sync_file_list);
|
||||
|
||||
sync_print_sync_file(s, sync_file);
|
||||
seq_puts(s, "\n");
|
||||
seq_putc(s, '\n');
|
||||
}
|
||||
spin_unlock_irqrestore(&sync_file_list_lock, flags);
|
||||
return 0;
|
||||
|
@ -41,8 +41,6 @@ static struct sync_file *sync_file_alloc(void)
|
||||
if (IS_ERR(sync_file->file))
|
||||
goto err;
|
||||
|
||||
kref_init(&sync_file->kref);
|
||||
|
||||
init_waitqueue_head(&sync_file->wq);
|
||||
|
||||
INIT_LIST_HEAD(&sync_file->cb.node);
|
||||
@ -277,22 +275,15 @@ err:
|
||||
|
||||
}
|
||||
|
||||
static void sync_file_free(struct kref *kref)
|
||||
static int sync_file_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct sync_file *sync_file = container_of(kref, struct sync_file,
|
||||
kref);
|
||||
struct sync_file *sync_file = file->private_data;
|
||||
|
||||
if (test_bit(POLL_ENABLED, &sync_file->fence->flags))
|
||||
dma_fence_remove_callback(sync_file->fence, &sync_file->cb);
|
||||
dma_fence_put(sync_file->fence);
|
||||
kfree(sync_file);
|
||||
}
|
||||
|
||||
static int sync_file_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct sync_file *sync_file = file->private_data;
|
||||
|
||||
kref_put(&sync_file->kref, sync_file_free);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -246,6 +246,8 @@ source "drivers/gpu/drm/fsl-dcu/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/tegra/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/stm/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/panel/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/bridge/Kconfig"
|
||||
@ -274,6 +276,8 @@ source "drivers/gpu/drm/meson/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/tinydrm/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/pl111/Kconfig"
|
||||
|
||||
# Keep legacy drivers last
|
||||
|
||||
menuconfig DRM_LEGACY
|
||||
|
@ -82,6 +82,7 @@ obj-$(CONFIG_DRM_BOCHS) += bochs/
|
||||
obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio/
|
||||
obj-$(CONFIG_DRM_MSM) += msm/
|
||||
obj-$(CONFIG_DRM_TEGRA) += tegra/
|
||||
obj-$(CONFIG_DRM_STM) += stm/
|
||||
obj-$(CONFIG_DRM_STI) += sti/
|
||||
obj-$(CONFIG_DRM_IMX) += imx/
|
||||
obj-$(CONFIG_DRM_MEDIATEK) += mediatek/
|
||||
@ -96,3 +97,4 @@ obj-y += hisilicon/
|
||||
obj-$(CONFIG_DRM_ZTE) += zte/
|
||||
obj-$(CONFIG_DRM_MXSFB) += mxsfb/
|
||||
obj-$(CONFIG_DRM_TINYDRM) += tinydrm/
|
||||
obj-$(CONFIG_DRM_PL111) += pl111/
|
||||
|
@ -1912,10 +1912,6 @@ int amdgpu_device_resume(struct drm_device *dev, bool resume, bool fbcon);
|
||||
u32 amdgpu_get_vblank_counter_kms(struct drm_device *dev, unsigned int pipe);
|
||||
int amdgpu_enable_vblank_kms(struct drm_device *dev, unsigned int pipe);
|
||||
void amdgpu_disable_vblank_kms(struct drm_device *dev, unsigned int pipe);
|
||||
int amdgpu_get_vblank_timestamp_kms(struct drm_device *dev, unsigned int pipe,
|
||||
int *max_error,
|
||||
struct timeval *vblank_time,
|
||||
unsigned flags);
|
||||
long amdgpu_kms_compat_ioctl(struct file *filp, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
|
||||
|
@ -715,6 +715,16 @@ static const struct file_operations amdgpu_driver_kms_fops = {
|
||||
#endif
|
||||
};
|
||||
|
||||
static bool
|
||||
amdgpu_get_crtc_scanout_position(struct drm_device *dev, unsigned int pipe,
|
||||
bool in_vblank_irq, int *vpos, int *hpos,
|
||||
ktime_t *stime, ktime_t *etime,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
return amdgpu_get_crtc_scanoutpos(dev, pipe, 0, vpos, hpos,
|
||||
stime, etime, mode);
|
||||
}
|
||||
|
||||
static struct drm_driver kms_driver = {
|
||||
.driver_features =
|
||||
DRIVER_USE_AGP |
|
||||
@ -729,8 +739,8 @@ static struct drm_driver kms_driver = {
|
||||
.get_vblank_counter = amdgpu_get_vblank_counter_kms,
|
||||
.enable_vblank = amdgpu_enable_vblank_kms,
|
||||
.disable_vblank = amdgpu_disable_vblank_kms,
|
||||
.get_vblank_timestamp = amdgpu_get_vblank_timestamp_kms,
|
||||
.get_scanout_position = amdgpu_get_crtc_scanoutpos,
|
||||
.get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos,
|
||||
.get_scanout_position = amdgpu_get_crtc_scanout_position,
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
.debugfs_init = amdgpu_debugfs_init,
|
||||
#endif
|
||||
|
@ -945,47 +945,6 @@ void amdgpu_disable_vblank_kms(struct drm_device *dev, unsigned int pipe)
|
||||
amdgpu_irq_put(adev, &adev->crtc_irq, idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_get_vblank_timestamp_kms - get vblank timestamp
|
||||
*
|
||||
* @dev: drm dev pointer
|
||||
* @crtc: crtc to get the timestamp for
|
||||
* @max_error: max error
|
||||
* @vblank_time: time value
|
||||
* @flags: flags passed to the driver
|
||||
*
|
||||
* Gets the timestamp on the requested crtc based on the
|
||||
* scanout position. (all asics).
|
||||
* Returns postive status flags on success, negative error on failure.
|
||||
*/
|
||||
int amdgpu_get_vblank_timestamp_kms(struct drm_device *dev, unsigned int pipe,
|
||||
int *max_error,
|
||||
struct timeval *vblank_time,
|
||||
unsigned flags)
|
||||
{
|
||||
struct drm_crtc *crtc;
|
||||
struct amdgpu_device *adev = dev->dev_private;
|
||||
|
||||
if (pipe >= dev->num_crtcs) {
|
||||
DRM_ERROR("Invalid crtc %u\n", pipe);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Get associated drm_crtc: */
|
||||
crtc = &adev->mode_info.crtcs[pipe]->base;
|
||||
if (!crtc) {
|
||||
/* This can occur on driver load if some component fails to
|
||||
* initialize completely and driver is unloaded */
|
||||
DRM_ERROR("Uninitialized crtc %d\n", pipe);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Helper routine in DRM core does all the work: */
|
||||
return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
|
||||
vblank_time, flags,
|
||||
&crtc->hwmode);
|
||||
}
|
||||
|
||||
const struct drm_ioctl_desc amdgpu_ioctls_kms[] = {
|
||||
DRM_IOCTL_DEF_DRV(AMDGPU_GEM_CREATE, amdgpu_gem_create_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
|
||||
DRM_IOCTL_DEF_DRV(AMDGPU_CTX, amdgpu_ctx_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
|
||||
|
@ -534,6 +534,9 @@ struct amdgpu_framebuffer {
|
||||
((em) == ATOM_ENCODER_MODE_DP_MST))
|
||||
|
||||
/* Driver internal use only flags of amdgpu_get_crtc_scanoutpos() */
|
||||
#define DRM_SCANOUTPOS_VALID (1 << 0)
|
||||
#define DRM_SCANOUTPOS_IN_VBLANK (1 << 1)
|
||||
#define DRM_SCANOUTPOS_ACCURATE (1 << 2)
|
||||
#define USE_REAL_VBLANKSTART (1 << 30)
|
||||
#define GET_DISTANCE_TO_VBLANKSTART (1 << 31)
|
||||
|
||||
|
@ -160,7 +160,7 @@ static int sii902x_get_modes(struct drm_connector *connector)
|
||||
time_before(jiffies, timeout));
|
||||
|
||||
if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
|
||||
dev_err(&sii902x->i2c->dev, "failed to acquire the i2c bus");
|
||||
dev_err(&sii902x->i2c->dev, "failed to acquire the i2c bus\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
@ -202,7 +202,7 @@ static int sii902x_get_modes(struct drm_connector *connector)
|
||||
|
||||
if (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
|
||||
SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
|
||||
dev_err(&sii902x->i2c->dev, "failed to release the i2c bus");
|
||||
dev_err(&sii902x->i2c->dev, "failed to release the i2c bus\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
@ -298,7 +298,7 @@ static int sii902x_bridge_attach(struct drm_bridge *bridge)
|
||||
|
||||
if (!drm_core_check_feature(drm, DRIVER_ATOMIC)) {
|
||||
dev_err(&sii902x->i2c->dev,
|
||||
"sii902x driver is only compatible with DRM devices supporting atomic updates");
|
||||
"sii902x driver is only compatible with DRM devices supporting atomic updates\n");
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
|
@ -173,6 +173,8 @@ struct dw_hdmi {
|
||||
|
||||
unsigned int reg_shift;
|
||||
struct regmap *regm;
|
||||
void (*enable_audio)(struct dw_hdmi *hdmi);
|
||||
void (*disable_audio)(struct dw_hdmi *hdmi);
|
||||
};
|
||||
|
||||
#define HDMI_IH_PHY_STAT0_RX_SENSE \
|
||||
@ -542,13 +544,41 @@ void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_rate);
|
||||
|
||||
static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi, bool enable)
|
||||
{
|
||||
hdmi_modb(hdmi, enable ? 0 : HDMI_MC_CLKDIS_AUDCLK_DISABLE,
|
||||
HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
|
||||
}
|
||||
|
||||
static void dw_hdmi_ahb_audio_enable(struct dw_hdmi *hdmi)
|
||||
{
|
||||
hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
|
||||
}
|
||||
|
||||
static void dw_hdmi_ahb_audio_disable(struct dw_hdmi *hdmi)
|
||||
{
|
||||
hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0);
|
||||
}
|
||||
|
||||
static void dw_hdmi_i2s_audio_enable(struct dw_hdmi *hdmi)
|
||||
{
|
||||
hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
|
||||
hdmi_enable_audio_clk(hdmi, true);
|
||||
}
|
||||
|
||||
static void dw_hdmi_i2s_audio_disable(struct dw_hdmi *hdmi)
|
||||
{
|
||||
hdmi_enable_audio_clk(hdmi, false);
|
||||
}
|
||||
|
||||
void dw_hdmi_audio_enable(struct dw_hdmi *hdmi)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&hdmi->audio_lock, flags);
|
||||
hdmi->audio_enable = true;
|
||||
hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
|
||||
if (hdmi->enable_audio)
|
||||
hdmi->enable_audio(hdmi);
|
||||
spin_unlock_irqrestore(&hdmi->audio_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_hdmi_audio_enable);
|
||||
@ -559,7 +589,8 @@ void dw_hdmi_audio_disable(struct dw_hdmi *hdmi)
|
||||
|
||||
spin_lock_irqsave(&hdmi->audio_lock, flags);
|
||||
hdmi->audio_enable = false;
|
||||
hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0);
|
||||
if (hdmi->disable_audio)
|
||||
hdmi->disable_audio(hdmi);
|
||||
spin_unlock_irqrestore(&hdmi->audio_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_hdmi_audio_disable);
|
||||
@ -1573,11 +1604,6 @@ static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi)
|
||||
HDMI_MC_FLOWCTRL);
|
||||
}
|
||||
|
||||
static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi)
|
||||
{
|
||||
hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
|
||||
}
|
||||
|
||||
/* Workaround to clear the overflow condition */
|
||||
static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)
|
||||
{
|
||||
@ -1691,7 +1717,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
|
||||
|
||||
/* HDMI Initialization Step E - Configure audio */
|
||||
hdmi_clk_regenerator_update_pixel_clock(hdmi);
|
||||
hdmi_enable_audio_clk(hdmi);
|
||||
hdmi_enable_audio_clk(hdmi, true);
|
||||
}
|
||||
|
||||
/* not for DVI mode */
|
||||
@ -2403,6 +2429,8 @@ __dw_hdmi_probe(struct platform_device *pdev,
|
||||
audio.irq = irq;
|
||||
audio.hdmi = hdmi;
|
||||
audio.eld = hdmi->connector.eld;
|
||||
hdmi->enable_audio = dw_hdmi_ahb_audio_enable;
|
||||
hdmi->disable_audio = dw_hdmi_ahb_audio_disable;
|
||||
|
||||
pdevinfo.name = "dw-hdmi-ahb-audio";
|
||||
pdevinfo.data = &audio;
|
||||
@ -2415,6 +2443,8 @@ __dw_hdmi_probe(struct platform_device *pdev,
|
||||
audio.hdmi = hdmi;
|
||||
audio.write = hdmi_writeb;
|
||||
audio.read = hdmi_readb;
|
||||
hdmi->enable_audio = dw_hdmi_i2s_audio_enable;
|
||||
hdmi->disable_audio = dw_hdmi_i2s_audio_disable;
|
||||
|
||||
pdevinfo.name = "dw-hdmi-i2s-audio";
|
||||
pdevinfo.data = &audio;
|
||||
|
@ -57,6 +57,7 @@ void drm_atomic_state_default_release(struct drm_atomic_state *state)
|
||||
kfree(state->connectors);
|
||||
kfree(state->crtcs);
|
||||
kfree(state->planes);
|
||||
kfree(state->private_objs);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_state_default_release);
|
||||
|
||||
@ -184,6 +185,17 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
|
||||
state->planes[i].ptr = NULL;
|
||||
state->planes[i].state = NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < state->num_private_objs; i++) {
|
||||
void *obj_state = state->private_objs[i].obj_state;
|
||||
|
||||
state->private_objs[i].funcs->destroy_state(obj_state);
|
||||
state->private_objs[i].obj = NULL;
|
||||
state->private_objs[i].obj_state = NULL;
|
||||
state->private_objs[i].funcs = NULL;
|
||||
}
|
||||
state->num_private_objs = 0;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_state_default_clear);
|
||||
|
||||
@ -425,7 +437,7 @@ drm_atomic_replace_property_blob(struct drm_property_blob **blob,
|
||||
}
|
||||
|
||||
static int
|
||||
drm_atomic_replace_property_blob_from_id(struct drm_crtc *crtc,
|
||||
drm_atomic_replace_property_blob_from_id(struct drm_device *dev,
|
||||
struct drm_property_blob **blob,
|
||||
uint64_t blob_id,
|
||||
ssize_t expected_size,
|
||||
@ -434,7 +446,7 @@ drm_atomic_replace_property_blob_from_id(struct drm_crtc *crtc,
|
||||
struct drm_property_blob *new_blob = NULL;
|
||||
|
||||
if (blob_id != 0) {
|
||||
new_blob = drm_property_lookup_blob(crtc->dev, blob_id);
|
||||
new_blob = drm_property_lookup_blob(dev, blob_id);
|
||||
if (new_blob == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
@ -483,7 +495,7 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
|
||||
drm_property_blob_put(mode);
|
||||
return ret;
|
||||
} else if (property == config->degamma_lut_property) {
|
||||
ret = drm_atomic_replace_property_blob_from_id(crtc,
|
||||
ret = drm_atomic_replace_property_blob_from_id(dev,
|
||||
&state->degamma_lut,
|
||||
val,
|
||||
-1,
|
||||
@ -491,7 +503,7 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
|
||||
state->color_mgmt_changed |= replaced;
|
||||
return ret;
|
||||
} else if (property == config->ctm_property) {
|
||||
ret = drm_atomic_replace_property_blob_from_id(crtc,
|
||||
ret = drm_atomic_replace_property_blob_from_id(dev,
|
||||
&state->ctm,
|
||||
val,
|
||||
sizeof(struct drm_color_ctm),
|
||||
@ -499,7 +511,7 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
|
||||
state->color_mgmt_changed |= replaced;
|
||||
return ret;
|
||||
} else if (property == config->gamma_lut_property) {
|
||||
ret = drm_atomic_replace_property_blob_from_id(crtc,
|
||||
ret = drm_atomic_replace_property_blob_from_id(dev,
|
||||
&state->gamma_lut,
|
||||
val,
|
||||
-1,
|
||||
@ -977,6 +989,59 @@ static void drm_atomic_plane_print_state(struct drm_printer *p,
|
||||
plane->funcs->atomic_print_state(p, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_atomic_get_private_obj_state - get private object state
|
||||
* @state: global atomic state
|
||||
* @obj: private object to get the state for
|
||||
* @funcs: pointer to the struct of function pointers that identify the object
|
||||
* type
|
||||
*
|
||||
* This function returns the private object state for the given private object,
|
||||
* allocating the state if needed. It does not grab any locks as the caller is
|
||||
* expected to care of any required locking.
|
||||
*
|
||||
* RETURNS:
|
||||
*
|
||||
* Either the allocated state or the error code encoded into a pointer.
|
||||
*/
|
||||
void *
|
||||
drm_atomic_get_private_obj_state(struct drm_atomic_state *state, void *obj,
|
||||
const struct drm_private_state_funcs *funcs)
|
||||
{
|
||||
int index, num_objs, i;
|
||||
size_t size;
|
||||
struct __drm_private_objs_state *arr;
|
||||
|
||||
for (i = 0; i < state->num_private_objs; i++)
|
||||
if (obj == state->private_objs[i].obj &&
|
||||
state->private_objs[i].obj_state)
|
||||
return state->private_objs[i].obj_state;
|
||||
|
||||
num_objs = state->num_private_objs + 1;
|
||||
size = sizeof(*state->private_objs) * num_objs;
|
||||
arr = krealloc(state->private_objs, size, GFP_KERNEL);
|
||||
if (!arr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
state->private_objs = arr;
|
||||
index = state->num_private_objs;
|
||||
memset(&state->private_objs[index], 0, sizeof(*state->private_objs));
|
||||
|
||||
state->private_objs[index].obj_state = funcs->duplicate_state(state, obj);
|
||||
if (!state->private_objs[index].obj_state)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
state->private_objs[index].obj = obj;
|
||||
state->private_objs[index].funcs = funcs;
|
||||
state->num_private_objs = num_objs;
|
||||
|
||||
DRM_DEBUG_ATOMIC("Added new private object state %p to %p\n",
|
||||
state->private_objs[index].obj_state, state);
|
||||
|
||||
return state->private_objs[index].obj_state;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_get_private_obj_state);
|
||||
|
||||
/**
|
||||
* drm_atomic_get_connector_state - get connector state
|
||||
* @state: global atomic state object
|
||||
@ -1123,6 +1188,10 @@ int drm_atomic_connector_set_property(struct drm_connector *connector,
|
||||
*/
|
||||
if (state->link_status != DRM_LINK_STATUS_GOOD)
|
||||
state->link_status = val;
|
||||
} else if (property == config->aspect_ratio_property) {
|
||||
state->picture_aspect_ratio = val;
|
||||
} else if (property == connector->scaling_mode_property) {
|
||||
state->scaling_mode = val;
|
||||
} else if (connector->funcs->atomic_set_property) {
|
||||
return connector->funcs->atomic_set_property(connector,
|
||||
state, property, val);
|
||||
@ -1199,6 +1268,10 @@ drm_atomic_connector_get_property(struct drm_connector *connector,
|
||||
*val = state->tv.hue;
|
||||
} else if (property == config->link_status_property) {
|
||||
*val = state->link_status;
|
||||
} else if (property == config->aspect_ratio_property) {
|
||||
*val = state->picture_aspect_ratio;
|
||||
} else if (property == connector->scaling_mode_property) {
|
||||
*val = state->scaling_mode;
|
||||
} else if (connector->funcs->atomic_get_property) {
|
||||
return connector->funcs->atomic_get_property(connector,
|
||||
state, property, val);
|
||||
@ -1618,7 +1691,7 @@ int drm_atomic_commit(struct drm_atomic_state *state)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
DRM_DEBUG_ATOMIC("commiting %p\n", state);
|
||||
DRM_DEBUG_ATOMIC("committing %p\n", state);
|
||||
|
||||
return config->funcs->atomic_commit(state->dev, state, false);
|
||||
}
|
||||
@ -1647,7 +1720,7 @@ int drm_atomic_nonblocking_commit(struct drm_atomic_state *state)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
DRM_DEBUG_ATOMIC("commiting %p nonblocking\n", state);
|
||||
DRM_DEBUG_ATOMIC("committing %p nonblocking\n", state);
|
||||
|
||||
return config->funcs->atomic_commit(state->dev, state, true);
|
||||
}
|
||||
|
@ -1070,8 +1070,8 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables);
|
||||
*
|
||||
* Note that @pre_swap is needed since the point where we block for fences moves
|
||||
* around depending upon whether an atomic commit is blocking or
|
||||
* non-blocking. For async commit all waiting needs to happen after
|
||||
* drm_atomic_helper_swap_state() is called, but for synchronous commits we want
|
||||
* non-blocking. For non-blocking commit all waiting needs to happen after
|
||||
* drm_atomic_helper_swap_state() is called, but for blocking commits we want
|
||||
* to wait **before** we do anything that can't be easily rolled back. That is
|
||||
* before we call drm_atomic_helper_swap_state().
|
||||
*
|
||||
@ -2032,6 +2032,8 @@ void drm_atomic_helper_swap_state(struct drm_atomic_state *state,
|
||||
struct drm_plane *plane;
|
||||
struct drm_plane_state *old_plane_state, *new_plane_state;
|
||||
struct drm_crtc_commit *commit;
|
||||
void *obj, *obj_state;
|
||||
const struct drm_private_state_funcs *funcs;
|
||||
|
||||
if (stall) {
|
||||
for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
|
||||
@ -2092,6 +2094,9 @@ void drm_atomic_helper_swap_state(struct drm_atomic_state *state,
|
||||
state->planes[i].state = old_plane_state;
|
||||
plane->state = new_plane_state;
|
||||
}
|
||||
|
||||
__for_each_private_obj(state, obj, obj_state, i, funcs)
|
||||
funcs->swap_state(obj, &state->private_objs[i].obj_state);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_helper_swap_state);
|
||||
|
||||
@ -3517,7 +3522,8 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state);
|
||||
*
|
||||
* Implements support for legacy gamma correction table for drivers
|
||||
* that support color management through the DEGAMMA_LUT/GAMMA_LUT
|
||||
* properties.
|
||||
* properties. See drm_crtc_enable_color_mgmt() and the containing chapter for
|
||||
* how the atomic color management and gamma tables work.
|
||||
*/
|
||||
int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc,
|
||||
u16 *red, u16 *green, u16 *blue,
|
||||
|
@ -43,7 +43,8 @@
|
||||
*
|
||||
* Setting this to NULL (blob property value set to 0) means a
|
||||
* linear/pass-thru gamma table should be used. This is generally the
|
||||
* driver boot-up state too.
|
||||
* driver boot-up state too. Drivers can access this blob through
|
||||
* &drm_crtc_state.degamma_lut.
|
||||
*
|
||||
* “DEGAMMA_LUT_SIZE”:
|
||||
* Unsinged range property to give the size of the lookup table to be set
|
||||
@ -60,7 +61,8 @@
|
||||
*
|
||||
* Setting this to NULL (blob property value set to 0) means a
|
||||
* unit/pass-thru matrix should be used. This is generally the driver
|
||||
* boot-up state too.
|
||||
* boot-up state too. Drivers can access the blob for the color conversion
|
||||
* matrix through &drm_crtc_state.ctm.
|
||||
*
|
||||
* “GAMMA_LUT”:
|
||||
* Blob property to set the gamma lookup table (LUT) mapping pixel data
|
||||
@ -72,7 +74,8 @@
|
||||
*
|
||||
* Setting this to NULL (blob property value set to 0) means a
|
||||
* linear/pass-thru gamma table should be used. This is generally the
|
||||
* driver boot-up state too.
|
||||
* driver boot-up state too. Drivers can access this blob through
|
||||
* &drm_crtc_state.gamma_lut.
|
||||
*
|
||||
* “GAMMA_LUT_SIZE”:
|
||||
* Unsigned range property to give the size of the lookup table to be set
|
||||
|
@ -941,6 +941,10 @@ EXPORT_SYMBOL(drm_mode_create_tv_properties);
|
||||
*
|
||||
* Called by a driver the first time it's needed, must be attached to desired
|
||||
* connectors.
|
||||
*
|
||||
* Atomic drivers should use drm_connector_attach_scaling_mode_property()
|
||||
* instead to correctly assign &drm_connector_state.picture_aspect_ratio
|
||||
* in the atomic state.
|
||||
*/
|
||||
int drm_mode_create_scaling_mode_property(struct drm_device *dev)
|
||||
{
|
||||
@ -960,6 +964,66 @@ int drm_mode_create_scaling_mode_property(struct drm_device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL(drm_mode_create_scaling_mode_property);
|
||||
|
||||
/**
|
||||
* drm_connector_attach_scaling_mode_property - attach atomic scaling mode property
|
||||
* @connector: connector to attach scaling mode property on.
|
||||
* @scaling_mode_mask: or'ed mask of BIT(%DRM_MODE_SCALE_\*).
|
||||
*
|
||||
* This is used to add support for scaling mode to atomic drivers.
|
||||
* The scaling mode will be set to &drm_connector_state.picture_aspect_ratio
|
||||
* and can be used from &drm_connector_helper_funcs->atomic_check for validation.
|
||||
*
|
||||
* This is the atomic version of drm_mode_create_scaling_mode_property().
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, negative errno on failure.
|
||||
*/
|
||||
int drm_connector_attach_scaling_mode_property(struct drm_connector *connector,
|
||||
u32 scaling_mode_mask)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_property *scaling_mode_property;
|
||||
int i, j = 0;
|
||||
const unsigned valid_scaling_mode_mask =
|
||||
(1U << ARRAY_SIZE(drm_scaling_mode_enum_list)) - 1;
|
||||
|
||||
if (WARN_ON(hweight32(scaling_mode_mask) < 2 ||
|
||||
scaling_mode_mask & ~valid_scaling_mode_mask))
|
||||
return -EINVAL;
|
||||
|
||||
scaling_mode_property =
|
||||
drm_property_create(dev, DRM_MODE_PROP_ENUM, "scaling mode",
|
||||
hweight32(scaling_mode_mask));
|
||||
|
||||
if (!scaling_mode_property)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(drm_scaling_mode_enum_list); i++) {
|
||||
int ret;
|
||||
|
||||
if (!(BIT(i) & scaling_mode_mask))
|
||||
continue;
|
||||
|
||||
ret = drm_property_add_enum(scaling_mode_property, j++,
|
||||
drm_scaling_mode_enum_list[i].type,
|
||||
drm_scaling_mode_enum_list[i].name);
|
||||
|
||||
if (ret) {
|
||||
drm_property_destroy(dev, scaling_mode_property);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
drm_object_attach_property(&connector->base,
|
||||
scaling_mode_property, 0);
|
||||
|
||||
connector->scaling_mode_property = scaling_mode_property;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_connector_attach_scaling_mode_property);
|
||||
|
||||
/**
|
||||
* drm_mode_create_aspect_ratio_property - create aspect ratio property
|
||||
* @dev: DRM device
|
||||
|
@ -737,16 +737,16 @@ static void drm_dp_mst_put_payload_id(struct drm_dp_mst_topology_mgr *mgr,
|
||||
static bool check_txmsg_state(struct drm_dp_mst_topology_mgr *mgr,
|
||||
struct drm_dp_sideband_msg_tx *txmsg)
|
||||
{
|
||||
bool ret;
|
||||
unsigned int state;
|
||||
|
||||
/*
|
||||
* All updates to txmsg->state are protected by mgr->qlock, and the two
|
||||
* cases we check here are terminal states. For those the barriers
|
||||
* provided by the wake_up/wait_event pair are enough.
|
||||
*/
|
||||
ret = (txmsg->state == DRM_DP_SIDEBAND_TX_RX ||
|
||||
txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT);
|
||||
return ret;
|
||||
state = READ_ONCE(txmsg->state);
|
||||
return (state == DRM_DP_SIDEBAND_TX_RX ||
|
||||
state == DRM_DP_SIDEBAND_TX_TIMEOUT);
|
||||
}
|
||||
|
||||
static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb,
|
||||
@ -855,7 +855,7 @@ static void drm_dp_destroy_mst_branch_device(struct kref *kref)
|
||||
mutex_unlock(&mstb->mgr->qlock);
|
||||
|
||||
if (wake_tx)
|
||||
wake_up(&mstb->mgr->tx_waitq);
|
||||
wake_up_all(&mstb->mgr->tx_waitq);
|
||||
|
||||
kref_put(kref, drm_dp_free_mst_branch_device);
|
||||
}
|
||||
@ -1510,7 +1510,7 @@ static void process_single_down_tx_qlock(struct drm_dp_mst_topology_mgr *mgr)
|
||||
if (txmsg->seqno != -1)
|
||||
txmsg->dst->tx_slots[txmsg->seqno] = NULL;
|
||||
txmsg->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
|
||||
wake_up(&mgr->tx_waitq);
|
||||
wake_up_all(&mgr->tx_waitq);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2258,7 +2258,7 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
|
||||
mstb->tx_slots[slot] = NULL;
|
||||
mutex_unlock(&mgr->qlock);
|
||||
|
||||
wake_up(&mgr->tx_waitq);
|
||||
wake_up_all(&mgr->tx_waitq);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -2497,6 +2497,81 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_dp_atomic_find_vcpi_slots() - Find and add vcpi slots to the state
|
||||
* @state: global atomic state
|
||||
* @mgr: MST topology manager for the port
|
||||
* @port: port to find vcpi slots for
|
||||
* @pbn: bandwidth required for the mode in PBN
|
||||
*
|
||||
* RETURNS:
|
||||
* Total slots in the atomic state assigned for this port or error
|
||||
*/
|
||||
int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
|
||||
struct drm_dp_mst_topology_mgr *mgr,
|
||||
struct drm_dp_mst_port *port, int pbn)
|
||||
{
|
||||
struct drm_dp_mst_topology_state *topology_state;
|
||||
int req_slots;
|
||||
|
||||
topology_state = drm_atomic_get_mst_topology_state(state, mgr);
|
||||
if (topology_state == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
port = drm_dp_get_validated_port_ref(mgr, port);
|
||||
if (port == NULL)
|
||||
return -EINVAL;
|
||||
req_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
|
||||
DRM_DEBUG_KMS("vcpi slots req=%d, avail=%d\n",
|
||||
req_slots, topology_state->avail_slots);
|
||||
|
||||
if (req_slots > topology_state->avail_slots) {
|
||||
drm_dp_put_port(port);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
topology_state->avail_slots -= req_slots;
|
||||
DRM_DEBUG_KMS("vcpi slots avail=%d", topology_state->avail_slots);
|
||||
|
||||
drm_dp_put_port(port);
|
||||
return req_slots;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);
|
||||
|
||||
/**
|
||||
* drm_dp_atomic_release_vcpi_slots() - Release allocated vcpi slots
|
||||
* @state: global atomic state
|
||||
* @mgr: MST topology manager for the port
|
||||
* @slots: number of vcpi slots to release
|
||||
*
|
||||
* RETURNS:
|
||||
* 0 if @slots were added back to &drm_dp_mst_topology_state->avail_slots or
|
||||
* negative error code
|
||||
*/
|
||||
int drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state,
|
||||
struct drm_dp_mst_topology_mgr *mgr,
|
||||
int slots)
|
||||
{
|
||||
struct drm_dp_mst_topology_state *topology_state;
|
||||
|
||||
topology_state = drm_atomic_get_mst_topology_state(state, mgr);
|
||||
if (topology_state == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* We cannot rely on port->vcpi.num_slots to update
|
||||
* topology_state->avail_slots as the port may not exist if the parent
|
||||
* branch device was unplugged. This should be fixed by tracking
|
||||
* per-port slot allocation in drm_dp_mst_topology_state instead of
|
||||
* depending on the caller to tell us how many slots to release.
|
||||
*/
|
||||
topology_state->avail_slots += slots;
|
||||
DRM_DEBUG_KMS("vcpi slots released=%d, avail=%d\n",
|
||||
slots, topology_state->avail_slots);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_atomic_release_vcpi_slots);
|
||||
|
||||
/**
|
||||
* drm_dp_mst_allocate_vcpi() - Allocate a virtual channel
|
||||
* @mgr: manager for this port
|
||||
@ -2936,6 +3011,69 @@ static void drm_dp_destroy_connector_work(struct work_struct *work)
|
||||
(*mgr->cbs->hotplug)(mgr);
|
||||
}
|
||||
|
||||
void *drm_dp_mst_duplicate_state(struct drm_atomic_state *state, void *obj)
|
||||
{
|
||||
struct drm_dp_mst_topology_mgr *mgr = obj;
|
||||
struct drm_dp_mst_topology_state *new_mst_state;
|
||||
|
||||
if (WARN_ON(!mgr->state))
|
||||
return NULL;
|
||||
|
||||
new_mst_state = kmemdup(mgr->state, sizeof(*new_mst_state), GFP_KERNEL);
|
||||
if (new_mst_state)
|
||||
new_mst_state->state = state;
|
||||
return new_mst_state;
|
||||
}
|
||||
|
||||
void drm_dp_mst_swap_state(void *obj, void **obj_state_ptr)
|
||||
{
|
||||
struct drm_dp_mst_topology_mgr *mgr = obj;
|
||||
struct drm_dp_mst_topology_state **topology_state_ptr;
|
||||
|
||||
topology_state_ptr = (struct drm_dp_mst_topology_state **)obj_state_ptr;
|
||||
|
||||
mgr->state->state = (*topology_state_ptr)->state;
|
||||
swap(*topology_state_ptr, mgr->state);
|
||||
mgr->state->state = NULL;
|
||||
}
|
||||
|
||||
void drm_dp_mst_destroy_state(void *obj_state)
|
||||
{
|
||||
kfree(obj_state);
|
||||
}
|
||||
|
||||
static const struct drm_private_state_funcs mst_state_funcs = {
|
||||
.duplicate_state = drm_dp_mst_duplicate_state,
|
||||
.swap_state = drm_dp_mst_swap_state,
|
||||
.destroy_state = drm_dp_mst_destroy_state,
|
||||
};
|
||||
|
||||
/**
|
||||
* drm_atomic_get_mst_topology_state: get MST topology state
|
||||
*
|
||||
* @state: global atomic state
|
||||
* @mgr: MST topology manager, also the private object in this case
|
||||
*
|
||||
* This function wraps drm_atomic_get_priv_obj_state() passing in the MST atomic
|
||||
* state vtable so that the private object state returned is that of a MST
|
||||
* topology object. Also, drm_atomic_get_private_obj_state() expects the caller
|
||||
* to care of the locking, so warn if don't hold the connection_mutex.
|
||||
*
|
||||
* RETURNS:
|
||||
*
|
||||
* The MST topology state or error pointer.
|
||||
*/
|
||||
struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_atomic_state *state,
|
||||
struct drm_dp_mst_topology_mgr *mgr)
|
||||
{
|
||||
struct drm_device *dev = mgr->dev;
|
||||
|
||||
WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
|
||||
return drm_atomic_get_private_obj_state(state, mgr,
|
||||
&mst_state_funcs);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_get_mst_topology_state);
|
||||
|
||||
/**
|
||||
* drm_dp_mst_topology_mgr_init - initialise a topology manager
|
||||
* @mgr: manager struct to initialise
|
||||
@ -2980,6 +3118,15 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
|
||||
if (test_calc_pbn_mode() < 0)
|
||||
DRM_ERROR("MST PBN self-test failed\n");
|
||||
|
||||
mgr->state = kzalloc(sizeof(*mgr->state), GFP_KERNEL);
|
||||
if (mgr->state == NULL)
|
||||
return -ENOMEM;
|
||||
mgr->state->mgr = mgr;
|
||||
|
||||
/* max. time slots - one slot for MTP header */
|
||||
mgr->state->avail_slots = 63;
|
||||
mgr->funcs = &mst_state_funcs;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_mst_topology_mgr_init);
|
||||
@ -3000,6 +3147,9 @@ void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
|
||||
mutex_unlock(&mgr->payload_lock);
|
||||
mgr->dev = NULL;
|
||||
mgr->aux = NULL;
|
||||
kfree(mgr->state);
|
||||
mgr->state = NULL;
|
||||
mgr->funcs = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy);
|
||||
|
||||
|
@ -189,7 +189,7 @@ struct drm_framebuffer *drm_fb_cma_create_with_funcs(struct drm_device *dev,
|
||||
obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[i]);
|
||||
if (!obj) {
|
||||
dev_err(dev->dev, "Failed to lookup GEM object\n");
|
||||
ret = -ENXIO;
|
||||
ret = -ENOENT;
|
||||
goto err_gem_object_put;
|
||||
}
|
||||
|
||||
@ -259,6 +259,33 @@ struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj);
|
||||
|
||||
/**
|
||||
* drm_fb_cma_get_gem_addr() - Get physical address for framebuffer
|
||||
* @fb: The framebuffer
|
||||
* @state: Which state of drm plane
|
||||
* @plane: Which plane
|
||||
* Return the CMA GEM address for given framebuffer.
|
||||
*
|
||||
* This function will usually be called from the PLANE callback functions.
|
||||
*/
|
||||
dma_addr_t drm_fb_cma_get_gem_addr(struct drm_framebuffer *fb,
|
||||
struct drm_plane_state *state,
|
||||
unsigned int plane)
|
||||
{
|
||||
struct drm_fb_cma *fb_cma = to_fb_cma(fb);
|
||||
dma_addr_t paddr;
|
||||
|
||||
if (plane >= 4)
|
||||
return 0;
|
||||
|
||||
paddr = fb_cma->obj[plane]->paddr + fb->offsets[plane];
|
||||
paddr += fb->format->cpp[plane] * (state->src_x >> 16);
|
||||
paddr += fb->pitches[plane] * (state->src_y >> 16);
|
||||
|
||||
return paddr;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_addr);
|
||||
|
||||
/**
|
||||
* drm_fb_cma_prepare_fb() - Prepare CMA framebuffer
|
||||
* @plane: Which plane
|
||||
|
@ -351,9 +351,8 @@ void drm_lastclose(struct drm_device * dev)
|
||||
*
|
||||
* This function must be used by drivers as their &file_operations.release
|
||||
* method. It frees any resources associated with the open file, and calls the
|
||||
* &drm_driver.preclose and &drm_driver.lastclose driver callbacks. If this is
|
||||
* the last open file for the DRM device also proceeds to call the
|
||||
* &drm_driver.lastclose driver callback.
|
||||
* &drm_driver.postclose driver callback. If this is the last open file for the
|
||||
* DRM device also proceeds to call the &drm_driver.lastclose driver callback.
|
||||
*
|
||||
* RETURNS:
|
||||
*
|
||||
@ -373,7 +372,8 @@ int drm_release(struct inode *inode, struct file *filp)
|
||||
list_del(&file_priv->lhead);
|
||||
mutex_unlock(&dev->filelist_mutex);
|
||||
|
||||
if (dev->driver->preclose)
|
||||
if (drm_core_check_feature(dev, DRIVER_LEGACY) &&
|
||||
dev->driver->preclose)
|
||||
dev->driver->preclose(dev, file_priv);
|
||||
|
||||
/* ========================================================
|
||||
|
@ -54,7 +54,7 @@
|
||||
|
||||
static bool
|
||||
drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
|
||||
struct timeval *tvblank, unsigned flags);
|
||||
struct timeval *tvblank, bool in_vblank_irq);
|
||||
|
||||
static unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */
|
||||
|
||||
@ -138,7 +138,7 @@ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe
|
||||
*/
|
||||
do {
|
||||
cur_vblank = __get_vblank_counter(dev, pipe);
|
||||
rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, 0);
|
||||
rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, false);
|
||||
} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
|
||||
|
||||
/*
|
||||
@ -171,7 +171,7 @@ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe
|
||||
* device vblank fields.
|
||||
*/
|
||||
static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
|
||||
unsigned long flags)
|
||||
bool in_vblank_irq)
|
||||
{
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
u32 cur_vblank, diff;
|
||||
@ -194,7 +194,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
|
||||
*/
|
||||
do {
|
||||
cur_vblank = __get_vblank_counter(dev, pipe);
|
||||
rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, flags);
|
||||
rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq);
|
||||
} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
|
||||
|
||||
if (dev->max_vblank_count != 0) {
|
||||
@ -214,13 +214,13 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
|
||||
*/
|
||||
diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns);
|
||||
|
||||
if (diff == 0 && flags & DRM_CALLED_FROM_VBLIRQ)
|
||||
if (diff == 0 && in_vblank_irq)
|
||||
DRM_DEBUG_VBL("crtc %u: Redundant vblirq ignored."
|
||||
" diff_ns = %lld, framedur_ns = %d)\n",
|
||||
pipe, (long long) diff_ns, framedur_ns);
|
||||
} else {
|
||||
/* some kind of default for drivers w/o accurate vbl timestamping */
|
||||
diff = (flags & DRM_CALLED_FROM_VBLIRQ) != 0;
|
||||
diff = in_vblank_irq ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -253,7 +253,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
|
||||
* Otherwise reinitialize delayed at next vblank interrupt and assign 0
|
||||
* for now, to mark the vblanktimestamp as invalid.
|
||||
*/
|
||||
if (!rc && (flags & DRM_CALLED_FROM_VBLIRQ) == 0)
|
||||
if (!rc && in_vblank_irq)
|
||||
t_vblank = (struct timeval) {0, 0};
|
||||
|
||||
store_vblank(dev, pipe, diff, &t_vblank, cur_vblank);
|
||||
@ -291,7 +291,7 @@ u32 drm_accurate_vblank_count(struct drm_crtc *crtc)
|
||||
|
||||
spin_lock_irqsave(&dev->vblank_time_lock, flags);
|
||||
|
||||
drm_update_vblank_count(dev, pipe, 0);
|
||||
drm_update_vblank_count(dev, pipe, false);
|
||||
vblank = drm_vblank_count(dev, pipe);
|
||||
|
||||
spin_unlock_irqrestore(&dev->vblank_time_lock, flags);
|
||||
@ -349,7 +349,7 @@ static void vblank_disable_and_save(struct drm_device *dev, unsigned int pipe)
|
||||
* this time. This makes the count account for the entire time
|
||||
* between drm_crtc_vblank_on() and drm_crtc_vblank_off().
|
||||
*/
|
||||
drm_update_vblank_count(dev, pipe, 0);
|
||||
drm_update_vblank_count(dev, pipe, false);
|
||||
|
||||
spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
|
||||
}
|
||||
@ -684,6 +684,7 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc,
|
||||
|
||||
vblank->linedur_ns = linedur_ns;
|
||||
vblank->framedur_ns = framedur_ns;
|
||||
vblank->hwmode = *mode;
|
||||
|
||||
DRM_DEBUG("crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n",
|
||||
crtc->base.id, mode->crtc_htotal,
|
||||
@ -700,10 +701,10 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
|
||||
* @max_error: Desired maximum allowable error in timestamps (nanosecs)
|
||||
* On return contains true maximum error of timestamp
|
||||
* @vblank_time: Pointer to struct timeval which should receive the timestamp
|
||||
* @flags: Flags to pass to driver:
|
||||
* 0 = Default,
|
||||
* DRM_CALLED_FROM_VBLIRQ = If function is called from vbl IRQ handler
|
||||
* @mode: mode which defines the scanout timings
|
||||
* @in_vblank_irq:
|
||||
* True when called from drm_crtc_handle_vblank(). Some drivers
|
||||
* need to apply some workarounds for gpu-specific vblank irq quirks
|
||||
* if flag is set.
|
||||
*
|
||||
* Implements calculation of exact vblank timestamps from given drm_display_mode
|
||||
* timings and current video scanout position of a CRTC. This can be called from
|
||||
@ -723,52 +724,62 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
|
||||
* returns as no operation if a doublescan or interlaced video mode is
|
||||
* active. Higher level code is expected to handle this.
|
||||
*
|
||||
* This function can be used to implement the &drm_driver.get_vblank_timestamp
|
||||
* directly, if the driver implements the &drm_driver.get_scanout_position hook.
|
||||
*
|
||||
* Note that atomic drivers must call drm_calc_timestamping_constants() before
|
||||
* enabling a CRTC. The atomic helpers already take care of that in
|
||||
* drm_atomic_helper_update_legacy_modeset_state().
|
||||
*
|
||||
* Returns:
|
||||
* Negative value on error, failure or if not supported in current
|
||||
* video mode:
|
||||
*
|
||||
* -EINVAL Invalid CRTC.
|
||||
* -EAGAIN Temporary unavailable, e.g., called before initial modeset.
|
||||
* -ENOTSUPP Function not supported in current display mode.
|
||||
* -EIO Failed, e.g., due to failed scanout position query.
|
||||
*
|
||||
* Returns or'ed positive status flags on success:
|
||||
*
|
||||
* DRM_VBLANKTIME_SCANOUTPOS_METHOD - Signal this method used for timestamping.
|
||||
* DRM_VBLANKTIME_INVBL - Timestamp taken while scanout was in vblank interval.
|
||||
*
|
||||
* Returns true on success, and false on failure, i.e. when no accurate
|
||||
* timestamp could be acquired.
|
||||
*/
|
||||
int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
|
||||
unsigned int pipe,
|
||||
int *max_error,
|
||||
struct timeval *vblank_time,
|
||||
unsigned flags,
|
||||
const struct drm_display_mode *mode)
|
||||
bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
|
||||
unsigned int pipe,
|
||||
int *max_error,
|
||||
struct timeval *vblank_time,
|
||||
bool in_vblank_irq)
|
||||
{
|
||||
struct timeval tv_etime;
|
||||
ktime_t stime, etime;
|
||||
unsigned int vbl_status;
|
||||
int ret = DRM_VBLANKTIME_SCANOUTPOS_METHOD;
|
||||
bool vbl_status;
|
||||
struct drm_crtc *crtc;
|
||||
const struct drm_display_mode *mode;
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
int vpos, hpos, i;
|
||||
int delta_ns, duration_ns;
|
||||
|
||||
if (pipe >= dev->num_crtcs) {
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return false;
|
||||
|
||||
crtc = drm_crtc_from_index(dev, pipe);
|
||||
|
||||
if (pipe >= dev->num_crtcs || !crtc) {
|
||||
DRM_ERROR("Invalid crtc %u\n", pipe);
|
||||
return -EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Scanout position query not supported? Should not happen. */
|
||||
if (!dev->driver->get_scanout_position) {
|
||||
DRM_ERROR("Called from driver w/o get_scanout_position()!?\n");
|
||||
return -EIO;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (drm_drv_uses_atomic_modeset(dev))
|
||||
mode = &vblank->hwmode;
|
||||
else
|
||||
mode = &crtc->hwmode;
|
||||
|
||||
/* If mode timing undefined, just return as no-op:
|
||||
* Happens during initial modesetting of a crtc.
|
||||
*/
|
||||
if (mode->crtc_clock == 0) {
|
||||
DRM_DEBUG("crtc %u: Noop due to uninitialized mode.\n", pipe);
|
||||
return -EAGAIN;
|
||||
WARN_ON_ONCE(drm_drv_uses_atomic_modeset(dev));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Get current scanout position with system timestamp.
|
||||
@ -783,16 +794,17 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
|
||||
* Get vertical and horizontal scanout position vpos, hpos,
|
||||
* and bounding timestamps stime, etime, pre/post query.
|
||||
*/
|
||||
vbl_status = dev->driver->get_scanout_position(dev, pipe, flags,
|
||||
vbl_status = dev->driver->get_scanout_position(dev, pipe,
|
||||
in_vblank_irq,
|
||||
&vpos, &hpos,
|
||||
&stime, &etime,
|
||||
mode);
|
||||
|
||||
/* Return as no-op if scanout query unsupported or failed. */
|
||||
if (!(vbl_status & DRM_SCANOUTPOS_VALID)) {
|
||||
DRM_DEBUG("crtc %u : scanoutpos query failed [0x%x].\n",
|
||||
pipe, vbl_status);
|
||||
return -EIO;
|
||||
if (!vbl_status) {
|
||||
DRM_DEBUG("crtc %u : scanoutpos query failed.\n",
|
||||
pipe);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Compute uncertainty in timestamp of scanout position query. */
|
||||
@ -830,13 +842,13 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
|
||||
etime = ktime_sub_ns(etime, delta_ns);
|
||||
*vblank_time = ktime_to_timeval(etime);
|
||||
|
||||
DRM_DEBUG_VBL("crtc %u : v 0x%x p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
|
||||
pipe, vbl_status, hpos, vpos,
|
||||
DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
|
||||
pipe, hpos, vpos,
|
||||
(long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
|
||||
(long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
|
||||
duration_ns/1000, i);
|
||||
|
||||
return ret;
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos);
|
||||
|
||||
@ -854,9 +866,10 @@ static struct timeval get_drm_timestamp(void)
|
||||
* @dev: DRM device
|
||||
* @pipe: index of CRTC whose vblank timestamp to retrieve
|
||||
* @tvblank: Pointer to target struct timeval which should receive the timestamp
|
||||
* @flags: Flags to pass to driver:
|
||||
* 0 = Default,
|
||||
* DRM_CALLED_FROM_VBLIRQ = If function is called from vbl IRQ handler
|
||||
* @in_vblank_irq:
|
||||
* True when called from drm_crtc_handle_vblank(). Some drivers
|
||||
* need to apply some workarounds for gpu-specific vblank irq quirks
|
||||
* if flag is set.
|
||||
*
|
||||
* Fetches the system timestamp corresponding to the time of the most recent
|
||||
* vblank interval on specified CRTC. May call into kms-driver to
|
||||
@ -870,27 +883,25 @@ static struct timeval get_drm_timestamp(void)
|
||||
*/
|
||||
static bool
|
||||
drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
|
||||
struct timeval *tvblank, unsigned flags)
|
||||
struct timeval *tvblank, bool in_vblank_irq)
|
||||
{
|
||||
int ret;
|
||||
bool ret = false;
|
||||
|
||||
/* Define requested maximum error on timestamps (nanoseconds). */
|
||||
int max_error = (int) drm_timestamp_precision * 1000;
|
||||
|
||||
/* Query driver if possible and precision timestamping enabled. */
|
||||
if (dev->driver->get_vblank_timestamp && (max_error > 0)) {
|
||||
if (dev->driver->get_vblank_timestamp && (max_error > 0))
|
||||
ret = dev->driver->get_vblank_timestamp(dev, pipe, &max_error,
|
||||
tvblank, flags);
|
||||
if (ret > 0)
|
||||
return true;
|
||||
}
|
||||
tvblank, in_vblank_irq);
|
||||
|
||||
/* GPU high precision timestamp query unsupported or failed.
|
||||
* Return current monotonic/gettimeofday timestamp as best estimate.
|
||||
*/
|
||||
*tvblank = get_drm_timestamp();
|
||||
if (!ret)
|
||||
*tvblank = get_drm_timestamp();
|
||||
|
||||
return false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1329,6 +1340,10 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc)
|
||||
send_vblank_event(dev, e, seq, &now);
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->event_lock, irqflags);
|
||||
|
||||
/* Will be reset by the modeset helpers when re-enabling the crtc by
|
||||
* calling drm_calc_timestamping_constants(). */
|
||||
vblank->hwmode.crtc_clock = 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_crtc_vblank_off);
|
||||
|
||||
@ -1760,7 +1775,7 @@ bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe)
|
||||
return false;
|
||||
}
|
||||
|
||||
drm_update_vblank_count(dev, pipe, DRM_CALLED_FROM_VBLIRQ);
|
||||
drm_update_vblank_count(dev, pipe, true);
|
||||
|
||||
spin_unlock(&dev->vblank_time_lock);
|
||||
|
||||
|
@ -381,6 +381,7 @@ EXPORT_SYMBOL(drm_primary_helper_update);
|
||||
/**
|
||||
* drm_primary_helper_disable() - Helper for primary plane disable
|
||||
* @plane: plane to disable
|
||||
* @ctx: lock acquire context, not used here
|
||||
*
|
||||
* Provides a default plane disable handler for primary planes. This is handler
|
||||
* is called in response to a userspace SetPlane operation on the plane with a
|
||||
@ -510,12 +511,10 @@ int drm_plane_helper_commit(struct drm_plane *plane,
|
||||
if (plane_funcs->cleanup_fb)
|
||||
plane_funcs->cleanup_fb(plane, plane_state);
|
||||
out:
|
||||
if (plane_state) {
|
||||
if (plane->funcs->atomic_destroy_state)
|
||||
plane->funcs->atomic_destroy_state(plane, plane_state);
|
||||
else
|
||||
drm_atomic_helper_plane_destroy_state(plane, plane_state);
|
||||
}
|
||||
if (plane->funcs->atomic_destroy_state)
|
||||
plane->funcs->atomic_destroy_state(plane, plane_state);
|
||||
else
|
||||
drm_atomic_helper_plane_destroy_state(plane, plane_state);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -595,15 +595,18 @@ out_unlock:
|
||||
EXPORT_SYMBOL(drm_gem_prime_handle_to_fd);
|
||||
|
||||
/**
|
||||
* drm_gem_prime_import - helper library implementation of the import callback
|
||||
* drm_gem_prime_import_dev - core implementation of the import callback
|
||||
* @dev: drm_device to import into
|
||||
* @dma_buf: dma-buf object to import
|
||||
* @attach_dev: struct device to dma_buf attach
|
||||
*
|
||||
* This is the implementation of the gem_prime_import functions for GEM drivers
|
||||
* using the PRIME helpers.
|
||||
* This is the core of drm_gem_prime_import. It's designed to be called by
|
||||
* drivers who want to use a different device structure than dev->dev for
|
||||
* attaching via dma_buf.
|
||||
*/
|
||||
struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev,
|
||||
struct dma_buf *dma_buf)
|
||||
struct drm_gem_object *drm_gem_prime_import_dev(struct drm_device *dev,
|
||||
struct dma_buf *dma_buf,
|
||||
struct device *attach_dev)
|
||||
{
|
||||
struct dma_buf_attachment *attach;
|
||||
struct sg_table *sgt;
|
||||
@ -625,7 +628,7 @@ struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev,
|
||||
if (!dev->driver->gem_prime_import_sg_table)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
attach = dma_buf_attach(dma_buf, dev->dev);
|
||||
attach = dma_buf_attach(dma_buf, attach_dev);
|
||||
if (IS_ERR(attach))
|
||||
return ERR_CAST(attach);
|
||||
|
||||
@ -655,6 +658,21 @@ fail_detach:
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_prime_import_dev);
|
||||
|
||||
/**
|
||||
* drm_gem_prime_import - helper library implementation of the import callback
|
||||
* @dev: drm_device to import into
|
||||
* @dma_buf: dma-buf object to import
|
||||
*
|
||||
* This is the implementation of the gem_prime_import functions for GEM drivers
|
||||
* using the PRIME helpers.
|
||||
*/
|
||||
struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev,
|
||||
struct dma_buf *dma_buf)
|
||||
{
|
||||
return drm_gem_prime_import_dev(dev, dma_buf, dev->dev);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_prime_import);
|
||||
|
||||
/**
|
||||
|
@ -82,14 +82,9 @@ err_file_priv_free:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void exynos_drm_preclose(struct drm_device *dev,
|
||||
struct drm_file *file)
|
||||
{
|
||||
exynos_drm_subdrv_close(dev, file);
|
||||
}
|
||||
|
||||
static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
|
||||
{
|
||||
exynos_drm_subdrv_close(dev, file);
|
||||
kfree(file->driver_priv);
|
||||
file->driver_priv = NULL;
|
||||
}
|
||||
@ -145,7 +140,6 @@ static struct drm_driver exynos_drm_driver = {
|
||||
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME
|
||||
| DRIVER_ATOMIC | DRIVER_RENDER,
|
||||
.open = exynos_drm_open,
|
||||
.preclose = exynos_drm_preclose,
|
||||
.lastclose = exynos_drm_lastclose,
|
||||
.postclose = exynos_drm_postclose,
|
||||
.gem_free_object_unlocked = exynos_drm_gem_free_object,
|
||||
|
@ -32,53 +32,20 @@ static struct drm_display_mode *tpo_vid_get_config_mode(struct drm_device *dev)
|
||||
struct drm_display_mode *mode;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct oaktrail_timing_info *ti = &dev_priv->gct_data.DTD;
|
||||
bool use_gct = false;
|
||||
|
||||
mode = kzalloc(sizeof(*mode), GFP_KERNEL);
|
||||
if (!mode)
|
||||
return NULL;
|
||||
|
||||
if (use_gct) {
|
||||
mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo;
|
||||
mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo;
|
||||
mode->hsync_start = mode->hdisplay +
|
||||
((ti->hsync_offset_hi << 8) |
|
||||
ti->hsync_offset_lo);
|
||||
mode->hsync_end = mode->hsync_start +
|
||||
((ti->hsync_pulse_width_hi << 8) |
|
||||
ti->hsync_pulse_width_lo);
|
||||
mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) |
|
||||
ti->hblank_lo);
|
||||
mode->vsync_start =
|
||||
mode->vdisplay + ((ti->vsync_offset_hi << 8) |
|
||||
ti->vsync_offset_lo);
|
||||
mode->vsync_end =
|
||||
mode->vsync_start + ((ti->vsync_pulse_width_hi << 8) |
|
||||
ti->vsync_pulse_width_lo);
|
||||
mode->vtotal = mode->vdisplay +
|
||||
((ti->vblank_hi << 8) | ti->vblank_lo);
|
||||
mode->clock = ti->pixel_clock * 10;
|
||||
|
||||
dev_dbg(dev->dev, "hdisplay is %d\n", mode->hdisplay);
|
||||
dev_dbg(dev->dev, "vdisplay is %d\n", mode->vdisplay);
|
||||
dev_dbg(dev->dev, "HSS is %d\n", mode->hsync_start);
|
||||
dev_dbg(dev->dev, "HSE is %d\n", mode->hsync_end);
|
||||
dev_dbg(dev->dev, "htotal is %d\n", mode->htotal);
|
||||
dev_dbg(dev->dev, "VSS is %d\n", mode->vsync_start);
|
||||
dev_dbg(dev->dev, "VSE is %d\n", mode->vsync_end);
|
||||
dev_dbg(dev->dev, "vtotal is %d\n", mode->vtotal);
|
||||
dev_dbg(dev->dev, "clock is %d\n", mode->clock);
|
||||
} else {
|
||||
mode->hdisplay = 864;
|
||||
mode->vdisplay = 480;
|
||||
mode->hsync_start = 873;
|
||||
mode->hsync_end = 876;
|
||||
mode->htotal = 887;
|
||||
mode->vsync_start = 487;
|
||||
mode->vsync_end = 490;
|
||||
mode->vtotal = 499;
|
||||
mode->clock = 33264;
|
||||
}
|
||||
mode->hdisplay = 864;
|
||||
mode->vdisplay = 480;
|
||||
mode->hsync_start = 873;
|
||||
mode->hsync_end = 876;
|
||||
mode->htotal = 887;
|
||||
mode->vsync_start = 487;
|
||||
mode->vsync_end = 490;
|
||||
mode->vtotal = 499;
|
||||
mode->clock = 33264;
|
||||
|
||||
drm_mode_set_name(mode);
|
||||
drm_mode_set_crtcinfo(mode, 0);
|
||||
|
@ -720,9 +720,7 @@ static u32 i915_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
|
||||
struct drm_i915_private *dev_priv = to_i915(dev);
|
||||
i915_reg_t high_frame, low_frame;
|
||||
u32 high1, high2, low, pixel, vbl_start, hsync_start, htotal;
|
||||
struct intel_crtc *intel_crtc = intel_get_crtc_for_pipe(dev_priv,
|
||||
pipe);
|
||||
const struct drm_display_mode *mode = &intel_crtc->base.hwmode;
|
||||
const struct drm_display_mode *mode = &dev->vblank[pipe].hwmode;
|
||||
unsigned long irqflags;
|
||||
|
||||
htotal = mode->crtc_htotal;
|
||||
@ -779,13 +777,17 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
|
||||
{
|
||||
struct drm_device *dev = crtc->base.dev;
|
||||
struct drm_i915_private *dev_priv = to_i915(dev);
|
||||
const struct drm_display_mode *mode = &crtc->base.hwmode;
|
||||
const struct drm_display_mode *mode;
|
||||
struct drm_vblank_crtc *vblank;
|
||||
enum pipe pipe = crtc->pipe;
|
||||
int position, vtotal;
|
||||
|
||||
if (!crtc->active)
|
||||
return -1;
|
||||
|
||||
vblank = &crtc->base.dev->vblank[drm_crtc_index(&crtc->base)];
|
||||
mode = &vblank->hwmode;
|
||||
|
||||
vtotal = mode->crtc_vtotal;
|
||||
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
|
||||
vtotal /= 2;
|
||||
@ -827,10 +829,10 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
|
||||
return (position + crtc->scanline_offset) % vtotal;
|
||||
}
|
||||
|
||||
static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
|
||||
unsigned int flags, int *vpos, int *hpos,
|
||||
ktime_t *stime, ktime_t *etime,
|
||||
const struct drm_display_mode *mode)
|
||||
static bool i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
|
||||
bool in_vblank_irq, int *vpos, int *hpos,
|
||||
ktime_t *stime, ktime_t *etime,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(dev);
|
||||
struct intel_crtc *intel_crtc = intel_get_crtc_for_pipe(dev_priv,
|
||||
@ -838,13 +840,12 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
|
||||
int position;
|
||||
int vbl_start, vbl_end, hsync_start, htotal, vtotal;
|
||||
bool in_vbl = true;
|
||||
int ret = 0;
|
||||
unsigned long irqflags;
|
||||
|
||||
if (WARN_ON(!mode->crtc_clock)) {
|
||||
DRM_DEBUG_DRIVER("trying to get scanoutpos for disabled "
|
||||
"pipe %c\n", pipe_name(pipe));
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
htotal = mode->crtc_htotal;
|
||||
@ -859,8 +860,6 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
|
||||
vtotal /= 2;
|
||||
}
|
||||
|
||||
ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE;
|
||||
|
||||
/*
|
||||
* Lock uncore.lock, as we will do multiple timing critical raw
|
||||
* register reads, potentially with preemption disabled, so the
|
||||
@ -944,11 +943,7 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
|
||||
*hpos = position - (*vpos * htotal);
|
||||
}
|
||||
|
||||
/* In vblank? */
|
||||
if (in_vbl)
|
||||
ret |= DRM_SCANOUTPOS_IN_VBLANK;
|
||||
|
||||
return ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
int intel_get_crtc_scanline(struct intel_crtc *crtc)
|
||||
@ -964,37 +959,6 @@ int intel_get_crtc_scanline(struct intel_crtc *crtc)
|
||||
return position;
|
||||
}
|
||||
|
||||
static int i915_get_vblank_timestamp(struct drm_device *dev, unsigned int pipe,
|
||||
int *max_error,
|
||||
struct timeval *vblank_time,
|
||||
unsigned flags)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(dev);
|
||||
struct intel_crtc *crtc;
|
||||
|
||||
if (pipe >= INTEL_INFO(dev_priv)->num_pipes) {
|
||||
DRM_ERROR("Invalid crtc %u\n", pipe);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Get drm_crtc to timestamp: */
|
||||
crtc = intel_get_crtc_for_pipe(dev_priv, pipe);
|
||||
if (crtc == NULL) {
|
||||
DRM_ERROR("Invalid crtc %u\n", pipe);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!crtc->base.hwmode.crtc_clock) {
|
||||
DRM_DEBUG_KMS("crtc %u is disabled\n", pipe);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* Helper routine in DRM core does all the work: */
|
||||
return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
|
||||
vblank_time, flags,
|
||||
&crtc->base.hwmode);
|
||||
}
|
||||
|
||||
static void ironlake_rps_change_irq_handler(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
u32 busy_up, busy_down, max_avg, min_avg;
|
||||
@ -4294,7 +4258,7 @@ void intel_irq_init(struct drm_i915_private *dev_priv)
|
||||
|
||||
dev_priv->hotplug.hpd_storm_threshold = HPD_STORM_DEFAULT_THRESHOLD;
|
||||
|
||||
dev->driver->get_vblank_timestamp = i915_get_vblank_timestamp;
|
||||
dev->driver->get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos;
|
||||
dev->driver->get_scanout_position = i915_get_crtc_scanoutpos;
|
||||
|
||||
if (IS_CHERRYVIEW(dev_priv)) {
|
||||
|
@ -11444,12 +11444,6 @@ intel_modeset_update_crtc_state(struct drm_atomic_state *state)
|
||||
for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
|
||||
to_intel_crtc(crtc)->config = to_intel_crtc_state(new_crtc_state);
|
||||
|
||||
/* Update hwmode for vblank functions */
|
||||
if (new_crtc_state->active)
|
||||
crtc->hwmode = new_crtc_state->adjusted_mode;
|
||||
else
|
||||
crtc->hwmode.crtc_clock = 0;
|
||||
|
||||
/*
|
||||
* Update legacy state to satisfy fbc code. This can
|
||||
* be removed when fbc uses the atomic state.
|
||||
@ -15425,8 +15419,6 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
|
||||
to_intel_crtc_state(crtc->base.state);
|
||||
int pixclk = 0;
|
||||
|
||||
crtc->base.hwmode = crtc_state->base.adjusted_mode;
|
||||
|
||||
memset(&crtc->base.mode, 0, sizeof(crtc->base.mode));
|
||||
if (crtc_state->base.active) {
|
||||
intel_mode_from_pipe_config(&crtc->base.mode, crtc_state);
|
||||
@ -15456,7 +15448,8 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
|
||||
if (IS_BROADWELL(dev_priv) && crtc_state->ips_enabled)
|
||||
pixclk = DIV_ROUND_UP(pixclk * 100, 95);
|
||||
|
||||
drm_calc_timestamping_constants(&crtc->base, &crtc->base.hwmode);
|
||||
drm_calc_timestamping_constants(&crtc->base,
|
||||
&crtc_state->base.adjusted_mode);
|
||||
update_scanline_offset(crtc);
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
|
||||
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
||||
struct intel_connector *connector =
|
||||
to_intel_connector(conn_state->connector);
|
||||
struct drm_atomic_state *state;
|
||||
struct drm_atomic_state *state = pipe_config->base.state;
|
||||
int bpp;
|
||||
int lane_count, slots;
|
||||
const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
|
||||
@ -57,20 +57,24 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
|
||||
* seem to suggest we should do otherwise.
|
||||
*/
|
||||
lane_count = drm_dp_max_lane_count(intel_dp->dpcd);
|
||||
|
||||
pipe_config->lane_count = lane_count;
|
||||
|
||||
pipe_config->pipe_bpp = bpp;
|
||||
pipe_config->port_clock = intel_dp_max_link_rate(intel_dp);
|
||||
|
||||
state = pipe_config->base.state;
|
||||
pipe_config->port_clock = intel_dp_max_link_rate(intel_dp);
|
||||
|
||||
if (drm_dp_mst_port_has_audio(&intel_dp->mst_mgr, connector->port))
|
||||
pipe_config->has_audio = true;
|
||||
mst_pbn = drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock, bpp);
|
||||
|
||||
mst_pbn = drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock, bpp);
|
||||
pipe_config->pbn = mst_pbn;
|
||||
slots = drm_dp_find_vcpi_slots(&intel_dp->mst_mgr, mst_pbn);
|
||||
|
||||
slots = drm_dp_atomic_find_vcpi_slots(state, &intel_dp->mst_mgr,
|
||||
connector->port, mst_pbn);
|
||||
if (slots < 0) {
|
||||
DRM_DEBUG_KMS("failed finding vcpi slots:%d\n", slots);
|
||||
return false;
|
||||
}
|
||||
|
||||
intel_link_compute_m_n(bpp, lane_count,
|
||||
adjusted_mode->crtc_clock,
|
||||
@ -80,7 +84,38 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
|
||||
pipe_config->dp_m_n.tu = slots;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int intel_dp_mst_atomic_check(struct drm_connector *connector,
|
||||
struct drm_connector_state *new_conn_state)
|
||||
{
|
||||
struct drm_atomic_state *state = new_conn_state->state;
|
||||
struct drm_connector_state *old_conn_state;
|
||||
struct drm_crtc *old_crtc;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
int slots, ret = 0;
|
||||
|
||||
old_conn_state = drm_atomic_get_old_connector_state(state, connector);
|
||||
old_crtc = old_conn_state->crtc;
|
||||
if (!old_crtc)
|
||||
return ret;
|
||||
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state, old_crtc);
|
||||
slots = to_intel_crtc_state(crtc_state)->dp_m_n.tu;
|
||||
if (drm_atomic_crtc_needs_modeset(crtc_state) && slots > 0) {
|
||||
struct drm_dp_mst_topology_mgr *mgr;
|
||||
struct drm_encoder *old_encoder;
|
||||
|
||||
old_encoder = old_conn_state->best_encoder;
|
||||
mgr = &enc_to_mst(old_encoder)->primary->dp.mst_mgr;
|
||||
|
||||
ret = drm_dp_atomic_release_vcpi_slots(state, mgr, slots);
|
||||
if (ret)
|
||||
DRM_DEBUG_KMS("failed releasing %d vcpi slots:%d\n", slots, ret);
|
||||
else
|
||||
to_intel_crtc_state(crtc_state)->dp_m_n.tu = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void intel_mst_disable_dp(struct intel_encoder *encoder,
|
||||
@ -387,6 +422,7 @@ static const struct drm_connector_helper_funcs intel_dp_mst_connector_helper_fun
|
||||
.mode_valid = intel_dp_mst_mode_valid,
|
||||
.atomic_best_encoder = intel_mst_atomic_best_encoder,
|
||||
.best_encoder = intel_mst_best_encoder,
|
||||
.atomic_check = intel_dp_mst_atomic_check,
|
||||
};
|
||||
|
||||
static void intel_dp_mst_encoder_destroy(struct drm_encoder *encoder)
|
||||
|
@ -869,7 +869,6 @@ struct intel_hdmi {
|
||||
bool has_audio;
|
||||
enum hdmi_force_audio force_audio;
|
||||
bool rgb_quant_range_selectable;
|
||||
enum hdmi_picture_aspect aspect_ratio;
|
||||
struct intel_connector *attached_connector;
|
||||
void (*write_infoframe)(struct drm_encoder *encoder,
|
||||
const struct intel_crtc_state *crtc_state,
|
||||
|
@ -1403,7 +1403,7 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
|
||||
}
|
||||
|
||||
/* Set user selected PAR to incoming mode's member */
|
||||
adjusted_mode->picture_aspect_ratio = intel_hdmi->aspect_ratio;
|
||||
adjusted_mode->picture_aspect_ratio = conn_state->picture_aspect_ratio;
|
||||
|
||||
pipe_config->lane_count = 4;
|
||||
|
||||
@ -1649,19 +1649,7 @@ intel_hdmi_set_property(struct drm_connector *connector,
|
||||
}
|
||||
|
||||
if (property == connector->dev->mode_config.aspect_ratio_property) {
|
||||
switch (val) {
|
||||
case DRM_MODE_PICTURE_ASPECT_NONE:
|
||||
intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
|
||||
break;
|
||||
case DRM_MODE_PICTURE_ASPECT_4_3:
|
||||
intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_4_3;
|
||||
break;
|
||||
case DRM_MODE_PICTURE_ASPECT_16_9:
|
||||
intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_16_9;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
connector->state->picture_aspect_ratio = val;
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -1823,7 +1811,7 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *c
|
||||
intel_attach_broadcast_rgb_property(connector);
|
||||
intel_hdmi->color_range_auto = true;
|
||||
intel_attach_aspect_ratio_property(connector);
|
||||
intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
|
||||
connector->state->picture_aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -106,11 +106,6 @@ struct intel_sdvo {
|
||||
uint32_t color_range;
|
||||
bool color_range_auto;
|
||||
|
||||
/**
|
||||
* HDMI user specified aspect ratio
|
||||
*/
|
||||
enum hdmi_picture_aspect aspect_ratio;
|
||||
|
||||
/**
|
||||
* This is set if we're going to treat the device as TV-out.
|
||||
*
|
||||
@ -1186,7 +1181,7 @@ static bool intel_sdvo_compute_config(struct intel_encoder *encoder,
|
||||
|
||||
/* Set user selected PAR to incoming mode's member */
|
||||
if (intel_sdvo->is_hdmi)
|
||||
adjusted_mode->picture_aspect_ratio = intel_sdvo->aspect_ratio;
|
||||
adjusted_mode->picture_aspect_ratio = conn_state->picture_aspect_ratio;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -2067,19 +2062,7 @@ intel_sdvo_set_property(struct drm_connector *connector,
|
||||
}
|
||||
|
||||
if (property == connector->dev->mode_config.aspect_ratio_property) {
|
||||
switch (val) {
|
||||
case DRM_MODE_PICTURE_ASPECT_NONE:
|
||||
intel_sdvo->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
|
||||
break;
|
||||
case DRM_MODE_PICTURE_ASPECT_4_3:
|
||||
intel_sdvo->aspect_ratio = HDMI_PICTURE_ASPECT_4_3;
|
||||
break;
|
||||
case DRM_MODE_PICTURE_ASPECT_16_9:
|
||||
intel_sdvo->aspect_ratio = HDMI_PICTURE_ASPECT_16_9;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
connector->state->picture_aspect_ratio = val;
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -2418,7 +2401,7 @@ intel_sdvo_add_hdmi_properties(struct intel_sdvo *intel_sdvo,
|
||||
intel_sdvo->color_range_auto = true;
|
||||
}
|
||||
intel_attach_aspect_ratio_property(&connector->base.base);
|
||||
intel_sdvo->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
|
||||
connector->base.base.state->picture_aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
|
||||
}
|
||||
|
||||
static struct intel_sdvo_connector *intel_sdvo_connector_alloc(void)
|
||||
|
@ -527,31 +527,28 @@ static struct drm_encoder *get_encoder_from_crtc(struct drm_crtc *crtc)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
|
||||
unsigned int flags, int *vpos, int *hpos,
|
||||
ktime_t *stime, ktime_t *etime,
|
||||
const struct drm_display_mode *mode)
|
||||
static bool mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
|
||||
bool in_vblank_irq, int *vpos, int *hpos,
|
||||
ktime_t *stime, ktime_t *etime,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_encoder *encoder;
|
||||
int line, vsw, vbp, vactive_start, vactive_end, vfp_end;
|
||||
int ret = 0;
|
||||
|
||||
crtc = priv->crtcs[pipe];
|
||||
if (!crtc) {
|
||||
DRM_ERROR("Invalid crtc %d\n", pipe);
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
encoder = get_encoder_from_crtc(crtc);
|
||||
if (!encoder) {
|
||||
DRM_ERROR("no encoder found for crtc %d\n", pipe);
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE;
|
||||
|
||||
vsw = mode->crtc_vsync_end - mode->crtc_vsync_start;
|
||||
vbp = mode->crtc_vtotal - mode->crtc_vsync_end;
|
||||
|
||||
@ -575,10 +572,8 @@ static int mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
|
||||
|
||||
if (line < vactive_start) {
|
||||
line -= vactive_start;
|
||||
ret |= DRM_SCANOUTPOS_IN_VBLANK;
|
||||
} else if (line > vactive_end) {
|
||||
line = line - vfp_end - vactive_start;
|
||||
ret |= DRM_SCANOUTPOS_IN_VBLANK;
|
||||
} else {
|
||||
line -= vactive_start;
|
||||
}
|
||||
@ -589,31 +584,7 @@ static int mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
|
||||
if (etime)
|
||||
*etime = ktime_get();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mdp5_get_vblank_timestamp(struct drm_device *dev, unsigned int pipe,
|
||||
int *max_error,
|
||||
struct timeval *vblank_time,
|
||||
unsigned flags)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
if (pipe < 0 || pipe >= priv->num_crtcs) {
|
||||
DRM_ERROR("Invalid crtc %d\n", pipe);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
crtc = priv->crtcs[pipe];
|
||||
if (!crtc) {
|
||||
DRM_ERROR("Invalid crtc %d\n", pipe);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
|
||||
vblank_time, flags,
|
||||
&crtc->mode);
|
||||
return true;
|
||||
}
|
||||
|
||||
static u32 mdp5_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
|
||||
@ -725,7 +696,7 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
|
||||
dev->mode_config.max_width = 0xffff;
|
||||
dev->mode_config.max_height = 0xffff;
|
||||
|
||||
dev->driver->get_vblank_timestamp = mdp5_get_vblank_timestamp;
|
||||
dev->driver->get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos;
|
||||
dev->driver->get_scanout_position = mdp5_get_scanoutpos;
|
||||
dev->driver->get_vblank_counter = mdp5_get_vblank_counter;
|
||||
dev->max_vblank_count = 0xffffffff;
|
||||
|
@ -98,7 +98,7 @@ calc(int blanks, int blanke, int total, int line)
|
||||
return line;
|
||||
}
|
||||
|
||||
static int
|
||||
static bool
|
||||
nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos,
|
||||
ktime_t *stime, ktime_t *etime)
|
||||
{
|
||||
@ -111,16 +111,16 @@ nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos,
|
||||
};
|
||||
struct nouveau_display *disp = nouveau_display(crtc->dev);
|
||||
struct drm_vblank_crtc *vblank = &crtc->dev->vblank[drm_crtc_index(crtc)];
|
||||
int ret, retry = 20;
|
||||
int retry = 20;
|
||||
bool ret = false;
|
||||
|
||||
do {
|
||||
ret = nvif_mthd(&disp->disp, 0, &args, sizeof(args));
|
||||
if (ret != 0)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
if (args.scan.vline) {
|
||||
ret |= DRM_SCANOUTPOS_ACCURATE;
|
||||
ret |= DRM_SCANOUTPOS_VALID;
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -133,14 +133,12 @@ nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos,
|
||||
if (stime) *stime = ns_to_ktime(args.scan.time[0]);
|
||||
if (etime) *etime = ns_to_ktime(args.scan.time[1]);
|
||||
|
||||
if (*vpos < 0)
|
||||
ret |= DRM_SCANOUTPOS_IN_VBLANK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
bool
|
||||
nouveau_display_scanoutpos(struct drm_device *dev, unsigned int pipe,
|
||||
unsigned int flags, int *vpos, int *hpos,
|
||||
bool in_vblank_irq, int *vpos, int *hpos,
|
||||
ktime_t *stime, ktime_t *etime,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
@ -153,28 +151,7 @@ nouveau_display_scanoutpos(struct drm_device *dev, unsigned int pipe,
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_display_vblstamp(struct drm_device *dev, unsigned int pipe,
|
||||
int *max_error, struct timeval *time, unsigned flags)
|
||||
{
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
if (nouveau_crtc(crtc)->index == pipe) {
|
||||
struct drm_display_mode *mode;
|
||||
if (drm_drv_uses_atomic_modeset(dev))
|
||||
mode = &crtc->state->adjusted_mode;
|
||||
else
|
||||
mode = &crtc->hwmode;
|
||||
return drm_calc_vbltimestamp_from_scanoutpos(dev,
|
||||
pipe, max_error, time, flags, mode);
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -68,11 +68,9 @@ int nouveau_display_suspend(struct drm_device *dev, bool runtime);
|
||||
void nouveau_display_resume(struct drm_device *dev, bool runtime);
|
||||
int nouveau_display_vblank_enable(struct drm_device *, unsigned int);
|
||||
void nouveau_display_vblank_disable(struct drm_device *, unsigned int);
|
||||
int nouveau_display_scanoutpos(struct drm_device *, unsigned int,
|
||||
unsigned int, int *, int *, ktime_t *,
|
||||
ktime_t *, const struct drm_display_mode *);
|
||||
int nouveau_display_vblstamp(struct drm_device *, unsigned int, int *,
|
||||
struct timeval *, unsigned);
|
||||
bool nouveau_display_scanoutpos(struct drm_device *, unsigned int,
|
||||
bool, int *, int *, ktime_t *,
|
||||
ktime_t *, const struct drm_display_mode *);
|
||||
|
||||
int nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
struct drm_pending_vblank_event *event,
|
||||
|
@ -881,7 +881,7 @@ done:
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_drm_preclose(struct drm_device *dev, struct drm_file *fpriv)
|
||||
nouveau_drm_postclose(struct drm_device *dev, struct drm_file *fpriv)
|
||||
{
|
||||
struct nouveau_cli *cli = nouveau_cli(fpriv);
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
@ -897,12 +897,6 @@ nouveau_drm_preclose(struct drm_device *dev, struct drm_file *fpriv)
|
||||
list_del(&cli->head);
|
||||
mutex_unlock(&drm->client.mutex);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_drm_postclose(struct drm_device *dev, struct drm_file *fpriv)
|
||||
{
|
||||
struct nouveau_cli *cli = nouveau_cli(fpriv);
|
||||
nouveau_cli_fini(cli);
|
||||
kfree(cli);
|
||||
pm_runtime_mark_last_busy(dev->dev);
|
||||
@ -974,7 +968,6 @@ driver_stub = {
|
||||
.load = nouveau_drm_load,
|
||||
.unload = nouveau_drm_unload,
|
||||
.open = nouveau_drm_open,
|
||||
.preclose = nouveau_drm_preclose,
|
||||
.postclose = nouveau_drm_postclose,
|
||||
.lastclose = nouveau_vga_lastclose,
|
||||
|
||||
@ -985,7 +978,7 @@ driver_stub = {
|
||||
.enable_vblank = nouveau_display_vblank_enable,
|
||||
.disable_vblank = nouveau_display_vblank_disable,
|
||||
.get_scanout_position = nouveau_display_scanoutpos,
|
||||
.get_vblank_timestamp = nouveau_display_vblstamp,
|
||||
.get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos,
|
||||
|
||||
.ioctls = nouveau_ioctls,
|
||||
.num_ioctls = ARRAY_SIZE(nouveau_ioctls),
|
||||
|
12
drivers/gpu/drm/pl111/Kconfig
Normal file
12
drivers/gpu/drm/pl111/Kconfig
Normal file
@ -0,0 +1,12 @@
|
||||
config DRM_PL111
|
||||
tristate "DRM Support for PL111 CLCD Controller"
|
||||
depends on DRM
|
||||
depends on ARM || ARM64 || COMPILE_TEST
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_KMS_CMA_HELPER
|
||||
select DRM_GEM_CMA_HELPER
|
||||
select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
|
||||
help
|
||||
Choose this option for DRM support for the PL111 CLCD controller.
|
||||
If M is selected the module will be called pl111_drm.
|
||||
|
5
drivers/gpu/drm/pl111/Makefile
Normal file
5
drivers/gpu/drm/pl111/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
pl111_drm-y += pl111_connector.o \
|
||||
pl111_display.o \
|
||||
pl111_drv.o
|
||||
|
||||
obj-$(CONFIG_DRM_PL111) += pl111_drm.o
|
127
drivers/gpu/drm/pl111/pl111_connector.c
Normal file
127
drivers/gpu/drm/pl111/pl111_connector.c
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
|
||||
*
|
||||
* Parts of this file were based on sources as follows:
|
||||
*
|
||||
* Copyright (c) 2006-2008 Intel Corporation
|
||||
* Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
|
||||
* Copyright (C) 2011 Texas Instruments
|
||||
*
|
||||
* This program is free software and is provided to you under the terms of the
|
||||
* GNU General Public License version 2 as published by the Free Software
|
||||
* Foundation, and any use by you of this program is subject to the terms of
|
||||
* such GNU licence.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* pl111_drm_connector.c
|
||||
* Implementation of the connector functions for PL111 DRM
|
||||
*/
|
||||
#include <linux/amba/clcd-regs.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/shmem_fs.h>
|
||||
#include <linux/dma-buf.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_panel.h>
|
||||
|
||||
#include "pl111_drm.h"
|
||||
|
||||
static void pl111_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct pl111_drm_connector *pl111_connector =
|
||||
to_pl111_connector(connector);
|
||||
|
||||
if (pl111_connector->panel)
|
||||
drm_panel_detach(pl111_connector->panel);
|
||||
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
static enum drm_connector_status pl111_connector_detect(struct drm_connector
|
||||
*connector, bool force)
|
||||
{
|
||||
struct pl111_drm_connector *pl111_connector =
|
||||
to_pl111_connector(connector);
|
||||
|
||||
return (pl111_connector->panel ?
|
||||
connector_status_connected :
|
||||
connector_status_disconnected);
|
||||
}
|
||||
|
||||
static int pl111_connector_helper_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct pl111_drm_connector *pl111_connector =
|
||||
to_pl111_connector(connector);
|
||||
|
||||
if (!pl111_connector->panel)
|
||||
return 0;
|
||||
|
||||
return drm_panel_get_modes(pl111_connector->panel);
|
||||
}
|
||||
|
||||
const struct drm_connector_funcs connector_funcs = {
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = pl111_connector_destroy,
|
||||
.detect = pl111_connector_detect,
|
||||
.dpms = drm_atomic_helper_connector_dpms,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
const struct drm_connector_helper_funcs connector_helper_funcs = {
|
||||
.get_modes = pl111_connector_helper_get_modes,
|
||||
};
|
||||
|
||||
/* Walks the OF graph to find the panel node and then asks DRM to look
|
||||
* up the panel.
|
||||
*/
|
||||
static struct drm_panel *pl111_get_panel(struct device *dev)
|
||||
{
|
||||
struct device_node *endpoint, *panel_node;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct drm_panel *panel;
|
||||
|
||||
endpoint = of_graph_get_next_endpoint(np, NULL);
|
||||
if (!endpoint) {
|
||||
dev_err(dev, "no endpoint to fetch panel\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* don't proceed if we have an endpoint but no panel_node tied to it */
|
||||
panel_node = of_graph_get_remote_port_parent(endpoint);
|
||||
of_node_put(endpoint);
|
||||
if (!panel_node) {
|
||||
dev_err(dev, "no valid panel node\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
panel = of_drm_find_panel(panel_node);
|
||||
of_node_put(panel_node);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
int pl111_connector_init(struct drm_device *dev)
|
||||
{
|
||||
struct pl111_drm_dev_private *priv = dev->dev_private;
|
||||
struct pl111_drm_connector *pl111_connector = &priv->connector;
|
||||
struct drm_connector *connector = &pl111_connector->connector;
|
||||
|
||||
drm_connector_init(dev, connector, &connector_funcs,
|
||||
DRM_MODE_CONNECTOR_DPI);
|
||||
drm_connector_helper_add(connector, &connector_helper_funcs);
|
||||
|
||||
pl111_connector->panel = pl111_get_panel(dev->dev);
|
||||
if (pl111_connector->panel)
|
||||
drm_panel_attach(pl111_connector->panel, connector);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
344
drivers/gpu/drm/pl111/pl111_display.c
Normal file
344
drivers/gpu/drm/pl111/pl111_display.c
Normal file
@ -0,0 +1,344 @@
|
||||
/*
|
||||
* (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
|
||||
*
|
||||
* Parts of this file were based on sources as follows:
|
||||
*
|
||||
* Copyright (c) 2006-2008 Intel Corporation
|
||||
* Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
|
||||
* Copyright (C) 2011 Texas Instruments
|
||||
*
|
||||
* This program is free software and is provided to you under the terms of the
|
||||
* GNU General Public License version 2 as published by the Free Software
|
||||
* Foundation, and any use by you of this program is subject to the terms of
|
||||
* such GNU licence.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/amba/clcd-regs.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/of_graph.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
|
||||
#include "pl111_drm.h"
|
||||
|
||||
irqreturn_t pl111_irq(int irq, void *data)
|
||||
{
|
||||
struct pl111_drm_dev_private *priv = data;
|
||||
u32 irq_stat;
|
||||
irqreturn_t status = IRQ_NONE;
|
||||
|
||||
irq_stat = readl(priv->regs + CLCD_PL111_MIS);
|
||||
|
||||
if (!irq_stat)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (irq_stat & CLCD_IRQ_NEXTBASE_UPDATE) {
|
||||
drm_crtc_handle_vblank(&priv->pipe.crtc);
|
||||
|
||||
status = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Clear the interrupt once done */
|
||||
writel(irq_stat, priv->regs + CLCD_PL111_ICR);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static u32 pl111_get_fb_offset(struct drm_plane_state *pstate)
|
||||
{
|
||||
struct drm_framebuffer *fb = pstate->fb;
|
||||
struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
|
||||
return (obj->paddr +
|
||||
fb->offsets[0] +
|
||||
fb->format->cpp[0] * pstate->src_x +
|
||||
fb->pitches[0] * pstate->src_y);
|
||||
}
|
||||
|
||||
static int pl111_display_check(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_plane_state *pstate,
|
||||
struct drm_crtc_state *cstate)
|
||||
{
|
||||
const struct drm_display_mode *mode = &cstate->mode;
|
||||
struct drm_framebuffer *old_fb = pipe->plane.state->fb;
|
||||
struct drm_framebuffer *fb = pstate->fb;
|
||||
|
||||
if (mode->hdisplay % 16)
|
||||
return -EINVAL;
|
||||
|
||||
if (fb) {
|
||||
u32 offset = pl111_get_fb_offset(pstate);
|
||||
|
||||
/* FB base address must be dword aligned. */
|
||||
if (offset & 3)
|
||||
return -EINVAL;
|
||||
|
||||
/* There's no pitch register -- the mode's hdisplay
|
||||
* controls it.
|
||||
*/
|
||||
if (fb->pitches[0] != mode->hdisplay * fb->format->cpp[0])
|
||||
return -EINVAL;
|
||||
|
||||
/* We can't change the FB format in a flicker-free
|
||||
* manner (and only update it during CRTC enable).
|
||||
*/
|
||||
if (old_fb && old_fb->format != fb->format)
|
||||
cstate->mode_changed = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_crtc_state *cstate)
|
||||
{
|
||||
struct drm_crtc *crtc = &pipe->crtc;
|
||||
struct drm_plane *plane = &pipe->plane;
|
||||
struct drm_device *drm = crtc->dev;
|
||||
struct pl111_drm_dev_private *priv = drm->dev_private;
|
||||
const struct drm_display_mode *mode = &cstate->mode;
|
||||
struct drm_framebuffer *fb = plane->state->fb;
|
||||
struct drm_connector *connector = &priv->connector.connector;
|
||||
u32 cntl;
|
||||
u32 ppl, hsw, hfp, hbp;
|
||||
u32 lpp, vsw, vfp, vbp;
|
||||
u32 cpl;
|
||||
int ret;
|
||||
|
||||
ret = clk_set_rate(priv->clk, mode->clock * 1000);
|
||||
if (ret) {
|
||||
dev_err(drm->dev,
|
||||
"Failed to set pixel clock rate to %d: %d\n",
|
||||
mode->clock * 1000, ret);
|
||||
}
|
||||
|
||||
clk_prepare_enable(priv->clk);
|
||||
|
||||
ppl = (mode->hdisplay / 16) - 1;
|
||||
hsw = mode->hsync_end - mode->hsync_start - 1;
|
||||
hfp = mode->hsync_start - mode->hdisplay - 1;
|
||||
hbp = mode->htotal - mode->hsync_end - 1;
|
||||
|
||||
lpp = mode->vdisplay - 1;
|
||||
vsw = mode->vsync_end - mode->vsync_start - 1;
|
||||
vfp = mode->vsync_start - mode->vdisplay;
|
||||
vbp = mode->vtotal - mode->vsync_end;
|
||||
|
||||
cpl = mode->hdisplay - 1;
|
||||
|
||||
writel((ppl << 2) |
|
||||
(hsw << 8) |
|
||||
(hfp << 16) |
|
||||
(hbp << 24),
|
||||
priv->regs + CLCD_TIM0);
|
||||
writel(lpp |
|
||||
(vsw << 10) |
|
||||
(vfp << 16) |
|
||||
(vbp << 24),
|
||||
priv->regs + CLCD_TIM1);
|
||||
/* XXX: We currently always use CLCDCLK with no divisor. We
|
||||
* could probably reduce power consumption by using HCLK
|
||||
* (apb_pclk) with a divisor when it gets us near our target
|
||||
* pixel clock.
|
||||
*/
|
||||
writel(((mode->flags & DRM_MODE_FLAG_NHSYNC) ? TIM2_IHS : 0) |
|
||||
((mode->flags & DRM_MODE_FLAG_NVSYNC) ? TIM2_IVS : 0) |
|
||||
((connector->display_info.bus_flags &
|
||||
DRM_BUS_FLAG_DE_LOW) ? TIM2_IOE : 0) |
|
||||
((connector->display_info.bus_flags &
|
||||
DRM_BUS_FLAG_PIXDATA_NEGEDGE) ? TIM2_IPC : 0) |
|
||||
TIM2_BCD |
|
||||
(cpl << 16),
|
||||
priv->regs + CLCD_TIM2);
|
||||
writel(0, priv->regs + CLCD_TIM3);
|
||||
|
||||
drm_panel_prepare(priv->connector.panel);
|
||||
|
||||
/* Enable and Power Up */
|
||||
cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDPWR | CNTL_LCDVCOMP(1);
|
||||
|
||||
/* Note that the the hardware's format reader takes 'r' from
|
||||
* the low bit, while DRM formats list channels from high bit
|
||||
* to low bit as you read left to right.
|
||||
*/
|
||||
switch (fb->format->format) {
|
||||
case DRM_FORMAT_ABGR8888:
|
||||
case DRM_FORMAT_XBGR8888:
|
||||
cntl |= CNTL_LCDBPP24;
|
||||
break;
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
cntl |= CNTL_LCDBPP24 | CNTL_BGR;
|
||||
break;
|
||||
case DRM_FORMAT_BGR565:
|
||||
cntl |= CNTL_LCDBPP16_565;
|
||||
break;
|
||||
case DRM_FORMAT_RGB565:
|
||||
cntl |= CNTL_LCDBPP16_565 | CNTL_BGR;
|
||||
break;
|
||||
case DRM_FORMAT_ABGR1555:
|
||||
case DRM_FORMAT_XBGR1555:
|
||||
cntl |= CNTL_LCDBPP16;
|
||||
break;
|
||||
case DRM_FORMAT_ARGB1555:
|
||||
case DRM_FORMAT_XRGB1555:
|
||||
cntl |= CNTL_LCDBPP16 | CNTL_BGR;
|
||||
break;
|
||||
case DRM_FORMAT_ABGR4444:
|
||||
case DRM_FORMAT_XBGR4444:
|
||||
cntl |= CNTL_LCDBPP16_444;
|
||||
break;
|
||||
case DRM_FORMAT_ARGB4444:
|
||||
case DRM_FORMAT_XRGB4444:
|
||||
cntl |= CNTL_LCDBPP16_444 | CNTL_BGR;
|
||||
break;
|
||||
default:
|
||||
WARN_ONCE(true, "Unknown FB format 0x%08x\n",
|
||||
fb->format->format);
|
||||
break;
|
||||
}
|
||||
|
||||
writel(cntl, priv->regs + CLCD_PL111_CNTL);
|
||||
|
||||
drm_panel_enable(priv->connector.panel);
|
||||
|
||||
drm_crtc_vblank_on(crtc);
|
||||
}
|
||||
|
||||
void pl111_display_disable(struct drm_simple_display_pipe *pipe)
|
||||
{
|
||||
struct drm_crtc *crtc = &pipe->crtc;
|
||||
struct drm_device *drm = crtc->dev;
|
||||
struct pl111_drm_dev_private *priv = drm->dev_private;
|
||||
|
||||
drm_crtc_vblank_off(crtc);
|
||||
|
||||
drm_panel_disable(priv->connector.panel);
|
||||
|
||||
/* Disable and Power Down */
|
||||
writel(0, priv->regs + CLCD_PL111_CNTL);
|
||||
|
||||
drm_panel_unprepare(priv->connector.panel);
|
||||
|
||||
clk_disable_unprepare(priv->clk);
|
||||
}
|
||||
|
||||
static void pl111_display_update(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_plane_state *old_pstate)
|
||||
{
|
||||
struct drm_crtc *crtc = &pipe->crtc;
|
||||
struct drm_device *drm = crtc->dev;
|
||||
struct pl111_drm_dev_private *priv = drm->dev_private;
|
||||
struct drm_pending_vblank_event *event = crtc->state->event;
|
||||
struct drm_plane *plane = &pipe->plane;
|
||||
struct drm_plane_state *pstate = plane->state;
|
||||
struct drm_framebuffer *fb = pstate->fb;
|
||||
|
||||
if (fb) {
|
||||
u32 addr = pl111_get_fb_offset(pstate);
|
||||
|
||||
writel(addr, priv->regs + CLCD_UBAS);
|
||||
}
|
||||
|
||||
if (event) {
|
||||
crtc->state->event = NULL;
|
||||
|
||||
spin_lock_irq(&crtc->dev->event_lock);
|
||||
if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0)
|
||||
drm_crtc_arm_vblank_event(crtc, event);
|
||||
else
|
||||
drm_crtc_send_vblank_event(crtc, event);
|
||||
spin_unlock_irq(&crtc->dev->event_lock);
|
||||
}
|
||||
}
|
||||
|
||||
int pl111_enable_vblank(struct drm_device *drm, unsigned int crtc)
|
||||
{
|
||||
struct pl111_drm_dev_private *priv = drm->dev_private;
|
||||
|
||||
writel(CLCD_IRQ_NEXTBASE_UPDATE, priv->regs + CLCD_PL111_IENB);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pl111_disable_vblank(struct drm_device *drm, unsigned int crtc)
|
||||
{
|
||||
struct pl111_drm_dev_private *priv = drm->dev_private;
|
||||
|
||||
writel(0, priv->regs + CLCD_PL111_IENB);
|
||||
}
|
||||
|
||||
static int pl111_display_prepare_fb(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_plane_state *plane_state)
|
||||
{
|
||||
return drm_fb_cma_prepare_fb(&pipe->plane, plane_state);
|
||||
}
|
||||
|
||||
const struct drm_simple_display_pipe_funcs pl111_display_funcs = {
|
||||
.check = pl111_display_check,
|
||||
.enable = pl111_display_enable,
|
||||
.disable = pl111_display_disable,
|
||||
.update = pl111_display_update,
|
||||
.prepare_fb = pl111_display_prepare_fb,
|
||||
};
|
||||
|
||||
int pl111_display_init(struct drm_device *drm)
|
||||
{
|
||||
struct pl111_drm_dev_private *priv = drm->dev_private;
|
||||
struct device *dev = drm->dev;
|
||||
struct device_node *endpoint;
|
||||
u32 tft_r0b0g0[3];
|
||||
int ret;
|
||||
static const u32 formats[] = {
|
||||
DRM_FORMAT_ABGR8888,
|
||||
DRM_FORMAT_XBGR8888,
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_BGR565,
|
||||
DRM_FORMAT_RGB565,
|
||||
DRM_FORMAT_ABGR1555,
|
||||
DRM_FORMAT_XBGR1555,
|
||||
DRM_FORMAT_ARGB1555,
|
||||
DRM_FORMAT_XRGB1555,
|
||||
DRM_FORMAT_ABGR4444,
|
||||
DRM_FORMAT_XBGR4444,
|
||||
DRM_FORMAT_ARGB4444,
|
||||
DRM_FORMAT_XRGB4444,
|
||||
};
|
||||
|
||||
endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
|
||||
if (!endpoint)
|
||||
return -ENODEV;
|
||||
|
||||
if (of_property_read_u32_array(endpoint,
|
||||
"arm,pl11x,tft-r0g0b0-pads",
|
||||
tft_r0b0g0,
|
||||
ARRAY_SIZE(tft_r0b0g0)) != 0) {
|
||||
dev_err(dev, "arm,pl11x,tft-r0g0b0-pads should be 3 ints\n");
|
||||
of_node_put(endpoint);
|
||||
return -ENOENT;
|
||||
}
|
||||
of_node_put(endpoint);
|
||||
|
||||
if (tft_r0b0g0[0] != 0 ||
|
||||
tft_r0b0g0[1] != 8 ||
|
||||
tft_r0b0g0[2] != 16) {
|
||||
dev_err(dev, "arm,pl11x,tft-r0g0b0-pads != [0,8,16] not yet supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = drm_simple_display_pipe_init(drm, &priv->pipe,
|
||||
&pl111_display_funcs,
|
||||
formats, ARRAY_SIZE(formats),
|
||||
&priv->connector.connector);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
56
drivers/gpu/drm/pl111/pl111_drm.h
Normal file
56
drivers/gpu/drm/pl111/pl111_drm.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
|
||||
*
|
||||
*
|
||||
* Parts of this file were based on sources as follows:
|
||||
*
|
||||
* Copyright (c) 2006-2008 Intel Corporation
|
||||
* Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
|
||||
* Copyright (C) 2011 Texas Instruments
|
||||
*
|
||||
* This program is free software and is provided to you under the terms of the
|
||||
* GNU General Public License version 2 as published by the Free Software
|
||||
* Foundation, and any use by you of this program is subject to the terms of
|
||||
* such GNU licence.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _PL111_DRM_H_
|
||||
#define _PL111_DRM_H_
|
||||
|
||||
#include <drm/drm_gem.h>
|
||||
#include <drm/drm_simple_kms_helper.h>
|
||||
|
||||
#define CLCD_IRQ_NEXTBASE_UPDATE BIT(2)
|
||||
|
||||
struct pl111_drm_connector {
|
||||
struct drm_connector connector;
|
||||
struct drm_panel *panel;
|
||||
};
|
||||
|
||||
struct pl111_drm_dev_private {
|
||||
struct drm_device *drm;
|
||||
|
||||
struct pl111_drm_connector connector;
|
||||
struct drm_simple_display_pipe pipe;
|
||||
struct drm_fbdev_cma *fbdev;
|
||||
|
||||
void *regs;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
#define to_pl111_connector(x) \
|
||||
container_of(x, struct pl111_drm_connector, connector)
|
||||
|
||||
int pl111_display_init(struct drm_device *dev);
|
||||
int pl111_enable_vblank(struct drm_device *drm, unsigned int crtc);
|
||||
void pl111_disable_vblank(struct drm_device *drm, unsigned int crtc);
|
||||
irqreturn_t pl111_irq(int irq, void *data);
|
||||
int pl111_connector_init(struct drm_device *dev);
|
||||
int pl111_encoder_init(struct drm_device *dev);
|
||||
int pl111_dumb_create(struct drm_file *file_priv,
|
||||
struct drm_device *dev,
|
||||
struct drm_mode_create_dumb *args);
|
||||
|
||||
#endif /* _PL111_DRM_H_ */
|
272
drivers/gpu/drm/pl111/pl111_drv.c
Normal file
272
drivers/gpu/drm/pl111/pl111_drv.c
Normal file
@ -0,0 +1,272 @@
|
||||
/*
|
||||
* (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
|
||||
*
|
||||
* Parts of this file were based on sources as follows:
|
||||
*
|
||||
* Copyright (c) 2006-2008 Intel Corporation
|
||||
* Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
|
||||
* Copyright (C) 2011 Texas Instruments
|
||||
*
|
||||
* This program is free software and is provided to you under the terms of the
|
||||
* GNU General Public License version 2 as published by the Free Software
|
||||
* Foundation, and any use by you of this program is subject to the terms of
|
||||
* such GNU licence.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* DOC: ARM PrimeCell PL111 CLCD Driver
|
||||
*
|
||||
* The PL111 is a simple LCD controller that can support TFT and STN
|
||||
* displays. This driver exposes a standard KMS interface for them.
|
||||
*
|
||||
* This driver uses the same Device Tree binding as the fbdev CLCD
|
||||
* driver. While the fbdev driver supports panels that may be
|
||||
* connected to the CLCD internally to the CLCD driver, in DRM the
|
||||
* panels get split out to drivers/gpu/drm/panels/. This means that,
|
||||
* in converting from using fbdev to using DRM, you also need to write
|
||||
* a panel driver (which may be as simple as an entry in
|
||||
* panel-simple.c).
|
||||
*
|
||||
* The driver currently doesn't expose the cursor. The DRM API for
|
||||
* cursors requires support for 64x64 ARGB8888 cursor images, while
|
||||
* the hardware can only support 64x64 monochrome with masking
|
||||
* cursors. While one could imagine trying to hack something together
|
||||
* to look at the ARGB8888 and program reasonable in monochrome, we
|
||||
* just don't expose the cursor at all instead, and leave cursor
|
||||
* support to the X11 software cursor layer.
|
||||
*
|
||||
* TODO:
|
||||
*
|
||||
* - Fix race between setting plane base address and getting IRQ for
|
||||
* vsync firing the pageflip completion.
|
||||
*
|
||||
* - Expose the correct set of formats we can support based on the
|
||||
* "arm,pl11x,tft-r0g0b0-pads" DT property.
|
||||
*
|
||||
* - Use the "max-memory-bandwidth" DT property to filter the
|
||||
* supported formats.
|
||||
*
|
||||
* - Read back hardware state at boot to skip reprogramming the
|
||||
* hardware when doing a no-op modeset.
|
||||
*
|
||||
* - Use the internal clock divisor to reduce power consumption by
|
||||
* using HCLK (apb_pclk) when appropriate.
|
||||
*/
|
||||
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/amba/clcd-regs.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/shmem_fs.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
|
||||
#include "pl111_drm.h"
|
||||
|
||||
#define DRIVER_DESC "DRM module for PL111"
|
||||
|
||||
struct drm_mode_config_funcs mode_config_funcs = {
|
||||
.fb_create = drm_fb_cma_create,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
};
|
||||
|
||||
static int pl111_modeset_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_mode_config *mode_config;
|
||||
struct pl111_drm_dev_private *priv = dev->dev_private;
|
||||
int ret = 0;
|
||||
|
||||
drm_mode_config_init(dev);
|
||||
mode_config = &dev->mode_config;
|
||||
mode_config->funcs = &mode_config_funcs;
|
||||
mode_config->min_width = 1;
|
||||
mode_config->max_width = 1024;
|
||||
mode_config->min_height = 1;
|
||||
mode_config->max_height = 768;
|
||||
|
||||
ret = pl111_connector_init(dev);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "Failed to create pl111_drm_connector\n");
|
||||
goto out_config;
|
||||
}
|
||||
|
||||
/* Don't actually attach if we didn't find a drm_panel
|
||||
* attached to us. This will allow a kernel to include both
|
||||
* the fbdev pl111 driver and this one, and choose between
|
||||
* them based on which subsystem has support for the panel.
|
||||
*/
|
||||
if (!priv->connector.panel) {
|
||||
dev_info(dev->dev,
|
||||
"Disabling due to lack of DRM panel device.\n");
|
||||
ret = -ENODEV;
|
||||
goto out_config;
|
||||
}
|
||||
|
||||
ret = pl111_display_init(dev);
|
||||
if (ret != 0) {
|
||||
dev_err(dev->dev, "Failed to init display\n");
|
||||
goto out_config;
|
||||
}
|
||||
|
||||
ret = drm_vblank_init(dev, 1);
|
||||
if (ret != 0) {
|
||||
dev_err(dev->dev, "Failed to init vblank\n");
|
||||
goto out_config;
|
||||
}
|
||||
|
||||
drm_mode_config_reset(dev);
|
||||
|
||||
priv->fbdev = drm_fbdev_cma_init(dev, 32,
|
||||
dev->mode_config.num_connector);
|
||||
|
||||
drm_kms_helper_poll_init(dev);
|
||||
|
||||
goto finish;
|
||||
|
||||
out_config:
|
||||
drm_mode_config_cleanup(dev);
|
||||
finish:
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEFINE_DRM_GEM_CMA_FOPS(drm_fops);
|
||||
|
||||
static void pl111_lastclose(struct drm_device *dev)
|
||||
{
|
||||
struct pl111_drm_dev_private *priv = dev->dev_private;
|
||||
|
||||
drm_fbdev_cma_restore_mode(priv->fbdev);
|
||||
}
|
||||
|
||||
static struct drm_driver pl111_drm_driver = {
|
||||
.driver_features =
|
||||
DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC,
|
||||
.lastclose = pl111_lastclose,
|
||||
.ioctls = NULL,
|
||||
.fops = &drm_fops,
|
||||
.name = "pl111",
|
||||
.desc = DRIVER_DESC,
|
||||
.date = "20170317",
|
||||
.major = 1,
|
||||
.minor = 0,
|
||||
.patchlevel = 0,
|
||||
.dumb_create = drm_gem_cma_dumb_create,
|
||||
.dumb_destroy = drm_gem_dumb_destroy,
|
||||
.dumb_map_offset = drm_gem_cma_dumb_map_offset,
|
||||
.gem_free_object = 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,
|
||||
.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
|
||||
.gem_prime_export = drm_gem_prime_export,
|
||||
.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ARM_AMBA
|
||||
static int pl111_amba_probe(struct amba_device *amba_dev,
|
||||
const struct amba_id *id)
|
||||
{
|
||||
struct device *dev = &amba_dev->dev;
|
||||
struct pl111_drm_dev_private *priv;
|
||||
struct drm_device *drm;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
drm = drm_dev_alloc(&pl111_drm_driver, dev);
|
||||
if (IS_ERR(drm))
|
||||
return PTR_ERR(drm);
|
||||
amba_set_drvdata(amba_dev, drm);
|
||||
priv->drm = drm;
|
||||
drm->dev_private = priv;
|
||||
|
||||
priv->clk = devm_clk_get(dev, "clcdclk");
|
||||
if (IS_ERR(priv->clk)) {
|
||||
dev_err(dev, "CLCD: unable to get clk.\n");
|
||||
ret = PTR_ERR(priv->clk);
|
||||
goto dev_unref;
|
||||
}
|
||||
|
||||
priv->regs = devm_ioremap_resource(dev, &amba_dev->res);
|
||||
if (!priv->regs) {
|
||||
dev_err(dev, "%s failed mmio\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* turn off interrupts before requesting the irq */
|
||||
writel(0, priv->regs + CLCD_PL111_IENB);
|
||||
|
||||
ret = devm_request_irq(dev, amba_dev->irq[0], pl111_irq, 0,
|
||||
"pl111", priv);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "%s failed irq %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = pl111_modeset_init(drm);
|
||||
if (ret != 0)
|
||||
goto dev_unref;
|
||||
|
||||
ret = drm_dev_register(drm, 0);
|
||||
if (ret < 0)
|
||||
goto dev_unref;
|
||||
|
||||
return 0;
|
||||
|
||||
dev_unref:
|
||||
drm_dev_unref(drm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pl111_amba_remove(struct amba_device *amba_dev)
|
||||
{
|
||||
struct drm_device *drm = amba_get_drvdata(amba_dev);
|
||||
struct pl111_drm_dev_private *priv = drm->dev_private;
|
||||
|
||||
drm_dev_unregister(drm);
|
||||
if (priv->fbdev)
|
||||
drm_fbdev_cma_fini(priv->fbdev);
|
||||
drm_mode_config_cleanup(drm);
|
||||
drm_dev_unref(drm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct amba_id pl111_id_table[] = {
|
||||
{
|
||||
.id = 0x00041111,
|
||||
.mask = 0x000fffff,
|
||||
},
|
||||
{0, 0},
|
||||
};
|
||||
|
||||
static struct amba_driver pl111_amba_driver = {
|
||||
.drv = {
|
||||
.name = "drm-clcd-pl111",
|
||||
},
|
||||
.probe = pl111_amba_probe,
|
||||
.remove = pl111_amba_remove,
|
||||
.id_table = pl111_id_table,
|
||||
};
|
||||
|
||||
module_amba_driver(pl111_amba_driver);
|
||||
#endif /* CONFIG_ARM_AMBA */
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_AUTHOR("ARM Ltd.");
|
||||
MODULE_LICENSE("GPL");
|
@ -115,10 +115,6 @@ int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon);
|
||||
u32 radeon_get_vblank_counter_kms(struct drm_device *dev, unsigned int pipe);
|
||||
int radeon_enable_vblank_kms(struct drm_device *dev, unsigned int pipe);
|
||||
void radeon_disable_vblank_kms(struct drm_device *dev, unsigned int pipe);
|
||||
int radeon_get_vblank_timestamp_kms(struct drm_device *dev, unsigned int pipe,
|
||||
int *max_error,
|
||||
struct timeval *vblank_time,
|
||||
unsigned flags);
|
||||
void radeon_driver_irq_preinstall_kms(struct drm_device *dev);
|
||||
int radeon_driver_irq_postinstall_kms(struct drm_device *dev);
|
||||
void radeon_driver_irq_uninstall_kms(struct drm_device *dev);
|
||||
@ -530,6 +526,16 @@ static const struct file_operations radeon_driver_kms_fops = {
|
||||
#endif
|
||||
};
|
||||
|
||||
static bool
|
||||
radeon_get_crtc_scanout_position(struct drm_device *dev, unsigned int pipe,
|
||||
bool in_vblank_irq, int *vpos, int *hpos,
|
||||
ktime_t *stime, ktime_t *etime,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
return radeon_get_crtc_scanoutpos(dev, pipe, 0, vpos, hpos,
|
||||
stime, etime, mode);
|
||||
}
|
||||
|
||||
static struct drm_driver kms_driver = {
|
||||
.driver_features =
|
||||
DRIVER_USE_AGP |
|
||||
@ -544,8 +550,8 @@ static struct drm_driver kms_driver = {
|
||||
.get_vblank_counter = radeon_get_vblank_counter_kms,
|
||||
.enable_vblank = radeon_enable_vblank_kms,
|
||||
.disable_vblank = radeon_disable_vblank_kms,
|
||||
.get_vblank_timestamp = radeon_get_vblank_timestamp_kms,
|
||||
.get_scanout_position = radeon_get_crtc_scanoutpos,
|
||||
.get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos,
|
||||
.get_scanout_position = radeon_get_crtc_scanout_position,
|
||||
.irq_preinstall = radeon_driver_irq_preinstall_kms,
|
||||
.irq_postinstall = radeon_driver_irq_postinstall_kms,
|
||||
.irq_uninstall = radeon_driver_irq_uninstall_kms,
|
||||
|
@ -858,43 +858,6 @@ void radeon_disable_vblank_kms(struct drm_device *dev, int crtc)
|
||||
spin_unlock_irqrestore(&rdev->irq.lock, irqflags);
|
||||
}
|
||||
|
||||
/**
|
||||
* radeon_get_vblank_timestamp_kms - get vblank timestamp
|
||||
*
|
||||
* @dev: drm dev pointer
|
||||
* @crtc: crtc to get the timestamp for
|
||||
* @max_error: max error
|
||||
* @vblank_time: time value
|
||||
* @flags: flags passed to the driver
|
||||
*
|
||||
* Gets the timestamp on the requested crtc based on the
|
||||
* scanout position. (all asics).
|
||||
* Returns postive status flags on success, negative error on failure.
|
||||
*/
|
||||
int radeon_get_vblank_timestamp_kms(struct drm_device *dev, int crtc,
|
||||
int *max_error,
|
||||
struct timeval *vblank_time,
|
||||
unsigned flags)
|
||||
{
|
||||
struct drm_crtc *drmcrtc;
|
||||
struct radeon_device *rdev = dev->dev_private;
|
||||
|
||||
if (crtc < 0 || crtc >= dev->num_crtcs) {
|
||||
DRM_ERROR("Invalid crtc %d\n", crtc);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Get associated drm_crtc: */
|
||||
drmcrtc = &rdev->mode_info.crtcs[crtc]->base;
|
||||
if (!drmcrtc)
|
||||
return -EINVAL;
|
||||
|
||||
/* Helper routine in DRM core does all the work: */
|
||||
return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error,
|
||||
vblank_time, flags,
|
||||
&drmcrtc->hwmode);
|
||||
}
|
||||
|
||||
const struct drm_ioctl_desc radeon_ioctls_kms[] = {
|
||||
DRM_IOCTL_DEF_DRV(RADEON_CP_INIT, drm_invalid_op, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
|
||||
DRM_IOCTL_DEF_DRV(RADEON_CP_START, drm_invalid_op, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
|
||||
|
@ -691,6 +691,9 @@ struct atom_voltage_table
|
||||
};
|
||||
|
||||
/* Driver internal use only flags of radeon_get_crtc_scanoutpos() */
|
||||
#define DRM_SCANOUTPOS_VALID (1 << 0)
|
||||
#define DRM_SCANOUTPOS_IN_VBLANK (1 << 1)
|
||||
#define DRM_SCANOUTPOS_ACCURATE (1 << 2)
|
||||
#define USE_REAL_VBLANKSTART (1 << 30)
|
||||
#define GET_DISTANCE_TO_VBLANKSTART (1 << 31)
|
||||
|
||||
|
@ -104,26 +104,18 @@ static void analogix_dp_psr_work(struct work_struct *work)
|
||||
{
|
||||
struct rockchip_dp_device *dp =
|
||||
container_of(work, typeof(*dp), psr_work);
|
||||
struct drm_crtc *crtc = dp->encoder.crtc;
|
||||
int psr_state = dp->psr_state;
|
||||
int vact_end;
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
if (!crtc)
|
||||
return;
|
||||
|
||||
vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay;
|
||||
|
||||
ret = rockchip_drm_wait_line_flag(dp->encoder.crtc, vact_end,
|
||||
PSR_WAIT_LINE_FLAG_TIMEOUT_MS);
|
||||
ret = rockchip_drm_wait_vact_end(dp->encoder.crtc,
|
||||
PSR_WAIT_LINE_FLAG_TIMEOUT_MS);
|
||||
if (ret) {
|
||||
dev_err(dp->dev, "line flag interrupt did not arrive\n");
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dp->psr_lock, flags);
|
||||
if (psr_state == EDP_VSC_PSR_STATE_ACTIVE)
|
||||
if (dp->psr_state == EDP_VSC_PSR_STATE_ACTIVE)
|
||||
analogix_dp_enable_psr(dp->dev);
|
||||
else
|
||||
analogix_dp_disable_psr(dp->dev);
|
||||
|
@ -62,8 +62,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
|
||||
struct device *dev);
|
||||
void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
|
||||
struct device *dev);
|
||||
int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
|
||||
unsigned int mstimeout);
|
||||
int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout);
|
||||
|
||||
extern struct platform_driver cdn_dp_driver;
|
||||
extern struct platform_driver dw_hdmi_rockchip_pltfm_driver;
|
||||
|
@ -468,7 +468,7 @@ static bool vop_line_flag_irq_is_enabled(struct vop *vop)
|
||||
return !!line_flag_irq;
|
||||
}
|
||||
|
||||
static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
|
||||
static void vop_line_flag_irq_enable(struct vop *vop)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
@ -477,7 +477,6 @@ static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
|
||||
|
||||
spin_lock_irqsave(&vop->irq_lock, flags);
|
||||
|
||||
VOP_CTRL_SET(vop, line_flag_num[0], line_num);
|
||||
VOP_INTR_SET_TYPE(vop, clear, LINE_FLAG_INTR, 1);
|
||||
VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
|
||||
|
||||
@ -981,6 +980,8 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
|
||||
VOP_CTRL_SET(vop, vact_st_end, val);
|
||||
VOP_CTRL_SET(vop, vpost_st_end, val);
|
||||
|
||||
VOP_CTRL_SET(vop, line_flag_num[0], vact_end);
|
||||
|
||||
clk_set_rate(vop->dclk, adjusted_mode->clock * 1000);
|
||||
|
||||
VOP_CTRL_SET(vop, standby, 0);
|
||||
@ -1507,19 +1508,16 @@ static void vop_win_init(struct vop *vop)
|
||||
}
|
||||
|
||||
/**
|
||||
* rockchip_drm_wait_line_flag - acqiure the give line flag event
|
||||
* rockchip_drm_wait_vact_end
|
||||
* @crtc: CRTC to enable line flag
|
||||
* @line_num: interested line number
|
||||
* @mstimeout: millisecond for timeout
|
||||
*
|
||||
* Driver would hold here until the interested line flag interrupt have
|
||||
* happened or timeout to wait.
|
||||
* Wait for vact_end line flag irq or timeout.
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, negative errno on failure.
|
||||
*/
|
||||
int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
|
||||
unsigned int mstimeout)
|
||||
int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout)
|
||||
{
|
||||
struct vop *vop = to_vop(crtc);
|
||||
unsigned long jiffies_left;
|
||||
@ -1527,14 +1525,14 @@ int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
|
||||
if (!crtc || !vop->is_enabled)
|
||||
return -ENODEV;
|
||||
|
||||
if (line_num > crtc->mode.vtotal || mstimeout <= 0)
|
||||
if (mstimeout <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (vop_line_flag_irq_is_enabled(vop))
|
||||
return -EBUSY;
|
||||
|
||||
reinit_completion(&vop->line_flag_completion);
|
||||
vop_line_flag_irq_enable(vop, line_num);
|
||||
vop_line_flag_irq_enable(vop);
|
||||
|
||||
jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
|
||||
msecs_to_jiffies(mstimeout));
|
||||
@ -1547,7 +1545,7 @@ int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
|
||||
EXPORT_SYMBOL(rockchip_drm_wait_vact_end);
|
||||
|
||||
static int vop_bind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
|
@ -514,6 +514,8 @@ static int igt_reserve(void *ignored)
|
||||
ret = __igt_reserve(count, size + 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -712,6 +714,10 @@ static int igt_insert(void *ignored)
|
||||
return ret;
|
||||
|
||||
ret = __igt_insert(count, size + 1, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -741,6 +747,10 @@ static int igt_replace(void *ignored)
|
||||
return ret;
|
||||
|
||||
ret = __igt_insert(count, size + 1, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1011,6 +1021,8 @@ static int igt_insert_range(void *ignored)
|
||||
ret = __igt_insert_range(count, size, max/4+1, 3*max/4-1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1056,6 +1068,7 @@ static int igt_align(void *ignored)
|
||||
drm_mm_for_each_node_safe(node, next, &mm)
|
||||
drm_mm_remove_node(node);
|
||||
DRM_MM_BUG_ON(!drm_mm_clean(&mm));
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
@ -1097,6 +1110,8 @@ static int igt_align_pot(int max)
|
||||
align, bit);
|
||||
goto out;
|
||||
}
|
||||
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
@ -1471,6 +1486,8 @@ static int igt_evict(void *ignored)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
@ -1566,6 +1583,8 @@ static int igt_evict_range(void *ignored)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
@ -1683,6 +1702,7 @@ static int igt_topdown(void *ignored)
|
||||
drm_mm_for_each_node_safe(node, next, &mm)
|
||||
drm_mm_remove_node(node);
|
||||
DRM_MM_BUG_ON(!drm_mm_clean(&mm));
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
@ -1783,6 +1803,7 @@ static int igt_bottomup(void *ignored)
|
||||
drm_mm_for_each_node_safe(node, next, &mm)
|
||||
drm_mm_remove_node(node);
|
||||
DRM_MM_BUG_ON(!drm_mm_clean(&mm));
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
@ -1970,6 +1991,8 @@ static int igt_color(void *ignored)
|
||||
drm_mm_remove_node(node);
|
||||
kfree(node);
|
||||
}
|
||||
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
@ -2047,6 +2070,7 @@ static int evict_color(struct drm_mm *mm,
|
||||
}
|
||||
}
|
||||
|
||||
cond_resched();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2132,6 +2156,8 @@ static int igt_color_evict(void *ignored)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
@ -2231,6 +2257,8 @@ static int igt_color_evict_range(void *ignored)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
@ -33,7 +33,7 @@
|
||||
#define STI_CURS_MAX_SIZE 128
|
||||
|
||||
/*
|
||||
* pixmap dma buffer stucture
|
||||
* pixmap dma buffer structure
|
||||
*
|
||||
* @paddr: physical address
|
||||
* @size: buffer size
|
||||
@ -121,8 +121,7 @@ static int cursor_dbg_show(struct seq_file *s, void *data)
|
||||
cursor_dbg_cml(s, cursor, readl(cursor->regs + CUR_CML));
|
||||
DBGFS_DUMP(CUR_AWS);
|
||||
DBGFS_DUMP(CUR_AWE);
|
||||
seq_puts(s, "\n");
|
||||
|
||||
seq_putc(s, '\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -186,8 +186,7 @@ static int dvo_dbg_show(struct seq_file *s, void *data)
|
||||
DBGFS_DUMP(DVO_LUT_PROG_MID);
|
||||
DBGFS_DUMP(DVO_LUT_PROG_HIGH);
|
||||
dvo_dbg_awg_microcode(s, dvo->regs + DVO_DIGSYNC_INSTR_I);
|
||||
seq_puts(s, "\n");
|
||||
|
||||
seq_putc(s, '\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,7 @@ static void gdp_dbg_ctl(struct seq_file *s, int val)
|
||||
seq_puts(s, "\tColor:");
|
||||
for (i = 0; i < ARRAY_SIZE(gdp_format_to_str); i++) {
|
||||
if (gdp_format_to_str[i].format == (val & 0x1F)) {
|
||||
seq_printf(s, gdp_format_to_str[i].name);
|
||||
seq_puts(s, gdp_format_to_str[i].name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -266,8 +266,7 @@ static void gdp_node_dump_node(struct seq_file *s, struct sti_gdp_node *node)
|
||||
seq_printf(s, "\n\tKEY2 0x%08X", node->gam_gdp_key2);
|
||||
seq_printf(s, "\n\tPPT 0x%08X", node->gam_gdp_ppt);
|
||||
gdp_dbg_ppt(s, node->gam_gdp_ppt);
|
||||
seq_printf(s, "\n\tCML 0x%08X", node->gam_gdp_cml);
|
||||
seq_puts(s, "\n");
|
||||
seq_printf(s, "\n\tCML 0x%08X\n", node->gam_gdp_cml);
|
||||
}
|
||||
|
||||
static int gdp_node_dbg_show(struct seq_file *s, void *arg)
|
||||
|
@ -320,8 +320,7 @@ static void hda_dbg_awg_microcode(struct seq_file *s, void __iomem *reg)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
seq_puts(s, "\n\n");
|
||||
seq_puts(s, " HDA AWG microcode:");
|
||||
seq_puts(s, "\n\n HDA AWG microcode:");
|
||||
for (i = 0; i < AWG_MAX_INST; i++) {
|
||||
if (i % 8 == 0)
|
||||
seq_printf(s, "\n %04X:", i);
|
||||
@ -333,8 +332,7 @@ static void hda_dbg_video_dacs_ctrl(struct seq_file *s, void __iomem *reg)
|
||||
{
|
||||
u32 val = readl(reg);
|
||||
|
||||
seq_puts(s, "\n");
|
||||
seq_printf(s, "\n %-25s 0x%08X", "VIDEO_DACS_CONTROL", val);
|
||||
seq_printf(s, "\n\n %-25s 0x%08X", "VIDEO_DACS_CONTROL", val);
|
||||
seq_puts(s, "\tHD DACs ");
|
||||
seq_puts(s, val & DAC_CFG_HD_HZUVW_OFF_MASK ? "disabled" : "enabled");
|
||||
}
|
||||
@ -356,8 +354,7 @@ static int hda_dbg_show(struct seq_file *s, void *data)
|
||||
hda_dbg_awg_microcode(s, hda->regs + HDA_SYNC_AWGI);
|
||||
if (hda->video_dacs_ctrl)
|
||||
hda_dbg_video_dacs_ctrl(s, hda->video_dacs_ctrl);
|
||||
seq_puts(s, "\n");
|
||||
|
||||
seq_putc(s, '\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -592,7 +592,7 @@ static void hdmi_dbg_cfg(struct seq_file *s, int val)
|
||||
{
|
||||
int tmp;
|
||||
|
||||
seq_puts(s, "\t");
|
||||
seq_putc(s, '\t');
|
||||
tmp = val & HDMI_CFG_HDMI_NOT_DVI;
|
||||
DBGFS_PRINT_STR("mode:", tmp ? "HDMI" : "DVI");
|
||||
seq_puts(s, "\t\t\t\t\t");
|
||||
@ -616,7 +616,7 @@ static void hdmi_dbg_sta(struct seq_file *s, int val)
|
||||
{
|
||||
int tmp;
|
||||
|
||||
seq_puts(s, "\t");
|
||||
seq_putc(s, '\t');
|
||||
tmp = (val & HDMI_STA_DLL_LCK);
|
||||
DBGFS_PRINT_STR("pll:", tmp ? "locked" : "not locked");
|
||||
seq_puts(s, "\t\t\t\t\t");
|
||||
@ -632,7 +632,7 @@ static void hdmi_dbg_sw_di_cfg(struct seq_file *s, int val)
|
||||
"once every field",
|
||||
"once every frame"};
|
||||
|
||||
seq_puts(s, "\t");
|
||||
seq_putc(s, '\t');
|
||||
tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 1));
|
||||
DBGFS_PRINT_STR("Data island 1:", en_di[tmp]);
|
||||
seq_puts(s, "\t\t\t\t\t");
|
||||
@ -664,16 +664,16 @@ static int hdmi_dbg_show(struct seq_file *s, void *data)
|
||||
DBGFS_DUMP("\n", HDMI_STA);
|
||||
hdmi_dbg_sta(s, hdmi_read(hdmi, HDMI_STA));
|
||||
DBGFS_DUMP("", HDMI_ACTIVE_VID_XMIN);
|
||||
seq_puts(s, "\t");
|
||||
seq_putc(s, '\t');
|
||||
DBGFS_PRINT_INT("Xmin:", hdmi_read(hdmi, HDMI_ACTIVE_VID_XMIN));
|
||||
DBGFS_DUMP("", HDMI_ACTIVE_VID_XMAX);
|
||||
seq_puts(s, "\t");
|
||||
seq_putc(s, '\t');
|
||||
DBGFS_PRINT_INT("Xmax:", hdmi_read(hdmi, HDMI_ACTIVE_VID_XMAX));
|
||||
DBGFS_DUMP("", HDMI_ACTIVE_VID_YMIN);
|
||||
seq_puts(s, "\t");
|
||||
seq_putc(s, '\t');
|
||||
DBGFS_PRINT_INT("Ymin:", hdmi_read(hdmi, HDMI_ACTIVE_VID_YMIN));
|
||||
DBGFS_DUMP("", HDMI_ACTIVE_VID_YMAX);
|
||||
seq_puts(s, "\t");
|
||||
seq_putc(s, '\t');
|
||||
DBGFS_PRINT_INT("Ymax:", hdmi_read(hdmi, HDMI_ACTIVE_VID_YMAX));
|
||||
DBGFS_DUMP("", HDMI_SW_DI_CFG);
|
||||
hdmi_dbg_sw_di_cfg(s, hdmi_read(hdmi, HDMI_SW_DI_CFG));
|
||||
@ -692,8 +692,7 @@ static int hdmi_dbg_show(struct seq_file *s, void *data)
|
||||
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_AVI);
|
||||
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_AVI);
|
||||
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_AVI);
|
||||
seq_puts(s, "\n");
|
||||
seq_printf(s, "\n AUDIO Infoframe (Data Island slot N=%d):",
|
||||
seq_printf(s, "\n\n AUDIO Infoframe (Data Island slot N=%d):",
|
||||
HDMI_IFRAME_SLOT_AUDIO);
|
||||
DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_AUDIO);
|
||||
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_AUDIO);
|
||||
@ -703,8 +702,7 @@ static int hdmi_dbg_show(struct seq_file *s, void *data)
|
||||
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_AUDIO);
|
||||
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_AUDIO);
|
||||
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_AUDIO);
|
||||
seq_puts(s, "\n");
|
||||
seq_printf(s, "\n VENDOR SPECIFIC Infoframe (Data Island slot N=%d):",
|
||||
seq_printf(s, "\n\n VENDOR SPECIFIC Infoframe (Data Island slot N=%d):",
|
||||
HDMI_IFRAME_SLOT_VENDOR);
|
||||
DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_VENDOR);
|
||||
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_VENDOR);
|
||||
@ -714,8 +712,7 @@ static int hdmi_dbg_show(struct seq_file *s, void *data)
|
||||
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_VENDOR);
|
||||
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_VENDOR);
|
||||
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_VENDOR);
|
||||
seq_puts(s, "\n");
|
||||
|
||||
seq_putc(s, '\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -625,8 +625,7 @@ static int hqvdp_dbg_show(struct seq_file *s, void *data)
|
||||
hqvdp_dbg_dump_cmd(s, (struct sti_hqvdp_cmd *)virt);
|
||||
}
|
||||
|
||||
seq_puts(s, "\n");
|
||||
|
||||
seq_putc(s, '\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1357,12 +1356,12 @@ static int sti_hqvdp_probe(struct platform_device *pdev)
|
||||
|
||||
/* Get Memory resources */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (res == NULL) {
|
||||
if (!res) {
|
||||
DRM_ERROR("Get memory resource failed\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
hqvdp->regs = devm_ioremap(dev, res->start, resource_size(res));
|
||||
if (hqvdp->regs == NULL) {
|
||||
if (!hqvdp->regs) {
|
||||
DRM_ERROR("Register mapping failed\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
@ -162,8 +162,7 @@ static int mixer_dbg_show(struct seq_file *s, void *arg)
|
||||
DBGFS_DUMP(GAM_MIXER_MBP);
|
||||
DBGFS_DUMP(GAM_MIXER_MX0);
|
||||
mixer_dbg_mxn(s, mixer->regs + GAM_MIXER_MX0);
|
||||
seq_puts(s, "\n");
|
||||
|
||||
seq_putc(s, '\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -459,7 +459,7 @@ static void tvout_dbg_vip(struct seq_file *s, int val)
|
||||
"Aux (color matrix by-passed)",
|
||||
"", "", "", "", "", "Force value"};
|
||||
|
||||
seq_puts(s, "\t");
|
||||
seq_putc(s, '\t');
|
||||
mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT;
|
||||
r = (val & mask) >> TVO_VIP_REORDER_R_SHIFT;
|
||||
mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT;
|
||||
@ -558,8 +558,7 @@ static int tvout_dbg_show(struct seq_file *s, void *data)
|
||||
DBGFS_DUMP(TVO_CSC_AUX_M6);
|
||||
DBGFS_DUMP(TVO_CSC_AUX_M7);
|
||||
DBGFS_DUMP(TVO_AUX_IN_VID_FORMAT);
|
||||
seq_puts(s, "\n");
|
||||
|
||||
seq_putc(s, '\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -847,7 +846,7 @@ static int sti_tvout_probe(struct platform_device *pdev)
|
||||
|
||||
tvout->dev = dev;
|
||||
|
||||
/* get Memory ressources */
|
||||
/* get memory resources */
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tvout-reg");
|
||||
if (!res) {
|
||||
DRM_ERROR("Invalid glue resource\n");
|
||||
|
@ -61,7 +61,7 @@
|
||||
static void vid_dbg_ctl(struct seq_file *s, int val)
|
||||
{
|
||||
val = val >> 30;
|
||||
seq_puts(s, "\t");
|
||||
seq_putc(s, '\t');
|
||||
|
||||
if (!(val & 1))
|
||||
seq_puts(s, "NOT ");
|
||||
@ -114,8 +114,7 @@ static int vid_dbg_show(struct seq_file *s, void *arg)
|
||||
DBGFS_DUMP(VID_BC);
|
||||
DBGFS_DUMP(VID_TINT);
|
||||
DBGFS_DUMP(VID_CSAT);
|
||||
seq_puts(s, "\n");
|
||||
|
||||
seq_putc(s, '\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
16
drivers/gpu/drm/stm/Kconfig
Normal file
16
drivers/gpu/drm/stm/Kconfig
Normal file
@ -0,0 +1,16 @@
|
||||
config DRM_STM
|
||||
tristate "DRM Support for STMicroelectronics SoC Series"
|
||||
depends on DRM && (ARCH_STM32 || ARCH_MULTIPLATFORM)
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_GEM_CMA_HELPER
|
||||
select DRM_KMS_CMA_HELPER
|
||||
select DRM_PANEL
|
||||
select VIDEOMODE_HELPERS
|
||||
select FB_PROVIDE_GET_FB_UNMAPPED_AREA
|
||||
default y
|
||||
|
||||
help
|
||||
Enable support for the on-chip display controller on
|
||||
STMicroelectronics STM32 MCUs.
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called stm-drm.
|
7
drivers/gpu/drm/stm/Makefile
Normal file
7
drivers/gpu/drm/stm/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
ccflags-y := -Iinclude/drm
|
||||
|
||||
stm-drm-y := \
|
||||
drv.o \
|
||||
ltdc.o
|
||||
|
||||
obj-$(CONFIG_DRM_STM) += stm-drm.o
|
221
drivers/gpu/drm/stm/drv.c
Normal file
221
drivers/gpu/drm/stm/drv.c
Normal file
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright (C) STMicroelectronics SA 2017
|
||||
*
|
||||
* Authors: Philippe Cornu <philippe.cornu@st.com>
|
||||
* Yannick Fertre <yannick.fertre@st.com>
|
||||
* Fabien Dessenne <fabien.dessenne@st.com>
|
||||
* Mickael Reulier <mickael.reulier@st.com>
|
||||
*
|
||||
* License terms: GNU General Public License (GPL), version 2
|
||||
*/
|
||||
|
||||
#include <linux/component.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
|
||||
#include "ltdc.h"
|
||||
|
||||
#define DRIVER_NAME "stm"
|
||||
#define DRIVER_DESC "STMicroelectronics SoC DRM"
|
||||
#define DRIVER_DATE "20170330"
|
||||
#define DRIVER_MAJOR 1
|
||||
#define DRIVER_MINOR 0
|
||||
#define DRIVER_PATCH_LEVEL 0
|
||||
|
||||
#define STM_MAX_FB_WIDTH 2048
|
||||
#define STM_MAX_FB_HEIGHT 2048 /* same as width to handle orientation */
|
||||
|
||||
static void drv_output_poll_changed(struct drm_device *ddev)
|
||||
{
|
||||
struct ltdc_device *ldev = ddev->dev_private;
|
||||
|
||||
drm_fbdev_cma_hotplug_event(ldev->fbdev);
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_funcs drv_mode_config_funcs = {
|
||||
.fb_create = drm_fb_cma_create,
|
||||
.output_poll_changed = drv_output_poll_changed,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
};
|
||||
|
||||
static void drv_lastclose(struct drm_device *ddev)
|
||||
{
|
||||
struct ltdc_device *ldev = ddev->dev_private;
|
||||
|
||||
DRM_DEBUG("%s\n", __func__);
|
||||
|
||||
drm_fbdev_cma_restore_mode(ldev->fbdev);
|
||||
}
|
||||
|
||||
DEFINE_DRM_GEM_CMA_FOPS(drv_driver_fops);
|
||||
|
||||
static struct drm_driver drv_driver = {
|
||||
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
|
||||
DRIVER_ATOMIC,
|
||||
.lastclose = drv_lastclose,
|
||||
.name = DRIVER_NAME,
|
||||
.desc = DRIVER_DESC,
|
||||
.date = DRIVER_DATE,
|
||||
.major = DRIVER_MAJOR,
|
||||
.minor = DRIVER_MINOR,
|
||||
.patchlevel = DRIVER_PATCH_LEVEL,
|
||||
.fops = &drv_driver_fops,
|
||||
.dumb_create = drm_gem_cma_dumb_create,
|
||||
.dumb_map_offset = drm_gem_cma_dumb_map_offset,
|
||||
.dumb_destroy = drm_gem_dumb_destroy,
|
||||
.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,
|
||||
.gem_vm_ops = &drm_gem_cma_vm_ops,
|
||||
.gem_prime_export = drm_gem_prime_export,
|
||||
.gem_prime_import = drm_gem_prime_import,
|
||||
.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
|
||||
.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
|
||||
.gem_prime_vmap = drm_gem_cma_prime_vmap,
|
||||
.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
|
||||
.gem_prime_mmap = drm_gem_cma_prime_mmap,
|
||||
.enable_vblank = ltdc_crtc_enable_vblank,
|
||||
.disable_vblank = ltdc_crtc_disable_vblank,
|
||||
};
|
||||
|
||||
static int drv_load(struct drm_device *ddev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(ddev->dev);
|
||||
struct drm_fbdev_cma *fbdev;
|
||||
struct ltdc_device *ldev;
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG("%s\n", __func__);
|
||||
|
||||
ldev = devm_kzalloc(ddev->dev, sizeof(*ldev), GFP_KERNEL);
|
||||
if (!ldev)
|
||||
return -ENOMEM;
|
||||
|
||||
ddev->dev_private = (void *)ldev;
|
||||
|
||||
drm_mode_config_init(ddev);
|
||||
|
||||
/*
|
||||
* set max width and height as default value.
|
||||
* this value would be used to check framebuffer size limitation
|
||||
* at drm_mode_addfb().
|
||||
*/
|
||||
ddev->mode_config.min_width = 0;
|
||||
ddev->mode_config.min_height = 0;
|
||||
ddev->mode_config.max_width = STM_MAX_FB_WIDTH;
|
||||
ddev->mode_config.max_height = STM_MAX_FB_HEIGHT;
|
||||
ddev->mode_config.funcs = &drv_mode_config_funcs;
|
||||
|
||||
ret = ltdc_load(ddev);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
drm_mode_config_reset(ddev);
|
||||
drm_kms_helper_poll_init(ddev);
|
||||
|
||||
if (ddev->mode_config.num_connector) {
|
||||
ldev = ddev->dev_private;
|
||||
fbdev = drm_fbdev_cma_init(ddev, 16,
|
||||
ddev->mode_config.num_connector);
|
||||
if (IS_ERR(fbdev)) {
|
||||
DRM_DEBUG("Warning: fails to create fbdev\n");
|
||||
fbdev = NULL;
|
||||
}
|
||||
ldev->fbdev = fbdev;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, ddev);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
drm_mode_config_cleanup(ddev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void drv_unload(struct drm_device *ddev)
|
||||
{
|
||||
struct ltdc_device *ldev = ddev->dev_private;
|
||||
|
||||
DRM_DEBUG("%s\n", __func__);
|
||||
|
||||
if (ldev->fbdev) {
|
||||
drm_fbdev_cma_fini(ldev->fbdev);
|
||||
ldev->fbdev = NULL;
|
||||
}
|
||||
drm_kms_helper_poll_fini(ddev);
|
||||
ltdc_unload(ddev);
|
||||
drm_mode_config_cleanup(ddev);
|
||||
}
|
||||
|
||||
static int stm_drm_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct drm_device *ddev;
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG("%s\n", __func__);
|
||||
|
||||
dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
|
||||
|
||||
ddev = drm_dev_alloc(&drv_driver, dev);
|
||||
if (IS_ERR(ddev))
|
||||
return PTR_ERR(ddev);
|
||||
|
||||
ret = drv_load(ddev);
|
||||
if (ret)
|
||||
goto err_unref;
|
||||
|
||||
ret = drm_dev_register(ddev, 0);
|
||||
if (ret)
|
||||
goto err_unref;
|
||||
|
||||
return 0;
|
||||
|
||||
err_unref:
|
||||
drm_dev_unref(ddev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm_drm_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct drm_device *ddev = platform_get_drvdata(pdev);
|
||||
|
||||
DRM_DEBUG("%s\n", __func__);
|
||||
|
||||
drm_dev_unregister(ddev);
|
||||
drv_unload(ddev);
|
||||
drm_dev_unref(ddev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id drv_dt_ids[] = {
|
||||
{ .compatible = "st,stm32-ltdc"},
|
||||
{ /* end node */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, drv_dt_ids);
|
||||
|
||||
static struct platform_driver stm_drm_platform_driver = {
|
||||
.probe = stm_drm_platform_probe,
|
||||
.remove = stm_drm_platform_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = drv_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(stm_drm_platform_driver);
|
||||
|
||||
MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
|
||||
MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
|
||||
MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>");
|
||||
MODULE_AUTHOR("Mickael Reulier <mickael.reulier@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics ST DRM LTDC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
1160
drivers/gpu/drm/stm/ltdc.c
Normal file
1160
drivers/gpu/drm/stm/ltdc.c
Normal file
File diff suppressed because it is too large
Load Diff
40
drivers/gpu/drm/stm/ltdc.h
Normal file
40
drivers/gpu/drm/stm/ltdc.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) STMicroelectronics SA 2017
|
||||
*
|
||||
* Authors: Philippe Cornu <philippe.cornu@st.com>
|
||||
* Yannick Fertre <yannick.fertre@st.com>
|
||||
* Fabien Dessenne <fabien.dessenne@st.com>
|
||||
* Mickael Reulier <mickael.reulier@st.com>
|
||||
*
|
||||
* License terms: GNU General Public License (GPL), version 2
|
||||
*/
|
||||
|
||||
#ifndef _LTDC_H_
|
||||
#define _LTDC_H_
|
||||
|
||||
struct ltdc_caps {
|
||||
u32 hw_version; /* hardware version */
|
||||
u32 nb_layers; /* number of supported layers */
|
||||
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 */
|
||||
};
|
||||
|
||||
struct ltdc_device {
|
||||
struct drm_fbdev_cma *fbdev;
|
||||
void __iomem *regs;
|
||||
struct clk *pixel_clk; /* lcd pixel clock */
|
||||
struct drm_panel *panel;
|
||||
struct mutex err_lock; /* protecting error_status */
|
||||
struct ltdc_caps caps;
|
||||
u32 clut[256]; /* color look up table */
|
||||
u32 error_status;
|
||||
u32 irq_status;
|
||||
};
|
||||
|
||||
int ltdc_crtc_enable_vblank(struct drm_device *dev, unsigned int pipe);
|
||||
void ltdc_crtc_disable_vblank(struct drm_device *dev, unsigned int pipe);
|
||||
int ltdc_load(struct drm_device *ddev);
|
||||
void ltdc_unload(struct drm_device *ddev);
|
||||
|
||||
#endif
|
@ -892,7 +892,7 @@ static int tegra_drm_context_cleanup(int id, void *p, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file)
|
||||
static void tegra_drm_postclose(struct drm_device *drm, struct drm_file *file)
|
||||
{
|
||||
struct tegra_drm_file *fpriv = file->driver_priv;
|
||||
|
||||
@ -960,7 +960,7 @@ static struct drm_driver tegra_drm_driver = {
|
||||
.load = tegra_drm_load,
|
||||
.unload = tegra_drm_unload,
|
||||
.open = tegra_drm_open,
|
||||
.preclose = tegra_drm_preclose,
|
||||
.postclose = tegra_drm_postclose,
|
||||
.lastclose = tegra_drm_lastclose,
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
|
@ -9,6 +9,7 @@ vc4-y := \
|
||||
vc4_drv.o \
|
||||
vc4_dpi.o \
|
||||
vc4_dsi.o \
|
||||
vc4_fence.o \
|
||||
vc4_kms.o \
|
||||
vc4_gem.o \
|
||||
vc4_hdmi.o \
|
||||
|
@ -19,6 +19,8 @@
|
||||
* rendering can return quickly.
|
||||
*/
|
||||
|
||||
#include <linux/dma-buf.h>
|
||||
|
||||
#include "vc4_drv.h"
|
||||
#include "uapi/drm/vc4_drm.h"
|
||||
|
||||
@ -88,6 +90,10 @@ static void vc4_bo_destroy(struct vc4_bo *bo)
|
||||
|
||||
vc4->bo_stats.num_allocated--;
|
||||
vc4->bo_stats.size_allocated -= obj->size;
|
||||
|
||||
if (bo->resv == &bo->_resv)
|
||||
reservation_object_fini(bo->resv);
|
||||
|
||||
drm_gem_cma_free_object(obj);
|
||||
}
|
||||
|
||||
@ -244,8 +250,12 @@ struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size,
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
}
|
||||
bo = to_vc4_bo(&cma_obj->base);
|
||||
|
||||
return to_vc4_bo(&cma_obj->base);
|
||||
bo->resv = &bo->_resv;
|
||||
reservation_object_init(bo->resv);
|
||||
|
||||
return bo;
|
||||
}
|
||||
|
||||
int vc4_dumb_create(struct drm_file *file_priv,
|
||||
@ -369,6 +379,13 @@ static void vc4_bo_cache_time_timer(unsigned long data)
|
||||
schedule_work(&vc4->bo_cache.time_work);
|
||||
}
|
||||
|
||||
struct reservation_object *vc4_prime_res_obj(struct drm_gem_object *obj)
|
||||
{
|
||||
struct vc4_bo *bo = to_vc4_bo(obj);
|
||||
|
||||
return bo->resv;
|
||||
}
|
||||
|
||||
struct dma_buf *
|
||||
vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags)
|
||||
{
|
||||
@ -440,6 +457,24 @@ void *vc4_prime_vmap(struct drm_gem_object *obj)
|
||||
return drm_gem_cma_prime_vmap(obj);
|
||||
}
|
||||
|
||||
struct drm_gem_object *
|
||||
vc4_prime_import_sg_table(struct drm_device *dev,
|
||||
struct dma_buf_attachment *attach,
|
||||
struct sg_table *sgt)
|
||||
{
|
||||
struct drm_gem_object *obj;
|
||||
struct vc4_bo *bo;
|
||||
|
||||
obj = drm_gem_cma_prime_import_sg_table(dev, attach, sgt);
|
||||
if (IS_ERR(obj))
|
||||
return obj;
|
||||
|
||||
bo = to_vc4_bo(obj);
|
||||
bo->resv = attach->dmabuf->resv;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
int vc4_create_bo_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
|
@ -151,10 +151,10 @@ int vc4_crtc_debugfs_regs(struct seq_file *m, void *unused)
|
||||
}
|
||||
#endif
|
||||
|
||||
int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
|
||||
unsigned int flags, int *vpos, int *hpos,
|
||||
ktime_t *stime, ktime_t *etime,
|
||||
const struct drm_display_mode *mode)
|
||||
bool vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
|
||||
bool in_vblank_irq, int *vpos, int *hpos,
|
||||
ktime_t *stime, ktime_t *etime,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||||
struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id);
|
||||
@ -162,7 +162,7 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
|
||||
u32 val;
|
||||
int fifo_lines;
|
||||
int vblank_lines;
|
||||
int ret = 0;
|
||||
bool ret = false;
|
||||
|
||||
/* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
|
||||
|
||||
@ -198,7 +198,7 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
|
||||
fifo_lines = vc4_crtc->cob_size / mode->crtc_hdisplay;
|
||||
|
||||
if (fifo_lines > 0)
|
||||
ret |= DRM_SCANOUTPOS_VALID;
|
||||
ret = true;
|
||||
|
||||
/* HVS more than fifo_lines into frame for compositing? */
|
||||
if (*vpos > fifo_lines) {
|
||||
@ -216,7 +216,6 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
|
||||
*/
|
||||
*vpos -= fifo_lines + 1;
|
||||
|
||||
ret |= DRM_SCANOUTPOS_ACCURATE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -229,10 +228,9 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
|
||||
* We can't get meaningful readings wrt. scanline position of the PV
|
||||
* and need to make things up in a approximative but consistent way.
|
||||
*/
|
||||
ret |= DRM_SCANOUTPOS_IN_VBLANK;
|
||||
vblank_lines = mode->vtotal - mode->vdisplay;
|
||||
|
||||
if (flags & DRM_CALLED_FROM_VBLIRQ) {
|
||||
if (in_vblank_irq) {
|
||||
/*
|
||||
* Assume the irq handler got called close to first
|
||||
* line of vblank, so PV has about a full vblank
|
||||
@ -254,9 +252,10 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
|
||||
* we are at the very beginning of vblank, as the hvs just
|
||||
* started refilling, and the stime and etime timestamps
|
||||
* truly correspond to start of vblank.
|
||||
*
|
||||
* Unfortunately there's no way to report this to upper levels
|
||||
* and make it more useful.
|
||||
*/
|
||||
if ((val & SCALER_DISPSTATX_FULL) != SCALER_DISPSTATX_FULL)
|
||||
ret |= DRM_SCANOUTPOS_ACCURATE;
|
||||
} else {
|
||||
/*
|
||||
* No clue where we are inside vblank. Return a vpos of zero,
|
||||
@ -270,19 +269,6 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
|
||||
return ret;
|
||||
}
|
||||
|
||||
int vc4_crtc_get_vblank_timestamp(struct drm_device *dev, unsigned int crtc_id,
|
||||
int *max_error, struct timeval *vblank_time,
|
||||
unsigned flags)
|
||||
{
|
||||
struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id);
|
||||
struct drm_crtc_state *state = crtc->state;
|
||||
|
||||
/* Helper routine in DRM core does all the work: */
|
||||
return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc_id, max_error,
|
||||
vblank_time, flags,
|
||||
&state->adjusted_mode);
|
||||
}
|
||||
|
||||
static void vc4_crtc_destroy(struct drm_crtc *crtc)
|
||||
{
|
||||
drm_crtc_cleanup(crtc);
|
||||
|
@ -154,7 +154,7 @@ static struct drm_driver vc4_drm_driver = {
|
||||
.irq_uninstall = vc4_irq_uninstall,
|
||||
|
||||
.get_scanout_position = vc4_crtc_get_scanoutpos,
|
||||
.get_vblank_timestamp = vc4_crtc_get_vblank_timestamp,
|
||||
.get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos,
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
.debugfs_init = vc4_debugfs_init,
|
||||
@ -168,8 +168,9 @@ static struct drm_driver vc4_drm_driver = {
|
||||
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
|
||||
.gem_prime_import = drm_gem_prime_import,
|
||||
.gem_prime_export = vc4_prime_export,
|
||||
.gem_prime_res_obj = vc4_prime_res_obj,
|
||||
.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
|
||||
.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
|
||||
.gem_prime_import_sg_table = vc4_prime_import_sg_table,
|
||||
.gem_prime_vmap = vc4_prime_vmap,
|
||||
.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
|
||||
.gem_prime_mmap = vc4_prime_mmap,
|
||||
@ -334,6 +335,7 @@ static int vc4_platform_drm_remove(struct platform_device *pdev)
|
||||
|
||||
static const struct of_device_id vc4_of_match[] = {
|
||||
{ .compatible = "brcm,bcm2835-vc4", },
|
||||
{ .compatible = "brcm,cygnus-vc4", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, vc4_of_match);
|
||||
|
@ -8,7 +8,9 @@
|
||||
|
||||
#include "drmP.h"
|
||||
#include "drm_gem_cma_helper.h"
|
||||
#include "drm_gem_cma_helper.h"
|
||||
|
||||
#include <linux/reservation.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
|
||||
struct vc4_dev {
|
||||
@ -56,6 +58,8 @@ struct vc4_dev {
|
||||
/* Protects bo_cache and the BO stats. */
|
||||
struct mutex bo_lock;
|
||||
|
||||
uint64_t dma_fence_context;
|
||||
|
||||
/* Sequence number for the last job queued in bin_job_list.
|
||||
* Starts at 0 (no jobs emitted).
|
||||
*/
|
||||
@ -95,12 +99,23 @@ struct vc4_dev {
|
||||
*/
|
||||
struct list_head seqno_cb_list;
|
||||
|
||||
/* The binner overflow memory that's currently set up in
|
||||
* BPOA/BPOS registers. When overflow occurs and a new one is
|
||||
* allocated, the previous one will be moved to
|
||||
* vc4->current_exec's free list.
|
||||
/* The memory used for storing binner tile alloc, tile state,
|
||||
* and overflow memory allocations. This is freed when V3D
|
||||
* powers down.
|
||||
*/
|
||||
struct vc4_bo *overflow_mem;
|
||||
struct vc4_bo *bin_bo;
|
||||
|
||||
/* Size of blocks allocated within bin_bo. */
|
||||
uint32_t bin_alloc_size;
|
||||
|
||||
/* Bitmask of the bin_alloc_size chunks in bin_bo that are
|
||||
* used.
|
||||
*/
|
||||
uint32_t bin_alloc_used;
|
||||
|
||||
/* Bitmask of the current bin_alloc used for overflow memory. */
|
||||
uint32_t bin_alloc_overflow;
|
||||
|
||||
struct work_struct overflow_mem_work;
|
||||
|
||||
int power_refcount;
|
||||
@ -150,6 +165,10 @@ struct vc4_bo {
|
||||
* DRM_IOCTL_VC4_CREATE_SHADER_BO.
|
||||
*/
|
||||
struct vc4_validated_shader_info *validated_shader;
|
||||
|
||||
/* normally (resv == &_resv) except for imported bo's */
|
||||
struct reservation_object *resv;
|
||||
struct reservation_object _resv;
|
||||
};
|
||||
|
||||
static inline struct vc4_bo *
|
||||
@ -158,6 +177,19 @@ to_vc4_bo(struct drm_gem_object *bo)
|
||||
return (struct vc4_bo *)bo;
|
||||
}
|
||||
|
||||
struct vc4_fence {
|
||||
struct dma_fence base;
|
||||
struct drm_device *dev;
|
||||
/* vc4 seqno for signaled() test */
|
||||
uint64_t seqno;
|
||||
};
|
||||
|
||||
static inline struct vc4_fence *
|
||||
to_vc4_fence(struct dma_fence *fence)
|
||||
{
|
||||
return (struct vc4_fence *)fence;
|
||||
}
|
||||
|
||||
struct vc4_seqno_cb {
|
||||
struct work_struct work;
|
||||
uint64_t seqno;
|
||||
@ -168,6 +200,7 @@ struct vc4_v3d {
|
||||
struct vc4_dev *vc4;
|
||||
struct platform_device *pdev;
|
||||
void __iomem *regs;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
struct vc4_hvs {
|
||||
@ -230,6 +263,8 @@ struct vc4_exec_info {
|
||||
/* Latest write_seqno of any BO that binning depends on. */
|
||||
uint64_t bin_dep_seqno;
|
||||
|
||||
struct dma_fence *fence;
|
||||
|
||||
/* Last current addresses the hardware was processing when the
|
||||
* hangcheck timer checked on us.
|
||||
*/
|
||||
@ -293,8 +328,12 @@ struct vc4_exec_info {
|
||||
bool found_increment_semaphore_packet;
|
||||
bool found_flush;
|
||||
uint8_t bin_tiles_x, bin_tiles_y;
|
||||
struct drm_gem_cma_object *tile_bo;
|
||||
/* Physical address of the start of the tile alloc array
|
||||
* (where each tile's binned CL will start)
|
||||
*/
|
||||
uint32_t tile_alloc_offset;
|
||||
/* Bitmask of which binner slots are freed when this job completes. */
|
||||
uint32_t bin_slots;
|
||||
|
||||
/**
|
||||
* Computed addresses pointing into exec_bo where we start the
|
||||
@ -436,7 +475,11 @@ int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data,
|
||||
int vc4_get_hang_state_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
int vc4_mmap(struct file *filp, struct vm_area_struct *vma);
|
||||
struct reservation_object *vc4_prime_res_obj(struct drm_gem_object *obj);
|
||||
int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
|
||||
struct drm_gem_object *vc4_prime_import_sg_table(struct drm_device *dev,
|
||||
struct dma_buf_attachment *attach,
|
||||
struct sg_table *sgt);
|
||||
void *vc4_prime_vmap(struct drm_gem_object *obj);
|
||||
void vc4_bo_cache_init(struct drm_device *dev);
|
||||
void vc4_bo_cache_destroy(struct drm_device *dev);
|
||||
@ -446,13 +489,10 @@ int vc4_bo_stats_debugfs(struct seq_file *m, void *arg);
|
||||
extern struct platform_driver vc4_crtc_driver;
|
||||
bool vc4_event_pending(struct drm_crtc *crtc);
|
||||
int vc4_crtc_debugfs_regs(struct seq_file *m, void *arg);
|
||||
int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
|
||||
unsigned int flags, int *vpos, int *hpos,
|
||||
ktime_t *stime, ktime_t *etime,
|
||||
const struct drm_display_mode *mode);
|
||||
int vc4_crtc_get_vblank_timestamp(struct drm_device *dev, unsigned int crtc_id,
|
||||
int *max_error, struct timeval *vblank_time,
|
||||
unsigned flags);
|
||||
bool vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
|
||||
bool in_vblank_irq, int *vpos, int *hpos,
|
||||
ktime_t *stime, ktime_t *etime,
|
||||
const struct drm_display_mode *mode);
|
||||
|
||||
/* vc4_debugfs.c */
|
||||
int vc4_debugfs_init(struct drm_minor *minor);
|
||||
@ -468,6 +508,9 @@ int vc4_dpi_debugfs_regs(struct seq_file *m, void *unused);
|
||||
extern struct platform_driver vc4_dsi_driver;
|
||||
int vc4_dsi_debugfs_regs(struct seq_file *m, void *unused);
|
||||
|
||||
/* vc4_fence.c */
|
||||
extern const struct dma_fence_ops vc4_fence_ops;
|
||||
|
||||
/* vc4_gem.c */
|
||||
void vc4_gem_init(struct drm_device *dev);
|
||||
void vc4_gem_destroy(struct drm_device *dev);
|
||||
@ -522,6 +565,7 @@ void vc4_plane_async_set_fb(struct drm_plane *plane,
|
||||
extern struct platform_driver vc4_v3d_driver;
|
||||
int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused);
|
||||
int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused);
|
||||
int vc4_v3d_get_bin_slot(struct vc4_dev *vc4);
|
||||
|
||||
/* vc4_validate.c */
|
||||
int
|
||||
|
56
drivers/gpu/drm/vc4/vc4_fence.c
Normal file
56
drivers/gpu/drm/vc4/vc4_fence.c
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright © 2017 Broadcom
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "vc4_drv.h"
|
||||
|
||||
static const char *vc4_fence_get_driver_name(struct dma_fence *fence)
|
||||
{
|
||||
return "vc4";
|
||||
}
|
||||
|
||||
static const char *vc4_fence_get_timeline_name(struct dma_fence *fence)
|
||||
{
|
||||
return "vc4-v3d";
|
||||
}
|
||||
|
||||
static bool vc4_fence_enable_signaling(struct dma_fence *fence)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool vc4_fence_signaled(struct dma_fence *fence)
|
||||
{
|
||||
struct vc4_fence *f = to_vc4_fence(fence);
|
||||
struct vc4_dev *vc4 = to_vc4_dev(f->dev);
|
||||
|
||||
return vc4->finished_seqno >= f->seqno;
|
||||
}
|
||||
|
||||
const struct dma_fence_ops vc4_fence_ops = {
|
||||
.get_driver_name = vc4_fence_get_driver_name,
|
||||
.get_timeline_name = vc4_fence_get_timeline_name,
|
||||
.enable_signaling = vc4_fence_enable_signaling,
|
||||
.signaled = vc4_fence_signaled,
|
||||
.wait = dma_fence_default_wait,
|
||||
.release = dma_fence_free,
|
||||
};
|
@ -463,6 +463,8 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
|
||||
for (i = 0; i < exec->bo_count; i++) {
|
||||
bo = to_vc4_bo(&exec->bo[i]->base);
|
||||
bo->seqno = seqno;
|
||||
|
||||
reservation_object_add_shared_fence(bo->resv, exec->fence);
|
||||
}
|
||||
|
||||
list_for_each_entry(bo, &exec->unref_list, unref_head) {
|
||||
@ -472,9 +474,105 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
|
||||
for (i = 0; i < exec->rcl_write_bo_count; i++) {
|
||||
bo = to_vc4_bo(&exec->rcl_write_bo[i]->base);
|
||||
bo->write_seqno = seqno;
|
||||
|
||||
reservation_object_add_excl_fence(bo->resv, exec->fence);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
vc4_unlock_bo_reservations(struct drm_device *dev,
|
||||
struct vc4_exec_info *exec,
|
||||
struct ww_acquire_ctx *acquire_ctx)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < exec->bo_count; i++) {
|
||||
struct vc4_bo *bo = to_vc4_bo(&exec->bo[i]->base);
|
||||
|
||||
ww_mutex_unlock(&bo->resv->lock);
|
||||
}
|
||||
|
||||
ww_acquire_fini(acquire_ctx);
|
||||
}
|
||||
|
||||
/* Takes the reservation lock on all the BOs being referenced, so that
|
||||
* at queue submit time we can update the reservations.
|
||||
*
|
||||
* We don't lock the RCL the tile alloc/state BOs, or overflow memory
|
||||
* (all of which are on exec->unref_list). They're entirely private
|
||||
* to vc4, so we don't attach dma-buf fences to them.
|
||||
*/
|
||||
static int
|
||||
vc4_lock_bo_reservations(struct drm_device *dev,
|
||||
struct vc4_exec_info *exec,
|
||||
struct ww_acquire_ctx *acquire_ctx)
|
||||
{
|
||||
int contended_lock = -1;
|
||||
int i, ret;
|
||||
struct vc4_bo *bo;
|
||||
|
||||
ww_acquire_init(acquire_ctx, &reservation_ww_class);
|
||||
|
||||
retry:
|
||||
if (contended_lock != -1) {
|
||||
bo = to_vc4_bo(&exec->bo[contended_lock]->base);
|
||||
ret = ww_mutex_lock_slow_interruptible(&bo->resv->lock,
|
||||
acquire_ctx);
|
||||
if (ret) {
|
||||
ww_acquire_done(acquire_ctx);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < exec->bo_count; i++) {
|
||||
if (i == contended_lock)
|
||||
continue;
|
||||
|
||||
bo = to_vc4_bo(&exec->bo[i]->base);
|
||||
|
||||
ret = ww_mutex_lock_interruptible(&bo->resv->lock, acquire_ctx);
|
||||
if (ret) {
|
||||
int j;
|
||||
|
||||
for (j = 0; j < i; j++) {
|
||||
bo = to_vc4_bo(&exec->bo[j]->base);
|
||||
ww_mutex_unlock(&bo->resv->lock);
|
||||
}
|
||||
|
||||
if (contended_lock != -1 && contended_lock >= i) {
|
||||
bo = to_vc4_bo(&exec->bo[contended_lock]->base);
|
||||
|
||||
ww_mutex_unlock(&bo->resv->lock);
|
||||
}
|
||||
|
||||
if (ret == -EDEADLK) {
|
||||
contended_lock = i;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
ww_acquire_done(acquire_ctx);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ww_acquire_done(acquire_ctx);
|
||||
|
||||
/* Reserve space for our shared (read-only) fence references,
|
||||
* before we commit the CL to the hardware.
|
||||
*/
|
||||
for (i = 0; i < exec->bo_count; i++) {
|
||||
bo = to_vc4_bo(&exec->bo[i]->base);
|
||||
|
||||
ret = reservation_object_reserve_shared(bo->resv);
|
||||
if (ret) {
|
||||
vc4_unlock_bo_reservations(dev, exec, acquire_ctx);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Queues a struct vc4_exec_info for execution. If no job is
|
||||
* currently executing, then submits it.
|
||||
*
|
||||
@ -484,19 +582,34 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
|
||||
* then bump the end address. That's a change for a later date,
|
||||
* though.
|
||||
*/
|
||||
static void
|
||||
vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec)
|
||||
static int
|
||||
vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec,
|
||||
struct ww_acquire_ctx *acquire_ctx)
|
||||
{
|
||||
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||||
uint64_t seqno;
|
||||
unsigned long irqflags;
|
||||
struct vc4_fence *fence;
|
||||
|
||||
fence = kzalloc(sizeof(*fence), GFP_KERNEL);
|
||||
if (!fence)
|
||||
return -ENOMEM;
|
||||
fence->dev = dev;
|
||||
|
||||
spin_lock_irqsave(&vc4->job_lock, irqflags);
|
||||
|
||||
seqno = ++vc4->emit_seqno;
|
||||
exec->seqno = seqno;
|
||||
|
||||
dma_fence_init(&fence->base, &vc4_fence_ops, &vc4->job_lock,
|
||||
vc4->dma_fence_context, exec->seqno);
|
||||
fence->seqno = exec->seqno;
|
||||
exec->fence = &fence->base;
|
||||
|
||||
vc4_update_bo_seqnos(exec, seqno);
|
||||
|
||||
vc4_unlock_bo_reservations(dev, exec, acquire_ctx);
|
||||
|
||||
list_add_tail(&exec->head, &vc4->bin_job_list);
|
||||
|
||||
/* If no job was executing, kick ours off. Otherwise, it'll
|
||||
@ -509,6 +622,8 @@ vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec)
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&vc4->job_lock, irqflags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -705,8 +820,15 @@ static void
|
||||
vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec)
|
||||
{
|
||||
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||||
unsigned long irqflags;
|
||||
unsigned i;
|
||||
|
||||
/* If we got force-completed because of GPU reset rather than
|
||||
* through our IRQ handler, signal the fence now.
|
||||
*/
|
||||
if (exec->fence)
|
||||
dma_fence_signal(exec->fence);
|
||||
|
||||
if (exec->bo) {
|
||||
for (i = 0; i < exec->bo_count; i++)
|
||||
drm_gem_object_unreference_unlocked(&exec->bo[i]->base);
|
||||
@ -720,6 +842,11 @@ vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec)
|
||||
drm_gem_object_unreference_unlocked(&bo->base.base);
|
||||
}
|
||||
|
||||
/* Free up the allocation of any bin slots we used. */
|
||||
spin_lock_irqsave(&vc4->job_lock, irqflags);
|
||||
vc4->bin_alloc_used &= ~exec->bin_slots;
|
||||
spin_unlock_irqrestore(&vc4->job_lock, irqflags);
|
||||
|
||||
mutex_lock(&vc4->power_lock);
|
||||
if (--vc4->power_refcount == 0) {
|
||||
pm_runtime_mark_last_busy(&vc4->v3d->pdev->dev);
|
||||
@ -874,6 +1001,7 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
|
||||
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||||
struct drm_vc4_submit_cl *args = data;
|
||||
struct vc4_exec_info *exec;
|
||||
struct ww_acquire_ctx acquire_ctx;
|
||||
int ret = 0;
|
||||
|
||||
if ((args->flags & ~VC4_SUBMIT_CL_USE_CLEAR_COLOR) != 0) {
|
||||
@ -888,13 +1016,16 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
|
||||
}
|
||||
|
||||
mutex_lock(&vc4->power_lock);
|
||||
if (vc4->power_refcount++ == 0)
|
||||
if (vc4->power_refcount++ == 0) {
|
||||
ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev);
|
||||
mutex_unlock(&vc4->power_lock);
|
||||
if (ret < 0) {
|
||||
kfree(exec);
|
||||
return ret;
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&vc4->power_lock);
|
||||
vc4->power_refcount--;
|
||||
kfree(exec);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&vc4->power_lock);
|
||||
|
||||
exec->args = args;
|
||||
INIT_LIST_HEAD(&exec->unref_list);
|
||||
@ -916,12 +1047,18 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
ret = vc4_lock_bo_reservations(dev, exec, &acquire_ctx);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
/* Clear this out of the struct we'll be putting in the queue,
|
||||
* since it's part of our stack.
|
||||
*/
|
||||
exec->args = NULL;
|
||||
|
||||
vc4_queue_submit(dev, exec);
|
||||
ret = vc4_queue_submit(dev, exec, &acquire_ctx);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
/* Return the seqno for our job. */
|
||||
args->seqno = vc4->emit_seqno;
|
||||
@ -939,6 +1076,8 @@ vc4_gem_init(struct drm_device *dev)
|
||||
{
|
||||
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||||
|
||||
vc4->dma_fence_context = dma_fence_context_alloc(1);
|
||||
|
||||
INIT_LIST_HEAD(&vc4->bin_job_list);
|
||||
INIT_LIST_HEAD(&vc4->render_job_list);
|
||||
INIT_LIST_HEAD(&vc4->job_done_list);
|
||||
@ -968,9 +1107,9 @@ vc4_gem_destroy(struct drm_device *dev)
|
||||
/* V3D should already have disabled its interrupt and cleared
|
||||
* the overflow allocation registers. Now free the object.
|
||||
*/
|
||||
if (vc4->overflow_mem) {
|
||||
drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base);
|
||||
vc4->overflow_mem = NULL;
|
||||
if (vc4->bin_bo) {
|
||||
drm_gem_object_put_unlocked(&vc4->bin_bo->base.base);
|
||||
vc4->bin_bo = NULL;
|
||||
}
|
||||
|
||||
if (vc4->hang_state)
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include "linux/of_address.h"
|
||||
#include "linux/of_gpio.h"
|
||||
#include "linux/of_platform.h"
|
||||
#include "linux/pm_runtime.h"
|
||||
#include "linux/rational.h"
|
||||
#include "sound/dmaengine_pcm.h"
|
||||
#include "sound/pcm_drm_eld.h"
|
||||
@ -449,13 +450,38 @@ static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder)
|
||||
vc4_hdmi_set_spd_infoframe(encoder);
|
||||
}
|
||||
|
||||
static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *unadjusted_mode,
|
||||
struct drm_display_mode *mode)
|
||||
static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||||
struct vc4_hdmi *hdmi = vc4->hdmi;
|
||||
int ret;
|
||||
|
||||
HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0);
|
||||
|
||||
HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
|
||||
HD_WRITE(VC4_HD_VID_CTL,
|
||||
HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
|
||||
|
||||
HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST);
|
||||
udelay(1);
|
||||
HD_WRITE(VC4_HD_M_CTL, 0);
|
||||
|
||||
clk_disable_unprepare(hdmi->hsm_clock);
|
||||
clk_disable_unprepare(hdmi->pixel_clock);
|
||||
|
||||
ret = pm_runtime_put(&hdmi->pdev->dev);
|
||||
if (ret < 0)
|
||||
DRM_ERROR("Failed to release power domain: %d\n", ret);
|
||||
}
|
||||
|
||||
static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
|
||||
struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||||
struct vc4_hdmi *hdmi = vc4->hdmi;
|
||||
bool debug_dump_regs = false;
|
||||
bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
|
||||
bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
|
||||
@ -475,6 +501,64 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
|
||||
interlaced,
|
||||
VC4_HDMI_VERTB_VBP));
|
||||
u32 csc_ctl;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(&hdmi->pdev->dev);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("Failed to retain power domain: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
/* This is the rate that is set by the firmware. The number
|
||||
* needs to be a bit higher than the pixel clock rate
|
||||
* (generally 148.5Mhz).
|
||||
*/
|
||||
ret = clk_set_rate(hdmi->hsm_clock, 163682864);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = clk_set_rate(hdmi->pixel_clock,
|
||||
mode->clock * 1000 *
|
||||
((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1));
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to set pixel clock rate: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(hdmi->pixel_clock);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to turn on pixel clock: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(hdmi->hsm_clock);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to turn on HDMI state machine clock: %d\n",
|
||||
ret);
|
||||
clk_disable_unprepare(hdmi->pixel_clock);
|
||||
return;
|
||||
}
|
||||
|
||||
HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST);
|
||||
udelay(1);
|
||||
HD_WRITE(VC4_HD_M_CTL, 0);
|
||||
|
||||
HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_ENABLE);
|
||||
|
||||
HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL,
|
||||
VC4_HDMI_SW_RESET_HDMI |
|
||||
VC4_HDMI_SW_RESET_FORMAT_DETECT);
|
||||
|
||||
HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL, 0);
|
||||
|
||||
/* PHY should be in reset, like
|
||||
* vc4_hdmi_encoder_disable() does.
|
||||
*/
|
||||
HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
|
||||
|
||||
HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0);
|
||||
|
||||
if (debug_dump_regs) {
|
||||
DRM_INFO("HDMI regs before:\n");
|
||||
@ -483,9 +567,6 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
|
||||
|
||||
HD_WRITE(VC4_HD_VID_CTL, 0);
|
||||
|
||||
clk_set_rate(vc4->hdmi->pixel_clock, mode->clock * 1000 *
|
||||
((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1));
|
||||
|
||||
HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
|
||||
HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
|
||||
VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT |
|
||||
@ -559,28 +640,6 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
|
||||
DRM_INFO("HDMI regs after:\n");
|
||||
vc4_hdmi_dump_regs(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||||
|
||||
HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0);
|
||||
|
||||
HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
|
||||
HD_WRITE(VC4_HD_VID_CTL,
|
||||
HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
|
||||
}
|
||||
|
||||
static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||||
int ret;
|
||||
|
||||
HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0);
|
||||
|
||||
HD_WRITE(VC4_HD_VID_CTL,
|
||||
HD_READ(VC4_HD_VID_CTL) |
|
||||
@ -646,7 +705,6 @@ static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
|
||||
.mode_set = vc4_hdmi_encoder_mode_set,
|
||||
.disable = vc4_hdmi_encoder_disable,
|
||||
.enable = vc4_hdmi_encoder_enable,
|
||||
};
|
||||
@ -1147,33 +1205,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
/* Enable the clocks at startup. We can't quite recover from
|
||||
* turning off the pixel clock during disable/enables yet, so
|
||||
* it's always running.
|
||||
*/
|
||||
ret = clk_prepare_enable(hdmi->pixel_clock);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to turn on pixel clock: %d\n", ret);
|
||||
goto err_put_i2c;
|
||||
}
|
||||
|
||||
/* This is the rate that is set by the firmware. The number
|
||||
* needs to be a bit higher than the pixel clock rate
|
||||
* (generally 148.5Mhz).
|
||||
*/
|
||||
ret = clk_set_rate(hdmi->hsm_clock, 163682864);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
|
||||
goto err_unprepare_pix;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(hdmi->hsm_clock);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to turn on HDMI state machine clock: %d\n",
|
||||
ret);
|
||||
goto err_unprepare_pix;
|
||||
}
|
||||
|
||||
/* Only use the GPIO HPD pin if present in the DT, otherwise
|
||||
* we'll use the HDMI core's register.
|
||||
*/
|
||||
@ -1185,7 +1216,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
|
||||
&hpd_gpio_flags);
|
||||
if (hdmi->hpd_gpio < 0) {
|
||||
ret = hdmi->hpd_gpio;
|
||||
goto err_unprepare_hsm;
|
||||
goto err_put_i2c;
|
||||
}
|
||||
|
||||
hdmi->hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW;
|
||||
@ -1193,25 +1224,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
|
||||
|
||||
vc4->hdmi = hdmi;
|
||||
|
||||
/* HDMI core must be enabled. */
|
||||
if (!(HD_READ(VC4_HD_M_CTL) & VC4_HD_M_ENABLE)) {
|
||||
HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST);
|
||||
udelay(1);
|
||||
HD_WRITE(VC4_HD_M_CTL, 0);
|
||||
|
||||
HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_ENABLE);
|
||||
|
||||
HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL,
|
||||
VC4_HDMI_SW_RESET_HDMI |
|
||||
VC4_HDMI_SW_RESET_FORMAT_DETECT);
|
||||
|
||||
HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL, 0);
|
||||
|
||||
/* PHY should be in reset, like
|
||||
* vc4_hdmi_encoder_disable() does.
|
||||
*/
|
||||
HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
|
||||
}
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
drm_encoder_init(drm, hdmi->encoder, &vc4_hdmi_encoder_funcs,
|
||||
DRM_MODE_ENCODER_TMDS, NULL);
|
||||
@ -1231,10 +1244,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
|
||||
|
||||
err_destroy_encoder:
|
||||
vc4_hdmi_encoder_destroy(hdmi->encoder);
|
||||
err_unprepare_hsm:
|
||||
clk_disable_unprepare(hdmi->hsm_clock);
|
||||
err_unprepare_pix:
|
||||
clk_disable_unprepare(hdmi->pixel_clock);
|
||||
pm_runtime_disable(dev);
|
||||
err_put_i2c:
|
||||
put_device(&hdmi->ddc->dev);
|
||||
|
||||
@ -1253,8 +1263,8 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
|
||||
vc4_hdmi_connector_destroy(hdmi->connector);
|
||||
vc4_hdmi_encoder_destroy(hdmi->encoder);
|
||||
|
||||
clk_disable_unprepare(hdmi->pixel_clock);
|
||||
clk_disable_unprepare(hdmi->hsm_clock);
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
put_device(&hdmi->ddc->dev);
|
||||
|
||||
vc4->hdmi = NULL;
|
||||
|
@ -59,50 +59,45 @@ vc4_overflow_mem_work(struct work_struct *work)
|
||||
{
|
||||
struct vc4_dev *vc4 =
|
||||
container_of(work, struct vc4_dev, overflow_mem_work);
|
||||
struct drm_device *dev = vc4->dev;
|
||||
struct vc4_bo *bo;
|
||||
struct vc4_bo *bo = vc4->bin_bo;
|
||||
int bin_bo_slot;
|
||||
struct vc4_exec_info *exec;
|
||||
unsigned long irqflags;
|
||||
|
||||
bo = vc4_bo_create(dev, 256 * 1024, true);
|
||||
if (IS_ERR(bo)) {
|
||||
bin_bo_slot = vc4_v3d_get_bin_slot(vc4);
|
||||
if (bin_bo_slot < 0) {
|
||||
DRM_ERROR("Couldn't allocate binner overflow mem\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* If there's a job executing currently, then our previous
|
||||
* overflow allocation is getting used in that job and we need
|
||||
* to queue it to be released when the job is done. But if no
|
||||
* job is executing at all, then we can free the old overflow
|
||||
* object direcctly.
|
||||
*
|
||||
* No lock necessary for this pointer since we're the only
|
||||
* ones that update the pointer, and our workqueue won't
|
||||
* reenter.
|
||||
*/
|
||||
if (vc4->overflow_mem) {
|
||||
struct vc4_exec_info *current_exec;
|
||||
unsigned long irqflags;
|
||||
spin_lock_irqsave(&vc4->job_lock, irqflags);
|
||||
|
||||
spin_lock_irqsave(&vc4->job_lock, irqflags);
|
||||
current_exec = vc4_first_bin_job(vc4);
|
||||
if (!current_exec)
|
||||
current_exec = vc4_last_render_job(vc4);
|
||||
if (current_exec) {
|
||||
vc4->overflow_mem->seqno = current_exec->seqno;
|
||||
list_add_tail(&vc4->overflow_mem->unref_head,
|
||||
¤t_exec->unref_list);
|
||||
vc4->overflow_mem = NULL;
|
||||
if (vc4->bin_alloc_overflow) {
|
||||
/* If we had overflow memory allocated previously,
|
||||
* then that chunk will free when the current bin job
|
||||
* is done. If we don't have a bin job running, then
|
||||
* the chunk will be done whenever the list of render
|
||||
* jobs has drained.
|
||||
*/
|
||||
exec = vc4_first_bin_job(vc4);
|
||||
if (!exec)
|
||||
exec = vc4_last_render_job(vc4);
|
||||
if (exec) {
|
||||
exec->bin_slots |= vc4->bin_alloc_overflow;
|
||||
} else {
|
||||
/* There's nothing queued in the hardware, so
|
||||
* the old slot is free immediately.
|
||||
*/
|
||||
vc4->bin_alloc_used &= ~vc4->bin_alloc_overflow;
|
||||
}
|
||||
spin_unlock_irqrestore(&vc4->job_lock, irqflags);
|
||||
}
|
||||
vc4->bin_alloc_overflow = BIT(bin_bo_slot);
|
||||
|
||||
if (vc4->overflow_mem)
|
||||
drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base);
|
||||
vc4->overflow_mem = bo;
|
||||
|
||||
V3D_WRITE(V3D_BPOA, bo->base.paddr);
|
||||
V3D_WRITE(V3D_BPOA, bo->base.paddr + bin_bo_slot * vc4->bin_alloc_size);
|
||||
V3D_WRITE(V3D_BPOS, bo->base.base.size);
|
||||
V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM);
|
||||
V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM);
|
||||
spin_unlock_irqrestore(&vc4->job_lock, irqflags);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -142,6 +137,10 @@ vc4_irq_finish_render_job(struct drm_device *dev)
|
||||
|
||||
vc4->finished_seqno++;
|
||||
list_move_tail(&exec->head, &vc4->job_done_list);
|
||||
if (exec->fence) {
|
||||
dma_fence_signal_locked(exec->fence);
|
||||
exec->fence = NULL;
|
||||
}
|
||||
vc4_submit_next_render_job(dev);
|
||||
|
||||
wake_up_all(&vc4->job_wait_queue);
|
||||
|
@ -230,10 +230,12 @@ int vc4_kms_load(struct drm_device *dev)
|
||||
|
||||
drm_mode_config_reset(dev);
|
||||
|
||||
vc4->fbdev = drm_fbdev_cma_init(dev, 32,
|
||||
dev->mode_config.num_connector);
|
||||
if (IS_ERR(vc4->fbdev))
|
||||
vc4->fbdev = NULL;
|
||||
if (dev->mode_config.num_connector) {
|
||||
vc4->fbdev = drm_fbdev_cma_init(dev, 32,
|
||||
dev->mode_config.num_connector);
|
||||
if (IS_ERR(vc4->fbdev))
|
||||
vc4->fbdev = NULL;
|
||||
}
|
||||
|
||||
drm_kms_helper_poll_init(dev);
|
||||
|
||||
|
@ -182,8 +182,7 @@ static void emit_tile(struct vc4_exec_info *exec,
|
||||
|
||||
if (has_bin) {
|
||||
rcl_u8(setup, VC4_PACKET_BRANCH_TO_SUB_LIST);
|
||||
rcl_u32(setup, (exec->tile_bo->paddr +
|
||||
exec->tile_alloc_offset +
|
||||
rcl_u32(setup, (exec->tile_alloc_offset +
|
||||
(y * exec->bin_tiles_x + x) * 32));
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "linux/clk.h"
|
||||
#include "linux/component.h"
|
||||
#include "linux/pm_runtime.h"
|
||||
#include "vc4_drv.h"
|
||||
@ -156,6 +157,144 @@ static void vc4_v3d_init_hw(struct drm_device *dev)
|
||||
V3D_WRITE(V3D_VPMBASE, 0);
|
||||
}
|
||||
|
||||
int vc4_v3d_get_bin_slot(struct vc4_dev *vc4)
|
||||
{
|
||||
struct drm_device *dev = vc4->dev;
|
||||
unsigned long irqflags;
|
||||
int slot;
|
||||
uint64_t seqno = 0;
|
||||
struct vc4_exec_info *exec;
|
||||
|
||||
try_again:
|
||||
spin_lock_irqsave(&vc4->job_lock, irqflags);
|
||||
slot = ffs(~vc4->bin_alloc_used);
|
||||
if (slot != 0) {
|
||||
/* Switch from ffs() bit index to a 0-based index. */
|
||||
slot--;
|
||||
vc4->bin_alloc_used |= BIT(slot);
|
||||
spin_unlock_irqrestore(&vc4->job_lock, irqflags);
|
||||
return slot;
|
||||
}
|
||||
|
||||
/* Couldn't find an open slot. Wait for render to complete
|
||||
* and try again.
|
||||
*/
|
||||
exec = vc4_last_render_job(vc4);
|
||||
if (exec)
|
||||
seqno = exec->seqno;
|
||||
spin_unlock_irqrestore(&vc4->job_lock, irqflags);
|
||||
|
||||
if (seqno) {
|
||||
int ret = vc4_wait_for_seqno(dev, seqno, ~0ull, true);
|
||||
|
||||
if (ret == 0)
|
||||
goto try_again;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* vc4_allocate_bin_bo() - allocates the memory that will be used for
|
||||
* tile binning.
|
||||
*
|
||||
* The binner has a limitation that the addresses in the tile state
|
||||
* buffer that point into the tile alloc buffer or binner overflow
|
||||
* memory only have 28 bits (256MB), and the top 4 on the bus for
|
||||
* tile alloc references end up coming from the tile state buffer's
|
||||
* address.
|
||||
*
|
||||
* To work around this, we allocate a single large buffer while V3D is
|
||||
* in use, make sure that it has the top 4 bits constant across its
|
||||
* entire extent, and then put the tile state, tile alloc, and binner
|
||||
* overflow memory inside that buffer.
|
||||
*
|
||||
* This creates a limitation where we may not be able to execute a job
|
||||
* if it doesn't fit within the buffer that we allocated up front.
|
||||
* However, it turns out that 16MB is "enough for anybody", and
|
||||
* real-world applications run into allocation failures from the
|
||||
* overall CMA pool before they make scenes complicated enough to run
|
||||
* out of bin space.
|
||||
*/
|
||||
int
|
||||
vc4_allocate_bin_bo(struct drm_device *drm)
|
||||
{
|
||||
struct vc4_dev *vc4 = to_vc4_dev(drm);
|
||||
struct vc4_v3d *v3d = vc4->v3d;
|
||||
uint32_t size = 16 * 1024 * 1024;
|
||||
int ret = 0;
|
||||
struct list_head list;
|
||||
|
||||
/* We may need to try allocating more than once to get a BO
|
||||
* that doesn't cross 256MB. Track the ones we've allocated
|
||||
* that failed so far, so that we can free them when we've got
|
||||
* one that succeeded (if we freed them right away, our next
|
||||
* allocation would probably be the same chunk of memory).
|
||||
*/
|
||||
INIT_LIST_HEAD(&list);
|
||||
|
||||
while (true) {
|
||||
struct vc4_bo *bo = vc4_bo_create(drm, size, true);
|
||||
|
||||
if (IS_ERR(bo)) {
|
||||
ret = PTR_ERR(bo);
|
||||
|
||||
dev_err(&v3d->pdev->dev,
|
||||
"Failed to allocate memory for tile binning: "
|
||||
"%d. You may need to enable CMA or give it "
|
||||
"more memory.",
|
||||
ret);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if this BO won't trigger the addressing bug. */
|
||||
if ((bo->base.paddr & 0xf0000000) ==
|
||||
((bo->base.paddr + bo->base.base.size - 1) & 0xf0000000)) {
|
||||
vc4->bin_bo = bo;
|
||||
|
||||
/* Set up for allocating 512KB chunks of
|
||||
* binner memory. The biggest allocation we
|
||||
* need to do is for the initial tile alloc +
|
||||
* tile state buffer. We can render to a
|
||||
* maximum of ((2048*2048) / (32*32) = 4096
|
||||
* tiles in a frame (until we do floating
|
||||
* point rendering, at which point it would be
|
||||
* 8192). Tile state is 48b/tile (rounded to
|
||||
* a page), and tile alloc is 32b/tile
|
||||
* (rounded to a page), plus a page of extra,
|
||||
* for a total of 320kb for our worst-case.
|
||||
* We choose 512kb so that it divides evenly
|
||||
* into our 16MB, and the rest of the 512kb
|
||||
* will be used as storage for the overflow
|
||||
* from the initial 32b CL per bin.
|
||||
*/
|
||||
vc4->bin_alloc_size = 512 * 1024;
|
||||
vc4->bin_alloc_used = 0;
|
||||
vc4->bin_alloc_overflow = 0;
|
||||
WARN_ON_ONCE(sizeof(vc4->bin_alloc_used) * 8 !=
|
||||
bo->base.base.size / vc4->bin_alloc_size);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Put it on the list to free later, and try again. */
|
||||
list_add(&bo->unref_head, &list);
|
||||
}
|
||||
|
||||
/* Free all the BOs we allocated but didn't choose. */
|
||||
while (!list_empty(&list)) {
|
||||
struct vc4_bo *bo = list_last_entry(&list,
|
||||
struct vc4_bo, unref_head);
|
||||
|
||||
list_del(&bo->unref_head);
|
||||
drm_gem_object_put_unlocked(&bo->base.base);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int vc4_v3d_runtime_suspend(struct device *dev)
|
||||
{
|
||||
@ -164,6 +303,11 @@ static int vc4_v3d_runtime_suspend(struct device *dev)
|
||||
|
||||
vc4_irq_uninstall(vc4->dev);
|
||||
|
||||
drm_gem_object_put_unlocked(&vc4->bin_bo->base.base);
|
||||
vc4->bin_bo = NULL;
|
||||
|
||||
clk_disable_unprepare(v3d->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -171,6 +315,15 @@ static int vc4_v3d_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct vc4_v3d *v3d = dev_get_drvdata(dev);
|
||||
struct vc4_dev *vc4 = v3d->vc4;
|
||||
int ret;
|
||||
|
||||
ret = vc4_allocate_bin_bo(vc4->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(v3d->clk);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
vc4_v3d_init_hw(vc4->dev);
|
||||
vc4_irq_postinstall(vc4->dev);
|
||||
@ -202,12 +355,38 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data)
|
||||
vc4->v3d = v3d;
|
||||
v3d->vc4 = vc4;
|
||||
|
||||
v3d->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(v3d->clk)) {
|
||||
int ret = PTR_ERR(v3d->clk);
|
||||
|
||||
if (ret == -ENOENT) {
|
||||
/* bcm2835 didn't have a clock reference in the DT. */
|
||||
ret = 0;
|
||||
v3d->clk = NULL;
|
||||
} else {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to get V3D clock: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (V3D_READ(V3D_IDENT0) != V3D_EXPECTED_IDENT0) {
|
||||
DRM_ERROR("V3D_IDENT0 read 0x%08x instead of 0x%08x\n",
|
||||
V3D_READ(V3D_IDENT0), V3D_EXPECTED_IDENT0);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(v3d->clk);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
ret = vc4_allocate_bin_bo(drm);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(v3d->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Reset the binner overflow address/size at setup, to be sure
|
||||
* we don't reuse an old one.
|
||||
*/
|
||||
@ -271,6 +450,7 @@ static int vc4_v3d_dev_remove(struct platform_device *pdev)
|
||||
|
||||
static const struct of_device_id vc4_v3d_dt_match[] = {
|
||||
{ .compatible = "brcm,bcm2835-v3d" },
|
||||
{ .compatible = "brcm,cygnus-v3d" },
|
||||
{ .compatible = "brcm,vc4-v3d" },
|
||||
{}
|
||||
};
|
||||
|
@ -348,10 +348,11 @@ static int
|
||||
validate_tile_binning_config(VALIDATE_ARGS)
|
||||
{
|
||||
struct drm_device *dev = exec->exec_bo->base.dev;
|
||||
struct vc4_bo *tile_bo;
|
||||
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||||
uint8_t flags;
|
||||
uint32_t tile_state_size, tile_alloc_size;
|
||||
uint32_t tile_count;
|
||||
uint32_t tile_state_size;
|
||||
uint32_t tile_count, bin_addr;
|
||||
int bin_slot;
|
||||
|
||||
if (exec->found_tile_binning_mode_config_packet) {
|
||||
DRM_ERROR("Duplicate VC4_PACKET_TILE_BINNING_MODE_CONFIG\n");
|
||||
@ -377,13 +378,28 @@ validate_tile_binning_config(VALIDATE_ARGS)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bin_slot = vc4_v3d_get_bin_slot(vc4);
|
||||
if (bin_slot < 0) {
|
||||
if (bin_slot != -EINTR && bin_slot != -ERESTARTSYS) {
|
||||
DRM_ERROR("Failed to allocate binner memory: %d\n",
|
||||
bin_slot);
|
||||
}
|
||||
return bin_slot;
|
||||
}
|
||||
|
||||
/* The slot we allocated will only be used by this job, and is
|
||||
* free when the job completes rendering.
|
||||
*/
|
||||
exec->bin_slots |= BIT(bin_slot);
|
||||
bin_addr = vc4->bin_bo->base.paddr + bin_slot * vc4->bin_alloc_size;
|
||||
|
||||
/* The tile state data array is 48 bytes per tile, and we put it at
|
||||
* the start of a BO containing both it and the tile alloc.
|
||||
*/
|
||||
tile_state_size = 48 * tile_count;
|
||||
|
||||
/* Since the tile alloc array will follow us, align. */
|
||||
exec->tile_alloc_offset = roundup(tile_state_size, 4096);
|
||||
exec->tile_alloc_offset = bin_addr + roundup(tile_state_size, 4096);
|
||||
|
||||
*(uint8_t *)(validated + 14) =
|
||||
((flags & ~(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK |
|
||||
@ -394,35 +410,13 @@ validate_tile_binning_config(VALIDATE_ARGS)
|
||||
VC4_SET_FIELD(VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128,
|
||||
VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE));
|
||||
|
||||
/* Initial block size. */
|
||||
tile_alloc_size = 32 * tile_count;
|
||||
|
||||
/*
|
||||
* The initial allocation gets rounded to the next 256 bytes before
|
||||
* the hardware starts fulfilling further allocations.
|
||||
*/
|
||||
tile_alloc_size = roundup(tile_alloc_size, 256);
|
||||
|
||||
/* Add space for the extra allocations. This is what gets used first,
|
||||
* before overflow memory. It must have at least 4096 bytes, but we
|
||||
* want to avoid overflow memory usage if possible.
|
||||
*/
|
||||
tile_alloc_size += 1024 * 1024;
|
||||
|
||||
tile_bo = vc4_bo_create(dev, exec->tile_alloc_offset + tile_alloc_size,
|
||||
true);
|
||||
exec->tile_bo = &tile_bo->base;
|
||||
if (IS_ERR(exec->tile_bo))
|
||||
return PTR_ERR(exec->tile_bo);
|
||||
list_add_tail(&tile_bo->unref_head, &exec->unref_list);
|
||||
|
||||
/* tile alloc address. */
|
||||
*(uint32_t *)(validated + 0) = (exec->tile_bo->paddr +
|
||||
exec->tile_alloc_offset);
|
||||
*(uint32_t *)(validated + 0) = exec->tile_alloc_offset;
|
||||
/* tile alloc size. */
|
||||
*(uint32_t *)(validated + 4) = tile_alloc_size;
|
||||
*(uint32_t *)(validated + 4) = (bin_addr + vc4->bin_alloc_size -
|
||||
exec->tile_alloc_offset);
|
||||
/* tile state address. */
|
||||
*(uint32_t *)(validated + 8) = exec->tile_bo->paddr;
|
||||
*(uint32_t *)(validated + 8) = bin_addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -42,10 +42,20 @@
|
||||
#define DRIVER_MAJOR 1
|
||||
#define DRIVER_MINOR 0
|
||||
|
||||
static struct vgem_device {
|
||||
struct drm_device drm;
|
||||
struct platform_device *platform;
|
||||
} *vgem_device;
|
||||
|
||||
static void vgem_gem_free_object(struct drm_gem_object *obj)
|
||||
{
|
||||
struct drm_vgem_gem_object *vgem_obj = to_vgem_bo(obj);
|
||||
|
||||
drm_free_large(vgem_obj->pages);
|
||||
|
||||
if (obj->import_attach)
|
||||
drm_prime_gem_destroy(obj, vgem_obj->table);
|
||||
|
||||
drm_gem_object_release(obj);
|
||||
kfree(vgem_obj);
|
||||
}
|
||||
@ -56,26 +66,49 @@ static int vgem_gem_fault(struct vm_fault *vmf)
|
||||
struct drm_vgem_gem_object *obj = vma->vm_private_data;
|
||||
/* We don't use vmf->pgoff since that has the fake offset */
|
||||
unsigned long vaddr = vmf->address;
|
||||
struct page *page;
|
||||
int ret;
|
||||
loff_t num_pages;
|
||||
pgoff_t page_offset;
|
||||
page_offset = (vaddr - vma->vm_start) >> PAGE_SHIFT;
|
||||
|
||||
num_pages = DIV_ROUND_UP(obj->base.size, PAGE_SIZE);
|
||||
|
||||
if (page_offset > num_pages)
|
||||
return VM_FAULT_SIGBUS;
|
||||
|
||||
if (obj->pages) {
|
||||
get_page(obj->pages[page_offset]);
|
||||
vmf->page = obj->pages[page_offset];
|
||||
ret = 0;
|
||||
} else {
|
||||
struct page *page;
|
||||
|
||||
page = shmem_read_mapping_page(
|
||||
file_inode(obj->base.filp)->i_mapping,
|
||||
page_offset);
|
||||
if (!IS_ERR(page)) {
|
||||
vmf->page = page;
|
||||
ret = 0;
|
||||
} else switch (PTR_ERR(page)) {
|
||||
case -ENOSPC:
|
||||
case -ENOMEM:
|
||||
ret = VM_FAULT_OOM;
|
||||
break;
|
||||
case -EBUSY:
|
||||
ret = VM_FAULT_RETRY;
|
||||
break;
|
||||
case -EFAULT:
|
||||
case -EINVAL:
|
||||
ret = VM_FAULT_SIGBUS;
|
||||
break;
|
||||
default:
|
||||
WARN_ON(PTR_ERR(page));
|
||||
ret = VM_FAULT_SIGBUS;
|
||||
break;
|
||||
}
|
||||
|
||||
page = shmem_read_mapping_page(file_inode(obj->base.filp)->i_mapping,
|
||||
(vaddr - vma->vm_start) >> PAGE_SHIFT);
|
||||
if (!IS_ERR(page)) {
|
||||
vmf->page = page;
|
||||
return 0;
|
||||
} else switch (PTR_ERR(page)) {
|
||||
case -ENOSPC:
|
||||
case -ENOMEM:
|
||||
return VM_FAULT_OOM;
|
||||
case -EBUSY:
|
||||
return VM_FAULT_RETRY;
|
||||
case -EFAULT:
|
||||
case -EINVAL:
|
||||
return VM_FAULT_SIGBUS;
|
||||
default:
|
||||
WARN_ON_ONCE(PTR_ERR(page));
|
||||
return VM_FAULT_SIGBUS;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct vm_operations_struct vgem_gem_vm_ops = {
|
||||
@ -112,12 +145,8 @@ static void vgem_postclose(struct drm_device *dev, struct drm_file *file)
|
||||
kfree(vfile);
|
||||
}
|
||||
|
||||
/* ioctls */
|
||||
|
||||
static struct drm_gem_object *vgem_gem_create(struct drm_device *dev,
|
||||
struct drm_file *file,
|
||||
unsigned int *handle,
|
||||
unsigned long size)
|
||||
static struct drm_vgem_gem_object *__vgem_gem_create(struct drm_device *dev,
|
||||
unsigned long size)
|
||||
{
|
||||
struct drm_vgem_gem_object *obj;
|
||||
int ret;
|
||||
@ -127,8 +156,31 @@ static struct drm_gem_object *vgem_gem_create(struct drm_device *dev,
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = drm_gem_object_init(dev, &obj->base, roundup(size, PAGE_SIZE));
|
||||
if (ret)
|
||||
goto err_free;
|
||||
if (ret) {
|
||||
kfree(obj);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static void __vgem_gem_destroy(struct drm_vgem_gem_object *obj)
|
||||
{
|
||||
drm_gem_object_release(&obj->base);
|
||||
kfree(obj);
|
||||
}
|
||||
|
||||
static struct drm_gem_object *vgem_gem_create(struct drm_device *dev,
|
||||
struct drm_file *file,
|
||||
unsigned int *handle,
|
||||
unsigned long size)
|
||||
{
|
||||
struct drm_vgem_gem_object *obj;
|
||||
int ret;
|
||||
|
||||
obj = __vgem_gem_create(dev, size);
|
||||
if (IS_ERR(obj))
|
||||
return ERR_CAST(obj);
|
||||
|
||||
ret = drm_gem_handle_create(file, &obj->base, handle);
|
||||
drm_gem_object_unreference_unlocked(&obj->base);
|
||||
@ -137,9 +189,8 @@ static struct drm_gem_object *vgem_gem_create(struct drm_device *dev,
|
||||
|
||||
return &obj->base;
|
||||
|
||||
err_free:
|
||||
kfree(obj);
|
||||
err:
|
||||
__vgem_gem_destroy(obj);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
@ -256,6 +307,37 @@ static struct sg_table *vgem_prime_get_sg_table(struct drm_gem_object *obj)
|
||||
return st;
|
||||
}
|
||||
|
||||
static struct drm_gem_object* vgem_prime_import(struct drm_device *dev,
|
||||
struct dma_buf *dma_buf)
|
||||
{
|
||||
struct vgem_device *vgem = container_of(dev, typeof(*vgem), drm);
|
||||
|
||||
return drm_gem_prime_import_dev(dev, dma_buf, &vgem->platform->dev);
|
||||
}
|
||||
|
||||
static struct drm_gem_object *vgem_prime_import_sg_table(struct drm_device *dev,
|
||||
struct dma_buf_attachment *attach, struct sg_table *sg)
|
||||
{
|
||||
struct drm_vgem_gem_object *obj;
|
||||
int npages;
|
||||
|
||||
obj = __vgem_gem_create(dev, attach->dmabuf->size);
|
||||
if (IS_ERR(obj))
|
||||
return ERR_CAST(obj);
|
||||
|
||||
npages = PAGE_ALIGN(attach->dmabuf->size) / PAGE_SIZE;
|
||||
|
||||
obj->table = sg;
|
||||
obj->pages = drm_malloc_ab(npages, sizeof(struct page *));
|
||||
if (!obj->pages) {
|
||||
__vgem_gem_destroy(obj);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
drm_prime_sg_to_page_addr_arrays(obj->table, obj->pages, NULL,
|
||||
npages);
|
||||
return &obj->base;
|
||||
}
|
||||
|
||||
static void *vgem_prime_vmap(struct drm_gem_object *obj)
|
||||
{
|
||||
long n_pages = obj->size >> PAGE_SHIFT;
|
||||
@ -300,8 +382,19 @@ static int vgem_prime_mmap(struct drm_gem_object *obj,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vgem_release(struct drm_device *dev)
|
||||
{
|
||||
struct vgem_device *vgem = container_of(dev, typeof(*vgem), drm);
|
||||
|
||||
platform_device_unregister(vgem->platform);
|
||||
drm_dev_fini(&vgem->drm);
|
||||
|
||||
kfree(vgem);
|
||||
}
|
||||
|
||||
static struct drm_driver vgem_driver = {
|
||||
.driver_features = DRIVER_GEM | DRIVER_PRIME,
|
||||
.release = vgem_release,
|
||||
.open = vgem_open,
|
||||
.postclose = vgem_postclose,
|
||||
.gem_free_object_unlocked = vgem_gem_free_object,
|
||||
@ -314,8 +407,11 @@ static struct drm_driver vgem_driver = {
|
||||
.dumb_map_offset = vgem_gem_dumb_map,
|
||||
|
||||
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
|
||||
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
|
||||
.gem_prime_pin = vgem_prime_pin,
|
||||
.gem_prime_import = vgem_prime_import,
|
||||
.gem_prime_export = drm_gem_prime_export,
|
||||
.gem_prime_import_sg_table = vgem_prime_import_sg_table,
|
||||
.gem_prime_get_sg_table = vgem_prime_get_sg_table,
|
||||
.gem_prime_vmap = vgem_prime_vmap,
|
||||
.gem_prime_vunmap = vgem_prime_vunmap,
|
||||
@ -328,34 +424,48 @@ static struct drm_driver vgem_driver = {
|
||||
.minor = DRIVER_MINOR,
|
||||
};
|
||||
|
||||
static struct drm_device *vgem_device;
|
||||
|
||||
static int __init vgem_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
vgem_device = drm_dev_alloc(&vgem_driver, NULL);
|
||||
if (IS_ERR(vgem_device)) {
|
||||
ret = PTR_ERR(vgem_device);
|
||||
goto out;
|
||||
vgem_device = kzalloc(sizeof(*vgem_device), GFP_KERNEL);
|
||||
if (!vgem_device)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = drm_dev_init(&vgem_device->drm, &vgem_driver, NULL);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
vgem_device->platform =
|
||||
platform_device_register_simple("vgem", -1, NULL, 0);
|
||||
if (!vgem_device->platform) {
|
||||
ret = -ENODEV;
|
||||
goto out_fini;
|
||||
}
|
||||
|
||||
ret = drm_dev_register(vgem_device, 0);
|
||||
dma_coerce_mask_and_coherent(&vgem_device->platform->dev,
|
||||
DMA_BIT_MASK(64));
|
||||
|
||||
/* Final step: expose the device/driver to userspace */
|
||||
ret = drm_dev_register(&vgem_device->drm, 0);
|
||||
if (ret)
|
||||
goto out_unref;
|
||||
goto out_unregister;
|
||||
|
||||
return 0;
|
||||
|
||||
out_unref:
|
||||
drm_dev_unref(vgem_device);
|
||||
out:
|
||||
out_unregister:
|
||||
platform_device_unregister(vgem_device->platform);
|
||||
out_fini:
|
||||
drm_dev_fini(&vgem_device->drm);
|
||||
out_free:
|
||||
kfree(vgem_device);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit vgem_exit(void)
|
||||
{
|
||||
drm_dev_unregister(vgem_device);
|
||||
drm_dev_unref(vgem_device);
|
||||
drm_dev_unregister(&vgem_device->drm);
|
||||
drm_dev_unref(&vgem_device->drm);
|
||||
}
|
||||
|
||||
module_init(vgem_init);
|
||||
|
@ -43,6 +43,8 @@ struct vgem_file {
|
||||
#define to_vgem_bo(x) container_of(x, struct drm_vgem_gem_object, base)
|
||||
struct drm_vgem_gem_object {
|
||||
struct drm_gem_object base;
|
||||
struct page **pages;
|
||||
struct sg_table *table;
|
||||
};
|
||||
|
||||
int vgem_fence_open(struct vgem_file *file);
|
||||
|
@ -3,6 +3,7 @@ zxdrm-y := \
|
||||
zx_hdmi.o \
|
||||
zx_plane.o \
|
||||
zx_tvenc.o \
|
||||
zx_vga.o \
|
||||
zx_vou.o
|
||||
|
||||
obj-$(CONFIG_DRM_ZTE) += zxdrm.o
|
||||
|
31
drivers/gpu/drm/zte/zx_common_regs.h
Normal file
31
drivers/gpu/drm/zte/zx_common_regs.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Sanechips Technology Co., Ltd.
|
||||
* Copyright 2017 Linaro Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __ZX_COMMON_REGS_H__
|
||||
#define __ZX_COMMON_REGS_H__
|
||||
|
||||
/* CSC registers */
|
||||
#define CSC_CTRL0 0x30
|
||||
#define CSC_COV_MODE_SHIFT 16
|
||||
#define CSC_COV_MODE_MASK (0xffff << CSC_COV_MODE_SHIFT)
|
||||
#define CSC_BT601_IMAGE_RGB2YCBCR 0
|
||||
#define CSC_BT601_IMAGE_YCBCR2RGB 1
|
||||
#define CSC_BT601_VIDEO_RGB2YCBCR 2
|
||||
#define CSC_BT601_VIDEO_YCBCR2RGB 3
|
||||
#define CSC_BT709_IMAGE_RGB2YCBCR 4
|
||||
#define CSC_BT709_IMAGE_YCBCR2RGB 5
|
||||
#define CSC_BT709_VIDEO_RGB2YCBCR 6
|
||||
#define CSC_BT709_VIDEO_YCBCR2RGB 7
|
||||
#define CSC_BT2020_IMAGE_RGB2YCBCR 8
|
||||
#define CSC_BT2020_IMAGE_YCBCR2RGB 9
|
||||
#define CSC_BT2020_VIDEO_RGB2YCBCR 10
|
||||
#define CSC_BT2020_VIDEO_YCBCR2RGB 11
|
||||
#define CSC_WORK_ENABLE BIT(0)
|
||||
|
||||
#endif /* __ZX_COMMON_REGS_H__ */
|
@ -233,6 +233,7 @@ static struct platform_driver *drivers[] = {
|
||||
&zx_crtc_driver,
|
||||
&zx_hdmi_driver,
|
||||
&zx_tvenc_driver,
|
||||
&zx_vga_driver,
|
||||
&zx_drm_platform_driver,
|
||||
};
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
extern struct platform_driver zx_crtc_driver;
|
||||
extern struct platform_driver zx_hdmi_driver;
|
||||
extern struct platform_driver zx_tvenc_driver;
|
||||
extern struct platform_driver zx_vga_driver;
|
||||
|
||||
static inline u32 zx_readl(void __iomem *reg)
|
||||
{
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drmP.h>
|
||||
|
||||
#include "zx_common_regs.h"
|
||||
#include "zx_drm_drv.h"
|
||||
#include "zx_plane.h"
|
||||
#include "zx_plane_regs.h"
|
||||
|
@ -77,24 +77,6 @@
|
||||
#define LUMA_STRIDE(x) (((x) << LUMA_STRIDE_SHIFT) & LUMA_STRIDE_MASK)
|
||||
#define CHROMA_STRIDE(x) (((x) << CHROMA_STRIDE_SHIFT) & CHROMA_STRIDE_MASK)
|
||||
|
||||
/* CSC registers */
|
||||
#define CSC_CTRL0 0x30
|
||||
#define CSC_COV_MODE_SHIFT 16
|
||||
#define CSC_COV_MODE_MASK (0xffff << CSC_COV_MODE_SHIFT)
|
||||
#define CSC_BT601_IMAGE_RGB2YCBCR 0
|
||||
#define CSC_BT601_IMAGE_YCBCR2RGB 1
|
||||
#define CSC_BT601_VIDEO_RGB2YCBCR 2
|
||||
#define CSC_BT601_VIDEO_YCBCR2RGB 3
|
||||
#define CSC_BT709_IMAGE_RGB2YCBCR 4
|
||||
#define CSC_BT709_IMAGE_YCBCR2RGB 5
|
||||
#define CSC_BT709_VIDEO_RGB2YCBCR 6
|
||||
#define CSC_BT709_VIDEO_YCBCR2RGB 7
|
||||
#define CSC_BT2020_IMAGE_RGB2YCBCR 8
|
||||
#define CSC_BT2020_IMAGE_YCBCR2RGB 9
|
||||
#define CSC_BT2020_VIDEO_RGB2YCBCR 10
|
||||
#define CSC_BT2020_VIDEO_YCBCR2RGB 11
|
||||
#define CSC_WORK_ENABLE BIT(0)
|
||||
|
||||
/* RSZ registers */
|
||||
#define RSZ_SRC_CFG 0x00
|
||||
#define RSZ_DEST_CFG 0x04
|
||||
|
531
drivers/gpu/drm/zte/zx_vga.c
Normal file
531
drivers/gpu/drm/zte/zx_vga.c
Normal file
@ -0,0 +1,531 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Sanechips Technology Co., Ltd.
|
||||
* Copyright 2017 Linaro Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drmP.h>
|
||||
|
||||
#include "zx_drm_drv.h"
|
||||
#include "zx_vga_regs.h"
|
||||
#include "zx_vou.h"
|
||||
|
||||
struct zx_vga_pwrctrl {
|
||||
struct regmap *regmap;
|
||||
u32 reg;
|
||||
u32 mask;
|
||||
};
|
||||
|
||||
struct zx_vga_i2c {
|
||||
struct i2c_adapter adap;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
struct zx_vga {
|
||||
struct drm_connector connector;
|
||||
struct drm_encoder encoder;
|
||||
struct zx_vga_i2c *ddc;
|
||||
struct device *dev;
|
||||
void __iomem *mmio;
|
||||
struct clk *i2c_wclk;
|
||||
struct zx_vga_pwrctrl pwrctrl;
|
||||
struct completion complete;
|
||||
bool connected;
|
||||
};
|
||||
|
||||
#define to_zx_vga(x) container_of(x, struct zx_vga, x)
|
||||
|
||||
static void zx_vga_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct zx_vga *vga = to_zx_vga(encoder);
|
||||
struct zx_vga_pwrctrl *pwrctrl = &vga->pwrctrl;
|
||||
|
||||
/* Set bit to power up VGA DACs */
|
||||
regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask,
|
||||
pwrctrl->mask);
|
||||
|
||||
vou_inf_enable(VOU_VGA, encoder->crtc);
|
||||
}
|
||||
|
||||
static void zx_vga_encoder_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct zx_vga *vga = to_zx_vga(encoder);
|
||||
struct zx_vga_pwrctrl *pwrctrl = &vga->pwrctrl;
|
||||
|
||||
vou_inf_disable(VOU_VGA, encoder->crtc);
|
||||
|
||||
/* Clear bit to power down VGA DACs */
|
||||
regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask, 0);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs zx_vga_encoder_helper_funcs = {
|
||||
.enable = zx_vga_encoder_enable,
|
||||
.disable = zx_vga_encoder_disable,
|
||||
};
|
||||
|
||||
static const struct drm_encoder_funcs zx_vga_encoder_funcs = {
|
||||
.destroy = drm_encoder_cleanup,
|
||||
};
|
||||
|
||||
static int zx_vga_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct zx_vga *vga = to_zx_vga(connector);
|
||||
struct edid *edid;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Clear both detection bits to switch I2C bus from device
|
||||
* detecting to EDID reading.
|
||||
*/
|
||||
zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, 0);
|
||||
|
||||
edid = drm_get_edid(connector, &vga->ddc->adap);
|
||||
if (!edid) {
|
||||
/*
|
||||
* If EDID reading fails, we set the device state into
|
||||
* disconnected. Locking is not required here, since the
|
||||
* VGA_AUTO_DETECT_SEL register write in irq handler cannot
|
||||
* be triggered when both detection bits are cleared as above.
|
||||
*/
|
||||
zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL,
|
||||
VGA_DETECT_SEL_NO_DEVICE);
|
||||
vga->connected = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* As edid reading succeeds, device must be connected, so we set
|
||||
* up detection bit for unplug interrupt here.
|
||||
*/
|
||||
zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, VGA_DETECT_SEL_HAS_DEVICE);
|
||||
|
||||
drm_mode_connector_update_edid_property(connector, edid);
|
||||
ret = drm_add_edid_modes(connector, edid);
|
||||
kfree(edid);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum drm_mode_status
|
||||
zx_vga_connector_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static struct drm_connector_helper_funcs zx_vga_connector_helper_funcs = {
|
||||
.get_modes = zx_vga_connector_get_modes,
|
||||
.mode_valid = zx_vga_connector_mode_valid,
|
||||
};
|
||||
|
||||
static enum drm_connector_status
|
||||
zx_vga_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct zx_vga *vga = to_zx_vga(connector);
|
||||
|
||||
return vga->connected ? connector_status_connected :
|
||||
connector_status_disconnected;
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs zx_vga_connector_funcs = {
|
||||
.dpms = drm_atomic_helper_connector_dpms,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.detect = zx_vga_connector_detect,
|
||||
.destroy = drm_connector_cleanup,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static int zx_vga_register(struct drm_device *drm, struct zx_vga *vga)
|
||||
{
|
||||
struct drm_encoder *encoder = &vga->encoder;
|
||||
struct drm_connector *connector = &vga->connector;
|
||||
struct device *dev = vga->dev;
|
||||
int ret;
|
||||
|
||||
encoder->possible_crtcs = VOU_CRTC_MASK;
|
||||
|
||||
ret = drm_encoder_init(drm, encoder, &zx_vga_encoder_funcs,
|
||||
DRM_MODE_ENCODER_DAC, NULL);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "failed to init encoder: %d\n", ret);
|
||||
return ret;
|
||||
};
|
||||
|
||||
drm_encoder_helper_add(encoder, &zx_vga_encoder_helper_funcs);
|
||||
|
||||
vga->connector.polled = DRM_CONNECTOR_POLL_HPD;
|
||||
|
||||
ret = drm_connector_init(drm, connector, &zx_vga_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_VGA);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "failed to init connector: %d\n", ret);
|
||||
goto clean_encoder;
|
||||
};
|
||||
|
||||
drm_connector_helper_add(connector, &zx_vga_connector_helper_funcs);
|
||||
|
||||
ret = drm_mode_connector_attach_encoder(connector, encoder);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "failed to attach encoder: %d\n", ret);
|
||||
goto clean_connector;
|
||||
};
|
||||
|
||||
return 0;
|
||||
|
||||
clean_connector:
|
||||
drm_connector_cleanup(connector);
|
||||
clean_encoder:
|
||||
drm_encoder_cleanup(encoder);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int zx_vga_pwrctrl_init(struct zx_vga *vga)
|
||||
{
|
||||
struct zx_vga_pwrctrl *pwrctrl = &vga->pwrctrl;
|
||||
struct device *dev = vga->dev;
|
||||
struct of_phandle_args out_args;
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
|
||||
ret = of_parse_phandle_with_fixed_args(dev->of_node,
|
||||
"zte,vga-power-control", 2, 0, &out_args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap = syscon_node_to_regmap(out_args.np);
|
||||
if (IS_ERR(regmap)) {
|
||||
ret = PTR_ERR(regmap);
|
||||
goto out;
|
||||
}
|
||||
|
||||
pwrctrl->regmap = regmap;
|
||||
pwrctrl->reg = out_args.args[0];
|
||||
pwrctrl->mask = out_args.args[1];
|
||||
|
||||
out:
|
||||
of_node_put(out_args.np);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int zx_vga_i2c_read(struct zx_vga *vga, struct i2c_msg *msg)
|
||||
{
|
||||
int len = msg->len;
|
||||
u8 *buf = msg->buf;
|
||||
u32 offset = 0;
|
||||
int i;
|
||||
|
||||
reinit_completion(&vga->complete);
|
||||
|
||||
/* Select combo write */
|
||||
zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_COMBO, VGA_CMD_COMBO);
|
||||
zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_RW, 0);
|
||||
|
||||
while (len > 0) {
|
||||
u32 cnt;
|
||||
|
||||
/* Clear RX FIFO */
|
||||
zx_writel_mask(vga->mmio + VGA_RXF_CTRL, VGA_RX_FIFO_CLEAR,
|
||||
VGA_RX_FIFO_CLEAR);
|
||||
|
||||
/* Data offset to read from */
|
||||
zx_writel(vga->mmio + VGA_SUB_ADDR, offset);
|
||||
|
||||
/* Kick off the transfer */
|
||||
zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_TRANS,
|
||||
VGA_CMD_TRANS);
|
||||
|
||||
if (!wait_for_completion_timeout(&vga->complete,
|
||||
msecs_to_jiffies(1000))) {
|
||||
DRM_DEV_ERROR(vga->dev, "transfer timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
cnt = zx_readl(vga->mmio + VGA_RXF_STATUS);
|
||||
cnt = (cnt & VGA_RXF_COUNT_MASK) >> VGA_RXF_COUNT_SHIFT;
|
||||
/* FIFO status may report more data than we need to read */
|
||||
cnt = min_t(u32, len, cnt);
|
||||
|
||||
for (i = 0; i < cnt; i++)
|
||||
*buf++ = zx_readl(vga->mmio + VGA_DATA);
|
||||
|
||||
len -= cnt;
|
||||
offset += cnt;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zx_vga_i2c_write(struct zx_vga *vga, struct i2c_msg *msg)
|
||||
{
|
||||
/*
|
||||
* The DDC I2C adapter is only for reading EDID data, so we assume
|
||||
* that the write to this adapter must be the EDID data offset.
|
||||
*/
|
||||
if ((msg->len != 1) || ((msg->addr != DDC_ADDR)))
|
||||
return -EINVAL;
|
||||
|
||||
/* Hardware will take care of the slave address shifting */
|
||||
zx_writel(vga->mmio + VGA_DEVICE_ADDR, msg->addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zx_vga_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||
int num)
|
||||
{
|
||||
struct zx_vga *vga = i2c_get_adapdata(adap);
|
||||
struct zx_vga_i2c *ddc = vga->ddc;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
mutex_lock(&ddc->lock);
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
if (msgs[i].flags & I2C_M_RD)
|
||||
ret = zx_vga_i2c_read(vga, &msgs[i]);
|
||||
else
|
||||
ret = zx_vga_i2c_write(vga, &msgs[i]);
|
||||
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
ret = num;
|
||||
|
||||
mutex_unlock(&ddc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 zx_vga_i2c_func(struct i2c_adapter *adapter)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm zx_vga_algorithm = {
|
||||
.master_xfer = zx_vga_i2c_xfer,
|
||||
.functionality = zx_vga_i2c_func,
|
||||
};
|
||||
|
||||
static int zx_vga_ddc_register(struct zx_vga *vga)
|
||||
{
|
||||
struct device *dev = vga->dev;
|
||||
struct i2c_adapter *adap;
|
||||
struct zx_vga_i2c *ddc;
|
||||
int ret;
|
||||
|
||||
ddc = devm_kzalloc(dev, sizeof(*ddc), GFP_KERNEL);
|
||||
if (!ddc)
|
||||
return -ENOMEM;
|
||||
|
||||
vga->ddc = ddc;
|
||||
mutex_init(&ddc->lock);
|
||||
|
||||
adap = &ddc->adap;
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->class = I2C_CLASS_DDC;
|
||||
adap->dev.parent = dev;
|
||||
adap->algo = &zx_vga_algorithm;
|
||||
snprintf(adap->name, sizeof(adap->name), "zx vga i2c");
|
||||
|
||||
ret = i2c_add_adapter(adap);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "failed to add I2C adapter: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c_set_adapdata(adap, vga);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t zx_vga_irq_thread(int irq, void *dev_id)
|
||||
{
|
||||
struct zx_vga *vga = dev_id;
|
||||
|
||||
drm_helper_hpd_irq_event(vga->connector.dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t zx_vga_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct zx_vga *vga = dev_id;
|
||||
u32 status;
|
||||
|
||||
status = zx_readl(vga->mmio + VGA_I2C_STATUS);
|
||||
|
||||
/* Clear interrupt status */
|
||||
zx_writel_mask(vga->mmio + VGA_I2C_STATUS, VGA_CLEAR_IRQ,
|
||||
VGA_CLEAR_IRQ);
|
||||
|
||||
if (status & VGA_DEVICE_CONNECTED) {
|
||||
/*
|
||||
* Since VGA_DETECT_SEL bits need to be reset for switching DDC
|
||||
* bus from device detection to EDID read, rather than setting
|
||||
* up HAS_DEVICE bit here, we need to do that in .get_modes
|
||||
* hook for unplug detecting after EDID read succeeds.
|
||||
*/
|
||||
vga->connected = true;
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
if (status & VGA_DEVICE_DISCONNECTED) {
|
||||
zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL,
|
||||
VGA_DETECT_SEL_NO_DEVICE);
|
||||
vga->connected = false;
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
if (status & VGA_TRANS_DONE) {
|
||||
complete(&vga->complete);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static void zx_vga_hw_init(struct zx_vga *vga)
|
||||
{
|
||||
unsigned long ref = clk_get_rate(vga->i2c_wclk);
|
||||
int div;
|
||||
|
||||
/*
|
||||
* Set up I2C fast speed divider per formula below to get 400kHz.
|
||||
* scl = ref / ((div + 1) * 4)
|
||||
*/
|
||||
div = DIV_ROUND_UP(ref / 1000, 400 * 4) - 1;
|
||||
zx_writel(vga->mmio + VGA_CLK_DIV_FS, div);
|
||||
|
||||
/* Set up device detection */
|
||||
zx_writel(vga->mmio + VGA_AUTO_DETECT_PARA, 0x80);
|
||||
zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, VGA_DETECT_SEL_NO_DEVICE);
|
||||
|
||||
/*
|
||||
* We need to poke monitor via DDC bus to get connection irq
|
||||
* start working.
|
||||
*/
|
||||
zx_writel(vga->mmio + VGA_DEVICE_ADDR, DDC_ADDR);
|
||||
zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_TRANS, VGA_CMD_TRANS);
|
||||
}
|
||||
|
||||
static int zx_vga_bind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct drm_device *drm = data;
|
||||
struct resource *res;
|
||||
struct zx_vga *vga;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
vga = devm_kzalloc(dev, sizeof(*vga), GFP_KERNEL);
|
||||
if (!vga)
|
||||
return -ENOMEM;
|
||||
|
||||
vga->dev = dev;
|
||||
dev_set_drvdata(dev, vga);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
vga->mmio = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(vga->mmio))
|
||||
return PTR_ERR(vga->mmio);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
vga->i2c_wclk = devm_clk_get(dev, "i2c_wclk");
|
||||
if (IS_ERR(vga->i2c_wclk)) {
|
||||
ret = PTR_ERR(vga->i2c_wclk);
|
||||
DRM_DEV_ERROR(dev, "failed to get i2c_wclk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = zx_vga_pwrctrl_init(vga);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "failed to init power control: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = zx_vga_ddc_register(vga);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "failed to register ddc: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = zx_vga_register(drm, vga);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "failed to register vga: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
init_completion(&vga->complete);
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, zx_vga_irq_handler,
|
||||
zx_vga_irq_thread, IRQF_SHARED,
|
||||
dev_name(dev), vga);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "failed to request threaded irq: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(vga->i2c_wclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
zx_vga_hw_init(vga);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void zx_vga_unbind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct zx_vga *vga = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(vga->i2c_wclk);
|
||||
}
|
||||
|
||||
static const struct component_ops zx_vga_component_ops = {
|
||||
.bind = zx_vga_bind,
|
||||
.unbind = zx_vga_unbind,
|
||||
};
|
||||
|
||||
static int zx_vga_probe(struct platform_device *pdev)
|
||||
{
|
||||
return component_add(&pdev->dev, &zx_vga_component_ops);
|
||||
}
|
||||
|
||||
static int zx_vga_remove(struct platform_device *pdev)
|
||||
{
|
||||
component_del(&pdev->dev, &zx_vga_component_ops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id zx_vga_of_match[] = {
|
||||
{ .compatible = "zte,zx296718-vga", },
|
||||
{ /* end */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, zx_vga_of_match);
|
||||
|
||||
struct platform_driver zx_vga_driver = {
|
||||
.probe = zx_vga_probe,
|
||||
.remove = zx_vga_remove,
|
||||
.driver = {
|
||||
.name = "zx-vga",
|
||||
.of_match_table = zx_vga_of_match,
|
||||
},
|
||||
};
|
36
drivers/gpu/drm/zte/zx_vga_regs.h
Normal file
36
drivers/gpu/drm/zte/zx_vga_regs.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Sanechips Technology Co., Ltd.
|
||||
* Copyright 2017 Linaro Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __ZX_VGA_REGS_H__
|
||||
#define __ZX_VGA_REGS_H__
|
||||
|
||||
#define VGA_CMD_CFG 0x04
|
||||
#define VGA_CMD_TRANS BIT(6)
|
||||
#define VGA_CMD_COMBO BIT(5)
|
||||
#define VGA_CMD_RW BIT(4)
|
||||
#define VGA_SUB_ADDR 0x0c
|
||||
#define VGA_DEVICE_ADDR 0x10
|
||||
#define VGA_CLK_DIV_FS 0x14
|
||||
#define VGA_RXF_CTRL 0x20
|
||||
#define VGA_RX_FIFO_CLEAR BIT(7)
|
||||
#define VGA_DATA 0x24
|
||||
#define VGA_I2C_STATUS 0x28
|
||||
#define VGA_DEVICE_DISCONNECTED BIT(7)
|
||||
#define VGA_DEVICE_CONNECTED BIT(6)
|
||||
#define VGA_CLEAR_IRQ BIT(4)
|
||||
#define VGA_TRANS_DONE BIT(0)
|
||||
#define VGA_RXF_STATUS 0x30
|
||||
#define VGA_RXF_COUNT_SHIFT 2
|
||||
#define VGA_RXF_COUNT_MASK GENMASK(7, 2)
|
||||
#define VGA_AUTO_DETECT_PARA 0x34
|
||||
#define VGA_AUTO_DETECT_SEL 0x38
|
||||
#define VGA_DETECT_SEL_HAS_DEVICE BIT(1)
|
||||
#define VGA_DETECT_SEL_NO_DEVICE BIT(0)
|
||||
|
||||
#endif /* __ZX_VGA_REGS_H__ */
|
@ -23,6 +23,7 @@
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drmP.h>
|
||||
|
||||
#include "zx_common_regs.h"
|
||||
#include "zx_drm_drv.h"
|
||||
#include "zx_plane.h"
|
||||
#include "zx_vou.h"
|
||||
@ -122,6 +123,8 @@ struct zx_crtc {
|
||||
struct drm_plane *primary;
|
||||
struct zx_vou_hw *vou;
|
||||
void __iomem *chnreg;
|
||||
void __iomem *chncsc;
|
||||
void __iomem *dither;
|
||||
const struct zx_crtc_regs *regs;
|
||||
const struct zx_crtc_bits *bits;
|
||||
enum vou_chn_type chn_type;
|
||||
@ -204,6 +207,11 @@ static struct vou_inf vou_infs[] = {
|
||||
.clocks_en_bits = BIT(15),
|
||||
.clocks_sel_bits = BIT(11) | BIT(0),
|
||||
},
|
||||
[VOU_VGA] = {
|
||||
.data_sel = VOU_RGB_888,
|
||||
.clocks_en_bits = BIT(1),
|
||||
.clocks_sel_bits = BIT(10),
|
||||
},
|
||||
};
|
||||
|
||||
static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc)
|
||||
@ -227,9 +235,26 @@ void vou_inf_enable(enum vou_inf_id id, struct drm_crtc *crtc)
|
||||
struct zx_crtc *zcrtc = to_zx_crtc(crtc);
|
||||
struct zx_vou_hw *vou = zcrtc->vou;
|
||||
struct vou_inf *inf = &vou_infs[id];
|
||||
void __iomem *dither = zcrtc->dither;
|
||||
void __iomem *csc = zcrtc->chncsc;
|
||||
bool is_main = zcrtc->chn_type == VOU_CHN_MAIN;
|
||||
u32 data_sel_shift = id << 1;
|
||||
|
||||
if (inf->data_sel != VOU_YUV444) {
|
||||
/* Enable channel CSC for RGB output */
|
||||
zx_writel_mask(csc + CSC_CTRL0, CSC_COV_MODE_MASK,
|
||||
CSC_BT709_IMAGE_YCBCR2RGB << CSC_COV_MODE_SHIFT);
|
||||
zx_writel_mask(csc + CSC_CTRL0, CSC_WORK_ENABLE,
|
||||
CSC_WORK_ENABLE);
|
||||
|
||||
/* Bypass Dither block for RGB output */
|
||||
zx_writel_mask(dither + OSD_DITHER_CTRL0, DITHER_BYSPASS,
|
||||
DITHER_BYSPASS);
|
||||
} else {
|
||||
zx_writel_mask(csc + CSC_CTRL0, CSC_WORK_ENABLE, 0);
|
||||
zx_writel_mask(dither + OSD_DITHER_CTRL0, DITHER_BYSPASS, 0);
|
||||
}
|
||||
|
||||
/* Select data format */
|
||||
zx_writel_mask(vou->vouctl + VOU_INF_DATA_SEL, 0x3 << data_sel_shift,
|
||||
inf->data_sel << data_sel_shift);
|
||||
@ -525,20 +550,24 @@ static int zx_crtc_init(struct drm_device *drm, struct zx_vou_hw *vou,
|
||||
|
||||
if (chn_type == VOU_CHN_MAIN) {
|
||||
zplane->layer = vou->osd + MAIN_GL_OFFSET;
|
||||
zplane->csc = vou->osd + MAIN_CSC_OFFSET;
|
||||
zplane->csc = vou->osd + MAIN_GL_CSC_OFFSET;
|
||||
zplane->hbsc = vou->osd + MAIN_HBSC_OFFSET;
|
||||
zplane->rsz = vou->otfppu + MAIN_RSZ_OFFSET;
|
||||
zplane->bits = &zx_gl_bits[0];
|
||||
zcrtc->chnreg = vou->osd + OSD_MAIN_CHN;
|
||||
zcrtc->chncsc = vou->osd + MAIN_CHN_CSC_OFFSET;
|
||||
zcrtc->dither = vou->osd + MAIN_DITHER_OFFSET;
|
||||
zcrtc->regs = &main_crtc_regs;
|
||||
zcrtc->bits = &main_crtc_bits;
|
||||
} else {
|
||||
zplane->layer = vou->osd + AUX_GL_OFFSET;
|
||||
zplane->csc = vou->osd + AUX_CSC_OFFSET;
|
||||
zplane->csc = vou->osd + AUX_GL_CSC_OFFSET;
|
||||
zplane->hbsc = vou->osd + AUX_HBSC_OFFSET;
|
||||
zplane->rsz = vou->otfppu + AUX_RSZ_OFFSET;
|
||||
zplane->bits = &zx_gl_bits[1];
|
||||
zcrtc->chnreg = vou->osd + OSD_AUX_CHN;
|
||||
zcrtc->chncsc = vou->osd + AUX_CHN_CSC_OFFSET;
|
||||
zcrtc->dither = vou->osd + AUX_DITHER_OFFSET;
|
||||
zcrtc->regs = &aux_crtc_regs;
|
||||
zcrtc->bits = &aux_crtc_bits;
|
||||
}
|
||||
@ -705,9 +734,6 @@ static void vou_hw_init(struct zx_vou_hw *vou)
|
||||
/* Release reset for all VOU modules */
|
||||
zx_writel(vou->vouctl + VOU_SOFT_RST, ~0);
|
||||
|
||||
/* Enable clock auto-gating for all VOU modules */
|
||||
zx_writel(vou->vouctl + VOU_CLK_REQEN, ~0);
|
||||
|
||||
/* Enable all VOU module clocks */
|
||||
zx_writel(vou->vouctl + VOU_CLK_EN, ~0);
|
||||
|
||||
|
@ -13,13 +13,17 @@
|
||||
|
||||
/* Sub-module offset */
|
||||
#define MAIN_GL_OFFSET 0x130
|
||||
#define MAIN_CSC_OFFSET 0x580
|
||||
#define MAIN_GL_CSC_OFFSET 0x580
|
||||
#define MAIN_CHN_CSC_OFFSET 0x6c0
|
||||
#define MAIN_HBSC_OFFSET 0x820
|
||||
#define MAIN_DITHER_OFFSET 0x960
|
||||
#define MAIN_RSZ_OFFSET 0x600 /* OTFPPU sub-module */
|
||||
|
||||
#define AUX_GL_OFFSET 0x200
|
||||
#define AUX_CSC_OFFSET 0x5d0
|
||||
#define AUX_GL_CSC_OFFSET 0x5d0
|
||||
#define AUX_CHN_CSC_OFFSET 0x710
|
||||
#define AUX_HBSC_OFFSET 0x860
|
||||
#define AUX_DITHER_OFFSET 0x970
|
||||
#define AUX_RSZ_OFFSET 0x800
|
||||
|
||||
#define OSD_VL0_OFFSET 0x040
|
||||
@ -78,6 +82,10 @@
|
||||
#define CHN_INTERLACE_BUF_CTRL 0x24
|
||||
#define CHN_INTERLACE_EN BIT(2)
|
||||
|
||||
/* Dither registers */
|
||||
#define OSD_DITHER_CTRL0 0x00
|
||||
#define DITHER_BYSPASS BIT(31)
|
||||
|
||||
/* TIMING_CTRL registers */
|
||||
#define TIMING_TC_ENABLE 0x04
|
||||
#define AUX_TC_EN BIT(1)
|
||||
|
@ -320,15 +320,6 @@ struct pci_controller;
|
||||
#define DRM_IF_VERSION(maj, min) (maj << 16 | min)
|
||||
|
||||
|
||||
/* Flags and return codes for get_vblank_timestamp() driver function. */
|
||||
#define DRM_CALLED_FROM_VBLIRQ 1
|
||||
#define DRM_VBLANKTIME_SCANOUTPOS_METHOD (1 << 0)
|
||||
|
||||
/* get_scanout_position() return flags */
|
||||
#define DRM_SCANOUTPOS_VALID (1 << 0)
|
||||
#define DRM_SCANOUTPOS_IN_VBLANK (1 << 1)
|
||||
#define DRM_SCANOUTPOS_ACCURATE (1 << 2)
|
||||
|
||||
/**
|
||||
* DRM device structure. This structure represent a complete card that
|
||||
* may contain multiple heads.
|
||||
|
@ -154,6 +154,53 @@ struct __drm_connnectors_state {
|
||||
struct drm_connector_state *state, *old_state, *new_state;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct drm_private_state_funcs - atomic state functions for private objects
|
||||
*
|
||||
* These hooks are used by atomic helpers to create, swap and destroy states of
|
||||
* private objects. The structure itself is used as a vtable to identify the
|
||||
* associated private object type. Each private object type that needs to be
|
||||
* added to the atomic states is expected to have an implementation of these
|
||||
* hooks and pass a pointer to it's drm_private_state_funcs struct to
|
||||
* drm_atomic_get_private_obj_state().
|
||||
*/
|
||||
struct drm_private_state_funcs {
|
||||
/**
|
||||
* @duplicate_state:
|
||||
*
|
||||
* Duplicate the current state of the private object and return it. It
|
||||
* is an error to call this before obj->state has been initialized.
|
||||
*
|
||||
* RETURNS:
|
||||
*
|
||||
* Duplicated atomic state or NULL when obj->state is not
|
||||
* initialized or allocation failed.
|
||||
*/
|
||||
void *(*duplicate_state)(struct drm_atomic_state *state, void *obj);
|
||||
|
||||
/**
|
||||
* @swap_state:
|
||||
*
|
||||
* This function swaps the existing state of a private object @obj with
|
||||
* it's newly created state, the pointer to which is passed as
|
||||
* @obj_state_ptr.
|
||||
*/
|
||||
void (*swap_state)(void *obj, void **obj_state_ptr);
|
||||
|
||||
/**
|
||||
* @destroy_state:
|
||||
*
|
||||
* Frees the private object state created with @duplicate_state.
|
||||
*/
|
||||
void (*destroy_state)(void *obj_state);
|
||||
};
|
||||
|
||||
struct __drm_private_objs_state {
|
||||
void *obj;
|
||||
void *obj_state;
|
||||
const struct drm_private_state_funcs *funcs;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct drm_atomic_state - the global state object for atomic updates
|
||||
* @ref: count of all references to this state (will not be freed until zero)
|
||||
@ -164,6 +211,8 @@ struct __drm_connnectors_state {
|
||||
* @crtcs: pointer to array of CRTC pointers
|
||||
* @num_connector: size of the @connectors and @connector_states arrays
|
||||
* @connectors: pointer to array of structures with per-connector data
|
||||
* @num_private_objs: size of the @private_objs array
|
||||
* @private_objs: pointer to array of private object pointers
|
||||
* @acquire_ctx: acquire context for this atomic modeset state update
|
||||
*/
|
||||
struct drm_atomic_state {
|
||||
@ -176,6 +225,8 @@ struct drm_atomic_state {
|
||||
struct __drm_crtcs_state *crtcs;
|
||||
int num_connector;
|
||||
struct __drm_connnectors_state *connectors;
|
||||
int num_private_objs;
|
||||
struct __drm_private_objs_state *private_objs;
|
||||
|
||||
struct drm_modeset_acquire_ctx *acquire_ctx;
|
||||
|
||||
@ -268,6 +319,11 @@ int drm_atomic_connector_set_property(struct drm_connector *connector,
|
||||
struct drm_connector_state *state, struct drm_property *property,
|
||||
uint64_t val);
|
||||
|
||||
void * __must_check
|
||||
drm_atomic_get_private_obj_state(struct drm_atomic_state *state,
|
||||
void *obj,
|
||||
const struct drm_private_state_funcs *funcs);
|
||||
|
||||
/**
|
||||
* drm_atomic_get_existing_crtc_state - get crtc state, if it exists
|
||||
* @state: global atomic state object
|
||||
@ -752,6 +808,45 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p);
|
||||
(__i)++) \
|
||||
for_each_if (plane)
|
||||
|
||||
/**
|
||||
* __for_each_private_obj - iterate over all private objects
|
||||
* @__state: &struct drm_atomic_state pointer
|
||||
* @obj: private object iteration cursor
|
||||
* @obj_state: private object state iteration cursor
|
||||
* @__i: int iteration cursor, for macro-internal use
|
||||
* @__funcs: &struct drm_private_state_funcs iteration cursor
|
||||
*
|
||||
* This macro iterates over the array containing private object data in atomic
|
||||
* state
|
||||
*/
|
||||
#define __for_each_private_obj(__state, obj, obj_state, __i, __funcs) \
|
||||
for ((__i) = 0; \
|
||||
(__i) < (__state)->num_private_objs && \
|
||||
((obj) = (__state)->private_objs[__i].obj, \
|
||||
(__funcs) = (__state)->private_objs[__i].funcs, \
|
||||
(obj_state) = (__state)->private_objs[__i].obj_state, \
|
||||
1); \
|
||||
(__i)++) \
|
||||
|
||||
/**
|
||||
* for_each_private_obj - iterate over a specify type of private object
|
||||
* @__state: &struct drm_atomic_state pointer
|
||||
* @obj_funcs: &struct drm_private_state_funcs function table to filter
|
||||
* private objects
|
||||
* @obj: private object iteration cursor
|
||||
* @obj_state: private object state iteration cursor
|
||||
* @__i: int iteration cursor, for macro-internal use
|
||||
* @__funcs: &struct drm_private_state_funcs iteration cursor
|
||||
*
|
||||
* This macro iterates over the private objects state array while filtering the
|
||||
* objects based on the vfunc table that is passed as @obj_funcs. New macros
|
||||
* can be created by passing in the vfunc table associated with a specific
|
||||
* private object.
|
||||
*/
|
||||
#define for_each_private_obj(__state, obj_funcs, obj, obj_state, __i, __funcs) \
|
||||
__for_each_private_obj(__state, obj, obj_state, __i, __funcs) \
|
||||
for_each_if (__funcs == obj_funcs)
|
||||
|
||||
/**
|
||||
* drm_atomic_crtc_needs_modeset - compute combined modeset need
|
||||
* @state: &drm_crtc_state for the CRTC
|
||||
|
@ -28,6 +28,7 @@
|
||||
|
||||
struct drm_device;
|
||||
struct drm_atomic_state;
|
||||
struct drm_plane;
|
||||
|
||||
/*
|
||||
* Rotation property bits. DRM_ROTATE_<degrees> rotates the image by the
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
#include <linux/ctype.h>
|
||||
|
||||
struct drm_crtc;
|
||||
|
||||
uint32_t drm_color_lut_extract(uint32_t user_input, uint32_t bit_precision);
|
||||
|
||||
void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc,
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/hdmi.h>
|
||||
#include <drm/drm_mode_object.h>
|
||||
|
||||
#include <uapi/drm/drm_mode.h>
|
||||
@ -326,6 +327,21 @@ struct drm_connector_state {
|
||||
struct drm_atomic_state *state;
|
||||
|
||||
struct drm_tv_connector_state tv;
|
||||
|
||||
/**
|
||||
* @picture_aspect_ratio: Connector property to control the
|
||||
* HDMI infoframe aspect ratio setting.
|
||||
*
|
||||
* The %DRM_MODE_PICTURE_ASPECT_\* values much match the
|
||||
* values for &enum hdmi_picture_aspect
|
||||
*/
|
||||
enum hdmi_picture_aspect picture_aspect_ratio;
|
||||
|
||||
/**
|
||||
* @scaling_mode: Connector property to control the
|
||||
* upscaling, mostly used for built-in panels.
|
||||
*/
|
||||
unsigned int scaling_mode;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -675,6 +691,7 @@ struct drm_cmdline_mode {
|
||||
* @tile_v_loc: vertical location of this tile
|
||||
* @tile_h_size: horizontal size of this tile.
|
||||
* @tile_v_size: vertical size of this tile.
|
||||
* @scaling_mode_property: Optional atomic property to control the upscaling.
|
||||
*
|
||||
* Each connector may be connected to one or more CRTCs, or may be clonable by
|
||||
* another connector if they can share a CRTC. Each connector also has a specific
|
||||
@ -754,6 +771,8 @@ struct drm_connector {
|
||||
struct drm_property_blob *edid_blob_ptr;
|
||||
struct drm_object_properties properties;
|
||||
|
||||
struct drm_property *scaling_mode_property;
|
||||
|
||||
/**
|
||||
* @path_blob_ptr:
|
||||
*
|
||||
@ -953,6 +972,8 @@ int drm_mode_create_tv_properties(struct drm_device *dev,
|
||||
unsigned int num_modes,
|
||||
const char * const modes[]);
|
||||
int drm_mode_create_scaling_mode_property(struct drm_device *dev);
|
||||
int drm_connector_attach_scaling_mode_property(struct drm_connector *connector,
|
||||
u32 scaling_mode_mask);
|
||||
int drm_mode_create_aspect_ratio_property(struct drm_device *dev);
|
||||
int drm_mode_create_suggested_offset_properties(struct drm_device *dev);
|
||||
|
||||
|
@ -93,11 +93,6 @@ struct drm_plane_helper_funcs;
|
||||
* @adjusted_mode: for use by helpers and drivers to compute adjusted mode timings
|
||||
* @mode: current mode timings
|
||||
* @mode_blob: &drm_property_blob for @mode
|
||||
* @degamma_lut: Lookup table for converting framebuffer pixel data
|
||||
* before apply the conversion matrix
|
||||
* @ctm: Transformation matrix
|
||||
* @gamma_lut: Lookup table for converting pixel data after the
|
||||
* conversion matrix
|
||||
* @state: backpointer to global drm_atomic_state
|
||||
*
|
||||
* Note that the distinction between @enable and @active is rather subtile:
|
||||
@ -144,9 +139,30 @@ struct drm_crtc_state {
|
||||
/* blob property to expose current mode to atomic userspace */
|
||||
struct drm_property_blob *mode_blob;
|
||||
|
||||
/* blob property to expose color management to userspace */
|
||||
/**
|
||||
* @degamma_lut:
|
||||
*
|
||||
* Lookup table for converting framebuffer pixel data before apply the
|
||||
* color conversion matrix @ctm. See drm_crtc_enable_color_mgmt(). The
|
||||
* blob (if not NULL) is an array of &struct drm_color_lut.
|
||||
*/
|
||||
struct drm_property_blob *degamma_lut;
|
||||
|
||||
/**
|
||||
* @ctm:
|
||||
*
|
||||
* Color transformation matrix. See drm_crtc_enable_color_mgmt(). The
|
||||
* blob (if not NULL) is a &struct drm_color_ctm.
|
||||
*/
|
||||
struct drm_property_blob *ctm;
|
||||
|
||||
/**
|
||||
* @gamma_lut:
|
||||
*
|
||||
* Lookup table for converting pixel data after the color conversion
|
||||
* matrix @ctm. See drm_crtc_enable_color_mgmt(). The blob (if not
|
||||
* NULL) is an array of &struct drm_color_lut.
|
||||
*/
|
||||
struct drm_property_blob *gamma_lut;
|
||||
|
||||
/**
|
||||
@ -313,6 +329,12 @@ struct drm_crtc_funcs {
|
||||
*
|
||||
* This callback is optional.
|
||||
*
|
||||
* Atomic drivers who want to support gamma tables should implement the
|
||||
* atomic color management support, enabled by calling
|
||||
* drm_crtc_enable_color_mgmt(), which then supports the legacy gamma
|
||||
* interface through the drm_atomic_helper_legacy_gamma_set()
|
||||
* compatibility implementation.
|
||||
*
|
||||
* NOTE:
|
||||
*
|
||||
* Drivers that support gamma tables and also fbdev emulation through
|
||||
|
@ -179,6 +179,111 @@
|
||||
|
||||
#define DP_GUID 0x030 /* 1.2 */
|
||||
|
||||
#define DP_DSC_SUPPORT 0x060 /* DP 1.4 */
|
||||
# define DP_DSC_DECOMPRESSION_IS_SUPPORTED (1 << 0)
|
||||
|
||||
#define DP_DSC_REV 0x061
|
||||
# define DP_DSC_MAJOR_MASK (0xf << 0)
|
||||
# define DP_DSC_MINOR_MASK (0xf << 4)
|
||||
# define DP_DSC_MAJOR_SHIFT 0
|
||||
# define DP_DSC_MINOR_SHIFT 4
|
||||
|
||||
#define DP_DSC_RC_BUF_BLK_SIZE 0x062
|
||||
# define DP_DSC_RC_BUF_BLK_SIZE_1 0x0
|
||||
# define DP_DSC_RC_BUF_BLK_SIZE_4 0x1
|
||||
# define DP_DSC_RC_BUF_BLK_SIZE_16 0x2
|
||||
# define DP_DSC_RC_BUF_BLK_SIZE_64 0x3
|
||||
|
||||
#define DP_DSC_RC_BUF_SIZE 0x063
|
||||
|
||||
#define DP_DSC_SLICE_CAP_1 0x064
|
||||
# define DP_DSC_1_PER_DP_DSC_SINK (1 << 0)
|
||||
# define DP_DSC_2_PER_DP_DSC_SINK (1 << 1)
|
||||
# define DP_DSC_4_PER_DP_DSC_SINK (1 << 3)
|
||||
# define DP_DSC_6_PER_DP_DSC_SINK (1 << 4)
|
||||
# define DP_DSC_8_PER_DP_DSC_SINK (1 << 5)
|
||||
# define DP_DSC_10_PER_DP_DSC_SINK (1 << 6)
|
||||
# define DP_DSC_12_PER_DP_DSC_SINK (1 << 7)
|
||||
|
||||
#define DP_DSC_LINE_BUF_BIT_DEPTH 0x065
|
||||
# define DP_DSC_LINE_BUF_BIT_DEPTH_MASK (0xf << 0)
|
||||
# define DP_DSC_LINE_BUF_BIT_DEPTH_9 0x0
|
||||
# define DP_DSC_LINE_BUF_BIT_DEPTH_10 0x1
|
||||
# define DP_DSC_LINE_BUF_BIT_DEPTH_11 0x2
|
||||
# define DP_DSC_LINE_BUF_BIT_DEPTH_12 0x3
|
||||
# define DP_DSC_LINE_BUF_BIT_DEPTH_13 0x4
|
||||
# define DP_DSC_LINE_BUF_BIT_DEPTH_14 0x5
|
||||
# define DP_DSC_LINE_BUF_BIT_DEPTH_15 0x6
|
||||
# define DP_DSC_LINE_BUF_BIT_DEPTH_16 0x7
|
||||
# define DP_DSC_LINE_BUF_BIT_DEPTH_8 0x8
|
||||
|
||||
#define DP_DSC_BLK_PREDICTION_SUPPORT 0x066
|
||||
# define DP_DSC_BLK_PREDICTION_IS_SUPPORTED (1 << 0)
|
||||
|
||||
#define DP_DSC_MAX_BITS_PER_PIXEL_LOW 0x067 /* eDP 1.4 */
|
||||
|
||||
#define DP_DSC_MAX_BITS_PER_PIXEL_HI 0x068 /* eDP 1.4 */
|
||||
|
||||
#define DP_DSC_DEC_COLOR_FORMAT_CAP 0x069
|
||||
# define DP_DSC_RGB (1 << 0)
|
||||
# define DP_DSC_YCbCr444 (1 << 1)
|
||||
# define DP_DSC_YCbCr422_Simple (1 << 2)
|
||||
# define DP_DSC_YCbCr422_Native (1 << 3)
|
||||
# define DP_DSC_YCbCr420_Native (1 << 4)
|
||||
|
||||
#define DP_DSC_DEC_COLOR_DEPTH_CAP 0x06A
|
||||
# define DP_DSC_8_BPC (1 << 1)
|
||||
# define DP_DSC_10_BPC (1 << 2)
|
||||
# define DP_DSC_12_BPC (1 << 3)
|
||||
|
||||
#define DP_DSC_PEAK_THROUGHPUT 0x06B
|
||||
# define DP_DSC_THROUGHPUT_MODE_0_MASK (0xf << 0)
|
||||
# define DP_DSC_THROUGHPUT_MODE_0_SHIFT 0
|
||||
# define DP_DSC_THROUGHPUT_MODE_0_340 (1 << 0)
|
||||
# define DP_DSC_THROUGHPUT_MODE_0_400 (2 << 0)
|
||||
# define DP_DSC_THROUGHPUT_MODE_0_450 (3 << 0)
|
||||
# define DP_DSC_THROUGHPUT_MODE_0_500 (4 << 0)
|
||||
# define DP_DSC_THROUGHPUT_MODE_0_550 (5 << 0)
|
||||
# define DP_DSC_THROUGHPUT_MODE_0_600 (6 << 0)
|
||||
# define DP_DSC_THROUGHPUT_MODE_0_650 (7 << 0)
|
||||
# define DP_DSC_THROUGHPUT_MODE_0_700 (8 << 0)
|
||||
# define DP_DSC_THROUGHPUT_MODE_0_750 (9 << 0)
|
||||
# define DP_DSC_THROUGHPUT_MODE_0_800 (10 << 0)
|
||||
# define DP_DSC_THROUGHPUT_MODE_0_850 (11 << 0)
|
||||
# define DP_DSC_THROUGHPUT_MODE_0_900 (12 << 0)
|
||||
# define DP_DSC_THROUGHPUT_MODE_0_950 (13 << 0)
|
||||
# define DP_DSC_THROUGHPUT_MODE_0_1000 (14 << 0)
|
||||
# define DP_DSC_THROUGHPUT_MODE_1_MASK (0xf << 4)
|
||||
# define DP_DSC_THROUGHPUT_MODE_1_SHIFT 4
|
||||
# define DP_DSC_THROUGHPUT_MODE_1_340 (1 << 4)
|
||||
# define DP_DSC_THROUGHPUT_MODE_1_400 (2 << 4)
|
||||
# define DP_DSC_THROUGHPUT_MODE_1_450 (3 << 4)
|
||||
# define DP_DSC_THROUGHPUT_MODE_1_500 (4 << 4)
|
||||
# define DP_DSC_THROUGHPUT_MODE_1_550 (5 << 4)
|
||||
# define DP_DSC_THROUGHPUT_MODE_1_600 (6 << 4)
|
||||
# define DP_DSC_THROUGHPUT_MODE_1_650 (7 << 4)
|
||||
# define DP_DSC_THROUGHPUT_MODE_1_700 (8 << 4)
|
||||
# define DP_DSC_THROUGHPUT_MODE_1_750 (9 << 4)
|
||||
# define DP_DSC_THROUGHPUT_MODE_1_800 (10 << 4)
|
||||
# define DP_DSC_THROUGHPUT_MODE_1_850 (11 << 4)
|
||||
# define DP_DSC_THROUGHPUT_MODE_1_900 (12 << 4)
|
||||
# define DP_DSC_THROUGHPUT_MODE_1_950 (13 << 4)
|
||||
# define DP_DSC_THROUGHPUT_MODE_1_1000 (14 << 4)
|
||||
|
||||
#define DP_DSC_MAX_SLICE_WIDTH 0x06C
|
||||
|
||||
#define DP_DSC_SLICE_CAP_2 0x06D
|
||||
# define DP_DSC_16_PER_DP_DSC_SINK (1 << 0)
|
||||
# define DP_DSC_20_PER_DP_DSC_SINK (1 << 1)
|
||||
# define DP_DSC_24_PER_DP_DSC_SINK (1 << 2)
|
||||
|
||||
#define DP_DSC_BITS_PER_PIXEL_INC 0x06F
|
||||
# define DP_DSC_BITS_PER_PIXEL_1_16 0x0
|
||||
# define DP_DSC_BITS_PER_PIXEL_1_8 0x1
|
||||
# define DP_DSC_BITS_PER_PIXEL_1_4 0x2
|
||||
# define DP_DSC_BITS_PER_PIXEL_1_2 0x3
|
||||
# define DP_DSC_BITS_PER_PIXEL_1 0x4
|
||||
|
||||
#define DP_PSR_SUPPORT 0x070 /* XXX 1.2? */
|
||||
# define DP_PSR_IS_SUPPORTED 1
|
||||
# define DP_PSR2_IS_SUPPORTED 2 /* eDP 1.4 */
|
||||
@ -339,6 +444,8 @@
|
||||
#define DP_AUX_FRAME_SYNC_VALUE 0x15c /* eDP 1.4 */
|
||||
# define DP_AUX_FRAME_SYNC_VALID (1 << 0)
|
||||
|
||||
#define DP_DSC_ENABLE 0x160 /* DP 1.4 */
|
||||
|
||||
#define DP_PSR_EN_CFG 0x170 /* XXX 1.2? */
|
||||
# define DP_PSR_ENABLE (1 << 0)
|
||||
# define DP_PSR_MAIN_LINK_ACTIVE (1 << 1)
|
||||
@ -603,6 +710,9 @@
|
||||
#define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 0x2003 /* 1.2 */
|
||||
|
||||
#define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI1 0x2004 /* 1.2 */
|
||||
# define DP_RX_GTC_MSTR_REQ_STATUS_CHANGE (1 << 0)
|
||||
# define DP_LOCK_ACQUISITION_REQUEST (1 << 1)
|
||||
# define DP_CEC_IRQ (1 << 2)
|
||||
|
||||
#define DP_LINK_SERVICE_IRQ_VECTOR_ESI0 0x2005 /* 1.2 */
|
||||
|
||||
@ -636,6 +746,62 @@
|
||||
# define DP_VSC_EXT_CEA_SDP_SUPPORTED (1 << 6) /* DP 1.4 */
|
||||
# define DP_VSC_EXT_CEA_SDP_CHAINING_SUPPORTED (1 << 7) /* DP 1.4 */
|
||||
|
||||
/* HDMI CEC tunneling over AUX DP 1.3 section 5.3.3.3.1 DPCD 1.4+ */
|
||||
#define DP_CEC_TUNNELING_CAPABILITY 0x3000
|
||||
# define DP_CEC_TUNNELING_CAPABLE (1 << 0)
|
||||
# define DP_CEC_SNOOPING_CAPABLE (1 << 1)
|
||||
# define DP_CEC_MULTIPLE_LA_CAPABLE (1 << 2)
|
||||
|
||||
#define DP_CEC_TUNNELING_CONTROL 0x3001
|
||||
# define DP_CEC_TUNNELING_ENABLE (1 << 0)
|
||||
# define DP_CEC_SNOOPING_ENABLE (1 << 1)
|
||||
|
||||
#define DP_CEC_RX_MESSAGE_INFO 0x3002
|
||||
# define DP_CEC_RX_MESSAGE_LEN_MASK (0xf << 0)
|
||||
# define DP_CEC_RX_MESSAGE_LEN_SHIFT 0
|
||||
# define DP_CEC_RX_MESSAGE_HPD_STATE (1 << 4)
|
||||
# define DP_CEC_RX_MESSAGE_HPD_LOST (1 << 5)
|
||||
# define DP_CEC_RX_MESSAGE_ACKED (1 << 6)
|
||||
# define DP_CEC_RX_MESSAGE_ENDED (1 << 7)
|
||||
|
||||
#define DP_CEC_TX_MESSAGE_INFO 0x3003
|
||||
# define DP_CEC_TX_MESSAGE_LEN_MASK (0xf << 0)
|
||||
# define DP_CEC_TX_MESSAGE_LEN_SHIFT 0
|
||||
# define DP_CEC_TX_RETRY_COUNT_MASK (0x7 << 4)
|
||||
# define DP_CEC_TX_RETRY_COUNT_SHIFT 4
|
||||
# define DP_CEC_TX_MESSAGE_SEND (1 << 7)
|
||||
|
||||
#define DP_CEC_TUNNELING_IRQ_FLAGS 0x3004
|
||||
# define DP_CEC_RX_MESSAGE_INFO_VALID (1 << 0)
|
||||
# define DP_CEC_RX_MESSAGE_OVERFLOW (1 << 1)
|
||||
# define DP_CEC_TX_MESSAGE_SENT (1 << 4)
|
||||
# define DP_CEC_TX_LINE_ERROR (1 << 5)
|
||||
# define DP_CEC_TX_ADDRESS_NACK_ERROR (1 << 6)
|
||||
# define DP_CEC_TX_DATA_NACK_ERROR (1 << 7)
|
||||
|
||||
#define DP_CEC_LOGICAL_ADDRESS_MASK 0x300E /* 0x300F word */
|
||||
# define DP_CEC_LOGICAL_ADDRESS_0 (1 << 0)
|
||||
# define DP_CEC_LOGICAL_ADDRESS_1 (1 << 1)
|
||||
# define DP_CEC_LOGICAL_ADDRESS_2 (1 << 2)
|
||||
# define DP_CEC_LOGICAL_ADDRESS_3 (1 << 3)
|
||||
# define DP_CEC_LOGICAL_ADDRESS_4 (1 << 4)
|
||||
# define DP_CEC_LOGICAL_ADDRESS_5 (1 << 5)
|
||||
# define DP_CEC_LOGICAL_ADDRESS_6 (1 << 6)
|
||||
# define DP_CEC_LOGICAL_ADDRESS_7 (1 << 7)
|
||||
#define DP_CEC_LOGICAL_ADDRESS_MASK_2 0x300F /* 0x300E word */
|
||||
# define DP_CEC_LOGICAL_ADDRESS_8 (1 << 0)
|
||||
# define DP_CEC_LOGICAL_ADDRESS_9 (1 << 1)
|
||||
# define DP_CEC_LOGICAL_ADDRESS_10 (1 << 2)
|
||||
# define DP_CEC_LOGICAL_ADDRESS_11 (1 << 3)
|
||||
# define DP_CEC_LOGICAL_ADDRESS_12 (1 << 4)
|
||||
# define DP_CEC_LOGICAL_ADDRESS_13 (1 << 5)
|
||||
# define DP_CEC_LOGICAL_ADDRESS_14 (1 << 6)
|
||||
# define DP_CEC_LOGICAL_ADDRESS_15 (1 << 7)
|
||||
|
||||
#define DP_CEC_RX_MESSAGE_BUFFER 0x3010
|
||||
#define DP_CEC_TX_MESSAGE_BUFFER 0x3020
|
||||
#define DP_CEC_MESSAGE_BUFFER_LENGTH 0x10
|
||||
|
||||
/* DP 1.2 Sideband message defines */
|
||||
/* peer device type - DP 1.2a Table 2-92 */
|
||||
#define DP_PEER_DEVICE_NONE 0x0
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user