Merge branch 'drm-next' of git://people.freedesktop.org/~robclark/linux into drm-next
Merge the MSM driver from Rob Clark * 'drm-next' of git://people.freedesktop.org/~robclark/linux: drm/msm: add basic hangcheck/recovery mechanism drm/msm: add a3xx gpu support drm/msm: add register definitions for gpu drm/msm: basic KMS driver for snapdragon drm/msm: add register definitions
This commit is contained in:
Коммит
e906d7bdd3
|
@ -223,3 +223,5 @@ source "drivers/gpu/drm/omapdrm/Kconfig"
|
|||
source "drivers/gpu/drm/tilcdc/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/qxl/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/msm/Kconfig"
|
||||
|
|
|
@ -54,4 +54,5 @@ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
|
|||
obj-$(CONFIG_DRM_OMAP) += omapdrm/
|
||||
obj-$(CONFIG_DRM_TILCDC) += tilcdc/
|
||||
obj-$(CONFIG_DRM_QXL) += qxl/
|
||||
obj-$(CONFIG_DRM_MSM) += msm/
|
||||
obj-y += i2c/
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
config DRM_MSM
|
||||
tristate "MSM DRM"
|
||||
depends on DRM
|
||||
depends on ARCH_MSM
|
||||
depends on ARCH_MSM8960
|
||||
select DRM_KMS_HELPER
|
||||
select SHMEM
|
||||
select TMPFS
|
||||
default y
|
||||
help
|
||||
DRM/KMS driver for MSM/snapdragon.
|
||||
|
||||
config DRM_MSM_FBDEV
|
||||
bool "Enable legacy fbdev support for MSM modesetting driver"
|
||||
depends on DRM_MSM
|
||||
select FB_SYS_FILLRECT
|
||||
select FB_SYS_COPYAREA
|
||||
select FB_SYS_IMAGEBLIT
|
||||
select FB_SYS_FOPS
|
||||
default y
|
||||
help
|
||||
Choose this option if you have a need for the legacy fbdev
|
||||
support. Note that this support also provide the linux console
|
||||
support on top of the MSM modesetting driver.
|
||||
|
||||
config DRM_MSM_REGISTER_LOGGING
|
||||
bool "MSM DRM register logging"
|
||||
depends on DRM_MSM
|
||||
default n
|
||||
help
|
||||
Compile in support for logging register reads/writes in a format
|
||||
that can be parsed by envytools demsm tool. If enabled, register
|
||||
logging can be switched on via msm.reglog=y module param.
|
|
@ -0,0 +1,30 @@
|
|||
ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/msm
|
||||
ifeq (, $(findstring -W,$(EXTRA_CFLAGS)))
|
||||
ccflags-y += -Werror
|
||||
endif
|
||||
|
||||
msm-y := \
|
||||
adreno/adreno_gpu.o \
|
||||
adreno/a3xx_gpu.o \
|
||||
hdmi/hdmi.o \
|
||||
hdmi/hdmi_connector.o \
|
||||
hdmi/hdmi_i2c.o \
|
||||
hdmi/hdmi_phy_8960.o \
|
||||
hdmi/hdmi_phy_8x60.o \
|
||||
mdp4/mdp4_crtc.o \
|
||||
mdp4/mdp4_dtv_encoder.o \
|
||||
mdp4/mdp4_format.o \
|
||||
mdp4/mdp4_irq.o \
|
||||
mdp4/mdp4_kms.o \
|
||||
mdp4/mdp4_plane.o \
|
||||
msm_connector.o \
|
||||
msm_drv.o \
|
||||
msm_fb.o \
|
||||
msm_gem.o \
|
||||
msm_gem_submit.o \
|
||||
msm_gpu.o \
|
||||
msm_ringbuffer.o
|
||||
|
||||
msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o
|
||||
|
||||
obj-$(CONFIG_DRM_MSM) += msm.o
|
|
@ -0,0 +1,69 @@
|
|||
NOTES about msm drm/kms driver:
|
||||
|
||||
In the current snapdragon SoC's, we have (at least) 3 different
|
||||
display controller blocks at play:
|
||||
+ MDP3 - ?? seems to be what is on geeksphone peak device
|
||||
+ MDP4 - S3 (APQ8060, touchpad), S4-pro (APQ8064, nexus4 & ifc6410)
|
||||
+ MDSS - snapdragon 800
|
||||
|
||||
(I don't have a completely clear picture on which display controller
|
||||
maps to which part #)
|
||||
|
||||
Plus a handful of blocks around them for HDMI/DSI/etc output.
|
||||
|
||||
And on gpu side of things:
|
||||
+ zero, one, or two 2d cores (z180)
|
||||
+ and either a2xx or a3xx 3d core.
|
||||
|
||||
But, HDMI/DSI/etc blocks seem like they can be shared across multiple
|
||||
display controller blocks. And I for sure don't want to have to deal
|
||||
with N different kms devices from xf86-video-freedreno. Plus, it
|
||||
seems like we can do some clever tricks like use GPU to trigger
|
||||
pageflip after rendering completes (ie. have the kms/crtc code build
|
||||
up gpu cmdstream to update scanout and write FLUSH register after).
|
||||
|
||||
So, the approach is one drm driver, with some modularity. Different
|
||||
'struct msm_kms' implementations, depending on display controller.
|
||||
And one or more 'struct msm_gpu' for the various different gpu sub-
|
||||
modules.
|
||||
|
||||
(Second part is not implemented yet. So far this is just basic KMS
|
||||
driver, and not exposing any custom ioctls to userspace for now.)
|
||||
|
||||
The kms module provides the plane, crtc, and encoder objects, and
|
||||
loads whatever connectors are appropriate.
|
||||
|
||||
For MDP4, the mapping is:
|
||||
|
||||
plane -> PIPE{RGBn,VGn} \
|
||||
crtc -> OVLP{n} + DMA{P,S,E} (??) |-> MDP "device"
|
||||
encoder -> DTV/LCDC/DSI (within MDP4) /
|
||||
connector -> HDMI/DSI/etc --> other device(s)
|
||||
|
||||
Since the irq's that drm core mostly cares about are vblank/framedone,
|
||||
we'll let msm_mdp4_kms provide the irq install/uninstall/etc functions
|
||||
and treat the MDP4 block's irq as "the" irq. Even though the connectors
|
||||
may have their own irqs which they install themselves. For this reason
|
||||
the display controller is the "master" device.
|
||||
|
||||
Each connector probably ends up being a separate device, just for the
|
||||
logistics of finding/mapping io region, irq, etc. Idealy we would
|
||||
have a better way than just stashing the platform device in a global
|
||||
(ie. like DT super-node.. but I don't have any snapdragon hw yet that
|
||||
is using DT).
|
||||
|
||||
Note that so far I've not been able to get any docs on the hw, and it
|
||||
seems that access to such docs would prevent me from working on the
|
||||
freedreno gallium driver. So there may be some mistakes in register
|
||||
names (I had to invent a few, since no sufficient hint was given in
|
||||
the downstream android fbdev driver), bitfield sizes, etc. My current
|
||||
state of understanding the registers is given in the envytools rnndb
|
||||
files at:
|
||||
|
||||
https://github.com/freedreno/envytools/tree/master/rnndb
|
||||
(the mdp4/hdmi/dsi directories)
|
||||
|
||||
These files are used both for a parser tool (in the same tree) to
|
||||
parse logged register reads/writes (both from downstream android fbdev
|
||||
driver, and this driver with register logging enabled), as well as to
|
||||
generate the register level headers.
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,502 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "a3xx_gpu.h"
|
||||
|
||||
#define A3XX_INT0_MASK \
|
||||
(A3XX_INT0_RBBM_AHB_ERROR | \
|
||||
A3XX_INT0_RBBM_ATB_BUS_OVERFLOW | \
|
||||
A3XX_INT0_CP_T0_PACKET_IN_IB | \
|
||||
A3XX_INT0_CP_OPCODE_ERROR | \
|
||||
A3XX_INT0_CP_RESERVED_BIT_ERROR | \
|
||||
A3XX_INT0_CP_HW_FAULT | \
|
||||
A3XX_INT0_CP_IB1_INT | \
|
||||
A3XX_INT0_CP_IB2_INT | \
|
||||
A3XX_INT0_CP_RB_INT | \
|
||||
A3XX_INT0_CP_REG_PROTECT_FAULT | \
|
||||
A3XX_INT0_CP_AHB_ERROR_HALT | \
|
||||
A3XX_INT0_UCHE_OOB_ACCESS)
|
||||
|
||||
static struct platform_device *a3xx_pdev;
|
||||
|
||||
static void a3xx_me_init(struct msm_gpu *gpu)
|
||||
{
|
||||
struct msm_ringbuffer *ring = gpu->rb;
|
||||
|
||||
OUT_PKT3(ring, CP_ME_INIT, 17);
|
||||
OUT_RING(ring, 0x000003f7);
|
||||
OUT_RING(ring, 0x00000000);
|
||||
OUT_RING(ring, 0x00000000);
|
||||
OUT_RING(ring, 0x00000000);
|
||||
OUT_RING(ring, 0x00000080);
|
||||
OUT_RING(ring, 0x00000100);
|
||||
OUT_RING(ring, 0x00000180);
|
||||
OUT_RING(ring, 0x00006600);
|
||||
OUT_RING(ring, 0x00000150);
|
||||
OUT_RING(ring, 0x0000014e);
|
||||
OUT_RING(ring, 0x00000154);
|
||||
OUT_RING(ring, 0x00000001);
|
||||
OUT_RING(ring, 0x00000000);
|
||||
OUT_RING(ring, 0x00000000);
|
||||
OUT_RING(ring, 0x00000000);
|
||||
OUT_RING(ring, 0x00000000);
|
||||
OUT_RING(ring, 0x00000000);
|
||||
|
||||
gpu->funcs->flush(gpu);
|
||||
gpu->funcs->idle(gpu);
|
||||
}
|
||||
|
||||
static int a3xx_hw_init(struct msm_gpu *gpu)
|
||||
{
|
||||
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
|
||||
uint32_t *ptr, len;
|
||||
int i, ret;
|
||||
|
||||
DBG("%s", gpu->name);
|
||||
|
||||
if (adreno_is_a305(adreno_gpu)) {
|
||||
/* Set up 16 deep read/write request queues: */
|
||||
gpu_write(gpu, REG_A3XX_VBIF_IN_RD_LIM_CONF0, 0x10101010);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_IN_RD_LIM_CONF1, 0x10101010);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_OUT_RD_LIM_CONF0, 0x10101010);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_OUT_WR_LIM_CONF0, 0x10101010);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_DDR_OUT_MAX_BURST, 0x0000303);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_IN_WR_LIM_CONF0, 0x10101010);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_IN_WR_LIM_CONF1, 0x10101010);
|
||||
/* Enable WR-REQ: */
|
||||
gpu_write(gpu, REG_A3XX_VBIF_GATE_OFF_WRREQ_EN, 0x0000ff);
|
||||
/* Set up round robin arbitration between both AXI ports: */
|
||||
gpu_write(gpu, REG_A3XX_VBIF_ARB_CTL, 0x00000030);
|
||||
/* Set up AOOO: */
|
||||
gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO_EN, 0x0000003c);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO, 0x003c003c);
|
||||
|
||||
} else if (adreno_is_a320(adreno_gpu)) {
|
||||
/* Set up 16 deep read/write request queues: */
|
||||
gpu_write(gpu, REG_A3XX_VBIF_IN_RD_LIM_CONF0, 0x10101010);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_IN_RD_LIM_CONF1, 0x10101010);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_OUT_RD_LIM_CONF0, 0x10101010);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_OUT_WR_LIM_CONF0, 0x10101010);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_DDR_OUT_MAX_BURST, 0x0000303);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_IN_WR_LIM_CONF0, 0x10101010);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_IN_WR_LIM_CONF1, 0x10101010);
|
||||
/* Enable WR-REQ: */
|
||||
gpu_write(gpu, REG_A3XX_VBIF_GATE_OFF_WRREQ_EN, 0x0000ff);
|
||||
/* Set up round robin arbitration between both AXI ports: */
|
||||
gpu_write(gpu, REG_A3XX_VBIF_ARB_CTL, 0x00000030);
|
||||
/* Set up AOOO: */
|
||||
gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO_EN, 0x0000003c);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO, 0x003c003c);
|
||||
/* Enable 1K sort: */
|
||||
gpu_write(gpu, REG_A3XX_VBIF_ABIT_SORT, 0x000000ff);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_ABIT_SORT_CONF, 0x000000a4);
|
||||
|
||||
} else if (adreno_is_a330(adreno_gpu)) {
|
||||
/* Set up 16 deep read/write request queues: */
|
||||
gpu_write(gpu, REG_A3XX_VBIF_IN_RD_LIM_CONF0, 0x18181818);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_IN_RD_LIM_CONF1, 0x18181818);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_OUT_RD_LIM_CONF0, 0x18181818);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_OUT_WR_LIM_CONF0, 0x18181818);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_DDR_OUT_MAX_BURST, 0x0000303);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_IN_WR_LIM_CONF0, 0x18181818);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_IN_WR_LIM_CONF1, 0x18181818);
|
||||
/* Enable WR-REQ: */
|
||||
gpu_write(gpu, REG_A3XX_VBIF_GATE_OFF_WRREQ_EN, 0x00003f);
|
||||
/* Set up round robin arbitration between both AXI ports: */
|
||||
gpu_write(gpu, REG_A3XX_VBIF_ARB_CTL, 0x00000030);
|
||||
/* Set up VBIF_ROUND_ROBIN_QOS_ARB: */
|
||||
gpu_write(gpu, REG_A3XX_VBIF_ROUND_ROBIN_QOS_ARB, 0x0001);
|
||||
/* Set up AOOO: */
|
||||
gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO_EN, 0x0000ffff);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO, 0xffffffff);
|
||||
/* Enable 1K sort: */
|
||||
gpu_write(gpu, REG_A3XX_VBIF_ABIT_SORT, 0x0001ffff);
|
||||
gpu_write(gpu, REG_A3XX_VBIF_ABIT_SORT_CONF, 0x000000a4);
|
||||
/* Disable VBIF clock gating. This is to enable AXI running
|
||||
* higher frequency than GPU:
|
||||
*/
|
||||
gpu_write(gpu, REG_A3XX_VBIF_CLKON, 0x00000001);
|
||||
|
||||
} else {
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* Make all blocks contribute to the GPU BUSY perf counter: */
|
||||
gpu_write(gpu, REG_A3XX_RBBM_GPU_BUSY_MASKED, 0xffffffff);
|
||||
|
||||
/* Tune the hystersis counters for SP and CP idle detection: */
|
||||
gpu_write(gpu, REG_A3XX_RBBM_SP_HYST_CNT, 0x10);
|
||||
gpu_write(gpu, REG_A3XX_RBBM_WAIT_IDLE_CLOCKS_CTL, 0x10);
|
||||
|
||||
/* Enable the RBBM error reporting bits. This lets us get
|
||||
* useful information on failure:
|
||||
*/
|
||||
gpu_write(gpu, REG_A3XX_RBBM_AHB_CTL0, 0x00000001);
|
||||
|
||||
/* Enable AHB error reporting: */
|
||||
gpu_write(gpu, REG_A3XX_RBBM_AHB_CTL1, 0xa6ffffff);
|
||||
|
||||
/* Turn on the power counters: */
|
||||
gpu_write(gpu, REG_A3XX_RBBM_RBBM_CTL, 0x00030000);
|
||||
|
||||
/* Turn on hang detection - this spews a lot of useful information
|
||||
* into the RBBM registers on a hang:
|
||||
*/
|
||||
gpu_write(gpu, REG_A3XX_RBBM_INTERFACE_HANG_INT_CTL, 0x00010fff);
|
||||
|
||||
/* Enable 64-byte cacheline size. HW Default is 32-byte (0x000000E0): */
|
||||
gpu_write(gpu, REG_A3XX_UCHE_CACHE_MODE_CONTROL_REG, 0x00000001);
|
||||
|
||||
/* Enable Clock gating: */
|
||||
gpu_write(gpu, REG_A3XX_RBBM_CLOCK_CTL, 0xbfffffff);
|
||||
|
||||
/* Set the OCMEM base address for A330 */
|
||||
//TODO:
|
||||
// if (adreno_is_a330(adreno_gpu)) {
|
||||
// gpu_write(gpu, REG_A3XX_RB_GMEM_BASE_ADDR,
|
||||
// (unsigned int)(a3xx_gpu->ocmem_base >> 14));
|
||||
// }
|
||||
|
||||
/* Turn on performance counters: */
|
||||
gpu_write(gpu, REG_A3XX_RBBM_PERFCTR_CTL, 0x01);
|
||||
|
||||
/* Set SP perfcounter 7 to count SP_FS_FULL_ALU_INSTRUCTIONS
|
||||
* we will use this to augment our hang detection:
|
||||
*/
|
||||
gpu_write(gpu, REG_A3XX_SP_PERFCOUNTER7_SELECT,
|
||||
SP_FS_FULL_ALU_INSTRUCTIONS);
|
||||
|
||||
gpu_write(gpu, REG_A3XX_RBBM_INT_0_MASK, A3XX_INT0_MASK);
|
||||
|
||||
ret = adreno_hw_init(gpu);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* setup access protection: */
|
||||
gpu_write(gpu, REG_A3XX_CP_PROTECT_CTRL, 0x00000007);
|
||||
|
||||
/* RBBM registers */
|
||||
gpu_write(gpu, REG_A3XX_CP_PROTECT(0), 0x63000040);
|
||||
gpu_write(gpu, REG_A3XX_CP_PROTECT(1), 0x62000080);
|
||||
gpu_write(gpu, REG_A3XX_CP_PROTECT(2), 0x600000cc);
|
||||
gpu_write(gpu, REG_A3XX_CP_PROTECT(3), 0x60000108);
|
||||
gpu_write(gpu, REG_A3XX_CP_PROTECT(4), 0x64000140);
|
||||
gpu_write(gpu, REG_A3XX_CP_PROTECT(5), 0x66000400);
|
||||
|
||||
/* CP registers */
|
||||
gpu_write(gpu, REG_A3XX_CP_PROTECT(6), 0x65000700);
|
||||
gpu_write(gpu, REG_A3XX_CP_PROTECT(7), 0x610007d8);
|
||||
gpu_write(gpu, REG_A3XX_CP_PROTECT(8), 0x620007e0);
|
||||
gpu_write(gpu, REG_A3XX_CP_PROTECT(9), 0x61001178);
|
||||
gpu_write(gpu, REG_A3XX_CP_PROTECT(10), 0x64001180);
|
||||
|
||||
/* RB registers */
|
||||
gpu_write(gpu, REG_A3XX_CP_PROTECT(11), 0x60003300);
|
||||
|
||||
/* VBIF registers */
|
||||
gpu_write(gpu, REG_A3XX_CP_PROTECT(12), 0x6b00c000);
|
||||
|
||||
/* NOTE: PM4/micro-engine firmware registers look to be the same
|
||||
* for a2xx and a3xx.. we could possibly push that part down to
|
||||
* adreno_gpu base class. Or push both PM4 and PFP but
|
||||
* parameterize the pfp ucode addr/data registers..
|
||||
*/
|
||||
|
||||
/* Load PM4: */
|
||||
ptr = (uint32_t *)(adreno_gpu->pm4->data);
|
||||
len = adreno_gpu->pm4->size / 4;
|
||||
DBG("loading PM4 ucode version: %u", ptr[0]);
|
||||
|
||||
gpu_write(gpu, REG_AXXX_CP_DEBUG,
|
||||
AXXX_CP_DEBUG_DYNAMIC_CLK_DISABLE |
|
||||
AXXX_CP_DEBUG_MIU_128BIT_WRITE_ENABLE);
|
||||
gpu_write(gpu, REG_AXXX_CP_ME_RAM_WADDR, 0);
|
||||
for (i = 1; i < len; i++)
|
||||
gpu_write(gpu, REG_AXXX_CP_ME_RAM_DATA, ptr[i]);
|
||||
|
||||
/* Load PFP: */
|
||||
ptr = (uint32_t *)(adreno_gpu->pfp->data);
|
||||
len = adreno_gpu->pfp->size / 4;
|
||||
DBG("loading PFP ucode version: %u", ptr[0]);
|
||||
|
||||
gpu_write(gpu, REG_A3XX_CP_PFP_UCODE_ADDR, 0);
|
||||
for (i = 1; i < len; i++)
|
||||
gpu_write(gpu, REG_A3XX_CP_PFP_UCODE_DATA, ptr[i]);
|
||||
|
||||
/* CP ROQ queue sizes (bytes) - RB:16, ST:16, IB1:32, IB2:64 */
|
||||
if (adreno_is_a305(adreno_gpu) || adreno_is_a320(adreno_gpu))
|
||||
gpu_write(gpu, REG_AXXX_CP_QUEUE_THRESHOLDS,
|
||||
AXXX_CP_QUEUE_THRESHOLDS_CSQ_IB1_START(2) |
|
||||
AXXX_CP_QUEUE_THRESHOLDS_CSQ_IB2_START(6) |
|
||||
AXXX_CP_QUEUE_THRESHOLDS_CSQ_ST_START(14));
|
||||
|
||||
|
||||
/* clear ME_HALT to start micro engine */
|
||||
gpu_write(gpu, REG_AXXX_CP_ME_CNTL, 0);
|
||||
|
||||
a3xx_me_init(gpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void a3xx_destroy(struct msm_gpu *gpu)
|
||||
{
|
||||
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
|
||||
struct a3xx_gpu *a3xx_gpu = to_a3xx_gpu(adreno_gpu);
|
||||
|
||||
DBG("%s", gpu->name);
|
||||
|
||||
adreno_gpu_cleanup(adreno_gpu);
|
||||
put_device(&a3xx_gpu->pdev->dev);
|
||||
kfree(a3xx_gpu);
|
||||
}
|
||||
|
||||
static void a3xx_idle(struct msm_gpu *gpu)
|
||||
{
|
||||
unsigned long t;
|
||||
|
||||
/* wait for ringbuffer to drain: */
|
||||
adreno_idle(gpu);
|
||||
|
||||
t = jiffies + ADRENO_IDLE_TIMEOUT;
|
||||
|
||||
/* then wait for GPU to finish: */
|
||||
do {
|
||||
uint32_t rbbm_status = gpu_read(gpu, REG_A3XX_RBBM_STATUS);
|
||||
if (!(rbbm_status & A3XX_RBBM_STATUS_GPU_BUSY))
|
||||
return;
|
||||
} while(time_before(jiffies, t));
|
||||
|
||||
DRM_ERROR("timeout waiting for %s to idle!\n", gpu->name);
|
||||
|
||||
/* TODO maybe we need to reset GPU here to recover from hang? */
|
||||
}
|
||||
|
||||
static irqreturn_t a3xx_irq(struct msm_gpu *gpu)
|
||||
{
|
||||
uint32_t status;
|
||||
|
||||
status = gpu_read(gpu, REG_A3XX_RBBM_INT_0_STATUS);
|
||||
DBG("%s: %08x", gpu->name, status);
|
||||
|
||||
// TODO
|
||||
|
||||
gpu_write(gpu, REG_A3XX_RBBM_INT_CLEAR_CMD, status);
|
||||
|
||||
msm_gpu_retire(gpu);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static const unsigned int a3xx_registers[] = {
|
||||
0x0000, 0x0002, 0x0010, 0x0012, 0x0018, 0x0018, 0x0020, 0x0027,
|
||||
0x0029, 0x002b, 0x002e, 0x0033, 0x0040, 0x0042, 0x0050, 0x005c,
|
||||
0x0060, 0x006c, 0x0080, 0x0082, 0x0084, 0x0088, 0x0090, 0x00e5,
|
||||
0x00ea, 0x00ed, 0x0100, 0x0100, 0x0110, 0x0123, 0x01c0, 0x01c1,
|
||||
0x01c3, 0x01c5, 0x01c7, 0x01c7, 0x01d5, 0x01d9, 0x01dc, 0x01dd,
|
||||
0x01ea, 0x01ea, 0x01ee, 0x01f1, 0x01f5, 0x01f5, 0x01fc, 0x01ff,
|
||||
0x0440, 0x0440, 0x0443, 0x0443, 0x0445, 0x0445, 0x044d, 0x044f,
|
||||
0x0452, 0x0452, 0x0454, 0x046f, 0x047c, 0x047c, 0x047f, 0x047f,
|
||||
0x0578, 0x057f, 0x0600, 0x0602, 0x0605, 0x0607, 0x060a, 0x060e,
|
||||
0x0612, 0x0614, 0x0c01, 0x0c02, 0x0c06, 0x0c1d, 0x0c3d, 0x0c3f,
|
||||
0x0c48, 0x0c4b, 0x0c80, 0x0c80, 0x0c88, 0x0c8b, 0x0ca0, 0x0cb7,
|
||||
0x0cc0, 0x0cc1, 0x0cc6, 0x0cc7, 0x0ce4, 0x0ce5, 0x0e00, 0x0e05,
|
||||
0x0e0c, 0x0e0c, 0x0e22, 0x0e23, 0x0e41, 0x0e45, 0x0e64, 0x0e65,
|
||||
0x0e80, 0x0e82, 0x0e84, 0x0e89, 0x0ea0, 0x0ea1, 0x0ea4, 0x0ea7,
|
||||
0x0ec4, 0x0ecb, 0x0ee0, 0x0ee0, 0x0f00, 0x0f01, 0x0f03, 0x0f09,
|
||||
0x2040, 0x2040, 0x2044, 0x2044, 0x2048, 0x204d, 0x2068, 0x2069,
|
||||
0x206c, 0x206d, 0x2070, 0x2070, 0x2072, 0x2072, 0x2074, 0x2075,
|
||||
0x2079, 0x207a, 0x20c0, 0x20d3, 0x20e4, 0x20ef, 0x2100, 0x2109,
|
||||
0x210c, 0x210c, 0x210e, 0x210e, 0x2110, 0x2111, 0x2114, 0x2115,
|
||||
0x21e4, 0x21e4, 0x21ea, 0x21ea, 0x21ec, 0x21ed, 0x21f0, 0x21f0,
|
||||
0x2200, 0x2212, 0x2214, 0x2217, 0x221a, 0x221a, 0x2240, 0x227e,
|
||||
0x2280, 0x228b, 0x22c0, 0x22c0, 0x22c4, 0x22ce, 0x22d0, 0x22d8,
|
||||
0x22df, 0x22e6, 0x22e8, 0x22e9, 0x22ec, 0x22ec, 0x22f0, 0x22f7,
|
||||
0x22ff, 0x22ff, 0x2340, 0x2343, 0x2348, 0x2349, 0x2350, 0x2356,
|
||||
0x2360, 0x2360, 0x2440, 0x2440, 0x2444, 0x2444, 0x2448, 0x244d,
|
||||
0x2468, 0x2469, 0x246c, 0x246d, 0x2470, 0x2470, 0x2472, 0x2472,
|
||||
0x2474, 0x2475, 0x2479, 0x247a, 0x24c0, 0x24d3, 0x24e4, 0x24ef,
|
||||
0x2500, 0x2509, 0x250c, 0x250c, 0x250e, 0x250e, 0x2510, 0x2511,
|
||||
0x2514, 0x2515, 0x25e4, 0x25e4, 0x25ea, 0x25ea, 0x25ec, 0x25ed,
|
||||
0x25f0, 0x25f0, 0x2600, 0x2612, 0x2614, 0x2617, 0x261a, 0x261a,
|
||||
0x2640, 0x267e, 0x2680, 0x268b, 0x26c0, 0x26c0, 0x26c4, 0x26ce,
|
||||
0x26d0, 0x26d8, 0x26df, 0x26e6, 0x26e8, 0x26e9, 0x26ec, 0x26ec,
|
||||
0x26f0, 0x26f7, 0x26ff, 0x26ff, 0x2740, 0x2743, 0x2748, 0x2749,
|
||||
0x2750, 0x2756, 0x2760, 0x2760, 0x300c, 0x300e, 0x301c, 0x301d,
|
||||
0x302a, 0x302a, 0x302c, 0x302d, 0x3030, 0x3031, 0x3034, 0x3036,
|
||||
0x303c, 0x303c, 0x305e, 0x305f,
|
||||
};
|
||||
|
||||
static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m)
|
||||
{
|
||||
int i;
|
||||
|
||||
adreno_show(gpu, m);
|
||||
seq_printf(m, "status: %08x\n",
|
||||
gpu_read(gpu, REG_A3XX_RBBM_STATUS));
|
||||
|
||||
/* dump these out in a form that can be parsed by demsm: */
|
||||
seq_printf(m, "IO:region %s 00000000 00020000\n", gpu->name);
|
||||
for (i = 0; i < ARRAY_SIZE(a3xx_registers); i += 2) {
|
||||
uint32_t start = a3xx_registers[i];
|
||||
uint32_t end = a3xx_registers[i+1];
|
||||
uint32_t addr;
|
||||
|
||||
for (addr = start; addr <= end; addr++) {
|
||||
uint32_t val = gpu_read(gpu, addr);
|
||||
seq_printf(m, "IO:R %08x %08x\n", addr<<2, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct adreno_gpu_funcs funcs = {
|
||||
.base = {
|
||||
.get_param = adreno_get_param,
|
||||
.hw_init = a3xx_hw_init,
|
||||
.pm_suspend = msm_gpu_pm_suspend,
|
||||
.pm_resume = msm_gpu_pm_resume,
|
||||
.recover = adreno_recover,
|
||||
.last_fence = adreno_last_fence,
|
||||
.submit = adreno_submit,
|
||||
.flush = adreno_flush,
|
||||
.idle = a3xx_idle,
|
||||
.irq = a3xx_irq,
|
||||
.destroy = a3xx_destroy,
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
.show = a3xx_show,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
|
||||
{
|
||||
struct a3xx_gpu *a3xx_gpu = NULL;
|
||||
struct msm_gpu *gpu;
|
||||
struct platform_device *pdev = a3xx_pdev;
|
||||
struct adreno_platform_config *config;
|
||||
int ret;
|
||||
|
||||
if (!pdev) {
|
||||
dev_err(dev->dev, "no a3xx device\n");
|
||||
ret = -ENXIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
config = pdev->dev.platform_data;
|
||||
|
||||
a3xx_gpu = kzalloc(sizeof(*a3xx_gpu), GFP_KERNEL);
|
||||
if (!a3xx_gpu) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
gpu = &a3xx_gpu->base.base;
|
||||
|
||||
get_device(&pdev->dev);
|
||||
a3xx_gpu->pdev = pdev;
|
||||
|
||||
gpu->fast_rate = config->fast_rate;
|
||||
gpu->slow_rate = config->slow_rate;
|
||||
gpu->bus_freq = config->bus_freq;
|
||||
|
||||
DBG("fast_rate=%u, slow_rate=%u, bus_freq=%u",
|
||||
gpu->fast_rate, gpu->slow_rate, gpu->bus_freq);
|
||||
|
||||
ret = adreno_gpu_init(dev, pdev, &a3xx_gpu->base,
|
||||
&funcs, config->rev);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
return &a3xx_gpu->base.base;
|
||||
|
||||
fail:
|
||||
if (a3xx_gpu)
|
||||
a3xx_destroy(&a3xx_gpu->base.base);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* The a3xx device:
|
||||
*/
|
||||
|
||||
static int a3xx_probe(struct platform_device *pdev)
|
||||
{
|
||||
static struct adreno_platform_config config = {};
|
||||
#ifdef CONFIG_OF
|
||||
/* TODO */
|
||||
#else
|
||||
uint32_t version = socinfo_get_version();
|
||||
if (cpu_is_apq8064ab()) {
|
||||
config.fast_rate = 450000000;
|
||||
config.slow_rate = 27000000;
|
||||
config.bus_freq = 4;
|
||||
config.rev = ADRENO_REV(3, 2, 1, 0);
|
||||
} else if (cpu_is_apq8064() || cpu_is_msm8960ab()) {
|
||||
config.fast_rate = 400000000;
|
||||
config.slow_rate = 27000000;
|
||||
config.bus_freq = 4;
|
||||
|
||||
if (SOCINFO_VERSION_MAJOR(version) == 2)
|
||||
config.rev = ADRENO_REV(3, 2, 0, 2);
|
||||
else if ((SOCINFO_VERSION_MAJOR(version) == 1) &&
|
||||
(SOCINFO_VERSION_MINOR(version) == 1))
|
||||
config.rev = ADRENO_REV(3, 2, 0, 1);
|
||||
else
|
||||
config.rev = ADRENO_REV(3, 2, 0, 0);
|
||||
|
||||
} else if (cpu_is_msm8930()) {
|
||||
config.fast_rate = 400000000;
|
||||
config.slow_rate = 27000000;
|
||||
config.bus_freq = 3;
|
||||
|
||||
if ((SOCINFO_VERSION_MAJOR(version) == 1) &&
|
||||
(SOCINFO_VERSION_MINOR(version) == 2))
|
||||
config.rev = ADRENO_REV(3, 0, 5, 2);
|
||||
else
|
||||
config.rev = ADRENO_REV(3, 0, 5, 0);
|
||||
|
||||
}
|
||||
#endif
|
||||
pdev->dev.platform_data = &config;
|
||||
a3xx_pdev = pdev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int a3xx_remove(struct platform_device *pdev)
|
||||
{
|
||||
a3xx_pdev = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver a3xx_driver = {
|
||||
.probe = a3xx_probe,
|
||||
.remove = a3xx_remove,
|
||||
.driver.name = "kgsl-3d0",
|
||||
};
|
||||
|
||||
void __init a3xx_register(void)
|
||||
{
|
||||
platform_driver_register(&a3xx_driver);
|
||||
}
|
||||
|
||||
void __exit a3xx_unregister(void)
|
||||
{
|
||||
platform_driver_unregister(&a3xx_driver);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __A3XX_GPU_H__
|
||||
#define __A3XX_GPU_H__
|
||||
|
||||
#include "adreno_gpu.h"
|
||||
#include "a3xx.xml.h"
|
||||
|
||||
struct a3xx_gpu {
|
||||
struct adreno_gpu base;
|
||||
struct platform_device *pdev;
|
||||
};
|
||||
#define to_a3xx_gpu(x) container_of(x, struct a3xx_gpu, base)
|
||||
|
||||
#endif /* __A3XX_GPU_H__ */
|
|
@ -0,0 +1,432 @@
|
|||
#ifndef ADRENO_COMMON_XML
|
||||
#define ADRENO_COMMON_XML
|
||||
|
||||
/* Autogenerated file, DO NOT EDIT manually!
|
||||
|
||||
This file was generated by the rules-ng-ng headergen tool in this git repository:
|
||||
http://0x04.net/cgit/index.cgi/rules-ng-ng
|
||||
git clone git://0x04.net/rules-ng-ng
|
||||
|
||||
The rules-ng-ng source files this header was generated from are:
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml ( 327 bytes, from 2013-07-05 19:21:12)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/a2xx/a2xx.xml ( 30005 bytes, from 2013-07-19 21:30:48)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/adreno_common.xml ( 8983 bytes, from 2013-07-24 01:38:36)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/adreno_pm4.xml ( 9712 bytes, from 2013-05-26 15:22:37)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/a3xx/a3xx.xml ( 51415 bytes, from 2013-08-03 14:26:05)
|
||||
|
||||
Copyright (C) 2013 by the following authors:
|
||||
- Rob Clark <robdclark@gmail.com> (robclark)
|
||||
|
||||
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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS 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.
|
||||
*/
|
||||
|
||||
|
||||
enum adreno_pa_su_sc_draw {
|
||||
PC_DRAW_POINTS = 0,
|
||||
PC_DRAW_LINES = 1,
|
||||
PC_DRAW_TRIANGLES = 2,
|
||||
};
|
||||
|
||||
enum adreno_compare_func {
|
||||
FUNC_NEVER = 0,
|
||||
FUNC_LESS = 1,
|
||||
FUNC_EQUAL = 2,
|
||||
FUNC_LEQUAL = 3,
|
||||
FUNC_GREATER = 4,
|
||||
FUNC_NOTEQUAL = 5,
|
||||
FUNC_GEQUAL = 6,
|
||||
FUNC_ALWAYS = 7,
|
||||
};
|
||||
|
||||
enum adreno_stencil_op {
|
||||
STENCIL_KEEP = 0,
|
||||
STENCIL_ZERO = 1,
|
||||
STENCIL_REPLACE = 2,
|
||||
STENCIL_INCR_CLAMP = 3,
|
||||
STENCIL_DECR_CLAMP = 4,
|
||||
STENCIL_INVERT = 5,
|
||||
STENCIL_INCR_WRAP = 6,
|
||||
STENCIL_DECR_WRAP = 7,
|
||||
};
|
||||
|
||||
enum adreno_rb_blend_factor {
|
||||
FACTOR_ZERO = 0,
|
||||
FACTOR_ONE = 1,
|
||||
FACTOR_SRC_COLOR = 4,
|
||||
FACTOR_ONE_MINUS_SRC_COLOR = 5,
|
||||
FACTOR_SRC_ALPHA = 6,
|
||||
FACTOR_ONE_MINUS_SRC_ALPHA = 7,
|
||||
FACTOR_DST_COLOR = 8,
|
||||
FACTOR_ONE_MINUS_DST_COLOR = 9,
|
||||
FACTOR_DST_ALPHA = 10,
|
||||
FACTOR_ONE_MINUS_DST_ALPHA = 11,
|
||||
FACTOR_CONSTANT_COLOR = 12,
|
||||
FACTOR_ONE_MINUS_CONSTANT_COLOR = 13,
|
||||
FACTOR_CONSTANT_ALPHA = 14,
|
||||
FACTOR_ONE_MINUS_CONSTANT_ALPHA = 15,
|
||||
FACTOR_SRC_ALPHA_SATURATE = 16,
|
||||
};
|
||||
|
||||
enum adreno_rb_blend_opcode {
|
||||
BLEND_DST_PLUS_SRC = 0,
|
||||
BLEND_SRC_MINUS_DST = 1,
|
||||
BLEND_MIN_DST_SRC = 2,
|
||||
BLEND_MAX_DST_SRC = 3,
|
||||
BLEND_DST_MINUS_SRC = 4,
|
||||
BLEND_DST_PLUS_SRC_BIAS = 5,
|
||||
};
|
||||
|
||||
enum adreno_rb_surface_endian {
|
||||
ENDIAN_NONE = 0,
|
||||
ENDIAN_8IN16 = 1,
|
||||
ENDIAN_8IN32 = 2,
|
||||
ENDIAN_16IN32 = 3,
|
||||
ENDIAN_8IN64 = 4,
|
||||
ENDIAN_8IN128 = 5,
|
||||
};
|
||||
|
||||
enum adreno_rb_dither_mode {
|
||||
DITHER_DISABLE = 0,
|
||||
DITHER_ALWAYS = 1,
|
||||
DITHER_IF_ALPHA_OFF = 2,
|
||||
};
|
||||
|
||||
enum adreno_rb_depth_format {
|
||||
DEPTHX_16 = 0,
|
||||
DEPTHX_24_8 = 1,
|
||||
};
|
||||
|
||||
enum adreno_mmu_clnt_beh {
|
||||
BEH_NEVR = 0,
|
||||
BEH_TRAN_RNG = 1,
|
||||
BEH_TRAN_FLT = 2,
|
||||
};
|
||||
|
||||
#define REG_AXXX_MH_MMU_CONFIG 0x00000040
|
||||
#define AXXX_MH_MMU_CONFIG_MMU_ENABLE 0x00000001
|
||||
#define AXXX_MH_MMU_CONFIG_SPLIT_MODE_ENABLE 0x00000002
|
||||
#define AXXX_MH_MMU_CONFIG_RB_W_CLNT_BEHAVIOR__MASK 0x00000030
|
||||
#define AXXX_MH_MMU_CONFIG_RB_W_CLNT_BEHAVIOR__SHIFT 4
|
||||
static inline uint32_t AXXX_MH_MMU_CONFIG_RB_W_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val)
|
||||
{
|
||||
return ((val) << AXXX_MH_MMU_CONFIG_RB_W_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_RB_W_CLNT_BEHAVIOR__MASK;
|
||||
}
|
||||
#define AXXX_MH_MMU_CONFIG_CP_W_CLNT_BEHAVIOR__MASK 0x000000c0
|
||||
#define AXXX_MH_MMU_CONFIG_CP_W_CLNT_BEHAVIOR__SHIFT 6
|
||||
static inline uint32_t AXXX_MH_MMU_CONFIG_CP_W_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val)
|
||||
{
|
||||
return ((val) << AXXX_MH_MMU_CONFIG_CP_W_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_CP_W_CLNT_BEHAVIOR__MASK;
|
||||
}
|
||||
#define AXXX_MH_MMU_CONFIG_CP_R0_CLNT_BEHAVIOR__MASK 0x00000300
|
||||
#define AXXX_MH_MMU_CONFIG_CP_R0_CLNT_BEHAVIOR__SHIFT 8
|
||||
static inline uint32_t AXXX_MH_MMU_CONFIG_CP_R0_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val)
|
||||
{
|
||||
return ((val) << AXXX_MH_MMU_CONFIG_CP_R0_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_CP_R0_CLNT_BEHAVIOR__MASK;
|
||||
}
|
||||
#define AXXX_MH_MMU_CONFIG_CP_R1_CLNT_BEHAVIOR__MASK 0x00000c00
|
||||
#define AXXX_MH_MMU_CONFIG_CP_R1_CLNT_BEHAVIOR__SHIFT 10
|
||||
static inline uint32_t AXXX_MH_MMU_CONFIG_CP_R1_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val)
|
||||
{
|
||||
return ((val) << AXXX_MH_MMU_CONFIG_CP_R1_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_CP_R1_CLNT_BEHAVIOR__MASK;
|
||||
}
|
||||
#define AXXX_MH_MMU_CONFIG_CP_R2_CLNT_BEHAVIOR__MASK 0x00003000
|
||||
#define AXXX_MH_MMU_CONFIG_CP_R2_CLNT_BEHAVIOR__SHIFT 12
|
||||
static inline uint32_t AXXX_MH_MMU_CONFIG_CP_R2_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val)
|
||||
{
|
||||
return ((val) << AXXX_MH_MMU_CONFIG_CP_R2_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_CP_R2_CLNT_BEHAVIOR__MASK;
|
||||
}
|
||||
#define AXXX_MH_MMU_CONFIG_CP_R3_CLNT_BEHAVIOR__MASK 0x0000c000
|
||||
#define AXXX_MH_MMU_CONFIG_CP_R3_CLNT_BEHAVIOR__SHIFT 14
|
||||
static inline uint32_t AXXX_MH_MMU_CONFIG_CP_R3_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val)
|
||||
{
|
||||
return ((val) << AXXX_MH_MMU_CONFIG_CP_R3_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_CP_R3_CLNT_BEHAVIOR__MASK;
|
||||
}
|
||||
#define AXXX_MH_MMU_CONFIG_CP_R4_CLNT_BEHAVIOR__MASK 0x00030000
|
||||
#define AXXX_MH_MMU_CONFIG_CP_R4_CLNT_BEHAVIOR__SHIFT 16
|
||||
static inline uint32_t AXXX_MH_MMU_CONFIG_CP_R4_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val)
|
||||
{
|
||||
return ((val) << AXXX_MH_MMU_CONFIG_CP_R4_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_CP_R4_CLNT_BEHAVIOR__MASK;
|
||||
}
|
||||
#define AXXX_MH_MMU_CONFIG_VGT_R0_CLNT_BEHAVIOR__MASK 0x000c0000
|
||||
#define AXXX_MH_MMU_CONFIG_VGT_R0_CLNT_BEHAVIOR__SHIFT 18
|
||||
static inline uint32_t AXXX_MH_MMU_CONFIG_VGT_R0_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val)
|
||||
{
|
||||
return ((val) << AXXX_MH_MMU_CONFIG_VGT_R0_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_VGT_R0_CLNT_BEHAVIOR__MASK;
|
||||
}
|
||||
#define AXXX_MH_MMU_CONFIG_VGT_R1_CLNT_BEHAVIOR__MASK 0x00300000
|
||||
#define AXXX_MH_MMU_CONFIG_VGT_R1_CLNT_BEHAVIOR__SHIFT 20
|
||||
static inline uint32_t AXXX_MH_MMU_CONFIG_VGT_R1_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val)
|
||||
{
|
||||
return ((val) << AXXX_MH_MMU_CONFIG_VGT_R1_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_VGT_R1_CLNT_BEHAVIOR__MASK;
|
||||
}
|
||||
#define AXXX_MH_MMU_CONFIG_TC_R_CLNT_BEHAVIOR__MASK 0x00c00000
|
||||
#define AXXX_MH_MMU_CONFIG_TC_R_CLNT_BEHAVIOR__SHIFT 22
|
||||
static inline uint32_t AXXX_MH_MMU_CONFIG_TC_R_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val)
|
||||
{
|
||||
return ((val) << AXXX_MH_MMU_CONFIG_TC_R_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_TC_R_CLNT_BEHAVIOR__MASK;
|
||||
}
|
||||
#define AXXX_MH_MMU_CONFIG_PA_W_CLNT_BEHAVIOR__MASK 0x03000000
|
||||
#define AXXX_MH_MMU_CONFIG_PA_W_CLNT_BEHAVIOR__SHIFT 24
|
||||
static inline uint32_t AXXX_MH_MMU_CONFIG_PA_W_CLNT_BEHAVIOR(enum adreno_mmu_clnt_beh val)
|
||||
{
|
||||
return ((val) << AXXX_MH_MMU_CONFIG_PA_W_CLNT_BEHAVIOR__SHIFT) & AXXX_MH_MMU_CONFIG_PA_W_CLNT_BEHAVIOR__MASK;
|
||||
}
|
||||
|
||||
#define REG_AXXX_MH_MMU_VA_RANGE 0x00000041
|
||||
|
||||
#define REG_AXXX_MH_MMU_PT_BASE 0x00000042
|
||||
|
||||
#define REG_AXXX_MH_MMU_PAGE_FAULT 0x00000043
|
||||
|
||||
#define REG_AXXX_MH_MMU_TRAN_ERROR 0x00000044
|
||||
|
||||
#define REG_AXXX_MH_MMU_INVALIDATE 0x00000045
|
||||
|
||||
#define REG_AXXX_MH_MMU_MPU_BASE 0x00000046
|
||||
|
||||
#define REG_AXXX_MH_MMU_MPU_END 0x00000047
|
||||
|
||||
#define REG_AXXX_CP_RB_BASE 0x000001c0
|
||||
|
||||
#define REG_AXXX_CP_RB_CNTL 0x000001c1
|
||||
#define AXXX_CP_RB_CNTL_BUFSZ__MASK 0x0000003f
|
||||
#define AXXX_CP_RB_CNTL_BUFSZ__SHIFT 0
|
||||
static inline uint32_t AXXX_CP_RB_CNTL_BUFSZ(uint32_t val)
|
||||
{
|
||||
return ((val) << AXXX_CP_RB_CNTL_BUFSZ__SHIFT) & AXXX_CP_RB_CNTL_BUFSZ__MASK;
|
||||
}
|
||||
#define AXXX_CP_RB_CNTL_BLKSZ__MASK 0x00003f00
|
||||
#define AXXX_CP_RB_CNTL_BLKSZ__SHIFT 8
|
||||
static inline uint32_t AXXX_CP_RB_CNTL_BLKSZ(uint32_t val)
|
||||
{
|
||||
return ((val) << AXXX_CP_RB_CNTL_BLKSZ__SHIFT) & AXXX_CP_RB_CNTL_BLKSZ__MASK;
|
||||
}
|
||||
#define AXXX_CP_RB_CNTL_BUF_SWAP__MASK 0x00030000
|
||||
#define AXXX_CP_RB_CNTL_BUF_SWAP__SHIFT 16
|
||||
static inline uint32_t AXXX_CP_RB_CNTL_BUF_SWAP(uint32_t val)
|
||||
{
|
||||
return ((val) << AXXX_CP_RB_CNTL_BUF_SWAP__SHIFT) & AXXX_CP_RB_CNTL_BUF_SWAP__MASK;
|
||||
}
|
||||
#define AXXX_CP_RB_CNTL_POLL_EN 0x00100000
|
||||
#define AXXX_CP_RB_CNTL_NO_UPDATE 0x08000000
|
||||
#define AXXX_CP_RB_CNTL_RPTR_WR_EN 0x80000000
|
||||
|
||||
#define REG_AXXX_CP_RB_RPTR_ADDR 0x000001c3
|
||||
#define AXXX_CP_RB_RPTR_ADDR_SWAP__MASK 0x00000003
|
||||
#define AXXX_CP_RB_RPTR_ADDR_SWAP__SHIFT 0
|
||||
static inline uint32_t AXXX_CP_RB_RPTR_ADDR_SWAP(uint32_t val)
|
||||
{
|
||||
return ((val) << AXXX_CP_RB_RPTR_ADDR_SWAP__SHIFT) & AXXX_CP_RB_RPTR_ADDR_SWAP__MASK;
|
||||
}
|
||||
#define AXXX_CP_RB_RPTR_ADDR_ADDR__MASK 0xfffffffc
|
||||
#define AXXX_CP_RB_RPTR_ADDR_ADDR__SHIFT 2
|
||||
static inline uint32_t AXXX_CP_RB_RPTR_ADDR_ADDR(uint32_t val)
|
||||
{
|
||||
return ((val >> 2) << AXXX_CP_RB_RPTR_ADDR_ADDR__SHIFT) & AXXX_CP_RB_RPTR_ADDR_ADDR__MASK;
|
||||
}
|
||||
|
||||
#define REG_AXXX_CP_RB_RPTR 0x000001c4
|
||||
|
||||
#define REG_AXXX_CP_RB_WPTR 0x000001c5
|
||||
|
||||
#define REG_AXXX_CP_RB_WPTR_DELAY 0x000001c6
|
||||
|
||||
#define REG_AXXX_CP_RB_RPTR_WR 0x000001c7
|
||||
|
||||
#define REG_AXXX_CP_RB_WPTR_BASE 0x000001c8
|
||||
|
||||
#define REG_AXXX_CP_QUEUE_THRESHOLDS 0x000001d5
|
||||
#define AXXX_CP_QUEUE_THRESHOLDS_CSQ_IB1_START__MASK 0x0000000f
|
||||
#define AXXX_CP_QUEUE_THRESHOLDS_CSQ_IB1_START__SHIFT 0
|
||||
static inline uint32_t AXXX_CP_QUEUE_THRESHOLDS_CSQ_IB1_START(uint32_t val)
|
||||
{
|
||||
return ((val) << AXXX_CP_QUEUE_THRESHOLDS_CSQ_IB1_START__SHIFT) & AXXX_CP_QUEUE_THRESHOLDS_CSQ_IB1_START__MASK;
|
||||
}
|
||||
#define AXXX_CP_QUEUE_THRESHOLDS_CSQ_IB2_START__MASK 0x00000f00
|
||||
#define AXXX_CP_QUEUE_THRESHOLDS_CSQ_IB2_START__SHIFT 8
|
||||
static inline uint32_t AXXX_CP_QUEUE_THRESHOLDS_CSQ_IB2_START(uint32_t val)
|
||||
{
|
||||
return ((val) << AXXX_CP_QUEUE_THRESHOLDS_CSQ_IB2_START__SHIFT) & AXXX_CP_QUEUE_THRESHOLDS_CSQ_IB2_START__MASK;
|
||||
}
|
||||
#define AXXX_CP_QUEUE_THRESHOLDS_CSQ_ST_START__MASK 0x000f0000
|
||||
#define AXXX_CP_QUEUE_THRESHOLDS_CSQ_ST_START__SHIFT 16
|
||||
static inline uint32_t AXXX_CP_QUEUE_THRESHOLDS_CSQ_ST_START(uint32_t val)
|
||||
{
|
||||
return ((val) << AXXX_CP_QUEUE_THRESHOLDS_CSQ_ST_START__SHIFT) & AXXX_CP_QUEUE_THRESHOLDS_CSQ_ST_START__MASK;
|
||||
}
|
||||
|
||||
#define REG_AXXX_CP_MEQ_THRESHOLDS 0x000001d6
|
||||
|
||||
#define REG_AXXX_CP_CSQ_AVAIL 0x000001d7
|
||||
#define AXXX_CP_CSQ_AVAIL_RING__MASK 0x0000007f
|
||||
#define AXXX_CP_CSQ_AVAIL_RING__SHIFT 0
|
||||
static inline uint32_t AXXX_CP_CSQ_AVAIL_RING(uint32_t val)
|
||||
{
|
||||
return ((val) << AXXX_CP_CSQ_AVAIL_RING__SHIFT) & AXXX_CP_CSQ_AVAIL_RING__MASK;
|
||||
}
|
||||
#define AXXX_CP_CSQ_AVAIL_IB1__MASK 0x00007f00
|
||||
#define AXXX_CP_CSQ_AVAIL_IB1__SHIFT 8
|
||||
static inline uint32_t AXXX_CP_CSQ_AVAIL_IB1(uint32_t val)
|
||||
{
|
||||
return ((val) << AXXX_CP_CSQ_AVAIL_IB1__SHIFT) & AXXX_CP_CSQ_AVAIL_IB1__MASK;
|
||||
}
|
||||
#define AXXX_CP_CSQ_AVAIL_IB2__MASK 0x007f0000
|
||||
#define AXXX_CP_CSQ_AVAIL_IB2__SHIFT 16
|
||||
static inline uint32_t AXXX_CP_CSQ_AVAIL_IB2(uint32_t val)
|
||||
{
|
||||
return ((val) << AXXX_CP_CSQ_AVAIL_IB2__SHIFT) & AXXX_CP_CSQ_AVAIL_IB2__MASK;
|
||||
}
|
||||
|
||||
#define REG_AXXX_CP_STQ_AVAIL 0x000001d8
|
||||
#define AXXX_CP_STQ_AVAIL_ST__MASK 0x0000007f
|
||||
#define AXXX_CP_STQ_AVAIL_ST__SHIFT 0
|
||||
static inline uint32_t AXXX_CP_STQ_AVAIL_ST(uint32_t val)
|
||||
{
|
||||
return ((val) << AXXX_CP_STQ_AVAIL_ST__SHIFT) & AXXX_CP_STQ_AVAIL_ST__MASK;
|
||||
}
|
||||
|
||||
#define REG_AXXX_CP_MEQ_AVAIL 0x000001d9
|
||||
#define AXXX_CP_MEQ_AVAIL_MEQ__MASK 0x0000001f
|
||||
#define AXXX_CP_MEQ_AVAIL_MEQ__SHIFT 0
|
||||
static inline uint32_t AXXX_CP_MEQ_AVAIL_MEQ(uint32_t val)
|
||||
{
|
||||
return ((val) << AXXX_CP_MEQ_AVAIL_MEQ__SHIFT) & AXXX_CP_MEQ_AVAIL_MEQ__MASK;
|
||||
}
|
||||
|
||||
#define REG_AXXX_SCRATCH_UMSK 0x000001dc
|
||||
#define AXXX_SCRATCH_UMSK_UMSK__MASK 0x000000ff
|
||||
#define AXXX_SCRATCH_UMSK_UMSK__SHIFT 0
|
||||
static inline uint32_t AXXX_SCRATCH_UMSK_UMSK(uint32_t val)
|
||||
{
|
||||
return ((val) << AXXX_SCRATCH_UMSK_UMSK__SHIFT) & AXXX_SCRATCH_UMSK_UMSK__MASK;
|
||||
}
|
||||
#define AXXX_SCRATCH_UMSK_SWAP__MASK 0x00030000
|
||||
#define AXXX_SCRATCH_UMSK_SWAP__SHIFT 16
|
||||
static inline uint32_t AXXX_SCRATCH_UMSK_SWAP(uint32_t val)
|
||||
{
|
||||
return ((val) << AXXX_SCRATCH_UMSK_SWAP__SHIFT) & AXXX_SCRATCH_UMSK_SWAP__MASK;
|
||||
}
|
||||
|
||||
#define REG_AXXX_SCRATCH_ADDR 0x000001dd
|
||||
|
||||
#define REG_AXXX_CP_ME_RDADDR 0x000001ea
|
||||
|
||||
#define REG_AXXX_CP_STATE_DEBUG_INDEX 0x000001ec
|
||||
|
||||
#define REG_AXXX_CP_STATE_DEBUG_DATA 0x000001ed
|
||||
|
||||
#define REG_AXXX_CP_INT_CNTL 0x000001f2
|
||||
|
||||
#define REG_AXXX_CP_INT_STATUS 0x000001f3
|
||||
|
||||
#define REG_AXXX_CP_INT_ACK 0x000001f4
|
||||
|
||||
#define REG_AXXX_CP_ME_CNTL 0x000001f6
|
||||
|
||||
#define REG_AXXX_CP_ME_STATUS 0x000001f7
|
||||
|
||||
#define REG_AXXX_CP_ME_RAM_WADDR 0x000001f8
|
||||
|
||||
#define REG_AXXX_CP_ME_RAM_RADDR 0x000001f9
|
||||
|
||||
#define REG_AXXX_CP_ME_RAM_DATA 0x000001fa
|
||||
|
||||
#define REG_AXXX_CP_DEBUG 0x000001fc
|
||||
#define AXXX_CP_DEBUG_PREDICATE_DISABLE 0x00800000
|
||||
#define AXXX_CP_DEBUG_PROG_END_PTR_ENABLE 0x01000000
|
||||
#define AXXX_CP_DEBUG_MIU_128BIT_WRITE_ENABLE 0x02000000
|
||||
#define AXXX_CP_DEBUG_PREFETCH_PASS_NOPS 0x04000000
|
||||
#define AXXX_CP_DEBUG_DYNAMIC_CLK_DISABLE 0x08000000
|
||||
#define AXXX_CP_DEBUG_PREFETCH_MATCH_DISABLE 0x10000000
|
||||
#define AXXX_CP_DEBUG_SIMPLE_ME_FLOW_CONTROL 0x40000000
|
||||
#define AXXX_CP_DEBUG_MIU_WRITE_PACK_DISABLE 0x80000000
|
||||
|
||||
#define REG_AXXX_CP_CSQ_RB_STAT 0x000001fd
|
||||
#define AXXX_CP_CSQ_RB_STAT_RPTR__MASK 0x0000007f
|
||||
#define AXXX_CP_CSQ_RB_STAT_RPTR__SHIFT 0
|
||||
static inline uint32_t AXXX_CP_CSQ_RB_STAT_RPTR(uint32_t val)
|
||||
{
|
||||
return ((val) << AXXX_CP_CSQ_RB_STAT_RPTR__SHIFT) & AXXX_CP_CSQ_RB_STAT_RPTR__MASK;
|
||||
}
|
||||
#define AXXX_CP_CSQ_RB_STAT_WPTR__MASK 0x007f0000
|
||||
#define AXXX_CP_CSQ_RB_STAT_WPTR__SHIFT 16
|
||||
static inline uint32_t AXXX_CP_CSQ_RB_STAT_WPTR(uint32_t val)
|
||||
{
|
||||
return ((val) << AXXX_CP_CSQ_RB_STAT_WPTR__SHIFT) & AXXX_CP_CSQ_RB_STAT_WPTR__MASK;
|
||||
}
|
||||
|
||||
#define REG_AXXX_CP_CSQ_IB1_STAT 0x000001fe
|
||||
#define AXXX_CP_CSQ_IB1_STAT_RPTR__MASK 0x0000007f
|
||||
#define AXXX_CP_CSQ_IB1_STAT_RPTR__SHIFT 0
|
||||
static inline uint32_t AXXX_CP_CSQ_IB1_STAT_RPTR(uint32_t val)
|
||||
{
|
||||
return ((val) << AXXX_CP_CSQ_IB1_STAT_RPTR__SHIFT) & AXXX_CP_CSQ_IB1_STAT_RPTR__MASK;
|
||||
}
|
||||
#define AXXX_CP_CSQ_IB1_STAT_WPTR__MASK 0x007f0000
|
||||
#define AXXX_CP_CSQ_IB1_STAT_WPTR__SHIFT 16
|
||||
static inline uint32_t AXXX_CP_CSQ_IB1_STAT_WPTR(uint32_t val)
|
||||
{
|
||||
return ((val) << AXXX_CP_CSQ_IB1_STAT_WPTR__SHIFT) & AXXX_CP_CSQ_IB1_STAT_WPTR__MASK;
|
||||
}
|
||||
|
||||
#define REG_AXXX_CP_CSQ_IB2_STAT 0x000001ff
|
||||
#define AXXX_CP_CSQ_IB2_STAT_RPTR__MASK 0x0000007f
|
||||
#define AXXX_CP_CSQ_IB2_STAT_RPTR__SHIFT 0
|
||||
static inline uint32_t AXXX_CP_CSQ_IB2_STAT_RPTR(uint32_t val)
|
||||
{
|
||||
return ((val) << AXXX_CP_CSQ_IB2_STAT_RPTR__SHIFT) & AXXX_CP_CSQ_IB2_STAT_RPTR__MASK;
|
||||
}
|
||||
#define AXXX_CP_CSQ_IB2_STAT_WPTR__MASK 0x007f0000
|
||||
#define AXXX_CP_CSQ_IB2_STAT_WPTR__SHIFT 16
|
||||
static inline uint32_t AXXX_CP_CSQ_IB2_STAT_WPTR(uint32_t val)
|
||||
{
|
||||
return ((val) << AXXX_CP_CSQ_IB2_STAT_WPTR__SHIFT) & AXXX_CP_CSQ_IB2_STAT_WPTR__MASK;
|
||||
}
|
||||
|
||||
#define REG_AXXX_CP_SCRATCH_REG0 0x00000578
|
||||
|
||||
#define REG_AXXX_CP_SCRATCH_REG1 0x00000579
|
||||
|
||||
#define REG_AXXX_CP_SCRATCH_REG2 0x0000057a
|
||||
|
||||
#define REG_AXXX_CP_SCRATCH_REG3 0x0000057b
|
||||
|
||||
#define REG_AXXX_CP_SCRATCH_REG4 0x0000057c
|
||||
|
||||
#define REG_AXXX_CP_SCRATCH_REG5 0x0000057d
|
||||
|
||||
#define REG_AXXX_CP_SCRATCH_REG6 0x0000057e
|
||||
|
||||
#define REG_AXXX_CP_SCRATCH_REG7 0x0000057f
|
||||
|
||||
#define REG_AXXX_CP_ME_CF_EVENT_SRC 0x0000060a
|
||||
|
||||
#define REG_AXXX_CP_ME_CF_EVENT_ADDR 0x0000060b
|
||||
|
||||
#define REG_AXXX_CP_ME_CF_EVENT_DATA 0x0000060c
|
||||
|
||||
#define REG_AXXX_CP_ME_NRT_ADDR 0x0000060d
|
||||
|
||||
#define REG_AXXX_CP_ME_NRT_DATA 0x0000060e
|
||||
|
||||
|
||||
#endif /* ADRENO_COMMON_XML */
|
|
@ -0,0 +1,370 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "adreno_gpu.h"
|
||||
#include "msm_gem.h"
|
||||
|
||||
struct adreno_info {
|
||||
struct adreno_rev rev;
|
||||
uint32_t revn;
|
||||
const char *name;
|
||||
const char *pm4fw, *pfpfw;
|
||||
uint32_t gmem;
|
||||
};
|
||||
|
||||
#define ANY_ID 0xff
|
||||
|
||||
static const struct adreno_info gpulist[] = {
|
||||
{
|
||||
.rev = ADRENO_REV(3, 0, 5, ANY_ID),
|
||||
.revn = 305,
|
||||
.name = "A305",
|
||||
.pm4fw = "a300_pm4.fw",
|
||||
.pfpfw = "a300_pfp.fw",
|
||||
.gmem = SZ_256K,
|
||||
}, {
|
||||
.rev = ADRENO_REV(3, 2, ANY_ID, ANY_ID),
|
||||
.revn = 320,
|
||||
.name = "A320",
|
||||
.pm4fw = "a300_pm4.fw",
|
||||
.pfpfw = "a300_pfp.fw",
|
||||
.gmem = SZ_512K,
|
||||
}, {
|
||||
.rev = ADRENO_REV(3, 3, 0, 0),
|
||||
.revn = 330,
|
||||
.name = "A330",
|
||||
.pm4fw = "a330_pm4.fw",
|
||||
.pfpfw = "a330_pfp.fw",
|
||||
.gmem = SZ_1M,
|
||||
},
|
||||
};
|
||||
|
||||
#define RB_SIZE SZ_32K
|
||||
#define RB_BLKSIZE 16
|
||||
|
||||
int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value)
|
||||
{
|
||||
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
|
||||
|
||||
switch (param) {
|
||||
case MSM_PARAM_GPU_ID:
|
||||
*value = adreno_gpu->info->revn;
|
||||
return 0;
|
||||
case MSM_PARAM_GMEM_SIZE:
|
||||
*value = adreno_gpu->info->gmem;
|
||||
return 0;
|
||||
default:
|
||||
DBG("%s: invalid param: %u", gpu->name, param);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#define rbmemptr(adreno_gpu, member) \
|
||||
((adreno_gpu)->memptrs_iova + offsetof(struct adreno_rbmemptrs, member))
|
||||
|
||||
int adreno_hw_init(struct msm_gpu *gpu)
|
||||
{
|
||||
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
|
||||
|
||||
DBG("%s", gpu->name);
|
||||
|
||||
/* Setup REG_CP_RB_CNTL: */
|
||||
gpu_write(gpu, REG_AXXX_CP_RB_CNTL,
|
||||
/* size is log2(quad-words): */
|
||||
AXXX_CP_RB_CNTL_BUFSZ(ilog2(gpu->rb->size / 8)) |
|
||||
AXXX_CP_RB_CNTL_BLKSZ(RB_BLKSIZE));
|
||||
|
||||
/* Setup ringbuffer address: */
|
||||
gpu_write(gpu, REG_AXXX_CP_RB_BASE, gpu->rb_iova);
|
||||
gpu_write(gpu, REG_AXXX_CP_RB_RPTR_ADDR, rbmemptr(adreno_gpu, rptr));
|
||||
|
||||
/* Setup scratch/timestamp: */
|
||||
gpu_write(gpu, REG_AXXX_SCRATCH_ADDR, rbmemptr(adreno_gpu, fence));
|
||||
|
||||
gpu_write(gpu, REG_AXXX_SCRATCH_UMSK, 0x1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t get_wptr(struct msm_ringbuffer *ring)
|
||||
{
|
||||
return ring->cur - ring->start;
|
||||
}
|
||||
|
||||
uint32_t adreno_last_fence(struct msm_gpu *gpu)
|
||||
{
|
||||
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
|
||||
return adreno_gpu->memptrs->fence;
|
||||
}
|
||||
|
||||
void adreno_recover(struct msm_gpu *gpu)
|
||||
{
|
||||
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
|
||||
struct drm_device *dev = gpu->dev;
|
||||
int ret;
|
||||
|
||||
gpu->funcs->pm_suspend(gpu);
|
||||
|
||||
/* reset ringbuffer: */
|
||||
gpu->rb->cur = gpu->rb->start;
|
||||
|
||||
/* reset completed fence seqno, just discard anything pending: */
|
||||
adreno_gpu->memptrs->fence = gpu->submitted_fence;
|
||||
|
||||
gpu->funcs->pm_resume(gpu);
|
||||
ret = gpu->funcs->hw_init(gpu);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "gpu hw init failed: %d\n", ret);
|
||||
/* hmm, oh well? */
|
||||
}
|
||||
}
|
||||
|
||||
int adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
|
||||
struct msm_file_private *ctx)
|
||||
{
|
||||
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
|
||||
struct msm_drm_private *priv = gpu->dev->dev_private;
|
||||
struct msm_ringbuffer *ring = gpu->rb;
|
||||
unsigned i, ibs = 0;
|
||||
|
||||
for (i = 0; i < submit->nr_cmds; i++) {
|
||||
switch (submit->cmd[i].type) {
|
||||
case MSM_SUBMIT_CMD_IB_TARGET_BUF:
|
||||
/* ignore IB-targets */
|
||||
break;
|
||||
case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
|
||||
/* ignore if there has not been a ctx switch: */
|
||||
if (priv->lastctx == ctx)
|
||||
break;
|
||||
case MSM_SUBMIT_CMD_BUF:
|
||||
OUT_PKT3(ring, CP_INDIRECT_BUFFER_PFD, 2);
|
||||
OUT_RING(ring, submit->cmd[i].iova);
|
||||
OUT_RING(ring, submit->cmd[i].size);
|
||||
ibs++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* on a320, at least, we seem to need to pad things out to an
|
||||
* even number of qwords to avoid issue w/ CP hanging on wrap-
|
||||
* around:
|
||||
*/
|
||||
if (ibs % 2)
|
||||
OUT_PKT2(ring);
|
||||
|
||||
OUT_PKT0(ring, REG_AXXX_CP_SCRATCH_REG2, 1);
|
||||
OUT_RING(ring, submit->fence);
|
||||
|
||||
if (adreno_is_a3xx(adreno_gpu)) {
|
||||
/* Flush HLSQ lazy updates to make sure there is nothing
|
||||
* pending for indirect loads after the timestamp has
|
||||
* passed:
|
||||
*/
|
||||
OUT_PKT3(ring, CP_EVENT_WRITE, 1);
|
||||
OUT_RING(ring, HLSQ_FLUSH);
|
||||
|
||||
OUT_PKT3(ring, CP_WAIT_FOR_IDLE, 1);
|
||||
OUT_RING(ring, 0x00000000);
|
||||
}
|
||||
|
||||
OUT_PKT3(ring, CP_EVENT_WRITE, 3);
|
||||
OUT_RING(ring, CACHE_FLUSH_TS);
|
||||
OUT_RING(ring, rbmemptr(adreno_gpu, fence));
|
||||
OUT_RING(ring, submit->fence);
|
||||
|
||||
/* we could maybe be clever and only CP_COND_EXEC the interrupt: */
|
||||
OUT_PKT3(ring, CP_INTERRUPT, 1);
|
||||
OUT_RING(ring, 0x80000000);
|
||||
|
||||
#if 0
|
||||
if (adreno_is_a3xx(adreno_gpu)) {
|
||||
/* Dummy set-constant to trigger context rollover */
|
||||
OUT_PKT3(ring, CP_SET_CONSTANT, 2);
|
||||
OUT_RING(ring, CP_REG(REG_A3XX_HLSQ_CL_KERNEL_GROUP_X_REG));
|
||||
OUT_RING(ring, 0x00000000);
|
||||
}
|
||||
#endif
|
||||
|
||||
gpu->funcs->flush(gpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void adreno_flush(struct msm_gpu *gpu)
|
||||
{
|
||||
uint32_t wptr = get_wptr(gpu->rb);
|
||||
|
||||
/* ensure writes to ringbuffer have hit system memory: */
|
||||
mb();
|
||||
|
||||
gpu_write(gpu, REG_AXXX_CP_RB_WPTR, wptr);
|
||||
}
|
||||
|
||||
void adreno_idle(struct msm_gpu *gpu)
|
||||
{
|
||||
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
|
||||
uint32_t rptr, wptr = get_wptr(gpu->rb);
|
||||
unsigned long t;
|
||||
|
||||
t = jiffies + ADRENO_IDLE_TIMEOUT;
|
||||
|
||||
/* then wait for CP to drain ringbuffer: */
|
||||
do {
|
||||
rptr = adreno_gpu->memptrs->rptr;
|
||||
if (rptr == wptr)
|
||||
return;
|
||||
} while(time_before(jiffies, t));
|
||||
|
||||
DRM_ERROR("timeout waiting for %s to drain ringbuffer!\n", gpu->name);
|
||||
|
||||
/* TODO maybe we need to reset GPU here to recover from hang? */
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void adreno_show(struct msm_gpu *gpu, struct seq_file *m)
|
||||
{
|
||||
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
|
||||
|
||||
seq_printf(m, "revision: %d (%d.%d.%d.%d)\n",
|
||||
adreno_gpu->info->revn, adreno_gpu->rev.core,
|
||||
adreno_gpu->rev.major, adreno_gpu->rev.minor,
|
||||
adreno_gpu->rev.patchid);
|
||||
|
||||
seq_printf(m, "fence: %d/%d\n", adreno_gpu->memptrs->fence,
|
||||
gpu->submitted_fence);
|
||||
seq_printf(m, "rptr: %d\n", adreno_gpu->memptrs->rptr);
|
||||
seq_printf(m, "wptr: %d\n", adreno_gpu->memptrs->wptr);
|
||||
seq_printf(m, "rb wptr: %d\n", get_wptr(gpu->rb));
|
||||
}
|
||||
#endif
|
||||
|
||||
void adreno_wait_ring(struct msm_gpu *gpu, uint32_t ndwords)
|
||||
{
|
||||
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
|
||||
uint32_t freedwords;
|
||||
do {
|
||||
uint32_t size = gpu->rb->size / 4;
|
||||
uint32_t wptr = get_wptr(gpu->rb);
|
||||
uint32_t rptr = adreno_gpu->memptrs->rptr;
|
||||
freedwords = (rptr + (size - 1) - wptr) % size;
|
||||
} while(freedwords < ndwords);
|
||||
}
|
||||
|
||||
static const char *iommu_ports[] = {
|
||||
"gfx3d_user", "gfx3d_priv",
|
||||
"gfx3d1_user", "gfx3d1_priv",
|
||||
};
|
||||
|
||||
static inline bool _rev_match(uint8_t entry, uint8_t id)
|
||||
{
|
||||
return (entry == ANY_ID) || (entry == id);
|
||||
}
|
||||
|
||||
int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
|
||||
struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs,
|
||||
struct adreno_rev rev)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
/* identify gpu: */
|
||||
for (i = 0; i < ARRAY_SIZE(gpulist); i++) {
|
||||
const struct adreno_info *info = &gpulist[i];
|
||||
if (_rev_match(info->rev.core, rev.core) &&
|
||||
_rev_match(info->rev.major, rev.major) &&
|
||||
_rev_match(info->rev.minor, rev.minor) &&
|
||||
_rev_match(info->rev.patchid, rev.patchid)) {
|
||||
gpu->info = info;
|
||||
gpu->revn = info->revn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(gpulist)) {
|
||||
dev_err(drm->dev, "Unknown GPU revision: %u.%u.%u.%u\n",
|
||||
rev.core, rev.major, rev.minor, rev.patchid);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
DBG("Found GPU: %s (%u.%u.%u.%u)", gpu->info->name,
|
||||
rev.core, rev.major, rev.minor, rev.patchid);
|
||||
|
||||
gpu->funcs = funcs;
|
||||
gpu->rev = rev;
|
||||
|
||||
ret = request_firmware(&gpu->pm4, gpu->info->pm4fw, drm->dev);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "failed to load %s PM4 firmware: %d\n",
|
||||
gpu->info->pm4fw, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = request_firmware(&gpu->pfp, gpu->info->pfpfw, drm->dev);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "failed to load %s PFP firmware: %d\n",
|
||||
gpu->info->pfpfw, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = msm_gpu_init(drm, pdev, &gpu->base, &funcs->base,
|
||||
gpu->info->name, "kgsl_3d0_reg_memory", "kgsl_3d0_irq",
|
||||
RB_SIZE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = msm_iommu_attach(drm, gpu->base.iommu,
|
||||
iommu_ports, ARRAY_SIZE(iommu_ports));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
gpu->memptrs_bo = msm_gem_new(drm, sizeof(*gpu->memptrs),
|
||||
MSM_BO_UNCACHED);
|
||||
if (IS_ERR(gpu->memptrs_bo)) {
|
||||
ret = PTR_ERR(gpu->memptrs_bo);
|
||||
gpu->memptrs_bo = NULL;
|
||||
dev_err(drm->dev, "could not allocate memptrs: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gpu->memptrs = msm_gem_vaddr_locked(gpu->memptrs_bo);
|
||||
if (!gpu->memptrs) {
|
||||
dev_err(drm->dev, "could not vmap memptrs\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = msm_gem_get_iova_locked(gpu->memptrs_bo, gpu->base.id,
|
||||
&gpu->memptrs_iova);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "could not map memptrs: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void adreno_gpu_cleanup(struct adreno_gpu *gpu)
|
||||
{
|
||||
if (gpu->memptrs_bo) {
|
||||
if (gpu->memptrs_iova)
|
||||
msm_gem_put_iova(gpu->memptrs_bo, gpu->base.id);
|
||||
drm_gem_object_unreference(gpu->memptrs_bo);
|
||||
}
|
||||
if (gpu->pm4)
|
||||
release_firmware(gpu->pm4);
|
||||
if (gpu->pfp)
|
||||
release_firmware(gpu->pfp);
|
||||
msm_gpu_cleanup(&gpu->base);
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __ADRENO_GPU_H__
|
||||
#define __ADRENO_GPU_H__
|
||||
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include "msm_gpu.h"
|
||||
|
||||
#include "adreno_common.xml.h"
|
||||
#include "adreno_pm4.xml.h"
|
||||
|
||||
struct adreno_rev {
|
||||
uint8_t core;
|
||||
uint8_t major;
|
||||
uint8_t minor;
|
||||
uint8_t patchid;
|
||||
};
|
||||
|
||||
#define ADRENO_REV(core, major, minor, patchid) \
|
||||
((struct adreno_rev){ core, major, minor, patchid })
|
||||
|
||||
struct adreno_gpu_funcs {
|
||||
struct msm_gpu_funcs base;
|
||||
};
|
||||
|
||||
struct adreno_info;
|
||||
|
||||
struct adreno_rbmemptrs {
|
||||
volatile uint32_t rptr;
|
||||
volatile uint32_t wptr;
|
||||
volatile uint32_t fence;
|
||||
};
|
||||
|
||||
struct adreno_gpu {
|
||||
struct msm_gpu base;
|
||||
struct adreno_rev rev;
|
||||
const struct adreno_info *info;
|
||||
uint32_t revn; /* numeric revision name */
|
||||
const struct adreno_gpu_funcs *funcs;
|
||||
|
||||
/* firmware: */
|
||||
const struct firmware *pm4, *pfp;
|
||||
|
||||
/* ringbuffer rptr/wptr: */
|
||||
// TODO should this be in msm_ringbuffer? I think it would be
|
||||
// different for z180..
|
||||
struct adreno_rbmemptrs *memptrs;
|
||||
struct drm_gem_object *memptrs_bo;
|
||||
uint32_t memptrs_iova;
|
||||
};
|
||||
#define to_adreno_gpu(x) container_of(x, struct adreno_gpu, base)
|
||||
|
||||
/* platform config data (ie. from DT, or pdata) */
|
||||
struct adreno_platform_config {
|
||||
struct adreno_rev rev;
|
||||
uint32_t fast_rate, slow_rate, bus_freq;
|
||||
};
|
||||
|
||||
#define ADRENO_IDLE_TIMEOUT (20 * 1000)
|
||||
|
||||
static inline bool adreno_is_a3xx(struct adreno_gpu *gpu)
|
||||
{
|
||||
return (gpu->revn >= 300) && (gpu->revn < 400);
|
||||
}
|
||||
|
||||
static inline bool adreno_is_a305(struct adreno_gpu *gpu)
|
||||
{
|
||||
return gpu->revn == 305;
|
||||
}
|
||||
|
||||
static inline bool adreno_is_a320(struct adreno_gpu *gpu)
|
||||
{
|
||||
return gpu->revn == 320;
|
||||
}
|
||||
|
||||
static inline bool adreno_is_a330(struct adreno_gpu *gpu)
|
||||
{
|
||||
return gpu->revn == 330;
|
||||
}
|
||||
|
||||
int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value);
|
||||
int adreno_hw_init(struct msm_gpu *gpu);
|
||||
uint32_t adreno_last_fence(struct msm_gpu *gpu);
|
||||
void adreno_recover(struct msm_gpu *gpu);
|
||||
int adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
|
||||
struct msm_file_private *ctx);
|
||||
void adreno_flush(struct msm_gpu *gpu);
|
||||
void adreno_idle(struct msm_gpu *gpu);
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void adreno_show(struct msm_gpu *gpu, struct seq_file *m);
|
||||
#endif
|
||||
void adreno_wait_ring(struct msm_gpu *gpu, uint32_t ndwords);
|
||||
|
||||
int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
|
||||
struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs,
|
||||
struct adreno_rev rev);
|
||||
void adreno_gpu_cleanup(struct adreno_gpu *gpu);
|
||||
|
||||
|
||||
/* ringbuffer helpers (the parts that are adreno specific) */
|
||||
|
||||
static inline void
|
||||
OUT_PKT0(struct msm_ringbuffer *ring, uint16_t regindx, uint16_t cnt)
|
||||
{
|
||||
adreno_wait_ring(ring->gpu, cnt+1);
|
||||
OUT_RING(ring, CP_TYPE0_PKT | ((cnt-1) << 16) | (regindx & 0x7FFF));
|
||||
}
|
||||
|
||||
/* no-op packet: */
|
||||
static inline void
|
||||
OUT_PKT2(struct msm_ringbuffer *ring)
|
||||
{
|
||||
adreno_wait_ring(ring->gpu, 1);
|
||||
OUT_RING(ring, CP_TYPE2_PKT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
OUT_PKT3(struct msm_ringbuffer *ring, uint8_t opcode, uint16_t cnt)
|
||||
{
|
||||
adreno_wait_ring(ring->gpu, cnt+1);
|
||||
OUT_RING(ring, CP_TYPE3_PKT | ((cnt-1) << 16) | ((opcode & 0xFF) << 8));
|
||||
}
|
||||
|
||||
|
||||
#endif /* __ADRENO_GPU_H__ */
|
|
@ -0,0 +1,254 @@
|
|||
#ifndef ADRENO_PM4_XML
|
||||
#define ADRENO_PM4_XML
|
||||
|
||||
/* Autogenerated file, DO NOT EDIT manually!
|
||||
|
||||
This file was generated by the rules-ng-ng headergen tool in this git repository:
|
||||
http://0x04.net/cgit/index.cgi/rules-ng-ng
|
||||
git clone git://0x04.net/rules-ng-ng
|
||||
|
||||
The rules-ng-ng source files this header was generated from are:
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml ( 327 bytes, from 2013-07-05 19:21:12)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/a2xx/a2xx.xml ( 30005 bytes, from 2013-07-19 21:30:48)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/adreno_common.xml ( 8983 bytes, from 2013-07-24 01:38:36)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/adreno_pm4.xml ( 9712 bytes, from 2013-05-26 15:22:37)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/a3xx/a3xx.xml ( 51415 bytes, from 2013-08-03 14:26:05)
|
||||
|
||||
Copyright (C) 2013 by the following authors:
|
||||
- Rob Clark <robdclark@gmail.com> (robclark)
|
||||
|
||||
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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS 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.
|
||||
*/
|
||||
|
||||
|
||||
enum vgt_event_type {
|
||||
VS_DEALLOC = 0,
|
||||
PS_DEALLOC = 1,
|
||||
VS_DONE_TS = 2,
|
||||
PS_DONE_TS = 3,
|
||||
CACHE_FLUSH_TS = 4,
|
||||
CONTEXT_DONE = 5,
|
||||
CACHE_FLUSH = 6,
|
||||
HLSQ_FLUSH = 7,
|
||||
VIZQUERY_START = 7,
|
||||
VIZQUERY_END = 8,
|
||||
SC_WAIT_WC = 9,
|
||||
RST_PIX_CNT = 13,
|
||||
RST_VTX_CNT = 14,
|
||||
TILE_FLUSH = 15,
|
||||
CACHE_FLUSH_AND_INV_TS_EVENT = 20,
|
||||
ZPASS_DONE = 21,
|
||||
CACHE_FLUSH_AND_INV_EVENT = 22,
|
||||
PERFCOUNTER_START = 23,
|
||||
PERFCOUNTER_STOP = 24,
|
||||
VS_FETCH_DONE = 27,
|
||||
FACENESS_FLUSH = 28,
|
||||
};
|
||||
|
||||
enum pc_di_primtype {
|
||||
DI_PT_NONE = 0,
|
||||
DI_PT_POINTLIST = 1,
|
||||
DI_PT_LINELIST = 2,
|
||||
DI_PT_LINESTRIP = 3,
|
||||
DI_PT_TRILIST = 4,
|
||||
DI_PT_TRIFAN = 5,
|
||||
DI_PT_TRISTRIP = 6,
|
||||
DI_PT_RECTLIST = 8,
|
||||
DI_PT_QUADLIST = 13,
|
||||
DI_PT_QUADSTRIP = 14,
|
||||
DI_PT_POLYGON = 15,
|
||||
DI_PT_2D_COPY_RECT_LIST_V0 = 16,
|
||||
DI_PT_2D_COPY_RECT_LIST_V1 = 17,
|
||||
DI_PT_2D_COPY_RECT_LIST_V2 = 18,
|
||||
DI_PT_2D_COPY_RECT_LIST_V3 = 19,
|
||||
DI_PT_2D_FILL_RECT_LIST = 20,
|
||||
DI_PT_2D_LINE_STRIP = 21,
|
||||
DI_PT_2D_TRI_STRIP = 22,
|
||||
};
|
||||
|
||||
enum pc_di_src_sel {
|
||||
DI_SRC_SEL_DMA = 0,
|
||||
DI_SRC_SEL_IMMEDIATE = 1,
|
||||
DI_SRC_SEL_AUTO_INDEX = 2,
|
||||
DI_SRC_SEL_RESERVED = 3,
|
||||
};
|
||||
|
||||
enum pc_di_index_size {
|
||||
INDEX_SIZE_IGN = 0,
|
||||
INDEX_SIZE_16_BIT = 0,
|
||||
INDEX_SIZE_32_BIT = 1,
|
||||
INDEX_SIZE_8_BIT = 2,
|
||||
INDEX_SIZE_INVALID = 0,
|
||||
};
|
||||
|
||||
enum pc_di_vis_cull_mode {
|
||||
IGNORE_VISIBILITY = 0,
|
||||
};
|
||||
|
||||
enum adreno_pm4_packet_type {
|
||||
CP_TYPE0_PKT = 0,
|
||||
CP_TYPE1_PKT = 0x40000000,
|
||||
CP_TYPE2_PKT = 0x80000000,
|
||||
CP_TYPE3_PKT = 0xc0000000,
|
||||
};
|
||||
|
||||
enum adreno_pm4_type3_packets {
|
||||
CP_ME_INIT = 72,
|
||||
CP_NOP = 16,
|
||||
CP_INDIRECT_BUFFER = 63,
|
||||
CP_INDIRECT_BUFFER_PFD = 55,
|
||||
CP_WAIT_FOR_IDLE = 38,
|
||||
CP_WAIT_REG_MEM = 60,
|
||||
CP_WAIT_REG_EQ = 82,
|
||||
CP_WAT_REG_GTE = 83,
|
||||
CP_WAIT_UNTIL_READ = 92,
|
||||
CP_WAIT_IB_PFD_COMPLETE = 93,
|
||||
CP_REG_RMW = 33,
|
||||
CP_SET_BIN_DATA = 47,
|
||||
CP_REG_TO_MEM = 62,
|
||||
CP_MEM_WRITE = 61,
|
||||
CP_MEM_WRITE_CNTR = 79,
|
||||
CP_COND_EXEC = 68,
|
||||
CP_COND_WRITE = 69,
|
||||
CP_EVENT_WRITE = 70,
|
||||
CP_EVENT_WRITE_SHD = 88,
|
||||
CP_EVENT_WRITE_CFL = 89,
|
||||
CP_EVENT_WRITE_ZPD = 91,
|
||||
CP_RUN_OPENCL = 49,
|
||||
CP_DRAW_INDX = 34,
|
||||
CP_DRAW_INDX_2 = 54,
|
||||
CP_DRAW_INDX_BIN = 52,
|
||||
CP_DRAW_INDX_2_BIN = 53,
|
||||
CP_VIZ_QUERY = 35,
|
||||
CP_SET_STATE = 37,
|
||||
CP_SET_CONSTANT = 45,
|
||||
CP_IM_LOAD = 39,
|
||||
CP_IM_LOAD_IMMEDIATE = 43,
|
||||
CP_LOAD_CONSTANT_CONTEXT = 46,
|
||||
CP_INVALIDATE_STATE = 59,
|
||||
CP_SET_SHADER_BASES = 74,
|
||||
CP_SET_BIN_MASK = 80,
|
||||
CP_SET_BIN_SELECT = 81,
|
||||
CP_CONTEXT_UPDATE = 94,
|
||||
CP_INTERRUPT = 64,
|
||||
CP_IM_STORE = 44,
|
||||
CP_SET_BIN_BASE_OFFSET = 75,
|
||||
CP_SET_DRAW_INIT_FLAGS = 75,
|
||||
CP_SET_PROTECTED_MODE = 95,
|
||||
CP_LOAD_STATE = 48,
|
||||
CP_COND_INDIRECT_BUFFER_PFE = 58,
|
||||
CP_COND_INDIRECT_BUFFER_PFD = 50,
|
||||
CP_INDIRECT_BUFFER_PFE = 63,
|
||||
CP_SET_BIN = 76,
|
||||
};
|
||||
|
||||
enum adreno_state_block {
|
||||
SB_VERT_TEX = 0,
|
||||
SB_VERT_MIPADDR = 1,
|
||||
SB_FRAG_TEX = 2,
|
||||
SB_FRAG_MIPADDR = 3,
|
||||
SB_VERT_SHADER = 4,
|
||||
SB_FRAG_SHADER = 6,
|
||||
};
|
||||
|
||||
enum adreno_state_type {
|
||||
ST_SHADER = 0,
|
||||
ST_CONSTANTS = 1,
|
||||
};
|
||||
|
||||
enum adreno_state_src {
|
||||
SS_DIRECT = 0,
|
||||
SS_INDIRECT = 4,
|
||||
};
|
||||
|
||||
#define REG_CP_LOAD_STATE_0 0x00000000
|
||||
#define CP_LOAD_STATE_0_DST_OFF__MASK 0x0000ffff
|
||||
#define CP_LOAD_STATE_0_DST_OFF__SHIFT 0
|
||||
static inline uint32_t CP_LOAD_STATE_0_DST_OFF(uint32_t val)
|
||||
{
|
||||
return ((val) << CP_LOAD_STATE_0_DST_OFF__SHIFT) & CP_LOAD_STATE_0_DST_OFF__MASK;
|
||||
}
|
||||
#define CP_LOAD_STATE_0_STATE_SRC__MASK 0x00070000
|
||||
#define CP_LOAD_STATE_0_STATE_SRC__SHIFT 16
|
||||
static inline uint32_t CP_LOAD_STATE_0_STATE_SRC(enum adreno_state_src val)
|
||||
{
|
||||
return ((val) << CP_LOAD_STATE_0_STATE_SRC__SHIFT) & CP_LOAD_STATE_0_STATE_SRC__MASK;
|
||||
}
|
||||
#define CP_LOAD_STATE_0_STATE_BLOCK__MASK 0x00380000
|
||||
#define CP_LOAD_STATE_0_STATE_BLOCK__SHIFT 19
|
||||
static inline uint32_t CP_LOAD_STATE_0_STATE_BLOCK(enum adreno_state_block val)
|
||||
{
|
||||
return ((val) << CP_LOAD_STATE_0_STATE_BLOCK__SHIFT) & CP_LOAD_STATE_0_STATE_BLOCK__MASK;
|
||||
}
|
||||
#define CP_LOAD_STATE_0_NUM_UNIT__MASK 0x7fc00000
|
||||
#define CP_LOAD_STATE_0_NUM_UNIT__SHIFT 22
|
||||
static inline uint32_t CP_LOAD_STATE_0_NUM_UNIT(uint32_t val)
|
||||
{
|
||||
return ((val) << CP_LOAD_STATE_0_NUM_UNIT__SHIFT) & CP_LOAD_STATE_0_NUM_UNIT__MASK;
|
||||
}
|
||||
|
||||
#define REG_CP_LOAD_STATE_1 0x00000001
|
||||
#define CP_LOAD_STATE_1_STATE_TYPE__MASK 0x00000003
|
||||
#define CP_LOAD_STATE_1_STATE_TYPE__SHIFT 0
|
||||
static inline uint32_t CP_LOAD_STATE_1_STATE_TYPE(enum adreno_state_type val)
|
||||
{
|
||||
return ((val) << CP_LOAD_STATE_1_STATE_TYPE__SHIFT) & CP_LOAD_STATE_1_STATE_TYPE__MASK;
|
||||
}
|
||||
#define CP_LOAD_STATE_1_EXT_SRC_ADDR__MASK 0xfffffffc
|
||||
#define CP_LOAD_STATE_1_EXT_SRC_ADDR__SHIFT 2
|
||||
static inline uint32_t CP_LOAD_STATE_1_EXT_SRC_ADDR(uint32_t val)
|
||||
{
|
||||
return ((val >> 2) << CP_LOAD_STATE_1_EXT_SRC_ADDR__SHIFT) & CP_LOAD_STATE_1_EXT_SRC_ADDR__MASK;
|
||||
}
|
||||
|
||||
#define REG_CP_SET_BIN_0 0x00000000
|
||||
|
||||
#define REG_CP_SET_BIN_1 0x00000001
|
||||
#define CP_SET_BIN_1_X1__MASK 0x0000ffff
|
||||
#define CP_SET_BIN_1_X1__SHIFT 0
|
||||
static inline uint32_t CP_SET_BIN_1_X1(uint32_t val)
|
||||
{
|
||||
return ((val) << CP_SET_BIN_1_X1__SHIFT) & CP_SET_BIN_1_X1__MASK;
|
||||
}
|
||||
#define CP_SET_BIN_1_Y1__MASK 0xffff0000
|
||||
#define CP_SET_BIN_1_Y1__SHIFT 16
|
||||
static inline uint32_t CP_SET_BIN_1_Y1(uint32_t val)
|
||||
{
|
||||
return ((val) << CP_SET_BIN_1_Y1__SHIFT) & CP_SET_BIN_1_Y1__MASK;
|
||||
}
|
||||
|
||||
#define REG_CP_SET_BIN_2 0x00000002
|
||||
#define CP_SET_BIN_2_X2__MASK 0x0000ffff
|
||||
#define CP_SET_BIN_2_X2__SHIFT 0
|
||||
static inline uint32_t CP_SET_BIN_2_X2(uint32_t val)
|
||||
{
|
||||
return ((val) << CP_SET_BIN_2_X2__SHIFT) & CP_SET_BIN_2_X2__MASK;
|
||||
}
|
||||
#define CP_SET_BIN_2_Y2__MASK 0xffff0000
|
||||
#define CP_SET_BIN_2_Y2__SHIFT 16
|
||||
static inline uint32_t CP_SET_BIN_2_Y2(uint32_t val)
|
||||
{
|
||||
return ((val) << CP_SET_BIN_2_Y2__SHIFT) & CP_SET_BIN_2_Y2__MASK;
|
||||
}
|
||||
|
||||
|
||||
#endif /* ADRENO_PM4_XML */
|
|
@ -0,0 +1,502 @@
|
|||
#ifndef DSI_XML
|
||||
#define DSI_XML
|
||||
|
||||
/* Autogenerated file, DO NOT EDIT manually!
|
||||
|
||||
This file was generated by the rules-ng-ng headergen tool in this git repository:
|
||||
http://0x04.net/cgit/index.cgi/rules-ng-ng
|
||||
git clone git://0x04.net/rules-ng-ng
|
||||
|
||||
The rules-ng-ng source files this header was generated from are:
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 595 bytes, from 2013-07-05 19:21:12)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml ( 19332 bytes, from 2013-08-16 22:16:36)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1544 bytes, from 2013-08-16 19:17:05)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 19288 bytes, from 2013-08-11 18:14:15)
|
||||
|
||||
Copyright (C) 2013 by the following authors:
|
||||
- Rob Clark <robdclark@gmail.com> (robclark)
|
||||
|
||||
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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS 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.
|
||||
*/
|
||||
|
||||
|
||||
enum dsi_traffic_mode {
|
||||
NON_BURST_SYNCH_PULSE = 0,
|
||||
NON_BURST_SYNCH_EVENT = 1,
|
||||
BURST_MODE = 2,
|
||||
};
|
||||
|
||||
enum dsi_dst_format {
|
||||
DST_FORMAT_RGB565 = 0,
|
||||
DST_FORMAT_RGB666 = 1,
|
||||
DST_FORMAT_RGB666_LOOSE = 2,
|
||||
DST_FORMAT_RGB888 = 3,
|
||||
};
|
||||
|
||||
enum dsi_rgb_swap {
|
||||
SWAP_RGB = 0,
|
||||
SWAP_RBG = 1,
|
||||
SWAP_BGR = 2,
|
||||
SWAP_BRG = 3,
|
||||
SWAP_GRB = 4,
|
||||
SWAP_GBR = 5,
|
||||
};
|
||||
|
||||
enum dsi_cmd_trigger {
|
||||
TRIGGER_NONE = 0,
|
||||
TRIGGER_TE = 2,
|
||||
TRIGGER_SW = 4,
|
||||
TRIGGER_SW_SEOF = 5,
|
||||
TRIGGER_SW_TE = 6,
|
||||
};
|
||||
|
||||
#define DSI_IRQ_CMD_DMA_DONE 0x00000001
|
||||
#define DSI_IRQ_MASK_CMD_DMA_DONE 0x00000002
|
||||
#define DSI_IRQ_CMD_MDP_DONE 0x00000100
|
||||
#define DSI_IRQ_MASK_CMD_MDP_DONE 0x00000200
|
||||
#define DSI_IRQ_VIDEO_DONE 0x00010000
|
||||
#define DSI_IRQ_MASK_VIDEO_DONE 0x00020000
|
||||
#define DSI_IRQ_ERROR 0x01000000
|
||||
#define DSI_IRQ_MASK_ERROR 0x02000000
|
||||
#define REG_DSI_CTRL 0x00000000
|
||||
#define DSI_CTRL_ENABLE 0x00000001
|
||||
#define DSI_CTRL_VID_MODE_EN 0x00000002
|
||||
#define DSI_CTRL_CMD_MODE_EN 0x00000004
|
||||
#define DSI_CTRL_LANE0 0x00000010
|
||||
#define DSI_CTRL_LANE1 0x00000020
|
||||
#define DSI_CTRL_LANE2 0x00000040
|
||||
#define DSI_CTRL_LANE3 0x00000080
|
||||
#define DSI_CTRL_CLK_EN 0x00000100
|
||||
#define DSI_CTRL_ECC_CHECK 0x00100000
|
||||
#define DSI_CTRL_CRC_CHECK 0x01000000
|
||||
|
||||
#define REG_DSI_STATUS0 0x00000004
|
||||
#define DSI_STATUS0_CMD_MODE_DMA_BUSY 0x00000002
|
||||
#define DSI_STATUS0_VIDEO_MODE_ENGINE_BUSY 0x00000008
|
||||
#define DSI_STATUS0_DSI_BUSY 0x00000010
|
||||
|
||||
#define REG_DSI_FIFO_STATUS 0x00000008
|
||||
|
||||
#define REG_DSI_VID_CFG0 0x0000000c
|
||||
#define DSI_VID_CFG0_VIRT_CHANNEL__MASK 0x00000003
|
||||
#define DSI_VID_CFG0_VIRT_CHANNEL__SHIFT 0
|
||||
static inline uint32_t DSI_VID_CFG0_VIRT_CHANNEL(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_VID_CFG0_VIRT_CHANNEL__SHIFT) & DSI_VID_CFG0_VIRT_CHANNEL__MASK;
|
||||
}
|
||||
#define DSI_VID_CFG0_DST_FORMAT__MASK 0x00000030
|
||||
#define DSI_VID_CFG0_DST_FORMAT__SHIFT 4
|
||||
static inline uint32_t DSI_VID_CFG0_DST_FORMAT(enum dsi_dst_format val)
|
||||
{
|
||||
return ((val) << DSI_VID_CFG0_DST_FORMAT__SHIFT) & DSI_VID_CFG0_DST_FORMAT__MASK;
|
||||
}
|
||||
#define DSI_VID_CFG0_TRAFFIC_MODE__MASK 0x00000300
|
||||
#define DSI_VID_CFG0_TRAFFIC_MODE__SHIFT 8
|
||||
static inline uint32_t DSI_VID_CFG0_TRAFFIC_MODE(enum dsi_traffic_mode val)
|
||||
{
|
||||
return ((val) << DSI_VID_CFG0_TRAFFIC_MODE__SHIFT) & DSI_VID_CFG0_TRAFFIC_MODE__MASK;
|
||||
}
|
||||
#define DSI_VID_CFG0_BLLP_POWER_STOP 0x00001000
|
||||
#define DSI_VID_CFG0_EOF_BLLP_POWER_STOP 0x00008000
|
||||
#define DSI_VID_CFG0_HSA_POWER_STOP 0x00010000
|
||||
#define DSI_VID_CFG0_HBP_POWER_STOP 0x00100000
|
||||
#define DSI_VID_CFG0_HFP_POWER_STOP 0x01000000
|
||||
#define DSI_VID_CFG0_PULSE_MODE_HSA_HE 0x10000000
|
||||
|
||||
#define REG_DSI_VID_CFG1 0x0000001c
|
||||
#define DSI_VID_CFG1_R_SEL 0x00000010
|
||||
#define DSI_VID_CFG1_G_SEL 0x00000100
|
||||
#define DSI_VID_CFG1_B_SEL 0x00001000
|
||||
#define DSI_VID_CFG1_RGB_SWAP__MASK 0x00070000
|
||||
#define DSI_VID_CFG1_RGB_SWAP__SHIFT 16
|
||||
static inline uint32_t DSI_VID_CFG1_RGB_SWAP(enum dsi_rgb_swap val)
|
||||
{
|
||||
return ((val) << DSI_VID_CFG1_RGB_SWAP__SHIFT) & DSI_VID_CFG1_RGB_SWAP__MASK;
|
||||
}
|
||||
#define DSI_VID_CFG1_INTERLEAVE_MAX__MASK 0x00f00000
|
||||
#define DSI_VID_CFG1_INTERLEAVE_MAX__SHIFT 20
|
||||
static inline uint32_t DSI_VID_CFG1_INTERLEAVE_MAX(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_VID_CFG1_INTERLEAVE_MAX__SHIFT) & DSI_VID_CFG1_INTERLEAVE_MAX__MASK;
|
||||
}
|
||||
|
||||
#define REG_DSI_ACTIVE_H 0x00000020
|
||||
#define DSI_ACTIVE_H_START__MASK 0x00000fff
|
||||
#define DSI_ACTIVE_H_START__SHIFT 0
|
||||
static inline uint32_t DSI_ACTIVE_H_START(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_ACTIVE_H_START__SHIFT) & DSI_ACTIVE_H_START__MASK;
|
||||
}
|
||||
#define DSI_ACTIVE_H_END__MASK 0x0fff0000
|
||||
#define DSI_ACTIVE_H_END__SHIFT 16
|
||||
static inline uint32_t DSI_ACTIVE_H_END(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_ACTIVE_H_END__SHIFT) & DSI_ACTIVE_H_END__MASK;
|
||||
}
|
||||
|
||||
#define REG_DSI_ACTIVE_V 0x00000024
|
||||
#define DSI_ACTIVE_V_START__MASK 0x00000fff
|
||||
#define DSI_ACTIVE_V_START__SHIFT 0
|
||||
static inline uint32_t DSI_ACTIVE_V_START(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_ACTIVE_V_START__SHIFT) & DSI_ACTIVE_V_START__MASK;
|
||||
}
|
||||
#define DSI_ACTIVE_V_END__MASK 0x0fff0000
|
||||
#define DSI_ACTIVE_V_END__SHIFT 16
|
||||
static inline uint32_t DSI_ACTIVE_V_END(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_ACTIVE_V_END__SHIFT) & DSI_ACTIVE_V_END__MASK;
|
||||
}
|
||||
|
||||
#define REG_DSI_TOTAL 0x00000028
|
||||
#define DSI_TOTAL_H_TOTAL__MASK 0x00000fff
|
||||
#define DSI_TOTAL_H_TOTAL__SHIFT 0
|
||||
static inline uint32_t DSI_TOTAL_H_TOTAL(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_TOTAL_H_TOTAL__SHIFT) & DSI_TOTAL_H_TOTAL__MASK;
|
||||
}
|
||||
#define DSI_TOTAL_V_TOTAL__MASK 0x0fff0000
|
||||
#define DSI_TOTAL_V_TOTAL__SHIFT 16
|
||||
static inline uint32_t DSI_TOTAL_V_TOTAL(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_TOTAL_V_TOTAL__SHIFT) & DSI_TOTAL_V_TOTAL__MASK;
|
||||
}
|
||||
|
||||
#define REG_DSI_ACTIVE_HSYNC 0x0000002c
|
||||
#define DSI_ACTIVE_HSYNC_START__MASK 0x00000fff
|
||||
#define DSI_ACTIVE_HSYNC_START__SHIFT 0
|
||||
static inline uint32_t DSI_ACTIVE_HSYNC_START(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_ACTIVE_HSYNC_START__SHIFT) & DSI_ACTIVE_HSYNC_START__MASK;
|
||||
}
|
||||
#define DSI_ACTIVE_HSYNC_END__MASK 0x0fff0000
|
||||
#define DSI_ACTIVE_HSYNC_END__SHIFT 16
|
||||
static inline uint32_t DSI_ACTIVE_HSYNC_END(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_ACTIVE_HSYNC_END__SHIFT) & DSI_ACTIVE_HSYNC_END__MASK;
|
||||
}
|
||||
|
||||
#define REG_DSI_ACTIVE_VSYNC 0x00000034
|
||||
#define DSI_ACTIVE_VSYNC_START__MASK 0x00000fff
|
||||
#define DSI_ACTIVE_VSYNC_START__SHIFT 0
|
||||
static inline uint32_t DSI_ACTIVE_VSYNC_START(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_ACTIVE_VSYNC_START__SHIFT) & DSI_ACTIVE_VSYNC_START__MASK;
|
||||
}
|
||||
#define DSI_ACTIVE_VSYNC_END__MASK 0x0fff0000
|
||||
#define DSI_ACTIVE_VSYNC_END__SHIFT 16
|
||||
static inline uint32_t DSI_ACTIVE_VSYNC_END(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_ACTIVE_VSYNC_END__SHIFT) & DSI_ACTIVE_VSYNC_END__MASK;
|
||||
}
|
||||
|
||||
#define REG_DSI_CMD_DMA_CTRL 0x00000038
|
||||
#define DSI_CMD_DMA_CTRL_FROM_FRAME_BUFFER 0x10000000
|
||||
#define DSI_CMD_DMA_CTRL_LOW_POWER 0x04000000
|
||||
|
||||
#define REG_DSI_CMD_CFG0 0x0000003c
|
||||
|
||||
#define REG_DSI_CMD_CFG1 0x00000040
|
||||
|
||||
#define REG_DSI_DMA_BASE 0x00000044
|
||||
|
||||
#define REG_DSI_DMA_LEN 0x00000048
|
||||
|
||||
#define REG_DSI_ACK_ERR_STATUS 0x00000064
|
||||
|
||||
static inline uint32_t REG_DSI_RDBK(uint32_t i0) { return 0x00000068 + 0x4*i0; }
|
||||
|
||||
static inline uint32_t REG_DSI_RDBK_DATA(uint32_t i0) { return 0x00000068 + 0x4*i0; }
|
||||
|
||||
#define REG_DSI_TRIG_CTRL 0x00000080
|
||||
#define DSI_TRIG_CTRL_DMA_TRIGGER__MASK 0x0000000f
|
||||
#define DSI_TRIG_CTRL_DMA_TRIGGER__SHIFT 0
|
||||
static inline uint32_t DSI_TRIG_CTRL_DMA_TRIGGER(enum dsi_cmd_trigger val)
|
||||
{
|
||||
return ((val) << DSI_TRIG_CTRL_DMA_TRIGGER__SHIFT) & DSI_TRIG_CTRL_DMA_TRIGGER__MASK;
|
||||
}
|
||||
#define DSI_TRIG_CTRL_MDP_TRIGGER__MASK 0x000000f0
|
||||
#define DSI_TRIG_CTRL_MDP_TRIGGER__SHIFT 4
|
||||
static inline uint32_t DSI_TRIG_CTRL_MDP_TRIGGER(enum dsi_cmd_trigger val)
|
||||
{
|
||||
return ((val) << DSI_TRIG_CTRL_MDP_TRIGGER__SHIFT) & DSI_TRIG_CTRL_MDP_TRIGGER__MASK;
|
||||
}
|
||||
#define DSI_TRIG_CTRL_STREAM 0x00000100
|
||||
#define DSI_TRIG_CTRL_TE 0x80000000
|
||||
|
||||
#define REG_DSI_TRIG_DMA 0x0000008c
|
||||
|
||||
#define REG_DSI_DLN0_PHY_ERR 0x000000b0
|
||||
|
||||
#define REG_DSI_TIMEOUT_STATUS 0x000000bc
|
||||
|
||||
#define REG_DSI_CLKOUT_TIMING_CTRL 0x000000c0
|
||||
#define DSI_CLKOUT_TIMING_CTRL_T_CLK_PRE__MASK 0x0000003f
|
||||
#define DSI_CLKOUT_TIMING_CTRL_T_CLK_PRE__SHIFT 0
|
||||
static inline uint32_t DSI_CLKOUT_TIMING_CTRL_T_CLK_PRE(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_CLKOUT_TIMING_CTRL_T_CLK_PRE__SHIFT) & DSI_CLKOUT_TIMING_CTRL_T_CLK_PRE__MASK;
|
||||
}
|
||||
#define DSI_CLKOUT_TIMING_CTRL_T_CLK_POST__MASK 0x00003f00
|
||||
#define DSI_CLKOUT_TIMING_CTRL_T_CLK_POST__SHIFT 8
|
||||
static inline uint32_t DSI_CLKOUT_TIMING_CTRL_T_CLK_POST(uint32_t val)
|
||||
{
|
||||
return ((val) << DSI_CLKOUT_TIMING_CTRL_T_CLK_POST__SHIFT) & DSI_CLKOUT_TIMING_CTRL_T_CLK_POST__MASK;
|
||||
}
|
||||
|
||||
#define REG_DSI_EOT_PACKET_CTRL 0x000000c8
|
||||
#define DSI_EOT_PACKET_CTRL_TX_EOT_APPEND 0x00000001
|
||||
#define DSI_EOT_PACKET_CTRL_RX_EOT_IGNORE 0x00000010
|
||||
|
||||
#define REG_DSI_LANE_SWAP_CTRL 0x000000ac
|
||||
|
||||
#define REG_DSI_ERR_INT_MASK0 0x00000108
|
||||
|
||||
#define REG_DSI_INTR_CTRL 0x0000010c
|
||||
|
||||
#define REG_DSI_RESET 0x00000114
|
||||
|
||||
#define REG_DSI_CLK_CTRL 0x00000118
|
||||
|
||||
#define REG_DSI_PHY_RESET 0x00000128
|
||||
|
||||
#define REG_DSI_PHY_PLL_CTRL_0 0x00000200
|
||||
#define DSI_PHY_PLL_CTRL_0_ENABLE 0x00000001
|
||||
|
||||
#define REG_DSI_PHY_PLL_CTRL_1 0x00000204
|
||||
|
||||
#define REG_DSI_PHY_PLL_CTRL_2 0x00000208
|
||||
|
||||
#define REG_DSI_PHY_PLL_CTRL_3 0x0000020c
|
||||
|
||||
#define REG_DSI_PHY_PLL_CTRL_4 0x00000210
|
||||
|
||||
#define REG_DSI_PHY_PLL_CTRL_5 0x00000214
|
||||
|
||||
#define REG_DSI_PHY_PLL_CTRL_6 0x00000218
|
||||
|
||||
#define REG_DSI_PHY_PLL_CTRL_7 0x0000021c
|
||||
|
||||
#define REG_DSI_PHY_PLL_CTRL_8 0x00000220
|
||||
|
||||
#define REG_DSI_PHY_PLL_CTRL_9 0x00000224
|
||||
|
||||
#define REG_DSI_PHY_PLL_CTRL_10 0x00000228
|
||||
|
||||
#define REG_DSI_PHY_PLL_CTRL_11 0x0000022c
|
||||
|
||||
#define REG_DSI_PHY_PLL_CTRL_12 0x00000230
|
||||
|
||||
#define REG_DSI_PHY_PLL_CTRL_13 0x00000234
|
||||
|
||||
#define REG_DSI_PHY_PLL_CTRL_14 0x00000238
|
||||
|
||||
#define REG_DSI_PHY_PLL_CTRL_15 0x0000023c
|
||||
|
||||
#define REG_DSI_PHY_PLL_CTRL_16 0x00000240
|
||||
|
||||
#define REG_DSI_PHY_PLL_CTRL_17 0x00000244
|
||||
|
||||
#define REG_DSI_PHY_PLL_CTRL_18 0x00000248
|
||||
|
||||
#define REG_DSI_PHY_PLL_CTRL_19 0x0000024c
|
||||
|
||||
#define REG_DSI_PHY_PLL_CTRL_20 0x00000250
|
||||
|
||||
#define REG_DSI_PHY_PLL_STATUS 0x00000280
|
||||
#define DSI_PHY_PLL_STATUS_PLL_BUSY 0x00000001
|
||||
|
||||
#define REG_DSI_8x60_PHY_TPA_CTRL_1 0x00000258
|
||||
|
||||
#define REG_DSI_8x60_PHY_TPA_CTRL_2 0x0000025c
|
||||
|
||||
#define REG_DSI_8x60_PHY_TIMING_CTRL_0 0x00000260
|
||||
|
||||
#define REG_DSI_8x60_PHY_TIMING_CTRL_1 0x00000264
|
||||
|
||||
#define REG_DSI_8x60_PHY_TIMING_CTRL_2 0x00000268
|
||||
|
||||
#define REG_DSI_8x60_PHY_TIMING_CTRL_3 0x0000026c
|
||||
|
||||
#define REG_DSI_8x60_PHY_TIMING_CTRL_4 0x00000270
|
||||
|
||||
#define REG_DSI_8x60_PHY_TIMING_CTRL_5 0x00000274
|
||||
|
||||
#define REG_DSI_8x60_PHY_TIMING_CTRL_6 0x00000278
|
||||
|
||||
#define REG_DSI_8x60_PHY_TIMING_CTRL_7 0x0000027c
|
||||
|
||||
#define REG_DSI_8x60_PHY_TIMING_CTRL_8 0x00000280
|
||||
|
||||
#define REG_DSI_8x60_PHY_TIMING_CTRL_9 0x00000284
|
||||
|
||||
#define REG_DSI_8x60_PHY_TIMING_CTRL_10 0x00000288
|
||||
|
||||
#define REG_DSI_8x60_PHY_TIMING_CTRL_11 0x0000028c
|
||||
|
||||
#define REG_DSI_8x60_PHY_CTRL_0 0x00000290
|
||||
|
||||
#define REG_DSI_8x60_PHY_CTRL_1 0x00000294
|
||||
|
||||
#define REG_DSI_8x60_PHY_CTRL_2 0x00000298
|
||||
|
||||
#define REG_DSI_8x60_PHY_CTRL_3 0x0000029c
|
||||
|
||||
#define REG_DSI_8x60_PHY_STRENGTH_0 0x000002a0
|
||||
|
||||
#define REG_DSI_8x60_PHY_STRENGTH_1 0x000002a4
|
||||
|
||||
#define REG_DSI_8x60_PHY_STRENGTH_2 0x000002a8
|
||||
|
||||
#define REG_DSI_8x60_PHY_STRENGTH_3 0x000002ac
|
||||
|
||||
#define REG_DSI_8x60_PHY_REGULATOR_CTRL_0 0x000002cc
|
||||
|
||||
#define REG_DSI_8x60_PHY_REGULATOR_CTRL_1 0x000002d0
|
||||
|
||||
#define REG_DSI_8x60_PHY_REGULATOR_CTRL_2 0x000002d4
|
||||
|
||||
#define REG_DSI_8x60_PHY_REGULATOR_CTRL_3 0x000002d8
|
||||
|
||||
#define REG_DSI_8x60_PHY_REGULATOR_CTRL_4 0x000002dc
|
||||
|
||||
#define REG_DSI_8x60_PHY_CAL_HW_TRIGGER 0x000000f0
|
||||
|
||||
#define REG_DSI_8x60_PHY_CAL_CTRL 0x000000f4
|
||||
|
||||
#define REG_DSI_8x60_PHY_CAL_STATUS 0x000000fc
|
||||
#define DSI_8x60_PHY_CAL_STATUS_CAL_BUSY 0x10000000
|
||||
|
||||
static inline uint32_t REG_DSI_8960_LN(uint32_t i0) { return 0x00000300 + 0x40*i0; }
|
||||
|
||||
static inline uint32_t REG_DSI_8960_LN_CFG_0(uint32_t i0) { return 0x00000300 + 0x40*i0; }
|
||||
|
||||
static inline uint32_t REG_DSI_8960_LN_CFG_1(uint32_t i0) { return 0x00000304 + 0x40*i0; }
|
||||
|
||||
static inline uint32_t REG_DSI_8960_LN_CFG_2(uint32_t i0) { return 0x00000308 + 0x40*i0; }
|
||||
|
||||
static inline uint32_t REG_DSI_8960_LN_TEST_DATAPATH(uint32_t i0) { return 0x0000030c + 0x40*i0; }
|
||||
|
||||
static inline uint32_t REG_DSI_8960_LN_TEST_STR_0(uint32_t i0) { return 0x00000314 + 0x40*i0; }
|
||||
|
||||
static inline uint32_t REG_DSI_8960_LN_TEST_STR_1(uint32_t i0) { return 0x00000318 + 0x40*i0; }
|
||||
|
||||
#define REG_DSI_8960_PHY_LNCK_CFG_0 0x00000400
|
||||
|
||||
#define REG_DSI_8960_PHY_LNCK_CFG_1 0x00000404
|
||||
|
||||
#define REG_DSI_8960_PHY_LNCK_CFG_2 0x00000408
|
||||
|
||||
#define REG_DSI_8960_PHY_LNCK_TEST_DATAPATH 0x0000040c
|
||||
|
||||
#define REG_DSI_8960_PHY_LNCK_TEST_STR0 0x00000414
|
||||
|
||||
#define REG_DSI_8960_PHY_LNCK_TEST_STR1 0x00000418
|
||||
|
||||
#define REG_DSI_8960_PHY_TIMING_CTRL_0 0x00000440
|
||||
|
||||
#define REG_DSI_8960_PHY_TIMING_CTRL_1 0x00000444
|
||||
|
||||
#define REG_DSI_8960_PHY_TIMING_CTRL_2 0x00000448
|
||||
|
||||
#define REG_DSI_8960_PHY_TIMING_CTRL_3 0x0000044c
|
||||
|
||||
#define REG_DSI_8960_PHY_TIMING_CTRL_4 0x00000450
|
||||
|
||||
#define REG_DSI_8960_PHY_TIMING_CTRL_5 0x00000454
|
||||
|
||||
#define REG_DSI_8960_PHY_TIMING_CTRL_6 0x00000458
|
||||
|
||||
#define REG_DSI_8960_PHY_TIMING_CTRL_7 0x0000045c
|
||||
|
||||
#define REG_DSI_8960_PHY_TIMING_CTRL_8 0x00000460
|
||||
|
||||
#define REG_DSI_8960_PHY_TIMING_CTRL_9 0x00000464
|
||||
|
||||
#define REG_DSI_8960_PHY_TIMING_CTRL_10 0x00000468
|
||||
|
||||
#define REG_DSI_8960_PHY_TIMING_CTRL_11 0x0000046c
|
||||
|
||||
#define REG_DSI_8960_PHY_CTRL_0 0x00000470
|
||||
|
||||
#define REG_DSI_8960_PHY_CTRL_1 0x00000474
|
||||
|
||||
#define REG_DSI_8960_PHY_CTRL_2 0x00000478
|
||||
|
||||
#define REG_DSI_8960_PHY_CTRL_3 0x0000047c
|
||||
|
||||
#define REG_DSI_8960_PHY_STRENGTH_0 0x00000480
|
||||
|
||||
#define REG_DSI_8960_PHY_STRENGTH_1 0x00000484
|
||||
|
||||
#define REG_DSI_8960_PHY_STRENGTH_2 0x00000488
|
||||
|
||||
#define REG_DSI_8960_PHY_BIST_CTRL_0 0x0000048c
|
||||
|
||||
#define REG_DSI_8960_PHY_BIST_CTRL_1 0x00000490
|
||||
|
||||
#define REG_DSI_8960_PHY_BIST_CTRL_2 0x00000494
|
||||
|
||||
#define REG_DSI_8960_PHY_BIST_CTRL_3 0x00000498
|
||||
|
||||
#define REG_DSI_8960_PHY_BIST_CTRL_4 0x0000049c
|
||||
|
||||
#define REG_DSI_8960_PHY_LDO_CTRL 0x000004b0
|
||||
|
||||
#define REG_DSI_8960_PHY_REGULATOR_CTRL_0 0x00000500
|
||||
|
||||
#define REG_DSI_8960_PHY_REGULATOR_CTRL_1 0x00000504
|
||||
|
||||
#define REG_DSI_8960_PHY_REGULATOR_CTRL_2 0x00000508
|
||||
|
||||
#define REG_DSI_8960_PHY_REGULATOR_CTRL_3 0x0000050c
|
||||
|
||||
#define REG_DSI_8960_PHY_REGULATOR_CTRL_4 0x00000510
|
||||
|
||||
#define REG_DSI_8960_PHY_REGULATOR_CAL_PWR_CFG 0x00000518
|
||||
|
||||
#define REG_DSI_8960_PHY_CAL_HW_TRIGGER 0x00000528
|
||||
|
||||
#define REG_DSI_8960_PHY_CAL_SW_CFG_0 0x0000052c
|
||||
|
||||
#define REG_DSI_8960_PHY_CAL_SW_CFG_1 0x00000530
|
||||
|
||||
#define REG_DSI_8960_PHY_CAL_SW_CFG_2 0x00000534
|
||||
|
||||
#define REG_DSI_8960_PHY_CAL_HW_CFG_0 0x00000538
|
||||
|
||||
#define REG_DSI_8960_PHY_CAL_HW_CFG_1 0x0000053c
|
||||
|
||||
#define REG_DSI_8960_PHY_CAL_HW_CFG_2 0x00000540
|
||||
|
||||
#define REG_DSI_8960_PHY_CAL_HW_CFG_3 0x00000544
|
||||
|
||||
#define REG_DSI_8960_PHY_CAL_HW_CFG_4 0x00000548
|
||||
|
||||
#define REG_DSI_8960_PHY_CAL_STATUS 0x00000550
|
||||
#define DSI_8960_PHY_CAL_STATUS_CAL_BUSY 0x00000010
|
||||
|
||||
|
||||
#endif /* DSI_XML */
|
|
@ -0,0 +1,114 @@
|
|||
#ifndef MMSS_CC_XML
|
||||
#define MMSS_CC_XML
|
||||
|
||||
/* Autogenerated file, DO NOT EDIT manually!
|
||||
|
||||
This file was generated by the rules-ng-ng headergen tool in this git repository:
|
||||
http://0x04.net/cgit/index.cgi/rules-ng-ng
|
||||
git clone git://0x04.net/rules-ng-ng
|
||||
|
||||
The rules-ng-ng source files this header was generated from are:
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 595 bytes, from 2013-07-05 19:21:12)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml ( 19332 bytes, from 2013-08-16 22:16:36)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1544 bytes, from 2013-08-16 19:17:05)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 19288 bytes, from 2013-08-11 18:14:15)
|
||||
|
||||
Copyright (C) 2013 by the following authors:
|
||||
- Rob Clark <robdclark@gmail.com> (robclark)
|
||||
|
||||
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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS 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.
|
||||
*/
|
||||
|
||||
|
||||
enum mmss_cc_clk {
|
||||
CLK = 0,
|
||||
PCLK = 1,
|
||||
};
|
||||
|
||||
#define REG_MMSS_CC_AHB 0x00000008
|
||||
|
||||
static inline uint32_t __offset_CLK(enum mmss_cc_clk idx)
|
||||
{
|
||||
switch (idx) {
|
||||
case CLK: return 0x0000004c;
|
||||
case PCLK: return 0x00000130;
|
||||
default: return INVALID_IDX(idx);
|
||||
}
|
||||
}
|
||||
static inline uint32_t REG_MMSS_CC_CLK(enum mmss_cc_clk i0) { return 0x00000000 + __offset_CLK(i0); }
|
||||
|
||||
static inline uint32_t REG_MMSS_CC_CLK_CC(enum mmss_cc_clk i0) { return 0x00000000 + __offset_CLK(i0); }
|
||||
#define MMSS_CC_CLK_CC_CLK_EN 0x00000001
|
||||
#define MMSS_CC_CLK_CC_ROOT_EN 0x00000004
|
||||
#define MMSS_CC_CLK_CC_MND_EN 0x00000020
|
||||
#define MMSS_CC_CLK_CC_MND_MODE__MASK 0x000000c0
|
||||
#define MMSS_CC_CLK_CC_MND_MODE__SHIFT 6
|
||||
static inline uint32_t MMSS_CC_CLK_CC_MND_MODE(uint32_t val)
|
||||
{
|
||||
return ((val) << MMSS_CC_CLK_CC_MND_MODE__SHIFT) & MMSS_CC_CLK_CC_MND_MODE__MASK;
|
||||
}
|
||||
#define MMSS_CC_CLK_CC_PMXO_SEL__MASK 0x00000300
|
||||
#define MMSS_CC_CLK_CC_PMXO_SEL__SHIFT 8
|
||||
static inline uint32_t MMSS_CC_CLK_CC_PMXO_SEL(uint32_t val)
|
||||
{
|
||||
return ((val) << MMSS_CC_CLK_CC_PMXO_SEL__SHIFT) & MMSS_CC_CLK_CC_PMXO_SEL__MASK;
|
||||
}
|
||||
|
||||
static inline uint32_t REG_MMSS_CC_CLK_MD(enum mmss_cc_clk i0) { return 0x00000004 + __offset_CLK(i0); }
|
||||
#define MMSS_CC_CLK_MD_D__MASK 0x000000ff
|
||||
#define MMSS_CC_CLK_MD_D__SHIFT 0
|
||||
static inline uint32_t MMSS_CC_CLK_MD_D(uint32_t val)
|
||||
{
|
||||
return ((val) << MMSS_CC_CLK_MD_D__SHIFT) & MMSS_CC_CLK_MD_D__MASK;
|
||||
}
|
||||
#define MMSS_CC_CLK_MD_M__MASK 0x0000ff00
|
||||
#define MMSS_CC_CLK_MD_M__SHIFT 8
|
||||
static inline uint32_t MMSS_CC_CLK_MD_M(uint32_t val)
|
||||
{
|
||||
return ((val) << MMSS_CC_CLK_MD_M__SHIFT) & MMSS_CC_CLK_MD_M__MASK;
|
||||
}
|
||||
|
||||
static inline uint32_t REG_MMSS_CC_CLK_NS(enum mmss_cc_clk i0) { return 0x00000008 + __offset_CLK(i0); }
|
||||
#define MMSS_CC_CLK_NS_SRC__MASK 0x0000000f
|
||||
#define MMSS_CC_CLK_NS_SRC__SHIFT 0
|
||||
static inline uint32_t MMSS_CC_CLK_NS_SRC(uint32_t val)
|
||||
{
|
||||
return ((val) << MMSS_CC_CLK_NS_SRC__SHIFT) & MMSS_CC_CLK_NS_SRC__MASK;
|
||||
}
|
||||
#define MMSS_CC_CLK_NS_PRE_DIV_FUNC__MASK 0x00fff000
|
||||
#define MMSS_CC_CLK_NS_PRE_DIV_FUNC__SHIFT 12
|
||||
static inline uint32_t MMSS_CC_CLK_NS_PRE_DIV_FUNC(uint32_t val)
|
||||
{
|
||||
return ((val) << MMSS_CC_CLK_NS_PRE_DIV_FUNC__SHIFT) & MMSS_CC_CLK_NS_PRE_DIV_FUNC__MASK;
|
||||
}
|
||||
#define MMSS_CC_CLK_NS_VAL__MASK 0xff000000
|
||||
#define MMSS_CC_CLK_NS_VAL__SHIFT 24
|
||||
static inline uint32_t MMSS_CC_CLK_NS_VAL(uint32_t val)
|
||||
{
|
||||
return ((val) << MMSS_CC_CLK_NS_VAL__SHIFT) & MMSS_CC_CLK_NS_VAL__MASK;
|
||||
}
|
||||
|
||||
|
||||
#endif /* MMSS_CC_XML */
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef SFPB_XML
|
||||
#define SFPB_XML
|
||||
|
||||
/* Autogenerated file, DO NOT EDIT manually!
|
||||
|
||||
This file was generated by the rules-ng-ng headergen tool in this git repository:
|
||||
http://0x04.net/cgit/index.cgi/rules-ng-ng
|
||||
git clone git://0x04.net/rules-ng-ng
|
||||
|
||||
The rules-ng-ng source files this header was generated from are:
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 595 bytes, from 2013-07-05 19:21:12)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml ( 19332 bytes, from 2013-08-16 22:16:36)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1544 bytes, from 2013-08-16 19:17:05)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 19288 bytes, from 2013-08-11 18:14:15)
|
||||
|
||||
Copyright (C) 2013 by the following authors:
|
||||
- Rob Clark <robdclark@gmail.com> (robclark)
|
||||
|
||||
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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS 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.
|
||||
*/
|
||||
|
||||
|
||||
#define REG_SFPB_CFG 0x00000058
|
||||
|
||||
|
||||
#endif /* SFPB_XML */
|
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hdmi.h"
|
||||
|
||||
static struct platform_device *hdmi_pdev;
|
||||
|
||||
void hdmi_set_mode(struct hdmi *hdmi, bool power_on)
|
||||
{
|
||||
uint32_t ctrl = 0;
|
||||
|
||||
if (power_on) {
|
||||
ctrl |= HDMI_CTRL_ENABLE;
|
||||
if (!hdmi->hdmi_mode) {
|
||||
ctrl |= HDMI_CTRL_HDMI;
|
||||
hdmi_write(hdmi, REG_HDMI_CTRL, ctrl);
|
||||
ctrl &= ~HDMI_CTRL_HDMI;
|
||||
} else {
|
||||
ctrl |= HDMI_CTRL_HDMI;
|
||||
}
|
||||
} else {
|
||||
ctrl = HDMI_CTRL_HDMI;
|
||||
}
|
||||
|
||||
hdmi_write(hdmi, REG_HDMI_CTRL, ctrl);
|
||||
DBG("HDMI Core: %s, HDMI_CTRL=0x%08x",
|
||||
power_on ? "Enable" : "Disable", ctrl);
|
||||
}
|
||||
|
||||
static irqreturn_t hdmi_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct hdmi *hdmi = dev_id;
|
||||
|
||||
/* Process HPD: */
|
||||
hdmi_connector_irq(hdmi->connector);
|
||||
|
||||
/* Process DDC: */
|
||||
hdmi_i2c_irq(hdmi->i2c);
|
||||
|
||||
/* TODO audio.. */
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void hdmi_destroy(struct hdmi *hdmi)
|
||||
{
|
||||
struct hdmi_phy *phy = hdmi->phy;
|
||||
|
||||
if (phy)
|
||||
phy->funcs->destroy(phy);
|
||||
|
||||
if (hdmi->i2c)
|
||||
hdmi_i2c_destroy(hdmi->i2c);
|
||||
|
||||
put_device(&hdmi->pdev->dev);
|
||||
}
|
||||
|
||||
/* initialize connector */
|
||||
int hdmi_init(struct hdmi *hdmi, struct drm_device *dev,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct platform_device *pdev = hdmi_pdev;
|
||||
struct hdmi_platform_config *config;
|
||||
int ret;
|
||||
|
||||
if (!pdev) {
|
||||
dev_err(dev->dev, "no hdmi device\n");
|
||||
ret = -ENXIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
config = pdev->dev.platform_data;
|
||||
|
||||
get_device(&pdev->dev);
|
||||
|
||||
hdmi->dev = dev;
|
||||
hdmi->pdev = pdev;
|
||||
hdmi->connector = connector;
|
||||
|
||||
/* not sure about which phy maps to which msm.. probably I miss some */
|
||||
if (config->phy_init)
|
||||
hdmi->phy = config->phy_init(hdmi);
|
||||
else
|
||||
hdmi->phy = ERR_PTR(-ENXIO);
|
||||
|
||||
if (IS_ERR(hdmi->phy)) {
|
||||
ret = PTR_ERR(hdmi->phy);
|
||||
dev_err(dev->dev, "failed to load phy: %d\n", ret);
|
||||
hdmi->phy = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hdmi->mmio = msm_ioremap(pdev, "hdmi_msm_hdmi_addr", "HDMI");
|
||||
if (IS_ERR(hdmi->mmio)) {
|
||||
ret = PTR_ERR(hdmi->mmio);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hdmi->mvs = devm_regulator_get(&pdev->dev, "8901_hdmi_mvs");
|
||||
if (IS_ERR(hdmi->mvs))
|
||||
hdmi->mvs = devm_regulator_get(&pdev->dev, "hdmi_mvs");
|
||||
if (IS_ERR(hdmi->mvs)) {
|
||||
ret = PTR_ERR(hdmi->mvs);
|
||||
dev_err(dev->dev, "failed to get mvs regulator: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hdmi->mpp0 = devm_regulator_get(&pdev->dev, "8901_mpp0");
|
||||
if (IS_ERR(hdmi->mpp0))
|
||||
hdmi->mpp0 = NULL;
|
||||
|
||||
hdmi->clk = devm_clk_get(&pdev->dev, "core_clk");
|
||||
if (IS_ERR(hdmi->clk)) {
|
||||
ret = PTR_ERR(hdmi->clk);
|
||||
dev_err(dev->dev, "failed to get 'clk': %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hdmi->m_pclk = devm_clk_get(&pdev->dev, "master_iface_clk");
|
||||
if (IS_ERR(hdmi->m_pclk)) {
|
||||
ret = PTR_ERR(hdmi->m_pclk);
|
||||
dev_err(dev->dev, "failed to get 'm_pclk': %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hdmi->s_pclk = devm_clk_get(&pdev->dev, "slave_iface_clk");
|
||||
if (IS_ERR(hdmi->s_pclk)) {
|
||||
ret = PTR_ERR(hdmi->s_pclk);
|
||||
dev_err(dev->dev, "failed to get 's_pclk': %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hdmi->i2c = hdmi_i2c_init(hdmi);
|
||||
if (IS_ERR(hdmi->i2c)) {
|
||||
ret = PTR_ERR(hdmi->i2c);
|
||||
dev_err(dev->dev, "failed to get i2c: %d\n", ret);
|
||||
hdmi->i2c = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hdmi->irq = platform_get_irq(pdev, 0);
|
||||
if (hdmi->irq < 0) {
|
||||
ret = hdmi->irq;
|
||||
dev_err(dev->dev, "failed to get irq: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, hdmi->irq,
|
||||
NULL, hdmi_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||
"hdmi_isr", hdmi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "failed to request IRQ%u: %d\n",
|
||||
hdmi->irq, ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (hdmi)
|
||||
hdmi_destroy(hdmi);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The hdmi device:
|
||||
*/
|
||||
|
||||
static int hdmi_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
static struct hdmi_platform_config config = {};
|
||||
#ifdef CONFIG_OF
|
||||
/* TODO */
|
||||
#else
|
||||
if (cpu_is_apq8064()) {
|
||||
config.phy_init = hdmi_phy_8960_init;
|
||||
config.ddc_clk_gpio = 70;
|
||||
config.ddc_data_gpio = 71;
|
||||
config.hpd_gpio = 72;
|
||||
config.pmic_gpio = 13 + NR_GPIO_IRQS;
|
||||
} else if (cpu_is_msm8960()) {
|
||||
config.phy_init = hdmi_phy_8960_init;
|
||||
config.ddc_clk_gpio = 100;
|
||||
config.ddc_data_gpio = 101;
|
||||
config.hpd_gpio = 102;
|
||||
config.pmic_gpio = -1;
|
||||
} else if (cpu_is_msm8x60()) {
|
||||
config.phy_init = hdmi_phy_8x60_init;
|
||||
config.ddc_clk_gpio = 170;
|
||||
config.ddc_data_gpio = 171;
|
||||
config.hpd_gpio = 172;
|
||||
config.pmic_gpio = -1;
|
||||
}
|
||||
#endif
|
||||
pdev->dev.platform_data = &config;
|
||||
hdmi_pdev = pdev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
hdmi_pdev = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver hdmi_driver = {
|
||||
.probe = hdmi_dev_probe,
|
||||
.remove = hdmi_dev_remove,
|
||||
.driver.name = "hdmi_msm",
|
||||
};
|
||||
|
||||
void __init hdmi_register(void)
|
||||
{
|
||||
platform_driver_register(&hdmi_driver);
|
||||
}
|
||||
|
||||
void __exit hdmi_unregister(void)
|
||||
{
|
||||
platform_driver_unregister(&hdmi_driver);
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __HDMI_CONNECTOR_H__
|
||||
#define __HDMI_CONNECTOR_H__
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "hdmi.xml.h"
|
||||
|
||||
|
||||
struct hdmi_phy;
|
||||
|
||||
struct hdmi {
|
||||
struct drm_device *dev;
|
||||
struct platform_device *pdev;
|
||||
|
||||
void __iomem *mmio;
|
||||
|
||||
struct regulator *mvs; /* HDMI_5V */
|
||||
struct regulator *mpp0; /* External 5V */
|
||||
|
||||
struct clk *clk;
|
||||
struct clk *m_pclk;
|
||||
struct clk *s_pclk;
|
||||
|
||||
struct hdmi_phy *phy;
|
||||
struct i2c_adapter *i2c;
|
||||
struct drm_connector *connector;
|
||||
|
||||
bool hdmi_mode; /* are we in hdmi mode? */
|
||||
|
||||
int irq;
|
||||
};
|
||||
|
||||
/* platform config data (ie. from DT, or pdata) */
|
||||
struct hdmi_platform_config {
|
||||
struct hdmi_phy *(*phy_init)(struct hdmi *hdmi);
|
||||
int ddc_clk_gpio, ddc_data_gpio, hpd_gpio, pmic_gpio;
|
||||
};
|
||||
|
||||
void hdmi_set_mode(struct hdmi *hdmi, bool power_on);
|
||||
void hdmi_destroy(struct hdmi *hdmi);
|
||||
int hdmi_init(struct hdmi *hdmi, struct drm_device *dev,
|
||||
struct drm_connector *connector);
|
||||
|
||||
static inline void hdmi_write(struct hdmi *hdmi, u32 reg, u32 data)
|
||||
{
|
||||
msm_writel(data, hdmi->mmio + reg);
|
||||
}
|
||||
|
||||
static inline u32 hdmi_read(struct hdmi *hdmi, u32 reg)
|
||||
{
|
||||
return msm_readl(hdmi->mmio + reg);
|
||||
}
|
||||
|
||||
/*
|
||||
* The phy appears to be different, for example between 8960 and 8x60,
|
||||
* so split the phy related functions out and load the correct one at
|
||||
* runtime:
|
||||
*/
|
||||
|
||||
struct hdmi_phy_funcs {
|
||||
void (*destroy)(struct hdmi_phy *phy);
|
||||
void (*reset)(struct hdmi_phy *phy);
|
||||
void (*powerup)(struct hdmi_phy *phy, unsigned long int pixclock);
|
||||
void (*powerdown)(struct hdmi_phy *phy);
|
||||
};
|
||||
|
||||
struct hdmi_phy {
|
||||
const struct hdmi_phy_funcs *funcs;
|
||||
};
|
||||
|
||||
/*
|
||||
* phy can be different on different generations:
|
||||
*/
|
||||
struct hdmi_phy *hdmi_phy_8960_init(struct hdmi *hdmi);
|
||||
struct hdmi_phy *hdmi_phy_8x60_init(struct hdmi *hdmi);
|
||||
|
||||
/*
|
||||
* hdmi connector:
|
||||
*/
|
||||
|
||||
void hdmi_connector_irq(struct drm_connector *connector);
|
||||
|
||||
/*
|
||||
* i2c adapter for ddc:
|
||||
*/
|
||||
|
||||
void hdmi_i2c_irq(struct i2c_adapter *i2c);
|
||||
void hdmi_i2c_destroy(struct i2c_adapter *i2c);
|
||||
struct i2c_adapter *hdmi_i2c_init(struct hdmi *hdmi);
|
||||
|
||||
#endif /* __HDMI_CONNECTOR_H__ */
|
|
@ -0,0 +1,508 @@
|
|||
#ifndef HDMI_XML
|
||||
#define HDMI_XML
|
||||
|
||||
/* Autogenerated file, DO NOT EDIT manually!
|
||||
|
||||
This file was generated by the rules-ng-ng headergen tool in this git repository:
|
||||
http://0x04.net/cgit/index.cgi/rules-ng-ng
|
||||
git clone git://0x04.net/rules-ng-ng
|
||||
|
||||
The rules-ng-ng source files this header was generated from are:
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 595 bytes, from 2013-07-05 19:21:12)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml ( 19332 bytes, from 2013-08-16 22:16:36)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1544 bytes, from 2013-08-16 19:17:05)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 19288 bytes, from 2013-08-11 18:14:15)
|
||||
|
||||
Copyright (C) 2013 by the following authors:
|
||||
- Rob Clark <robdclark@gmail.com> (robclark)
|
||||
|
||||
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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS 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.
|
||||
*/
|
||||
|
||||
|
||||
enum hdmi_hdcp_key_state {
|
||||
NO_KEYS = 0,
|
||||
NOT_CHECKED = 1,
|
||||
CHECKING = 2,
|
||||
KEYS_VALID = 3,
|
||||
AKSV_INVALID = 4,
|
||||
CHECKSUM_MISMATCH = 5,
|
||||
};
|
||||
|
||||
enum hdmi_ddc_read_write {
|
||||
DDC_WRITE = 0,
|
||||
DDC_READ = 1,
|
||||
};
|
||||
|
||||
enum hdmi_acr_cts {
|
||||
ACR_NONE = 0,
|
||||
ACR_32 = 1,
|
||||
ACR_44 = 2,
|
||||
ACR_48 = 3,
|
||||
};
|
||||
|
||||
#define REG_HDMI_CTRL 0x00000000
|
||||
#define HDMI_CTRL_ENABLE 0x00000001
|
||||
#define HDMI_CTRL_HDMI 0x00000002
|
||||
#define HDMI_CTRL_ENCRYPTED 0x00000004
|
||||
|
||||
#define REG_HDMI_AUDIO_PKT_CTRL1 0x00000020
|
||||
#define HDMI_AUDIO_PKT_CTRL1_AUDIO_SAMPLE_SEND 0x00000001
|
||||
|
||||
#define REG_HDMI_ACR_PKT_CTRL 0x00000024
|
||||
#define HDMI_ACR_PKT_CTRL_CONT 0x00000001
|
||||
#define HDMI_ACR_PKT_CTRL_SEND 0x00000002
|
||||
#define HDMI_ACR_PKT_CTRL_SELECT__MASK 0x00000030
|
||||
#define HDMI_ACR_PKT_CTRL_SELECT__SHIFT 4
|
||||
static inline uint32_t HDMI_ACR_PKT_CTRL_SELECT(enum hdmi_acr_cts val)
|
||||
{
|
||||
return ((val) << HDMI_ACR_PKT_CTRL_SELECT__SHIFT) & HDMI_ACR_PKT_CTRL_SELECT__MASK;
|
||||
}
|
||||
#define HDMI_ACR_PKT_CTRL_SOURCE 0x00000100
|
||||
#define HDMI_ACR_PKT_CTRL_N_MULTIPLIER__MASK 0x00070000
|
||||
#define HDMI_ACR_PKT_CTRL_N_MULTIPLIER__SHIFT 16
|
||||
static inline uint32_t HDMI_ACR_PKT_CTRL_N_MULTIPLIER(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_ACR_PKT_CTRL_N_MULTIPLIER__SHIFT) & HDMI_ACR_PKT_CTRL_N_MULTIPLIER__MASK;
|
||||
}
|
||||
#define HDMI_ACR_PKT_CTRL_AUDIO_PRIORITY 0x80000000
|
||||
|
||||
#define REG_HDMI_VBI_PKT_CTRL 0x00000028
|
||||
#define HDMI_VBI_PKT_CTRL_GC_ENABLE 0x00000010
|
||||
#define HDMI_VBI_PKT_CTRL_GC_EVERY_FRAME 0x00000020
|
||||
#define HDMI_VBI_PKT_CTRL_ISRC_SEND 0x00000100
|
||||
#define HDMI_VBI_PKT_CTRL_ISRC_CONTINUOUS 0x00000200
|
||||
#define HDMI_VBI_PKT_CTRL_ACP_SEND 0x00001000
|
||||
#define HDMI_VBI_PKT_CTRL_ACP_SRC_SW 0x00002000
|
||||
|
||||
#define REG_HDMI_INFOFRAME_CTRL0 0x0000002c
|
||||
#define HDMI_INFOFRAME_CTRL0_AVI_SEND 0x00000001
|
||||
#define HDMI_INFOFRAME_CTRL0_AVI_CONT 0x00000002
|
||||
#define HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND 0x00000010
|
||||
#define HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT 0x00000020
|
||||
#define HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE 0x00000040
|
||||
#define HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE 0x00000080
|
||||
|
||||
#define REG_HDMI_GEN_PKT_CTRL 0x00000034
|
||||
#define HDMI_GEN_PKT_CTRL_GENERIC0_SEND 0x00000001
|
||||
#define HDMI_GEN_PKT_CTRL_GENERIC0_CONT 0x00000002
|
||||
#define HDMI_GEN_PKT_CTRL_GENERIC0_UPDATE__MASK 0x0000000c
|
||||
#define HDMI_GEN_PKT_CTRL_GENERIC0_UPDATE__SHIFT 2
|
||||
static inline uint32_t HDMI_GEN_PKT_CTRL_GENERIC0_UPDATE(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_GEN_PKT_CTRL_GENERIC0_UPDATE__SHIFT) & HDMI_GEN_PKT_CTRL_GENERIC0_UPDATE__MASK;
|
||||
}
|
||||
#define HDMI_GEN_PKT_CTRL_GENERIC1_SEND 0x00000010
|
||||
#define HDMI_GEN_PKT_CTRL_GENERIC1_CONT 0x00000020
|
||||
#define HDMI_GEN_PKT_CTRL_GENERIC0_LINE__MASK 0x003f0000
|
||||
#define HDMI_GEN_PKT_CTRL_GENERIC0_LINE__SHIFT 16
|
||||
static inline uint32_t HDMI_GEN_PKT_CTRL_GENERIC0_LINE(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_GEN_PKT_CTRL_GENERIC0_LINE__SHIFT) & HDMI_GEN_PKT_CTRL_GENERIC0_LINE__MASK;
|
||||
}
|
||||
#define HDMI_GEN_PKT_CTRL_GENERIC1_LINE__MASK 0x3f000000
|
||||
#define HDMI_GEN_PKT_CTRL_GENERIC1_LINE__SHIFT 24
|
||||
static inline uint32_t HDMI_GEN_PKT_CTRL_GENERIC1_LINE(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_GEN_PKT_CTRL_GENERIC1_LINE__SHIFT) & HDMI_GEN_PKT_CTRL_GENERIC1_LINE__MASK;
|
||||
}
|
||||
|
||||
#define REG_HDMI_GC 0x00000040
|
||||
#define HDMI_GC_MUTE 0x00000001
|
||||
|
||||
#define REG_HDMI_AUDIO_PKT_CTRL2 0x00000044
|
||||
#define HDMI_AUDIO_PKT_CTRL2_OVERRIDE 0x00000001
|
||||
#define HDMI_AUDIO_PKT_CTRL2_LAYOUT 0x00000002
|
||||
|
||||
static inline uint32_t REG_HDMI_AVI_INFO(uint32_t i0) { return 0x0000006c + 0x4*i0; }
|
||||
|
||||
#define REG_HDMI_GENERIC0_HDR 0x00000084
|
||||
|
||||
static inline uint32_t REG_HDMI_GENERIC0(uint32_t i0) { return 0x00000088 + 0x4*i0; }
|
||||
|
||||
#define REG_HDMI_GENERIC1_HDR 0x000000a4
|
||||
|
||||
static inline uint32_t REG_HDMI_GENERIC1(uint32_t i0) { return 0x000000a8 + 0x4*i0; }
|
||||
|
||||
static inline uint32_t REG_HDMI_ACR(uint32_t i0) { return 0x000000c4 + 0x8*i0; }
|
||||
|
||||
static inline uint32_t REG_HDMI_ACR_0(uint32_t i0) { return 0x000000c4 + 0x8*i0; }
|
||||
#define HDMI_ACR_0_CTS__MASK 0xfffff000
|
||||
#define HDMI_ACR_0_CTS__SHIFT 12
|
||||
static inline uint32_t HDMI_ACR_0_CTS(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_ACR_0_CTS__SHIFT) & HDMI_ACR_0_CTS__MASK;
|
||||
}
|
||||
|
||||
static inline uint32_t REG_HDMI_ACR_1(uint32_t i0) { return 0x000000c8 + 0x8*i0; }
|
||||
#define HDMI_ACR_1_N__MASK 0xffffffff
|
||||
#define HDMI_ACR_1_N__SHIFT 0
|
||||
static inline uint32_t HDMI_ACR_1_N(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_ACR_1_N__SHIFT) & HDMI_ACR_1_N__MASK;
|
||||
}
|
||||
|
||||
#define REG_HDMI_AUDIO_INFO0 0x000000e4
|
||||
#define HDMI_AUDIO_INFO0_CHECKSUM__MASK 0x000000ff
|
||||
#define HDMI_AUDIO_INFO0_CHECKSUM__SHIFT 0
|
||||
static inline uint32_t HDMI_AUDIO_INFO0_CHECKSUM(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_AUDIO_INFO0_CHECKSUM__SHIFT) & HDMI_AUDIO_INFO0_CHECKSUM__MASK;
|
||||
}
|
||||
#define HDMI_AUDIO_INFO0_CC__MASK 0x00000700
|
||||
#define HDMI_AUDIO_INFO0_CC__SHIFT 8
|
||||
static inline uint32_t HDMI_AUDIO_INFO0_CC(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_AUDIO_INFO0_CC__SHIFT) & HDMI_AUDIO_INFO0_CC__MASK;
|
||||
}
|
||||
|
||||
#define REG_HDMI_AUDIO_INFO1 0x000000e8
|
||||
#define HDMI_AUDIO_INFO1_CA__MASK 0x000000ff
|
||||
#define HDMI_AUDIO_INFO1_CA__SHIFT 0
|
||||
static inline uint32_t HDMI_AUDIO_INFO1_CA(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_AUDIO_INFO1_CA__SHIFT) & HDMI_AUDIO_INFO1_CA__MASK;
|
||||
}
|
||||
#define HDMI_AUDIO_INFO1_LSV__MASK 0x00007800
|
||||
#define HDMI_AUDIO_INFO1_LSV__SHIFT 11
|
||||
static inline uint32_t HDMI_AUDIO_INFO1_LSV(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_AUDIO_INFO1_LSV__SHIFT) & HDMI_AUDIO_INFO1_LSV__MASK;
|
||||
}
|
||||
#define HDMI_AUDIO_INFO1_DM_INH 0x00008000
|
||||
|
||||
#define REG_HDMI_HDCP_CTRL 0x00000110
|
||||
#define HDMI_HDCP_CTRL_ENABLE 0x00000001
|
||||
#define HDMI_HDCP_CTRL_ENCRYPTION_ENABLE 0x00000100
|
||||
|
||||
#define REG_HDMI_HDCP_INT_CTRL 0x00000118
|
||||
|
||||
#define REG_HDMI_HDCP_LINK0_STATUS 0x0000011c
|
||||
#define HDMI_HDCP_LINK0_STATUS_AN_0_READY 0x00000100
|
||||
#define HDMI_HDCP_LINK0_STATUS_AN_1_READY 0x00000200
|
||||
#define HDMI_HDCP_LINK0_STATUS_KEY_STATE__MASK 0x70000000
|
||||
#define HDMI_HDCP_LINK0_STATUS_KEY_STATE__SHIFT 28
|
||||
static inline uint32_t HDMI_HDCP_LINK0_STATUS_KEY_STATE(enum hdmi_hdcp_key_state val)
|
||||
{
|
||||
return ((val) << HDMI_HDCP_LINK0_STATUS_KEY_STATE__SHIFT) & HDMI_HDCP_LINK0_STATUS_KEY_STATE__MASK;
|
||||
}
|
||||
|
||||
#define REG_HDMI_HDCP_RESET 0x00000130
|
||||
#define HDMI_HDCP_RESET_LINK0_DEAUTHENTICATE 0x00000001
|
||||
|
||||
#define REG_HDMI_AUDIO_CFG 0x000001d0
|
||||
#define HDMI_AUDIO_CFG_ENGINE_ENABLE 0x00000001
|
||||
#define HDMI_AUDIO_CFG_FIFO_WATERMARK__MASK 0x000000f0
|
||||
#define HDMI_AUDIO_CFG_FIFO_WATERMARK__SHIFT 4
|
||||
static inline uint32_t HDMI_AUDIO_CFG_FIFO_WATERMARK(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_AUDIO_CFG_FIFO_WATERMARK__SHIFT) & HDMI_AUDIO_CFG_FIFO_WATERMARK__MASK;
|
||||
}
|
||||
|
||||
#define REG_HDMI_USEC_REFTIMER 0x00000208
|
||||
|
||||
#define REG_HDMI_DDC_CTRL 0x0000020c
|
||||
#define HDMI_DDC_CTRL_GO 0x00000001
|
||||
#define HDMI_DDC_CTRL_SOFT_RESET 0x00000002
|
||||
#define HDMI_DDC_CTRL_SEND_RESET 0x00000004
|
||||
#define HDMI_DDC_CTRL_SW_STATUS_RESET 0x00000008
|
||||
#define HDMI_DDC_CTRL_TRANSACTION_CNT__MASK 0x00300000
|
||||
#define HDMI_DDC_CTRL_TRANSACTION_CNT__SHIFT 20
|
||||
static inline uint32_t HDMI_DDC_CTRL_TRANSACTION_CNT(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_DDC_CTRL_TRANSACTION_CNT__SHIFT) & HDMI_DDC_CTRL_TRANSACTION_CNT__MASK;
|
||||
}
|
||||
|
||||
#define REG_HDMI_DDC_INT_CTRL 0x00000214
|
||||
#define HDMI_DDC_INT_CTRL_SW_DONE_INT 0x00000001
|
||||
#define HDMI_DDC_INT_CTRL_SW_DONE_ACK 0x00000002
|
||||
#define HDMI_DDC_INT_CTRL_SW_DONE_MASK 0x00000004
|
||||
|
||||
#define REG_HDMI_DDC_SW_STATUS 0x00000218
|
||||
#define HDMI_DDC_SW_STATUS_NACK0 0x00001000
|
||||
#define HDMI_DDC_SW_STATUS_NACK1 0x00002000
|
||||
#define HDMI_DDC_SW_STATUS_NACK2 0x00004000
|
||||
#define HDMI_DDC_SW_STATUS_NACK3 0x00008000
|
||||
|
||||
#define REG_HDMI_DDC_HW_STATUS 0x0000021c
|
||||
|
||||
#define REG_HDMI_DDC_SPEED 0x00000220
|
||||
#define HDMI_DDC_SPEED_THRESHOLD__MASK 0x00000003
|
||||
#define HDMI_DDC_SPEED_THRESHOLD__SHIFT 0
|
||||
static inline uint32_t HDMI_DDC_SPEED_THRESHOLD(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_DDC_SPEED_THRESHOLD__SHIFT) & HDMI_DDC_SPEED_THRESHOLD__MASK;
|
||||
}
|
||||
#define HDMI_DDC_SPEED_PRESCALE__MASK 0xffff0000
|
||||
#define HDMI_DDC_SPEED_PRESCALE__SHIFT 16
|
||||
static inline uint32_t HDMI_DDC_SPEED_PRESCALE(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_DDC_SPEED_PRESCALE__SHIFT) & HDMI_DDC_SPEED_PRESCALE__MASK;
|
||||
}
|
||||
|
||||
#define REG_HDMI_DDC_SETUP 0x00000224
|
||||
#define HDMI_DDC_SETUP_TIMEOUT__MASK 0xff000000
|
||||
#define HDMI_DDC_SETUP_TIMEOUT__SHIFT 24
|
||||
static inline uint32_t HDMI_DDC_SETUP_TIMEOUT(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_DDC_SETUP_TIMEOUT__SHIFT) & HDMI_DDC_SETUP_TIMEOUT__MASK;
|
||||
}
|
||||
|
||||
static inline uint32_t REG_HDMI_I2C_TRANSACTION(uint32_t i0) { return 0x00000228 + 0x4*i0; }
|
||||
|
||||
static inline uint32_t REG_HDMI_I2C_TRANSACTION_REG(uint32_t i0) { return 0x00000228 + 0x4*i0; }
|
||||
#define HDMI_I2C_TRANSACTION_REG_RW__MASK 0x00000001
|
||||
#define HDMI_I2C_TRANSACTION_REG_RW__SHIFT 0
|
||||
static inline uint32_t HDMI_I2C_TRANSACTION_REG_RW(enum hdmi_ddc_read_write val)
|
||||
{
|
||||
return ((val) << HDMI_I2C_TRANSACTION_REG_RW__SHIFT) & HDMI_I2C_TRANSACTION_REG_RW__MASK;
|
||||
}
|
||||
#define HDMI_I2C_TRANSACTION_REG_STOP_ON_NACK 0x00000100
|
||||
#define HDMI_I2C_TRANSACTION_REG_START 0x00001000
|
||||
#define HDMI_I2C_TRANSACTION_REG_STOP 0x00002000
|
||||
#define HDMI_I2C_TRANSACTION_REG_CNT__MASK 0x00ff0000
|
||||
#define HDMI_I2C_TRANSACTION_REG_CNT__SHIFT 16
|
||||
static inline uint32_t HDMI_I2C_TRANSACTION_REG_CNT(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_I2C_TRANSACTION_REG_CNT__SHIFT) & HDMI_I2C_TRANSACTION_REG_CNT__MASK;
|
||||
}
|
||||
|
||||
#define REG_HDMI_DDC_DATA 0x00000238
|
||||
#define HDMI_DDC_DATA_DATA_RW__MASK 0x00000001
|
||||
#define HDMI_DDC_DATA_DATA_RW__SHIFT 0
|
||||
static inline uint32_t HDMI_DDC_DATA_DATA_RW(enum hdmi_ddc_read_write val)
|
||||
{
|
||||
return ((val) << HDMI_DDC_DATA_DATA_RW__SHIFT) & HDMI_DDC_DATA_DATA_RW__MASK;
|
||||
}
|
||||
#define HDMI_DDC_DATA_DATA__MASK 0x0000ff00
|
||||
#define HDMI_DDC_DATA_DATA__SHIFT 8
|
||||
static inline uint32_t HDMI_DDC_DATA_DATA(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_DDC_DATA_DATA__SHIFT) & HDMI_DDC_DATA_DATA__MASK;
|
||||
}
|
||||
#define HDMI_DDC_DATA_INDEX__MASK 0x00ff0000
|
||||
#define HDMI_DDC_DATA_INDEX__SHIFT 16
|
||||
static inline uint32_t HDMI_DDC_DATA_INDEX(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_DDC_DATA_INDEX__SHIFT) & HDMI_DDC_DATA_INDEX__MASK;
|
||||
}
|
||||
#define HDMI_DDC_DATA_INDEX_WRITE 0x80000000
|
||||
|
||||
#define REG_HDMI_HPD_INT_STATUS 0x00000250
|
||||
#define HDMI_HPD_INT_STATUS_INT 0x00000001
|
||||
#define HDMI_HPD_INT_STATUS_CABLE_DETECTED 0x00000002
|
||||
|
||||
#define REG_HDMI_HPD_INT_CTRL 0x00000254
|
||||
#define HDMI_HPD_INT_CTRL_INT_ACK 0x00000001
|
||||
#define HDMI_HPD_INT_CTRL_INT_CONNECT 0x00000002
|
||||
#define HDMI_HPD_INT_CTRL_INT_EN 0x00000004
|
||||
#define HDMI_HPD_INT_CTRL_RX_INT_ACK 0x00000010
|
||||
#define HDMI_HPD_INT_CTRL_RX_INT_EN 0x00000020
|
||||
#define HDMI_HPD_INT_CTRL_RCV_PLUGIN_DET_MASK 0x00000200
|
||||
|
||||
#define REG_HDMI_HPD_CTRL 0x00000258
|
||||
#define HDMI_HPD_CTRL_TIMEOUT__MASK 0x00001fff
|
||||
#define HDMI_HPD_CTRL_TIMEOUT__SHIFT 0
|
||||
static inline uint32_t HDMI_HPD_CTRL_TIMEOUT(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_HPD_CTRL_TIMEOUT__SHIFT) & HDMI_HPD_CTRL_TIMEOUT__MASK;
|
||||
}
|
||||
#define HDMI_HPD_CTRL_ENABLE 0x10000000
|
||||
|
||||
#define REG_HDMI_DDC_REF 0x0000027c
|
||||
#define HDMI_DDC_REF_REFTIMER_ENABLE 0x00010000
|
||||
#define HDMI_DDC_REF_REFTIMER__MASK 0x0000ffff
|
||||
#define HDMI_DDC_REF_REFTIMER__SHIFT 0
|
||||
static inline uint32_t HDMI_DDC_REF_REFTIMER(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_DDC_REF_REFTIMER__SHIFT) & HDMI_DDC_REF_REFTIMER__MASK;
|
||||
}
|
||||
|
||||
#define REG_HDMI_ACTIVE_HSYNC 0x000002b4
|
||||
#define HDMI_ACTIVE_HSYNC_START__MASK 0x00000fff
|
||||
#define HDMI_ACTIVE_HSYNC_START__SHIFT 0
|
||||
static inline uint32_t HDMI_ACTIVE_HSYNC_START(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_ACTIVE_HSYNC_START__SHIFT) & HDMI_ACTIVE_HSYNC_START__MASK;
|
||||
}
|
||||
#define HDMI_ACTIVE_HSYNC_END__MASK 0x0fff0000
|
||||
#define HDMI_ACTIVE_HSYNC_END__SHIFT 16
|
||||
static inline uint32_t HDMI_ACTIVE_HSYNC_END(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_ACTIVE_HSYNC_END__SHIFT) & HDMI_ACTIVE_HSYNC_END__MASK;
|
||||
}
|
||||
|
||||
#define REG_HDMI_ACTIVE_VSYNC 0x000002b8
|
||||
#define HDMI_ACTIVE_VSYNC_START__MASK 0x00000fff
|
||||
#define HDMI_ACTIVE_VSYNC_START__SHIFT 0
|
||||
static inline uint32_t HDMI_ACTIVE_VSYNC_START(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_ACTIVE_VSYNC_START__SHIFT) & HDMI_ACTIVE_VSYNC_START__MASK;
|
||||
}
|
||||
#define HDMI_ACTIVE_VSYNC_END__MASK 0x0fff0000
|
||||
#define HDMI_ACTIVE_VSYNC_END__SHIFT 16
|
||||
static inline uint32_t HDMI_ACTIVE_VSYNC_END(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_ACTIVE_VSYNC_END__SHIFT) & HDMI_ACTIVE_VSYNC_END__MASK;
|
||||
}
|
||||
|
||||
#define REG_HDMI_VSYNC_ACTIVE_F2 0x000002bc
|
||||
#define HDMI_VSYNC_ACTIVE_F2_START__MASK 0x00000fff
|
||||
#define HDMI_VSYNC_ACTIVE_F2_START__SHIFT 0
|
||||
static inline uint32_t HDMI_VSYNC_ACTIVE_F2_START(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_VSYNC_ACTIVE_F2_START__SHIFT) & HDMI_VSYNC_ACTIVE_F2_START__MASK;
|
||||
}
|
||||
#define HDMI_VSYNC_ACTIVE_F2_END__MASK 0x0fff0000
|
||||
#define HDMI_VSYNC_ACTIVE_F2_END__SHIFT 16
|
||||
static inline uint32_t HDMI_VSYNC_ACTIVE_F2_END(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_VSYNC_ACTIVE_F2_END__SHIFT) & HDMI_VSYNC_ACTIVE_F2_END__MASK;
|
||||
}
|
||||
|
||||
#define REG_HDMI_TOTAL 0x000002c0
|
||||
#define HDMI_TOTAL_H_TOTAL__MASK 0x00000fff
|
||||
#define HDMI_TOTAL_H_TOTAL__SHIFT 0
|
||||
static inline uint32_t HDMI_TOTAL_H_TOTAL(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_TOTAL_H_TOTAL__SHIFT) & HDMI_TOTAL_H_TOTAL__MASK;
|
||||
}
|
||||
#define HDMI_TOTAL_V_TOTAL__MASK 0x0fff0000
|
||||
#define HDMI_TOTAL_V_TOTAL__SHIFT 16
|
||||
static inline uint32_t HDMI_TOTAL_V_TOTAL(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_TOTAL_V_TOTAL__SHIFT) & HDMI_TOTAL_V_TOTAL__MASK;
|
||||
}
|
||||
|
||||
#define REG_HDMI_VSYNC_TOTAL_F2 0x000002c4
|
||||
#define HDMI_VSYNC_TOTAL_F2_V_TOTAL__MASK 0x00000fff
|
||||
#define HDMI_VSYNC_TOTAL_F2_V_TOTAL__SHIFT 0
|
||||
static inline uint32_t HDMI_VSYNC_TOTAL_F2_V_TOTAL(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_VSYNC_TOTAL_F2_V_TOTAL__SHIFT) & HDMI_VSYNC_TOTAL_F2_V_TOTAL__MASK;
|
||||
}
|
||||
|
||||
#define REG_HDMI_FRAME_CTRL 0x000002c8
|
||||
#define HDMI_FRAME_CTRL_RGB_MUX_SEL_BGR 0x00001000
|
||||
#define HDMI_FRAME_CTRL_VSYNC_LOW 0x10000000
|
||||
#define HDMI_FRAME_CTRL_HSYNC_LOW 0x20000000
|
||||
#define HDMI_FRAME_CTRL_INTERLACED_EN 0x80000000
|
||||
|
||||
#define REG_HDMI_PHY_CTRL 0x000002d4
|
||||
#define HDMI_PHY_CTRL_SW_RESET_PLL 0x00000001
|
||||
#define HDMI_PHY_CTRL_SW_RESET_PLL_LOW 0x00000002
|
||||
#define HDMI_PHY_CTRL_SW_RESET 0x00000004
|
||||
#define HDMI_PHY_CTRL_SW_RESET_LOW 0x00000008
|
||||
|
||||
#define REG_HDMI_AUD_INT 0x000002cc
|
||||
#define HDMI_AUD_INT_AUD_FIFO_URUN_INT 0x00000001
|
||||
#define HDMI_AUD_INT_AUD_FIFO_URAN_MASK 0x00000002
|
||||
#define HDMI_AUD_INT_AUD_SAM_DROP_INT 0x00000004
|
||||
#define HDMI_AUD_INT_AUD_SAM_DROP_MASK 0x00000008
|
||||
|
||||
#define REG_HDMI_8x60_PHY_REG0 0x00000300
|
||||
#define HDMI_8x60_PHY_REG0_DESER_DEL_CTRL__MASK 0x0000001c
|
||||
#define HDMI_8x60_PHY_REG0_DESER_DEL_CTRL__SHIFT 2
|
||||
static inline uint32_t HDMI_8x60_PHY_REG0_DESER_DEL_CTRL(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_8x60_PHY_REG0_DESER_DEL_CTRL__SHIFT) & HDMI_8x60_PHY_REG0_DESER_DEL_CTRL__MASK;
|
||||
}
|
||||
|
||||
#define REG_HDMI_8x60_PHY_REG1 0x00000304
|
||||
#define HDMI_8x60_PHY_REG1_DTEST_MUX_SEL__MASK 0x000000f0
|
||||
#define HDMI_8x60_PHY_REG1_DTEST_MUX_SEL__SHIFT 4
|
||||
static inline uint32_t HDMI_8x60_PHY_REG1_DTEST_MUX_SEL(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_8x60_PHY_REG1_DTEST_MUX_SEL__SHIFT) & HDMI_8x60_PHY_REG1_DTEST_MUX_SEL__MASK;
|
||||
}
|
||||
#define HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL__MASK 0x0000000f
|
||||
#define HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL__SHIFT 0
|
||||
static inline uint32_t HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL(uint32_t val)
|
||||
{
|
||||
return ((val) << HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL__SHIFT) & HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL__MASK;
|
||||
}
|
||||
|
||||
#define REG_HDMI_8x60_PHY_REG2 0x00000308
|
||||
#define HDMI_8x60_PHY_REG2_PD_DESER 0x00000001
|
||||
#define HDMI_8x60_PHY_REG2_PD_DRIVE_1 0x00000002
|
||||
#define HDMI_8x60_PHY_REG2_PD_DRIVE_2 0x00000004
|
||||
#define HDMI_8x60_PHY_REG2_PD_DRIVE_3 0x00000008
|
||||
#define HDMI_8x60_PHY_REG2_PD_DRIVE_4 0x00000010
|
||||
#define HDMI_8x60_PHY_REG2_PD_PLL 0x00000020
|
||||
#define HDMI_8x60_PHY_REG2_PD_PWRGEN 0x00000040
|
||||
#define HDMI_8x60_PHY_REG2_RCV_SENSE_EN 0x00000080
|
||||
|
||||
#define REG_HDMI_8x60_PHY_REG3 0x0000030c
|
||||
#define HDMI_8x60_PHY_REG3_PLL_ENABLE 0x00000001
|
||||
|
||||
#define REG_HDMI_8x60_PHY_REG4 0x00000310
|
||||
|
||||
#define REG_HDMI_8x60_PHY_REG5 0x00000314
|
||||
|
||||
#define REG_HDMI_8x60_PHY_REG6 0x00000318
|
||||
|
||||
#define REG_HDMI_8x60_PHY_REG7 0x0000031c
|
||||
|
||||
#define REG_HDMI_8x60_PHY_REG8 0x00000320
|
||||
|
||||
#define REG_HDMI_8x60_PHY_REG9 0x00000324
|
||||
|
||||
#define REG_HDMI_8x60_PHY_REG10 0x00000328
|
||||
|
||||
#define REG_HDMI_8x60_PHY_REG11 0x0000032c
|
||||
|
||||
#define REG_HDMI_8x60_PHY_REG12 0x00000330
|
||||
#define HDMI_8x60_PHY_REG12_RETIMING_EN 0x00000001
|
||||
#define HDMI_8x60_PHY_REG12_PLL_LOCK_DETECT_EN 0x00000002
|
||||
#define HDMI_8x60_PHY_REG12_FORCE_LOCK 0x00000010
|
||||
|
||||
#define REG_HDMI_8960_PHY_REG0 0x00000400
|
||||
|
||||
#define REG_HDMI_8960_PHY_REG1 0x00000404
|
||||
|
||||
#define REG_HDMI_8960_PHY_REG2 0x00000408
|
||||
|
||||
#define REG_HDMI_8960_PHY_REG3 0x0000040c
|
||||
|
||||
#define REG_HDMI_8960_PHY_REG4 0x00000410
|
||||
|
||||
#define REG_HDMI_8960_PHY_REG5 0x00000414
|
||||
|
||||
#define REG_HDMI_8960_PHY_REG6 0x00000418
|
||||
|
||||
#define REG_HDMI_8960_PHY_REG7 0x0000041c
|
||||
|
||||
#define REG_HDMI_8960_PHY_REG8 0x00000420
|
||||
|
||||
#define REG_HDMI_8960_PHY_REG9 0x00000424
|
||||
|
||||
#define REG_HDMI_8960_PHY_REG10 0x00000428
|
||||
|
||||
#define REG_HDMI_8960_PHY_REG11 0x0000042c
|
||||
|
||||
#define REG_HDMI_8960_PHY_REG12 0x00000430
|
||||
|
||||
|
||||
#endif /* HDMI_XML */
|
|
@ -0,0 +1,461 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#include "msm_connector.h"
|
||||
#include "hdmi.h"
|
||||
|
||||
struct hdmi_connector {
|
||||
struct msm_connector base;
|
||||
struct hdmi hdmi;
|
||||
unsigned long int pixclock;
|
||||
bool enabled;
|
||||
};
|
||||
#define to_hdmi_connector(x) container_of(x, struct hdmi_connector, base)
|
||||
|
||||
static int gpio_config(struct hdmi *hdmi, bool on)
|
||||
{
|
||||
struct drm_device *dev = hdmi->dev;
|
||||
struct hdmi_platform_config *config =
|
||||
hdmi->pdev->dev.platform_data;
|
||||
int ret;
|
||||
|
||||
if (on) {
|
||||
ret = gpio_request(config->ddc_clk_gpio, "HDMI_DDC_CLK");
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n",
|
||||
"HDMI_DDC_CLK", config->ddc_clk_gpio, ret);
|
||||
goto error1;
|
||||
}
|
||||
ret = gpio_request(config->ddc_data_gpio, "HDMI_DDC_DATA");
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n",
|
||||
"HDMI_DDC_DATA", config->ddc_data_gpio, ret);
|
||||
goto error2;
|
||||
}
|
||||
ret = gpio_request(config->hpd_gpio, "HDMI_HPD");
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n",
|
||||
"HDMI_HPD", config->hpd_gpio, ret);
|
||||
goto error3;
|
||||
}
|
||||
if (config->pmic_gpio != -1) {
|
||||
ret = gpio_request(config->pmic_gpio, "PMIC_HDMI_MUX_SEL");
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n",
|
||||
"PMIC_HDMI_MUX_SEL", config->pmic_gpio, ret);
|
||||
goto error4;
|
||||
}
|
||||
gpio_set_value_cansleep(config->pmic_gpio, 0);
|
||||
}
|
||||
DBG("gpio on");
|
||||
} else {
|
||||
gpio_free(config->ddc_clk_gpio);
|
||||
gpio_free(config->ddc_data_gpio);
|
||||
gpio_free(config->hpd_gpio);
|
||||
|
||||
if (config->pmic_gpio != -1) {
|
||||
gpio_set_value_cansleep(config->pmic_gpio, 1);
|
||||
gpio_free(config->pmic_gpio);
|
||||
}
|
||||
DBG("gpio off");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error4:
|
||||
gpio_free(config->hpd_gpio);
|
||||
error3:
|
||||
gpio_free(config->ddc_data_gpio);
|
||||
error2:
|
||||
gpio_free(config->ddc_clk_gpio);
|
||||
error1:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hpd_enable(struct hdmi_connector *hdmi_connector)
|
||||
{
|
||||
struct hdmi *hdmi = &hdmi_connector->hdmi;
|
||||
struct drm_device *dev = hdmi_connector->base.base.dev;
|
||||
struct hdmi_phy *phy = hdmi->phy;
|
||||
uint32_t hpd_ctrl;
|
||||
int ret;
|
||||
|
||||
ret = gpio_config(hdmi, true);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to configure GPIOs: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(hdmi->clk);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to enable 'clk': %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(hdmi->m_pclk);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to enable 'm_pclk': %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(hdmi->s_pclk);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to enable 's_pclk': %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (hdmi->mpp0)
|
||||
ret = regulator_enable(hdmi->mpp0);
|
||||
if (!ret)
|
||||
ret = regulator_enable(hdmi->mvs);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to enable regulators: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hdmi_set_mode(hdmi, false);
|
||||
phy->funcs->reset(phy);
|
||||
hdmi_set_mode(hdmi, true);
|
||||
|
||||
hdmi_write(hdmi, REG_HDMI_USEC_REFTIMER, 0x0001001b);
|
||||
|
||||
/* enable HPD events: */
|
||||
hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,
|
||||
HDMI_HPD_INT_CTRL_INT_CONNECT |
|
||||
HDMI_HPD_INT_CTRL_INT_EN);
|
||||
|
||||
/* set timeout to 4.1ms (max) for hardware debounce */
|
||||
hpd_ctrl = hdmi_read(hdmi, REG_HDMI_HPD_CTRL);
|
||||
hpd_ctrl |= HDMI_HPD_CTRL_TIMEOUT(0x1fff);
|
||||
|
||||
/* Toggle HPD circuit to trigger HPD sense */
|
||||
hdmi_write(hdmi, REG_HDMI_HPD_CTRL,
|
||||
~HDMI_HPD_CTRL_ENABLE & hpd_ctrl);
|
||||
hdmi_write(hdmi, REG_HDMI_HPD_CTRL,
|
||||
HDMI_HPD_CTRL_ENABLE | hpd_ctrl);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hdp_disable(struct hdmi_connector *hdmi_connector)
|
||||
{
|
||||
struct hdmi *hdmi = &hdmi_connector->hdmi;
|
||||
struct drm_device *dev = hdmi_connector->base.base.dev;
|
||||
int ret = 0;
|
||||
|
||||
/* Disable HPD interrupt */
|
||||
hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, 0);
|
||||
|
||||
hdmi_set_mode(hdmi, false);
|
||||
|
||||
if (hdmi->mpp0)
|
||||
ret = regulator_disable(hdmi->mpp0);
|
||||
if (!ret)
|
||||
ret = regulator_disable(hdmi->mvs);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to enable regulators: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
clk_disable_unprepare(hdmi->clk);
|
||||
clk_disable_unprepare(hdmi->m_pclk);
|
||||
clk_disable_unprepare(hdmi->s_pclk);
|
||||
|
||||
ret = gpio_config(hdmi, false);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to unconfigure GPIOs: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void hdmi_connector_irq(struct drm_connector *connector)
|
||||
{
|
||||
struct msm_connector *msm_connector = to_msm_connector(connector);
|
||||
struct hdmi_connector *hdmi_connector = to_hdmi_connector(msm_connector);
|
||||
struct hdmi *hdmi = &hdmi_connector->hdmi;
|
||||
uint32_t hpd_int_status, hpd_int_ctrl;
|
||||
|
||||
/* Process HPD: */
|
||||
hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
|
||||
hpd_int_ctrl = hdmi_read(hdmi, REG_HDMI_HPD_INT_CTRL);
|
||||
|
||||
if ((hpd_int_ctrl & HDMI_HPD_INT_CTRL_INT_EN) &&
|
||||
(hpd_int_status & HDMI_HPD_INT_STATUS_INT)) {
|
||||
bool detected = !!(hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED);
|
||||
|
||||
DBG("status=%04x, ctrl=%04x", hpd_int_status, hpd_int_ctrl);
|
||||
|
||||
/* ack the irq: */
|
||||
hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,
|
||||
hpd_int_ctrl | HDMI_HPD_INT_CTRL_INT_ACK);
|
||||
|
||||
drm_helper_hpd_irq_event(connector->dev);
|
||||
|
||||
/* detect disconnect if we are connected or visa versa: */
|
||||
hpd_int_ctrl = HDMI_HPD_INT_CTRL_INT_EN;
|
||||
if (!detected)
|
||||
hpd_int_ctrl |= HDMI_HPD_INT_CTRL_INT_CONNECT;
|
||||
hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, hpd_int_ctrl);
|
||||
}
|
||||
}
|
||||
|
||||
static enum drm_connector_status hdmi_connector_detect(
|
||||
struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct msm_connector *msm_connector = to_msm_connector(connector);
|
||||
struct hdmi_connector *hdmi_connector = to_hdmi_connector(msm_connector);
|
||||
struct hdmi *hdmi = &hdmi_connector->hdmi;
|
||||
uint32_t hpd_int_status;
|
||||
int retry = 20;
|
||||
|
||||
hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
|
||||
|
||||
/* sense seems to in some cases be momentarily de-asserted, don't
|
||||
* let that trick us into thinking the monitor is gone:
|
||||
*/
|
||||
while (retry-- && !(hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED)) {
|
||||
mdelay(10);
|
||||
hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
|
||||
DBG("status=%08x", hpd_int_status);
|
||||
}
|
||||
|
||||
return (hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED) ?
|
||||
connector_status_connected : connector_status_disconnected;
|
||||
}
|
||||
|
||||
static void hdmi_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct msm_connector *msm_connector = to_msm_connector(connector);
|
||||
struct hdmi_connector *hdmi_connector = to_hdmi_connector(msm_connector);
|
||||
|
||||
hdp_disable(hdmi_connector);
|
||||
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
|
||||
hdmi_destroy(&hdmi_connector->hdmi);
|
||||
|
||||
kfree(hdmi_connector);
|
||||
}
|
||||
|
||||
static int hdmi_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct msm_connector *msm_connector = to_msm_connector(connector);
|
||||
struct hdmi_connector *hdmi_connector = to_hdmi_connector(msm_connector);
|
||||
struct hdmi *hdmi = &hdmi_connector->hdmi;
|
||||
struct edid *edid;
|
||||
uint32_t hdmi_ctrl;
|
||||
int ret = 0;
|
||||
|
||||
hdmi_ctrl = hdmi_read(hdmi, REG_HDMI_CTRL);
|
||||
hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl | HDMI_CTRL_ENABLE);
|
||||
|
||||
edid = drm_get_edid(connector, hdmi->i2c);
|
||||
|
||||
hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl);
|
||||
|
||||
drm_mode_connector_update_edid_property(connector, edid);
|
||||
|
||||
if (edid) {
|
||||
ret = drm_add_edid_modes(connector, edid);
|
||||
kfree(edid);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hdmi_connector_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct msm_connector *msm_connector = to_msm_connector(connector);
|
||||
struct msm_drm_private *priv = connector->dev->dev_private;
|
||||
struct msm_kms *kms = priv->kms;
|
||||
long actual, requested;
|
||||
|
||||
requested = 1000 * mode->clock;
|
||||
actual = kms->funcs->round_pixclk(kms,
|
||||
requested, msm_connector->encoder);
|
||||
|
||||
DBG("requested=%ld, actual=%ld", requested, actual);
|
||||
|
||||
if (actual != requested)
|
||||
return MODE_CLOCK_RANGE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs hdmi_connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.detect = hdmi_connector_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = hdmi_connector_destroy,
|
||||
};
|
||||
|
||||
static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
|
||||
.get_modes = hdmi_connector_get_modes,
|
||||
.mode_valid = hdmi_connector_mode_valid,
|
||||
.best_encoder = msm_connector_attached_encoder,
|
||||
};
|
||||
|
||||
static void hdmi_connector_dpms(struct msm_connector *msm_connector, int mode)
|
||||
{
|
||||
struct hdmi_connector *hdmi_connector = to_hdmi_connector(msm_connector);
|
||||
struct hdmi *hdmi = &hdmi_connector->hdmi;
|
||||
struct hdmi_phy *phy = hdmi->phy;
|
||||
bool enabled = (mode == DRM_MODE_DPMS_ON);
|
||||
|
||||
DBG("mode=%d", mode);
|
||||
|
||||
if (enabled == hdmi_connector->enabled)
|
||||
return;
|
||||
|
||||
if (enabled) {
|
||||
phy->funcs->powerup(phy, hdmi_connector->pixclock);
|
||||
hdmi_set_mode(hdmi, true);
|
||||
} else {
|
||||
hdmi_set_mode(hdmi, false);
|
||||
phy->funcs->powerdown(phy);
|
||||
}
|
||||
|
||||
hdmi_connector->enabled = enabled;
|
||||
}
|
||||
|
||||
static void hdmi_connector_mode_set(struct msm_connector *msm_connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct hdmi_connector *hdmi_connector = to_hdmi_connector(msm_connector);
|
||||
struct hdmi *hdmi = &hdmi_connector->hdmi;
|
||||
int hstart, hend, vstart, vend;
|
||||
uint32_t frame_ctrl;
|
||||
|
||||
hdmi_connector->pixclock = mode->clock * 1000;
|
||||
|
||||
hdmi->hdmi_mode = drm_match_cea_mode(mode) > 1;
|
||||
|
||||
hstart = mode->htotal - mode->hsync_start;
|
||||
hend = mode->htotal - mode->hsync_start + mode->hdisplay;
|
||||
|
||||
vstart = mode->vtotal - mode->vsync_start - 1;
|
||||
vend = mode->vtotal - mode->vsync_start + mode->vdisplay - 1;
|
||||
|
||||
DBG("htotal=%d, vtotal=%d, hstart=%d, hend=%d, vstart=%d, vend=%d",
|
||||
mode->htotal, mode->vtotal, hstart, hend, vstart, vend);
|
||||
|
||||
hdmi_write(hdmi, REG_HDMI_TOTAL,
|
||||
HDMI_TOTAL_H_TOTAL(mode->htotal - 1) |
|
||||
HDMI_TOTAL_V_TOTAL(mode->vtotal - 1));
|
||||
|
||||
hdmi_write(hdmi, REG_HDMI_ACTIVE_HSYNC,
|
||||
HDMI_ACTIVE_HSYNC_START(hstart) |
|
||||
HDMI_ACTIVE_HSYNC_END(hend));
|
||||
hdmi_write(hdmi, REG_HDMI_ACTIVE_VSYNC,
|
||||
HDMI_ACTIVE_VSYNC_START(vstart) |
|
||||
HDMI_ACTIVE_VSYNC_END(vend));
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
|
||||
hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2,
|
||||
HDMI_VSYNC_TOTAL_F2_V_TOTAL(mode->vtotal));
|
||||
hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2,
|
||||
HDMI_VSYNC_ACTIVE_F2_START(vstart + 1) |
|
||||
HDMI_VSYNC_ACTIVE_F2_END(vend + 1));
|
||||
} else {
|
||||
hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2,
|
||||
HDMI_VSYNC_TOTAL_F2_V_TOTAL(0));
|
||||
hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2,
|
||||
HDMI_VSYNC_ACTIVE_F2_START(0) |
|
||||
HDMI_VSYNC_ACTIVE_F2_END(0));
|
||||
}
|
||||
|
||||
frame_ctrl = 0;
|
||||
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
|
||||
frame_ctrl |= HDMI_FRAME_CTRL_HSYNC_LOW;
|
||||
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
|
||||
frame_ctrl |= HDMI_FRAME_CTRL_VSYNC_LOW;
|
||||
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
|
||||
frame_ctrl |= HDMI_FRAME_CTRL_INTERLACED_EN;
|
||||
DBG("frame_ctrl=%08x", frame_ctrl);
|
||||
hdmi_write(hdmi, REG_HDMI_FRAME_CTRL, frame_ctrl);
|
||||
|
||||
// TODO until we have audio, this might be safest:
|
||||
if (hdmi->hdmi_mode)
|
||||
hdmi_write(hdmi, REG_HDMI_GC, HDMI_GC_MUTE);
|
||||
}
|
||||
|
||||
static const struct msm_connector_funcs msm_connector_funcs = {
|
||||
.dpms = hdmi_connector_dpms,
|
||||
.mode_set = hdmi_connector_mode_set,
|
||||
};
|
||||
|
||||
/* initialize connector */
|
||||
struct drm_connector *hdmi_connector_init(struct drm_device *dev,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_connector *connector = NULL;
|
||||
struct hdmi_connector *hdmi_connector;
|
||||
int ret;
|
||||
|
||||
hdmi_connector = kzalloc(sizeof(*hdmi_connector), GFP_KERNEL);
|
||||
if (!hdmi_connector) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
connector = &hdmi_connector->base.base;
|
||||
|
||||
msm_connector_init(&hdmi_connector->base,
|
||||
&msm_connector_funcs, encoder);
|
||||
drm_connector_init(dev, connector, &hdmi_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_HDMIA);
|
||||
drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
|
||||
|
||||
connector->polled = DRM_CONNECTOR_POLL_HPD;
|
||||
|
||||
connector->interlace_allowed = 1;
|
||||
connector->doublescan_allowed = 0;
|
||||
|
||||
drm_sysfs_connector_add(connector);
|
||||
|
||||
ret = hdmi_init(&hdmi_connector->hdmi, dev, connector);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
ret = hpd_enable(hdmi_connector);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to enable HPD: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
drm_mode_connector_attach_encoder(connector, encoder);
|
||||
|
||||
return connector;
|
||||
|
||||
fail:
|
||||
if (connector)
|
||||
hdmi_connector_destroy(connector);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hdmi.h"
|
||||
|
||||
struct hdmi_i2c_adapter {
|
||||
struct i2c_adapter base;
|
||||
struct hdmi *hdmi;
|
||||
bool sw_done;
|
||||
wait_queue_head_t ddc_event;
|
||||
};
|
||||
#define to_hdmi_i2c_adapter(x) container_of(x, struct hdmi_i2c_adapter, base)
|
||||
|
||||
static void init_ddc(struct hdmi_i2c_adapter *hdmi_i2c)
|
||||
{
|
||||
struct hdmi *hdmi = hdmi_i2c->hdmi;
|
||||
|
||||
hdmi_write(hdmi, REG_HDMI_DDC_CTRL,
|
||||
HDMI_DDC_CTRL_SW_STATUS_RESET);
|
||||
hdmi_write(hdmi, REG_HDMI_DDC_CTRL,
|
||||
HDMI_DDC_CTRL_SOFT_RESET);
|
||||
|
||||
hdmi_write(hdmi, REG_HDMI_DDC_SPEED,
|
||||
HDMI_DDC_SPEED_THRESHOLD(2) |
|
||||
HDMI_DDC_SPEED_PRESCALE(10));
|
||||
|
||||
hdmi_write(hdmi, REG_HDMI_DDC_SETUP,
|
||||
HDMI_DDC_SETUP_TIMEOUT(0xff));
|
||||
|
||||
/* enable reference timer for 27us */
|
||||
hdmi_write(hdmi, REG_HDMI_DDC_REF,
|
||||
HDMI_DDC_REF_REFTIMER_ENABLE |
|
||||
HDMI_DDC_REF_REFTIMER(27));
|
||||
}
|
||||
|
||||
static int ddc_clear_irq(struct hdmi_i2c_adapter *hdmi_i2c)
|
||||
{
|
||||
struct hdmi *hdmi = hdmi_i2c->hdmi;
|
||||
struct drm_device *dev = hdmi->dev;
|
||||
uint32_t retry = 0xffff;
|
||||
uint32_t ddc_int_ctrl;
|
||||
|
||||
do {
|
||||
--retry;
|
||||
|
||||
hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL,
|
||||
HDMI_DDC_INT_CTRL_SW_DONE_ACK |
|
||||
HDMI_DDC_INT_CTRL_SW_DONE_MASK);
|
||||
|
||||
ddc_int_ctrl = hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL);
|
||||
|
||||
} while ((ddc_int_ctrl & HDMI_DDC_INT_CTRL_SW_DONE_INT) && retry);
|
||||
|
||||
if (!retry) {
|
||||
dev_err(dev->dev, "timeout waiting for DDC\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
hdmi_i2c->sw_done = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MAX_TRANSACTIONS 4
|
||||
|
||||
static bool sw_done(struct hdmi_i2c_adapter *hdmi_i2c)
|
||||
{
|
||||
struct hdmi *hdmi = hdmi_i2c->hdmi;
|
||||
|
||||
if (!hdmi_i2c->sw_done) {
|
||||
uint32_t ddc_int_ctrl;
|
||||
|
||||
ddc_int_ctrl = hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL);
|
||||
|
||||
if ((ddc_int_ctrl & HDMI_DDC_INT_CTRL_SW_DONE_MASK) &&
|
||||
(ddc_int_ctrl & HDMI_DDC_INT_CTRL_SW_DONE_INT)) {
|
||||
hdmi_i2c->sw_done = true;
|
||||
hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL,
|
||||
HDMI_DDC_INT_CTRL_SW_DONE_ACK);
|
||||
}
|
||||
}
|
||||
|
||||
return hdmi_i2c->sw_done;
|
||||
}
|
||||
|
||||
static int hdmi_i2c_xfer(struct i2c_adapter *i2c,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
struct hdmi_i2c_adapter *hdmi_i2c = to_hdmi_i2c_adapter(i2c);
|
||||
struct hdmi *hdmi = hdmi_i2c->hdmi;
|
||||
struct drm_device *dev = hdmi->dev;
|
||||
static const uint32_t nack[] = {
|
||||
HDMI_DDC_SW_STATUS_NACK0, HDMI_DDC_SW_STATUS_NACK1,
|
||||
HDMI_DDC_SW_STATUS_NACK2, HDMI_DDC_SW_STATUS_NACK3,
|
||||
};
|
||||
int indices[MAX_TRANSACTIONS];
|
||||
int ret, i, j, index = 0;
|
||||
uint32_t ddc_status, ddc_data, i2c_trans;
|
||||
|
||||
num = min(num, MAX_TRANSACTIONS);
|
||||
|
||||
WARN_ON(!(hdmi_read(hdmi, REG_HDMI_CTRL) & HDMI_CTRL_ENABLE));
|
||||
|
||||
if (num == 0)
|
||||
return num;
|
||||
|
||||
init_ddc(hdmi_i2c);
|
||||
|
||||
ret = ddc_clear_irq(hdmi_i2c);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
struct i2c_msg *p = &msgs[i];
|
||||
uint32_t raw_addr = p->addr << 1;
|
||||
|
||||
if (p->flags & I2C_M_RD)
|
||||
raw_addr |= 1;
|
||||
|
||||
ddc_data = HDMI_DDC_DATA_DATA(raw_addr) |
|
||||
HDMI_DDC_DATA_DATA_RW(DDC_WRITE);
|
||||
|
||||
if (i == 0) {
|
||||
ddc_data |= HDMI_DDC_DATA_INDEX(0) |
|
||||
HDMI_DDC_DATA_INDEX_WRITE;
|
||||
}
|
||||
|
||||
hdmi_write(hdmi, REG_HDMI_DDC_DATA, ddc_data);
|
||||
index++;
|
||||
|
||||
indices[i] = index;
|
||||
|
||||
if (p->flags & I2C_M_RD) {
|
||||
index += p->len;
|
||||
} else {
|
||||
for (j = 0; j < p->len; j++) {
|
||||
ddc_data = HDMI_DDC_DATA_DATA(p->buf[j]) |
|
||||
HDMI_DDC_DATA_DATA_RW(DDC_WRITE);
|
||||
hdmi_write(hdmi, REG_HDMI_DDC_DATA, ddc_data);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_trans = HDMI_I2C_TRANSACTION_REG_CNT(p->len) |
|
||||
HDMI_I2C_TRANSACTION_REG_RW(
|
||||
(p->flags & I2C_M_RD) ? DDC_READ : DDC_WRITE) |
|
||||
HDMI_I2C_TRANSACTION_REG_START;
|
||||
|
||||
if (i == (num - 1))
|
||||
i2c_trans |= HDMI_I2C_TRANSACTION_REG_STOP;
|
||||
|
||||
hdmi_write(hdmi, REG_HDMI_I2C_TRANSACTION(i), i2c_trans);
|
||||
}
|
||||
|
||||
/* trigger the transfer: */
|
||||
hdmi_write(hdmi, REG_HDMI_DDC_CTRL,
|
||||
HDMI_DDC_CTRL_TRANSACTION_CNT(num - 1) |
|
||||
HDMI_DDC_CTRL_GO);
|
||||
|
||||
ret = wait_event_timeout(hdmi_i2c->ddc_event, sw_done(hdmi_i2c), HZ/4);
|
||||
if (ret <= 0) {
|
||||
if (ret == 0)
|
||||
ret = -ETIMEDOUT;
|
||||
dev_warn(dev->dev, "DDC timeout: %d\n", ret);
|
||||
DBG("sw_status=%08x, hw_status=%08x, int_ctrl=%08x",
|
||||
hdmi_read(hdmi, REG_HDMI_DDC_SW_STATUS),
|
||||
hdmi_read(hdmi, REG_HDMI_DDC_HW_STATUS),
|
||||
hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ddc_status = hdmi_read(hdmi, REG_HDMI_DDC_SW_STATUS);
|
||||
|
||||
/* read back results of any read transactions: */
|
||||
for (i = 0; i < num; i++) {
|
||||
struct i2c_msg *p = &msgs[i];
|
||||
|
||||
if (!(p->flags & I2C_M_RD))
|
||||
continue;
|
||||
|
||||
/* check for NACK: */
|
||||
if (ddc_status & nack[i]) {
|
||||
DBG("ddc_status=%08x", ddc_status);
|
||||
break;
|
||||
}
|
||||
|
||||
ddc_data = HDMI_DDC_DATA_DATA_RW(DDC_READ) |
|
||||
HDMI_DDC_DATA_INDEX(indices[i]) |
|
||||
HDMI_DDC_DATA_INDEX_WRITE;
|
||||
|
||||
hdmi_write(hdmi, REG_HDMI_DDC_DATA, ddc_data);
|
||||
|
||||
/* discard first byte: */
|
||||
hdmi_read(hdmi, REG_HDMI_DDC_DATA);
|
||||
|
||||
for (j = 0; j < p->len; j++) {
|
||||
ddc_data = hdmi_read(hdmi, REG_HDMI_DDC_DATA);
|
||||
p->buf[j] = FIELD(ddc_data, HDMI_DDC_DATA_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static u32 hdmi_i2c_func(struct i2c_adapter *adapter)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm hdmi_i2c_algorithm = {
|
||||
.master_xfer = hdmi_i2c_xfer,
|
||||
.functionality = hdmi_i2c_func,
|
||||
};
|
||||
|
||||
void hdmi_i2c_irq(struct i2c_adapter *i2c)
|
||||
{
|
||||
struct hdmi_i2c_adapter *hdmi_i2c = to_hdmi_i2c_adapter(i2c);
|
||||
|
||||
if (sw_done(hdmi_i2c))
|
||||
wake_up_all(&hdmi_i2c->ddc_event);
|
||||
}
|
||||
|
||||
void hdmi_i2c_destroy(struct i2c_adapter *i2c)
|
||||
{
|
||||
struct hdmi_i2c_adapter *hdmi_i2c = to_hdmi_i2c_adapter(i2c);
|
||||
i2c_del_adapter(i2c);
|
||||
kfree(hdmi_i2c);
|
||||
}
|
||||
|
||||
struct i2c_adapter *hdmi_i2c_init(struct hdmi *hdmi)
|
||||
{
|
||||
struct drm_device *dev = hdmi->dev;
|
||||
struct hdmi_i2c_adapter *hdmi_i2c;
|
||||
struct i2c_adapter *i2c = NULL;
|
||||
int ret;
|
||||
|
||||
hdmi_i2c = kzalloc(sizeof(*hdmi_i2c), GFP_KERNEL);
|
||||
if (!hdmi_i2c) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
i2c = &hdmi_i2c->base;
|
||||
|
||||
hdmi_i2c->hdmi = hdmi;
|
||||
init_waitqueue_head(&hdmi_i2c->ddc_event);
|
||||
|
||||
|
||||
i2c->owner = THIS_MODULE;
|
||||
i2c->class = I2C_CLASS_DDC;
|
||||
snprintf(i2c->name, sizeof(i2c->name), "msm hdmi i2c");
|
||||
i2c->dev.parent = &hdmi->pdev->dev;
|
||||
i2c->algo = &hdmi_i2c_algorithm;
|
||||
|
||||
ret = i2c_add_adapter(i2c);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to register hdmi i2c: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return i2c;
|
||||
|
||||
fail:
|
||||
if (i2c)
|
||||
hdmi_i2c_destroy(i2c);
|
||||
return ERR_PTR(ret);
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hdmi.h"
|
||||
|
||||
struct hdmi_phy_8960 {
|
||||
struct hdmi_phy base;
|
||||
struct hdmi *hdmi;
|
||||
};
|
||||
#define to_hdmi_phy_8960(x) container_of(x, struct hdmi_phy_8960, base)
|
||||
|
||||
static void hdmi_phy_8960_destroy(struct hdmi_phy *phy)
|
||||
{
|
||||
struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
|
||||
kfree(phy_8960);
|
||||
}
|
||||
|
||||
static void hdmi_phy_8960_reset(struct hdmi_phy *phy)
|
||||
{
|
||||
struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
|
||||
struct hdmi *hdmi = phy_8960->hdmi;
|
||||
unsigned int val;
|
||||
|
||||
val = hdmi_read(hdmi, REG_HDMI_PHY_CTRL);
|
||||
|
||||
if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
|
||||
/* pull low */
|
||||
hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
|
||||
val & ~HDMI_PHY_CTRL_SW_RESET);
|
||||
} else {
|
||||
/* pull high */
|
||||
hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
|
||||
val | HDMI_PHY_CTRL_SW_RESET);
|
||||
}
|
||||
|
||||
if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) {
|
||||
/* pull low */
|
||||
hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
|
||||
val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
|
||||
} else {
|
||||
/* pull high */
|
||||
hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
|
||||
val | HDMI_PHY_CTRL_SW_RESET_PLL);
|
||||
}
|
||||
|
||||
msleep(100);
|
||||
|
||||
if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
|
||||
/* pull high */
|
||||
hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
|
||||
val | HDMI_PHY_CTRL_SW_RESET);
|
||||
} else {
|
||||
/* pull low */
|
||||
hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
|
||||
val & ~HDMI_PHY_CTRL_SW_RESET);
|
||||
}
|
||||
|
||||
if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) {
|
||||
/* pull high */
|
||||
hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
|
||||
val | HDMI_PHY_CTRL_SW_RESET_PLL);
|
||||
} else {
|
||||
/* pull low */
|
||||
hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
|
||||
val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
|
||||
}
|
||||
}
|
||||
|
||||
static void hdmi_phy_8960_powerup(struct hdmi_phy *phy,
|
||||
unsigned long int pixclock)
|
||||
{
|
||||
struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
|
||||
struct hdmi *hdmi = phy_8960->hdmi;
|
||||
|
||||
hdmi_write(hdmi, REG_HDMI_8960_PHY_REG0, 0x1b);
|
||||
hdmi_write(hdmi, REG_HDMI_8960_PHY_REG1, 0xf2);
|
||||
hdmi_write(hdmi, REG_HDMI_8960_PHY_REG4, 0x00);
|
||||
hdmi_write(hdmi, REG_HDMI_8960_PHY_REG5, 0x00);
|
||||
hdmi_write(hdmi, REG_HDMI_8960_PHY_REG6, 0x00);
|
||||
hdmi_write(hdmi, REG_HDMI_8960_PHY_REG7, 0x00);
|
||||
hdmi_write(hdmi, REG_HDMI_8960_PHY_REG8, 0x00);
|
||||
hdmi_write(hdmi, REG_HDMI_8960_PHY_REG9, 0x00);
|
||||
hdmi_write(hdmi, REG_HDMI_8960_PHY_REG10, 0x00);
|
||||
hdmi_write(hdmi, REG_HDMI_8960_PHY_REG11, 0x00);
|
||||
hdmi_write(hdmi, REG_HDMI_8960_PHY_REG3, 0x20);
|
||||
}
|
||||
|
||||
static void hdmi_phy_8960_powerdown(struct hdmi_phy *phy)
|
||||
{
|
||||
struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
|
||||
struct hdmi *hdmi = phy_8960->hdmi;
|
||||
|
||||
hdmi_write(hdmi, REG_HDMI_8960_PHY_REG2, 0x7f);
|
||||
}
|
||||
|
||||
static const struct hdmi_phy_funcs hdmi_phy_8960_funcs = {
|
||||
.destroy = hdmi_phy_8960_destroy,
|
||||
.reset = hdmi_phy_8960_reset,
|
||||
.powerup = hdmi_phy_8960_powerup,
|
||||
.powerdown = hdmi_phy_8960_powerdown,
|
||||
};
|
||||
|
||||
struct hdmi_phy *hdmi_phy_8960_init(struct hdmi *hdmi)
|
||||
{
|
||||
struct hdmi_phy_8960 *phy_8960;
|
||||
struct hdmi_phy *phy = NULL;
|
||||
int ret;
|
||||
|
||||
phy_8960 = kzalloc(sizeof(*phy_8960), GFP_KERNEL);
|
||||
if (!phy_8960) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
phy = &phy_8960->base;
|
||||
|
||||
phy->funcs = &hdmi_phy_8960_funcs;
|
||||
|
||||
phy_8960->hdmi = hdmi;
|
||||
|
||||
return phy;
|
||||
|
||||
fail:
|
||||
if (phy)
|
||||
hdmi_phy_8960_destroy(phy);
|
||||
return ERR_PTR(ret);
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hdmi.h"
|
||||
|
||||
struct hdmi_phy_8x60 {
|
||||
struct hdmi_phy base;
|
||||
struct hdmi *hdmi;
|
||||
};
|
||||
#define to_hdmi_phy_8x60(x) container_of(x, struct hdmi_phy_8x60, base)
|
||||
|
||||
static void hdmi_phy_8x60_destroy(struct hdmi_phy *phy)
|
||||
{
|
||||
struct hdmi_phy_8x60 *phy_8x60 = to_hdmi_phy_8x60(phy);
|
||||
kfree(phy_8x60);
|
||||
}
|
||||
|
||||
static void hdmi_phy_8x60_reset(struct hdmi_phy *phy)
|
||||
{
|
||||
struct hdmi_phy_8x60 *phy_8x60 = to_hdmi_phy_8x60(phy);
|
||||
struct hdmi *hdmi = phy_8x60->hdmi;
|
||||
unsigned int val;
|
||||
|
||||
val = hdmi_read(hdmi, REG_HDMI_PHY_CTRL);
|
||||
|
||||
if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
|
||||
/* pull low */
|
||||
hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
|
||||
val & ~HDMI_PHY_CTRL_SW_RESET);
|
||||
} else {
|
||||
/* pull high */
|
||||
hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
|
||||
val | HDMI_PHY_CTRL_SW_RESET);
|
||||
}
|
||||
|
||||
msleep(100);
|
||||
|
||||
if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
|
||||
/* pull high */
|
||||
hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
|
||||
val | HDMI_PHY_CTRL_SW_RESET);
|
||||
} else {
|
||||
/* pull low */
|
||||
hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
|
||||
val & ~HDMI_PHY_CTRL_SW_RESET);
|
||||
}
|
||||
}
|
||||
|
||||
static void hdmi_phy_8x60_powerup(struct hdmi_phy *phy,
|
||||
unsigned long int pixclock)
|
||||
{
|
||||
struct hdmi_phy_8x60 *phy_8x60 = to_hdmi_phy_8x60(phy);
|
||||
struct hdmi *hdmi = phy_8x60->hdmi;
|
||||
|
||||
/* De-serializer delay D/C for non-lbk mode: */
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG0,
|
||||
HDMI_8x60_PHY_REG0_DESER_DEL_CTRL(3));
|
||||
|
||||
if (pixclock == 27000000) {
|
||||
/* video_format == HDMI_VFRMT_720x480p60_16_9 */
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG1,
|
||||
HDMI_8x60_PHY_REG1_DTEST_MUX_SEL(5) |
|
||||
HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL(3));
|
||||
} else {
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG1,
|
||||
HDMI_8x60_PHY_REG1_DTEST_MUX_SEL(5) |
|
||||
HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL(4));
|
||||
}
|
||||
|
||||
/* No matter what, start from the power down mode: */
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2,
|
||||
HDMI_8x60_PHY_REG2_PD_PWRGEN |
|
||||
HDMI_8x60_PHY_REG2_PD_PLL |
|
||||
HDMI_8x60_PHY_REG2_PD_DRIVE_4 |
|
||||
HDMI_8x60_PHY_REG2_PD_DRIVE_3 |
|
||||
HDMI_8x60_PHY_REG2_PD_DRIVE_2 |
|
||||
HDMI_8x60_PHY_REG2_PD_DRIVE_1 |
|
||||
HDMI_8x60_PHY_REG2_PD_DESER);
|
||||
|
||||
/* Turn PowerGen on: */
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2,
|
||||
HDMI_8x60_PHY_REG2_PD_PLL |
|
||||
HDMI_8x60_PHY_REG2_PD_DRIVE_4 |
|
||||
HDMI_8x60_PHY_REG2_PD_DRIVE_3 |
|
||||
HDMI_8x60_PHY_REG2_PD_DRIVE_2 |
|
||||
HDMI_8x60_PHY_REG2_PD_DRIVE_1 |
|
||||
HDMI_8x60_PHY_REG2_PD_DESER);
|
||||
|
||||
/* Turn PLL power on: */
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2,
|
||||
HDMI_8x60_PHY_REG2_PD_DRIVE_4 |
|
||||
HDMI_8x60_PHY_REG2_PD_DRIVE_3 |
|
||||
HDMI_8x60_PHY_REG2_PD_DRIVE_2 |
|
||||
HDMI_8x60_PHY_REG2_PD_DRIVE_1 |
|
||||
HDMI_8x60_PHY_REG2_PD_DESER);
|
||||
|
||||
/* Write to HIGH after PLL power down de-assert: */
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG3,
|
||||
HDMI_8x60_PHY_REG3_PLL_ENABLE);
|
||||
|
||||
/* ASIC power on; PHY REG9 = 0 */
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG9, 0);
|
||||
|
||||
/* Enable PLL lock detect, PLL lock det will go high after lock
|
||||
* Enable the re-time logic
|
||||
*/
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG12,
|
||||
HDMI_8x60_PHY_REG12_RETIMING_EN |
|
||||
HDMI_8x60_PHY_REG12_PLL_LOCK_DETECT_EN);
|
||||
|
||||
/* Drivers are on: */
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2,
|
||||
HDMI_8x60_PHY_REG2_PD_DESER);
|
||||
|
||||
/* If the RX detector is needed: */
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2,
|
||||
HDMI_8x60_PHY_REG2_RCV_SENSE_EN |
|
||||
HDMI_8x60_PHY_REG2_PD_DESER);
|
||||
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG4, 0);
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG5, 0);
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG6, 0);
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG7, 0);
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG8, 0);
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG9, 0);
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG10, 0);
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG11, 0);
|
||||
|
||||
/* If we want to use lock enable based on counting: */
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG12,
|
||||
HDMI_8x60_PHY_REG12_RETIMING_EN |
|
||||
HDMI_8x60_PHY_REG12_PLL_LOCK_DETECT_EN |
|
||||
HDMI_8x60_PHY_REG12_FORCE_LOCK);
|
||||
}
|
||||
|
||||
static void hdmi_phy_8x60_powerdown(struct hdmi_phy *phy)
|
||||
{
|
||||
struct hdmi_phy_8x60 *phy_8x60 = to_hdmi_phy_8x60(phy);
|
||||
struct hdmi *hdmi = phy_8x60->hdmi;
|
||||
|
||||
/* Assert RESET PHY from controller */
|
||||
hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
|
||||
HDMI_PHY_CTRL_SW_RESET);
|
||||
udelay(10);
|
||||
/* De-assert RESET PHY from controller */
|
||||
hdmi_write(hdmi, REG_HDMI_PHY_CTRL, 0);
|
||||
/* Turn off Driver */
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2,
|
||||
HDMI_8x60_PHY_REG2_PD_DRIVE_4 |
|
||||
HDMI_8x60_PHY_REG2_PD_DRIVE_3 |
|
||||
HDMI_8x60_PHY_REG2_PD_DRIVE_2 |
|
||||
HDMI_8x60_PHY_REG2_PD_DRIVE_1 |
|
||||
HDMI_8x60_PHY_REG2_PD_DESER);
|
||||
udelay(10);
|
||||
/* Disable PLL */
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG3, 0);
|
||||
/* Power down PHY, but keep RX-sense: */
|
||||
hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2,
|
||||
HDMI_8x60_PHY_REG2_RCV_SENSE_EN |
|
||||
HDMI_8x60_PHY_REG2_PD_PWRGEN |
|
||||
HDMI_8x60_PHY_REG2_PD_PLL |
|
||||
HDMI_8x60_PHY_REG2_PD_DRIVE_4 |
|
||||
HDMI_8x60_PHY_REG2_PD_DRIVE_3 |
|
||||
HDMI_8x60_PHY_REG2_PD_DRIVE_2 |
|
||||
HDMI_8x60_PHY_REG2_PD_DRIVE_1 |
|
||||
HDMI_8x60_PHY_REG2_PD_DESER);
|
||||
}
|
||||
|
||||
static const struct hdmi_phy_funcs hdmi_phy_8x60_funcs = {
|
||||
.destroy = hdmi_phy_8x60_destroy,
|
||||
.reset = hdmi_phy_8x60_reset,
|
||||
.powerup = hdmi_phy_8x60_powerup,
|
||||
.powerdown = hdmi_phy_8x60_powerdown,
|
||||
};
|
||||
|
||||
struct hdmi_phy *hdmi_phy_8x60_init(struct hdmi *hdmi)
|
||||
{
|
||||
struct hdmi_phy_8x60 *phy_8x60;
|
||||
struct hdmi_phy *phy = NULL;
|
||||
int ret;
|
||||
|
||||
phy_8x60 = kzalloc(sizeof(*phy_8x60), GFP_KERNEL);
|
||||
if (!phy_8x60) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
phy = &phy_8x60->base;
|
||||
|
||||
phy->funcs = &hdmi_phy_8x60_funcs;
|
||||
|
||||
phy_8x60->hdmi = hdmi;
|
||||
|
||||
return phy;
|
||||
|
||||
fail:
|
||||
if (phy)
|
||||
hdmi_phy_8x60_destroy(phy);
|
||||
return ERR_PTR(ret);
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef QFPROM_XML
|
||||
#define QFPROM_XML
|
||||
|
||||
/* Autogenerated file, DO NOT EDIT manually!
|
||||
|
||||
This file was generated by the rules-ng-ng headergen tool in this git repository:
|
||||
http://0x04.net/cgit/index.cgi/rules-ng-ng
|
||||
git clone git://0x04.net/rules-ng-ng
|
||||
|
||||
The rules-ng-ng source files this header was generated from are:
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 595 bytes, from 2013-07-05 19:21:12)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml ( 19332 bytes, from 2013-08-16 22:16:36)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1544 bytes, from 2013-08-16 19:17:05)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12)
|
||||
- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 19288 bytes, from 2013-08-11 18:14:15)
|
||||
|
||||
Copyright (C) 2013 by the following authors:
|
||||
- Rob Clark <robdclark@gmail.com> (robclark)
|
||||
|
||||
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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS 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.
|
||||
*/
|
||||
|
||||
|
||||
#define REG_QFPROM_CONFIG_ROW0_LSB 0x00000238
|
||||
#define QFPROM_CONFIG_ROW0_LSB_HDMI_DISABLE 0x00200000
|
||||
#define QFPROM_CONFIG_ROW0_LSB_HDCP_DISABLE 0x00400000
|
||||
|
||||
|
||||
#endif /* QFPROM_XML */
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,684 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "mdp4_kms.h"
|
||||
|
||||
#include <drm/drm_mode.h>
|
||||
#include "drm_crtc.h"
|
||||
#include "drm_crtc_helper.h"
|
||||
#include "drm_flip_work.h"
|
||||
|
||||
struct mdp4_crtc {
|
||||
struct drm_crtc base;
|
||||
char name[8];
|
||||
struct drm_plane *plane;
|
||||
int id;
|
||||
int ovlp;
|
||||
enum mdp4_dma dma;
|
||||
bool enabled;
|
||||
|
||||
/* which mixer/encoder we route output to: */
|
||||
int mixer;
|
||||
|
||||
struct {
|
||||
spinlock_t lock;
|
||||
bool stale;
|
||||
uint32_t width, height;
|
||||
|
||||
/* next cursor to scan-out: */
|
||||
uint32_t next_iova;
|
||||
struct drm_gem_object *next_bo;
|
||||
|
||||
/* current cursor being scanned out: */
|
||||
struct drm_gem_object *scanout_bo;
|
||||
} cursor;
|
||||
|
||||
|
||||
/* if there is a pending flip, these will be non-null: */
|
||||
struct drm_pending_vblank_event *event;
|
||||
struct work_struct pageflip_work;
|
||||
|
||||
/* the fb that we currently hold a scanout ref to: */
|
||||
struct drm_framebuffer *fb;
|
||||
|
||||
/* for unref'ing framebuffers after scanout completes: */
|
||||
struct drm_flip_work unref_fb_work;
|
||||
|
||||
/* for unref'ing cursor bo's after scanout completes: */
|
||||
struct drm_flip_work unref_cursor_work;
|
||||
|
||||
struct mdp4_irq vblank;
|
||||
struct mdp4_irq err;
|
||||
};
|
||||
#define to_mdp4_crtc(x) container_of(x, struct mdp4_crtc, base)
|
||||
|
||||
static struct mdp4_kms *get_kms(struct drm_crtc *crtc)
|
||||
{
|
||||
struct msm_drm_private *priv = crtc->dev->dev_private;
|
||||
return to_mdp4_kms(priv->kms);
|
||||
}
|
||||
|
||||
static void update_fb(struct drm_crtc *crtc, bool async,
|
||||
struct drm_framebuffer *new_fb)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
|
||||
struct drm_framebuffer *old_fb = mdp4_crtc->fb;
|
||||
|
||||
if (old_fb)
|
||||
drm_flip_work_queue(&mdp4_crtc->unref_fb_work, old_fb);
|
||||
|
||||
/* grab reference to incoming scanout fb: */
|
||||
drm_framebuffer_reference(new_fb);
|
||||
mdp4_crtc->base.fb = new_fb;
|
||||
mdp4_crtc->fb = new_fb;
|
||||
|
||||
if (!async) {
|
||||
/* enable vblank to pick up the old_fb */
|
||||
mdp4_irq_register(get_kms(crtc), &mdp4_crtc->vblank);
|
||||
}
|
||||
}
|
||||
|
||||
static void complete_flip(struct drm_crtc *crtc, bool canceled)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct drm_pending_vblank_event *event;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
event = mdp4_crtc->event;
|
||||
if (event) {
|
||||
mdp4_crtc->event = NULL;
|
||||
if (canceled)
|
||||
event->base.destroy(&event->base);
|
||||
else
|
||||
drm_send_vblank_event(dev, mdp4_crtc->id, event);
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
}
|
||||
|
||||
static void crtc_flush(struct drm_crtc *crtc)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
|
||||
struct mdp4_kms *mdp4_kms = get_kms(crtc);
|
||||
uint32_t flush = 0;
|
||||
|
||||
flush |= pipe2flush(mdp4_plane_pipe(mdp4_crtc->plane));
|
||||
flush |= ovlp2flush(mdp4_crtc->ovlp);
|
||||
|
||||
DBG("%s: flush=%08x", mdp4_crtc->name, flush);
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_OVERLAY_FLUSH, flush);
|
||||
}
|
||||
|
||||
static void pageflip_worker(struct work_struct *work)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc =
|
||||
container_of(work, struct mdp4_crtc, pageflip_work);
|
||||
struct drm_crtc *crtc = &mdp4_crtc->base;
|
||||
|
||||
mdp4_plane_set_scanout(mdp4_crtc->plane, crtc->fb);
|
||||
crtc_flush(crtc);
|
||||
|
||||
/* enable vblank to complete flip: */
|
||||
mdp4_irq_register(get_kms(crtc), &mdp4_crtc->vblank);
|
||||
}
|
||||
|
||||
static void unref_fb_worker(struct drm_flip_work *work, void *val)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc =
|
||||
container_of(work, struct mdp4_crtc, unref_fb_work);
|
||||
struct drm_device *dev = mdp4_crtc->base.dev;
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
drm_framebuffer_unreference(val);
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
}
|
||||
|
||||
static void unref_cursor_worker(struct drm_flip_work *work, void *val)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc =
|
||||
container_of(work, struct mdp4_crtc, unref_cursor_work);
|
||||
struct mdp4_kms *mdp4_kms = get_kms(&mdp4_crtc->base);
|
||||
|
||||
msm_gem_put_iova(val, mdp4_kms->id);
|
||||
drm_gem_object_unreference_unlocked(val);
|
||||
}
|
||||
|
||||
static void mdp4_crtc_destroy(struct drm_crtc *crtc)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
|
||||
|
||||
mdp4_crtc->plane->funcs->destroy(mdp4_crtc->plane);
|
||||
|
||||
drm_crtc_cleanup(crtc);
|
||||
drm_flip_work_cleanup(&mdp4_crtc->unref_fb_work);
|
||||
drm_flip_work_cleanup(&mdp4_crtc->unref_cursor_work);
|
||||
|
||||
kfree(mdp4_crtc);
|
||||
}
|
||||
|
||||
static void mdp4_crtc_dpms(struct drm_crtc *crtc, int mode)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
|
||||
struct mdp4_kms *mdp4_kms = get_kms(crtc);
|
||||
bool enabled = (mode == DRM_MODE_DPMS_ON);
|
||||
|
||||
DBG("%s: mode=%d", mdp4_crtc->name, mode);
|
||||
|
||||
if (enabled != mdp4_crtc->enabled) {
|
||||
if (enabled) {
|
||||
mdp4_enable(mdp4_kms);
|
||||
mdp4_irq_register(mdp4_kms, &mdp4_crtc->err);
|
||||
} else {
|
||||
mdp4_irq_unregister(mdp4_kms, &mdp4_crtc->err);
|
||||
mdp4_disable(mdp4_kms);
|
||||
}
|
||||
mdp4_crtc->enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
static bool mdp4_crtc_mode_fixup(struct drm_crtc *crtc,
|
||||
const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void blend_setup(struct drm_crtc *crtc)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
|
||||
struct mdp4_kms *mdp4_kms = get_kms(crtc);
|
||||
int i, ovlp = mdp4_crtc->ovlp;
|
||||
uint32_t mixer_cfg = 0;
|
||||
|
||||
/*
|
||||
* This probably would also need to be triggered by any attached
|
||||
* plane when it changes.. for now since we are only using a single
|
||||
* private plane, the configuration is hard-coded:
|
||||
*/
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_LOW0(ovlp), 0);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_LOW1(ovlp), 0);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_HIGH0(ovlp), 0);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_HIGH1(ovlp), 0);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_FG_ALPHA(ovlp, i), 0);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_BG_ALPHA(ovlp, i), 0);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_OP(ovlp, i),
|
||||
MDP4_OVLP_STAGE_OP_FG_ALPHA(FG_CONST) |
|
||||
MDP4_OVLP_STAGE_OP_BG_ALPHA(BG_CONST));
|
||||
mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_CO3(ovlp, i), 0);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_LOW0(ovlp, i), 0);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_LOW1(ovlp, i), 0);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_HIGH0(ovlp, i), 0);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_HIGH1(ovlp, i), 0);
|
||||
}
|
||||
|
||||
/* TODO single register for all CRTCs, so this won't work properly
|
||||
* when multiple CRTCs are active..
|
||||
*/
|
||||
switch (mdp4_plane_pipe(mdp4_crtc->plane)) {
|
||||
case VG1:
|
||||
mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE0(STAGE_BASE) |
|
||||
COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE0_MIXER1);
|
||||
break;
|
||||
case VG2:
|
||||
mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE1(STAGE_BASE) |
|
||||
COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE1_MIXER1);
|
||||
break;
|
||||
case RGB1:
|
||||
mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE2(STAGE_BASE) |
|
||||
COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE2_MIXER1);
|
||||
break;
|
||||
case RGB2:
|
||||
mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE3(STAGE_BASE) |
|
||||
COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE3_MIXER1);
|
||||
break;
|
||||
case RGB3:
|
||||
mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE4(STAGE_BASE) |
|
||||
COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE4_MIXER1);
|
||||
break;
|
||||
case VG3:
|
||||
mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE5(STAGE_BASE) |
|
||||
COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE5_MIXER1);
|
||||
break;
|
||||
case VG4:
|
||||
mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE6(STAGE_BASE) |
|
||||
COND(mdp4_crtc->mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE6_MIXER1);
|
||||
break;
|
||||
default:
|
||||
WARN_ON("invalid pipe");
|
||||
break;
|
||||
}
|
||||
mdp4_write(mdp4_kms, REG_MDP4_LAYERMIXER_IN_CFG, mixer_cfg);
|
||||
}
|
||||
|
||||
static int mdp4_crtc_mode_set(struct drm_crtc *crtc,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode,
|
||||
int x, int y,
|
||||
struct drm_framebuffer *old_fb)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
|
||||
struct mdp4_kms *mdp4_kms = get_kms(crtc);
|
||||
enum mdp4_dma dma = mdp4_crtc->dma;
|
||||
int ret, ovlp = mdp4_crtc->ovlp;
|
||||
|
||||
mode = adjusted_mode;
|
||||
|
||||
DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
|
||||
mdp4_crtc->name, mode->base.id, mode->name,
|
||||
mode->vrefresh, mode->clock,
|
||||
mode->hdisplay, mode->hsync_start,
|
||||
mode->hsync_end, mode->htotal,
|
||||
mode->vdisplay, mode->vsync_start,
|
||||
mode->vsync_end, mode->vtotal,
|
||||
mode->type, mode->flags);
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DMA_SRC_SIZE(dma),
|
||||
MDP4_DMA_SRC_SIZE_WIDTH(mode->hdisplay) |
|
||||
MDP4_DMA_SRC_SIZE_HEIGHT(mode->vdisplay));
|
||||
|
||||
/* take data from pipe: */
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DMA_SRC_BASE(dma), 0);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DMA_SRC_STRIDE(dma),
|
||||
crtc->fb->pitches[0]);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DMA_DST_SIZE(dma),
|
||||
MDP4_DMA_DST_SIZE_WIDTH(0) |
|
||||
MDP4_DMA_DST_SIZE_HEIGHT(0));
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_OVLP_BASE(ovlp), 0);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_OVLP_SIZE(ovlp),
|
||||
MDP4_OVLP_SIZE_WIDTH(mode->hdisplay) |
|
||||
MDP4_OVLP_SIZE_HEIGHT(mode->vdisplay));
|
||||
mdp4_write(mdp4_kms, REG_MDP4_OVLP_STRIDE(ovlp),
|
||||
crtc->fb->pitches[0]);
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_OVLP_CFG(ovlp), 1);
|
||||
|
||||
update_fb(crtc, false, crtc->fb);
|
||||
|
||||
ret = mdp4_plane_mode_set(mdp4_crtc->plane, crtc, crtc->fb,
|
||||
0, 0, mode->hdisplay, mode->vdisplay,
|
||||
x << 16, y << 16,
|
||||
mode->hdisplay << 16, mode->vdisplay << 16);
|
||||
if (ret) {
|
||||
dev_err(crtc->dev->dev, "%s: failed to set mode on plane: %d\n",
|
||||
mdp4_crtc->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (dma == DMA_E) {
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DMA_E_QUANT(0), 0x00ff0000);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DMA_E_QUANT(1), 0x00ff0000);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DMA_E_QUANT(2), 0x00ff0000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mdp4_crtc_prepare(struct drm_crtc *crtc)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
|
||||
DBG("%s", mdp4_crtc->name);
|
||||
/* make sure we hold a ref to mdp clks while setting up mode: */
|
||||
mdp4_enable(get_kms(crtc));
|
||||
mdp4_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
|
||||
}
|
||||
|
||||
static void mdp4_crtc_commit(struct drm_crtc *crtc)
|
||||
{
|
||||
mdp4_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
|
||||
crtc_flush(crtc);
|
||||
/* drop the ref to mdp clk's that we got in prepare: */
|
||||
mdp4_disable(get_kms(crtc));
|
||||
}
|
||||
|
||||
static int mdp4_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
|
||||
struct drm_framebuffer *old_fb)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
|
||||
struct drm_plane *plane = mdp4_crtc->plane;
|
||||
struct drm_display_mode *mode = &crtc->mode;
|
||||
|
||||
update_fb(crtc, false, crtc->fb);
|
||||
|
||||
return mdp4_plane_mode_set(plane, crtc, crtc->fb,
|
||||
0, 0, mode->hdisplay, mode->vdisplay,
|
||||
x << 16, y << 16,
|
||||
mode->hdisplay << 16, mode->vdisplay << 16);
|
||||
}
|
||||
|
||||
static void mdp4_crtc_load_lut(struct drm_crtc *crtc)
|
||||
{
|
||||
}
|
||||
|
||||
static int mdp4_crtc_page_flip(struct drm_crtc *crtc,
|
||||
struct drm_framebuffer *new_fb,
|
||||
struct drm_pending_vblank_event *event)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct drm_gem_object *obj;
|
||||
|
||||
if (mdp4_crtc->event) {
|
||||
dev_err(dev->dev, "already pending flip!\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
obj = msm_framebuffer_bo(new_fb, 0);
|
||||
|
||||
mdp4_crtc->event = event;
|
||||
update_fb(crtc, true, new_fb);
|
||||
|
||||
return msm_gem_queue_inactive_work(obj,
|
||||
&mdp4_crtc->pageflip_work);
|
||||
}
|
||||
|
||||
static int mdp4_crtc_set_property(struct drm_crtc *crtc,
|
||||
struct drm_property *property, uint64_t val)
|
||||
{
|
||||
// XXX
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#define CURSOR_WIDTH 64
|
||||
#define CURSOR_HEIGHT 64
|
||||
|
||||
/* called from IRQ to update cursor related registers (if needed). The
|
||||
* cursor registers, other than x/y position, appear not to be double
|
||||
* buffered, and changing them other than from vblank seems to trigger
|
||||
* underflow.
|
||||
*/
|
||||
static void update_cursor(struct drm_crtc *crtc)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
|
||||
enum mdp4_dma dma = mdp4_crtc->dma;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mdp4_crtc->cursor.lock, flags);
|
||||
if (mdp4_crtc->cursor.stale) {
|
||||
struct mdp4_kms *mdp4_kms = get_kms(crtc);
|
||||
struct drm_gem_object *next_bo = mdp4_crtc->cursor.next_bo;
|
||||
struct drm_gem_object *prev_bo = mdp4_crtc->cursor.scanout_bo;
|
||||
uint32_t iova = mdp4_crtc->cursor.next_iova;
|
||||
|
||||
if (next_bo) {
|
||||
/* take a obj ref + iova ref when we start scanning out: */
|
||||
drm_gem_object_reference(next_bo);
|
||||
msm_gem_get_iova_locked(next_bo, mdp4_kms->id, &iova);
|
||||
|
||||
/* enable cursor: */
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_SIZE(dma),
|
||||
MDP4_DMA_CURSOR_SIZE_WIDTH(mdp4_crtc->cursor.width) |
|
||||
MDP4_DMA_CURSOR_SIZE_HEIGHT(mdp4_crtc->cursor.height));
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_BASE(dma), iova);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_BLEND_CONFIG(dma),
|
||||
MDP4_DMA_CURSOR_BLEND_CONFIG_FORMAT(CURSOR_ARGB) |
|
||||
MDP4_DMA_CURSOR_BLEND_CONFIG_CURSOR_EN);
|
||||
} else {
|
||||
/* disable cursor: */
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_BASE(dma), 0);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_BLEND_CONFIG(dma),
|
||||
MDP4_DMA_CURSOR_BLEND_CONFIG_FORMAT(CURSOR_ARGB));
|
||||
}
|
||||
|
||||
/* and drop the iova ref + obj rev when done scanning out: */
|
||||
if (prev_bo)
|
||||
drm_flip_work_queue(&mdp4_crtc->unref_cursor_work, prev_bo);
|
||||
|
||||
mdp4_crtc->cursor.scanout_bo = next_bo;
|
||||
mdp4_crtc->cursor.stale = false;
|
||||
}
|
||||
spin_unlock_irqrestore(&mdp4_crtc->cursor.lock, flags);
|
||||
}
|
||||
|
||||
static int mdp4_crtc_cursor_set(struct drm_crtc *crtc,
|
||||
struct drm_file *file_priv, uint32_t handle,
|
||||
uint32_t width, uint32_t height)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
|
||||
struct mdp4_kms *mdp4_kms = get_kms(crtc);
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct drm_gem_object *cursor_bo, *old_bo;
|
||||
unsigned long flags;
|
||||
uint32_t iova;
|
||||
int ret;
|
||||
|
||||
if ((width > CURSOR_WIDTH) || (height > CURSOR_HEIGHT)) {
|
||||
dev_err(dev->dev, "bad cursor size: %dx%d\n", width, height);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (handle) {
|
||||
cursor_bo = drm_gem_object_lookup(dev, file_priv, handle);
|
||||
if (!cursor_bo)
|
||||
return -ENOENT;
|
||||
} else {
|
||||
cursor_bo = NULL;
|
||||
}
|
||||
|
||||
if (cursor_bo) {
|
||||
ret = msm_gem_get_iova(cursor_bo, mdp4_kms->id, &iova);
|
||||
if (ret)
|
||||
goto fail;
|
||||
} else {
|
||||
iova = 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&mdp4_crtc->cursor.lock, flags);
|
||||
old_bo = mdp4_crtc->cursor.next_bo;
|
||||
mdp4_crtc->cursor.next_bo = cursor_bo;
|
||||
mdp4_crtc->cursor.next_iova = iova;
|
||||
mdp4_crtc->cursor.width = width;
|
||||
mdp4_crtc->cursor.height = height;
|
||||
mdp4_crtc->cursor.stale = true;
|
||||
spin_unlock_irqrestore(&mdp4_crtc->cursor.lock, flags);
|
||||
|
||||
if (old_bo) {
|
||||
/* drop our previous reference: */
|
||||
msm_gem_put_iova(old_bo, mdp4_kms->id);
|
||||
drm_gem_object_unreference_unlocked(old_bo);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
drm_gem_object_unreference_unlocked(cursor_bo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mdp4_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
|
||||
struct mdp4_kms *mdp4_kms = get_kms(crtc);
|
||||
enum mdp4_dma dma = mdp4_crtc->dma;
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_POS(dma),
|
||||
MDP4_DMA_CURSOR_POS_X(x) |
|
||||
MDP4_DMA_CURSOR_POS_Y(y));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_crtc_funcs mdp4_crtc_funcs = {
|
||||
.set_config = drm_crtc_helper_set_config,
|
||||
.destroy = mdp4_crtc_destroy,
|
||||
.page_flip = mdp4_crtc_page_flip,
|
||||
.set_property = mdp4_crtc_set_property,
|
||||
.cursor_set = mdp4_crtc_cursor_set,
|
||||
.cursor_move = mdp4_crtc_cursor_move,
|
||||
};
|
||||
|
||||
static const struct drm_crtc_helper_funcs mdp4_crtc_helper_funcs = {
|
||||
.dpms = mdp4_crtc_dpms,
|
||||
.mode_fixup = mdp4_crtc_mode_fixup,
|
||||
.mode_set = mdp4_crtc_mode_set,
|
||||
.prepare = mdp4_crtc_prepare,
|
||||
.commit = mdp4_crtc_commit,
|
||||
.mode_set_base = mdp4_crtc_mode_set_base,
|
||||
.load_lut = mdp4_crtc_load_lut,
|
||||
};
|
||||
|
||||
static void mdp4_crtc_vblank_irq(struct mdp4_irq *irq, uint32_t irqstatus)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc = container_of(irq, struct mdp4_crtc, vblank);
|
||||
struct drm_crtc *crtc = &mdp4_crtc->base;
|
||||
struct msm_drm_private *priv = crtc->dev->dev_private;
|
||||
|
||||
update_cursor(crtc);
|
||||
complete_flip(crtc, false);
|
||||
mdp4_irq_unregister(get_kms(crtc), &mdp4_crtc->vblank);
|
||||
|
||||
drm_flip_work_commit(&mdp4_crtc->unref_fb_work, priv->wq);
|
||||
drm_flip_work_commit(&mdp4_crtc->unref_cursor_work, priv->wq);
|
||||
}
|
||||
|
||||
static void mdp4_crtc_err_irq(struct mdp4_irq *irq, uint32_t irqstatus)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc = container_of(irq, struct mdp4_crtc, err);
|
||||
struct drm_crtc *crtc = &mdp4_crtc->base;
|
||||
DBG("%s: error: %08x", mdp4_crtc->name, irqstatus);
|
||||
crtc_flush(crtc);
|
||||
}
|
||||
|
||||
uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
|
||||
return mdp4_crtc->vblank.irqmask;
|
||||
}
|
||||
|
||||
void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc)
|
||||
{
|
||||
complete_flip(crtc, true);
|
||||
}
|
||||
|
||||
/* set dma config, ie. the format the encoder wants. */
|
||||
void mdp4_crtc_set_config(struct drm_crtc *crtc, uint32_t config)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
|
||||
struct mdp4_kms *mdp4_kms = get_kms(crtc);
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DMA_CONFIG(mdp4_crtc->dma), config);
|
||||
}
|
||||
|
||||
/* set interface for routing crtc->encoder: */
|
||||
void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf)
|
||||
{
|
||||
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
|
||||
struct mdp4_kms *mdp4_kms = get_kms(crtc);
|
||||
uint32_t intf_sel;
|
||||
|
||||
intf_sel = mdp4_read(mdp4_kms, REG_MDP4_DISP_INTF_SEL);
|
||||
|
||||
switch (mdp4_crtc->dma) {
|
||||
case DMA_P:
|
||||
intf_sel &= ~MDP4_DISP_INTF_SEL_PRIM__MASK;
|
||||
intf_sel |= MDP4_DISP_INTF_SEL_PRIM(intf);
|
||||
break;
|
||||
case DMA_S:
|
||||
intf_sel &= ~MDP4_DISP_INTF_SEL_SEC__MASK;
|
||||
intf_sel |= MDP4_DISP_INTF_SEL_SEC(intf);
|
||||
break;
|
||||
case DMA_E:
|
||||
intf_sel &= ~MDP4_DISP_INTF_SEL_EXT__MASK;
|
||||
intf_sel |= MDP4_DISP_INTF_SEL_EXT(intf);
|
||||
break;
|
||||
}
|
||||
|
||||
if (intf == INTF_DSI_VIDEO) {
|
||||
intf_sel &= ~MDP4_DISP_INTF_SEL_DSI_CMD;
|
||||
intf_sel |= MDP4_DISP_INTF_SEL_DSI_VIDEO;
|
||||
mdp4_crtc->mixer = 0;
|
||||
} else if (intf == INTF_DSI_CMD) {
|
||||
intf_sel &= ~MDP4_DISP_INTF_SEL_DSI_VIDEO;
|
||||
intf_sel |= MDP4_DISP_INTF_SEL_DSI_CMD;
|
||||
mdp4_crtc->mixer = 0;
|
||||
} else if (intf == INTF_LCDC_DTV){
|
||||
mdp4_crtc->mixer = 1;
|
||||
}
|
||||
|
||||
blend_setup(crtc);
|
||||
|
||||
DBG("%s: intf_sel=%08x", mdp4_crtc->name, intf_sel);
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DISP_INTF_SEL, intf_sel);
|
||||
}
|
||||
|
||||
static const char *dma_names[] = {
|
||||
"DMA_P", "DMA_S", "DMA_E",
|
||||
};
|
||||
|
||||
/* initialize crtc */
|
||||
struct drm_crtc *mdp4_crtc_init(struct drm_device *dev,
|
||||
struct drm_plane *plane, int id, int ovlp_id,
|
||||
enum mdp4_dma dma_id)
|
||||
{
|
||||
struct drm_crtc *crtc = NULL;
|
||||
struct mdp4_crtc *mdp4_crtc;
|
||||
int ret;
|
||||
|
||||
mdp4_crtc = kzalloc(sizeof(*mdp4_crtc), GFP_KERNEL);
|
||||
if (!mdp4_crtc) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
crtc = &mdp4_crtc->base;
|
||||
|
||||
mdp4_crtc->plane = plane;
|
||||
mdp4_crtc->plane->crtc = crtc;
|
||||
|
||||
mdp4_crtc->ovlp = ovlp_id;
|
||||
mdp4_crtc->dma = dma_id;
|
||||
|
||||
mdp4_crtc->vblank.irqmask = dma2irq(mdp4_crtc->dma);
|
||||
mdp4_crtc->vblank.irq = mdp4_crtc_vblank_irq;
|
||||
|
||||
mdp4_crtc->err.irqmask = dma2err(mdp4_crtc->dma);
|
||||
mdp4_crtc->err.irq = mdp4_crtc_err_irq;
|
||||
|
||||
snprintf(mdp4_crtc->name, sizeof(mdp4_crtc->name), "%s:%d",
|
||||
dma_names[dma_id], ovlp_id);
|
||||
|
||||
spin_lock_init(&mdp4_crtc->cursor.lock);
|
||||
|
||||
ret = drm_flip_work_init(&mdp4_crtc->unref_fb_work, 16,
|
||||
"unref fb", unref_fb_worker);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
ret = drm_flip_work_init(&mdp4_crtc->unref_cursor_work, 64,
|
||||
"unref cursor", unref_cursor_worker);
|
||||
|
||||
INIT_WORK(&mdp4_crtc->pageflip_work, pageflip_worker);
|
||||
|
||||
drm_crtc_init(dev, crtc, &mdp4_crtc_funcs);
|
||||
drm_crtc_helper_add(crtc, &mdp4_crtc_helper_funcs);
|
||||
|
||||
mdp4_plane_install_properties(mdp4_crtc->plane, &crtc->base);
|
||||
|
||||
return crtc;
|
||||
|
||||
fail:
|
||||
if (crtc)
|
||||
mdp4_crtc_destroy(crtc);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
|
@ -0,0 +1,317 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <mach/clk.h>
|
||||
|
||||
#include "mdp4_kms.h"
|
||||
#include "msm_connector.h"
|
||||
|
||||
#include "drm_crtc.h"
|
||||
#include "drm_crtc_helper.h"
|
||||
|
||||
|
||||
struct mdp4_dtv_encoder {
|
||||
struct drm_encoder base;
|
||||
struct clk *src_clk;
|
||||
struct clk *hdmi_clk;
|
||||
struct clk *mdp_clk;
|
||||
unsigned long int pixclock;
|
||||
bool enabled;
|
||||
uint32_t bsc;
|
||||
};
|
||||
#define to_mdp4_dtv_encoder(x) container_of(x, struct mdp4_dtv_encoder, base)
|
||||
|
||||
static struct mdp4_kms *get_kms(struct drm_encoder *encoder)
|
||||
{
|
||||
struct msm_drm_private *priv = encoder->dev->dev_private;
|
||||
return to_mdp4_kms(priv->kms);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MSM_BUS_SCALING
|
||||
#include <mach/board.h>
|
||||
/* not ironically named at all.. no, really.. */
|
||||
static void bs_init(struct mdp4_dtv_encoder *mdp4_dtv_encoder)
|
||||
{
|
||||
struct drm_device *dev = mdp4_dtv_encoder->base.dev;
|
||||
struct lcdc_platform_data *dtv_pdata = mdp4_find_pdata("dtv.0");
|
||||
|
||||
if (!dtv_pdata) {
|
||||
dev_err(dev->dev, "could not find dtv pdata\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dtv_pdata->bus_scale_table) {
|
||||
mdp4_dtv_encoder->bsc = msm_bus_scale_register_client(
|
||||
dtv_pdata->bus_scale_table);
|
||||
DBG("bus scale client: %08x", mdp4_dtv_encoder->bsc);
|
||||
DBG("lcdc_power_save: %p", dtv_pdata->lcdc_power_save);
|
||||
if (dtv_pdata->lcdc_power_save)
|
||||
dtv_pdata->lcdc_power_save(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void bs_fini(struct mdp4_dtv_encoder *mdp4_dtv_encoder)
|
||||
{
|
||||
if (mdp4_dtv_encoder->bsc) {
|
||||
msm_bus_scale_unregister_client(mdp4_dtv_encoder->bsc);
|
||||
mdp4_dtv_encoder->bsc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void bs_set(struct mdp4_dtv_encoder *mdp4_dtv_encoder, int idx)
|
||||
{
|
||||
if (mdp4_dtv_encoder->bsc) {
|
||||
DBG("set bus scaling: %d", idx);
|
||||
msm_bus_scale_client_update_request(mdp4_dtv_encoder->bsc, idx);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void bs_init(struct mdp4_dtv_encoder *mdp4_dtv_encoder) {}
|
||||
static void bs_fini(struct mdp4_dtv_encoder *mdp4_dtv_encoder) {}
|
||||
static void bs_set(struct mdp4_dtv_encoder *mdp4_dtv_encoder, int idx) {}
|
||||
#endif
|
||||
|
||||
static void mdp4_dtv_encoder_destroy(struct drm_encoder *encoder)
|
||||
{
|
||||
struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder);
|
||||
bs_fini(mdp4_dtv_encoder);
|
||||
drm_encoder_cleanup(encoder);
|
||||
kfree(mdp4_dtv_encoder);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_funcs mdp4_dtv_encoder_funcs = {
|
||||
.destroy = mdp4_dtv_encoder_destroy,
|
||||
};
|
||||
|
||||
static void mdp4_dtv_encoder_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder);
|
||||
struct msm_connector *msm_connector = get_connector(encoder);
|
||||
struct mdp4_kms *mdp4_kms = get_kms(encoder);
|
||||
bool enabled = (mode == DRM_MODE_DPMS_ON);
|
||||
|
||||
DBG("mode=%d", mode);
|
||||
|
||||
if (enabled == mdp4_dtv_encoder->enabled)
|
||||
return;
|
||||
|
||||
if (enabled) {
|
||||
unsigned long pc = mdp4_dtv_encoder->pixclock;
|
||||
int ret;
|
||||
|
||||
bs_set(mdp4_dtv_encoder, 1);
|
||||
|
||||
if (msm_connector)
|
||||
msm_connector->funcs->dpms(msm_connector, mode);
|
||||
|
||||
DBG("setting src_clk=%lu", pc);
|
||||
|
||||
ret = clk_set_rate(mdp4_dtv_encoder->src_clk, pc);
|
||||
if (ret)
|
||||
dev_err(dev->dev, "failed to set src_clk to %lu: %d\n", pc, ret);
|
||||
clk_prepare_enable(mdp4_dtv_encoder->src_clk);
|
||||
ret = clk_prepare_enable(mdp4_dtv_encoder->hdmi_clk);
|
||||
if (ret)
|
||||
dev_err(dev->dev, "failed to enable hdmi_clk: %d\n", ret);
|
||||
ret = clk_prepare_enable(mdp4_dtv_encoder->mdp_clk);
|
||||
if (ret)
|
||||
dev_err(dev->dev, "failed to enabled mdp_clk: %d\n", ret);
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DTV_ENABLE, 1);
|
||||
} else {
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DTV_ENABLE, 0);
|
||||
|
||||
/*
|
||||
* Wait for a vsync so we know the ENABLE=0 latched before
|
||||
* the (connector) source of the vsync's gets disabled,
|
||||
* otherwise we end up in a funny state if we re-enable
|
||||
* before the disable latches, which results that some of
|
||||
* the settings changes for the new modeset (like new
|
||||
* scanout buffer) don't latch properly..
|
||||
*/
|
||||
mdp4_irq_wait(mdp4_kms, MDP4_IRQ_EXTERNAL_VSYNC);
|
||||
|
||||
clk_disable_unprepare(mdp4_dtv_encoder->src_clk);
|
||||
clk_disable_unprepare(mdp4_dtv_encoder->hdmi_clk);
|
||||
clk_disable_unprepare(mdp4_dtv_encoder->mdp_clk);
|
||||
|
||||
if (msm_connector)
|
||||
msm_connector->funcs->dpms(msm_connector, mode);
|
||||
|
||||
bs_set(mdp4_dtv_encoder, 0);
|
||||
}
|
||||
|
||||
mdp4_dtv_encoder->enabled = enabled;
|
||||
}
|
||||
|
||||
static bool mdp4_dtv_encoder_mode_fixup(struct drm_encoder *encoder,
|
||||
const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void mdp4_dtv_encoder_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder);
|
||||
struct msm_connector *msm_connector = get_connector(encoder);
|
||||
struct mdp4_kms *mdp4_kms = get_kms(encoder);
|
||||
uint32_t dtv_hsync_skew, vsync_period, vsync_len, ctrl_pol;
|
||||
uint32_t display_v_start, display_v_end;
|
||||
uint32_t hsync_start_x, hsync_end_x;
|
||||
|
||||
mode = adjusted_mode;
|
||||
|
||||
DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
|
||||
mode->base.id, mode->name,
|
||||
mode->vrefresh, mode->clock,
|
||||
mode->hdisplay, mode->hsync_start,
|
||||
mode->hsync_end, mode->htotal,
|
||||
mode->vdisplay, mode->vsync_start,
|
||||
mode->vsync_end, mode->vtotal,
|
||||
mode->type, mode->flags);
|
||||
|
||||
mdp4_dtv_encoder->pixclock = mode->clock * 1000;
|
||||
|
||||
DBG("pixclock=%lu", mdp4_dtv_encoder->pixclock);
|
||||
|
||||
ctrl_pol = 0;
|
||||
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
|
||||
ctrl_pol |= MDP4_DTV_CTRL_POLARITY_HSYNC_LOW;
|
||||
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
|
||||
ctrl_pol |= MDP4_DTV_CTRL_POLARITY_VSYNC_LOW;
|
||||
/* probably need to get DATA_EN polarity from panel.. */
|
||||
|
||||
dtv_hsync_skew = 0; /* get this from panel? */
|
||||
|
||||
hsync_start_x = (mode->htotal - mode->hsync_start);
|
||||
hsync_end_x = mode->htotal - (mode->hsync_start - mode->hdisplay) - 1;
|
||||
|
||||
vsync_period = mode->vtotal * mode->htotal;
|
||||
vsync_len = (mode->vsync_end - mode->vsync_start) * mode->htotal;
|
||||
display_v_start = (mode->vtotal - mode->vsync_start) * mode->htotal + dtv_hsync_skew;
|
||||
display_v_end = vsync_period - ((mode->vsync_start - mode->vdisplay) * mode->htotal) + dtv_hsync_skew - 1;
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DTV_HSYNC_CTRL,
|
||||
MDP4_DTV_HSYNC_CTRL_PULSEW(mode->hsync_end - mode->hsync_start) |
|
||||
MDP4_DTV_HSYNC_CTRL_PERIOD(mode->htotal));
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DTV_VSYNC_PERIOD, vsync_period);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DTV_VSYNC_LEN, vsync_len);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DTV_DISPLAY_HCTRL,
|
||||
MDP4_DTV_DISPLAY_HCTRL_START(hsync_start_x) |
|
||||
MDP4_DTV_DISPLAY_HCTRL_END(hsync_end_x));
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DTV_DISPLAY_VSTART, display_v_start);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DTV_DISPLAY_VEND, display_v_end);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DTV_BORDER_CLR, 0);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DTV_UNDERFLOW_CLR,
|
||||
MDP4_DTV_UNDERFLOW_CLR_ENABLE_RECOVERY |
|
||||
MDP4_DTV_UNDERFLOW_CLR_COLOR(0xff));
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DTV_HSYNC_SKEW, dtv_hsync_skew);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DTV_CTRL_POLARITY, ctrl_pol);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DTV_ACTIVE_HCTL,
|
||||
MDP4_DTV_ACTIVE_HCTL_START(0) |
|
||||
MDP4_DTV_ACTIVE_HCTL_END(0));
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DTV_ACTIVE_VSTART, 0);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DTV_ACTIVE_VEND, 0);
|
||||
|
||||
if (msm_connector)
|
||||
msm_connector->funcs->mode_set(msm_connector, mode);
|
||||
}
|
||||
|
||||
static void mdp4_dtv_encoder_prepare(struct drm_encoder *encoder)
|
||||
{
|
||||
mdp4_dtv_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
|
||||
}
|
||||
|
||||
static void mdp4_dtv_encoder_commit(struct drm_encoder *encoder)
|
||||
{
|
||||
mdp4_crtc_set_config(encoder->crtc,
|
||||
MDP4_DMA_CONFIG_R_BPC(BPC8) |
|
||||
MDP4_DMA_CONFIG_G_BPC(BPC8) |
|
||||
MDP4_DMA_CONFIG_B_BPC(BPC8) |
|
||||
MDP4_DMA_CONFIG_PACK(0x21));
|
||||
mdp4_crtc_set_intf(encoder->crtc, INTF_LCDC_DTV);
|
||||
mdp4_dtv_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs mdp4_dtv_encoder_helper_funcs = {
|
||||
.dpms = mdp4_dtv_encoder_dpms,
|
||||
.mode_fixup = mdp4_dtv_encoder_mode_fixup,
|
||||
.mode_set = mdp4_dtv_encoder_mode_set,
|
||||
.prepare = mdp4_dtv_encoder_prepare,
|
||||
.commit = mdp4_dtv_encoder_commit,
|
||||
};
|
||||
|
||||
long mdp4_dtv_round_pixclk(struct drm_encoder *encoder, unsigned long rate)
|
||||
{
|
||||
struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder);
|
||||
return clk_round_rate(mdp4_dtv_encoder->src_clk, rate);
|
||||
}
|
||||
|
||||
/* initialize encoder */
|
||||
struct drm_encoder *mdp4_dtv_encoder_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_encoder *encoder = NULL;
|
||||
struct mdp4_dtv_encoder *mdp4_dtv_encoder;
|
||||
int ret;
|
||||
|
||||
mdp4_dtv_encoder = kzalloc(sizeof(*mdp4_dtv_encoder), GFP_KERNEL);
|
||||
if (!mdp4_dtv_encoder) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
encoder = &mdp4_dtv_encoder->base;
|
||||
|
||||
drm_encoder_init(dev, encoder, &mdp4_dtv_encoder_funcs,
|
||||
DRM_MODE_ENCODER_TMDS);
|
||||
drm_encoder_helper_add(encoder, &mdp4_dtv_encoder_helper_funcs);
|
||||
|
||||
mdp4_dtv_encoder->src_clk = devm_clk_get(dev->dev, "src_clk");
|
||||
if (IS_ERR(mdp4_dtv_encoder->src_clk)) {
|
||||
dev_err(dev->dev, "failed to get src_clk\n");
|
||||
ret = PTR_ERR(mdp4_dtv_encoder->src_clk);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mdp4_dtv_encoder->hdmi_clk = devm_clk_get(dev->dev, "hdmi_clk");
|
||||
if (IS_ERR(mdp4_dtv_encoder->hdmi_clk)) {
|
||||
dev_err(dev->dev, "failed to get hdmi_clk\n");
|
||||
ret = PTR_ERR(mdp4_dtv_encoder->hdmi_clk);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mdp4_dtv_encoder->mdp_clk = devm_clk_get(dev->dev, "mdp_clk");
|
||||
if (IS_ERR(mdp4_dtv_encoder->mdp_clk)) {
|
||||
dev_err(dev->dev, "failed to get mdp_clk\n");
|
||||
ret = PTR_ERR(mdp4_dtv_encoder->mdp_clk);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs_init(mdp4_dtv_encoder);
|
||||
|
||||
return encoder;
|
||||
|
||||
fail:
|
||||
if (encoder)
|
||||
mdp4_dtv_encoder_destroy(encoder);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "mdp4_kms.h"
|
||||
|
||||
#define FMT(name, a, r, g, b, e0, e1, e2, e3, alpha, tight, c, cnt) { \
|
||||
.base = { .pixel_format = DRM_FORMAT_ ## name }, \
|
||||
.bpc_a = BPC ## a ## A, \
|
||||
.bpc_r = BPC ## r, \
|
||||
.bpc_g = BPC ## g, \
|
||||
.bpc_b = BPC ## b, \
|
||||
.unpack = { e0, e1, e2, e3 }, \
|
||||
.alpha_enable = alpha, \
|
||||
.unpack_tight = tight, \
|
||||
.cpp = c, \
|
||||
.unpack_count = cnt, \
|
||||
}
|
||||
|
||||
#define BPC0A 0
|
||||
|
||||
static const struct mdp4_format formats[] = {
|
||||
/* name a r g b e0 e1 e2 e3 alpha tight cpp cnt */
|
||||
FMT(ARGB8888, 8, 8, 8, 8, 1, 0, 2, 3, true, true, 4, 4),
|
||||
FMT(XRGB8888, 8, 8, 8, 8, 1, 0, 2, 3, false, true, 4, 4),
|
||||
FMT(RGB888, 0, 8, 8, 8, 1, 0, 2, 0, false, true, 3, 3),
|
||||
FMT(BGR888, 0, 8, 8, 8, 2, 0, 1, 0, false, true, 3, 3),
|
||||
FMT(RGB565, 0, 5, 6, 5, 1, 0, 2, 0, false, true, 2, 3),
|
||||
FMT(BGR565, 0, 5, 6, 5, 2, 0, 1, 0, false, true, 2, 3),
|
||||
};
|
||||
|
||||
const struct msm_format *mdp4_get_format(struct msm_kms *kms, uint32_t format)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(formats); i++) {
|
||||
const struct mdp4_format *f = &formats[i];
|
||||
if (f->base.pixel_format == format)
|
||||
return &f->base;
|
||||
}
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "mdp4_kms.h"
|
||||
|
||||
|
||||
struct mdp4_irq_wait {
|
||||
struct mdp4_irq irq;
|
||||
int count;
|
||||
};
|
||||
|
||||
static DECLARE_WAIT_QUEUE_HEAD(wait_event);
|
||||
|
||||
static DEFINE_SPINLOCK(list_lock);
|
||||
|
||||
static void update_irq(struct mdp4_kms *mdp4_kms)
|
||||
{
|
||||
struct mdp4_irq *irq;
|
||||
uint32_t irqmask = mdp4_kms->vblank_mask;
|
||||
|
||||
BUG_ON(!spin_is_locked(&list_lock));
|
||||
|
||||
list_for_each_entry(irq, &mdp4_kms->irq_list, node)
|
||||
irqmask |= irq->irqmask;
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_INTR_ENABLE, irqmask);
|
||||
}
|
||||
|
||||
static void update_irq_unlocked(struct mdp4_kms *mdp4_kms)
|
||||
{
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&list_lock, flags);
|
||||
update_irq(mdp4_kms);
|
||||
spin_unlock_irqrestore(&list_lock, flags);
|
||||
}
|
||||
|
||||
static void mdp4_irq_error_handler(struct mdp4_irq *irq, uint32_t irqstatus)
|
||||
{
|
||||
DRM_ERROR("errors: %08x\n", irqstatus);
|
||||
}
|
||||
|
||||
void mdp4_irq_preinstall(struct msm_kms *kms)
|
||||
{
|
||||
struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_INTR_CLEAR, 0xffffffff);
|
||||
}
|
||||
|
||||
int mdp4_irq_postinstall(struct msm_kms *kms)
|
||||
{
|
||||
struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms);
|
||||
struct mdp4_irq *error_handler = &mdp4_kms->error_handler;
|
||||
|
||||
INIT_LIST_HEAD(&mdp4_kms->irq_list);
|
||||
|
||||
error_handler->irq = mdp4_irq_error_handler;
|
||||
error_handler->irqmask = MDP4_IRQ_PRIMARY_INTF_UDERRUN |
|
||||
MDP4_IRQ_EXTERNAL_INTF_UDERRUN;
|
||||
|
||||
mdp4_irq_register(mdp4_kms, error_handler);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mdp4_irq_uninstall(struct msm_kms *kms)
|
||||
{
|
||||
struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_INTR_ENABLE, 0x00000000);
|
||||
}
|
||||
|
||||
irqreturn_t mdp4_irq(struct msm_kms *kms)
|
||||
{
|
||||
struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms);
|
||||
struct drm_device *dev = mdp4_kms->dev;
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct mdp4_irq *handler, *n;
|
||||
unsigned long flags;
|
||||
unsigned int id;
|
||||
uint32_t status;
|
||||
|
||||
status = mdp4_read(mdp4_kms, REG_MDP4_INTR_STATUS);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_INTR_CLEAR, status);
|
||||
|
||||
VERB("status=%08x", status);
|
||||
|
||||
for (id = 0; id < priv->num_crtcs; id++)
|
||||
if (status & mdp4_crtc_vblank(priv->crtcs[id]))
|
||||
drm_handle_vblank(dev, id);
|
||||
|
||||
spin_lock_irqsave(&list_lock, flags);
|
||||
mdp4_kms->in_irq = true;
|
||||
list_for_each_entry_safe(handler, n, &mdp4_kms->irq_list, node) {
|
||||
if (handler->irqmask & status) {
|
||||
spin_unlock_irqrestore(&list_lock, flags);
|
||||
handler->irq(handler, handler->irqmask & status);
|
||||
spin_lock_irqsave(&list_lock, flags);
|
||||
}
|
||||
}
|
||||
mdp4_kms->in_irq = false;
|
||||
update_irq(mdp4_kms);
|
||||
spin_unlock_irqrestore(&list_lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int mdp4_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc)
|
||||
{
|
||||
struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&list_lock, flags);
|
||||
mdp4_kms->vblank_mask |= mdp4_crtc_vblank(crtc);
|
||||
update_irq(mdp4_kms);
|
||||
spin_unlock_irqrestore(&list_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mdp4_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc)
|
||||
{
|
||||
struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&list_lock, flags);
|
||||
mdp4_kms->vblank_mask &= ~mdp4_crtc_vblank(crtc);
|
||||
update_irq(mdp4_kms);
|
||||
spin_unlock_irqrestore(&list_lock, flags);
|
||||
}
|
||||
|
||||
static void wait_irq(struct mdp4_irq *irq, uint32_t irqstatus)
|
||||
{
|
||||
struct mdp4_irq_wait *wait =
|
||||
container_of(irq, struct mdp4_irq_wait, irq);
|
||||
wait->count--;
|
||||
wake_up_all(&wait_event);
|
||||
}
|
||||
|
||||
void mdp4_irq_wait(struct mdp4_kms *mdp4_kms, uint32_t irqmask)
|
||||
{
|
||||
struct mdp4_irq_wait wait = {
|
||||
.irq = {
|
||||
.irq = wait_irq,
|
||||
.irqmask = irqmask,
|
||||
},
|
||||
.count = 1,
|
||||
};
|
||||
mdp4_irq_register(mdp4_kms, &wait.irq);
|
||||
wait_event(wait_event, (wait.count <= 0));
|
||||
mdp4_irq_unregister(mdp4_kms, &wait.irq);
|
||||
}
|
||||
|
||||
void mdp4_irq_register(struct mdp4_kms *mdp4_kms, struct mdp4_irq *irq)
|
||||
{
|
||||
unsigned long flags;
|
||||
bool needs_update = false;
|
||||
|
||||
spin_lock_irqsave(&list_lock, flags);
|
||||
|
||||
if (!irq->registered) {
|
||||
irq->registered = true;
|
||||
list_add(&irq->node, &mdp4_kms->irq_list);
|
||||
needs_update = !mdp4_kms->in_irq;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&list_lock, flags);
|
||||
|
||||
if (needs_update)
|
||||
update_irq_unlocked(mdp4_kms);
|
||||
}
|
||||
|
||||
void mdp4_irq_unregister(struct mdp4_kms *mdp4_kms, struct mdp4_irq *irq)
|
||||
{
|
||||
unsigned long flags;
|
||||
bool needs_update = false;
|
||||
|
||||
spin_lock_irqsave(&list_lock, flags);
|
||||
|
||||
if (irq->registered) {
|
||||
irq->registered = false;
|
||||
list_del(&irq->node);
|
||||
needs_update = !mdp4_kms->in_irq;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&list_lock, flags);
|
||||
|
||||
if (needs_update)
|
||||
update_irq_unlocked(mdp4_kms);
|
||||
}
|
|
@ -0,0 +1,368 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "mdp4_kms.h"
|
||||
|
||||
#include <mach/iommu.h>
|
||||
|
||||
static struct mdp4_platform_config *mdp4_get_config(struct platform_device *dev);
|
||||
|
||||
static int mdp4_hw_init(struct msm_kms *kms)
|
||||
{
|
||||
struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms);
|
||||
struct drm_device *dev = mdp4_kms->dev;
|
||||
uint32_t version, major, minor, dmap_cfg, vg_cfg;
|
||||
unsigned long clk;
|
||||
int ret = 0;
|
||||
|
||||
pm_runtime_get_sync(dev->dev);
|
||||
|
||||
version = mdp4_read(mdp4_kms, REG_MDP4_VERSION);
|
||||
|
||||
major = FIELD(version, MDP4_VERSION_MAJOR);
|
||||
minor = FIELD(version, MDP4_VERSION_MINOR);
|
||||
|
||||
DBG("found MDP version v%d.%d", major, minor);
|
||||
|
||||
if (major != 4) {
|
||||
dev_err(dev->dev, "unexpected MDP version: v%d.%d\n",
|
||||
major, minor);
|
||||
ret = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mdp4_kms->rev = minor;
|
||||
|
||||
if (mdp4_kms->dsi_pll_vdda) {
|
||||
if ((mdp4_kms->rev == 2) || (mdp4_kms->rev == 4)) {
|
||||
ret = regulator_set_voltage(mdp4_kms->dsi_pll_vdda,
|
||||
1200000, 1200000);
|
||||
if (ret) {
|
||||
dev_err(dev->dev,
|
||||
"failed to set dsi_pll_vdda voltage: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mdp4_kms->dsi_pll_vddio) {
|
||||
if (mdp4_kms->rev == 2) {
|
||||
ret = regulator_set_voltage(mdp4_kms->dsi_pll_vddio,
|
||||
1800000, 1800000);
|
||||
if (ret) {
|
||||
dev_err(dev->dev,
|
||||
"failed to set dsi_pll_vddio voltage: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mdp4_kms->rev > 1) {
|
||||
mdp4_write(mdp4_kms, REG_MDP4_CS_CONTROLLER0, 0x0707ffff);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_CS_CONTROLLER1, 0x03073f3f);
|
||||
}
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_PORTMAP_MODE, 0x3);
|
||||
|
||||
/* max read pending cmd config, 3 pending requests: */
|
||||
mdp4_write(mdp4_kms, REG_MDP4_READ_CNFG, 0x02222);
|
||||
|
||||
clk = clk_get_rate(mdp4_kms->clk);
|
||||
|
||||
if ((mdp4_kms->rev >= 1) || (clk >= 90000000)) {
|
||||
dmap_cfg = 0x47; /* 16 bytes-burst x 8 req */
|
||||
vg_cfg = 0x47; /* 16 bytes-burs x 8 req */
|
||||
} else {
|
||||
dmap_cfg = 0x27; /* 8 bytes-burst x 8 req */
|
||||
vg_cfg = 0x43; /* 16 bytes-burst x 4 req */
|
||||
}
|
||||
|
||||
DBG("fetch config: dmap=%02x, vg=%02x", dmap_cfg, vg_cfg);
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DMA_FETCH_CONFIG(DMA_P), dmap_cfg);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DMA_FETCH_CONFIG(DMA_E), dmap_cfg);
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(VG1), vg_cfg);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(VG2), vg_cfg);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(RGB1), vg_cfg);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(RGB2), vg_cfg);
|
||||
|
||||
if (mdp4_kms->rev >= 2)
|
||||
mdp4_write(mdp4_kms, REG_MDP4_LAYERMIXER_IN_CFG_UPDATE_METHOD, 1);
|
||||
|
||||
/* disable CSC matrix / YUV by default: */
|
||||
mdp4_write(mdp4_kms, REG_MDP4_PIPE_OP_MODE(VG1), 0);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_PIPE_OP_MODE(VG2), 0);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DMA_P_OP_MODE, 0);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DMA_S_OP_MODE, 0);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_OVLP_CSC_CONFIG(1), 0);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_OVLP_CSC_CONFIG(2), 0);
|
||||
|
||||
if (mdp4_kms->rev > 1)
|
||||
mdp4_write(mdp4_kms, REG_MDP4_RESET_STATUS, 1);
|
||||
|
||||
out:
|
||||
pm_runtime_put_sync(dev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long mdp4_round_pixclk(struct msm_kms *kms, unsigned long rate,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
/* if we had >1 encoder, we'd need something more clever: */
|
||||
return mdp4_dtv_round_pixclk(encoder, rate);
|
||||
}
|
||||
|
||||
static void mdp4_preclose(struct msm_kms *kms, struct drm_file *file)
|
||||
{
|
||||
struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms);
|
||||
struct msm_drm_private *priv = mdp4_kms->dev->dev_private;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < priv->num_crtcs; i++)
|
||||
mdp4_crtc_cancel_pending_flip(priv->crtcs[i]);
|
||||
}
|
||||
|
||||
static void mdp4_destroy(struct msm_kms *kms)
|
||||
{
|
||||
struct mdp4_kms *mdp4_kms = to_mdp4_kms(kms);
|
||||
kfree(mdp4_kms);
|
||||
}
|
||||
|
||||
static const struct msm_kms_funcs kms_funcs = {
|
||||
.hw_init = mdp4_hw_init,
|
||||
.irq_preinstall = mdp4_irq_preinstall,
|
||||
.irq_postinstall = mdp4_irq_postinstall,
|
||||
.irq_uninstall = mdp4_irq_uninstall,
|
||||
.irq = mdp4_irq,
|
||||
.enable_vblank = mdp4_enable_vblank,
|
||||
.disable_vblank = mdp4_disable_vblank,
|
||||
.get_format = mdp4_get_format,
|
||||
.round_pixclk = mdp4_round_pixclk,
|
||||
.preclose = mdp4_preclose,
|
||||
.destroy = mdp4_destroy,
|
||||
};
|
||||
|
||||
int mdp4_disable(struct mdp4_kms *mdp4_kms)
|
||||
{
|
||||
DBG("");
|
||||
|
||||
clk_disable_unprepare(mdp4_kms->clk);
|
||||
if (mdp4_kms->pclk)
|
||||
clk_disable_unprepare(mdp4_kms->pclk);
|
||||
clk_disable_unprepare(mdp4_kms->lut_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdp4_enable(struct mdp4_kms *mdp4_kms)
|
||||
{
|
||||
DBG("");
|
||||
|
||||
clk_prepare_enable(mdp4_kms->clk);
|
||||
if (mdp4_kms->pclk)
|
||||
clk_prepare_enable(mdp4_kms->pclk);
|
||||
clk_prepare_enable(mdp4_kms->lut_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int modeset_init(struct mdp4_kms *mdp4_kms)
|
||||
{
|
||||
struct drm_device *dev = mdp4_kms->dev;
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct drm_plane *plane;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_connector *connector;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* NOTE: this is a bit simplistic until we add support
|
||||
* for more than just RGB1->DMA_E->DTV->HDMI
|
||||
*/
|
||||
|
||||
/* the CRTCs get constructed with a private plane: */
|
||||
plane = mdp4_plane_init(dev, RGB1, true);
|
||||
if (IS_ERR(plane)) {
|
||||
dev_err(dev->dev, "failed to construct plane for RGB1\n");
|
||||
ret = PTR_ERR(plane);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
crtc = mdp4_crtc_init(dev, plane, priv->num_crtcs, 1, DMA_E);
|
||||
if (IS_ERR(crtc)) {
|
||||
dev_err(dev->dev, "failed to construct crtc for DMA_E\n");
|
||||
ret = PTR_ERR(crtc);
|
||||
goto fail;
|
||||
}
|
||||
priv->crtcs[priv->num_crtcs++] = crtc;
|
||||
|
||||
encoder = mdp4_dtv_encoder_init(dev);
|
||||
if (IS_ERR(encoder)) {
|
||||
dev_err(dev->dev, "failed to construct DTV encoder\n");
|
||||
ret = PTR_ERR(encoder);
|
||||
goto fail;
|
||||
}
|
||||
encoder->possible_crtcs = 0x1; /* DTV can be hooked to DMA_E */
|
||||
priv->encoders[priv->num_encoders++] = encoder;
|
||||
|
||||
connector = hdmi_connector_init(dev, encoder);
|
||||
if (IS_ERR(connector)) {
|
||||
dev_err(dev->dev, "failed to construct HDMI connector\n");
|
||||
ret = PTR_ERR(connector);
|
||||
goto fail;
|
||||
}
|
||||
priv->connectors[priv->num_connectors++] = connector;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char *iommu_ports[] = {
|
||||
"mdp_port0_cb0", "mdp_port1_cb0",
|
||||
};
|
||||
|
||||
struct msm_kms *mdp4_kms_init(struct drm_device *dev)
|
||||
{
|
||||
struct platform_device *pdev = dev->platformdev;
|
||||
struct mdp4_platform_config *config = mdp4_get_config(pdev);
|
||||
struct mdp4_kms *mdp4_kms;
|
||||
struct msm_kms *kms = NULL;
|
||||
int ret;
|
||||
|
||||
mdp4_kms = kzalloc(sizeof(*mdp4_kms), GFP_KERNEL);
|
||||
if (!mdp4_kms) {
|
||||
dev_err(dev->dev, "failed to allocate kms\n");
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
kms = &mdp4_kms->base;
|
||||
kms->funcs = &kms_funcs;
|
||||
|
||||
mdp4_kms->dev = dev;
|
||||
|
||||
mdp4_kms->mmio = msm_ioremap(pdev, NULL, "MDP4");
|
||||
if (IS_ERR(mdp4_kms->mmio)) {
|
||||
ret = PTR_ERR(mdp4_kms->mmio);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mdp4_kms->dsi_pll_vdda = devm_regulator_get(&pdev->dev, "dsi_pll_vdda");
|
||||
if (IS_ERR(mdp4_kms->dsi_pll_vdda))
|
||||
mdp4_kms->dsi_pll_vdda = NULL;
|
||||
|
||||
mdp4_kms->dsi_pll_vddio = devm_regulator_get(&pdev->dev, "dsi_pll_vddio");
|
||||
if (IS_ERR(mdp4_kms->dsi_pll_vddio))
|
||||
mdp4_kms->dsi_pll_vddio = NULL;
|
||||
|
||||
mdp4_kms->vdd = devm_regulator_get(&pdev->dev, "vdd");
|
||||
if (IS_ERR(mdp4_kms->vdd))
|
||||
mdp4_kms->vdd = NULL;
|
||||
|
||||
if (mdp4_kms->vdd) {
|
||||
ret = regulator_enable(mdp4_kms->vdd);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to enable regulator vdd: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
mdp4_kms->clk = devm_clk_get(&pdev->dev, "core_clk");
|
||||
if (IS_ERR(mdp4_kms->clk)) {
|
||||
dev_err(dev->dev, "failed to get core_clk\n");
|
||||
ret = PTR_ERR(mdp4_kms->clk);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mdp4_kms->pclk = devm_clk_get(&pdev->dev, "iface_clk");
|
||||
if (IS_ERR(mdp4_kms->pclk))
|
||||
mdp4_kms->pclk = NULL;
|
||||
|
||||
// XXX if (rev >= MDP_REV_42) { ???
|
||||
mdp4_kms->lut_clk = devm_clk_get(&pdev->dev, "lut_clk");
|
||||
if (IS_ERR(mdp4_kms->lut_clk)) {
|
||||
dev_err(dev->dev, "failed to get lut_clk\n");
|
||||
ret = PTR_ERR(mdp4_kms->lut_clk);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
clk_set_rate(mdp4_kms->clk, config->max_clk);
|
||||
clk_set_rate(mdp4_kms->lut_clk, config->max_clk);
|
||||
|
||||
if (!config->iommu) {
|
||||
dev_err(dev->dev, "no iommu\n");
|
||||
ret = -ENXIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* make sure things are off before attaching iommu (bootloader could
|
||||
* have left things on, in which case we'll start getting faults if
|
||||
* we don't disable):
|
||||
*/
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DTV_ENABLE, 0);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 0);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_DSI_ENABLE, 0);
|
||||
mdelay(16);
|
||||
|
||||
ret = msm_iommu_attach(dev, config->iommu,
|
||||
iommu_ports, ARRAY_SIZE(iommu_ports));
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
mdp4_kms->id = msm_register_iommu(dev, config->iommu);
|
||||
if (mdp4_kms->id < 0) {
|
||||
ret = mdp4_kms->id;
|
||||
dev_err(dev->dev, "failed to register mdp4 iommu: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = modeset_init(mdp4_kms);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "modeset_init failed: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return kms;
|
||||
|
||||
fail:
|
||||
if (kms)
|
||||
mdp4_destroy(kms);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static struct mdp4_platform_config *mdp4_get_config(struct platform_device *dev)
|
||||
{
|
||||
static struct mdp4_platform_config config = {};
|
||||
#ifdef CONFIG_OF
|
||||
/* TODO */
|
||||
#else
|
||||
if (cpu_is_apq8064())
|
||||
config.max_clk = 266667000;
|
||||
else
|
||||
config.max_clk = 200000000;
|
||||
|
||||
config.iommu = msm_get_iommu_domain(DISPLAY_READ_DOMAIN);
|
||||
#endif
|
||||
return &config;
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MDP4_KMS_H__
|
||||
#define __MDP4_KMS_H__
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "mdp4.xml.h"
|
||||
|
||||
|
||||
/* For transiently registering for different MDP4 irqs that various parts
|
||||
* of the KMS code need during setup/configuration. We these are not
|
||||
* necessarily the same as what drm_vblank_get/put() are requesting, and
|
||||
* the hysteresis in drm_vblank_put() is not necessarily desirable for
|
||||
* internal housekeeping related irq usage.
|
||||
*/
|
||||
struct mdp4_irq {
|
||||
struct list_head node;
|
||||
uint32_t irqmask;
|
||||
bool registered;
|
||||
void (*irq)(struct mdp4_irq *irq, uint32_t irqstatus);
|
||||
};
|
||||
|
||||
struct mdp4_kms {
|
||||
struct msm_kms base;
|
||||
|
||||
struct drm_device *dev;
|
||||
|
||||
int rev;
|
||||
|
||||
/* mapper-id used to request GEM buffer mapped for scanout: */
|
||||
int id;
|
||||
|
||||
void __iomem *mmio;
|
||||
|
||||
struct regulator *dsi_pll_vdda;
|
||||
struct regulator *dsi_pll_vddio;
|
||||
struct regulator *vdd;
|
||||
|
||||
struct clk *clk;
|
||||
struct clk *pclk;
|
||||
struct clk *lut_clk;
|
||||
|
||||
/* irq handling: */
|
||||
bool in_irq;
|
||||
struct list_head irq_list; /* list of mdp4_irq */
|
||||
uint32_t vblank_mask; /* irq bits set for userspace vblank */
|
||||
struct mdp4_irq error_handler;
|
||||
};
|
||||
#define to_mdp4_kms(x) container_of(x, struct mdp4_kms, base)
|
||||
|
||||
/* platform config data (ie. from DT, or pdata) */
|
||||
struct mdp4_platform_config {
|
||||
struct iommu_domain *iommu;
|
||||
uint32_t max_clk;
|
||||
};
|
||||
|
||||
struct mdp4_format {
|
||||
struct msm_format base;
|
||||
enum mpd4_bpc bpc_r, bpc_g, bpc_b;
|
||||
enum mpd4_bpc_alpha bpc_a;
|
||||
uint8_t unpack[4];
|
||||
bool alpha_enable, unpack_tight;
|
||||
uint8_t cpp, unpack_count;
|
||||
};
|
||||
#define to_mdp4_format(x) container_of(x, struct mdp4_format, base)
|
||||
|
||||
static inline void mdp4_write(struct mdp4_kms *mdp4_kms, u32 reg, u32 data)
|
||||
{
|
||||
msm_writel(data, mdp4_kms->mmio + reg);
|
||||
}
|
||||
|
||||
static inline u32 mdp4_read(struct mdp4_kms *mdp4_kms, u32 reg)
|
||||
{
|
||||
return msm_readl(mdp4_kms->mmio + reg);
|
||||
}
|
||||
|
||||
static inline uint32_t pipe2flush(enum mpd4_pipe pipe)
|
||||
{
|
||||
switch (pipe) {
|
||||
case VG1: return MDP4_OVERLAY_FLUSH_VG1;
|
||||
case VG2: return MDP4_OVERLAY_FLUSH_VG2;
|
||||
case RGB1: return MDP4_OVERLAY_FLUSH_RGB1;
|
||||
case RGB2: return MDP4_OVERLAY_FLUSH_RGB1;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t ovlp2flush(int ovlp)
|
||||
{
|
||||
switch (ovlp) {
|
||||
case 0: return MDP4_OVERLAY_FLUSH_OVLP0;
|
||||
case 1: return MDP4_OVERLAY_FLUSH_OVLP1;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t dma2irq(enum mdp4_dma dma)
|
||||
{
|
||||
switch (dma) {
|
||||
case DMA_P: return MDP4_IRQ_DMA_P_DONE;
|
||||
case DMA_S: return MDP4_IRQ_DMA_S_DONE;
|
||||
case DMA_E: return MDP4_IRQ_DMA_E_DONE;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t dma2err(enum mdp4_dma dma)
|
||||
{
|
||||
switch (dma) {
|
||||
case DMA_P: return MDP4_IRQ_PRIMARY_INTF_UDERRUN;
|
||||
case DMA_S: return 0; // ???
|
||||
case DMA_E: return MDP4_IRQ_EXTERNAL_INTF_UDERRUN;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int mdp4_disable(struct mdp4_kms *mdp4_kms);
|
||||
int mdp4_enable(struct mdp4_kms *mdp4_kms);
|
||||
|
||||
void mdp4_irq_preinstall(struct msm_kms *kms);
|
||||
int mdp4_irq_postinstall(struct msm_kms *kms);
|
||||
void mdp4_irq_uninstall(struct msm_kms *kms);
|
||||
irqreturn_t mdp4_irq(struct msm_kms *kms);
|
||||
void mdp4_irq_wait(struct mdp4_kms *mdp4_kms, uint32_t irqmask);
|
||||
void mdp4_irq_register(struct mdp4_kms *mdp4_kms, struct mdp4_irq *irq);
|
||||
void mdp4_irq_unregister(struct mdp4_kms *mdp4_kms, struct mdp4_irq *irq);
|
||||
int mdp4_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc);
|
||||
void mdp4_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc);
|
||||
|
||||
const struct msm_format *mdp4_get_format(struct msm_kms *kms, uint32_t format);
|
||||
|
||||
void mdp4_plane_install_properties(struct drm_plane *plane,
|
||||
struct drm_mode_object *obj);
|
||||
void mdp4_plane_set_scanout(struct drm_plane *plane,
|
||||
struct drm_framebuffer *fb);
|
||||
int mdp4_plane_mode_set(struct drm_plane *plane,
|
||||
struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
int crtc_x, int crtc_y,
|
||||
unsigned int crtc_w, unsigned int crtc_h,
|
||||
uint32_t src_x, uint32_t src_y,
|
||||
uint32_t src_w, uint32_t src_h);
|
||||
enum mpd4_pipe mdp4_plane_pipe(struct drm_plane *plane);
|
||||
struct drm_plane *mdp4_plane_init(struct drm_device *dev,
|
||||
enum mpd4_pipe pipe_id, bool private_plane);
|
||||
|
||||
uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc);
|
||||
void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc);
|
||||
void mdp4_crtc_set_config(struct drm_crtc *crtc, uint32_t config);
|
||||
void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf);
|
||||
struct drm_crtc *mdp4_crtc_init(struct drm_device *dev,
|
||||
struct drm_plane *plane, int id, int ovlp_id,
|
||||
enum mdp4_dma dma_id);
|
||||
|
||||
long mdp4_dtv_round_pixclk(struct drm_encoder *encoder, unsigned long rate);
|
||||
struct drm_encoder *mdp4_dtv_encoder_init(struct drm_device *dev);
|
||||
|
||||
#ifdef CONFIG_MSM_BUS_SCALING
|
||||
static inline int match_dev_name(struct device *dev, void *data)
|
||||
{
|
||||
return !strcmp(dev_name(dev), data);
|
||||
}
|
||||
/* bus scaling data is associated with extra pointless platform devices,
|
||||
* "dtv", etc.. this is a bit of a hack, but we need a way for encoders
|
||||
* to find their pdata to make the bus-scaling stuff work.
|
||||
*/
|
||||
static inline void *mdp4_find_pdata(const char *devname)
|
||||
{
|
||||
struct device *dev;
|
||||
dev = bus_find_device(&platform_bus_type, NULL,
|
||||
(void *)devname, match_dev_name);
|
||||
return dev ? dev->platform_data : NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __MDP4_KMS_H__ */
|
|
@ -0,0 +1,243 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "mdp4_kms.h"
|
||||
|
||||
|
||||
struct mdp4_plane {
|
||||
struct drm_plane base;
|
||||
const char *name;
|
||||
|
||||
enum mpd4_pipe pipe;
|
||||
|
||||
uint32_t nformats;
|
||||
uint32_t formats[32];
|
||||
|
||||
bool enabled;
|
||||
};
|
||||
#define to_mdp4_plane(x) container_of(x, struct mdp4_plane, base)
|
||||
|
||||
static struct mdp4_kms *get_kms(struct drm_plane *plane)
|
||||
{
|
||||
struct msm_drm_private *priv = plane->dev->dev_private;
|
||||
return to_mdp4_kms(priv->kms);
|
||||
}
|
||||
|
||||
static int mdp4_plane_update(struct drm_plane *plane,
|
||||
struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
int crtc_x, int crtc_y,
|
||||
unsigned int crtc_w, unsigned int crtc_h,
|
||||
uint32_t src_x, uint32_t src_y,
|
||||
uint32_t src_w, uint32_t src_h)
|
||||
{
|
||||
struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
|
||||
|
||||
mdp4_plane->enabled = true;
|
||||
|
||||
if (plane->fb)
|
||||
drm_framebuffer_unreference(plane->fb);
|
||||
|
||||
drm_framebuffer_reference(fb);
|
||||
|
||||
return mdp4_plane_mode_set(plane, crtc, fb,
|
||||
crtc_x, crtc_y, crtc_w, crtc_h,
|
||||
src_x, src_y, src_w, src_h);
|
||||
}
|
||||
|
||||
static int mdp4_plane_disable(struct drm_plane *plane)
|
||||
{
|
||||
struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
|
||||
DBG("%s: TODO", mdp4_plane->name); // XXX
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mdp4_plane_destroy(struct drm_plane *plane)
|
||||
{
|
||||
struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
|
||||
|
||||
mdp4_plane_disable(plane);
|
||||
drm_plane_cleanup(plane);
|
||||
|
||||
kfree(mdp4_plane);
|
||||
}
|
||||
|
||||
/* helper to install properties which are common to planes and crtcs */
|
||||
void mdp4_plane_install_properties(struct drm_plane *plane,
|
||||
struct drm_mode_object *obj)
|
||||
{
|
||||
// XXX
|
||||
}
|
||||
|
||||
int mdp4_plane_set_property(struct drm_plane *plane,
|
||||
struct drm_property *property, uint64_t val)
|
||||
{
|
||||
// XXX
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct drm_plane_funcs mdp4_plane_funcs = {
|
||||
.update_plane = mdp4_plane_update,
|
||||
.disable_plane = mdp4_plane_disable,
|
||||
.destroy = mdp4_plane_destroy,
|
||||
.set_property = mdp4_plane_set_property,
|
||||
};
|
||||
|
||||
void mdp4_plane_set_scanout(struct drm_plane *plane,
|
||||
struct drm_framebuffer *fb)
|
||||
{
|
||||
struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
|
||||
struct mdp4_kms *mdp4_kms = get_kms(plane);
|
||||
enum mpd4_pipe pipe = mdp4_plane->pipe;
|
||||
uint32_t iova;
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_STRIDE_A(pipe),
|
||||
MDP4_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) |
|
||||
MDP4_PIPE_SRC_STRIDE_A_P1(fb->pitches[1]));
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_STRIDE_B(pipe),
|
||||
MDP4_PIPE_SRC_STRIDE_B_P2(fb->pitches[2]) |
|
||||
MDP4_PIPE_SRC_STRIDE_B_P3(fb->pitches[3]));
|
||||
|
||||
msm_gem_get_iova(msm_framebuffer_bo(fb, 0), mdp4_kms->id, &iova);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRCP0_BASE(pipe), iova);
|
||||
|
||||
plane->fb = fb;
|
||||
}
|
||||
|
||||
#define MDP4_VG_PHASE_STEP_DEFAULT 0x20000000
|
||||
|
||||
int mdp4_plane_mode_set(struct drm_plane *plane,
|
||||
struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
int crtc_x, int crtc_y,
|
||||
unsigned int crtc_w, unsigned int crtc_h,
|
||||
uint32_t src_x, uint32_t src_y,
|
||||
uint32_t src_w, uint32_t src_h)
|
||||
{
|
||||
struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
|
||||
struct mdp4_kms *mdp4_kms = get_kms(plane);
|
||||
enum mpd4_pipe pipe = mdp4_plane->pipe;
|
||||
const struct mdp4_format *format;
|
||||
uint32_t op_mode = 0;
|
||||
uint32_t phasex_step = MDP4_VG_PHASE_STEP_DEFAULT;
|
||||
uint32_t phasey_step = MDP4_VG_PHASE_STEP_DEFAULT;
|
||||
|
||||
/* src values are in Q16 fixed point, convert to integer: */
|
||||
src_x = src_x >> 16;
|
||||
src_y = src_y >> 16;
|
||||
src_w = src_w >> 16;
|
||||
src_h = src_h >> 16;
|
||||
|
||||
if (src_w != crtc_w) {
|
||||
op_mode |= MDP4_PIPE_OP_MODE_SCALEX_EN;
|
||||
/* TODO calc phasex_step */
|
||||
}
|
||||
|
||||
if (src_h != crtc_h) {
|
||||
op_mode |= MDP4_PIPE_OP_MODE_SCALEY_EN;
|
||||
/* TODO calc phasey_step */
|
||||
}
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_SIZE(pipe),
|
||||
MDP4_PIPE_SRC_SIZE_WIDTH(src_w) |
|
||||
MDP4_PIPE_SRC_SIZE_HEIGHT(src_h));
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_XY(pipe),
|
||||
MDP4_PIPE_SRC_XY_X(src_x) |
|
||||
MDP4_PIPE_SRC_XY_Y(src_y));
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_PIPE_DST_SIZE(pipe),
|
||||
MDP4_PIPE_DST_SIZE_WIDTH(crtc_w) |
|
||||
MDP4_PIPE_DST_SIZE_HEIGHT(crtc_h));
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_PIPE_DST_XY(pipe),
|
||||
MDP4_PIPE_SRC_XY_X(crtc_x) |
|
||||
MDP4_PIPE_SRC_XY_Y(crtc_y));
|
||||
|
||||
mdp4_plane_set_scanout(plane, fb);
|
||||
|
||||
format = to_mdp4_format(msm_framebuffer_format(fb));
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_FORMAT(pipe),
|
||||
MDP4_PIPE_SRC_FORMAT_A_BPC(format->bpc_a) |
|
||||
MDP4_PIPE_SRC_FORMAT_R_BPC(format->bpc_r) |
|
||||
MDP4_PIPE_SRC_FORMAT_G_BPC(format->bpc_g) |
|
||||
MDP4_PIPE_SRC_FORMAT_B_BPC(format->bpc_b) |
|
||||
COND(format->alpha_enable, MDP4_PIPE_SRC_FORMAT_ALPHA_ENABLE) |
|
||||
MDP4_PIPE_SRC_FORMAT_CPP(format->cpp - 1) |
|
||||
MDP4_PIPE_SRC_FORMAT_UNPACK_COUNT(format->unpack_count - 1) |
|
||||
COND(format->unpack_tight, MDP4_PIPE_SRC_FORMAT_UNPACK_TIGHT));
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_UNPACK(pipe),
|
||||
MDP4_PIPE_SRC_UNPACK_ELEM0(format->unpack[0]) |
|
||||
MDP4_PIPE_SRC_UNPACK_ELEM1(format->unpack[1]) |
|
||||
MDP4_PIPE_SRC_UNPACK_ELEM2(format->unpack[2]) |
|
||||
MDP4_PIPE_SRC_UNPACK_ELEM3(format->unpack[3]));
|
||||
|
||||
mdp4_write(mdp4_kms, REG_MDP4_PIPE_OP_MODE(pipe), op_mode);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_PIPE_PHASEX_STEP(pipe), phasex_step);
|
||||
mdp4_write(mdp4_kms, REG_MDP4_PIPE_PHASEY_STEP(pipe), phasey_step);
|
||||
|
||||
plane->crtc = crtc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *pipe_names[] = {
|
||||
"VG1", "VG2",
|
||||
"RGB1", "RGB2", "RGB3",
|
||||
"VG3", "VG4",
|
||||
};
|
||||
|
||||
enum mpd4_pipe mdp4_plane_pipe(struct drm_plane *plane)
|
||||
{
|
||||
struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
|
||||
return mdp4_plane->pipe;
|
||||
}
|
||||
|
||||
/* initialize plane */
|
||||
struct drm_plane *mdp4_plane_init(struct drm_device *dev,
|
||||
enum mpd4_pipe pipe_id, bool private_plane)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct drm_plane *plane = NULL;
|
||||
struct mdp4_plane *mdp4_plane;
|
||||
int ret;
|
||||
|
||||
mdp4_plane = kzalloc(sizeof(*mdp4_plane), GFP_KERNEL);
|
||||
if (!mdp4_plane) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
plane = &mdp4_plane->base;
|
||||
|
||||
mdp4_plane->pipe = pipe_id;
|
||||
mdp4_plane->name = pipe_names[pipe_id];
|
||||
|
||||
drm_plane_init(dev, plane, (1 << priv->num_crtcs) - 1, &mdp4_plane_funcs,
|
||||
mdp4_plane->formats, mdp4_plane->nformats, private_plane);
|
||||
|
||||
mdp4_plane_install_properties(plane, &plane->base);
|
||||
|
||||
return plane;
|
||||
|
||||
fail:
|
||||
if (plane)
|
||||
mdp4_plane_destroy(plane);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "msm_connector.h"
|
||||
|
||||
void msm_connector_init(struct msm_connector *connector,
|
||||
const struct msm_connector_funcs *funcs,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
connector->funcs = funcs;
|
||||
connector->encoder = encoder;
|
||||
}
|
||||
|
||||
struct drm_encoder *msm_connector_attached_encoder(
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct msm_connector *msm_connector = to_msm_connector(connector);
|
||||
return msm_connector->encoder;
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MSM_CONNECTOR_H__
|
||||
#define __MSM_CONNECTOR_H__
|
||||
|
||||
#include "msm_drv.h"
|
||||
|
||||
/*
|
||||
* Base class for MSM connectors. Typically a connector is a bit more
|
||||
* passive. But with the split between (for example) DTV within MDP4,
|
||||
* and HDMI encoder, we really need two parts to an encoder. Instead
|
||||
* what we do is have the part external to the display controller block
|
||||
* in the connector, which is called from the encoder to delegate the
|
||||
* appropriate parts of modeset.
|
||||
*/
|
||||
|
||||
struct msm_connector;
|
||||
|
||||
struct msm_connector_funcs {
|
||||
void (*dpms)(struct msm_connector *connector, int mode);
|
||||
void (*mode_set)(struct msm_connector *connector,
|
||||
struct drm_display_mode *mode);
|
||||
};
|
||||
|
||||
struct msm_connector {
|
||||
struct drm_connector base;
|
||||
struct drm_encoder *encoder;
|
||||
const struct msm_connector_funcs *funcs;
|
||||
};
|
||||
#define to_msm_connector(x) container_of(x, struct msm_connector, base)
|
||||
|
||||
void msm_connector_init(struct msm_connector *connector,
|
||||
const struct msm_connector_funcs *funcs,
|
||||
struct drm_encoder *encoder);
|
||||
|
||||
struct drm_encoder *msm_connector_attached_encoder(
|
||||
struct drm_connector *connector);
|
||||
|
||||
static inline struct msm_connector *get_connector(struct drm_encoder *encoder)
|
||||
{
|
||||
struct msm_drm_private *priv = encoder->dev->dev_private;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < priv->num_connectors; i++) {
|
||||
struct drm_connector *connector = priv->connectors[i];
|
||||
if (msm_connector_attached_encoder(connector) == encoder)
|
||||
return to_msm_connector(connector);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* __MSM_CONNECTOR_H__ */
|
|
@ -0,0 +1,776 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "msm_gpu.h"
|
||||
|
||||
#include <mach/iommu.h>
|
||||
|
||||
static void msm_fb_output_poll_changed(struct drm_device *dev)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
if (priv->fbdev)
|
||||
drm_fb_helper_hotplug_event(priv->fbdev);
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_funcs mode_config_funcs = {
|
||||
.fb_create = msm_framebuffer_create,
|
||||
.output_poll_changed = msm_fb_output_poll_changed,
|
||||
};
|
||||
|
||||
static int msm_fault_handler(struct iommu_domain *iommu, struct device *dev,
|
||||
unsigned long iova, int flags, void *arg)
|
||||
{
|
||||
DBG("*** fault: iova=%08lx, flags=%d", iova, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msm_register_iommu(struct drm_device *dev, struct iommu_domain *iommu)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
int idx = priv->num_iommus++;
|
||||
|
||||
if (WARN_ON(idx >= ARRAY_SIZE(priv->iommus)))
|
||||
return -EINVAL;
|
||||
|
||||
priv->iommus[idx] = iommu;
|
||||
|
||||
iommu_set_fault_handler(iommu, msm_fault_handler, dev);
|
||||
|
||||
/* need to iommu_attach_device() somewhere?? on resume?? */
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
int msm_iommu_attach(struct drm_device *dev, struct iommu_domain *iommu,
|
||||
const char **names, int cnt)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
struct device *ctx = msm_iommu_get_ctx(names[i]);
|
||||
if (!ctx)
|
||||
continue;
|
||||
ret = iommu_attach_device(iommu, ctx);
|
||||
if (ret) {
|
||||
dev_warn(dev->dev, "could not attach iommu to %s", names[i]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DRM_MSM_REGISTER_LOGGING
|
||||
static bool reglog = false;
|
||||
MODULE_PARM_DESC(reglog, "Enable register read/write logging");
|
||||
module_param(reglog, bool, 0600);
|
||||
#else
|
||||
#define reglog 0
|
||||
#endif
|
||||
|
||||
void __iomem *msm_ioremap(struct platform_device *pdev, const char *name,
|
||||
const char *dbgname)
|
||||
{
|
||||
struct resource *res;
|
||||
unsigned long size;
|
||||
void __iomem *ptr;
|
||||
|
||||
if (name)
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
|
||||
else
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "failed to get memory resource: %s\n", name);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
size = resource_size(res);
|
||||
|
||||
ptr = devm_ioremap_nocache(&pdev->dev, res->start, size);
|
||||
if (!ptr) {
|
||||
dev_err(&pdev->dev, "failed to ioremap: %s\n", name);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
if (reglog)
|
||||
printk(KERN_DEBUG "IO:region %s %08x %08lx\n", dbgname, (u32)ptr, size);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void msm_writel(u32 data, void __iomem *addr)
|
||||
{
|
||||
if (reglog)
|
||||
printk(KERN_DEBUG "IO:W %08x %08x\n", (u32)addr, data);
|
||||
writel(data, addr);
|
||||
}
|
||||
|
||||
u32 msm_readl(const void __iomem *addr)
|
||||
{
|
||||
u32 val = readl(addr);
|
||||
if (reglog)
|
||||
printk(KERN_ERR "IO:R %08x %08x\n", (u32)addr, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* DRM operations:
|
||||
*/
|
||||
|
||||
static int msm_unload(struct drm_device *dev)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct msm_kms *kms = priv->kms;
|
||||
struct msm_gpu *gpu = priv->gpu;
|
||||
|
||||
drm_kms_helper_poll_fini(dev);
|
||||
drm_mode_config_cleanup(dev);
|
||||
drm_vblank_cleanup(dev);
|
||||
|
||||
pm_runtime_get_sync(dev->dev);
|
||||
drm_irq_uninstall(dev);
|
||||
pm_runtime_put_sync(dev->dev);
|
||||
|
||||
flush_workqueue(priv->wq);
|
||||
destroy_workqueue(priv->wq);
|
||||
|
||||
if (kms) {
|
||||
pm_runtime_disable(dev->dev);
|
||||
kms->funcs->destroy(kms);
|
||||
}
|
||||
|
||||
if (gpu) {
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
gpu->funcs->pm_suspend(gpu);
|
||||
gpu->funcs->destroy(gpu);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
}
|
||||
|
||||
dev->dev_private = NULL;
|
||||
|
||||
kfree(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_load(struct drm_device *dev, unsigned long flags)
|
||||
{
|
||||
struct platform_device *pdev = dev->platformdev;
|
||||
struct msm_drm_private *priv;
|
||||
struct msm_kms *kms;
|
||||
int ret;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(dev->dev, "failed to allocate private data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dev->dev_private = priv;
|
||||
|
||||
priv->wq = alloc_ordered_workqueue("msm", 0);
|
||||
init_waitqueue_head(&priv->fence_event);
|
||||
|
||||
INIT_LIST_HEAD(&priv->inactive_list);
|
||||
|
||||
drm_mode_config_init(dev);
|
||||
|
||||
kms = mdp4_kms_init(dev);
|
||||
if (IS_ERR(kms)) {
|
||||
/*
|
||||
* NOTE: once we have GPU support, having no kms should not
|
||||
* be considered fatal.. ideally we would still support gpu
|
||||
* and (for example) use dmabuf/prime to share buffers with
|
||||
* imx drm driver on iMX5
|
||||
*/
|
||||
dev_err(dev->dev, "failed to load kms\n");
|
||||
ret = PTR_ERR(priv->kms);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
priv->kms = kms;
|
||||
|
||||
if (kms) {
|
||||
pm_runtime_enable(dev->dev);
|
||||
ret = kms->funcs->hw_init(kms);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "kms hw init failed: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
dev->mode_config.min_width = 0;
|
||||
dev->mode_config.min_height = 0;
|
||||
dev->mode_config.max_width = 2048;
|
||||
dev->mode_config.max_height = 2048;
|
||||
dev->mode_config.funcs = &mode_config_funcs;
|
||||
|
||||
ret = drm_vblank_init(dev, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "failed to initialize vblank\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pm_runtime_get_sync(dev->dev);
|
||||
ret = drm_irq_install(dev);
|
||||
pm_runtime_put_sync(dev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "failed to install IRQ handler\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, dev);
|
||||
|
||||
#ifdef CONFIG_DRM_MSM_FBDEV
|
||||
priv->fbdev = msm_fbdev_init(dev);
|
||||
#endif
|
||||
|
||||
drm_kms_helper_poll_init(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
msm_unload(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void load_gpu(struct drm_device *dev)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct msm_gpu *gpu;
|
||||
|
||||
if (priv->gpu)
|
||||
return;
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
gpu = a3xx_gpu_init(dev);
|
||||
if (IS_ERR(gpu)) {
|
||||
dev_warn(dev->dev, "failed to load a3xx gpu\n");
|
||||
gpu = NULL;
|
||||
/* not fatal */
|
||||
}
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
if (gpu) {
|
||||
int ret;
|
||||
gpu->funcs->pm_resume(gpu);
|
||||
ret = gpu->funcs->hw_init(gpu);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "gpu hw init failed: %d\n", ret);
|
||||
gpu->funcs->destroy(gpu);
|
||||
gpu = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
priv->gpu = gpu;
|
||||
}
|
||||
|
||||
static int msm_open(struct drm_device *dev, struct drm_file *file)
|
||||
{
|
||||
struct msm_file_private *ctx;
|
||||
|
||||
/* For now, load gpu on open.. to avoid the requirement of having
|
||||
* firmware in the initrd.
|
||||
*/
|
||||
load_gpu(dev);
|
||||
|
||||
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
file->driver_priv = ctx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void msm_preclose(struct drm_device *dev, struct drm_file *file)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct msm_file_private *ctx = file->driver_priv;
|
||||
struct msm_kms *kms = priv->kms;
|
||||
|
||||
if (kms)
|
||||
kms->funcs->preclose(kms, file);
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
if (ctx == priv->lastctx)
|
||||
priv->lastctx = NULL;
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
kfree(ctx);
|
||||
}
|
||||
|
||||
static void msm_lastclose(struct drm_device *dev)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
if (priv->fbdev) {
|
||||
drm_modeset_lock_all(dev);
|
||||
drm_fb_helper_restore_fbdev_mode(priv->fbdev);
|
||||
drm_modeset_unlock_all(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t msm_irq(DRM_IRQ_ARGS)
|
||||
{
|
||||
struct drm_device *dev = arg;
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct msm_kms *kms = priv->kms;
|
||||
BUG_ON(!kms);
|
||||
return kms->funcs->irq(kms);
|
||||
}
|
||||
|
||||
static void msm_irq_preinstall(struct drm_device *dev)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct msm_kms *kms = priv->kms;
|
||||
BUG_ON(!kms);
|
||||
kms->funcs->irq_preinstall(kms);
|
||||
}
|
||||
|
||||
static int msm_irq_postinstall(struct drm_device *dev)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct msm_kms *kms = priv->kms;
|
||||
BUG_ON(!kms);
|
||||
return kms->funcs->irq_postinstall(kms);
|
||||
}
|
||||
|
||||
static void msm_irq_uninstall(struct drm_device *dev)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct msm_kms *kms = priv->kms;
|
||||
BUG_ON(!kms);
|
||||
kms->funcs->irq_uninstall(kms);
|
||||
}
|
||||
|
||||
static int msm_enable_vblank(struct drm_device *dev, int crtc_id)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct msm_kms *kms = priv->kms;
|
||||
if (!kms)
|
||||
return -ENXIO;
|
||||
DBG("dev=%p, crtc=%d", dev, crtc_id);
|
||||
return kms->funcs->enable_vblank(kms, priv->crtcs[crtc_id]);
|
||||
}
|
||||
|
||||
static void msm_disable_vblank(struct drm_device *dev, int crtc_id)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct msm_kms *kms = priv->kms;
|
||||
if (!kms)
|
||||
return;
|
||||
DBG("dev=%p, crtc=%d", dev, crtc_id);
|
||||
kms->funcs->disable_vblank(kms, priv->crtcs[crtc_id]);
|
||||
}
|
||||
|
||||
/*
|
||||
* DRM debugfs:
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static int msm_gpu_show(struct drm_device *dev, struct seq_file *m)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct msm_gpu *gpu = priv->gpu;
|
||||
|
||||
if (gpu) {
|
||||
seq_printf(m, "%s Status:\n", gpu->name);
|
||||
gpu->funcs->show(gpu, m);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_gem_show(struct drm_device *dev, struct seq_file *m)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct msm_gpu *gpu = priv->gpu;
|
||||
|
||||
if (gpu) {
|
||||
seq_printf(m, "Active Objects (%s):\n", gpu->name);
|
||||
msm_gem_describe_objects(&gpu->active_list, m);
|
||||
}
|
||||
|
||||
seq_printf(m, "Inactive Objects:\n");
|
||||
msm_gem_describe_objects(&priv->inactive_list, m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_mm_show(struct drm_device *dev, struct seq_file *m)
|
||||
{
|
||||
return drm_mm_dump_table(m, dev->mm_private);
|
||||
}
|
||||
|
||||
static int msm_fb_show(struct drm_device *dev, struct seq_file *m)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct drm_framebuffer *fb, *fbdev_fb = NULL;
|
||||
|
||||
if (priv->fbdev) {
|
||||
seq_printf(m, "fbcon ");
|
||||
fbdev_fb = priv->fbdev->fb;
|
||||
msm_framebuffer_describe(fbdev_fb, m);
|
||||
}
|
||||
|
||||
mutex_lock(&dev->mode_config.fb_lock);
|
||||
list_for_each_entry(fb, &dev->mode_config.fb_list, head) {
|
||||
if (fb == fbdev_fb)
|
||||
continue;
|
||||
|
||||
seq_printf(m, "user ");
|
||||
msm_framebuffer_describe(fb, m);
|
||||
}
|
||||
mutex_unlock(&dev->mode_config.fb_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_locked(struct seq_file *m, void *arg)
|
||||
{
|
||||
struct drm_info_node *node = (struct drm_info_node *) m->private;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
int (*show)(struct drm_device *dev, struct seq_file *m) =
|
||||
node->info_ent->data;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = show(dev, m);
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct drm_info_list msm_debugfs_list[] = {
|
||||
{"gpu", show_locked, 0, msm_gpu_show},
|
||||
{"gem", show_locked, 0, msm_gem_show},
|
||||
{ "mm", show_locked, 0, msm_mm_show },
|
||||
{ "fb", show_locked, 0, msm_fb_show },
|
||||
};
|
||||
|
||||
static int msm_debugfs_init(struct drm_minor *minor)
|
||||
{
|
||||
struct drm_device *dev = minor->dev;
|
||||
int ret;
|
||||
|
||||
ret = drm_debugfs_create_files(msm_debugfs_list,
|
||||
ARRAY_SIZE(msm_debugfs_list),
|
||||
minor->debugfs_root, minor);
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "could not install msm_debugfs_list\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void msm_debugfs_cleanup(struct drm_minor *minor)
|
||||
{
|
||||
drm_debugfs_remove_files(msm_debugfs_list,
|
||||
ARRAY_SIZE(msm_debugfs_list), minor);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Fences:
|
||||
*/
|
||||
|
||||
int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence,
|
||||
struct timespec *timeout)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
unsigned long timeout_jiffies = timespec_to_jiffies(timeout);
|
||||
unsigned long start_jiffies = jiffies;
|
||||
unsigned long remaining_jiffies;
|
||||
int ret;
|
||||
|
||||
if (time_after(start_jiffies, timeout_jiffies))
|
||||
remaining_jiffies = 0;
|
||||
else
|
||||
remaining_jiffies = timeout_jiffies - start_jiffies;
|
||||
|
||||
ret = wait_event_interruptible_timeout(priv->fence_event,
|
||||
priv->completed_fence >= fence,
|
||||
remaining_jiffies);
|
||||
if (ret == 0) {
|
||||
DBG("timeout waiting for fence: %u (completed: %u)",
|
||||
fence, priv->completed_fence);
|
||||
ret = -ETIMEDOUT;
|
||||
} else if (ret != -ERESTARTSYS) {
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* call under struct_mutex */
|
||||
void msm_update_fence(struct drm_device *dev, uint32_t fence)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
|
||||
if (fence > priv->completed_fence) {
|
||||
priv->completed_fence = fence;
|
||||
wake_up_all(&priv->fence_event);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* DRM ioctls:
|
||||
*/
|
||||
|
||||
static int msm_ioctl_get_param(struct drm_device *dev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct drm_msm_param *args = data;
|
||||
struct msm_gpu *gpu;
|
||||
|
||||
/* for now, we just have 3d pipe.. eventually this would need to
|
||||
* be more clever to dispatch to appropriate gpu module:
|
||||
*/
|
||||
if (args->pipe != MSM_PIPE_3D0)
|
||||
return -EINVAL;
|
||||
|
||||
gpu = priv->gpu;
|
||||
|
||||
if (!gpu)
|
||||
return -ENXIO;
|
||||
|
||||
return gpu->funcs->get_param(gpu, args->param, &args->value);
|
||||
}
|
||||
|
||||
static int msm_ioctl_gem_new(struct drm_device *dev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct drm_msm_gem_new *args = data;
|
||||
return msm_gem_new_handle(dev, file, args->size,
|
||||
args->flags, &args->handle);
|
||||
}
|
||||
|
||||
#define TS(t) ((struct timespec){ .tv_sec = (t).tv_sec, .tv_nsec = (t).tv_nsec })
|
||||
|
||||
static int msm_ioctl_gem_cpu_prep(struct drm_device *dev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct drm_msm_gem_cpu_prep *args = data;
|
||||
struct drm_gem_object *obj;
|
||||
int ret;
|
||||
|
||||
obj = drm_gem_object_lookup(dev, file, args->handle);
|
||||
if (!obj)
|
||||
return -ENOENT;
|
||||
|
||||
ret = msm_gem_cpu_prep(obj, args->op, &TS(args->timeout));
|
||||
|
||||
drm_gem_object_unreference_unlocked(obj);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_ioctl_gem_cpu_fini(struct drm_device *dev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct drm_msm_gem_cpu_fini *args = data;
|
||||
struct drm_gem_object *obj;
|
||||
int ret;
|
||||
|
||||
obj = drm_gem_object_lookup(dev, file, args->handle);
|
||||
if (!obj)
|
||||
return -ENOENT;
|
||||
|
||||
ret = msm_gem_cpu_fini(obj);
|
||||
|
||||
drm_gem_object_unreference_unlocked(obj);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_ioctl_gem_info(struct drm_device *dev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct drm_msm_gem_info *args = data;
|
||||
struct drm_gem_object *obj;
|
||||
int ret = 0;
|
||||
|
||||
if (args->pad)
|
||||
return -EINVAL;
|
||||
|
||||
obj = drm_gem_object_lookup(dev, file, args->handle);
|
||||
if (!obj)
|
||||
return -ENOENT;
|
||||
|
||||
args->offset = msm_gem_mmap_offset(obj);
|
||||
|
||||
drm_gem_object_unreference_unlocked(obj);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_ioctl_wait_fence(struct drm_device *dev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct drm_msm_wait_fence *args = data;
|
||||
return msm_wait_fence_interruptable(dev, args->fence, &TS(args->timeout));
|
||||
}
|
||||
|
||||
static const struct drm_ioctl_desc msm_ioctls[] = {
|
||||
DRM_IOCTL_DEF_DRV(MSM_GET_PARAM, msm_ioctl_get_param, DRM_UNLOCKED|DRM_AUTH),
|
||||
DRM_IOCTL_DEF_DRV(MSM_GEM_NEW, msm_ioctl_gem_new, DRM_UNLOCKED|DRM_AUTH),
|
||||
DRM_IOCTL_DEF_DRV(MSM_GEM_INFO, msm_ioctl_gem_info, DRM_UNLOCKED|DRM_AUTH),
|
||||
DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_PREP, msm_ioctl_gem_cpu_prep, DRM_UNLOCKED|DRM_AUTH),
|
||||
DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_FINI, msm_ioctl_gem_cpu_fini, DRM_UNLOCKED|DRM_AUTH),
|
||||
DRM_IOCTL_DEF_DRV(MSM_GEM_SUBMIT, msm_ioctl_gem_submit, DRM_UNLOCKED|DRM_AUTH),
|
||||
DRM_IOCTL_DEF_DRV(MSM_WAIT_FENCE, msm_ioctl_wait_fence, DRM_UNLOCKED|DRM_AUTH),
|
||||
};
|
||||
|
||||
static const struct vm_operations_struct vm_ops = {
|
||||
.fault = msm_gem_fault,
|
||||
.open = drm_gem_vm_open,
|
||||
.close = drm_gem_vm_close,
|
||||
};
|
||||
|
||||
static const struct file_operations fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
#endif
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = no_llseek,
|
||||
.mmap = msm_gem_mmap,
|
||||
};
|
||||
|
||||
static struct drm_driver msm_driver = {
|
||||
.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET,
|
||||
.load = msm_load,
|
||||
.unload = msm_unload,
|
||||
.open = msm_open,
|
||||
.preclose = msm_preclose,
|
||||
.lastclose = msm_lastclose,
|
||||
.irq_handler = msm_irq,
|
||||
.irq_preinstall = msm_irq_preinstall,
|
||||
.irq_postinstall = msm_irq_postinstall,
|
||||
.irq_uninstall = msm_irq_uninstall,
|
||||
.get_vblank_counter = drm_vblank_count,
|
||||
.enable_vblank = msm_enable_vblank,
|
||||
.disable_vblank = msm_disable_vblank,
|
||||
.gem_free_object = msm_gem_free_object,
|
||||
.gem_vm_ops = &vm_ops,
|
||||
.dumb_create = msm_gem_dumb_create,
|
||||
.dumb_map_offset = msm_gem_dumb_map_offset,
|
||||
.dumb_destroy = msm_gem_dumb_destroy,
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
.debugfs_init = msm_debugfs_init,
|
||||
.debugfs_cleanup = msm_debugfs_cleanup,
|
||||
#endif
|
||||
.ioctls = msm_ioctls,
|
||||
.num_ioctls = DRM_MSM_NUM_IOCTLS,
|
||||
.fops = &fops,
|
||||
.name = "msm",
|
||||
.desc = "MSM Snapdragon DRM",
|
||||
.date = "20130625",
|
||||
.major = 1,
|
||||
.minor = 0,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int msm_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct drm_device *ddev = dev_get_drvdata(dev);
|
||||
|
||||
drm_kms_helper_poll_disable(ddev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_pm_resume(struct device *dev)
|
||||
{
|
||||
struct drm_device *ddev = dev_get_drvdata(dev);
|
||||
|
||||
drm_kms_helper_poll_enable(ddev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops msm_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(msm_pm_suspend, msm_pm_resume)
|
||||
};
|
||||
|
||||
/*
|
||||
* Platform driver:
|
||||
*/
|
||||
|
||||
static int msm_pdev_probe(struct platform_device *pdev)
|
||||
{
|
||||
return drm_platform_init(&msm_driver, pdev);
|
||||
}
|
||||
|
||||
static int msm_pdev_remove(struct platform_device *pdev)
|
||||
{
|
||||
drm_platform_exit(&msm_driver, pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id msm_id[] = {
|
||||
{ "mdp", 0 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_driver msm_platform_driver = {
|
||||
.probe = msm_pdev_probe,
|
||||
.remove = msm_pdev_remove,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "msm",
|
||||
.pm = &msm_pm_ops,
|
||||
},
|
||||
.id_table = msm_id,
|
||||
};
|
||||
|
||||
static int __init msm_drm_register(void)
|
||||
{
|
||||
DBG("init");
|
||||
hdmi_register();
|
||||
a3xx_register();
|
||||
return platform_driver_register(&msm_platform_driver);
|
||||
}
|
||||
|
||||
static void __exit msm_drm_unregister(void)
|
||||
{
|
||||
DBG("fini");
|
||||
platform_driver_unregister(&msm_platform_driver);
|
||||
hdmi_unregister();
|
||||
a3xx_unregister();
|
||||
}
|
||||
|
||||
module_init(msm_drm_register);
|
||||
module_exit(msm_drm_unregister);
|
||||
|
||||
MODULE_AUTHOR("Rob Clark <robdclark@gmail.com");
|
||||
MODULE_DESCRIPTION("MSM DRM Driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MSM_DRV_H__
|
||||
#define __MSM_DRV_H__
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/sizes.h>
|
||||
|
||||
#ifndef CONFIG_OF
|
||||
#include <mach/board.h>
|
||||
#include <mach/socinfo.h>
|
||||
#include <mach/iommu_domains.h>
|
||||
#endif
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/msm_drm.h>
|
||||
|
||||
struct msm_kms;
|
||||
struct msm_gpu;
|
||||
|
||||
#define NUM_DOMAINS 2 /* one for KMS, then one per gpu core (?) */
|
||||
|
||||
struct msm_file_private {
|
||||
/* currently we don't do anything useful with this.. but when
|
||||
* per-context address spaces are supported we'd keep track of
|
||||
* the context's page-tables here.
|
||||
*/
|
||||
int dummy;
|
||||
};
|
||||
|
||||
struct msm_drm_private {
|
||||
|
||||
struct msm_kms *kms;
|
||||
|
||||
/* when we have more than one 'msm_gpu' these need to be an array: */
|
||||
struct msm_gpu *gpu;
|
||||
struct msm_file_private *lastctx;
|
||||
|
||||
struct drm_fb_helper *fbdev;
|
||||
|
||||
uint32_t next_fence, completed_fence;
|
||||
wait_queue_head_t fence_event;
|
||||
|
||||
/* list of GEM objects: */
|
||||
struct list_head inactive_list;
|
||||
|
||||
struct workqueue_struct *wq;
|
||||
|
||||
/* registered IOMMU domains: */
|
||||
unsigned int num_iommus;
|
||||
struct iommu_domain *iommus[NUM_DOMAINS];
|
||||
|
||||
unsigned int num_crtcs;
|
||||
struct drm_crtc *crtcs[8];
|
||||
|
||||
unsigned int num_encoders;
|
||||
struct drm_encoder *encoders[8];
|
||||
|
||||
unsigned int num_connectors;
|
||||
struct drm_connector *connectors[8];
|
||||
};
|
||||
|
||||
struct msm_format {
|
||||
uint32_t pixel_format;
|
||||
};
|
||||
|
||||
/* As there are different display controller blocks depending on the
|
||||
* snapdragon version, the kms support is split out and the appropriate
|
||||
* implementation is loaded at runtime. The kms module is responsible
|
||||
* for constructing the appropriate planes/crtcs/encoders/connectors.
|
||||
*/
|
||||
struct msm_kms_funcs {
|
||||
/* hw initialization: */
|
||||
int (*hw_init)(struct msm_kms *kms);
|
||||
/* irq handling: */
|
||||
void (*irq_preinstall)(struct msm_kms *kms);
|
||||
int (*irq_postinstall)(struct msm_kms *kms);
|
||||
void (*irq_uninstall)(struct msm_kms *kms);
|
||||
irqreturn_t (*irq)(struct msm_kms *kms);
|
||||
int (*enable_vblank)(struct msm_kms *kms, struct drm_crtc *crtc);
|
||||
void (*disable_vblank)(struct msm_kms *kms, struct drm_crtc *crtc);
|
||||
/* misc: */
|
||||
const struct msm_format *(*get_format)(struct msm_kms *kms, uint32_t format);
|
||||
long (*round_pixclk)(struct msm_kms *kms, unsigned long rate,
|
||||
struct drm_encoder *encoder);
|
||||
/* cleanup: */
|
||||
void (*preclose)(struct msm_kms *kms, struct drm_file *file);
|
||||
void (*destroy)(struct msm_kms *kms);
|
||||
};
|
||||
|
||||
struct msm_kms {
|
||||
const struct msm_kms_funcs *funcs;
|
||||
};
|
||||
|
||||
struct msm_kms *mdp4_kms_init(struct drm_device *dev);
|
||||
|
||||
int msm_register_iommu(struct drm_device *dev, struct iommu_domain *iommu);
|
||||
int msm_iommu_attach(struct drm_device *dev, struct iommu_domain *iommu,
|
||||
const char **names, int cnt);
|
||||
|
||||
int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence,
|
||||
struct timespec *timeout);
|
||||
void msm_update_fence(struct drm_device *dev, uint32_t fence);
|
||||
|
||||
int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
|
||||
struct drm_file *file);
|
||||
|
||||
int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
|
||||
int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
|
||||
uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj);
|
||||
int msm_gem_get_iova_locked(struct drm_gem_object *obj, int id,
|
||||
uint32_t *iova);
|
||||
int msm_gem_get_iova(struct drm_gem_object *obj, int id, uint32_t *iova);
|
||||
void msm_gem_put_iova(struct drm_gem_object *obj, int id);
|
||||
int msm_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
|
||||
struct drm_mode_create_dumb *args);
|
||||
int msm_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev,
|
||||
uint32_t handle);
|
||||
int msm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
|
||||
uint32_t handle, uint64_t *offset);
|
||||
void *msm_gem_vaddr_locked(struct drm_gem_object *obj);
|
||||
void *msm_gem_vaddr(struct drm_gem_object *obj);
|
||||
int msm_gem_queue_inactive_work(struct drm_gem_object *obj,
|
||||
struct work_struct *work);
|
||||
void msm_gem_move_to_active(struct drm_gem_object *obj,
|
||||
struct msm_gpu *gpu, uint32_t fence);
|
||||
void msm_gem_move_to_inactive(struct drm_gem_object *obj);
|
||||
int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op,
|
||||
struct timespec *timeout);
|
||||
int msm_gem_cpu_fini(struct drm_gem_object *obj);
|
||||
void msm_gem_free_object(struct drm_gem_object *obj);
|
||||
int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file,
|
||||
uint32_t size, uint32_t flags, uint32_t *handle);
|
||||
struct drm_gem_object *msm_gem_new(struct drm_device *dev,
|
||||
uint32_t size, uint32_t flags);
|
||||
|
||||
struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane);
|
||||
const struct msm_format *msm_framebuffer_format(struct drm_framebuffer *fb);
|
||||
struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
|
||||
struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos);
|
||||
struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev,
|
||||
struct drm_file *file, struct drm_mode_fb_cmd2 *mode_cmd);
|
||||
|
||||
struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev);
|
||||
|
||||
struct drm_connector *hdmi_connector_init(struct drm_device *dev,
|
||||
struct drm_encoder *encoder);
|
||||
void __init hdmi_register(void);
|
||||
void __exit hdmi_unregister(void);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m);
|
||||
void msm_gem_describe_objects(struct list_head *list, struct seq_file *m);
|
||||
void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m);
|
||||
#endif
|
||||
|
||||
void __iomem *msm_ioremap(struct platform_device *pdev, const char *name,
|
||||
const char *dbgname);
|
||||
void msm_writel(u32 data, void __iomem *addr);
|
||||
u32 msm_readl(const void __iomem *addr);
|
||||
|
||||
#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
|
||||
#define VERB(fmt, ...) if (0) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
|
||||
|
||||
static inline int align_pitch(int width, int bpp)
|
||||
{
|
||||
int bytespp = (bpp + 7) / 8;
|
||||
/* adreno needs pitch aligned to 32 pixels: */
|
||||
return bytespp * ALIGN(width, 32);
|
||||
}
|
||||
|
||||
/* for the generated headers: */
|
||||
#define INVALID_IDX(idx) ({BUG(); 0;})
|
||||
#define fui(x) ({BUG(); 0;})
|
||||
#define util_float_to_half(x) ({BUG(); 0;})
|
||||
|
||||
|
||||
#define FIELD(val, name) (((val) & name ## __MASK) >> name ## __SHIFT)
|
||||
|
||||
/* for conditionally setting boolean flag(s): */
|
||||
#define COND(bool, val) ((bool) ? (val) : 0)
|
||||
|
||||
|
||||
#endif /* __MSM_DRV_H__ */
|
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "msm_drv.h"
|
||||
|
||||
#include "drm_crtc.h"
|
||||
#include "drm_crtc_helper.h"
|
||||
|
||||
struct msm_framebuffer {
|
||||
struct drm_framebuffer base;
|
||||
const struct msm_format *format;
|
||||
struct drm_gem_object *planes[2];
|
||||
};
|
||||
#define to_msm_framebuffer(x) container_of(x, struct msm_framebuffer, base)
|
||||
|
||||
|
||||
static int msm_framebuffer_create_handle(struct drm_framebuffer *fb,
|
||||
struct drm_file *file_priv,
|
||||
unsigned int *handle)
|
||||
{
|
||||
struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
|
||||
return drm_gem_handle_create(file_priv,
|
||||
msm_fb->planes[0], handle);
|
||||
}
|
||||
|
||||
static void msm_framebuffer_destroy(struct drm_framebuffer *fb)
|
||||
{
|
||||
struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
|
||||
int i, n = drm_format_num_planes(fb->pixel_format);
|
||||
|
||||
DBG("destroy: FB ID: %d (%p)", fb->base.id, fb);
|
||||
|
||||
drm_framebuffer_cleanup(fb);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
struct drm_gem_object *bo = msm_fb->planes[i];
|
||||
if (bo)
|
||||
drm_gem_object_unreference_unlocked(bo);
|
||||
}
|
||||
|
||||
kfree(msm_fb);
|
||||
}
|
||||
|
||||
static int msm_framebuffer_dirty(struct drm_framebuffer *fb,
|
||||
struct drm_file *file_priv, unsigned flags, unsigned color,
|
||||
struct drm_clip_rect *clips, unsigned num_clips)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_framebuffer_funcs msm_framebuffer_funcs = {
|
||||
.create_handle = msm_framebuffer_create_handle,
|
||||
.destroy = msm_framebuffer_destroy,
|
||||
.dirty = msm_framebuffer_dirty,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m)
|
||||
{
|
||||
struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
|
||||
int i, n = drm_format_num_planes(fb->pixel_format);
|
||||
|
||||
seq_printf(m, "fb: %dx%d@%4.4s (%2d, ID:%d)\n",
|
||||
fb->width, fb->height, (char *)&fb->pixel_format,
|
||||
fb->refcount.refcount.counter, fb->base.id);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
seq_printf(m, " %d: offset=%d pitch=%d, obj: ",
|
||||
i, fb->offsets[i], fb->pitches[i]);
|
||||
msm_gem_describe(msm_fb->planes[i], m);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane)
|
||||
{
|
||||
struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
|
||||
return msm_fb->planes[plane];
|
||||
}
|
||||
|
||||
const struct msm_format *msm_framebuffer_format(struct drm_framebuffer *fb)
|
||||
{
|
||||
struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
|
||||
return msm_fb->format;
|
||||
}
|
||||
|
||||
struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev,
|
||||
struct drm_file *file, struct drm_mode_fb_cmd2 *mode_cmd)
|
||||
{
|
||||
struct drm_gem_object *bos[4] = {0};
|
||||
struct drm_framebuffer *fb;
|
||||
int ret, i, n = drm_format_num_planes(mode_cmd->pixel_format);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
bos[i] = drm_gem_object_lookup(dev, file,
|
||||
mode_cmd->handles[i]);
|
||||
if (!bos[i]) {
|
||||
ret = -ENXIO;
|
||||
goto out_unref;
|
||||
}
|
||||
}
|
||||
|
||||
fb = msm_framebuffer_init(dev, mode_cmd, bos);
|
||||
if (IS_ERR(fb)) {
|
||||
ret = PTR_ERR(fb);
|
||||
goto out_unref;
|
||||
}
|
||||
|
||||
return fb;
|
||||
|
||||
out_unref:
|
||||
for (i = 0; i < n; i++)
|
||||
drm_gem_object_unreference_unlocked(bos[i]);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
|
||||
struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct msm_kms *kms = priv->kms;
|
||||
struct msm_framebuffer *msm_fb;
|
||||
struct drm_framebuffer *fb = NULL;
|
||||
const struct msm_format *format;
|
||||
int ret, i, n;
|
||||
unsigned int hsub, vsub;
|
||||
|
||||
DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)",
|
||||
dev, mode_cmd, mode_cmd->width, mode_cmd->height,
|
||||
(char *)&mode_cmd->pixel_format);
|
||||
|
||||
n = drm_format_num_planes(mode_cmd->pixel_format);
|
||||
hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
|
||||
vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
|
||||
|
||||
format = kms->funcs->get_format(kms, mode_cmd->pixel_format);
|
||||
if (!format) {
|
||||
dev_err(dev->dev, "unsupported pixel format: %4.4s\n",
|
||||
(char *)&mode_cmd->pixel_format);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
msm_fb = kzalloc(sizeof(*msm_fb), GFP_KERNEL);
|
||||
if (!msm_fb) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fb = &msm_fb->base;
|
||||
|
||||
msm_fb->format = format;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
unsigned int width = mode_cmd->width / (i ? hsub : 1);
|
||||
unsigned int height = mode_cmd->height / (i ? vsub : 1);
|
||||
unsigned int min_size;
|
||||
|
||||
min_size = (height - 1) * mode_cmd->pitches[i]
|
||||
+ width * drm_format_plane_cpp(mode_cmd->pixel_format, i)
|
||||
+ mode_cmd->offsets[i];
|
||||
|
||||
if (bos[i]->size < min_size) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
msm_fb->planes[i] = bos[i];
|
||||
}
|
||||
|
||||
drm_helper_mode_fill_fb_struct(fb, mode_cmd);
|
||||
|
||||
ret = drm_framebuffer_init(dev, fb, &msm_framebuffer_funcs);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "framebuffer init failed: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DBG("create: FB ID: %d (%p)", fb->base.id, fb);
|
||||
|
||||
return fb;
|
||||
|
||||
fail:
|
||||
if (fb)
|
||||
msm_framebuffer_destroy(fb);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
|
@ -0,0 +1,258 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "msm_drv.h"
|
||||
|
||||
#include "drm_crtc.h"
|
||||
#include "drm_fb_helper.h"
|
||||
|
||||
/*
|
||||
* fbdev funcs, to implement legacy fbdev interface on top of drm driver
|
||||
*/
|
||||
|
||||
#define to_msm_fbdev(x) container_of(x, struct msm_fbdev, base)
|
||||
|
||||
struct msm_fbdev {
|
||||
struct drm_fb_helper base;
|
||||
struct drm_framebuffer *fb;
|
||||
struct drm_gem_object *bo;
|
||||
};
|
||||
|
||||
static struct fb_ops msm_fb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
|
||||
/* Note: to properly handle manual update displays, we wrap the
|
||||
* basic fbdev ops which write to the framebuffer
|
||||
*/
|
||||
.fb_read = fb_sys_read,
|
||||
.fb_write = fb_sys_write,
|
||||
.fb_fillrect = sys_fillrect,
|
||||
.fb_copyarea = sys_copyarea,
|
||||
.fb_imageblit = sys_imageblit,
|
||||
|
||||
.fb_check_var = drm_fb_helper_check_var,
|
||||
.fb_set_par = drm_fb_helper_set_par,
|
||||
.fb_pan_display = drm_fb_helper_pan_display,
|
||||
.fb_blank = drm_fb_helper_blank,
|
||||
.fb_setcmap = drm_fb_helper_setcmap,
|
||||
};
|
||||
|
||||
static int msm_fbdev_create(struct drm_fb_helper *helper,
|
||||
struct drm_fb_helper_surface_size *sizes)
|
||||
{
|
||||
struct msm_fbdev *fbdev = to_msm_fbdev(helper);
|
||||
struct drm_device *dev = helper->dev;
|
||||
struct drm_framebuffer *fb = NULL;
|
||||
struct fb_info *fbi = NULL;
|
||||
struct drm_mode_fb_cmd2 mode_cmd = {0};
|
||||
dma_addr_t paddr;
|
||||
int ret, size;
|
||||
|
||||
/* only doing ARGB32 since this is what is needed to alpha-blend
|
||||
* with video overlays:
|
||||
*/
|
||||
sizes->surface_bpp = 32;
|
||||
sizes->surface_depth = 32;
|
||||
|
||||
DBG("create fbdev: %dx%d@%d (%dx%d)", sizes->surface_width,
|
||||
sizes->surface_height, sizes->surface_bpp,
|
||||
sizes->fb_width, sizes->fb_height);
|
||||
|
||||
mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
|
||||
sizes->surface_depth);
|
||||
|
||||
mode_cmd.width = sizes->surface_width;
|
||||
mode_cmd.height = sizes->surface_height;
|
||||
|
||||
mode_cmd.pitches[0] = align_pitch(
|
||||
mode_cmd.width, sizes->surface_bpp);
|
||||
|
||||
/* allocate backing bo */
|
||||
size = mode_cmd.pitches[0] * mode_cmd.height;
|
||||
DBG("allocating %d bytes for fb %d", size, dev->primary->index);
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
fbdev->bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
if (IS_ERR(fbdev->bo)) {
|
||||
ret = PTR_ERR(fbdev->bo);
|
||||
fbdev->bo = NULL;
|
||||
dev_err(dev->dev, "failed to allocate buffer object: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fb = msm_framebuffer_init(dev, &mode_cmd, &fbdev->bo);
|
||||
if (IS_ERR(fb)) {
|
||||
dev_err(dev->dev, "failed to allocate fb\n");
|
||||
/* note: if fb creation failed, we can't rely on fb destroy
|
||||
* to unref the bo:
|
||||
*/
|
||||
drm_gem_object_unreference(fbdev->bo);
|
||||
ret = PTR_ERR(fb);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
|
||||
/* TODO implement our own fb_mmap so we don't need this: */
|
||||
msm_gem_get_iova_locked(fbdev->bo, 0, &paddr);
|
||||
|
||||
fbi = framebuffer_alloc(0, dev->dev);
|
||||
if (!fbi) {
|
||||
dev_err(dev->dev, "failed to allocate fb info\n");
|
||||
ret = -ENOMEM;
|
||||
goto fail_unlock;
|
||||
}
|
||||
|
||||
DBG("fbi=%p, dev=%p", fbi, dev);
|
||||
|
||||
fbdev->fb = fb;
|
||||
helper->fb = fb;
|
||||
helper->fbdev = fbi;
|
||||
|
||||
fbi->par = helper;
|
||||
fbi->flags = FBINFO_DEFAULT;
|
||||
fbi->fbops = &msm_fb_ops;
|
||||
|
||||
strcpy(fbi->fix.id, "msm");
|
||||
|
||||
ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
|
||||
if (ret) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_unlock;
|
||||
}
|
||||
|
||||
drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
|
||||
drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height);
|
||||
|
||||
dev->mode_config.fb_base = paddr;
|
||||
|
||||
fbi->screen_base = msm_gem_vaddr_locked(fbdev->bo);
|
||||
fbi->screen_size = fbdev->bo->size;
|
||||
fbi->fix.smem_start = paddr;
|
||||
fbi->fix.smem_len = fbdev->bo->size;
|
||||
|
||||
DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres);
|
||||
DBG("allocated %dx%d fb", fbdev->fb->width, fbdev->fb->height);
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
return 0;
|
||||
|
||||
fail_unlock:
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
fail:
|
||||
|
||||
if (ret) {
|
||||
if (fbi)
|
||||
framebuffer_release(fbi);
|
||||
if (fb) {
|
||||
drm_framebuffer_unregister_private(fb);
|
||||
drm_framebuffer_remove(fb);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void msm_crtc_fb_gamma_set(struct drm_crtc *crtc,
|
||||
u16 red, u16 green, u16 blue, int regno)
|
||||
{
|
||||
DBG("fbdev: set gamma");
|
||||
}
|
||||
|
||||
static void msm_crtc_fb_gamma_get(struct drm_crtc *crtc,
|
||||
u16 *red, u16 *green, u16 *blue, int regno)
|
||||
{
|
||||
DBG("fbdev: get gamma");
|
||||
}
|
||||
|
||||
static struct drm_fb_helper_funcs msm_fb_helper_funcs = {
|
||||
.gamma_set = msm_crtc_fb_gamma_set,
|
||||
.gamma_get = msm_crtc_fb_gamma_get,
|
||||
.fb_probe = msm_fbdev_create,
|
||||
};
|
||||
|
||||
/* initialize fbdev helper */
|
||||
struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct msm_fbdev *fbdev = NULL;
|
||||
struct drm_fb_helper *helper;
|
||||
int ret = 0;
|
||||
|
||||
fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
|
||||
if (!fbdev)
|
||||
goto fail;
|
||||
|
||||
helper = &fbdev->base;
|
||||
|
||||
helper->funcs = &msm_fb_helper_funcs;
|
||||
|
||||
ret = drm_fb_helper_init(dev, helper,
|
||||
priv->num_crtcs, priv->num_connectors);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "could not init fbdev: ret=%d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
drm_fb_helper_single_add_all_connectors(helper);
|
||||
|
||||
/* disable all the possible outputs/crtcs before entering KMS mode */
|
||||
drm_helper_disable_unused_functions(dev);
|
||||
|
||||
drm_fb_helper_initial_config(helper, 32);
|
||||
|
||||
priv->fbdev = helper;
|
||||
|
||||
return helper;
|
||||
|
||||
fail:
|
||||
kfree(fbdev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void msm_fbdev_free(struct drm_device *dev)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct drm_fb_helper *helper = priv->fbdev;
|
||||
struct msm_fbdev *fbdev;
|
||||
struct fb_info *fbi;
|
||||
|
||||
DBG();
|
||||
|
||||
fbi = helper->fbdev;
|
||||
|
||||
/* only cleanup framebuffer if it is present */
|
||||
if (fbi) {
|
||||
unregister_framebuffer(fbi);
|
||||
framebuffer_release(fbi);
|
||||
}
|
||||
|
||||
drm_fb_helper_fini(helper);
|
||||
|
||||
fbdev = to_msm_fbdev(priv->fbdev);
|
||||
|
||||
/* this will free the backing object */
|
||||
if (fbdev->fb) {
|
||||
drm_framebuffer_unregister_private(fbdev->fb);
|
||||
drm_framebuffer_remove(fbdev->fb);
|
||||
}
|
||||
|
||||
kfree(fbdev);
|
||||
|
||||
priv->fbdev = NULL;
|
||||
}
|
|
@ -0,0 +1,597 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/shmem_fs.h>
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "msm_gem.h"
|
||||
#include "msm_gpu.h"
|
||||
|
||||
|
||||
/* called with dev->struct_mutex held */
|
||||
static struct page **get_pages(struct drm_gem_object *obj)
|
||||
{
|
||||
struct msm_gem_object *msm_obj = to_msm_bo(obj);
|
||||
|
||||
if (!msm_obj->pages) {
|
||||
struct drm_device *dev = obj->dev;
|
||||
struct page **p = drm_gem_get_pages(obj, 0);
|
||||
int npages = obj->size >> PAGE_SHIFT;
|
||||
|
||||
if (IS_ERR(p)) {
|
||||
dev_err(dev->dev, "could not get pages: %ld\n",
|
||||
PTR_ERR(p));
|
||||
return p;
|
||||
}
|
||||
|
||||
msm_obj->sgt = drm_prime_pages_to_sg(p, npages);
|
||||
if (!msm_obj->sgt) {
|
||||
dev_err(dev->dev, "failed to allocate sgt\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
msm_obj->pages = p;
|
||||
|
||||
/* For non-cached buffers, ensure the new pages are clean
|
||||
* because display controller, GPU, etc. are not coherent:
|
||||
*/
|
||||
if (msm_obj->flags & (MSM_BO_WC|MSM_BO_UNCACHED))
|
||||
dma_map_sg(dev->dev, msm_obj->sgt->sgl,
|
||||
msm_obj->sgt->nents, DMA_BIDIRECTIONAL);
|
||||
}
|
||||
|
||||
return msm_obj->pages;
|
||||
}
|
||||
|
||||
static void put_pages(struct drm_gem_object *obj)
|
||||
{
|
||||
struct msm_gem_object *msm_obj = to_msm_bo(obj);
|
||||
|
||||
if (msm_obj->pages) {
|
||||
/* For non-cached buffers, ensure the new pages are clean
|
||||
* because display controller, GPU, etc. are not coherent:
|
||||
*/
|
||||
if (msm_obj->flags & (MSM_BO_WC|MSM_BO_UNCACHED))
|
||||
dma_unmap_sg(obj->dev->dev, msm_obj->sgt->sgl,
|
||||
msm_obj->sgt->nents, DMA_BIDIRECTIONAL);
|
||||
sg_free_table(msm_obj->sgt);
|
||||
kfree(msm_obj->sgt);
|
||||
|
||||
drm_gem_put_pages(obj, msm_obj->pages, true, false);
|
||||
msm_obj->pages = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int msm_gem_mmap_obj(struct drm_gem_object *obj,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
struct msm_gem_object *msm_obj = to_msm_bo(obj);
|
||||
|
||||
vma->vm_flags &= ~VM_PFNMAP;
|
||||
vma->vm_flags |= VM_MIXEDMAP;
|
||||
|
||||
if (msm_obj->flags & MSM_BO_WC) {
|
||||
vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
|
||||
} else if (msm_obj->flags & MSM_BO_UNCACHED) {
|
||||
vma->vm_page_prot = pgprot_noncached(vm_get_page_prot(vma->vm_flags));
|
||||
} else {
|
||||
/*
|
||||
* Shunt off cached objs to shmem file so they have their own
|
||||
* address_space (so unmap_mapping_range does what we want,
|
||||
* in particular in the case of mmap'd dmabufs)
|
||||
*/
|
||||
fput(vma->vm_file);
|
||||
get_file(obj->filp);
|
||||
vma->vm_pgoff = 0;
|
||||
vma->vm_file = obj->filp;
|
||||
|
||||
vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = drm_gem_mmap(filp, vma);
|
||||
if (ret) {
|
||||
DBG("mmap failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return msm_gem_mmap_obj(vma->vm_private_data, vma);
|
||||
}
|
||||
|
||||
int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
|
||||
{
|
||||
struct drm_gem_object *obj = vma->vm_private_data;
|
||||
struct msm_gem_object *msm_obj = to_msm_bo(obj);
|
||||
struct drm_device *dev = obj->dev;
|
||||
struct page **pages;
|
||||
unsigned long pfn;
|
||||
pgoff_t pgoff;
|
||||
int ret;
|
||||
|
||||
/* Make sure we don't parallel update on a fault, nor move or remove
|
||||
* something from beneath our feet
|
||||
*/
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* make sure we have pages attached now */
|
||||
pages = get_pages(obj);
|
||||
if (IS_ERR(pages)) {
|
||||
ret = PTR_ERR(pages);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* We don't use vmf->pgoff since that has the fake offset: */
|
||||
pgoff = ((unsigned long)vmf->virtual_address -
|
||||
vma->vm_start) >> PAGE_SHIFT;
|
||||
|
||||
pfn = page_to_pfn(msm_obj->pages[pgoff]);
|
||||
|
||||
VERB("Inserting %p pfn %lx, pa %lx", vmf->virtual_address,
|
||||
pfn, pfn << PAGE_SHIFT);
|
||||
|
||||
ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
out:
|
||||
switch (ret) {
|
||||
case -EAGAIN:
|
||||
set_need_resched();
|
||||
case 0:
|
||||
case -ERESTARTSYS:
|
||||
case -EINTR:
|
||||
return VM_FAULT_NOPAGE;
|
||||
case -ENOMEM:
|
||||
return VM_FAULT_OOM;
|
||||
default:
|
||||
return VM_FAULT_SIGBUS;
|
||||
}
|
||||
}
|
||||
|
||||
/** get mmap offset */
|
||||
static uint64_t mmap_offset(struct drm_gem_object *obj)
|
||||
{
|
||||
struct drm_device *dev = obj->dev;
|
||||
int ret;
|
||||
|
||||
WARN_ON(!mutex_is_locked(&dev->struct_mutex));
|
||||
|
||||
/* Make it mmapable */
|
||||
ret = drm_gem_create_mmap_offset(obj);
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "could not allocate mmap offset\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return drm_vma_node_offset_addr(&obj->vma_node);
|
||||
}
|
||||
|
||||
uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj)
|
||||
{
|
||||
uint64_t offset;
|
||||
mutex_lock(&obj->dev->struct_mutex);
|
||||
offset = mmap_offset(obj);
|
||||
mutex_unlock(&obj->dev->struct_mutex);
|
||||
return offset;
|
||||
}
|
||||
|
||||
/* helpers for dealing w/ iommu: */
|
||||
static int map_range(struct iommu_domain *domain, unsigned int iova,
|
||||
struct sg_table *sgt, unsigned int len, int prot)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
unsigned int da = iova;
|
||||
unsigned int i, j;
|
||||
int ret;
|
||||
|
||||
if (!domain || !sgt)
|
||||
return -EINVAL;
|
||||
|
||||
for_each_sg(sgt->sgl, sg, sgt->nents, i) {
|
||||
u32 pa = sg_phys(sg) - sg->offset;
|
||||
size_t bytes = sg->length + sg->offset;
|
||||
|
||||
VERB("map[%d]: %08x %08x(%x)", i, iova, pa, bytes);
|
||||
|
||||
ret = iommu_map(domain, da, pa, bytes, prot);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
da += bytes;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
da = iova;
|
||||
|
||||
for_each_sg(sgt->sgl, sg, i, j) {
|
||||
size_t bytes = sg->length + sg->offset;
|
||||
iommu_unmap(domain, da, bytes);
|
||||
da += bytes;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void unmap_range(struct iommu_domain *domain, unsigned int iova,
|
||||
struct sg_table *sgt, unsigned int len)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
unsigned int da = iova;
|
||||
int i;
|
||||
|
||||
for_each_sg(sgt->sgl, sg, sgt->nents, i) {
|
||||
size_t bytes = sg->length + sg->offset;
|
||||
size_t unmapped;
|
||||
|
||||
unmapped = iommu_unmap(domain, da, bytes);
|
||||
if (unmapped < bytes)
|
||||
break;
|
||||
|
||||
VERB("unmap[%d]: %08x(%x)", i, iova, bytes);
|
||||
|
||||
BUG_ON(!IS_ALIGNED(bytes, PAGE_SIZE));
|
||||
|
||||
da += bytes;
|
||||
}
|
||||
}
|
||||
|
||||
/* should be called under struct_mutex.. although it can be called
|
||||
* from atomic context without struct_mutex to acquire an extra
|
||||
* iova ref if you know one is already held.
|
||||
*
|
||||
* That means when I do eventually need to add support for unpinning
|
||||
* the refcnt counter needs to be atomic_t.
|
||||
*/
|
||||
int msm_gem_get_iova_locked(struct drm_gem_object *obj, int id,
|
||||
uint32_t *iova)
|
||||
{
|
||||
struct msm_gem_object *msm_obj = to_msm_bo(obj);
|
||||
int ret = 0;
|
||||
|
||||
if (!msm_obj->domain[id].iova) {
|
||||
struct msm_drm_private *priv = obj->dev->dev_private;
|
||||
uint32_t offset = (uint32_t)mmap_offset(obj);
|
||||
struct page **pages;
|
||||
pages = get_pages(obj);
|
||||
if (IS_ERR(pages))
|
||||
return PTR_ERR(pages);
|
||||
// XXX ideally we would not map buffers writable when not needed...
|
||||
ret = map_range(priv->iommus[id], offset, msm_obj->sgt,
|
||||
obj->size, IOMMU_READ | IOMMU_WRITE);
|
||||
msm_obj->domain[id].iova = offset;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
*iova = msm_obj->domain[id].iova;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int msm_gem_get_iova(struct drm_gem_object *obj, int id, uint32_t *iova)
|
||||
{
|
||||
int ret;
|
||||
mutex_lock(&obj->dev->struct_mutex);
|
||||
ret = msm_gem_get_iova_locked(obj, id, iova);
|
||||
mutex_unlock(&obj->dev->struct_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void msm_gem_put_iova(struct drm_gem_object *obj, int id)
|
||||
{
|
||||
// XXX TODO ..
|
||||
// NOTE: probably don't need a _locked() version.. we wouldn't
|
||||
// normally unmap here, but instead just mark that it could be
|
||||
// unmapped (if the iova refcnt drops to zero), but then later
|
||||
// if another _get_iova_locked() fails we can start unmapping
|
||||
// things that are no longer needed..
|
||||
}
|
||||
|
||||
int msm_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
|
||||
struct drm_mode_create_dumb *args)
|
||||
{
|
||||
args->pitch = align_pitch(args->width, args->bpp);
|
||||
args->size = PAGE_ALIGN(args->pitch * args->height);
|
||||
return msm_gem_new_handle(dev, file, args->size,
|
||||
MSM_BO_SCANOUT | MSM_BO_WC, &args->handle);
|
||||
}
|
||||
|
||||
int msm_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev,
|
||||
uint32_t handle)
|
||||
{
|
||||
/* No special work needed, drop the reference and see what falls out */
|
||||
return drm_gem_handle_delete(file, handle);
|
||||
}
|
||||
|
||||
int msm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
|
||||
uint32_t handle, uint64_t *offset)
|
||||
{
|
||||
struct drm_gem_object *obj;
|
||||
int ret = 0;
|
||||
|
||||
/* GEM does all our handle to object mapping */
|
||||
obj = drm_gem_object_lookup(dev, file, handle);
|
||||
if (obj == NULL) {
|
||||
ret = -ENOENT;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*offset = msm_gem_mmap_offset(obj);
|
||||
|
||||
drm_gem_object_unreference_unlocked(obj);
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *msm_gem_vaddr_locked(struct drm_gem_object *obj)
|
||||
{
|
||||
struct msm_gem_object *msm_obj = to_msm_bo(obj);
|
||||
WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex));
|
||||
if (!msm_obj->vaddr) {
|
||||
struct page **pages = get_pages(obj);
|
||||
if (IS_ERR(pages))
|
||||
return ERR_CAST(pages);
|
||||
msm_obj->vaddr = vmap(pages, obj->size >> PAGE_SHIFT,
|
||||
VM_MAP, pgprot_writecombine(PAGE_KERNEL));
|
||||
}
|
||||
return msm_obj->vaddr;
|
||||
}
|
||||
|
||||
void *msm_gem_vaddr(struct drm_gem_object *obj)
|
||||
{
|
||||
void *ret;
|
||||
mutex_lock(&obj->dev->struct_mutex);
|
||||
ret = msm_gem_vaddr_locked(obj);
|
||||
mutex_unlock(&obj->dev->struct_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int msm_gem_queue_inactive_work(struct drm_gem_object *obj,
|
||||
struct work_struct *work)
|
||||
{
|
||||
struct drm_device *dev = obj->dev;
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct msm_gem_object *msm_obj = to_msm_bo(obj);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
if (!list_empty(&work->entry)) {
|
||||
ret = -EINVAL;
|
||||
} else if (is_active(msm_obj)) {
|
||||
list_add_tail(&work->entry, &msm_obj->inactive_work);
|
||||
} else {
|
||||
queue_work(priv->wq, work);
|
||||
}
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void msm_gem_move_to_active(struct drm_gem_object *obj,
|
||||
struct msm_gpu *gpu, uint32_t fence)
|
||||
{
|
||||
struct msm_gem_object *msm_obj = to_msm_bo(obj);
|
||||
msm_obj->gpu = gpu;
|
||||
msm_obj->fence = fence;
|
||||
list_del_init(&msm_obj->mm_list);
|
||||
list_add_tail(&msm_obj->mm_list, &gpu->active_list);
|
||||
}
|
||||
|
||||
void msm_gem_move_to_inactive(struct drm_gem_object *obj)
|
||||
{
|
||||
struct drm_device *dev = obj->dev;
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct msm_gem_object *msm_obj = to_msm_bo(obj);
|
||||
|
||||
WARN_ON(!mutex_is_locked(&dev->struct_mutex));
|
||||
|
||||
msm_obj->gpu = NULL;
|
||||
msm_obj->fence = 0;
|
||||
list_del_init(&msm_obj->mm_list);
|
||||
list_add_tail(&msm_obj->mm_list, &priv->inactive_list);
|
||||
|
||||
while (!list_empty(&msm_obj->inactive_work)) {
|
||||
struct work_struct *work;
|
||||
|
||||
work = list_first_entry(&msm_obj->inactive_work,
|
||||
struct work_struct, entry);
|
||||
|
||||
list_del_init(&work->entry);
|
||||
queue_work(priv->wq, work);
|
||||
}
|
||||
}
|
||||
|
||||
int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op,
|
||||
struct timespec *timeout)
|
||||
{
|
||||
struct drm_device *dev = obj->dev;
|
||||
struct msm_gem_object *msm_obj = to_msm_bo(obj);
|
||||
int ret = 0;
|
||||
|
||||
if (is_active(msm_obj) && !(op & MSM_PREP_NOSYNC))
|
||||
ret = msm_wait_fence_interruptable(dev, msm_obj->fence, timeout);
|
||||
|
||||
/* TODO cache maintenance */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int msm_gem_cpu_fini(struct drm_gem_object *obj)
|
||||
{
|
||||
/* TODO cache maintenance */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
|
||||
{
|
||||
struct drm_device *dev = obj->dev;
|
||||
struct msm_gem_object *msm_obj = to_msm_bo(obj);
|
||||
uint64_t off = drm_vma_node_start(&obj->vma_node);
|
||||
|
||||
WARN_ON(!mutex_is_locked(&dev->struct_mutex));
|
||||
seq_printf(m, "%08x: %c(%d) %2d (%2d) %08llx %p %d\n",
|
||||
msm_obj->flags, is_active(msm_obj) ? 'A' : 'I',
|
||||
msm_obj->fence, obj->name, obj->refcount.refcount.counter,
|
||||
off, msm_obj->vaddr, obj->size);
|
||||
}
|
||||
|
||||
void msm_gem_describe_objects(struct list_head *list, struct seq_file *m)
|
||||
{
|
||||
struct msm_gem_object *msm_obj;
|
||||
int count = 0;
|
||||
size_t size = 0;
|
||||
|
||||
list_for_each_entry(msm_obj, list, mm_list) {
|
||||
struct drm_gem_object *obj = &msm_obj->base;
|
||||
seq_printf(m, " ");
|
||||
msm_gem_describe(obj, m);
|
||||
count++;
|
||||
size += obj->size;
|
||||
}
|
||||
|
||||
seq_printf(m, "Total %d objects, %zu bytes\n", count, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
void msm_gem_free_object(struct drm_gem_object *obj)
|
||||
{
|
||||
struct drm_device *dev = obj->dev;
|
||||
struct msm_gem_object *msm_obj = to_msm_bo(obj);
|
||||
int id;
|
||||
|
||||
WARN_ON(!mutex_is_locked(&dev->struct_mutex));
|
||||
|
||||
/* object should not be on active list: */
|
||||
WARN_ON(is_active(msm_obj));
|
||||
|
||||
list_del(&msm_obj->mm_list);
|
||||
|
||||
for (id = 0; id < ARRAY_SIZE(msm_obj->domain); id++) {
|
||||
if (msm_obj->domain[id].iova) {
|
||||
struct msm_drm_private *priv = obj->dev->dev_private;
|
||||
uint32_t offset = (uint32_t)mmap_offset(obj);
|
||||
unmap_range(priv->iommus[id], offset,
|
||||
msm_obj->sgt, obj->size);
|
||||
}
|
||||
}
|
||||
|
||||
drm_gem_free_mmap_offset(obj);
|
||||
|
||||
if (msm_obj->vaddr)
|
||||
vunmap(msm_obj->vaddr);
|
||||
|
||||
put_pages(obj);
|
||||
|
||||
if (msm_obj->resv == &msm_obj->_resv)
|
||||
reservation_object_fini(msm_obj->resv);
|
||||
|
||||
drm_gem_object_release(obj);
|
||||
|
||||
kfree(msm_obj);
|
||||
}
|
||||
|
||||
/* convenience method to construct a GEM buffer object, and userspace handle */
|
||||
int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file,
|
||||
uint32_t size, uint32_t flags, uint32_t *handle)
|
||||
{
|
||||
struct drm_gem_object *obj;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
obj = msm_gem_new(dev, size, flags);
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
if (IS_ERR(obj))
|
||||
return PTR_ERR(obj);
|
||||
|
||||
ret = drm_gem_handle_create(file, obj, handle);
|
||||
|
||||
/* drop reference from allocate - handle holds it now */
|
||||
drm_gem_object_unreference_unlocked(obj);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct drm_gem_object *msm_gem_new(struct drm_device *dev,
|
||||
uint32_t size, uint32_t flags)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct msm_gem_object *msm_obj;
|
||||
struct drm_gem_object *obj = NULL;
|
||||
int ret;
|
||||
|
||||
WARN_ON(!mutex_is_locked(&dev->struct_mutex));
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
|
||||
switch (flags & MSM_BO_CACHE_MASK) {
|
||||
case MSM_BO_UNCACHED:
|
||||
case MSM_BO_CACHED:
|
||||
case MSM_BO_WC:
|
||||
break;
|
||||
default:
|
||||
dev_err(dev->dev, "invalid cache flag: %x\n",
|
||||
(flags & MSM_BO_CACHE_MASK));
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
msm_obj = kzalloc(sizeof(*msm_obj), GFP_KERNEL);
|
||||
if (!msm_obj) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
obj = &msm_obj->base;
|
||||
|
||||
ret = drm_gem_object_init(dev, obj, size);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
msm_obj->flags = flags;
|
||||
|
||||
msm_obj->resv = &msm_obj->_resv;
|
||||
reservation_object_init(msm_obj->resv);
|
||||
|
||||
INIT_LIST_HEAD(&msm_obj->submit_entry);
|
||||
INIT_LIST_HEAD(&msm_obj->inactive_work);
|
||||
list_add_tail(&msm_obj->mm_list, &priv->inactive_list);
|
||||
|
||||
return obj;
|
||||
|
||||
fail:
|
||||
if (obj)
|
||||
drm_gem_object_unreference_unlocked(obj);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MSM_GEM_H__
|
||||
#define __MSM_GEM_H__
|
||||
|
||||
#include <linux/reservation.h>
|
||||
#include "msm_drv.h"
|
||||
|
||||
struct msm_gem_object {
|
||||
struct drm_gem_object base;
|
||||
|
||||
uint32_t flags;
|
||||
|
||||
/* And object is either:
|
||||
* inactive - on priv->inactive_list
|
||||
* active - on one one of the gpu's active_list.. well, at
|
||||
* least for now we don't have (I don't think) hw sync between
|
||||
* 2d and 3d one devices which have both, meaning we need to
|
||||
* block on submit if a bo is already on other ring
|
||||
*
|
||||
*/
|
||||
struct list_head mm_list;
|
||||
struct msm_gpu *gpu; /* non-null if active */
|
||||
uint32_t fence;
|
||||
|
||||
/* Transiently in the process of submit ioctl, objects associated
|
||||
* with the submit are on submit->bo_list.. this only lasts for
|
||||
* the duration of the ioctl, so one bo can never be on multiple
|
||||
* submit lists.
|
||||
*/
|
||||
struct list_head submit_entry;
|
||||
|
||||
/* work defered until bo is inactive: */
|
||||
struct list_head inactive_work;
|
||||
|
||||
struct page **pages;
|
||||
struct sg_table *sgt;
|
||||
void *vaddr;
|
||||
|
||||
struct {
|
||||
// XXX
|
||||
uint32_t iova;
|
||||
} domain[NUM_DOMAINS];
|
||||
|
||||
/* normally (resv == &_resv) except for imported bo's */
|
||||
struct reservation_object *resv;
|
||||
struct reservation_object _resv;
|
||||
};
|
||||
#define to_msm_bo(x) container_of(x, struct msm_gem_object, base)
|
||||
|
||||
static inline bool is_active(struct msm_gem_object *msm_obj)
|
||||
{
|
||||
return msm_obj->gpu != NULL;
|
||||
}
|
||||
|
||||
#define MAX_CMDS 4
|
||||
|
||||
/* Created per submit-ioctl, to track bo's and cmdstream bufs, etc,
|
||||
* associated with the cmdstream submission for synchronization (and
|
||||
* make it easier to unwind when things go wrong, etc). This only
|
||||
* lasts for the duration of the submit-ioctl.
|
||||
*/
|
||||
struct msm_gem_submit {
|
||||
struct drm_device *dev;
|
||||
struct msm_gpu *gpu;
|
||||
struct list_head bo_list;
|
||||
struct ww_acquire_ctx ticket;
|
||||
uint32_t fence;
|
||||
bool valid;
|
||||
unsigned int nr_cmds;
|
||||
unsigned int nr_bos;
|
||||
struct {
|
||||
uint32_t type;
|
||||
uint32_t size; /* in dwords */
|
||||
uint32_t iova;
|
||||
} cmd[MAX_CMDS];
|
||||
struct {
|
||||
uint32_t flags;
|
||||
struct msm_gem_object *obj;
|
||||
uint32_t iova;
|
||||
} bos[0];
|
||||
};
|
||||
|
||||
#endif /* __MSM_GEM_H__ */
|
|
@ -0,0 +1,412 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "msm_gpu.h"
|
||||
#include "msm_gem.h"
|
||||
|
||||
/*
|
||||
* Cmdstream submission:
|
||||
*/
|
||||
|
||||
#define BO_INVALID_FLAGS ~(MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE)
|
||||
/* make sure these don't conflict w/ MSM_SUBMIT_BO_x */
|
||||
#define BO_VALID 0x8000
|
||||
#define BO_LOCKED 0x4000
|
||||
#define BO_PINNED 0x2000
|
||||
|
||||
static inline void __user *to_user_ptr(u64 address)
|
||||
{
|
||||
return (void __user *)(uintptr_t)address;
|
||||
}
|
||||
|
||||
static struct msm_gem_submit *submit_create(struct drm_device *dev,
|
||||
struct msm_gpu *gpu, int nr)
|
||||
{
|
||||
struct msm_gem_submit *submit;
|
||||
int sz = sizeof(*submit) + (nr * sizeof(submit->bos[0]));
|
||||
|
||||
submit = kmalloc(sz, GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY);
|
||||
if (submit) {
|
||||
submit->dev = dev;
|
||||
submit->gpu = gpu;
|
||||
|
||||
/* initially, until copy_from_user() and bo lookup succeeds: */
|
||||
submit->nr_bos = 0;
|
||||
submit->nr_cmds = 0;
|
||||
|
||||
INIT_LIST_HEAD(&submit->bo_list);
|
||||
ww_acquire_init(&submit->ticket, &reservation_ww_class);
|
||||
}
|
||||
|
||||
return submit;
|
||||
}
|
||||
|
||||
static int submit_lookup_objects(struct msm_gem_submit *submit,
|
||||
struct drm_msm_gem_submit *args, struct drm_file *file)
|
||||
{
|
||||
unsigned i;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock(&file->table_lock);
|
||||
|
||||
for (i = 0; i < args->nr_bos; i++) {
|
||||
struct drm_msm_gem_submit_bo submit_bo;
|
||||
struct drm_gem_object *obj;
|
||||
struct msm_gem_object *msm_obj;
|
||||
void __user *userptr =
|
||||
to_user_ptr(args->bos + (i * sizeof(submit_bo)));
|
||||
|
||||
ret = copy_from_user(&submit_bo, userptr, sizeof(submit_bo));
|
||||
if (ret) {
|
||||
ret = -EFAULT;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (submit_bo.flags & BO_INVALID_FLAGS) {
|
||||
DBG("invalid flags: %x", submit_bo.flags);
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
submit->bos[i].flags = submit_bo.flags;
|
||||
/* in validate_objects() we figure out if this is true: */
|
||||
submit->bos[i].iova = submit_bo.presumed;
|
||||
|
||||
/* normally use drm_gem_object_lookup(), but for bulk lookup
|
||||
* all under single table_lock just hit object_idr directly:
|
||||
*/
|
||||
obj = idr_find(&file->object_idr, submit_bo.handle);
|
||||
if (!obj) {
|
||||
DBG("invalid handle %u at index %u", submit_bo.handle, i);
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
msm_obj = to_msm_bo(obj);
|
||||
|
||||
if (!list_empty(&msm_obj->submit_entry)) {
|
||||
DBG("handle %u at index %u already on submit list",
|
||||
submit_bo.handle, i);
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
drm_gem_object_reference(obj);
|
||||
|
||||
submit->bos[i].obj = msm_obj;
|
||||
|
||||
list_add_tail(&msm_obj->submit_entry, &submit->bo_list);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
submit->nr_bos = i;
|
||||
spin_unlock(&file->table_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void submit_unlock_unpin_bo(struct msm_gem_submit *submit, int i)
|
||||
{
|
||||
struct msm_gem_object *msm_obj = submit->bos[i].obj;
|
||||
|
||||
if (submit->bos[i].flags & BO_PINNED)
|
||||
msm_gem_put_iova(&msm_obj->base, submit->gpu->id);
|
||||
|
||||
if (submit->bos[i].flags & BO_LOCKED)
|
||||
ww_mutex_unlock(&msm_obj->resv->lock);
|
||||
|
||||
if (!(submit->bos[i].flags & BO_VALID))
|
||||
submit->bos[i].iova = 0;
|
||||
|
||||
submit->bos[i].flags &= ~(BO_LOCKED | BO_PINNED);
|
||||
}
|
||||
|
||||
/* This is where we make sure all the bo's are reserved and pin'd: */
|
||||
static int submit_validate_objects(struct msm_gem_submit *submit)
|
||||
{
|
||||
int contended, slow_locked = -1, i, ret = 0;
|
||||
|
||||
retry:
|
||||
submit->valid = true;
|
||||
|
||||
for (i = 0; i < submit->nr_bos; i++) {
|
||||
struct msm_gem_object *msm_obj = submit->bos[i].obj;
|
||||
uint32_t iova;
|
||||
|
||||
if (slow_locked == i)
|
||||
slow_locked = -1;
|
||||
|
||||
contended = i;
|
||||
|
||||
if (!(submit->bos[i].flags & BO_LOCKED)) {
|
||||
ret = ww_mutex_lock_interruptible(&msm_obj->resv->lock,
|
||||
&submit->ticket);
|
||||
if (ret)
|
||||
goto fail;
|
||||
submit->bos[i].flags |= BO_LOCKED;
|
||||
}
|
||||
|
||||
|
||||
/* if locking succeeded, pin bo: */
|
||||
ret = msm_gem_get_iova(&msm_obj->base,
|
||||
submit->gpu->id, &iova);
|
||||
|
||||
/* this would break the logic in the fail path.. there is no
|
||||
* reason for this to happen, but just to be on the safe side
|
||||
* let's notice if this starts happening in the future:
|
||||
*/
|
||||
WARN_ON(ret == -EDEADLK);
|
||||
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
submit->bos[i].flags |= BO_PINNED;
|
||||
|
||||
if (iova == submit->bos[i].iova) {
|
||||
submit->bos[i].flags |= BO_VALID;
|
||||
} else {
|
||||
submit->bos[i].iova = iova;
|
||||
submit->bos[i].flags &= ~BO_VALID;
|
||||
submit->valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
ww_acquire_done(&submit->ticket);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
for (; i >= 0; i--)
|
||||
submit_unlock_unpin_bo(submit, i);
|
||||
|
||||
if (slow_locked > 0)
|
||||
submit_unlock_unpin_bo(submit, slow_locked);
|
||||
|
||||
if (ret == -EDEADLK) {
|
||||
struct msm_gem_object *msm_obj = submit->bos[contended].obj;
|
||||
/* we lost out in a seqno race, lock and retry.. */
|
||||
ret = ww_mutex_lock_slow_interruptible(&msm_obj->resv->lock,
|
||||
&submit->ticket);
|
||||
if (!ret) {
|
||||
submit->bos[contended].flags |= BO_LOCKED;
|
||||
slow_locked = contended;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int submit_bo(struct msm_gem_submit *submit, uint32_t idx,
|
||||
struct msm_gem_object **obj, uint32_t *iova, bool *valid)
|
||||
{
|
||||
if (idx >= submit->nr_bos) {
|
||||
DBG("invalid buffer index: %u (out of %u)", idx, submit->nr_bos);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (obj)
|
||||
*obj = submit->bos[idx].obj;
|
||||
if (iova)
|
||||
*iova = submit->bos[idx].iova;
|
||||
if (valid)
|
||||
*valid = !!(submit->bos[idx].flags & BO_VALID);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* process the reloc's and patch up the cmdstream as needed: */
|
||||
static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *obj,
|
||||
uint32_t offset, uint32_t nr_relocs, uint64_t relocs)
|
||||
{
|
||||
uint32_t i, last_offset = 0;
|
||||
uint32_t *ptr;
|
||||
int ret;
|
||||
|
||||
if (offset % 4) {
|
||||
DBG("non-aligned cmdstream buffer: %u", offset);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* For now, just map the entire thing. Eventually we probably
|
||||
* to do it page-by-page, w/ kmap() if not vmap()d..
|
||||
*/
|
||||
ptr = msm_gem_vaddr(&obj->base);
|
||||
|
||||
if (IS_ERR(ptr)) {
|
||||
ret = PTR_ERR(ptr);
|
||||
DBG("failed to map: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_relocs; i++) {
|
||||
struct drm_msm_gem_submit_reloc submit_reloc;
|
||||
void __user *userptr =
|
||||
to_user_ptr(relocs + (i * sizeof(submit_reloc)));
|
||||
uint32_t iova, off;
|
||||
bool valid;
|
||||
|
||||
ret = copy_from_user(&submit_reloc, userptr, sizeof(submit_reloc));
|
||||
if (ret)
|
||||
return -EFAULT;
|
||||
|
||||
if (submit_reloc.submit_offset % 4) {
|
||||
DBG("non-aligned reloc offset: %u",
|
||||
submit_reloc.submit_offset);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* offset in dwords: */
|
||||
off = submit_reloc.submit_offset / 4;
|
||||
|
||||
if ((off >= (obj->base.size / 4)) ||
|
||||
(off < last_offset)) {
|
||||
DBG("invalid offset %u at reloc %u", off, i);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = submit_bo(submit, submit_reloc.reloc_idx, NULL, &iova, &valid);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (valid)
|
||||
continue;
|
||||
|
||||
iova += submit_reloc.reloc_offset;
|
||||
|
||||
if (submit_reloc.shift < 0)
|
||||
iova >>= -submit_reloc.shift;
|
||||
else
|
||||
iova <<= submit_reloc.shift;
|
||||
|
||||
ptr[off] = iova | submit_reloc.or;
|
||||
|
||||
last_offset = off;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void submit_cleanup(struct msm_gem_submit *submit, bool fail)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
mutex_lock(&submit->dev->struct_mutex);
|
||||
for (i = 0; i < submit->nr_bos; i++) {
|
||||
struct msm_gem_object *msm_obj = submit->bos[i].obj;
|
||||
submit_unlock_unpin_bo(submit, i);
|
||||
list_del_init(&msm_obj->submit_entry);
|
||||
drm_gem_object_unreference(&msm_obj->base);
|
||||
}
|
||||
mutex_unlock(&submit->dev->struct_mutex);
|
||||
|
||||
ww_acquire_fini(&submit->ticket);
|
||||
kfree(submit);
|
||||
}
|
||||
|
||||
int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
struct drm_msm_gem_submit *args = data;
|
||||
struct msm_file_private *ctx = file->driver_priv;
|
||||
struct msm_gem_submit *submit;
|
||||
struct msm_gpu *gpu;
|
||||
unsigned i;
|
||||
int ret;
|
||||
|
||||
/* for now, we just have 3d pipe.. eventually this would need to
|
||||
* be more clever to dispatch to appropriate gpu module:
|
||||
*/
|
||||
if (args->pipe != MSM_PIPE_3D0)
|
||||
return -EINVAL;
|
||||
|
||||
gpu = priv->gpu;
|
||||
|
||||
if (args->nr_cmds > MAX_CMDS)
|
||||
return -EINVAL;
|
||||
|
||||
submit = submit_create(dev, gpu, args->nr_bos);
|
||||
if (!submit) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = submit_lookup_objects(submit, args, file);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = submit_validate_objects(submit);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < args->nr_cmds; i++) {
|
||||
struct drm_msm_gem_submit_cmd submit_cmd;
|
||||
void __user *userptr =
|
||||
to_user_ptr(args->cmds + (i * sizeof(submit_cmd)));
|
||||
struct msm_gem_object *msm_obj;
|
||||
uint32_t iova;
|
||||
|
||||
ret = copy_from_user(&submit_cmd, userptr, sizeof(submit_cmd));
|
||||
if (ret) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = submit_bo(submit, submit_cmd.submit_idx,
|
||||
&msm_obj, &iova, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (submit_cmd.size % 4) {
|
||||
DBG("non-aligned cmdstream buffer size: %u",
|
||||
submit_cmd.size);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (submit_cmd.size >= msm_obj->base.size) {
|
||||
DBG("invalid cmdstream size: %u", submit_cmd.size);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
submit->cmd[i].type = submit_cmd.type;
|
||||
submit->cmd[i].size = submit_cmd.size / 4;
|
||||
submit->cmd[i].iova = iova + submit_cmd.submit_offset;
|
||||
|
||||
if (submit->valid)
|
||||
continue;
|
||||
|
||||
ret = submit_reloc(submit, msm_obj, submit_cmd.submit_offset,
|
||||
submit_cmd.nr_relocs, submit_cmd.relocs);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
submit->nr_cmds = i;
|
||||
|
||||
ret = msm_gpu_submit(gpu, submit, ctx);
|
||||
|
||||
args->fence = submit->fence;
|
||||
|
||||
out:
|
||||
if (submit)
|
||||
submit_cleanup(submit, !!ret);
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,463 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "msm_gpu.h"
|
||||
#include "msm_gem.h"
|
||||
|
||||
|
||||
/*
|
||||
* Power Management:
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_MSM_BUS_SCALING
|
||||
#include <mach/board.h>
|
||||
#include <mach/kgsl.h>
|
||||
static void bs_init(struct msm_gpu *gpu, struct platform_device *pdev)
|
||||
{
|
||||
struct drm_device *dev = gpu->dev;
|
||||
struct kgsl_device_platform_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
if (!pdev) {
|
||||
dev_err(dev->dev, "could not find dtv pdata\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pdata->bus_scale_table) {
|
||||
gpu->bsc = msm_bus_scale_register_client(pdata->bus_scale_table);
|
||||
DBG("bus scale client: %08x", gpu->bsc);
|
||||
}
|
||||
}
|
||||
|
||||
static void bs_fini(struct msm_gpu *gpu)
|
||||
{
|
||||
if (gpu->bsc) {
|
||||
msm_bus_scale_unregister_client(gpu->bsc);
|
||||
gpu->bsc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void bs_set(struct msm_gpu *gpu, int idx)
|
||||
{
|
||||
if (gpu->bsc) {
|
||||
DBG("set bus scaling: %d", idx);
|
||||
msm_bus_scale_client_update_request(gpu->bsc, idx);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void bs_init(struct msm_gpu *gpu, struct platform_device *pdev) {}
|
||||
static void bs_fini(struct msm_gpu *gpu) {}
|
||||
static void bs_set(struct msm_gpu *gpu, int idx) {}
|
||||
#endif
|
||||
|
||||
static int enable_pwrrail(struct msm_gpu *gpu)
|
||||
{
|
||||
struct drm_device *dev = gpu->dev;
|
||||
int ret = 0;
|
||||
|
||||
if (gpu->gpu_reg) {
|
||||
ret = regulator_enable(gpu->gpu_reg);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to enable 'gpu_reg': %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (gpu->gpu_cx) {
|
||||
ret = regulator_enable(gpu->gpu_cx);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to enable 'gpu_cx': %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int disable_pwrrail(struct msm_gpu *gpu)
|
||||
{
|
||||
if (gpu->gpu_cx)
|
||||
regulator_disable(gpu->gpu_cx);
|
||||
if (gpu->gpu_reg)
|
||||
regulator_disable(gpu->gpu_reg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enable_clk(struct msm_gpu *gpu)
|
||||
{
|
||||
struct clk *rate_clk = NULL;
|
||||
int i;
|
||||
|
||||
/* NOTE: kgsl_pwrctrl_clk() ignores grp_clks[0].. */
|
||||
for (i = ARRAY_SIZE(gpu->grp_clks) - 1; i > 0; i--) {
|
||||
if (gpu->grp_clks[i]) {
|
||||
clk_prepare(gpu->grp_clks[i]);
|
||||
rate_clk = gpu->grp_clks[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (rate_clk && gpu->fast_rate)
|
||||
clk_set_rate(rate_clk, gpu->fast_rate);
|
||||
|
||||
for (i = ARRAY_SIZE(gpu->grp_clks) - 1; i > 0; i--)
|
||||
if (gpu->grp_clks[i])
|
||||
clk_enable(gpu->grp_clks[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int disable_clk(struct msm_gpu *gpu)
|
||||
{
|
||||
struct clk *rate_clk = NULL;
|
||||
int i;
|
||||
|
||||
/* NOTE: kgsl_pwrctrl_clk() ignores grp_clks[0].. */
|
||||
for (i = ARRAY_SIZE(gpu->grp_clks) - 1; i > 0; i--) {
|
||||
if (gpu->grp_clks[i]) {
|
||||
clk_disable(gpu->grp_clks[i]);
|
||||
rate_clk = gpu->grp_clks[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (rate_clk && gpu->slow_rate)
|
||||
clk_set_rate(rate_clk, gpu->slow_rate);
|
||||
|
||||
for (i = ARRAY_SIZE(gpu->grp_clks) - 1; i > 0; i--)
|
||||
if (gpu->grp_clks[i])
|
||||
clk_unprepare(gpu->grp_clks[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enable_axi(struct msm_gpu *gpu)
|
||||
{
|
||||
if (gpu->ebi1_clk)
|
||||
clk_prepare_enable(gpu->ebi1_clk);
|
||||
if (gpu->bus_freq)
|
||||
bs_set(gpu, gpu->bus_freq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int disable_axi(struct msm_gpu *gpu)
|
||||
{
|
||||
if (gpu->ebi1_clk)
|
||||
clk_disable_unprepare(gpu->ebi1_clk);
|
||||
if (gpu->bus_freq)
|
||||
bs_set(gpu, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msm_gpu_pm_resume(struct msm_gpu *gpu)
|
||||
{
|
||||
int ret;
|
||||
|
||||
DBG("%s", gpu->name);
|
||||
|
||||
ret = enable_pwrrail(gpu);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = enable_clk(gpu);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = enable_axi(gpu);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msm_gpu_pm_suspend(struct msm_gpu *gpu)
|
||||
{
|
||||
int ret;
|
||||
|
||||
DBG("%s", gpu->name);
|
||||
|
||||
ret = disable_axi(gpu);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = disable_clk(gpu);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = disable_pwrrail(gpu);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hangcheck detection for locked gpu:
|
||||
*/
|
||||
|
||||
static void recover_worker(struct work_struct *work)
|
||||
{
|
||||
struct msm_gpu *gpu = container_of(work, struct msm_gpu, recover_work);
|
||||
struct drm_device *dev = gpu->dev;
|
||||
|
||||
dev_err(dev->dev, "%s: hangcheck recover!\n", gpu->name);
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
gpu->funcs->recover(gpu);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
msm_gpu_retire(gpu);
|
||||
}
|
||||
|
||||
static void hangcheck_timer_reset(struct msm_gpu *gpu)
|
||||
{
|
||||
DBG("%s", gpu->name);
|
||||
mod_timer(&gpu->hangcheck_timer,
|
||||
round_jiffies_up(jiffies + DRM_MSM_HANGCHECK_JIFFIES));
|
||||
}
|
||||
|
||||
static void hangcheck_handler(unsigned long data)
|
||||
{
|
||||
struct msm_gpu *gpu = (struct msm_gpu *)data;
|
||||
uint32_t fence = gpu->funcs->last_fence(gpu);
|
||||
|
||||
if (fence != gpu->hangcheck_fence) {
|
||||
/* some progress has been made.. ya! */
|
||||
gpu->hangcheck_fence = fence;
|
||||
} else if (fence < gpu->submitted_fence) {
|
||||
/* no progress and not done.. hung! */
|
||||
struct msm_drm_private *priv = gpu->dev->dev_private;
|
||||
gpu->hangcheck_fence = fence;
|
||||
queue_work(priv->wq, &gpu->recover_work);
|
||||
}
|
||||
|
||||
/* if still more pending work, reset the hangcheck timer: */
|
||||
if (gpu->submitted_fence > gpu->hangcheck_fence)
|
||||
hangcheck_timer_reset(gpu);
|
||||
}
|
||||
|
||||
/*
|
||||
* Cmdstream submission/retirement:
|
||||
*/
|
||||
|
||||
static void retire_worker(struct work_struct *work)
|
||||
{
|
||||
struct msm_gpu *gpu = container_of(work, struct msm_gpu, retire_work);
|
||||
struct drm_device *dev = gpu->dev;
|
||||
uint32_t fence = gpu->funcs->last_fence(gpu);
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
|
||||
while (!list_empty(&gpu->active_list)) {
|
||||
struct msm_gem_object *obj;
|
||||
|
||||
obj = list_first_entry(&gpu->active_list,
|
||||
struct msm_gem_object, mm_list);
|
||||
|
||||
if (obj->fence <= fence) {
|
||||
/* move to inactive: */
|
||||
msm_gem_move_to_inactive(&obj->base);
|
||||
msm_gem_put_iova(&obj->base, gpu->id);
|
||||
drm_gem_object_unreference(&obj->base);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
msm_update_fence(gpu->dev, fence);
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
}
|
||||
|
||||
/* call from irq handler to schedule work to retire bo's */
|
||||
void msm_gpu_retire(struct msm_gpu *gpu)
|
||||
{
|
||||
struct msm_drm_private *priv = gpu->dev->dev_private;
|
||||
queue_work(priv->wq, &gpu->retire_work);
|
||||
}
|
||||
|
||||
/* add bo's to gpu's ring, and kick gpu: */
|
||||
int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
|
||||
struct msm_file_private *ctx)
|
||||
{
|
||||
struct drm_device *dev = gpu->dev;
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
int i, ret;
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
|
||||
submit->fence = ++priv->next_fence;
|
||||
|
||||
gpu->submitted_fence = submit->fence;
|
||||
|
||||
ret = gpu->funcs->submit(gpu, submit, ctx);
|
||||
priv->lastctx = ctx;
|
||||
|
||||
for (i = 0; i < submit->nr_bos; i++) {
|
||||
struct msm_gem_object *msm_obj = submit->bos[i].obj;
|
||||
|
||||
/* can't happen yet.. but when we add 2d support we'll have
|
||||
* to deal w/ cross-ring synchronization:
|
||||
*/
|
||||
WARN_ON(is_active(msm_obj) && (msm_obj->gpu != gpu));
|
||||
|
||||
if (!is_active(msm_obj)) {
|
||||
uint32_t iova;
|
||||
|
||||
/* ring takes a reference to the bo and iova: */
|
||||
drm_gem_object_reference(&msm_obj->base);
|
||||
msm_gem_get_iova_locked(&msm_obj->base,
|
||||
submit->gpu->id, &iova);
|
||||
}
|
||||
|
||||
msm_gem_move_to_active(&msm_obj->base, gpu, submit->fence);
|
||||
}
|
||||
hangcheck_timer_reset(gpu);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Init/Cleanup:
|
||||
*/
|
||||
|
||||
static irqreturn_t irq_handler(int irq, void *data)
|
||||
{
|
||||
struct msm_gpu *gpu = data;
|
||||
return gpu->funcs->irq(gpu);
|
||||
}
|
||||
|
||||
static const char *clk_names[] = {
|
||||
"src_clk", "core_clk", "iface_clk", "mem_clk", "mem_iface_clk",
|
||||
};
|
||||
|
||||
int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
|
||||
struct msm_gpu *gpu, const struct msm_gpu_funcs *funcs,
|
||||
const char *name, const char *ioname, const char *irqname, int ringsz)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
gpu->dev = drm;
|
||||
gpu->funcs = funcs;
|
||||
gpu->name = name;
|
||||
|
||||
INIT_LIST_HEAD(&gpu->active_list);
|
||||
INIT_WORK(&gpu->retire_work, retire_worker);
|
||||
INIT_WORK(&gpu->recover_work, recover_worker);
|
||||
|
||||
setup_timer(&gpu->hangcheck_timer, hangcheck_handler,
|
||||
(unsigned long)gpu);
|
||||
|
||||
BUG_ON(ARRAY_SIZE(clk_names) != ARRAY_SIZE(gpu->grp_clks));
|
||||
|
||||
/* Map registers: */
|
||||
gpu->mmio = msm_ioremap(pdev, ioname, name);
|
||||
if (IS_ERR(gpu->mmio)) {
|
||||
ret = PTR_ERR(gpu->mmio);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Get Interrupt: */
|
||||
gpu->irq = platform_get_irq_byname(pdev, irqname);
|
||||
if (gpu->irq < 0) {
|
||||
ret = gpu->irq;
|
||||
dev_err(drm->dev, "failed to get irq: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, gpu->irq, irq_handler,
|
||||
IRQF_TRIGGER_HIGH, gpu->name, gpu);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "failed to request IRQ%u: %d\n", gpu->irq, ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Acquire clocks: */
|
||||
for (i = 0; i < ARRAY_SIZE(clk_names); i++) {
|
||||
gpu->grp_clks[i] = devm_clk_get(&pdev->dev, clk_names[i]);
|
||||
DBG("grp_clks[%s]: %p", clk_names[i], gpu->grp_clks[i]);
|
||||
if (IS_ERR(gpu->grp_clks[i]))
|
||||
gpu->grp_clks[i] = NULL;
|
||||
}
|
||||
|
||||
gpu->ebi1_clk = devm_clk_get(&pdev->dev, "bus_clk");
|
||||
DBG("ebi1_clk: %p", gpu->ebi1_clk);
|
||||
if (IS_ERR(gpu->ebi1_clk))
|
||||
gpu->ebi1_clk = NULL;
|
||||
|
||||
/* Acquire regulators: */
|
||||
gpu->gpu_reg = devm_regulator_get(&pdev->dev, "vdd");
|
||||
DBG("gpu_reg: %p", gpu->gpu_reg);
|
||||
if (IS_ERR(gpu->gpu_reg))
|
||||
gpu->gpu_reg = NULL;
|
||||
|
||||
gpu->gpu_cx = devm_regulator_get(&pdev->dev, "vddcx");
|
||||
DBG("gpu_cx: %p", gpu->gpu_cx);
|
||||
if (IS_ERR(gpu->gpu_cx))
|
||||
gpu->gpu_cx = NULL;
|
||||
|
||||
/* Setup IOMMU.. eventually we will (I think) do this once per context
|
||||
* and have separate page tables per context. For now, to keep things
|
||||
* simple and to get something working, just use a single address space:
|
||||
*/
|
||||
gpu->iommu = iommu_domain_alloc(&platform_bus_type);
|
||||
if (!gpu->iommu) {
|
||||
dev_err(drm->dev, "failed to allocate IOMMU\n");
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
gpu->id = msm_register_iommu(drm, gpu->iommu);
|
||||
|
||||
/* Create ringbuffer: */
|
||||
gpu->rb = msm_ringbuffer_new(gpu, ringsz);
|
||||
if (IS_ERR(gpu->rb)) {
|
||||
ret = PTR_ERR(gpu->rb);
|
||||
gpu->rb = NULL;
|
||||
dev_err(drm->dev, "could not create ringbuffer: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = msm_gem_get_iova_locked(gpu->rb->bo, gpu->id, &gpu->rb_iova);
|
||||
if (ret) {
|
||||
gpu->rb_iova = 0;
|
||||
dev_err(drm->dev, "could not map ringbuffer: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs_init(gpu, pdev);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void msm_gpu_cleanup(struct msm_gpu *gpu)
|
||||
{
|
||||
DBG("%s", gpu->name);
|
||||
|
||||
WARN_ON(!list_empty(&gpu->active_list));
|
||||
|
||||
bs_fini(gpu);
|
||||
|
||||
if (gpu->rb) {
|
||||
if (gpu->rb_iova)
|
||||
msm_gem_put_iova(gpu->rb->bo, gpu->id);
|
||||
msm_ringbuffer_destroy(gpu->rb);
|
||||
}
|
||||
|
||||
if (gpu->iommu)
|
||||
iommu_domain_free(gpu->iommu);
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MSM_GPU_H__
|
||||
#define __MSM_GPU_H__
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "msm_ringbuffer.h"
|
||||
|
||||
struct msm_gem_submit;
|
||||
|
||||
/* So far, with hardware that I've seen to date, we can have:
|
||||
* + zero, one, or two z180 2d cores
|
||||
* + a3xx or a2xx 3d core, which share a common CP (the firmware
|
||||
* for the CP seems to implement some different PM4 packet types
|
||||
* but the basics of cmdstream submission are the same)
|
||||
*
|
||||
* Which means that the eventual complete "class" hierarchy, once
|
||||
* support for all past and present hw is in place, becomes:
|
||||
* + msm_gpu
|
||||
* + adreno_gpu
|
||||
* + a3xx_gpu
|
||||
* + a2xx_gpu
|
||||
* + z180_gpu
|
||||
*/
|
||||
struct msm_gpu_funcs {
|
||||
int (*get_param)(struct msm_gpu *gpu, uint32_t param, uint64_t *value);
|
||||
int (*hw_init)(struct msm_gpu *gpu);
|
||||
int (*pm_suspend)(struct msm_gpu *gpu);
|
||||
int (*pm_resume)(struct msm_gpu *gpu);
|
||||
int (*submit)(struct msm_gpu *gpu, struct msm_gem_submit *submit,
|
||||
struct msm_file_private *ctx);
|
||||
void (*flush)(struct msm_gpu *gpu);
|
||||
void (*idle)(struct msm_gpu *gpu);
|
||||
irqreturn_t (*irq)(struct msm_gpu *irq);
|
||||
uint32_t (*last_fence)(struct msm_gpu *gpu);
|
||||
void (*recover)(struct msm_gpu *gpu);
|
||||
void (*destroy)(struct msm_gpu *gpu);
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
/* show GPU status in debugfs: */
|
||||
void (*show)(struct msm_gpu *gpu, struct seq_file *m);
|
||||
#endif
|
||||
};
|
||||
|
||||
struct msm_gpu {
|
||||
const char *name;
|
||||
struct drm_device *dev;
|
||||
const struct msm_gpu_funcs *funcs;
|
||||
|
||||
struct msm_ringbuffer *rb;
|
||||
uint32_t rb_iova;
|
||||
|
||||
/* list of GEM active objects: */
|
||||
struct list_head active_list;
|
||||
|
||||
uint32_t submitted_fence;
|
||||
|
||||
/* worker for handling active-list retiring: */
|
||||
struct work_struct retire_work;
|
||||
|
||||
void __iomem *mmio;
|
||||
int irq;
|
||||
|
||||
struct iommu_domain *iommu;
|
||||
int id;
|
||||
|
||||
/* Power Control: */
|
||||
struct regulator *gpu_reg, *gpu_cx;
|
||||
struct clk *ebi1_clk, *grp_clks[5];
|
||||
uint32_t fast_rate, slow_rate, bus_freq;
|
||||
uint32_t bsc;
|
||||
|
||||
/* Hang Detction: */
|
||||
#define DRM_MSM_HANGCHECK_PERIOD 500 /* in ms */
|
||||
#define DRM_MSM_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_MSM_HANGCHECK_PERIOD)
|
||||
struct timer_list hangcheck_timer;
|
||||
uint32_t hangcheck_fence;
|
||||
struct work_struct recover_work;
|
||||
};
|
||||
|
||||
static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data)
|
||||
{
|
||||
msm_writel(data, gpu->mmio + (reg << 2));
|
||||
}
|
||||
|
||||
static inline u32 gpu_read(struct msm_gpu *gpu, u32 reg)
|
||||
{
|
||||
return msm_readl(gpu->mmio + (reg << 2));
|
||||
}
|
||||
|
||||
int msm_gpu_pm_suspend(struct msm_gpu *gpu);
|
||||
int msm_gpu_pm_resume(struct msm_gpu *gpu);
|
||||
|
||||
void msm_gpu_retire(struct msm_gpu *gpu);
|
||||
int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
|
||||
struct msm_file_private *ctx);
|
||||
|
||||
int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
|
||||
struct msm_gpu *gpu, const struct msm_gpu_funcs *funcs,
|
||||
const char *name, const char *ioname, const char *irqname, int ringsz);
|
||||
void msm_gpu_cleanup(struct msm_gpu *gpu);
|
||||
|
||||
struct msm_gpu *a3xx_gpu_init(struct drm_device *dev);
|
||||
void __init a3xx_register(void);
|
||||
void __exit a3xx_unregister(void);
|
||||
|
||||
#endif /* __MSM_GPU_H__ */
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "msm_ringbuffer.h"
|
||||
#include "msm_gpu.h"
|
||||
|
||||
struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int size)
|
||||
{
|
||||
struct msm_ringbuffer *ring;
|
||||
int ret;
|
||||
|
||||
size = ALIGN(size, 4); /* size should be dword aligned */
|
||||
|
||||
ring = kzalloc(sizeof(*ring), GFP_KERNEL);
|
||||
if (!ring) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ring->gpu = gpu;
|
||||
ring->bo = msm_gem_new(gpu->dev, size, MSM_BO_WC);
|
||||
if (IS_ERR(ring->bo)) {
|
||||
ret = PTR_ERR(ring->bo);
|
||||
ring->bo = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ring->start = msm_gem_vaddr_locked(ring->bo);
|
||||
ring->end = ring->start + (size / 4);
|
||||
ring->cur = ring->start;
|
||||
|
||||
ring->size = size;
|
||||
|
||||
return ring;
|
||||
|
||||
fail:
|
||||
if (ring)
|
||||
msm_ringbuffer_destroy(ring);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
void msm_ringbuffer_destroy(struct msm_ringbuffer *ring)
|
||||
{
|
||||
if (ring->bo)
|
||||
drm_gem_object_unreference(ring->bo);
|
||||
kfree(ring);
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MSM_RINGBUFFER_H__
|
||||
#define __MSM_RINGBUFFER_H__
|
||||
|
||||
#include "msm_drv.h"
|
||||
|
||||
struct msm_ringbuffer {
|
||||
struct msm_gpu *gpu;
|
||||
int size;
|
||||
struct drm_gem_object *bo;
|
||||
uint32_t *start, *end, *cur;
|
||||
};
|
||||
|
||||
struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int size);
|
||||
void msm_ringbuffer_destroy(struct msm_ringbuffer *ring);
|
||||
|
||||
/* ringbuffer helpers (the parts that are same for a3xx/a2xx/z180..) */
|
||||
|
||||
static inline void
|
||||
OUT_RING(struct msm_ringbuffer *ring, uint32_t data)
|
||||
{
|
||||
if (ring->cur == ring->end)
|
||||
ring->cur = ring->start;
|
||||
*(ring->cur++) = data;
|
||||
}
|
||||
|
||||
#endif /* __MSM_RINGBUFFER_H__ */
|
|
@ -16,3 +16,4 @@ header-y += sis_drm.h
|
|||
header-y += tegra_drm.h
|
||||
header-y += via_drm.h
|
||||
header-y += vmwgfx_drm.h
|
||||
header-y += msm_drm.h
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MSM_DRM_H__
|
||||
#define __MSM_DRM_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <drm/drm.h>
|
||||
|
||||
/* Please note that modifications to all structs defined here are
|
||||
* subject to backwards-compatibility constraints:
|
||||
* 1) Do not use pointers, use uint64_t instead for 32 bit / 64 bit
|
||||
* user/kernel compatibility
|
||||
* 2) Keep fields aligned to their size
|
||||
* 3) Because of how drm_ioctl() works, we can add new fields at
|
||||
* the end of an ioctl if some care is taken: drm_ioctl() will
|
||||
* zero out the new fields at the tail of the ioctl, so a zero
|
||||
* value should have a backwards compatible meaning. And for
|
||||
* output params, userspace won't see the newly added output
|
||||
* fields.. so that has to be somehow ok.
|
||||
*/
|
||||
|
||||
#define MSM_PIPE_NONE 0x00
|
||||
#define MSM_PIPE_2D0 0x01
|
||||
#define MSM_PIPE_2D1 0x02
|
||||
#define MSM_PIPE_3D0 0x10
|
||||
|
||||
/* timeouts are specified in clock-monotonic absolute times (to simplify
|
||||
* restarting interrupted ioctls). The following struct is logically the
|
||||
* same as 'struct timespec' but 32/64b ABI safe.
|
||||
*/
|
||||
struct drm_msm_timespec {
|
||||
int64_t tv_sec; /* seconds */
|
||||
int64_t tv_nsec; /* nanoseconds */
|
||||
};
|
||||
|
||||
#define MSM_PARAM_GPU_ID 0x01
|
||||
#define MSM_PARAM_GMEM_SIZE 0x02
|
||||
|
||||
struct drm_msm_param {
|
||||
uint32_t pipe; /* in, MSM_PIPE_x */
|
||||
uint32_t param; /* in, MSM_PARAM_x */
|
||||
uint64_t value; /* out (get_param) or in (set_param) */
|
||||
};
|
||||
|
||||
/*
|
||||
* GEM buffers:
|
||||
*/
|
||||
|
||||
#define MSM_BO_SCANOUT 0x00000001 /* scanout capable */
|
||||
#define MSM_BO_GPU_READONLY 0x00000002
|
||||
#define MSM_BO_CACHE_MASK 0x000f0000
|
||||
/* cache modes */
|
||||
#define MSM_BO_CACHED 0x00010000
|
||||
#define MSM_BO_WC 0x00020000
|
||||
#define MSM_BO_UNCACHED 0x00040000
|
||||
|
||||
struct drm_msm_gem_new {
|
||||
uint64_t size; /* in */
|
||||
uint32_t flags; /* in, mask of MSM_BO_x */
|
||||
uint32_t handle; /* out */
|
||||
};
|
||||
|
||||
struct drm_msm_gem_info {
|
||||
uint32_t handle; /* in */
|
||||
uint32_t pad;
|
||||
uint64_t offset; /* out, offset to pass to mmap() */
|
||||
};
|
||||
|
||||
#define MSM_PREP_READ 0x01
|
||||
#define MSM_PREP_WRITE 0x02
|
||||
#define MSM_PREP_NOSYNC 0x04
|
||||
|
||||
struct drm_msm_gem_cpu_prep {
|
||||
uint32_t handle; /* in */
|
||||
uint32_t op; /* in, mask of MSM_PREP_x */
|
||||
struct drm_msm_timespec timeout; /* in */
|
||||
};
|
||||
|
||||
struct drm_msm_gem_cpu_fini {
|
||||
uint32_t handle; /* in */
|
||||
};
|
||||
|
||||
/*
|
||||
* Cmdstream Submission:
|
||||
*/
|
||||
|
||||
/* The value written into the cmdstream is logically:
|
||||
*
|
||||
* ((relocbuf->gpuaddr + reloc_offset) << shift) | or
|
||||
*
|
||||
* When we have GPU's w/ >32bit ptrs, it should be possible to deal
|
||||
* with this by emit'ing two reloc entries with appropriate shift
|
||||
* values. Or a new MSM_SUBMIT_CMD_x type would also be an option.
|
||||
*
|
||||
* NOTE that reloc's must be sorted by order of increasing submit_offset,
|
||||
* otherwise EINVAL.
|
||||
*/
|
||||
struct drm_msm_gem_submit_reloc {
|
||||
uint32_t submit_offset; /* in, offset from submit_bo */
|
||||
uint32_t or; /* in, value OR'd with result */
|
||||
int32_t shift; /* in, amount of left shift (can be negative) */
|
||||
uint32_t reloc_idx; /* in, index of reloc_bo buffer */
|
||||
uint64_t reloc_offset; /* in, offset from start of reloc_bo */
|
||||
};
|
||||
|
||||
/* submit-types:
|
||||
* BUF - this cmd buffer is executed normally.
|
||||
* IB_TARGET_BUF - this cmd buffer is an IB target. Reloc's are
|
||||
* processed normally, but the kernel does not setup an IB to
|
||||
* this buffer in the first-level ringbuffer
|
||||
* CTX_RESTORE_BUF - only executed if there has been a GPU context
|
||||
* switch since the last SUBMIT ioctl
|
||||
*/
|
||||
#define MSM_SUBMIT_CMD_BUF 0x0001
|
||||
#define MSM_SUBMIT_CMD_IB_TARGET_BUF 0x0002
|
||||
#define MSM_SUBMIT_CMD_CTX_RESTORE_BUF 0x0003
|
||||
struct drm_msm_gem_submit_cmd {
|
||||
uint32_t type; /* in, one of MSM_SUBMIT_CMD_x */
|
||||
uint32_t submit_idx; /* in, index of submit_bo cmdstream buffer */
|
||||
uint32_t submit_offset; /* in, offset into submit_bo */
|
||||
uint32_t size; /* in, cmdstream size */
|
||||
uint32_t pad;
|
||||
uint32_t nr_relocs; /* in, number of submit_reloc's */
|
||||
uint64_t __user relocs; /* in, ptr to array of submit_reloc's */
|
||||
};
|
||||
|
||||
/* Each buffer referenced elsewhere in the cmdstream submit (ie. the
|
||||
* cmdstream buffer(s) themselves or reloc entries) has one (and only
|
||||
* one) entry in the submit->bos[] table.
|
||||
*
|
||||
* As a optimization, the current buffer (gpu virtual address) can be
|
||||
* passed back through the 'presumed' field. If on a subsequent reloc,
|
||||
* userspace passes back a 'presumed' address that is still valid,
|
||||
* then patching the cmdstream for this entry is skipped. This can
|
||||
* avoid kernel needing to map/access the cmdstream bo in the common
|
||||
* case.
|
||||
*/
|
||||
#define MSM_SUBMIT_BO_READ 0x0001
|
||||
#define MSM_SUBMIT_BO_WRITE 0x0002
|
||||
struct drm_msm_gem_submit_bo {
|
||||
uint32_t flags; /* in, mask of MSM_SUBMIT_BO_x */
|
||||
uint32_t handle; /* in, GEM handle */
|
||||
uint64_t presumed; /* in/out, presumed buffer address */
|
||||
};
|
||||
|
||||
/* Each cmdstream submit consists of a table of buffers involved, and
|
||||
* one or more cmdstream buffers. This allows for conditional execution
|
||||
* (context-restore), and IB buffers needed for per tile/bin draw cmds.
|
||||
*/
|
||||
struct drm_msm_gem_submit {
|
||||
uint32_t pipe; /* in, MSM_PIPE_x */
|
||||
uint32_t fence; /* out */
|
||||
uint32_t nr_bos; /* in, number of submit_bo's */
|
||||
uint32_t nr_cmds; /* in, number of submit_cmd's */
|
||||
uint64_t __user bos; /* in, ptr to array of submit_bo's */
|
||||
uint64_t __user cmds; /* in, ptr to array of submit_cmd's */
|
||||
};
|
||||
|
||||
/* The normal way to synchronize with the GPU is just to CPU_PREP on
|
||||
* a buffer if you need to access it from the CPU (other cmdstream
|
||||
* submission from same or other contexts, PAGE_FLIP ioctl, etc, all
|
||||
* handle the required synchronization under the hood). This ioctl
|
||||
* mainly just exists as a way to implement the gallium pipe_fence
|
||||
* APIs without requiring a dummy bo to synchronize on.
|
||||
*/
|
||||
struct drm_msm_wait_fence {
|
||||
uint32_t fence; /* in */
|
||||
uint32_t pad;
|
||||
struct drm_msm_timespec timeout; /* in */
|
||||
};
|
||||
|
||||
#define DRM_MSM_GET_PARAM 0x00
|
||||
/* placeholder:
|
||||
#define DRM_MSM_SET_PARAM 0x01
|
||||
*/
|
||||
#define DRM_MSM_GEM_NEW 0x02
|
||||
#define DRM_MSM_GEM_INFO 0x03
|
||||
#define DRM_MSM_GEM_CPU_PREP 0x04
|
||||
#define DRM_MSM_GEM_CPU_FINI 0x05
|
||||
#define DRM_MSM_GEM_SUBMIT 0x06
|
||||
#define DRM_MSM_WAIT_FENCE 0x07
|
||||
#define DRM_MSM_NUM_IOCTLS 0x08
|
||||
|
||||
#define DRM_IOCTL_MSM_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GET_PARAM, struct drm_msm_param)
|
||||
#define DRM_IOCTL_MSM_GEM_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_NEW, struct drm_msm_gem_new)
|
||||
#define DRM_IOCTL_MSM_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_INFO, struct drm_msm_gem_info)
|
||||
#define DRM_IOCTL_MSM_GEM_CPU_PREP DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_GEM_CPU_PREP, struct drm_msm_gem_cpu_prep)
|
||||
#define DRM_IOCTL_MSM_GEM_CPU_FINI DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_GEM_CPU_FINI, struct drm_msm_gem_cpu_fini)
|
||||
#define DRM_IOCTL_MSM_GEM_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_SUBMIT, struct drm_msm_gem_submit)
|
||||
#define DRM_IOCTL_MSM_WAIT_FENCE DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_WAIT_FENCE, struct drm_msm_wait_fence)
|
||||
|
||||
#endif /* __MSM_DRM_H__ */
|
Загрузка…
Ссылка в новой задаче