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:
Dave Airlie 2013-08-28 14:27:15 +10:00
Родитель acb4652703 bd6f82d828
Коммит e906d7bdd3
47 изменённых файлов: 14844 добавлений и 0 удалений

Просмотреть файл

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

69
drivers/gpu/drm/msm/NOTES Normal file
Просмотреть файл

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

207
include/uapi/drm/msm_drm.h Normal file
Просмотреть файл

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