Initial commit; kernel source import

This commit is contained in:
Nathan
2025-04-06 23:50:55 -05:00
commit 25c6d769f4
45093 changed files with 18199410 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
config DRM_TEGRA
tristate "NVIDIA Tegra DRM"
depends on ARCH_TEGRA || (ARM && COMPILE_TEST)
depends on DRM
depends on RESET_CONTROLLER
select DRM_KMS_HELPER
select DRM_MIPI_DSI
select DRM_PANEL
help
Choose this option if you have an NVIDIA Tegra SoC.
To compile this driver as a module, choose M here: the module
will be called tegra-drm.
if DRM_TEGRA
config DRM_TEGRA_FBDEV
bool "Enable legacy fbdev support"
select DRM_KMS_FB_HELPER
select FB_SYS_FILLRECT
select FB_SYS_COPYAREA
select FB_SYS_IMAGEBLIT
default y
help
Choose this option if you have a need for the legacy fbdev support.
Note that this support also provides the Linux console on top of
the Tegra modesetting driver.
config DRM_TEGRA_DEBUG
bool "NVIDIA Tegra DRM debug support"
help
Say yes here to enable debugging support.
config DRM_TEGRA_STAGING
bool "Enable HOST1X interface"
depends on STAGING
help
Say yes if HOST1X should be available for userspace DRM users.
If unsure, choose N.
endif

View File

@@ -0,0 +1,16 @@
ccflags-$(CONFIG_DRM_TEGRA_DEBUG) += -DDEBUG
tegra-drm-y := \
bus.o \
drm.o \
gem.o \
fb.o \
dc.o \
output.o \
rgb.o \
hdmi.o \
mipi-phy.o \
sor.o \
dpaux.o
obj-$(CONFIG_DRM_TEGRA) += tegra-drm.o

136
drivers/gpu/drm/tegra/bus.c Normal file
View File

@@ -0,0 +1,136 @@
/*
* Copyright (C) 2013 NVIDIA Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "drm.h"
static DEFINE_MUTEX(clients_lock);
static LIST_HEAD(clients);
void drm_host1x_exit(struct drm_driver *driver, struct host1x_device *device)
{
struct tegra_drm *tegra = dev_get_drvdata(&device->dev);
drm_put_dev(tegra->drm);
}
int drm_host1x_init(struct drm_driver *driver, struct host1x_device *device)
{
mutex_init(&clients_lock);
INIT_LIST_HEAD(&device->clients);
return 0;
}
int drm_host1x_register(struct host1x_client *client)
{
struct host1x_client *pos;
mutex_lock(&clients_lock);
list_for_each_entry(pos, &clients, list) {
if (pos->dev == client->dev) {
mutex_unlock(&clients_lock);
return 0;
}
}
list_add_tail(&client->list, &clients);
mutex_unlock(&clients_lock);
return 0;
}
int drm_host1x_unregister(struct host1x_client *client)
{
struct host1x_client *c;
list_for_each_entry(c, &clients, list) {
if (c == client) {
list_del(&c->list);
return 0;
}
}
return 1;
}
int drm_host1x_device_init(struct drm_device *drm, struct host1x_device *device)
{
struct host1x_client *client;
int err;
mutex_lock(&clients_lock);
list_for_each_entry(client, &clients, list) {
if (client->ops && client->ops->init) {
client->parent = drm->dev;
err = client->ops->init(client);
if (err < 0) {
dev_err(&device->dev,
"failed to initialize %s: %d\n",
dev_name(client->dev), err);
mutex_unlock(&clients_lock);
return err;
}
}
}
mutex_unlock(&clients_lock);
return 0;
}
int drm_host1x_device_exit(struct host1x_device *device)
{
struct host1x_client *client;
int err;
mutex_lock(&clients_lock);
list_for_each_entry_reverse(client, &clients, list) {
if (client->ops && client->ops->exit) {
err = client->ops->exit(client);
if (err < 0) {
dev_err(&device->dev,
"failed to cleanup %s: %d\n",
dev_name(client->dev), err);
mutex_unlock(&clients_lock);
return err;
}
}
}
mutex_unlock(&clients_lock);
return 0;
}
struct host1x_client *drm_host1x_get_client(struct device *dev)
{
struct host1x_client *pos;
mutex_lock(&clients_lock);
list_for_each_entry(pos, &clients, list) {
if (pos->dev == dev) {
mutex_unlock(&clients_lock);
return pos;
}
}
mutex_unlock(&clients_lock);
return NULL;
}
int drm_host1x_check_clients_probed(void)
{
struct host1x_client *pos;
mutex_lock(&clients_lock);
list_for_each_entry(pos, &clients, list) {
if (!pos->driver_probed) {
mutex_unlock(&clients_lock);
return 0;
}
}
mutex_unlock(&clients_lock);
return 1;
}

1856
drivers/gpu/drm/tegra/dc.c Normal file

File diff suppressed because it is too large Load Diff

445
drivers/gpu/drm/tegra/dc.h Normal file
View File

@@ -0,0 +1,445 @@
/*
* Copyright (C) 2012 Avionic Design GmbH
* Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef TEGRA_DC_H
#define TEGRA_DC_H 1
#define DC_CMD_GENERAL_INCR_SYNCPT 0x000
#define DC_CMD_GENERAL_INCR_SYNCPT_CNTRL 0x001
#define DC_CMD_GENERAL_INCR_SYNCPT_ERROR 0x002
#define DC_CMD_WIN_A_INCR_SYNCPT 0x008
#define DC_CMD_WIN_A_INCR_SYNCPT_CNTRL 0x009
#define DC_CMD_WIN_A_INCR_SYNCPT_ERROR 0x00a
#define DC_CMD_WIN_B_INCR_SYNCPT 0x010
#define DC_CMD_WIN_B_INCR_SYNCPT_CNTRL 0x011
#define DC_CMD_WIN_B_INCR_SYNCPT_ERROR 0x012
#define DC_CMD_WIN_C_INCR_SYNCPT 0x018
#define DC_CMD_WIN_C_INCR_SYNCPT_CNTRL 0x019
#define DC_CMD_WIN_C_INCR_SYNCPT_ERROR 0x01a
#define DC_CMD_CONT_SYNCPT_VSYNC 0x028
#define DC_CMD_DISPLAY_COMMAND_OPTION0 0x031
#define DC_CMD_DISPLAY_COMMAND 0x032
#define DISP_CTRL_MODE_STOP (0 << 5)
#define DISP_CTRL_MODE_C_DISPLAY (1 << 5)
#define DISP_CTRL_MODE_NC_DISPLAY (2 << 5)
#define DISP_CTRL_MODE_MASK (3 << 5)
#define DC_CMD_SIGNAL_RAISE 0x033
#define DC_CMD_DISPLAY_POWER_CONTROL 0x036
#define PW0_ENABLE (1 << 0)
#define PW1_ENABLE (1 << 2)
#define PW2_ENABLE (1 << 4)
#define PW3_ENABLE (1 << 6)
#define PW4_ENABLE (1 << 8)
#define PM0_ENABLE (1 << 16)
#define PM1_ENABLE (1 << 18)
#define DC_CMD_INT_STATUS 0x037
#define DC_CMD_INT_MASK 0x038
#define DC_CMD_INT_ENABLE 0x039
#define DC_CMD_INT_TYPE 0x03a
#define DC_CMD_INT_POLARITY 0x03b
#define CTXSW_INT (1 << 0)
#define FRAME_END_INT (1 << 1)
#define VBLANK_INT (1 << 2)
#define WIN_A_UF_INT (1 << 8)
#define WIN_B_UF_INT (1 << 9)
#define WIN_C_UF_INT (1 << 10)
#define WIN_A_OF_INT (1 << 14)
#define WIN_B_OF_INT (1 << 15)
#define WIN_C_OF_INT (1 << 16)
#define DC_CMD_SIGNAL_RAISE1 0x03c
#define DC_CMD_SIGNAL_RAISE2 0x03d
#define DC_CMD_SIGNAL_RAISE3 0x03e
#define DC_CMD_STATE_ACCESS 0x040
#define READ_MUX (1 << 0)
#define WRITE_MUX (1 << 2)
#define DC_CMD_STATE_CONTROL 0x041
#define GENERAL_ACT_REQ (1 << 0)
#define WIN_A_ACT_REQ (1 << 1)
#define WIN_B_ACT_REQ (1 << 2)
#define WIN_C_ACT_REQ (1 << 3)
#define CURSOR_ACT_REQ (1 << 7)
#define GENERAL_UPDATE (1 << 8)
#define WIN_A_UPDATE (1 << 9)
#define WIN_B_UPDATE (1 << 10)
#define WIN_C_UPDATE (1 << 11)
#define CURSOR_UPDATE (1 << 15)
#define NC_HOST_TRIG (1 << 24)
#define DC_CMD_DISPLAY_WINDOW_HEADER 0x042
#define WINDOW_A_SELECT (1 << 4)
#define WINDOW_B_SELECT (1 << 5)
#define WINDOW_C_SELECT (1 << 6)
#define DC_CMD_REG_ACT_CONTROL 0x043
#define DC_COM_CRC_CONTROL 0x300
#define DC_COM_CRC_CHECKSUM 0x301
#define DC_COM_PIN_OUTPUT_ENABLE(x) (0x302 + (x))
#define DC_COM_PIN_OUTPUT_POLARITY(x) (0x306 + (x))
#define LVS_OUTPUT_POLARITY_LOW (1 << 28)
#define LHS_OUTPUT_POLARITY_LOW (1 << 30)
#define DC_COM_PIN_OUTPUT_DATA(x) (0x30a + (x))
#define DC_COM_PIN_INPUT_ENABLE(x) (0x30e + (x))
#define DC_COM_PIN_INPUT_DATA(x) (0x312 + (x))
#define DC_COM_PIN_OUTPUT_SELECT(x) (0x314 + (x))
#define DC_COM_PIN_MISC_CONTROL 0x31b
#define DC_COM_PIN_PM0_CONTROL 0x31c
#define DC_COM_PIN_PM0_DUTY_CYCLE 0x31d
#define DC_COM_PIN_PM1_CONTROL 0x31e
#define DC_COM_PIN_PM1_DUTY_CYCLE 0x31f
#define DC_COM_SPI_CONTROL 0x320
#define DC_COM_SPI_START_BYTE 0x321
#define DC_COM_HSPI_WRITE_DATA_AB 0x322
#define DC_COM_HSPI_WRITE_DATA_CD 0x323
#define DC_COM_HSPI_CS_DC 0x324
#define DC_COM_SCRATCH_REGISTER_A 0x325
#define DC_COM_SCRATCH_REGISTER_B 0x326
#define DC_COM_GPIO_CTRL 0x327
#define DC_COM_GPIO_DEBOUNCE_COUNTER 0x328
#define DC_COM_CRC_CHECKSUM_LATCHED 0x329
#define DC_DISP_DISP_SIGNAL_OPTIONS0 0x400
#define H_PULSE_0_ENABLE (1 << 8)
#define H_PULSE_1_ENABLE (1 << 10)
#define H_PULSE_2_ENABLE (1 << 12)
#define DC_DISP_DISP_SIGNAL_OPTIONS1 0x401
#define DC_DISP_DISP_WIN_OPTIONS 0x402
#define HDMI_ENABLE (1 << 30)
#define DSI_ENABLE (1 << 29)
#define SOR_ENABLE (1 << 25)
#define CURSOR_ENABLE (1 << 16)
#define DC_DISP_DISP_MEM_HIGH_PRIORITY 0x403
#define CURSOR_THRESHOLD(x) (((x) & 0x03) << 24)
#define WINDOW_A_THRESHOLD(x) (((x) & 0x7f) << 16)
#define WINDOW_B_THRESHOLD(x) (((x) & 0x7f) << 8)
#define WINDOW_C_THRESHOLD(x) (((x) & 0xff) << 0)
#define DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER 0x404
#define CURSOR_DELAY(x) (((x) & 0x3f) << 24)
#define WINDOW_A_DELAY(x) (((x) & 0x3f) << 16)
#define WINDOW_B_DELAY(x) (((x) & 0x3f) << 8)
#define WINDOW_C_DELAY(x) (((x) & 0x3f) << 0)
#define DC_DISP_DISP_TIMING_OPTIONS 0x405
#define VSYNC_H_POSITION(x) ((x) & 0xfff)
#define DC_DISP_REF_TO_SYNC 0x406
#define DC_DISP_SYNC_WIDTH 0x407
#define DC_DISP_BACK_PORCH 0x408
#define DC_DISP_ACTIVE 0x409
#define DC_DISP_FRONT_PORCH 0x40a
#define DC_DISP_H_PULSE0_CONTROL 0x40b
#define DC_DISP_H_PULSE0_POSITION_A 0x40c
#define DC_DISP_H_PULSE0_POSITION_B 0x40d
#define DC_DISP_H_PULSE0_POSITION_C 0x40e
#define DC_DISP_H_PULSE0_POSITION_D 0x40f
#define DC_DISP_H_PULSE1_CONTROL 0x410
#define DC_DISP_H_PULSE1_POSITION_A 0x411
#define DC_DISP_H_PULSE1_POSITION_B 0x412
#define DC_DISP_H_PULSE1_POSITION_C 0x413
#define DC_DISP_H_PULSE1_POSITION_D 0x414
#define DC_DISP_H_PULSE2_CONTROL 0x415
#define DC_DISP_H_PULSE2_POSITION_A 0x416
#define DC_DISP_H_PULSE2_POSITION_B 0x417
#define DC_DISP_H_PULSE2_POSITION_C 0x418
#define DC_DISP_H_PULSE2_POSITION_D 0x419
#define DC_DISP_V_PULSE0_CONTROL 0x41a
#define DC_DISP_V_PULSE0_POSITION_A 0x41b
#define DC_DISP_V_PULSE0_POSITION_B 0x41c
#define DC_DISP_V_PULSE0_POSITION_C 0x41d
#define DC_DISP_V_PULSE1_CONTROL 0x41e
#define DC_DISP_V_PULSE1_POSITION_A 0x41f
#define DC_DISP_V_PULSE1_POSITION_B 0x420
#define DC_DISP_V_PULSE1_POSITION_C 0x421
#define DC_DISP_V_PULSE2_CONTROL 0x422
#define DC_DISP_V_PULSE2_POSITION_A 0x423
#define DC_DISP_V_PULSE3_CONTROL 0x424
#define DC_DISP_V_PULSE3_POSITION_A 0x425
#define DC_DISP_M0_CONTROL 0x426
#define DC_DISP_M1_CONTROL 0x427
#define DC_DISP_DI_CONTROL 0x428
#define DC_DISP_PP_CONTROL 0x429
#define DC_DISP_PP_SELECT_A 0x42a
#define DC_DISP_PP_SELECT_B 0x42b
#define DC_DISP_PP_SELECT_C 0x42c
#define DC_DISP_PP_SELECT_D 0x42d
#define PULSE_MODE_NORMAL (0 << 3)
#define PULSE_MODE_ONE_CLOCK (1 << 3)
#define PULSE_POLARITY_HIGH (0 << 4)
#define PULSE_POLARITY_LOW (1 << 4)
#define PULSE_QUAL_ALWAYS (0 << 6)
#define PULSE_QUAL_VACTIVE (2 << 6)
#define PULSE_QUAL_VACTIVE1 (3 << 6)
#define PULSE_LAST_START_A (0 << 8)
#define PULSE_LAST_END_A (1 << 8)
#define PULSE_LAST_START_B (2 << 8)
#define PULSE_LAST_END_B (3 << 8)
#define PULSE_LAST_START_C (4 << 8)
#define PULSE_LAST_END_C (5 << 8)
#define PULSE_LAST_START_D (6 << 8)
#define PULSE_LAST_END_D (7 << 8)
#define PULSE_START(x) (((x) & 0xfff) << 0)
#define PULSE_END(x) (((x) & 0xfff) << 16)
#define DC_DISP_DISP_CLOCK_CONTROL 0x42e
#define PIXEL_CLK_DIVIDER_PCD1 (0 << 8)
#define PIXEL_CLK_DIVIDER_PCD1H (1 << 8)
#define PIXEL_CLK_DIVIDER_PCD2 (2 << 8)
#define PIXEL_CLK_DIVIDER_PCD3 (3 << 8)
#define PIXEL_CLK_DIVIDER_PCD4 (4 << 8)
#define PIXEL_CLK_DIVIDER_PCD6 (5 << 8)
#define PIXEL_CLK_DIVIDER_PCD8 (6 << 8)
#define PIXEL_CLK_DIVIDER_PCD9 (7 << 8)
#define PIXEL_CLK_DIVIDER_PCD12 (8 << 8)
#define PIXEL_CLK_DIVIDER_PCD16 (9 << 8)
#define PIXEL_CLK_DIVIDER_PCD18 (10 << 8)
#define PIXEL_CLK_DIVIDER_PCD24 (11 << 8)
#define PIXEL_CLK_DIVIDER_PCD13 (12 << 8)
#define SHIFT_CLK_DIVIDER(x) ((x) & 0xff)
#define DC_DISP_DISP_INTERFACE_CONTROL 0x42f
#define DISP_DATA_FORMAT_DF1P1C (0 << 0)
#define DISP_DATA_FORMAT_DF1P2C24B (1 << 0)
#define DISP_DATA_FORMAT_DF1P2C18B (2 << 0)
#define DISP_DATA_FORMAT_DF1P2C16B (3 << 0)
#define DISP_DATA_FORMAT_DF2S (4 << 0)
#define DISP_DATA_FORMAT_DF3S (5 << 0)
#define DISP_DATA_FORMAT_DFSPI (6 << 0)
#define DISP_DATA_FORMAT_DF1P3C24B (7 << 0)
#define DISP_DATA_FORMAT_DF1P3C18B (8 << 0)
#define DISP_ALIGNMENT_MSB (0 << 8)
#define DISP_ALIGNMENT_LSB (1 << 8)
#define DISP_ORDER_RED_BLUE (0 << 9)
#define DISP_ORDER_BLUE_RED (1 << 9)
#define DC_DISP_DISP_COLOR_CONTROL 0x430
#define BASE_COLOR_SIZE666 (0 << 0)
#define BASE_COLOR_SIZE111 (1 << 0)
#define BASE_COLOR_SIZE222 (2 << 0)
#define BASE_COLOR_SIZE333 (3 << 0)
#define BASE_COLOR_SIZE444 (4 << 0)
#define BASE_COLOR_SIZE555 (5 << 0)
#define BASE_COLOR_SIZE565 (6 << 0)
#define BASE_COLOR_SIZE332 (7 << 0)
#define BASE_COLOR_SIZE888 (8 << 0)
#define DITHER_CONTROL_DISABLE (0 << 8)
#define DITHER_CONTROL_ORDERED (2 << 8)
#define DITHER_CONTROL_ERRDIFF (3 << 8)
#define DC_DISP_SHIFT_CLOCK_OPTIONS 0x431
#define SC1_H_QUALIFIER_NONE (1 << 16)
#define SC0_H_QUALIFIER_NONE (1 << 0)
#define DC_DISP_DATA_ENABLE_OPTIONS 0x432
#define DE_SELECT_ACTIVE_BLANK (0 << 0)
#define DE_SELECT_ACTIVE (1 << 0)
#define DE_SELECT_ACTIVE_IS (2 << 0)
#define DE_CONTROL_ONECLK (0 << 2)
#define DE_CONTROL_NORMAL (1 << 2)
#define DE_CONTROL_EARLY_EXT (2 << 2)
#define DE_CONTROL_EARLY (3 << 2)
#define DE_CONTROL_ACTIVE_BLANK (4 << 2)
#define DC_DISP_SERIAL_INTERFACE_OPTIONS 0x433
#define DC_DISP_LCD_SPI_OPTIONS 0x434
#define DC_DISP_BORDER_COLOR 0x435
#define DC_DISP_COLOR_KEY0_LOWER 0x436
#define DC_DISP_COLOR_KEY0_UPPER 0x437
#define DC_DISP_COLOR_KEY1_LOWER 0x438
#define DC_DISP_COLOR_KEY1_UPPER 0x439
#define DC_DISP_CURSOR_FOREGROUND 0x43c
#define DC_DISP_CURSOR_BACKGROUND 0x43d
#define DC_DISP_CURSOR_START_ADDR 0x43e
#define CURSOR_CLIP_DISPLAY (0 << 28)
#define CURSOR_CLIP_WIN_A (1 << 28)
#define CURSOR_CLIP_WIN_B (2 << 28)
#define CURSOR_CLIP_WIN_C (3 << 28)
#define CURSOR_SIZE_32x32 (0 << 24)
#define CURSOR_SIZE_64x64 (1 << 24)
#define CURSOR_SIZE_128x128 (2 << 24)
#define CURSOR_SIZE_256x256 (3 << 24)
#define DC_DISP_CURSOR_START_ADDR_NS 0x43f
#define DC_DISP_CURSOR_POSITION 0x440
#define DC_DISP_CURSOR_POSITION_NS 0x441
#define DC_DISP_INIT_SEQ_CONTROL 0x442
#define DC_DISP_SPI_INIT_SEQ_DATA_A 0x443
#define DC_DISP_SPI_INIT_SEQ_DATA_B 0x444
#define DC_DISP_SPI_INIT_SEQ_DATA_C 0x445
#define DC_DISP_SPI_INIT_SEQ_DATA_D 0x446
#define DC_DISP_DC_MCCIF_FIFOCTRL 0x480
#define DC_DISP_MCCIF_DISPLAY0A_HYST 0x481
#define DC_DISP_MCCIF_DISPLAY0B_HYST 0x482
#define DC_DISP_MCCIF_DISPLAY1A_HYST 0x483
#define DC_DISP_MCCIF_DISPLAY1B_HYST 0x484
#define DC_DISP_DAC_CRT_CTRL 0x4c0
#define DC_DISP_DISP_MISC_CONTROL 0x4c1
#define DC_DISP_SD_CONTROL 0x4c2
#define DC_DISP_SD_CSC_COEFF 0x4c3
#define DC_DISP_SD_LUT(x) (0x4c4 + (x))
#define DC_DISP_SD_FLICKER_CONTROL 0x4cd
#define DC_DISP_DC_PIXEL_COUNT 0x4ce
#define DC_DISP_SD_HISTOGRAM(x) (0x4cf + (x))
#define DC_DISP_SD_BL_PARAMETERS 0x4d7
#define DC_DISP_SD_BL_TF(x) (0x4d8 + (x))
#define DC_DISP_SD_BL_CONTROL 0x4dc
#define DC_DISP_SD_HW_K_VALUES 0x4dd
#define DC_DISP_SD_MAN_K_VALUES 0x4de
#define DC_DISP_INTERLACE_CONTROL 0x4e5
#define INTERLACE_STATUS (1 << 2)
#define INTERLACE_START (1 << 1)
#define INTERLACE_ENABLE (1 << 0)
#define DC_DISP_CURSOR_START_ADDR_HI 0x4ec
#define DC_DISP_BLEND_CURSOR_CONTROL 0x4f1
#define CURSOR_MODE_LEGACY (0 << 24)
#define CURSOR_MODE_NORMAL (1 << 24)
#define CURSOR_DST_BLEND_ZERO (0 << 16)
#define CURSOR_DST_BLEND_K1 (1 << 16)
#define CURSOR_DST_BLEND_NEG_K1_TIMES_SRC (2 << 16)
#define CURSOR_DST_BLEND_MASK (3 << 16)
#define CURSOR_SRC_BLEND_K1 (0 << 8)
#define CURSOR_SRC_BLEND_K1_TIMES_SRC (1 << 8)
#define CURSOR_SRC_BLEND_MASK (3 << 8)
#define CURSOR_ALPHA 0xff
#define DC_WIN_CSC_YOF 0x611
#define DC_WIN_CSC_KYRGB 0x612
#define DC_WIN_CSC_KUR 0x613
#define DC_WIN_CSC_KVR 0x614
#define DC_WIN_CSC_KUG 0x615
#define DC_WIN_CSC_KVG 0x616
#define DC_WIN_CSC_KUB 0x617
#define DC_WIN_CSC_KVB 0x618
#define DC_WIN_WIN_OPTIONS 0x700
#define H_DIRECTION (1 << 0)
#define V_DIRECTION (1 << 2)
#define COLOR_EXPAND (1 << 6)
#define CSC_ENABLE (1 << 18)
#define WIN_ENABLE (1 << 30)
#define DC_WIN_BYTE_SWAP 0x701
#define BYTE_SWAP_NOSWAP (0 << 0)
#define BYTE_SWAP_SWAP2 (1 << 0)
#define BYTE_SWAP_SWAP4 (2 << 0)
#define BYTE_SWAP_SWAP4HW (3 << 0)
#define DC_WIN_BUFFER_CONTROL 0x702
#define BUFFER_CONTROL_HOST (0 << 0)
#define BUFFER_CONTROL_VI (1 << 0)
#define BUFFER_CONTROL_EPP (2 << 0)
#define BUFFER_CONTROL_MPEGE (3 << 0)
#define BUFFER_CONTROL_SB2D (4 << 0)
#define DC_WIN_COLOR_DEPTH 0x703
#define WIN_COLOR_DEPTH_P1 0
#define WIN_COLOR_DEPTH_P2 1
#define WIN_COLOR_DEPTH_P4 2
#define WIN_COLOR_DEPTH_P8 3
#define WIN_COLOR_DEPTH_B4G4R4A4 4
#define WIN_COLOR_DEPTH_B5G5R5A 5
#define WIN_COLOR_DEPTH_B5G6R5 6
#define WIN_COLOR_DEPTH_AB5G5R5 7
#define WIN_COLOR_DEPTH_B8G8R8A8 12
#define WIN_COLOR_DEPTH_R8G8B8A8 13
#define WIN_COLOR_DEPTH_B6x2G6x2R6x2A8 14
#define WIN_COLOR_DEPTH_R6x2G6x2B6x2A8 15
#define WIN_COLOR_DEPTH_YCbCr422 16
#define WIN_COLOR_DEPTH_YUV422 17
#define WIN_COLOR_DEPTH_YCbCr420P 18
#define WIN_COLOR_DEPTH_YUV420P 19
#define WIN_COLOR_DEPTH_YCbCr422P 20
#define WIN_COLOR_DEPTH_YUV422P 21
#define WIN_COLOR_DEPTH_YCbCr422R 22
#define WIN_COLOR_DEPTH_YUV422R 23
#define WIN_COLOR_DEPTH_YCbCr422RA 24
#define WIN_COLOR_DEPTH_YUV422RA 25
#define DC_WIN_POSITION 0x704
#define H_POSITION(x) (((x) & 0x1fff) << 0)
#define V_POSITION(x) (((x) & 0x1fff) << 16)
#define DC_WIN_SIZE 0x705
#define H_SIZE(x) (((x) & 0x1fff) << 0)
#define V_SIZE(x) (((x) & 0x1fff) << 16)
#define DC_WIN_PRESCALED_SIZE 0x706
#define H_PRESCALED_SIZE(x) (((x) & 0x7fff) << 0)
#define V_PRESCALED_SIZE(x) (((x) & 0x1fff) << 16)
#define DC_WIN_H_INITIAL_DDA 0x707
#define DC_WIN_V_INITIAL_DDA 0x708
#define DC_WIN_DDA_INC 0x709
#define H_DDA_INC(x) (((x) & 0xffff) << 0)
#define V_DDA_INC(x) (((x) & 0xffff) << 16)
#define DC_WIN_LINE_STRIDE 0x70a
#define DC_WIN_BUF_STRIDE 0x70b
#define DC_WIN_UV_BUF_STRIDE 0x70c
#define DC_WIN_BUFFER_ADDR_MODE 0x70d
#define DC_WIN_BUFFER_ADDR_MODE_LINEAR (0 << 0)
#define DC_WIN_BUFFER_ADDR_MODE_TILE (1 << 0)
#define DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV (0 << 16)
#define DC_WIN_BUFFER_ADDR_MODE_TILE_UV (1 << 16)
#define DC_WIN_DV_CONTROL 0x70e
#define DC_WIN_BLEND_NOKEY 0x70f
#define DC_WIN_BLEND_1WIN 0x710
#define DC_WIN_BLEND_2WIN_X 0x711
#define DC_WIN_BLEND_2WIN_Y 0x712
#define DC_WIN_BLEND_3WIN_XY 0x713
#define DC_WIN_HP_FETCH_CONTROL 0x714
#define DC_WINBUF_START_ADDR 0x800
#define DC_WINBUF_START_ADDR_NS 0x801
#define DC_WINBUF_START_ADDR_U 0x802
#define DC_WINBUF_START_ADDR_U_NS 0x803
#define DC_WINBUF_START_ADDR_V 0x804
#define DC_WINBUF_START_ADDR_V_NS 0x805
#define DC_WINBUF_ADDR_H_OFFSET 0x806
#define DC_WINBUF_ADDR_H_OFFSET_NS 0x807
#define DC_WINBUF_ADDR_V_OFFSET 0x808
#define DC_WINBUF_ADDR_V_OFFSET_NS 0x809
#define DC_WINBUF_UFLOW_STATUS 0x80a
#define DC_WINBUF_SURFACE_KIND 0x80b
#define DC_WINBUF_SURFACE_KIND_PITCH (0 << 0)
#define DC_WINBUF_SURFACE_KIND_TILED (1 << 0)
#define DC_WINBUF_SURFACE_KIND_BLOCK (2 << 0)
#define DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(x) (((x) & 0x7) << 4)
#define DC_WINBUF_AD_UFLOW_STATUS 0xbca
#define DC_WINBUF_BD_UFLOW_STATUS 0xdca
#define DC_WINBUF_CD_UFLOW_STATUS 0xfca
/* synchronization points */
#define SYNCPT_VBLANK0 26
#define SYNCPT_VBLANK1 27
#endif /* TEGRA_DC_H */

View File

@@ -0,0 +1,704 @@
/*
* Copyright (C) 2013 NVIDIA Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/tegra-powergate.h>
#include <drm/drm_dp_helper.h>
#include <drm/drm_panel.h>
#include "dpaux.h"
#include "drm.h"
static DEFINE_MUTEX(dpaux_lock);
static LIST_HEAD(dpaux_list);
struct tegra_dpaux {
struct drm_dp_aux aux;
struct host1x_client client;
struct device *dev;
void __iomem *regs;
int irq;
struct tegra_output *output;
struct reset_control *rst;
struct clk *clk_parent;
struct clk *clk;
struct completion complete;
struct list_head list;
bool enabled;
struct drm_minor *minor;
struct drm_info_list *debugfs_files;
struct dentry *debugfs;
};
static inline struct tegra_dpaux *
host1x_client_to_dpaux(struct host1x_client *client)
{
return container_of(client, struct tegra_dpaux, client);
}
static inline struct tegra_dpaux *to_dpaux(struct drm_dp_aux *aux)
{
return container_of(aux, struct tegra_dpaux, aux);
}
static inline unsigned long tegra_dpaux_readl(struct tegra_dpaux *dpaux,
unsigned long offset)
{
return readl(dpaux->regs + (offset << 2));
}
static inline void tegra_dpaux_writel(struct tegra_dpaux *dpaux,
unsigned long value,
unsigned long offset)
{
writel(value, dpaux->regs + (offset << 2));
}
static void tegra_dpaux_write_fifo(struct tegra_dpaux *dpaux, const u8 *buffer,
size_t size)
{
unsigned long offset = DPAUX_DP_AUXDATA_WRITE(0);
size_t i, j;
for (i = 0; i < size; i += 4) {
size_t num = min_t(size_t, size - i, 4);
unsigned long value = 0;
for (j = 0; j < num; j++)
value |= buffer[i + j] << (j * 8);
tegra_dpaux_writel(dpaux, value, offset++);
}
}
static void tegra_dpaux_read_fifo(struct tegra_dpaux *dpaux, u8 *buffer,
size_t size)
{
unsigned long offset = DPAUX_DP_AUXDATA_READ(0);
size_t i, j;
for (i = 0; i < size; i += 4) {
size_t num = min_t(size_t, size - i, 4);
unsigned long value;
value = tegra_dpaux_readl(dpaux, offset++);
for (j = 0; j < num; j++)
buffer[i + j] = value >> (j * 8);
}
}
static int tegra_dpaux_wait_plugged(struct tegra_dpaux *dpaux)
{
unsigned long value;
unsigned long timeout;
/*
* Typical plugged latency is 250us, but we use usleep below which
* involves kernel schedule, so set a somewhat big timeout here.
*/
timeout = jiffies + msecs_to_jiffies(250);
while (time_before(jiffies, timeout)) {
value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXSTAT);
if (value & DPAUX_DP_AUXSTAT_HPD_STATUS)
return 0;
usleep_range(1000, 2000);
}
/* Recheck in case the polling is skipped due to process schedule */
value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXSTAT);
if (value & DPAUX_DP_AUXSTAT_HPD_STATUS) {
return 0;
} else {
WARN(1, "dpaux waits for plugged timeout.\n");
return -ETIMEDOUT;
}
}
static ssize_t tegra_dpaux_transfer(struct drm_dp_aux *aux,
struct drm_dp_aux_msg *msg)
{
unsigned long timeout = msecs_to_jiffies(250);
struct tegra_dpaux *dpaux = to_dpaux(aux);
unsigned long status;
ssize_t ret = 0;
u32 value;
/* Tegra has 4x4 byte DP AUX transmit and receive FIFOs. */
if (msg->size > 16)
return -EINVAL;
/*
* Allow zero-sized messages only for I2C, in which case they specify
* address-only transactions.
*/
if (msg->size < 1) {
switch (msg->request & ~DP_AUX_I2C_MOT) {
case DP_AUX_I2C_WRITE:
case DP_AUX_I2C_READ:
value = DPAUX_DP_AUXCTL_CMD_ADDRESS_ONLY;
break;
default:
return -EINVAL;
}
} else {
/* For non-zero-sized messages, set the CMDLEN field. */
value = DPAUX_DP_AUXCTL_CMDLEN(msg->size - 1);
}
switch (msg->request & ~DP_AUX_I2C_MOT) {
case DP_AUX_I2C_WRITE:
if (msg->request & DP_AUX_I2C_MOT)
value |= DPAUX_DP_AUXCTL_CMD_MOT_WR;
else
value |= DPAUX_DP_AUXCTL_CMD_I2C_WR;
break;
case DP_AUX_I2C_READ:
if (msg->request & DP_AUX_I2C_MOT)
value |= DPAUX_DP_AUXCTL_CMD_MOT_RD;
else
value |= DPAUX_DP_AUXCTL_CMD_I2C_RD;
break;
case DP_AUX_I2C_STATUS:
if (msg->request & DP_AUX_I2C_MOT)
value |= DPAUX_DP_AUXCTL_CMD_MOT_RQ;
else
value |= DPAUX_DP_AUXCTL_CMD_I2C_RQ;
break;
case DP_AUX_NATIVE_WRITE:
value |= DPAUX_DP_AUXCTL_CMD_AUX_WR;
break;
case DP_AUX_NATIVE_READ:
value |= DPAUX_DP_AUXCTL_CMD_AUX_RD;
break;
default:
return -EINVAL;
}
if (!dpaux->enabled)
tegra_dpaux_enable(dpaux);
if (tegra_dpaux_wait_plugged(dpaux) < 0) {
WARN(1, "wait HPD failed in dpaux transfer.\n");
ret = -ETIMEDOUT;
goto out;
}
tegra_dpaux_writel(dpaux, msg->address, DPAUX_DP_AUXADDR);
tegra_dpaux_writel(dpaux, value, DPAUX_DP_AUXCTL);
if ((msg->request & DP_AUX_I2C_READ) == 0) {
tegra_dpaux_write_fifo(dpaux, msg->buffer, msg->size);
ret = msg->size;
}
/* start transaction */
value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXCTL);
value |= DPAUX_DP_AUXCTL_TRANSACTREQ;
tegra_dpaux_writel(dpaux, value, DPAUX_DP_AUXCTL);
status = wait_for_completion_timeout(&dpaux->complete, timeout);
if (!status) {
WARN(1, "wait dpaux transfer complete timeout.\n");
ret = -ETIMEDOUT;
goto out;
}
/* read status and clear errors */
value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXSTAT);
tegra_dpaux_writel(dpaux, 0xf00, DPAUX_DP_AUXSTAT);
if (value & DPAUX_DP_AUXSTAT_TIMEOUT_ERROR) {
WARN(1, "dpaux transfer error: timedout.\n");
ret = -ETIMEDOUT;
goto out;
}
if ((value & DPAUX_DP_AUXSTAT_RX_ERROR) ||
(value & DPAUX_DP_AUXSTAT_SINKSTAT_ERROR) ||
(value & DPAUX_DP_AUXSTAT_NO_STOP_ERROR)) {
ret = -EIO;
goto out;
}
switch ((value & DPAUX_DP_AUXSTAT_REPLY_TYPE_MASK) >> 16) {
case 0x00:
msg->reply = DP_AUX_NATIVE_REPLY_ACK;
break;
case 0x01:
msg->reply = DP_AUX_NATIVE_REPLY_NACK;
break;
case 0x02:
msg->reply = DP_AUX_NATIVE_REPLY_DEFER;
break;
case 0x04:
msg->reply = DP_AUX_I2C_REPLY_NACK;
break;
case 0x08:
msg->reply = DP_AUX_I2C_REPLY_DEFER;
break;
}
if ((msg->size > 0) && (msg->reply == DP_AUX_NATIVE_REPLY_ACK)) {
if (msg->request & DP_AUX_I2C_READ) {
size_t count = value & DPAUX_DP_AUXSTAT_REPLY_MASK;
if (WARN_ON(count != msg->size))
count = min_t(size_t, count, msg->size);
tegra_dpaux_read_fifo(dpaux, msg->buffer, count);
ret = count;
}
} else if (msg->reply &
(DP_AUX_NATIVE_REPLY_DEFER | DP_AUX_I2C_REPLY_DEFER)) {
/* return the requested size for retry */
ret = msg->size;
}
out:
return ret;
}
static irqreturn_t tegra_dpaux_irq(int irq, void *data)
{
struct tegra_dpaux *dpaux = data;
irqreturn_t ret = IRQ_HANDLED;
unsigned long value;
/* clear interrupts */
value = tegra_dpaux_readl(dpaux, DPAUX_INTR_AUX);
tegra_dpaux_writel(dpaux, value, DPAUX_INTR_AUX);
if (value & DPAUX_INTR_IRQ_EVENT) {
/* TODO: handle this */
}
if (value & DPAUX_INTR_AUX_DONE)
complete(&dpaux->complete);
return ret;
}
static int tegra_dpaux_show_regs(struct seq_file *s, void *data)
{
struct drm_info_node *node = s->private;
struct tegra_dpaux *dpaux = node->info_ent->data;
#define DUMP_REG(name) \
seq_printf(s, "%-40s %#05x %08lx\n", #name, name, \
tegra_dpaux_readl(dpaux, name))
DUMP_REG(DPAUX_CTXSW);
DUMP_REG(DPAUX_INTR_EN_AUX);
DUMP_REG(DPAUX_INTR_AUX);
DUMP_REG(DPAUX_DP_AUXADDR);
DUMP_REG(DPAUX_DP_AUXCTL);
DUMP_REG(DPAUX_DP_AUXSTAT);
DUMP_REG(DPAUX_DP_AUX_SINKSTAT_LO);
DUMP_REG(DPAUX_DP_AUX_SINKSTAT_HI);
DUMP_REG(DPAUX_HPD_CONFIG);
DUMP_REG(DPAUX_HPD_IRQ_CONFIG);
DUMP_REG(DPAUX_DP_AUX_CONFIG);
DUMP_REG(DPAUX_HYBRID_PADCTL);
DUMP_REG(DPAUX_HYBRID_SPARE);
DUMP_REG(DPAUX_SCRATCH_REG0);
DUMP_REG(DPAUX_SCRATCH_REG1);
DUMP_REG(DPAUX_SCRATCH_REG2);
#undef DUMP_REG
return 0;
}
static struct drm_info_list debugfs_files[] = {
{ "regs", tegra_dpaux_show_regs, 0, NULL },
};
static int tegra_dpaux_debugfs_init(struct tegra_dpaux *dpaux,
struct drm_minor *minor)
{
unsigned int i;
int err;
dpaux->debugfs = debugfs_create_dir("dpaux", minor->debugfs_root);
if (!dpaux->debugfs)
return -ENOMEM;
dpaux->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files),
GFP_KERNEL);
if (!dpaux->debugfs_files) {
err = -ENOMEM;
goto remove;
}
for (i = 0; i < ARRAY_SIZE(debugfs_files); i++)
dpaux->debugfs_files[i].data = dpaux;
err = drm_debugfs_create_files(dpaux->debugfs_files,
ARRAY_SIZE(debugfs_files),
dpaux->debugfs, minor);
if (err < 0)
goto free;
dpaux->minor = minor;
return 0;
free:
kfree(dpaux->debugfs_files);
dpaux->debugfs_files = NULL;
remove:
debugfs_remove(dpaux->debugfs);
dpaux->debugfs = NULL;
return err;
}
static int tegra_dpaux_debugfs_exit(struct tegra_dpaux *dpaux)
{
drm_debugfs_remove_files(dpaux->debugfs_files, ARRAY_SIZE(debugfs_files),
dpaux->minor);
dpaux->minor = NULL;
kfree(dpaux->debugfs_files);
dpaux->debugfs_files = NULL;
debugfs_remove(dpaux->debugfs);
dpaux->debugfs = NULL;
return 0;
}
static int tegra_dpaux_init(struct host1x_client *client)
{
struct tegra_drm *tegra = dev_get_drvdata(client->parent);
struct tegra_dpaux *dpaux = host1x_client_to_dpaux(client);
int err;
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
err = tegra_dpaux_debugfs_init(dpaux, tegra->drm->primary);
if (err < 0)
dev_err(dpaux->dev, "debugfs setup failed: %d\n", err);
return err;
}
return 0;
}
static int tegra_dpaux_exit(struct host1x_client *client)
{
struct tegra_dpaux *dpaux = host1x_client_to_dpaux(client);
int err;
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
err = tegra_dpaux_debugfs_exit(dpaux);
if (err < 0)
dev_err(dpaux->dev,
"debugfs cleanup failed: %d\n", err);
return err;
}
return 0;
}
static const struct host1x_client_ops dpaux_client_ops = {
.init = tegra_dpaux_init,
.exit = tegra_dpaux_exit,
};
static int tegra_dpaux_probe(struct platform_device *pdev)
{
struct tegra_dpaux *dpaux;
struct host1x_client *client;
struct resource *regs;
unsigned long value;
int err;
client = drm_host1x_get_client(&pdev->dev);
if (client) {
dpaux = host1x_client_to_dpaux(client);
} else {
dpaux = kzalloc(sizeof(*dpaux), GFP_KERNEL);
if (!dpaux)
return -ENOMEM;
INIT_LIST_HEAD(&dpaux->client.list);
dpaux->client.ops = &dpaux_client_ops;
dpaux->client.dev = &pdev->dev;
}
err = drm_host1x_register(&dpaux->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to register host1x client: %d\n",
err);
return err;
}
init_completion(&dpaux->complete);
INIT_LIST_HEAD(&dpaux->list);
dpaux->dev = &pdev->dev;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dpaux->regs = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(dpaux->regs))
return PTR_ERR(dpaux->regs);
dpaux->irq = platform_get_irq(pdev, 0);
if (dpaux->irq < 0) {
dev_err(&pdev->dev, "failed to get IRQ\n");
return -ENXIO;
}
dpaux->rst = devm_reset_control_get(&pdev->dev, "dpaux");
if (IS_ERR(dpaux->rst))
return PTR_ERR(dpaux->rst);
dpaux->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(dpaux->clk))
return PTR_ERR(dpaux->clk);
dpaux->clk_parent = devm_clk_get(&pdev->dev, "parent");
if (IS_ERR(dpaux->clk_parent))
return PTR_ERR(dpaux->clk_parent);
err = clk_prepare_enable(dpaux->clk_parent);
if (err < 0)
return err;
err = clk_set_rate(dpaux->clk_parent, 270000000);
if (err < 0) {
dev_err(&pdev->dev, "failed to set clock to 270 MHz: %d\n",
err);
return err;
}
err = devm_request_irq(dpaux->dev, dpaux->irq, tegra_dpaux_irq, 0,
dev_name(dpaux->dev), dpaux);
if (err < 0) {
dev_err(dpaux->dev, "failed to request IRQ#%u: %d\n",
dpaux->irq, err);
return err;
}
disable_irq(dpaux->irq);
dpaux->aux.transfer = tegra_dpaux_transfer;
dpaux->aux.dev = &pdev->dev;
err = drm_dp_aux_register_i2c_bus(&dpaux->aux);
if (err < 0)
return err;
/* enable and clear all interrupts */
value = DPAUX_INTR_AUX_DONE | DPAUX_INTR_IRQ_EVENT;
tegra_dpaux_writel(dpaux, value, DPAUX_INTR_EN_AUX);
tegra_dpaux_writel(dpaux, 0xffffffff, DPAUX_INTR_AUX);
mutex_lock(&dpaux_lock);
list_add_tail(&dpaux->list, &dpaux_list);
mutex_unlock(&dpaux_lock);
platform_set_drvdata(pdev, dpaux);
dpaux->client.driver_probed = 1;
dev_info(&pdev->dev, "initialized\n");
return 0;
}
static int tegra_dpaux_remove(struct platform_device *pdev)
{
struct tegra_dpaux *dpaux = platform_get_drvdata(pdev);
drm_dp_aux_unregister_i2c_bus(&dpaux->aux);
mutex_lock(&dpaux_lock);
list_del(&dpaux->list);
mutex_unlock(&dpaux_lock);
clk_disable_unprepare(dpaux->clk_parent);
reset_control_assert(dpaux->rst);
clk_disable_unprepare(dpaux->clk);
kfree(dpaux);
return 0;
}
static const struct of_device_id tegra_dpaux_of_match[] = {
{ .compatible = "nvidia,tegra124-dpaux", },
{ },
};
struct platform_driver tegra_dpaux_driver = {
.driver = {
.name = "tegra-dpaux",
.of_match_table = tegra_dpaux_of_match,
},
.probe = tegra_dpaux_probe,
.remove = tegra_dpaux_remove,
};
struct tegra_dpaux *tegra_dpaux_find_by_of_node(struct device_node *np)
{
struct tegra_dpaux *dpaux;
mutex_lock(&dpaux_lock);
list_for_each_entry(dpaux, &dpaux_list, list)
if (np == dpaux->dev->of_node) {
mutex_unlock(&dpaux_lock);
return dpaux;
}
mutex_unlock(&dpaux_lock);
return NULL;
}
int tegra_dpaux_attach(struct tegra_dpaux *dpaux, struct tegra_output *output)
{
unsigned long timeout;
output->connector.polled = DRM_CONNECTOR_POLL_HPD;
output->ddc = &dpaux->aux.ddc;
dpaux->output = output;
timeout = jiffies + msecs_to_jiffies(250);
while (time_before(jiffies, timeout)) {
enum drm_connector_status status;
status = tegra_dpaux_detect(dpaux);
if (status == connector_status_connected) {
enable_irq(dpaux->irq);
return 0;
}
usleep_range(1000, 2000);
}
return -ETIMEDOUT;
}
int tegra_dpaux_detach(struct tegra_dpaux *dpaux)
{
unsigned long timeout;
disable_irq(dpaux->irq);
timeout = jiffies + msecs_to_jiffies(250);
while (time_before(jiffies, timeout)) {
enum drm_connector_status status;
status = tegra_dpaux_detect(dpaux);
if (status == connector_status_disconnected) {
dpaux->output = NULL;
return 0;
}
usleep_range(1000, 2000);
}
return -ETIMEDOUT;
}
enum drm_connector_status tegra_dpaux_detect(struct tegra_dpaux *dpaux)
{
/* nyan doesn't support DP, so for eDP panel, it's always connected. */
return connector_status_connected;
}
int tegra_dpaux_enable(struct tegra_dpaux *dpaux)
{
unsigned long value;
int err;
if (dpaux->enabled)
return 0;
tegra_output_panel_prepare(dpaux->output);
tegra_unpowergate_partition(TEGRA_POWERGATE_SOR);
err = clk_prepare_enable(dpaux->clk);
if (err < 0) {
dev_info(dpaux->dev, "enable dpaux clock failed: %d\n", err);
tegra_powergate_partition(TEGRA_POWERGATE_SOR);
return err;
}
reset_control_deassert(dpaux->rst);
udelay(10);
/* enable and clear all interrupts */
value = DPAUX_INTR_AUX_DONE | DPAUX_INTR_IRQ_EVENT;
tegra_dpaux_writel(dpaux, value, DPAUX_INTR_EN_AUX);
tegra_dpaux_writel(dpaux, 0xffffffff, DPAUX_INTR_AUX);
value = DPAUX_HYBRID_PADCTL_AUX_CMH(2) |
DPAUX_HYBRID_PADCTL_AUX_DRVZ(4) |
DPAUX_HYBRID_PADCTL_AUX_DRVI(0x18) |
DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV |
DPAUX_HYBRID_PADCTL_MODE_AUX;
tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_PADCTL);
value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);
value &= ~DPAUX_HYBRID_SPARE_PAD_POWER_DOWN;
tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE);
dpaux->enabled = true;
return 0;
}
int tegra_dpaux_disable(struct tegra_dpaux *dpaux)
{
unsigned long value;
if (!dpaux->enabled)
return 0;
/* disable and clear all interrupts */
tegra_dpaux_writel(dpaux, 0, DPAUX_INTR_EN_AUX);
tegra_dpaux_writel(dpaux, 0xffffffff, DPAUX_INTR_AUX);
value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);
value |= DPAUX_HYBRID_SPARE_PAD_POWER_DOWN;
tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE);
tegra_powergate_partition(TEGRA_POWERGATE_SOR);
reset_control_assert(dpaux->rst);
udelay(10);
clk_disable_unprepare(dpaux->clk);
tegra_output_panel_unprepare(dpaux->output);
dpaux->enabled = false;
return 0;
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2013 NVIDIA Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef DRM_TEGRA_DPAUX_H
#define DRM_TEGRA_DPAUX_H
#define DPAUX_CTXSW 0x00
#define DPAUX_INTR_EN_AUX 0x01
#define DPAUX_INTR_AUX 0x05
#define DPAUX_INTR_AUX_DONE (1 << 3)
#define DPAUX_INTR_IRQ_EVENT (1 << 2)
#define DPAUX_INTR_UNPLUG_EVENT (1 << 1)
#define DPAUX_INTR_PLUG_EVENT (1 << 0)
#define DPAUX_DP_AUXDATA_WRITE(x) (0x09 + ((x) << 2))
#define DPAUX_DP_AUXDATA_READ(x) (0x19 + ((x) << 2))
#define DPAUX_DP_AUXADDR 0x29
#define DPAUX_DP_AUXCTL 0x2d
#define DPAUX_DP_AUXCTL_TRANSACTREQ (1 << 16)
#define DPAUX_DP_AUXCTL_CMD_AUX_RD (9 << 12)
#define DPAUX_DP_AUXCTL_CMD_AUX_WR (8 << 12)
#define DPAUX_DP_AUXCTL_CMD_MOT_RQ (6 << 12)
#define DPAUX_DP_AUXCTL_CMD_MOT_RD (5 << 12)
#define DPAUX_DP_AUXCTL_CMD_MOT_WR (4 << 12)
#define DPAUX_DP_AUXCTL_CMD_I2C_RQ (2 << 12)
#define DPAUX_DP_AUXCTL_CMD_I2C_RD (1 << 12)
#define DPAUX_DP_AUXCTL_CMD_I2C_WR (0 << 12)
#define DPAUX_DP_AUXCTL_CMD_ADDRESS_ONLY (1 << 8)
#define DPAUX_DP_AUXCTL_CMDLEN(x) ((x) & 0xff)
#define DPAUX_DP_AUXSTAT 0x31
#define DPAUX_DP_AUXSTAT_HPD_STATUS (1 << 28)
#define DPAUX_DP_AUXSTAT_REPLY_TYPE_MASK (0xf0000)
#define DPAUX_DP_AUXSTAT_NO_STOP_ERROR (1 << 11)
#define DPAUX_DP_AUXSTAT_SINKSTAT_ERROR (1 << 10)
#define DPAUX_DP_AUXSTAT_RX_ERROR (1 << 9)
#define DPAUX_DP_AUXSTAT_TIMEOUT_ERROR (1 << 8)
#define DPAUX_DP_AUXSTAT_REPLY_MASK (0xff)
#define DPAUX_DP_AUX_SINKSTAT_LO 0x35
#define DPAUX_DP_AUX_SINKSTAT_HI 0x39
#define DPAUX_HPD_CONFIG 0x3d
#define DPAUX_HPD_CONFIG_UNPLUG_MIN_TIME(x) (((x) & 0xffff) << 16)
#define DPAUX_HPD_CONFIG_PLUG_MIN_TIME(x) ((x) & 0xffff)
#define DPAUX_HPD_IRQ_CONFIG 0x41
#define DPAUX_HPD_IRQ_CONFIG_MIN_LOW_TIME(x) ((x) & 0xffff)
#define DPAUX_DP_AUX_CONFIG 0x45
#define DPAUX_HYBRID_PADCTL 0x49
#define DPAUX_HYBRID_PADCTL_AUX_CMH(x) (((x) & 0x3) << 12)
#define DPAUX_HYBRID_PADCTL_AUX_DRVZ(x) (((x) & 0x7) << 8)
#define DPAUX_HYBRID_PADCTL_AUX_DRVI(x) (((x) & 0x3f) << 2)
#define DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV (1 << 1)
#define DPAUX_HYBRID_PADCTL_MODE_I2C (1 << 0)
#define DPAUX_HYBRID_PADCTL_MODE_AUX (0 << 0)
#define DPAUX_HYBRID_SPARE 0x4d
#define DPAUX_HYBRID_SPARE_PAD_POWER_DOWN (1 << 0)
#define DPAUX_SCRATCH_REG0 0x51
#define DPAUX_SCRATCH_REG1 0x55
#define DPAUX_SCRATCH_REG2 0x59
#endif

867
drivers/gpu/drm/tegra/drm.c Normal file
View File

@@ -0,0 +1,867 @@
/*
* Copyright (C) 2012 Avionic Design GmbH
* Copyright (C) 2012-2013 NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/host1x.h>
#include <linux/iommu.h>
#include "drm.h"
#include "gem.h"
#define DRIVER_NAME "tegra"
#define DRIVER_DESC "NVIDIA Tegra graphics"
#define DRIVER_DATE "20120330"
#define DRIVER_MAJOR 0
#define DRIVER_MINOR 0
#define DRIVER_PATCHLEVEL 0
struct tegra_drm_file {
struct list_head contexts;
};
static struct platform_device *tegra_drm_pdev;
static struct drm_driver tegra_drm_driver;
struct iommu_domain *tegradrm_iommu_domain;
static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
{
struct host1x_device *device = to_host1x_device(drm->dev);
struct tegra_drm *tegra;
int err;
tegra = kzalloc(sizeof(*tegra), GFP_KERNEL);
if (!tegra)
return -ENOMEM;
tegra->domain = tegradrm_iommu_domain;
drm_mm_init(&tegra->mm, 0, SZ_2G);
dev_set_drvdata(drm->dev, tegra);
mutex_init(&tegra->clients_lock);
INIT_LIST_HEAD(&tegra->clients);
drm->dev_private = tegra;
tegra->drm = drm;
drm_mode_config_init(drm);
err = tegra_drm_fb_prepare(drm);
if (err < 0)
goto config;
drm_kms_helper_poll_init(drm);
err = drm_host1x_device_init(drm, device);
if (err < 0)
goto fbdev;
/*
* We don't use the drm_irq_install() helpers provided by the DRM
* core, so we need to set this manually in order to allow the
* DRM_IOCTL_WAIT_VBLANK to operate correctly.
*/
drm->irq_enabled = true;
err = drm_vblank_init(drm, drm->mode_config.num_crtc);
if (err < 0)
goto device;
err = tegra_drm_fb_init(drm);
if (err < 0)
goto vblank;
return 0;
vblank:
drm_vblank_cleanup(drm);
device:
drm_host1x_exit(&tegra_drm_driver, device);
fbdev:
drm_kms_helper_poll_fini(drm);
tegra_drm_fb_free(drm);
config:
drm_mode_config_cleanup(drm);
if (tegra->domain)
drm_mm_takedown(&tegra->mm);
kfree(tegra);
return err;
}
static int tegra_drm_unload(struct drm_device *drm)
{
struct host1x_device *device = to_host1x_device(drm->dev);
struct tegra_drm *tegra = drm->dev_private;
drm_kms_helper_poll_fini(drm);
tegra_drm_fb_exit(drm);
drm_vblank_cleanup(drm);
drm_mode_config_cleanup(drm);
drm_host1x_exit(&tegra_drm_driver, device);
if (tegra->domain)
drm_mm_takedown(&tegra->mm);
kfree(tegra);
return 0;
}
static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp)
{
struct tegra_drm_file *fpriv;
fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL);
if (!fpriv)
return -ENOMEM;
INIT_LIST_HEAD(&fpriv->contexts);
filp->driver_priv = fpriv;
return 0;
}
static void tegra_drm_context_free(struct tegra_drm_context *context)
{
context->client->ops->close_channel(context);
kfree(context);
}
static void tegra_drm_lastclose(struct drm_device *drm)
{
#ifdef CONFIG_DRM_TEGRA_FBDEV
struct tegra_drm *tegra = drm->dev_private;
tegra_fbdev_restore_mode(tegra->fbdev);
#endif
}
static struct tegra_drm_context *tegra_drm_get_context(__u64 context)
{
return (struct tegra_drm_context *)(uintptr_t)context;
}
static bool tegra_drm_file_owns_context(struct tegra_drm_file *file,
struct tegra_drm_context *context)
{
struct tegra_drm_context *ctx;
list_for_each_entry(ctx, &file->contexts, list)
if (ctx == context)
return true;
return false;
}
static int tegra_gem_create(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct drm_tegra_gem_create *args = data;
struct tegra_bo *bo;
bo = tegra_bo_create_with_handle(file, drm, args->size, args->flags,
&args->handle);
if (IS_ERR(bo))
return PTR_ERR(bo);
return 0;
}
static int tegra_gem_mmap(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct drm_tegra_gem_mmap *args = data;
struct drm_gem_object *gem;
struct tegra_bo *bo;
gem = drm_gem_object_lookup(drm, file, args->handle);
if (!gem)
return -EINVAL;
bo = to_tegra_bo(gem);
args->offset = drm_vma_node_offset_addr(&bo->gem.vma_node);
drm_gem_object_unreference(gem);
return 0;
}
#ifdef CONFIG_DRM_TEGRA_STAGING
static int tegra_syncpt_read(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct host1x *host = dev_get_drvdata(drm->dev->parent);
struct drm_tegra_syncpt_read *args = data;
struct host1x_syncpt *sp;
sp = host1x_syncpt_get(host, args->id);
if (!sp)
return -EINVAL;
args->value = host1x_syncpt_read_min(sp);
return 0;
}
static int tegra_syncpt_incr(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
struct drm_tegra_syncpt_incr *args = data;
struct host1x_syncpt *sp;
sp = host1x_syncpt_get(host1x, args->id);
if (!sp)
return -EINVAL;
return host1x_syncpt_incr(sp);
}
static int tegra_syncpt_wait(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
struct drm_tegra_syncpt_wait *args = data;
struct host1x_syncpt *sp;
sp = host1x_syncpt_get(host1x, args->id);
if (!sp)
return -EINVAL;
return host1x_syncpt_wait(sp, args->thresh, args->timeout,
&args->value);
}
#endif
static int tegra_open_channel(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct tegra_drm_file *fpriv = file->driver_priv;
struct tegra_drm *tegra = drm->dev_private;
struct drm_tegra_open_channel *args = data;
struct tegra_drm_context *context;
struct tegra_drm_client *client;
int err = -ENODEV;
context = kzalloc(sizeof(*context), GFP_KERNEL);
if (!context)
return -ENOMEM;
list_for_each_entry(client, &tegra->clients, list)
if (client->base.class == args->client) {
err = client->ops->open_channel(client, context);
if (err)
break;
list_add(&context->list, &fpriv->contexts);
args->context = (uintptr_t)context;
context->client = client;
return 0;
}
kfree(context);
return err;
}
static int tegra_close_channel(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct tegra_drm_file *fpriv = file->driver_priv;
struct drm_tegra_close_channel *args = data;
struct tegra_drm_context *context;
context = tegra_drm_get_context(args->context);
if (!tegra_drm_file_owns_context(fpriv, context))
return -EINVAL;
list_del(&context->list);
tegra_drm_context_free(context);
return 0;
}
#ifdef CONFIG_DRM_TEGRA_STAGING
static int tegra_get_syncpt(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct tegra_drm_file *fpriv = file->driver_priv;
struct drm_tegra_get_syncpt *args = data;
struct tegra_drm_context *context;
struct host1x_syncpt *syncpt;
context = tegra_drm_get_context(args->context);
if (!tegra_drm_file_owns_context(fpriv, context))
return -ENODEV;
if (args->index >= context->client->base.num_syncpts)
return -EINVAL;
syncpt = context->client->base.syncpts[args->index];
args->id = host1x_syncpt_id(syncpt);
return 0;
}
#endif
static int tegra_submit(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct tegra_drm_file *fpriv = file->driver_priv;
struct drm_tegra_submit *args = data;
struct tegra_drm_context *context;
context = tegra_drm_get_context(args->context);
if (!tegra_drm_file_owns_context(fpriv, context))
return -ENODEV;
return context->client->ops->submit(context, args, drm, file);
}
#ifdef CONFIG_DRM_TEGRA_STAGING
static int tegra_get_syncpt_base(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct tegra_drm_file *fpriv = file->driver_priv;
struct drm_tegra_get_syncpt_base *args = data;
struct tegra_drm_context *context;
struct host1x_syncpt_base *base;
struct host1x_syncpt *syncpt;
context = tegra_drm_get_context(args->context);
if (!tegra_drm_file_owns_context(fpriv, context))
return -ENODEV;
if (args->syncpt >= context->client->base.num_syncpts)
return -EINVAL;
syncpt = context->client->base.syncpts[args->syncpt];
base = host1x_syncpt_get_base(syncpt);
if (!base)
return -ENXIO;
args->id = host1x_syncpt_base_id(base);
return 0;
}
#endif
static int tegra_gem_set_tiling(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct drm_tegra_gem_set_tiling *args = data;
enum tegra_bo_tiling_mode mode;
struct drm_gem_object *gem;
unsigned long value = 0;
struct tegra_bo *bo;
switch (args->mode) {
case DRM_TEGRA_GEM_TILING_MODE_PITCH:
mode = TEGRA_BO_TILING_MODE_PITCH;
if (args->value != 0)
return -EINVAL;
break;
case DRM_TEGRA_GEM_TILING_MODE_TILED:
mode = TEGRA_BO_TILING_MODE_TILED;
if (args->value != 0)
return -EINVAL;
break;
case DRM_TEGRA_GEM_TILING_MODE_BLOCK:
mode = TEGRA_BO_TILING_MODE_BLOCK;
if (args->value > 5)
return -EINVAL;
value = args->value;
break;
default:
return -EINVAL;
}
gem = drm_gem_object_lookup(drm, file, args->handle);
if (!gem)
return -ENOENT;
bo = to_tegra_bo(gem);
bo->tiling.mode = mode;
bo->tiling.value = value;
drm_gem_object_unreference(gem);
return 0;
}
static int tegra_gem_get_tiling(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct drm_tegra_gem_get_tiling *args = data;
struct drm_gem_object *gem;
struct tegra_bo *bo;
int err = 0;
gem = drm_gem_object_lookup(drm, file, args->handle);
if (!gem)
return -ENOENT;
bo = to_tegra_bo(gem);
switch (bo->tiling.mode) {
case TEGRA_BO_TILING_MODE_PITCH:
args->mode = DRM_TEGRA_GEM_TILING_MODE_PITCH;
args->value = 0;
break;
case TEGRA_BO_TILING_MODE_TILED:
args->mode = DRM_TEGRA_GEM_TILING_MODE_TILED;
args->value = 0;
break;
case TEGRA_BO_TILING_MODE_BLOCK:
args->mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK;
args->value = bo->tiling.value;
break;
default:
err = -EINVAL;
break;
}
drm_gem_object_unreference(gem);
return err;
}
static int tegra_gem_set_flags(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct drm_tegra_gem_set_flags *args = data;
struct drm_gem_object *gem;
struct tegra_bo *bo;
if (args->flags & ~DRM_TEGRA_GEM_FLAGS)
return -EINVAL;
gem = drm_gem_object_lookup(drm, file, args->handle);
if (!gem)
return -ENOENT;
bo = to_tegra_bo(gem);
bo->flags = 0;
if (args->flags & DRM_TEGRA_GEM_BOTTOM_UP)
bo->flags |= TEGRA_BO_BOTTOM_UP;
drm_gem_object_unreference(gem);
return 0;
}
static int tegra_gem_get_flags(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct drm_tegra_gem_get_flags *args = data;
struct drm_gem_object *gem;
struct tegra_bo *bo;
gem = drm_gem_object_lookup(drm, file, args->handle);
if (!gem)
return -ENOENT;
bo = to_tegra_bo(gem);
args->flags = 0;
if (bo->flags & TEGRA_BO_BOTTOM_UP)
args->flags |= DRM_TEGRA_GEM_BOTTOM_UP;
drm_gem_object_unreference(gem);
return 0;
}
static const struct drm_ioctl_desc tegra_drm_ioctls[] = {
DRM_IOCTL_DEF_DRV(TEGRA_GEM_CREATE, tegra_gem_create, DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_GEM_MMAP, tegra_gem_mmap, DRM_UNLOCKED|DRM_RENDER_ALLOW),
#ifdef CONFIG_DRM_TEGRA_STAGING
DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_READ, tegra_syncpt_read, DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_INCR, tegra_syncpt_incr, DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_WAIT, tegra_syncpt_wait, DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT, tegra_get_syncpt, DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT_BASE, tegra_get_syncpt_base, DRM_UNLOCKED|DRM_RENDER_ALLOW),
#endif
DRM_IOCTL_DEF_DRV(TEGRA_OPEN_CHANNEL, tegra_open_channel, DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_CLOSE_CHANNEL, tegra_close_channel, DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_SUBMIT, tegra_submit, DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_GEM_SET_TILING, tegra_gem_set_tiling, DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_GEM_GET_TILING, tegra_gem_get_tiling, DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_GEM_SET_FLAGS, tegra_gem_set_flags, DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_GEM_GET_FLAGS, tegra_gem_get_flags, DRM_UNLOCKED|DRM_RENDER_ALLOW),
};
static const struct file_operations tegra_drm_fops = {
.owner = THIS_MODULE,
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = drm_ioctl,
.mmap = tegra_drm_mmap,
.poll = drm_poll,
.read = drm_read,
#ifdef CONFIG_COMPAT
.compat_ioctl = drm_compat_ioctl,
#endif
.llseek = noop_llseek,
};
static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm, int pipe)
{
struct drm_crtc *crtc;
list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) {
struct tegra_dc *dc = to_tegra_dc(crtc);
if (dc->pipe == pipe)
return crtc;
}
return NULL;
}
static u32 tegra_drm_get_vblank_counter(struct drm_device *dev, int crtc)
{
/* TODO: implement real hardware counter using syncpoints */
return drm_vblank_count(dev, crtc);
}
static int tegra_drm_enable_vblank(struct drm_device *drm, int pipe)
{
struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
struct tegra_dc *dc = to_tegra_dc(crtc);
if (!crtc)
return -ENODEV;
tegra_dc_enable_vblank(dc);
return 0;
}
static void tegra_drm_disable_vblank(struct drm_device *drm, int pipe)
{
struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
struct tegra_dc *dc = to_tegra_dc(crtc);
if (crtc)
tegra_dc_disable_vblank(dc);
}
static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file)
{
struct tegra_drm_file *fpriv = file->driver_priv;
struct tegra_drm_context *context, *tmp;
struct drm_crtc *crtc;
list_for_each_entry(crtc, &drm->mode_config.crtc_list, head)
tegra_dc_cancel_page_flip(crtc, file);
list_for_each_entry_safe(context, tmp, &fpriv->contexts, list)
tegra_drm_context_free(context);
kfree(fpriv);
}
#ifdef CONFIG_DEBUG_FS
static int tegra_debugfs_framebuffers(struct seq_file *s, void *data)
{
struct drm_info_node *node = (struct drm_info_node *)s->private;
struct drm_device *drm = node->minor->dev;
struct drm_framebuffer *fb;
mutex_lock(&drm->mode_config.fb_lock);
list_for_each_entry(fb, &drm->mode_config.fb_list, head) {
seq_printf(s, "%3d: user size: %d x %d, depth %d, %d bpp, refcount %d\n",
fb->base.id, fb->width, fb->height, fb->depth,
fb->bits_per_pixel,
atomic_read(&fb->refcount.refcount));
}
mutex_unlock(&drm->mode_config.fb_lock);
return 0;
}
static struct drm_info_list tegra_debugfs_list[] = {
{ "framebuffers", tegra_debugfs_framebuffers, 0 },
};
static int tegra_debugfs_init(struct drm_minor *minor)
{
return drm_debugfs_create_files(tegra_debugfs_list,
ARRAY_SIZE(tegra_debugfs_list),
minor->debugfs_root, minor);
}
static void tegra_debugfs_cleanup(struct drm_minor *minor)
{
drm_debugfs_remove_files(tegra_debugfs_list,
ARRAY_SIZE(tegra_debugfs_list), minor);
}
#endif
static struct drm_driver tegra_drm_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_RENDER,
.load = tegra_drm_load,
.unload = tegra_drm_unload,
.open = tegra_drm_open,
.preclose = tegra_drm_preclose,
.lastclose = tegra_drm_lastclose,
.get_vblank_counter = tegra_drm_get_vblank_counter,
.enable_vblank = tegra_drm_enable_vblank,
.disable_vblank = tegra_drm_disable_vblank,
#if defined(CONFIG_DEBUG_FS)
.debugfs_init = tegra_debugfs_init,
.debugfs_cleanup = tegra_debugfs_cleanup,
#endif
.gem_free_object = tegra_bo_free_object,
.gem_vm_ops = &tegra_bo_vm_ops,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_export = tegra_gem_prime_export,
.gem_prime_import = tegra_gem_prime_import,
.dumb_create = tegra_bo_dumb_create,
.dumb_map_offset = tegra_bo_dumb_map_offset,
.dumb_destroy = drm_gem_dumb_destroy,
.ioctls = tegra_drm_ioctls,
.num_ioctls = ARRAY_SIZE(tegra_drm_ioctls),
.fops = &tegra_drm_fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
.patchlevel = DRIVER_PATCHLEVEL,
};
int tegra_drm_register_client(struct tegra_drm *tegra,
struct tegra_drm_client *client)
{
mutex_lock(&tegra->clients_lock);
list_add_tail(&client->list, &tegra->clients);
mutex_unlock(&tegra->clients_lock);
return 0;
}
int tegra_drm_unregister_client(struct tegra_drm *tegra,
struct tegra_drm_client *client)
{
mutex_lock(&tegra->clients_lock);
list_del_init(&client->list);
mutex_unlock(&tegra->clients_lock);
return 0;
}
static const struct of_device_id host1x_drm_subdevs[] = {
{ .compatible = "nvidia,tegra20-dc", },
{ .compatible = "nvidia,tegra20-hdmi", },
{ .compatible = "nvidia,tegra20-gr2d", },
{ .compatible = "nvidia,tegra20-gr3d", },
{ .compatible = "nvidia,tegra30-dc", },
{ .compatible = "nvidia,tegra30-hdmi", },
{ .compatible = "nvidia,tegra30-gr2d", },
{ .compatible = "nvidia,tegra30-gr3d", },
{ .compatible = "nvidia,tegra114-dsi", },
{ .compatible = "nvidia,tegra114-hdmi", },
{ .compatible = "nvidia,tegra114-gr3d", },
{ .compatible = "nvidia,tegra124-dc", },
{ .compatible = "nvidia,tegra124-sor", },
{ .compatible = "nvidia,tegra124-hdmi", },
{ /* sentinel */ }
};
static int tegra_drm_platform_probe(struct platform_device *pdev)
{
if (!drm_host1x_check_clients_probed()) {
dev_info(&pdev->dev,
"clients are still probing, wait until they're done.\n");
return -EPROBE_DEFER;
}
return drm_platform_init(&tegra_drm_driver, pdev);
}
static int tegra_drm_platform_remove(struct platform_device *pdev)
{
drm_platform_exit(&tegra_drm_driver, pdev);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int tegra_drm_platform_suspend(struct device *dev)
{
struct tegra_drm *tegra = dev_get_drvdata(dev);
struct drm_device *drm = tegra->drm;
struct drm_connector *connector;
struct drm_encoder *encoder;
struct drm_crtc *crtc;
struct drm_encoder_helper_funcs *encoder_funcs = NULL;
struct drm_crtc_helper_funcs *crtc_funcs = NULL;
drm_kms_helper_poll_disable(drm);
drm_modeset_lock_all(drm);
list_for_each_entry(connector, &drm->mode_config.connector_list, head) {
if (connector->funcs->save)
connector->funcs->save(connector);
}
list_for_each_entry(encoder, &drm->mode_config.encoder_list, head) {
if (!drm_helper_encoder_in_use(encoder))
continue;
encoder_funcs = encoder->helper_private;
if (encoder_funcs->disable)
(*encoder_funcs->disable)(encoder);
list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) {
crtc_funcs = crtc->helper_private;
if (encoder->crtc == crtc && crtc_funcs->disable)
(*crtc_funcs->disable)(crtc);
}
}
drm_modeset_unlock_all(drm);
return 0;
}
static int tegra_drm_platform_resume(struct device *dev)
{
struct tegra_drm *tegra = dev_get_drvdata(dev);
struct drm_device *drm = tegra->drm;
struct drm_connector *connector;
drm_modeset_lock_all(drm);
drm_helper_resume_force_mode(drm);
list_for_each_entry(connector, &drm->mode_config.connector_list, head) {
if (connector->funcs->restore)
connector->funcs->restore(connector);
}
drm_modeset_unlock_all(drm);
drm_kms_helper_poll_enable(drm);
return 0;
}
#endif
static const struct dev_pm_ops tegra_drm_platform_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(tegra_drm_platform_suspend, tegra_drm_platform_resume)
};
static struct platform_driver tegra_drm_platform_driver = {
.probe = tegra_drm_platform_probe,
.remove = tegra_drm_platform_remove,
.driver = {
.owner = THIS_MODULE,
.name = "tegra-drm",
.pm = &tegra_drm_platform_pm_ops,
},
};
static int __init host1x_drm_init(void)
{
int err;
/*
* Will get the last smmu AS here because smmu probes before tegradrm.
* And smmu driver will allocate (num_as - NUM_OF_RESERVED_AS) domains when probe.
*/
if (iommu_present(&platform_bus_type)) {
tegradrm_iommu_domain = iommu_domain_alloc(&platform_bus_type);
if (IS_ERR(tegradrm_iommu_domain)) {
err = PTR_ERR(tegradrm_iommu_domain);
return err;
}
}
err = platform_driver_register(&tegra_dpaux_driver);
if (err < 0)
goto out;
err = platform_driver_register(&tegra_dc_driver);
if (err < 0)
goto unregister_dpaux;
if (err < 0)
goto unregister_dc;
err = platform_driver_register(&tegra_sor_driver);
if (err < 0)
goto unregister_dsi;
err = platform_driver_register(&tegra_hdmi_driver);
if (err < 0)
goto unregister_sor;
err = platform_driver_register(&tegra_drm_platform_driver);
if (err < 0)
goto unregister_hdmi;
tegra_drm_pdev = platform_device_register_simple("tegra-drm", -1, NULL, 0);
if (IS_ERR(tegra_drm_pdev)) {
err = PTR_ERR(tegra_drm_pdev);
goto unregister_platform;
}
return 0;
unregister_platform:
platform_driver_unregister(&tegra_drm_platform_driver);
unregister_hdmi:
platform_driver_unregister(&tegra_hdmi_driver);
unregister_sor:
platform_driver_unregister(&tegra_sor_driver);
unregister_dsi:
unregister_dc:
platform_driver_unregister(&tegra_dc_driver);
unregister_dpaux:
platform_driver_unregister(&tegra_dpaux_driver);
out:
return err;
}
module_init(host1x_drm_init);
static void __exit host1x_drm_exit(void)
{
platform_driver_unregister(&tegra_hdmi_driver);
platform_driver_unregister(&tegra_sor_driver);
platform_driver_unregister(&tegra_dc_driver);
platform_driver_unregister(&tegra_dpaux_driver);
if (tegradrm_iommu_domain)
iommu_domain_free(tegradrm_iommu_domain);
}
module_exit(host1x_drm_exit);
MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
MODULE_DESCRIPTION("NVIDIA Tegra DRM driver");
MODULE_LICENSE("GPL v2");

342
drivers/gpu/drm/tegra/drm.h Normal file
View File

@@ -0,0 +1,342 @@
/*
* Copyright (C) 2012 Avionic Design GmbH
* Copyright (C) 2012-2013 NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef HOST1X_DRM_H
#define HOST1X_DRM_H 1
#include <uapi/drm/tegra_drm.h>
#include <linux/host1x.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_fixed.h>
#include "gem.h"
struct reset_control;
struct tegra_fb {
struct drm_framebuffer base;
struct tegra_bo **planes;
unsigned int num_planes;
};
#ifdef CONFIG_DRM_TEGRA_FBDEV
struct tegra_fbdev {
struct drm_fb_helper base;
struct tegra_fb *fb;
};
#endif
struct tegra_drm {
struct drm_device *drm;
struct iommu_domain *domain;
struct drm_mm mm;
struct mutex clients_lock;
struct list_head clients;
#ifdef CONFIG_DRM_TEGRA_FBDEV
struct tegra_fbdev *fbdev;
#endif
unsigned int pitch_align;
};
struct tegra_drm_client;
struct tegra_drm_context {
struct tegra_drm_client *client;
struct host1x_channel *channel;
struct list_head list;
};
struct tegra_drm_client_ops {
int (*open_channel)(struct tegra_drm_client *client,
struct tegra_drm_context *context);
void (*close_channel)(struct tegra_drm_context *context);
int (*is_addr_reg)(struct device *dev, u32 class, u32 offset);
int (*submit)(struct tegra_drm_context *context,
struct drm_tegra_submit *args, struct drm_device *drm,
struct drm_file *file);
};
int tegra_drm_submit(struct tegra_drm_context *context,
struct drm_tegra_submit *args, struct drm_device *drm,
struct drm_file *file);
struct tegra_drm_client {
struct host1x_client base;
struct list_head list;
const struct tegra_drm_client_ops *ops;
};
static inline struct tegra_drm_client *
host1x_to_drm_client(struct host1x_client *client)
{
return container_of(client, struct tegra_drm_client, base);
}
int tegra_drm_register_client(struct tegra_drm *tegra,
struct tegra_drm_client *client);
int tegra_drm_unregister_client(struct tegra_drm *tegra,
struct tegra_drm_client *client);
int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm);
int tegra_drm_exit(struct tegra_drm *tegra);
struct tegra_dc_soc_info;
struct tegra_output;
struct dirty_fb {
struct list_head list;
struct tegra_bo *bo;
u32 offset;
};
struct tegra_dc {
struct host1x_client client;
struct device *dev;
spinlock_t lock;
struct drm_crtc base;
int pipe;
struct clk *clk;
struct reset_control *rst;
struct clk *emc_clk;
void __iomem *regs;
int irq;
struct tegra_output *rgb;
struct list_head list;
struct dirty_fb *fb;
struct list_head dirty_fbs;
spinlock_t dirty_fbs_lock;
struct work_struct dirty_fbs_work;
struct drm_info_list *debugfs_files;
struct drm_minor *minor;
struct dentry *debugfs;
/* page-flip handling */
struct drm_pending_vblank_event *event;
const struct tegra_dc_soc_info *soc;
struct iommu_domain *domain;
struct tegra_bo *shadow_cursor;
bool enabled;
};
static inline struct tegra_dc *
host1x_client_to_dc(struct host1x_client *client)
{
return container_of(client, struct tegra_dc, client);
}
static inline struct tegra_dc *to_tegra_dc(struct drm_crtc *crtc)
{
return crtc ? container_of(crtc, struct tegra_dc, base) : NULL;
}
static inline void tegra_dc_writel(struct tegra_dc *dc, unsigned long value,
unsigned long reg)
{
writel(value, dc->regs + (reg << 2));
}
static inline unsigned long tegra_dc_readl(struct tegra_dc *dc,
unsigned long reg)
{
return readl(dc->regs + (reg << 2));
}
struct tegra_dc_window {
struct {
unsigned int x;
unsigned int y;
unsigned int w;
unsigned int h;
} src;
struct {
unsigned int x;
unsigned int y;
unsigned int w;
unsigned int h;
} dst;
unsigned int bits_per_pixel;
unsigned int format;
unsigned int swap;
unsigned int stride[2];
unsigned long base[3];
bool bottom_up;
struct tegra_bo_tiling tiling;
};
/* from dc.c */
void tegra_dc_enable_vblank(struct tegra_dc *dc);
void tegra_dc_disable_vblank(struct tegra_dc *dc);
void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file);
struct tegra_output_ops {
int (*enable)(struct tegra_output *output);
int (*disable)(struct tegra_output *output);
int (*setup_clock)(struct tegra_output *output, struct clk *clk,
unsigned long pclk, unsigned int *div);
int (*check_mode)(struct tegra_output *output,
struct drm_display_mode *mode,
enum drm_mode_status *status);
enum drm_connector_status (*detect)(struct tegra_output *output);
};
enum tegra_output_type {
TEGRA_OUTPUT_RGB,
TEGRA_OUTPUT_HDMI,
TEGRA_OUTPUT_DSI,
TEGRA_OUTPUT_EDP,
};
struct tegra_output {
struct device_node *of_node;
struct device *dev;
const struct tegra_output_ops *ops;
enum tegra_output_type type;
struct drm_panel *panel;
bool panel_enabled;
bool panel_prepared;
struct i2c_adapter *ddc;
const struct edid *edid;
unsigned int hpd_irq;
int hpd_gpio;
bool enabled;
bool suspended;
struct drm_display_mode saved_mode;
struct drm_encoder encoder;
struct drm_connector connector;
};
static inline struct tegra_output *encoder_to_output(struct drm_encoder *e)
{
return container_of(e, struct tegra_output, encoder);
}
static inline struct tegra_output *connector_to_output(struct drm_connector *c)
{
return container_of(c, struct tegra_output, connector);
}
static inline int tegra_output_enable(struct tegra_output *output)
{
if (output && output->ops && output->ops->enable)
return output->ops->enable(output);
return output ? -ENOSYS : -EINVAL;
}
static inline int tegra_output_disable(struct tegra_output *output)
{
if (output && output->ops && output->ops->disable)
return output->ops->disable(output);
return output ? -ENOSYS : -EINVAL;
}
static inline int tegra_output_setup_clock(struct tegra_output *output,
struct clk *clk, unsigned long pclk,
unsigned int *div)
{
if (output && output->ops && output->ops->setup_clock)
return output->ops->setup_clock(output, clk, pclk, div);
return output ? -ENOSYS : -EINVAL;
}
static inline int tegra_output_check_mode(struct tegra_output *output,
struct drm_display_mode *mode,
enum drm_mode_status *status)
{
if (output && output->ops && output->ops->check_mode)
return output->ops->check_mode(output, mode, status);
return output ? -ENOSYS : -EINVAL;
}
/* from bus.c */
int drm_host1x_init(struct drm_driver *driver, struct host1x_device *device);
void drm_host1x_exit(struct drm_driver *driver, struct host1x_device *device);
int drm_host1x_register(struct host1x_client *client);
int drm_host1x_unregister(struct host1x_client *client);
int drm_host1x_device_init(struct drm_device *drm, struct host1x_device *device);
int drm_host1x_device_exit(struct host1x_device *device);
struct host1x_client *drm_host1x_get_client(struct device *dev);
int drm_host1x_check_clients_probed(void);
/* from rgb.c */
int tegra_dc_rgb_probe(struct tegra_dc *dc);
int tegra_dc_rgb_remove(struct tegra_dc *dc);
int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc);
int tegra_dc_rgb_exit(struct tegra_dc *dc);
/* from output.c */
int tegra_output_probe(struct tegra_output *output);
int tegra_output_remove(struct tegra_output *output);
int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
int tegra_output_exit(struct tegra_output *output);
int tegra_output_panel_enable(struct tegra_output *output);
int tegra_output_panel_disable(struct tegra_output *output);
int tegra_output_panel_prepare(struct tegra_output *output);
int tegra_output_panel_unprepare(struct tegra_output *output);
/* from dpaux.c */
struct tegra_dpaux;
struct drm_dp_link;
struct tegra_dpaux *tegra_dpaux_find_by_of_node(struct device_node *np);
enum drm_connector_status tegra_dpaux_detect(struct tegra_dpaux *dpaux);
int tegra_dpaux_attach(struct tegra_dpaux *dpaux, struct tegra_output *output);
int tegra_dpaux_detach(struct tegra_dpaux *dpaux);
int tegra_dpaux_enable(struct tegra_dpaux *dpaux);
int tegra_dpaux_disable(struct tegra_dpaux *dpaux);
/* from fb.c */
struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
unsigned int index);
bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer);
int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer,
struct tegra_bo_tiling *tiling);
int tegra_drm_fb_prepare(struct drm_device *drm);
void tegra_drm_fb_free(struct drm_device *drm);
int tegra_drm_fb_init(struct drm_device *drm);
void tegra_drm_fb_exit(struct drm_device *drm);
#ifdef CONFIG_DRM_TEGRA_FBDEV
void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev);
#endif
extern struct platform_driver tegra_dc_driver;
extern struct platform_driver tegra_dsi_driver;
extern struct platform_driver tegra_sor_driver;
extern struct platform_driver tegra_hdmi_driver;
extern struct platform_driver tegra_dpaux_driver;
extern struct platform_driver tegra_gr2d_driver;
extern struct platform_driver tegra_gr3d_driver;
#endif /* HOST1X_DRM_H */

451
drivers/gpu/drm/tegra/fb.c Normal file
View File

@@ -0,0 +1,451 @@
/*
* Copyright (C) 2012-2013 Avionic Design GmbH
* Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
*
* Based on the KMS/FB CMA helpers
* Copyright (C) 2012 Analog Device Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "drm.h"
#include "gem.h"
static inline struct tegra_fb *to_tegra_fb(struct drm_framebuffer *fb)
{
return container_of(fb, struct tegra_fb, base);
}
#ifdef CONFIG_DRM_TEGRA_FBDEV
static inline struct tegra_fbdev *to_tegra_fbdev(struct drm_fb_helper *helper)
{
return container_of(helper, struct tegra_fbdev, base);
}
#endif
struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
unsigned int index)
{
struct tegra_fb *fb = to_tegra_fb(framebuffer);
if (index >= drm_format_num_planes(framebuffer->pixel_format))
return NULL;
return fb->planes[index];
}
bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer)
{
struct tegra_fb *fb = to_tegra_fb(framebuffer);
if (fb->planes[0]->flags & TEGRA_BO_BOTTOM_UP)
return true;
return false;
}
int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer,
struct tegra_bo_tiling *tiling)
{
struct tegra_fb *fb = to_tegra_fb(framebuffer);
/* TODO: handle YUV formats? */
*tiling = fb->planes[0]->tiling;
return 0;
}
static void tegra_fb_destroy(struct drm_framebuffer *framebuffer)
{
struct tegra_fb *fb = to_tegra_fb(framebuffer);
unsigned int i;
for (i = 0; i < fb->num_planes; i++) {
struct tegra_bo *bo = fb->planes[i];
if (bo) {
if (bo->pages && bo->vaddr)
vunmap(bo->vaddr);
drm_gem_object_unreference_unlocked(&bo->gem);
}
}
drm_framebuffer_cleanup(framebuffer);
kfree(fb->planes);
kfree(fb);
}
static int tegra_fb_create_handle(struct drm_framebuffer *framebuffer,
struct drm_file *file, unsigned int *handle)
{
struct tegra_fb *fb = to_tegra_fb(framebuffer);
return drm_gem_handle_create(file, &fb->planes[0]->gem, handle);
}
static struct drm_framebuffer_funcs tegra_fb_funcs = {
.destroy = tegra_fb_destroy,
.create_handle = tegra_fb_create_handle,
};
static struct tegra_fb *tegra_fb_alloc(struct drm_device *drm,
struct drm_mode_fb_cmd2 *mode_cmd,
struct tegra_bo **planes,
unsigned int num_planes)
{
struct tegra_fb *fb;
unsigned int i;
int err;
fb = kzalloc(sizeof(*fb), GFP_KERNEL);
if (!fb)
return ERR_PTR(-ENOMEM);
fb->planes = kzalloc(num_planes * sizeof(*planes), GFP_KERNEL);
if (!fb->planes) {
kfree(fb);
return ERR_PTR(-ENOMEM);
}
fb->num_planes = num_planes;
drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd);
for (i = 0; i < fb->num_planes; i++)
fb->planes[i] = planes[i];
err = drm_framebuffer_init(drm, &fb->base, &tegra_fb_funcs);
if (err < 0) {
dev_err(drm->dev, "failed to initialize framebuffer: %d\n",
err);
kfree(fb->planes);
kfree(fb);
return ERR_PTR(err);
}
return fb;
}
static struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
struct drm_file *file,
struct drm_mode_fb_cmd2 *cmd)
{
unsigned int hsub, vsub, i;
struct tegra_bo *planes[4];
struct drm_gem_object *gem;
struct tegra_fb *fb;
int err;
hsub = drm_format_horz_chroma_subsampling(cmd->pixel_format);
vsub = drm_format_vert_chroma_subsampling(cmd->pixel_format);
for (i = 0; i < drm_format_num_planes(cmd->pixel_format); i++) {
unsigned int width = cmd->width / (i ? hsub : 1);
unsigned int height = cmd->height / (i ? vsub : 1);
unsigned int size, bpp;
gem = drm_gem_object_lookup(drm, file, cmd->handles[i]);
if (!gem) {
err = -ENXIO;
goto unreference;
}
bpp = drm_format_plane_cpp(cmd->pixel_format, i);
size = (height - 1) * cmd->pitches[i] +
width * bpp + cmd->offsets[i];
if (gem->size < size) {
err = -EINVAL;
goto unreference;
}
planes[i] = to_tegra_bo(gem);
}
fb = tegra_fb_alloc(drm, cmd, planes, i);
if (IS_ERR(fb)) {
err = PTR_ERR(fb);
goto unreference;
}
return &fb->base;
unreference:
while (i--)
drm_gem_object_unreference_unlocked(&planes[i]->gem);
return ERR_PTR(err);
}
#ifdef CONFIG_DRM_TEGRA_FBDEV
static struct fb_ops tegra_fb_ops = {
.owner = THIS_MODULE,
.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_blank = drm_fb_helper_blank,
.fb_pan_display = drm_fb_helper_pan_display,
.fb_setcmap = drm_fb_helper_setcmap,
};
static int tegra_fbdev_probe(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
struct tegra_fbdev *fbdev = to_tegra_fbdev(helper);
struct tegra_drm *tegra = helper->dev->dev_private;
struct drm_device *drm = helper->dev;
struct drm_mode_fb_cmd2 cmd = { 0 };
unsigned int bytes_per_pixel;
struct drm_framebuffer *fb;
unsigned long offset;
struct fb_info *info;
struct tegra_bo *bo;
size_t size;
int err;
bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
cmd.width = sizes->surface_width;
cmd.height = sizes->surface_height;
cmd.pitches[0] = round_up(sizes->surface_width * bytes_per_pixel,
tegra->pitch_align);
cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
sizes->surface_depth);
size = cmd.pitches[0] * cmd.height;
bo = tegra_bo_create(drm, size, 0);
if (IS_ERR(bo))
return PTR_ERR(bo);
info = framebuffer_alloc(0, drm->dev);
if (!info) {
dev_err(drm->dev, "failed to allocate framebuffer info\n");
tegra_bo_free_object(&bo->gem);
return -ENOMEM;
}
fbdev->fb = tegra_fb_alloc(drm, &cmd, &bo, 1);
if (IS_ERR(fbdev->fb)) {
dev_err(drm->dev, "failed to allocate DRM framebuffer\n");
err = PTR_ERR(fbdev->fb);
goto release;
}
fb = &fbdev->fb->base;
helper->fb = fb;
helper->fbdev = info;
info->par = helper;
info->flags = FBINFO_FLAG_DEFAULT;
info->fbops = &tegra_fb_ops;
err = fb_alloc_cmap(&info->cmap, 256, 0);
if (err < 0) {
dev_err(drm->dev, "failed to allocate color map: %d\n", err);
goto destroy;
}
drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
drm_fb_helper_fill_var(info, helper, fb->width, fb->height);
offset = info->var.xoffset * bytes_per_pixel +
info->var.yoffset * fb->pitches[0];
if (bo->pages) {
bo->vaddr = vmap(bo->pages, bo->num_pages, VM_MAP,
pgprot_writecombine(PAGE_KERNEL));
if (!bo->vaddr) {
dev_err(drm->dev, "failed to vmap() framebuffer\n");
err = -ENOMEM;
goto destroy;
}
}
drm->mode_config.fb_base = (resource_size_t)bo->paddr;
info->screen_base = (void __iomem *)bo->vaddr + offset;
info->screen_size = size;
info->fix.smem_start = (unsigned long)(bo->paddr + offset);
info->fix.smem_len = size;
return 0;
destroy:
drm_framebuffer_unregister_private(fb);
tegra_fb_destroy(fb);
release:
framebuffer_release(info);
return err;
}
static struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
.fb_probe = tegra_fbdev_probe,
};
static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm)
{
struct drm_fb_helper *helper;
struct tegra_fbdev *fbdev;
fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
if (!fbdev) {
dev_err(drm->dev, "failed to allocate DRM fbdev\n");
return ERR_PTR(-ENOMEM);
}
fbdev->base.funcs = &tegra_fb_helper_funcs;
helper = &fbdev->base;
return fbdev;
}
static void tegra_fbdev_free(struct tegra_fbdev *fbdev)
{
kfree(fbdev);
}
static int tegra_fbdev_init(struct tegra_fbdev *fbdev,
unsigned int preferred_bpp,
unsigned int num_crtc,
unsigned int max_connectors)
{
struct drm_device *drm = fbdev->base.dev;
int err;
err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors);
if (err < 0) {
dev_err(drm->dev, "failed to initialize DRM FB helper\n");
return err;
}
err = drm_fb_helper_single_add_all_connectors(&fbdev->base);
if (err < 0) {
dev_err(drm->dev, "failed to add connectors\n");
goto fini;
}
err = drm_fb_helper_initial_config(&fbdev->base, preferred_bpp);
if (err < 0) {
dev_err(drm->dev, "failed to set initial configuration\n");
goto fini;
}
return 0;
fini:
drm_fb_helper_fini(&fbdev->base);
return err;
}
static void tegra_fbdev_exit(struct tegra_fbdev *fbdev)
{
struct fb_info *info = fbdev->base.fbdev;
if (info) {
int err;
err = unregister_framebuffer(info);
if (err < 0)
DRM_DEBUG_KMS("failed to unregister framebuffer\n");
if (info->cmap.len)
fb_dealloc_cmap(&info->cmap);
framebuffer_release(info);
}
if (fbdev->fb) {
drm_framebuffer_unregister_private(&fbdev->fb->base);
tegra_fb_destroy(&fbdev->fb->base);
}
drm_fb_helper_fini(&fbdev->base);
tegra_fbdev_free(fbdev);
}
void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev)
{
if (fbdev) {
drm_modeset_lock_all(fbdev->base.dev);
drm_fb_helper_restore_fbdev_mode(&fbdev->base);
drm_modeset_unlock_all(fbdev->base.dev);
}
}
static void tegra_fb_output_poll_changed(struct drm_device *drm)
{
struct tegra_drm *tegra = drm->dev_private;
if (tegra->fbdev)
drm_fb_helper_hotplug_event(&tegra->fbdev->base);
}
#endif
static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
.fb_create = tegra_fb_create,
#ifdef CONFIG_DRM_TEGRA_FBDEV
.output_poll_changed = tegra_fb_output_poll_changed,
#endif
};
int tegra_drm_fb_prepare(struct drm_device *drm)
{
#ifdef CONFIG_DRM_TEGRA_FBDEV
struct tegra_drm *tegra = drm->dev_private;
#endif
drm->mode_config.min_width = 0;
drm->mode_config.min_height = 0;
drm->mode_config.max_width = 4096;
drm->mode_config.max_height = 4096;
drm->mode_config.funcs = &tegra_drm_mode_funcs;
#ifdef CONFIG_DRM_TEGRA_FBDEV
tegra->fbdev = tegra_fbdev_create(drm);
if (IS_ERR(tegra->fbdev))
return PTR_ERR(tegra->fbdev);
#endif
return 0;
}
void tegra_drm_fb_free(struct drm_device *drm)
{
#ifdef CONFIG_DRM_TEGRA_FBDEV
struct tegra_drm *tegra = drm->dev_private;
tegra_fbdev_free(tegra->fbdev);
#endif
}
int tegra_drm_fb_init(struct drm_device *drm)
{
#ifdef CONFIG_DRM_TEGRA_FBDEV
struct tegra_drm *tegra = drm->dev_private;
int err;
err = tegra_fbdev_init(tegra->fbdev, 32, drm->mode_config.num_crtc,
drm->mode_config.num_connector);
if (err < 0)
return err;
#endif
return 0;
}
void tegra_drm_fb_exit(struct drm_device *drm)
{
#ifdef CONFIG_DRM_TEGRA_FBDEV
struct tegra_drm *tegra = drm->dev_private;
tegra_fbdev_exit(tegra->fbdev);
#endif
}

801
drivers/gpu/drm/tegra/gem.c Normal file
View File

@@ -0,0 +1,801 @@
/*
* NVIDIA Tegra DRM GEM helper functions
*
* Copyright (C) 2012 Sascha Hauer, Pengutronix
* Copyright (C) 2013 NVIDIA CORPORATION, All rights reserved.
*
* Based on the GEM/CMA helpers
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/dma-buf.h>
#include <linux/iommu.h>
#include <drm/tegra_drm.h>
#include "drm.h"
#include "gem.h"
struct tegra_gem_importer {
struct device *dev;
void *drvdata;
void (*delete)(void *);
};
#define NUM_TEGRA_GEM_IMPORTER 5 /* FIXME: dynamically allocate more than this */
struct tegra_gem_object {
struct drm_gem_object *gem;
struct tegra_gem_importer importer[NUM_TEGRA_GEM_IMPORTER];
struct mutex lock;
};
static inline struct tegra_bo *host1x_to_tegra_bo(struct host1x_bo *bo)
{
return container_of(bo, struct tegra_bo, base);
}
static void tegra_bo_put(struct host1x_bo *bo)
{
struct tegra_bo *obj = host1x_to_tegra_bo(bo);
struct drm_device *drm = obj->gem.dev;
mutex_lock(&drm->struct_mutex);
drm_gem_object_unreference(&obj->gem);
mutex_unlock(&drm->struct_mutex);
}
static dma_addr_t tegra_bo_pin(struct host1x_bo *bo, struct sg_table **sgt)
{
struct tegra_bo *obj = host1x_to_tegra_bo(bo);
return obj->paddr;
}
static void tegra_bo_unpin(struct host1x_bo *bo, struct sg_table *sgt)
{
}
static void *tegra_bo_mmap(struct host1x_bo *bo)
{
struct tegra_bo *obj = host1x_to_tegra_bo(bo);
return obj->vaddr;
}
static void tegra_bo_munmap(struct host1x_bo *bo, void *addr)
{
}
static void *tegra_bo_kmap(struct host1x_bo *bo, unsigned int page)
{
struct tegra_bo *obj = host1x_to_tegra_bo(bo);
if (page >= obj->num_pages) {
WARN(1, "kmap pages beyonds bo's size: (%u : %lu).\n",
page, obj->num_pages);
return NULL;
}
return obj->vaddr + page * PAGE_SIZE;
}
static void tegra_bo_kunmap(struct host1x_bo *bo, unsigned int page,
void *addr)
{
}
static struct host1x_bo *tegra_bo_get(struct host1x_bo *bo)
{
struct tegra_bo *obj = host1x_to_tegra_bo(bo);
struct drm_device *drm = obj->gem.dev;
mutex_lock(&drm->struct_mutex);
drm_gem_object_reference(&obj->gem);
mutex_unlock(&drm->struct_mutex);
return bo;
}
static const struct host1x_bo_ops tegra_bo_ops = {
.get = tegra_bo_get,
.put = tegra_bo_put,
.pin = tegra_bo_pin,
.unpin = tegra_bo_unpin,
.mmap = tegra_bo_mmap,
.munmap = tegra_bo_munmap,
.kmap = tegra_bo_kmap,
.kunmap = tegra_bo_kunmap,
};
/*
* A generic iommu_map_sg() function is being reviewed and will hopefully be
* merged soon. At that point this function can be dropped in favour of the
* one provided by the IOMMU API.
*/
static ssize_t __iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
struct scatterlist *sg, unsigned int nents,
int prot)
{
struct scatterlist *s;
size_t offset = 0;
unsigned int i;
int err;
for_each_sg(sg, s, nents, i) {
phys_addr_t phys = page_to_phys(sg_page(s));
size_t length = s->offset + s->length;
err = iommu_map(domain, iova + offset, phys, length, prot);
if (err < 0) {
iommu_unmap(domain, iova, offset);
return err;
}
offset += length;
}
return offset;
}
static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo)
{
int prot = IOMMU_READ | IOMMU_WRITE;
ssize_t err;
if (bo->mm)
return -EBUSY;
bo->mm = kzalloc(sizeof(*bo->mm), GFP_KERNEL);
if (!bo->mm)
return -ENOMEM;
err = drm_mm_insert_node_generic(&tegra->mm, bo->mm, bo->gem.size,
PAGE_SIZE, 0, 0);
if (err < 0) {
dev_err(tegra->drm->dev, "out of I/O virtual memory: %zd\n",
err);
goto free;
}
bo->paddr = bo->mm->start;
err = __iommu_map_sg(tegra->domain, bo->paddr, bo->sgt->sgl,
bo->sgt->nents, prot);
if (err < 0) {
dev_err(tegra->drm->dev, "failed to map buffer: %zd\n", err);
goto remove;
}
bo->size = err;
return 0;
remove:
drm_mm_remove_node(bo->mm);
free:
kfree(bo->mm);
return err;
}
static int tegra_bo_iommu_unmap(struct tegra_drm *tegra, struct tegra_bo *bo)
{
if (!bo->mm)
return 0;
iommu_unmap(tegra->domain, bo->paddr, bo->size);
drm_mm_remove_node(bo->mm);
kfree(bo->mm);
return 0;
}
static struct tegra_bo *tegra_bo_alloc_object(struct drm_device *drm,
size_t size)
{
struct tegra_bo *bo;
int err;
bo = kzalloc(sizeof(*bo), GFP_KERNEL);
if (!bo)
return ERR_PTR(-ENOMEM);
host1x_bo_init(&bo->base, &tegra_bo_ops);
size = round_up(size, PAGE_SIZE);
if (!size) {
err = -EINVAL;
goto free;
}
err = drm_gem_object_init(drm, &bo->gem, size);
if (err < 0)
goto free;
err = drm_gem_create_mmap_offset(&bo->gem);
if (err < 0)
goto release;
return bo;
release:
drm_gem_object_release(&bo->gem);
free:
kfree(bo);
return ERR_PTR(err);
}
static void tegra_bo_free(struct drm_device *drm, struct tegra_bo *bo)
{
if (bo->pages) {
drm_gem_put_pages(&bo->gem, bo->pages, true, true);
sg_free_table(bo->sgt);
kfree(bo->sgt);
} else {
dma_free_writecombine(drm->dev, bo->gem.size, bo->vaddr,
bo->paddr);
}
}
static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo,
size_t size)
{
struct scatterlist *s;
unsigned int i;
bo->pages = drm_gem_get_pages(&bo->gem, GFP_KERNEL);
if (IS_ERR(bo->pages))
return PTR_ERR(bo->pages);
bo->num_pages = size >> PAGE_SHIFT;
bo->sgt = drm_prime_pages_to_sg(bo->pages, bo->num_pages);
if (IS_ERR(bo->sgt))
goto put_pages;
/*
* Fake up the SG table so that dma_sync_sg_for_device() can be used
* to flush the pages associated with it.
*
* TODO: Replace this by drm_clflash_sg() once it can be implemented
* without relying on symbols that are not exported.
*/
for_each_sg(bo->sgt->sgl, s, bo->sgt->nents, i)
sg_dma_address(s) = sg_phys(s);
dma_sync_sg_for_device(drm->dev, bo->sgt->sgl, bo->sgt->nents,
DMA_TO_DEVICE);
return 0;
put_pages:
drm_gem_put_pages(&bo->gem, bo->pages, false, false);
return PTR_ERR(bo->sgt);
}
static int tegra_bo_alloc(struct drm_device *drm, struct tegra_bo *bo,
size_t size)
{
struct tegra_drm *tegra = drm->dev_private;
int err;
if (tegra->domain) {
err = tegra_bo_get_pages(drm, bo, size);
if (err < 0)
return err;
err = tegra_bo_iommu_map(tegra, bo);
if (err < 0) {
tegra_bo_free(drm, bo);
return err;
}
} else {
bo->vaddr = dma_alloc_writecombine(drm->dev, size, &bo->paddr,
GFP_KERNEL | __GFP_NOWARN);
if (!bo->vaddr) {
dev_err(drm->dev,
"failed to allocate buffer of size %zu\n",
size);
return -ENOMEM;
}
}
return 0;
}
struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size,
unsigned long flags)
{
struct tegra_bo *bo;
int err;
bo = tegra_bo_alloc_object(drm, size);
if (IS_ERR(bo))
return bo;
err = tegra_bo_alloc(drm, bo, size);
if (err < 0)
goto release;
if (flags & DRM_TEGRA_GEM_CREATE_TILED)
bo->tiling.mode = TEGRA_BO_TILING_MODE_TILED;
if (flags & DRM_TEGRA_GEM_CREATE_BOTTOM_UP)
bo->flags |= TEGRA_BO_BOTTOM_UP;
return bo;
release:
drm_gem_object_release(&bo->gem);
kfree(bo);
return ERR_PTR(err);
}
struct tegra_bo *tegra_bo_create_with_handle(struct drm_file *file,
struct drm_device *drm,
unsigned int size,
unsigned long flags,
unsigned int *handle)
{
struct tegra_bo *bo;
int err;
bo = tegra_bo_create(drm, size, flags);
if (IS_ERR(bo))
return bo;
err = drm_gem_handle_create(file, &bo->gem, handle);
if (err) {
tegra_bo_free_object(&bo->gem);
return ERR_PTR(err);
}
drm_gem_object_unreference_unlocked(&bo->gem);
return bo;
}
static struct tegra_bo *tegra_bo_import(struct drm_device *drm,
struct dma_buf *buf)
{
struct tegra_drm *tegra = drm->dev_private;
struct dma_buf_attachment *attach;
struct tegra_bo *bo;
int err;
bo = tegra_bo_alloc_object(drm, buf->size);
if (IS_ERR(bo))
return bo;
attach = dma_buf_attach(buf, drm->dev);
if (IS_ERR(attach)) {
err = PTR_ERR(attach);
goto free;
}
get_dma_buf(buf);
bo->sgt = dma_buf_map_attachment(attach, DMA_TO_DEVICE);
if (!bo->sgt) {
err = -ENOMEM;
goto detach;
}
if (IS_ERR(bo->sgt)) {
err = PTR_ERR(bo->sgt);
goto detach;
}
if (tegra->domain) {
err = tegra_bo_iommu_map(tegra, bo);
if (err < 0)
goto detach;
} else {
if (bo->sgt->nents > 1) {
err = -EINVAL;
goto detach;
}
bo->paddr = sg_dma_address(bo->sgt->sgl);
}
bo->gem.import_attach = attach;
return bo;
detach:
if (!IS_ERR_OR_NULL(bo->sgt))
dma_buf_unmap_attachment(attach, bo->sgt, DMA_TO_DEVICE);
dma_buf_detach(buf, attach);
dma_buf_put(buf);
free:
drm_gem_object_release(&bo->gem);
kfree(bo);
return ERR_PTR(err);
}
void tegra_bo_free_object(struct drm_gem_object *gem)
{
struct tegra_drm *tegra = gem->dev->dev_private;
struct tegra_bo *bo = to_tegra_bo(gem);
if (tegra->domain)
tegra_bo_iommu_unmap(tegra, bo);
if (gem->import_attach) {
dma_buf_unmap_attachment(gem->import_attach, bo->sgt,
DMA_TO_DEVICE);
drm_prime_gem_destroy(gem, NULL);
} else {
tegra_bo_free(gem->dev, bo);
}
drm_gem_free_mmap_offset(&bo->gem);
drm_gem_object_release(gem);
kfree(bo);
}
int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm,
struct drm_mode_create_dumb *args)
{
int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
struct tegra_drm *tegra = drm->dev_private;
struct tegra_bo *bo;
min_pitch = round_up(min_pitch, tegra->pitch_align);
if (args->pitch < min_pitch)
args->pitch = min_pitch;
if (args->size < args->pitch * args->height)
args->size = args->pitch * args->height;
if (args->size == 0)
return -ENOMEM;
bo = tegra_bo_create_with_handle(file, drm, args->size, 0,
&args->handle);
if (IS_ERR(bo))
return PTR_ERR(bo);
return 0;
}
int tegra_bo_dumb_map_offset(struct drm_file *file, struct drm_device *drm,
uint32_t handle, uint64_t *offset)
{
struct drm_gem_object *gem;
struct tegra_bo *bo;
mutex_lock(&drm->struct_mutex);
gem = drm_gem_object_lookup(drm, file, handle);
if (!gem) {
dev_err(drm->dev, "failed to lookup GEM object\n");
mutex_unlock(&drm->struct_mutex);
return -EINVAL;
}
bo = to_tegra_bo(gem);
*offset = drm_vma_node_offset_addr(&bo->gem.vma_node);
drm_gem_object_unreference(gem);
mutex_unlock(&drm->struct_mutex);
return 0;
}
static int tegra_bo_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct drm_gem_object *gem = vma->vm_private_data;
struct tegra_bo *bo = to_tegra_bo(gem);
struct page *page;
pgoff_t offset;
int err;
if (!bo->pages)
return VM_FAULT_SIGBUS;
offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >> PAGE_SHIFT;
page = bo->pages[offset];
err = vm_insert_page(vma, (unsigned long)vmf->virtual_address, page);
switch (err) {
case -EAGAIN:
case 0:
case -ERESTARTSYS:
case -EINTR:
case -EBUSY:
return VM_FAULT_NOPAGE;
case -ENOMEM:
return VM_FAULT_OOM;
}
return VM_FAULT_SIGBUS;
}
const struct vm_operations_struct tegra_bo_vm_ops = {
.fault = tegra_bo_fault,
.open = drm_gem_vm_open,
.close = drm_gem_vm_close,
};
int tegra_drm_mmap(struct file *file, struct vm_area_struct *vma)
{
struct drm_gem_object *gem;
struct tegra_bo *bo;
int ret;
ret = drm_gem_mmap(file, vma);
if (ret)
return ret;
gem = vma->vm_private_data;
bo = to_tegra_bo(gem);
if (!bo->pages) {
unsigned long vm_pgoff = vma->vm_pgoff;
vma->vm_flags &= ~VM_PFNMAP;
vma->vm_pgoff = 0;
ret = dma_mmap_writecombine(gem->dev->dev, vma, bo->vaddr,
bo->paddr, gem->size);
if (ret) {
drm_gem_vm_close(vma);
return ret;
}
vma->vm_pgoff = vm_pgoff;
} else {
pgprot_t prot = vm_get_page_prot(vma->vm_flags);
vma->vm_flags |= VM_MIXEDMAP;
vma->vm_flags &= ~VM_PFNMAP;
vma->vm_page_prot = pgprot_writecombine(prot);
}
return 0;
}
static struct sg_table *
tegra_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
enum dma_data_direction dir)
{
struct tegra_gem_object *priv = attach->dmabuf->priv;
struct drm_gem_object *gem = priv->gem;
struct tegra_bo *bo = to_tegra_bo(gem);
struct sg_table *sgt;
if (bo->pages) {
sgt = drm_prime_pages_to_sg(bo->pages, bo->num_pages);
if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0)
goto free;
} else {
sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
if (!sgt)
return NULL;
if (sg_alloc_table(sgt, 1, GFP_KERNEL))
goto free;
sg_dma_address(sgt->sgl) = bo->paddr;
sg_dma_len(sgt->sgl) = gem->size;
}
return sgt;
free:
sg_free_table(sgt);
kfree(sgt);
return NULL;
}
static void tegra_gem_prime_unmap_dma_buf(struct dma_buf_attachment *attach,
struct sg_table *sgt,
enum dma_data_direction dir)
{
struct tegra_gem_object *priv = attach->dmabuf->priv;
struct drm_gem_object *gem = priv->gem;
struct tegra_bo *bo = to_tegra_bo(gem);
if (bo->pages)
dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir);
sg_free_table(sgt);
kfree(sgt);
}
static void tegra_gem_prime_release(struct dma_buf *buf)
{
struct tegra_gem_object *priv = buf->priv;
int i;
for (i = 0; i < ARRAY_SIZE(priv->importer); i++) {
struct tegra_gem_importer *imp = &priv->importer[i];
if (imp->dev && imp->delete)
imp->delete(imp->drvdata);
}
drm_gem_object_unreference_unlocked(priv->gem);
kfree(priv);
}
static void *tegra_gem_prime_kmap_atomic(struct dma_buf *buf,
unsigned long page)
{
return NULL;
}
static void tegra_gem_prime_kunmap_atomic(struct dma_buf *buf,
unsigned long page,
void *addr)
{
}
static void *tegra_gem_prime_kmap(struct dma_buf *buf, unsigned long page)
{
return NULL;
}
static void tegra_gem_prime_kunmap(struct dma_buf *buf, unsigned long page,
void *addr)
{
}
static int tegra_gem_prime_mmap(struct dma_buf *buf, struct vm_area_struct *vma)
{
struct tegra_gem_object *obj = buf->priv;
int ret;
if (obj->gem->size < vma->vm_end - vma->vm_start)
return -EINVAL;
if (!obj->gem->filp)
return -ENODEV;
ret = obj->gem->filp->f_op->mmap(obj->gem->filp, vma);
if (ret)
return ret;
fput(vma->vm_file);
vma->vm_file = get_file(obj->gem->filp);
return 0;
}
static void *tegra_gem_prime_vmap(struct dma_buf *buf)
{
struct tegra_gem_object *priv = buf->priv;
struct drm_gem_object *gem = priv->gem;
struct tegra_bo *bo = to_tegra_bo(gem);
return bo->vaddr;
}
static void tegra_gem_prime_vunmap(struct dma_buf *buf, void *vaddr)
{
}
static int tegra_gem_set_drvdata(struct dma_buf *dmabuf, struct device *dev,
void *drvdata, void (*delete)(void *))
{
struct tegra_gem_object *priv = dmabuf->priv;
struct tegra_gem_importer *imp;
int i, empty = -1, err = 0;
mutex_lock(&priv->lock);
for (i = 0; i < ARRAY_SIZE(priv->importer); i++) {
imp = &priv->importer[i];
if ((empty == -1) && !imp->dev)
empty = i;
if (dev == imp->dev)
break;
}
if (i == ARRAY_SIZE(priv->importer)) {
if (empty == -1) {
WARN(1, "tegra_gem: Needs more importer space\n");
err = -ENOMEM;
goto out;
}
imp = &priv->importer[empty];
i = empty;
}
imp->dev = dev;
imp->drvdata = drvdata;
imp->delete = delete;
out:
mutex_unlock(&priv->lock);
dev_dbg(dev, "%s() dmabuf=%p err=%d i=%d priv=%p\n",
__func__, dmabuf, err, i, priv);
return err;
}
static void *tegra_gem_get_drvdata(struct dma_buf *dmabuf,
struct device *dev)
{
struct tegra_gem_object *priv = dmabuf->priv;
void *drvdata = NULL;
int i;
mutex_lock(&priv->lock);
for (i = 0; i < ARRAY_SIZE(priv->importer); i++) {
struct tegra_gem_importer *imp;
imp = &priv->importer[i];
if (dev == imp->dev) {
drvdata = imp->drvdata;
break;
}
}
mutex_unlock(&priv->lock);
dev_dbg(dev, "%s() dmabuf=%p i=%d priv=%p\n",
__func__, dmabuf, i, priv);
return drvdata;
}
static const struct dma_buf_ops tegra_gem_prime_dmabuf_ops = {
.map_dma_buf = tegra_gem_prime_map_dma_buf,
.unmap_dma_buf = tegra_gem_prime_unmap_dma_buf,
.release = tegra_gem_prime_release,
.kmap_atomic = tegra_gem_prime_kmap_atomic,
.kunmap_atomic = tegra_gem_prime_kunmap_atomic,
.kmap = tegra_gem_prime_kmap,
.kunmap = tegra_gem_prime_kunmap,
.mmap = tegra_gem_prime_mmap,
.vmap = tegra_gem_prime_vmap,
.vunmap = tegra_gem_prime_vunmap,
.set_drvdata = tegra_gem_set_drvdata,
.get_drvdata = tegra_gem_get_drvdata,
};
struct dma_buf *tegra_gem_prime_export(struct drm_device *drm,
struct drm_gem_object *gem,
int flags)
{
struct tegra_gem_object *priv;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return ERR_PTR(-ENOMEM);
priv->gem = gem;
mutex_init(&priv->lock);
return dma_buf_export(priv, &tegra_gem_prime_dmabuf_ops, gem->size,
flags);
}
struct drm_gem_object *tegra_gem_prime_import(struct drm_device *drm,
struct dma_buf *buf)
{
struct tegra_bo *bo;
if (buf->ops == &tegra_gem_prime_dmabuf_ops) {
struct tegra_gem_object *priv = buf->priv;
struct drm_gem_object *gem = priv->gem;
if (gem->dev == drm) {
drm_gem_object_reference(gem);
return gem;
}
}
bo = tegra_bo_import(drm, buf);
if (IS_ERR(bo))
return ERR_CAST(bo);
return &bo->gem;
}

View File

@@ -0,0 +1,77 @@
/*
* Tegra host1x GEM implementation
*
* Copyright (c) 2012-2013, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __HOST1X_GEM_H
#define __HOST1X_GEM_H
#include <linux/host1x.h>
#include <drm/drm.h>
#include <drm/drmP.h>
#define TEGRA_BO_BOTTOM_UP (1 << 0)
enum tegra_bo_tiling_mode {
TEGRA_BO_TILING_MODE_PITCH,
TEGRA_BO_TILING_MODE_TILED,
TEGRA_BO_TILING_MODE_BLOCK,
};
struct tegra_bo_tiling {
enum tegra_bo_tiling_mode mode;
unsigned long value;
};
struct tegra_bo {
struct drm_gem_object gem;
struct host1x_bo base;
unsigned long flags;
struct sg_table *sgt;
dma_addr_t paddr;
void *vaddr;
struct drm_mm_node *mm;
unsigned long num_pages;
struct page **pages;
/* size of IOMMU mapping */
size_t size;
struct tegra_bo_tiling tiling;
};
static inline struct tegra_bo *to_tegra_bo(struct drm_gem_object *gem)
{
return container_of(gem, struct tegra_bo, gem);
}
struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size,
unsigned long flags);
struct tegra_bo *tegra_bo_create_with_handle(struct drm_file *file,
struct drm_device *drm,
unsigned int size,
unsigned long flags,
unsigned int *handle);
void tegra_bo_free_object(struct drm_gem_object *gem);
int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm,
struct drm_mode_create_dumb *args);
int tegra_bo_dumb_map_offset(struct drm_file *file, struct drm_device *drm,
uint32_t handle, uint64_t *offset);
int tegra_drm_mmap(struct file *file, struct vm_area_struct *vma);
extern const struct vm_operations_struct tegra_bo_vm_ops;
struct dma_buf *tegra_gem_prime_export(struct drm_device *drm,
struct drm_gem_object *gem,
int flags);
struct drm_gem_object *tegra_gem_prime_import(struct drm_device *drm,
struct dma_buf *buf);
#endif

1573
drivers/gpu/drm/tegra/hdmi.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,541 @@
/*
* Copyright (C) 2012 Avionic Design GmbH
* Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef TEGRA_HDMI_H
#define TEGRA_HDMI_H 1
/* register definitions */
#define HDMI_CTXSW 0x00
#define HDMI_NV_PDISP_SOR_STATE0 0x01
#define SOR_STATE_UPDATE (1 << 0)
#define HDMI_NV_PDISP_SOR_STATE1 0x02
#define SOR_STATE_ASY_HEAD_OPMODE_AWAKE (2 << 0)
#define SOR_STATE_ASY_ORMODE_NORMAL (1 << 2)
#define SOR_STATE_ATTACHED (1 << 3)
#define HDMI_NV_PDISP_SOR_STATE2 0x03
#define SOR_STATE_ASY_OWNER_NONE (0 << 0)
#define SOR_STATE_ASY_OWNER_HEAD0 (1 << 0)
#define SOR_STATE_ASY_SUBOWNER_NONE (0 << 4)
#define SOR_STATE_ASY_SUBOWNER_SUBHEAD0 (1 << 4)
#define SOR_STATE_ASY_SUBOWNER_SUBHEAD1 (2 << 4)
#define SOR_STATE_ASY_SUBOWNER_BOTH (3 << 4)
#define SOR_STATE_ASY_CRCMODE_ACTIVE (0 << 6)
#define SOR_STATE_ASY_CRCMODE_COMPLETE (1 << 6)
#define SOR_STATE_ASY_CRCMODE_NON_ACTIVE (2 << 6)
#define SOR_STATE_ASY_PROTOCOL_SINGLE_TMDS_A (1 << 8)
#define SOR_STATE_ASY_PROTOCOL_CUSTOM (15 << 8)
#define SOR_STATE_ASY_HSYNCPOL_POS (0 << 12)
#define SOR_STATE_ASY_HSYNCPOL_NEG (1 << 12)
#define SOR_STATE_ASY_VSYNCPOL_POS (0 << 13)
#define SOR_STATE_ASY_VSYNCPOL_NEG (1 << 13)
#define SOR_STATE_ASY_DEPOL_POS (0 << 14)
#define SOR_STATE_ASY_DEPOL_NEG (1 << 14)
#define HDMI_NV_PDISP_RG_HDCP_AN_MSB 0x04
#define HDMI_NV_PDISP_RG_HDCP_AN_LSB 0x05
#define HDMI_NV_PDISP_RG_HDCP_CN_MSB 0x06
#define HDMI_NV_PDISP_RG_HDCP_CN_LSB 0x07
#define HDMI_NV_PDISP_RG_HDCP_AKSV_MSB 0x08
#define HDMI_NV_PDISP_RG_HDCP_AKSV_LSB 0x09
#define HDMI_NV_PDISP_RG_HDCP_BKSV_MSB 0x0a
#define HDMI_NV_PDISP_RG_HDCP_BKSV_LSB 0x0b
#define HDMI_NV_PDISP_RG_HDCP_CKSV_MSB 0x0c
#define HDMI_NV_PDISP_RG_HDCP_CKSV_LSB 0x0d
#define HDMI_NV_PDISP_RG_HDCP_DKSV_MSB 0x0e
#define HDMI_NV_PDISP_RG_HDCP_DKSV_LSB 0x0f
#define HDMI_NV_PDISP_RG_HDCP_CTRL 0x10
#define HDMI_NV_PDISP_RG_HDCP_CMODE 0x11
#define HDMI_NV_PDISP_RG_HDCP_MPRIME_MSB 0x12
#define HDMI_NV_PDISP_RG_HDCP_MPRIME_LSB 0x13
#define HDMI_NV_PDISP_RG_HDCP_SPRIME_MSB 0x14
#define HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB2 0x15
#define HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB1 0x16
#define HDMI_NV_PDISP_RG_HDCP_RI 0x17
#define HDMI_NV_PDISP_RG_HDCP_CS_MSB 0x18
#define HDMI_NV_PDISP_RG_HDCP_CS_LSB 0x19
#define HDMI_NV_PDISP_HDMI_AUDIO_EMU0 0x1a
#define HDMI_NV_PDISP_HDMI_AUDIO_EMU_RDATA0 0x1b
#define HDMI_NV_PDISP_HDMI_AUDIO_EMU1 0x1c
#define HDMI_NV_PDISP_HDMI_AUDIO_EMU2 0x1d
#define HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL 0x1e
#define HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_STATUS 0x1f
#define HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER 0x20
#define HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW 0x21
#define HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH 0x22
#define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL 0x23
#define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_STATUS 0x24
#define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER 0x25
#define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW 0x26
#define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH 0x27
#define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW 0x28
#define HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH 0x29
#define INFOFRAME_CTRL_ENABLE (1 << 0)
#define INFOFRAME_HEADER_TYPE(x) (((x) & 0xff) << 0)
#define INFOFRAME_HEADER_VERSION(x) (((x) & 0xff) << 8)
#define INFOFRAME_HEADER_LEN(x) (((x) & 0x0f) << 16)
#define HDMI_NV_PDISP_HDMI_GENERIC_CTRL 0x2a
#define GENERIC_CTRL_ENABLE (1 << 0)
#define GENERIC_CTRL_OTHER (1 << 4)
#define GENERIC_CTRL_SINGLE (1 << 8)
#define GENERIC_CTRL_HBLANK (1 << 12)
#define GENERIC_CTRL_AUDIO (1 << 16)
#define HDMI_NV_PDISP_HDMI_GENERIC_STATUS 0x2b
#define HDMI_NV_PDISP_HDMI_GENERIC_HEADER 0x2c
#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_LOW 0x2d
#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_HIGH 0x2e
#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_LOW 0x2f
#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_HIGH 0x30
#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_LOW 0x31
#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_HIGH 0x32
#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_LOW 0x33
#define HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_HIGH 0x34
#define HDMI_NV_PDISP_HDMI_ACR_CTRL 0x35
#define HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW 0x36
#define HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_HIGH 0x37
#define HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW 0x38
#define HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH 0x39
#define HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW 0x3a
#define HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_HIGH 0x3b
#define HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW 0x3c
#define HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_HIGH 0x3d
#define HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW 0x3e
#define HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_HIGH 0x3f
#define HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW 0x40
#define HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_HIGH 0x41
#define HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW 0x42
#define HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_HIGH 0x43
#define ACR_SUBPACK_CTS(x) (((x) & 0xffffff) << 8)
#define ACR_SUBPACK_N(x) (((x) & 0xffffff) << 0)
#define ACR_ENABLE (1 << 31)
#define HDMI_NV_PDISP_HDMI_CTRL 0x44
#define HDMI_CTRL_REKEY(x) (((x) & 0x7f) << 0)
#define HDMI_CTRL_MAX_AC_PACKET(x) (((x) & 0x1f) << 16)
#define HDMI_CTRL_ENABLE (1 << 30)
#define HDMI_NV_PDISP_HDMI_VSYNC_KEEPOUT 0x45
#define HDMI_NV_PDISP_HDMI_VSYNC_WINDOW 0x46
#define VSYNC_WINDOW_END(x) (((x) & 0x3ff) << 0)
#define VSYNC_WINDOW_START(x) (((x) & 0x3ff) << 16)
#define VSYNC_WINDOW_ENABLE (1 << 31)
#define HDMI_NV_PDISP_HDMI_GCP_CTRL 0x47
#define HDMI_NV_PDISP_HDMI_GCP_STATUS 0x48
#define HDMI_NV_PDISP_HDMI_GCP_SUBPACK 0x49
#define HDMI_NV_PDISP_HDMI_CHANNEL_STATUS1 0x4a
#define HDMI_NV_PDISP_HDMI_CHANNEL_STATUS2 0x4b
#define HDMI_NV_PDISP_HDMI_EMU0 0x4c
#define HDMI_NV_PDISP_HDMI_EMU1 0x4d
#define HDMI_NV_PDISP_HDMI_EMU1_RDATA 0x4e
#define HDMI_NV_PDISP_HDMI_SPARE 0x4f
#define SPARE_HW_CTS (1 << 0)
#define SPARE_FORCE_SW_CTS (1 << 1)
#define SPARE_CTS_RESET_VAL(x) (((x) & 0x7) << 16)
#define HDMI_NV_PDISP_HDMI_SPDIF_CHN_STATUS1 0x50
#define HDMI_NV_PDISP_HDMI_SPDIF_CHN_STATUS2 0x51
#define HDMI_NV_PDISP_HDMI_HDCPRIF_ROM_CTRL 0x53
#define HDMI_NV_PDISP_SOR_CAP 0x54
#define HDMI_NV_PDISP_SOR_PWR 0x55
#define SOR_PWR_NORMAL_STATE_PD (0 << 0)
#define SOR_PWR_NORMAL_STATE_PU (1 << 0)
#define SOR_PWR_NORMAL_START_NORMAL (0 << 1)
#define SOR_PWR_NORMAL_START_ALT (1 << 1)
#define SOR_PWR_SAFE_STATE_PD (0 << 16)
#define SOR_PWR_SAFE_STATE_PU (1 << 16)
#define SOR_PWR_SETTING_NEW_DONE (0 << 31)
#define SOR_PWR_SETTING_NEW_PENDING (1 << 31)
#define SOR_PWR_SETTING_NEW_TRIGGER (1 << 31)
#define HDMI_NV_PDISP_SOR_TEST 0x56
#define HDMI_NV_PDISP_SOR_PLL0 0x57
#define SOR_PLL_PWR (1 << 0)
#define SOR_PLL_PDBG (1 << 1)
#define SOR_PLL_VCAPD (1 << 2)
#define SOR_PLL_PDPORT (1 << 3)
#define SOR_PLL_RESISTORSEL (1 << 4)
#define SOR_PLL_PULLDOWN (1 << 5)
#define SOR_PLL_VCOCAP(x) (((x) & 0xf) << 8)
#define SOR_PLL_BG_V17_S(x) (((x) & 0xf) << 12)
#define SOR_PLL_FILTER(x) (((x) & 0xf) << 16)
#define SOR_PLL_ICHPMP(x) (((x) & 0xf) << 24)
#define SOR_PLL_TX_REG_LOAD(x) (((x) & 0xf) << 28)
#define HDMI_NV_PDISP_SOR_PLL1 0x58
#define SOR_PLL_TMDS_TERM_ENABLE (1 << 8)
#define SOR_PLL_TMDS_TERMADJ(x) (((x) & 0xf) << 9)
#define SOR_PLL_LOADADJ(x) (((x) & 0xf) << 20)
#define SOR_PLL_PE_EN (1 << 28)
#define SOR_PLL_HALF_FULL_PE (1 << 29)
#define SOR_PLL_S_D_PIN_PE (1 << 30)
#define HDMI_NV_PDISP_SOR_PLL2 0x59
#define HDMI_NV_PDISP_SOR_CSTM 0x5a
#define SOR_CSTM_ROTCLK(x) (((x) & 0xf) << 24)
#define SOR_CSTM_PLLDIV (1 << 21)
#define SOR_CSTM_LVDS_ENABLE (1 << 16)
#define SOR_CSTM_MODE_LVDS (0 << 12)
#define SOR_CSTM_MODE_TMDS (1 << 12)
#define SOR_CSTM_MODE_MASK (3 << 12)
#define HDMI_NV_PDISP_SOR_LVDS 0x5b
#define HDMI_NV_PDISP_SOR_CRCA 0x5c
#define HDMI_NV_PDISP_SOR_CRCB 0x5d
#define HDMI_NV_PDISP_SOR_BLANK 0x5e
#define HDMI_NV_PDISP_SOR_SEQ_CTL 0x5f
#define SOR_SEQ_CTL_PU_PC(x) (((x) & 0xf) << 0)
#define SOR_SEQ_PU_PC_ALT(x) (((x) & 0xf) << 4)
#define SOR_SEQ_PD_PC(x) (((x) & 0xf) << 8)
#define SOR_SEQ_PD_PC_ALT(x) (((x) & 0xf) << 12)
#define SOR_SEQ_PC(x) (((x) & 0xf) << 16)
#define SOR_SEQ_STATUS (1 << 28)
#define SOR_SEQ_SWITCH (1 << 30)
#define HDMI_NV_PDISP_SOR_SEQ_INST(x) (0x60 + (x))
#define SOR_SEQ_INST_WAIT_TIME(x) (((x) & 0x3ff) << 0)
#define SOR_SEQ_INST_WAIT_UNITS_VSYNC (2 << 12)
#define SOR_SEQ_INST_HALT (1 << 15)
#define SOR_SEQ_INST_PIN_A_LOW (0 << 21)
#define SOR_SEQ_INST_PIN_A_HIGH (1 << 21)
#define SOR_SEQ_INST_PIN_B_LOW (0 << 22)
#define SOR_SEQ_INST_PIN_B_HIGH (1 << 22)
#define SOR_SEQ_INST_DRIVE_PWM_OUT_LO (1 << 23)
#define HDMI_NV_PDISP_SOR_VCRCA0 0x72
#define HDMI_NV_PDISP_SOR_VCRCA1 0x73
#define HDMI_NV_PDISP_SOR_CCRCA0 0x74
#define HDMI_NV_PDISP_SOR_CCRCA1 0x75
#define HDMI_NV_PDISP_SOR_EDATAA0 0x76
#define HDMI_NV_PDISP_SOR_EDATAA1 0x77
#define HDMI_NV_PDISP_SOR_COUNTA0 0x78
#define HDMI_NV_PDISP_SOR_COUNTA1 0x79
#define HDMI_NV_PDISP_SOR_DEBUGA0 0x7a
#define HDMI_NV_PDISP_SOR_DEBUGA1 0x7b
#define HDMI_NV_PDISP_SOR_TRIG 0x7c
#define HDMI_NV_PDISP_SOR_MSCHECK 0x7d
#define HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT 0x7e
#define DRIVE_CURRENT_LANE0(x) (((x) & 0x3f) << 0)
#define DRIVE_CURRENT_LANE1(x) (((x) & 0x3f) << 8)
#define DRIVE_CURRENT_LANE2(x) (((x) & 0x3f) << 16)
#define DRIVE_CURRENT_LANE3(x) (((x) & 0x3f) << 24)
#define DRIVE_CURRENT_LANE0_T114(x) (((x) & 0x7f) << 0)
#define DRIVE_CURRENT_LANE1_T114(x) (((x) & 0x7f) << 8)
#define DRIVE_CURRENT_LANE2_T114(x) (((x) & 0x7f) << 16)
#define DRIVE_CURRENT_LANE3_T114(x) (((x) & 0x7f) << 24)
#define DRIVE_CURRENT_1_500_mA 0x00
#define DRIVE_CURRENT_1_875_mA 0x01
#define DRIVE_CURRENT_2_250_mA 0x02
#define DRIVE_CURRENT_2_625_mA 0x03
#define DRIVE_CURRENT_3_000_mA 0x04
#define DRIVE_CURRENT_3_375_mA 0x05
#define DRIVE_CURRENT_3_750_mA 0x06
#define DRIVE_CURRENT_4_125_mA 0x07
#define DRIVE_CURRENT_4_500_mA 0x08
#define DRIVE_CURRENT_4_875_mA 0x09
#define DRIVE_CURRENT_5_250_mA 0x0a
#define DRIVE_CURRENT_5_625_mA 0x0b
#define DRIVE_CURRENT_6_000_mA 0x0c
#define DRIVE_CURRENT_6_375_mA 0x0d
#define DRIVE_CURRENT_6_750_mA 0x0e
#define DRIVE_CURRENT_7_125_mA 0x0f
#define DRIVE_CURRENT_7_500_mA 0x10
#define DRIVE_CURRENT_7_875_mA 0x11
#define DRIVE_CURRENT_8_250_mA 0x12
#define DRIVE_CURRENT_8_625_mA 0x13
#define DRIVE_CURRENT_9_000_mA 0x14
#define DRIVE_CURRENT_9_375_mA 0x15
#define DRIVE_CURRENT_9_750_mA 0x16
#define DRIVE_CURRENT_10_125_mA 0x17
#define DRIVE_CURRENT_10_500_mA 0x18
#define DRIVE_CURRENT_10_875_mA 0x19
#define DRIVE_CURRENT_11_250_mA 0x1a
#define DRIVE_CURRENT_11_625_mA 0x1b
#define DRIVE_CURRENT_12_000_mA 0x1c
#define DRIVE_CURRENT_12_375_mA 0x1d
#define DRIVE_CURRENT_12_750_mA 0x1e
#define DRIVE_CURRENT_13_125_mA 0x1f
#define DRIVE_CURRENT_13_500_mA 0x20
#define DRIVE_CURRENT_13_875_mA 0x21
#define DRIVE_CURRENT_14_250_mA 0x22
#define DRIVE_CURRENT_14_625_mA 0x23
#define DRIVE_CURRENT_15_000_mA 0x24
#define DRIVE_CURRENT_15_375_mA 0x25
#define DRIVE_CURRENT_15_750_mA 0x26
#define DRIVE_CURRENT_16_125_mA 0x27
#define DRIVE_CURRENT_16_500_mA 0x28
#define DRIVE_CURRENT_16_875_mA 0x29
#define DRIVE_CURRENT_17_250_mA 0x2a
#define DRIVE_CURRENT_17_625_mA 0x2b
#define DRIVE_CURRENT_18_000_mA 0x2c
#define DRIVE_CURRENT_18_375_mA 0x2d
#define DRIVE_CURRENT_18_750_mA 0x2e
#define DRIVE_CURRENT_19_125_mA 0x2f
#define DRIVE_CURRENT_19_500_mA 0x30
#define DRIVE_CURRENT_19_875_mA 0x31
#define DRIVE_CURRENT_20_250_mA 0x32
#define DRIVE_CURRENT_20_625_mA 0x33
#define DRIVE_CURRENT_21_000_mA 0x34
#define DRIVE_CURRENT_21_375_mA 0x35
#define DRIVE_CURRENT_21_750_mA 0x36
#define DRIVE_CURRENT_22_125_mA 0x37
#define DRIVE_CURRENT_22_500_mA 0x38
#define DRIVE_CURRENT_22_875_mA 0x39
#define DRIVE_CURRENT_23_250_mA 0x3a
#define DRIVE_CURRENT_23_625_mA 0x3b
#define DRIVE_CURRENT_24_000_mA 0x3c
#define DRIVE_CURRENT_24_375_mA 0x3d
#define DRIVE_CURRENT_24_750_mA 0x3e
#define DRIVE_CURRENT_0_000_mA_T114 0x00
#define DRIVE_CURRENT_0_400_mA_T114 0x01
#define DRIVE_CURRENT_0_800_mA_T114 0x02
#define DRIVE_CURRENT_1_200_mA_T114 0x03
#define DRIVE_CURRENT_1_600_mA_T114 0x04
#define DRIVE_CURRENT_2_000_mA_T114 0x05
#define DRIVE_CURRENT_2_400_mA_T114 0x06
#define DRIVE_CURRENT_2_800_mA_T114 0x07
#define DRIVE_CURRENT_3_200_mA_T114 0x08
#define DRIVE_CURRENT_3_600_mA_T114 0x09
#define DRIVE_CURRENT_4_000_mA_T114 0x0a
#define DRIVE_CURRENT_4_400_mA_T114 0x0b
#define DRIVE_CURRENT_4_800_mA_T114 0x0c
#define DRIVE_CURRENT_5_200_mA_T114 0x0d
#define DRIVE_CURRENT_5_600_mA_T114 0x0e
#define DRIVE_CURRENT_6_000_mA_T114 0x0f
#define DRIVE_CURRENT_6_400_mA_T114 0x10
#define DRIVE_CURRENT_6_800_mA_T114 0x11
#define DRIVE_CURRENT_7_200_mA_T114 0x12
#define DRIVE_CURRENT_7_600_mA_T114 0x13
#define DRIVE_CURRENT_8_000_mA_T114 0x14
#define DRIVE_CURRENT_8_400_mA_T114 0x15
#define DRIVE_CURRENT_8_800_mA_T114 0x16
#define DRIVE_CURRENT_9_200_mA_T114 0x17
#define DRIVE_CURRENT_9_600_mA_T114 0x18
#define DRIVE_CURRENT_10_000_mA_T114 0x19
#define DRIVE_CURRENT_10_400_mA_T114 0x1a
#define DRIVE_CURRENT_10_800_mA_T114 0x1b
#define DRIVE_CURRENT_11_200_mA_T114 0x1c
#define DRIVE_CURRENT_11_600_mA_T114 0x1d
#define DRIVE_CURRENT_12_000_mA_T114 0x1e
#define DRIVE_CURRENT_12_400_mA_T114 0x1f
#define DRIVE_CURRENT_12_800_mA_T114 0x20
#define DRIVE_CURRENT_13_200_mA_T114 0x21
#define DRIVE_CURRENT_13_600_mA_T114 0x22
#define DRIVE_CURRENT_14_000_mA_T114 0x23
#define DRIVE_CURRENT_14_400_mA_T114 0x24
#define DRIVE_CURRENT_14_800_mA_T114 0x25
#define DRIVE_CURRENT_15_200_mA_T114 0x26
#define DRIVE_CURRENT_15_600_mA_T114 0x27
#define DRIVE_CURRENT_16_000_mA_T114 0x28
#define DRIVE_CURRENT_16_400_mA_T114 0x29
#define DRIVE_CURRENT_16_800_mA_T114 0x2a
#define DRIVE_CURRENT_17_200_mA_T114 0x2b
#define DRIVE_CURRENT_17_600_mA_T114 0x2c
#define DRIVE_CURRENT_18_000_mA_T114 0x2d
#define DRIVE_CURRENT_18_400_mA_T114 0x2e
#define DRIVE_CURRENT_18_800_mA_T114 0x2f
#define DRIVE_CURRENT_19_200_mA_T114 0x30
#define DRIVE_CURRENT_19_600_mA_T114 0x31
#define DRIVE_CURRENT_20_000_mA_T114 0x32
#define DRIVE_CURRENT_20_400_mA_T114 0x33
#define DRIVE_CURRENT_20_800_mA_T114 0x34
#define DRIVE_CURRENT_21_200_mA_T114 0x35
#define DRIVE_CURRENT_21_600_mA_T114 0x36
#define DRIVE_CURRENT_22_000_mA_T114 0x37
#define DRIVE_CURRENT_22_400_mA_T114 0x38
#define DRIVE_CURRENT_22_800_mA_T114 0x39
#define DRIVE_CURRENT_23_200_mA_T114 0x3a
#define DRIVE_CURRENT_23_600_mA_T114 0x3b
#define DRIVE_CURRENT_24_000_mA_T114 0x3c
#define DRIVE_CURRENT_24_400_mA_T114 0x3d
#define DRIVE_CURRENT_24_800_mA_T114 0x3e
#define DRIVE_CURRENT_25_200_mA_T114 0x3f
#define DRIVE_CURRENT_25_400_mA_T114 0x40
#define DRIVE_CURRENT_25_800_mA_T114 0x41
#define DRIVE_CURRENT_26_200_mA_T114 0x42
#define DRIVE_CURRENT_26_600_mA_T114 0x43
#define DRIVE_CURRENT_27_000_mA_T114 0x44
#define DRIVE_CURRENT_27_400_mA_T114 0x45
#define DRIVE_CURRENT_27_800_mA_T114 0x46
#define DRIVE_CURRENT_28_200_mA_T114 0x47
#define HDMI_NV_PDISP_AUDIO_DEBUG0 0x7f
#define HDMI_NV_PDISP_AUDIO_DEBUG1 0x80
#define HDMI_NV_PDISP_AUDIO_DEBUG2 0x81
#define HDMI_NV_PDISP_AUDIO_FS(x) (0x82 + (x))
#define AUDIO_FS_LOW(x) (((x) & 0xfff) << 0)
#define AUDIO_FS_HIGH(x) (((x) & 0xfff) << 16)
#define HDMI_NV_PDISP_AUDIO_PULSE_WIDTH 0x89
#define HDMI_NV_PDISP_AUDIO_THRESHOLD 0x8a
#define HDMI_NV_PDISP_AUDIO_CNTRL0 0x8b
#define AUDIO_CNTRL0_ERROR_TOLERANCE(x) (((x) & 0xff) << 0)
#define AUDIO_CNTRL0_SOURCE_SELECT_AUTO (0 << 20)
#define AUDIO_CNTRL0_SOURCE_SELECT_SPDIF (1 << 20)
#define AUDIO_CNTRL0_SOURCE_SELECT_HDAL (2 << 20)
#define AUDIO_CNTRL0_FRAMES_PER_BLOCK(x) (((x) & 0xff) << 24)
#define HDMI_NV_PDISP_AUDIO_N 0x8c
#define AUDIO_N_VALUE(x) (((x) & 0xfffff) << 0)
#define AUDIO_N_RESETF (1 << 20)
#define AUDIO_N_GENERATE_NORMAL (0 << 24)
#define AUDIO_N_GENERATE_ALTERNATE (1 << 24)
#define HDMI_NV_PDISP_HDCPRIF_ROM_TIMING 0x94
#define HDMI_NV_PDISP_SOR_REFCLK 0x95
#define SOR_REFCLK_DIV_INT(x) (((x) & 0xff) << 8)
#define SOR_REFCLK_DIV_FRAC(x) (((x) & 0x03) << 6)
#define HDMI_NV_PDISP_CRC_CONTROL 0x96
#define HDMI_NV_PDISP_INPUT_CONTROL 0x97
#define HDMI_SRC_DISPLAYA (0 << 0)
#define HDMI_SRC_DISPLAYB (1 << 0)
#define ARM_VIDEO_RANGE_FULL (0 << 1)
#define ARM_VIDEO_RANGE_LIMITED (1 << 1)
#define HDMI_NV_PDISP_SCRATCH 0x98
#define HDMI_NV_PDISP_PE_CURRENT 0x99
#define PE_CURRENT0(x) (((x) & 0xf) << 0)
#define PE_CURRENT1(x) (((x) & 0xf) << 8)
#define PE_CURRENT2(x) (((x) & 0xf) << 16)
#define PE_CURRENT3(x) (((x) & 0xf) << 24)
#define PE_CURRENT_0_0_mA 0x0
#define PE_CURRENT_0_5_mA 0x1
#define PE_CURRENT_1_0_mA 0x2
#define PE_CURRENT_1_5_mA 0x3
#define PE_CURRENT_2_0_mA 0x4
#define PE_CURRENT_2_5_mA 0x5
#define PE_CURRENT_3_0_mA 0x6
#define PE_CURRENT_3_5_mA 0x7
#define PE_CURRENT_4_0_mA 0x8
#define PE_CURRENT_4_5_mA 0x9
#define PE_CURRENT_5_0_mA 0xa
#define PE_CURRENT_5_5_mA 0xb
#define PE_CURRENT_6_0_mA 0xc
#define PE_CURRENT_6_5_mA 0xd
#define PE_CURRENT_7_0_mA 0xe
#define PE_CURRENT_7_5_mA 0xf
#define PE_CURRENT_0_mA_T114 0x0
#define PE_CURRENT_1_mA_T114 0x1
#define PE_CURRENT_2_mA_T114 0x2
#define PE_CURRENT_3_mA_T114 0x3
#define PE_CURRENT_4_mA_T114 0x4
#define PE_CURRENT_5_mA_T114 0x5
#define PE_CURRENT_6_mA_T114 0x6
#define PE_CURRENT_7_mA_T114 0x7
#define PE_CURRENT_8_mA_T114 0x8
#define PE_CURRENT_9_mA_T114 0x9
#define PE_CURRENT_10_mA_T114 0xa
#define PE_CURRENT_11_mA_T114 0xb
#define PE_CURRENT_12_mA_T114 0xc
#define PE_CURRENT_13_mA_T114 0xd
#define PE_CURRENT_14_mA_T114 0xe
#define PE_CURRENT_15_mA_T114 0xf
#define HDMI_NV_PDISP_KEY_CTRL 0x9a
#define HDMI_NV_PDISP_KEY_DEBUG0 0x9b
#define HDMI_NV_PDISP_KEY_DEBUG1 0x9c
#define HDMI_NV_PDISP_KEY_DEBUG2 0x9d
#define HDMI_NV_PDISP_KEY_HDCP_KEY_0 0x9e
#define HDMI_NV_PDISP_KEY_HDCP_KEY_1 0x9f
#define HDMI_NV_PDISP_KEY_HDCP_KEY_2 0xa0
#define HDMI_NV_PDISP_KEY_HDCP_KEY_3 0xa1
#define HDMI_NV_PDISP_KEY_HDCP_KEY_TRIG 0xa2
#define HDMI_NV_PDISP_KEY_SKEY_INDEX 0xa3
#define HDMI_NV_PDISP_SOR_AUDIO_CNTRL0 0xac
#define AUDIO_CNTRL0_INJECT_NULLSMPL (1 << 29)
#define HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR 0xbc
#define HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE 0xbd
#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0320 0xbf
#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0441 0xc0
#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0882 0xc1
#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_1764 0xc2
#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0480 0xc3
#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0960 0xc4
#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_1920 0xc5
#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_DEFAULT 0xc5
#define HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT 0xd1
#define PEAK_CURRENT_LANE0(x) (((x) & 0x7f) << 0)
#define PEAK_CURRENT_LANE1(x) (((x) & 0x7f) << 8)
#define PEAK_CURRENT_LANE2(x) (((x) & 0x7f) << 16)
#define PEAK_CURRENT_LANE3(x) (((x) & 0x7f) << 24)
#define PEAK_CURRENT_0_000_mA 0x00
#define PEAK_CURRENT_0_200_mA 0x01
#define PEAK_CURRENT_0_400_mA 0x02
#define PEAK_CURRENT_0_600_mA 0x03
#define PEAK_CURRENT_0_800_mA 0x04
#define PEAK_CURRENT_1_000_mA 0x05
#define PEAK_CURRENT_1_200_mA 0x06
#define PEAK_CURRENT_1_400_mA 0x07
#define PEAK_CURRENT_1_600_mA 0x08
#define PEAK_CURRENT_1_800_mA 0x09
#define PEAK_CURRENT_2_000_mA 0x0a
#define PEAK_CURRENT_2_200_mA 0x0b
#define PEAK_CURRENT_2_400_mA 0x0c
#define PEAK_CURRENT_2_600_mA 0x0d
#define PEAK_CURRENT_2_800_mA 0x0e
#define PEAK_CURRENT_3_000_mA 0x0f
#define PEAK_CURRENT_3_200_mA 0x10
#define PEAK_CURRENT_3_400_mA 0x11
#define PEAK_CURRENT_3_600_mA 0x12
#define PEAK_CURRENT_3_800_mA 0x13
#define PEAK_CURRENT_4_000_mA 0x14
#define PEAK_CURRENT_4_200_mA 0x15
#define PEAK_CURRENT_4_400_mA 0x16
#define PEAK_CURRENT_4_600_mA 0x17
#define PEAK_CURRENT_4_800_mA 0x18
#define PEAK_CURRENT_5_000_mA 0x19
#define PEAK_CURRENT_5_200_mA 0x1a
#define PEAK_CURRENT_5_400_mA 0x1b
#define PEAK_CURRENT_5_600_mA 0x1c
#define PEAK_CURRENT_5_800_mA 0x1d
#define PEAK_CURRENT_6_000_mA 0x1e
#define PEAK_CURRENT_6_200_mA 0x1f
#define PEAK_CURRENT_6_400_mA 0x20
#define PEAK_CURRENT_6_600_mA 0x21
#define PEAK_CURRENT_6_800_mA 0x22
#define PEAK_CURRENT_7_000_mA 0x23
#define PEAK_CURRENT_7_200_mA 0x24
#define PEAK_CURRENT_7_400_mA 0x25
#define PEAK_CURRENT_7_600_mA 0x26
#define PEAK_CURRENT_7_800_mA 0x27
#define PEAK_CURRENT_8_000_mA 0x28
#define PEAK_CURRENT_8_200_mA 0x29
#define PEAK_CURRENT_8_400_mA 0x2a
#define PEAK_CURRENT_8_600_mA 0x2b
#define PEAK_CURRENT_8_800_mA 0x2c
#define PEAK_CURRENT_9_000_mA 0x2d
#define PEAK_CURRENT_9_200_mA 0x2e
#define PEAK_CURRENT_9_400_mA 0x2f
#define HDMI_NV_PDISP_SOR_PAD_CTLS0 0xd2
#endif /* TEGRA_HDMI_H */

View File

@@ -0,0 +1,124 @@
/*
* Copyright (C) 2013 NVIDIA Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/errno.h>
#include <linux/kernel.h>
#include "mipi-phy.h"
/*
* Default D-PHY timings based on MIPI D-PHY specification. Derived from
* the valid ranges specified in Section 5.9 of the D-PHY specification
* with minor adjustments.
*/
int mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing,
unsigned long period)
{
timing->clkmiss = 0;
timing->clkpost = 70 + 52 * period;
timing->clkpre = 8;
timing->clkprepare = 65;
timing->clksettle = 95;
timing->clktermen = 0;
timing->clktrail = 80;
timing->clkzero = 260;
timing->dtermen = 0;
timing->eot = 0;
timing->hsexit = 120;
timing->hsprepare = 65 + 5 * period;
timing->hszero = 145 + 5 * period;
timing->hssettle = 85 + 6 * period;
timing->hsskip = 40;
timing->hstrail = max(8 * period, 60 + 4 * period);
timing->init = 100000;
timing->lpx = 60;
timing->taget = 5 * timing->lpx;
timing->tago = 4 * timing->lpx;
timing->tasure = 2 * timing->lpx;
timing->wakeup = 1000000;
return 0;
}
/*
* Validate D-PHY timing according to MIPI Alliance Specification for D-PHY,
* Section 5.9 "Global Operation Timing Parameters".
*/
int mipi_dphy_timing_validate(struct mipi_dphy_timing *timing,
unsigned long period)
{
if (timing->clkmiss > 60)
return -EINVAL;
if (timing->clkpost < (60 + 52 * period))
return -EINVAL;
if (timing->clkpre < 8)
return -EINVAL;
if (timing->clkprepare < 38 || timing->clkprepare > 95)
return -EINVAL;
if (timing->clksettle < 95 || timing->clksettle > 300)
return -EINVAL;
if (timing->clktermen > 38)
return -EINVAL;
if (timing->clktrail < 60)
return -EINVAL;
if (timing->clkprepare + timing->clkzero < 300)
return -EINVAL;
if (timing->dtermen > 35 + 4 * period)
return -EINVAL;
if (timing->eot > 105 + 12 * period)
return -EINVAL;
if (timing->hsexit < 100)
return -EINVAL;
if (timing->hsprepare < 40 + 4 * period ||
timing->hsprepare > 85 + 6 * period)
return -EINVAL;
if (timing->hsprepare + timing->hszero < 145 + 10 * period)
return -EINVAL;
if ((timing->hssettle < 85 + 6 * period) ||
(timing->hssettle > 145 + 10 * period))
return -EINVAL;
if (timing->hsskip < 40 || timing->hsskip > 55 + 4 * period)
return -EINVAL;
if (timing->hstrail < max(8 * period, 60 + 4 * period))
return -EINVAL;
if (timing->init < 100000)
return -EINVAL;
if (timing->lpx < 50)
return -EINVAL;
if (timing->taget != 5 * timing->lpx)
return -EINVAL;
if (timing->tago != 4 * timing->lpx)
return -EINVAL;
if (timing->tasure < timing->lpx || timing->tasure > 2 * timing->lpx)
return -EINVAL;
if (timing->wakeup < 1000000)
return -EINVAL;
return 0;
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2013 NVIDIA Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef DRM_TEGRA_MIPI_PHY_H
#define DRM_TEGRA_MIPI_PHY_H
/*
* D-PHY timing parameters
*
* A detailed description of these parameters can be found in the MIPI
* Alliance Specification for D-PHY, Section 5.9 "Global Operation Timing
* Parameters".
*
* All parameters are specified in nanoseconds.
*/
struct mipi_dphy_timing {
unsigned int clkmiss;
unsigned int clkpost;
unsigned int clkpre;
unsigned int clkprepare;
unsigned int clksettle;
unsigned int clktermen;
unsigned int clktrail;
unsigned int clkzero;
unsigned int dtermen;
unsigned int eot;
unsigned int hsexit;
unsigned int hsprepare;
unsigned int hszero;
unsigned int hssettle;
unsigned int hsskip;
unsigned int hstrail;
unsigned int init;
unsigned int lpx;
unsigned int taget;
unsigned int tago;
unsigned int tasure;
unsigned int wakeup;
};
int mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing,
unsigned long period);
int mipi_dphy_timing_validate(struct mipi_dphy_timing *timing,
unsigned long period);
#endif

View File

@@ -0,0 +1,486 @@
/*
* Copyright (C) 2012 Avionic Design GmbH
* Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/of_i2c.h>
#include <linux/of_gpio.h>
#include <linux/of_i2c.h>
#include <drm/drm_panel.h>
#include "drm.h"
static int tegra_connector_get_modes(struct drm_connector *connector)
{
struct tegra_output *output = connector_to_output(connector);
struct edid *edid = NULL;
int err = 0;
if (output->edid) {
edid = kmemdup(output->edid, sizeof(*edid), GFP_KERNEL);
} else if (output->ddc) {
edid = drm_get_edid(connector, output->ddc);
} else if (output->panel) {
err = output->panel->funcs->get_modes(output->panel);
if (err > 0)
return err;
}
drm_mode_connector_update_edid_property(connector, edid);
if (edid) {
err = drm_add_edid_modes(connector, edid);
kfree(edid);
}
return err;
}
static int tegra_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct tegra_output *output = connector_to_output(connector);
enum drm_mode_status status = MODE_OK;
int err;
err = tegra_output_check_mode(output, mode, &status);
if (err < 0)
return MODE_ERROR;
return status;
}
static struct drm_encoder *
tegra_connector_best_encoder(struct drm_connector *connector)
{
struct tegra_output *output = connector_to_output(connector);
return &output->encoder;
}
static const struct drm_connector_helper_funcs connector_helper_funcs = {
.get_modes = tegra_connector_get_modes,
.mode_valid = tegra_connector_mode_valid,
.best_encoder = tegra_connector_best_encoder,
};
static enum drm_connector_status
tegra_connector_detect(struct drm_connector *connector, bool force)
{
struct tegra_output *output = connector_to_output(connector);
enum drm_connector_status status = connector_status_unknown;
if (output->ops->detect)
return output->ops->detect(output);
if (gpio_is_valid(output->hpd_gpio)) {
if (gpio_get_value(output->hpd_gpio) == 0)
status = connector_status_disconnected;
else
status = connector_status_connected;
} else {
if (!output->panel)
status = connector_status_disconnected;
else
status = connector_status_connected;
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
status = connector_status_connected;
}
return status;
}
static void drm_connector_clear(struct drm_connector *connector)
{
memset(connector, 0, sizeof(*connector));
}
static void tegra_connector_destroy(struct drm_connector *connector)
{
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
drm_connector_clear(connector);
}
static void tegra_connector_dpms(struct drm_connector *connector, int mode)
{
/*
* We don't make use of connector->dpms.
* But we need this function otherwise kernel crashes in
* drm_crtc_helper_set_config because it calls connector->dpms
* without checking whether it is defined.
*/
}
static void tegra_connector_save(struct drm_connector *connector)
{
struct tegra_output *output = connector_to_output(connector);
output->suspended = true;
}
static void tegra_connector_restore(struct drm_connector *connector)
{
struct tegra_output *output = connector_to_output(connector);
output->suspended = false;
}
static const struct drm_connector_funcs connector_funcs = {
.dpms = tegra_connector_dpms,
.detect = tegra_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = tegra_connector_destroy,
.save = tegra_connector_save,
.restore = tegra_connector_restore,
};
int tegra_output_panel_enable(struct tegra_output *output)
{
int err;
if (!output->panel)
return 0;
if (output->panel_enabled)
return 0;
err = drm_panel_enable(output->panel);
if (err < 0) {
dev_err(output->dev, "panel enable failed: %d\n", err);
return err;
}
output->panel_enabled = true;
return 0;
}
int tegra_output_panel_disable(struct tegra_output *output)
{
int err;
if (!output->panel)
return 0;
if (!output->panel_enabled)
return 0;
err = drm_panel_disable(output->panel);
if (err < 0) {
dev_err(output->dev, "panel disable failed: %d\n", err);
return err;
}
output->panel_enabled = false;
return 0;
}
int tegra_output_panel_prepare(struct tegra_output *output)
{
int err;
if (!output->panel)
return 0;
if (output->panel_prepared)
return 0;
err = drm_panel_prepare(output->panel);
if (err < 0) {
dev_err(output->dev, "panel prepare failed: %d\n", err);
return err;
}
output->panel_prepared = true;
return 0;
}
int tegra_output_panel_unprepare(struct tegra_output *output)
{
int err;
if (!output->panel)
return 0;
if (!output->panel_prepared)
return 0;
err = drm_panel_unprepare(output->panel);
if (err < 0) {
dev_err(output->dev, "panel unprepare failed: %d\n", err);
return err;
}
output->panel_prepared = false;
return 0;
}
static void drm_encoder_clear(struct drm_encoder *encoder)
{
memset(encoder, 0, sizeof(*encoder));
}
static void tegra_encoder_destroy(struct drm_encoder *encoder)
{
drm_encoder_cleanup(encoder);
drm_encoder_clear(encoder);
}
static const struct drm_encoder_funcs encoder_funcs = {
.destroy = tegra_encoder_destroy,
};
static void tegra_encoder_disable(struct drm_encoder *encoder)
{
struct tegra_output *output = encoder_to_output(encoder);
if (!output->enabled)
return;
tegra_output_panel_disable(output);
tegra_output_disable(output);
output->enabled = false;
}
static void tegra_encoder_enable(struct drm_encoder *encoder)
{
struct tegra_output *output = encoder_to_output(encoder);
if (output->enabled)
return;
tegra_output_enable(output);
tegra_output_panel_enable(output);
output->enabled = true;
}
static bool tegra_encoder_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted)
{
return true;
}
static void tegra_encoder_prepare(struct drm_encoder *encoder)
{
struct tegra_output *output = encoder_to_output(encoder);
bool mode_changed = false;
if (encoder->crtc)
mode_changed = !drm_mode_equal(&encoder->crtc->mode,
&output->saved_mode);
if (mode_changed) {
output->saved_mode = encoder->crtc->mode;
/*
* For some outputs(like HDMI), userspace may change resolutions
* without disable output first. So we check mode change here.
* In addition, in order to not break the output enable/disable
* & panel enable/disable refcounting balance, we disable
* encoder manually.
*/
tegra_encoder_disable(encoder);
}
}
static void tegra_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted)
{
}
static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
.disable = tegra_encoder_disable,
.mode_fixup = tegra_encoder_mode_fixup,
.prepare = tegra_encoder_prepare,
.commit = tegra_encoder_enable,
.mode_set = tegra_encoder_mode_set,
};
static irqreturn_t hpd_irq(int irq, void *data)
{
struct tegra_output *output = data;
if (output->connector.dev && !output->suspended)
drm_helper_hpd_irq_event(output->connector.dev);
return IRQ_HANDLED;
}
int tegra_output_probe(struct tegra_output *output)
{
struct device_node *ddc, *panel;
enum of_gpio_flags flags;
int err, size;
if (!output->of_node)
output->of_node = output->dev->of_node;
panel = of_parse_phandle(output->of_node, "nvidia,panel", 0);
if (panel) {
output->panel = of_drm_find_panel(panel);
if (!output->panel)
return -EPROBE_DEFER;
of_node_put(panel);
}
output->edid = of_get_property(output->of_node, "nvidia,edid", &size);
ddc = of_parse_phandle(output->of_node, "nvidia,ddc-i2c-bus", 0);
if (ddc) {
output->ddc = of_find_i2c_adapter_by_node(ddc);
if (!output->ddc) {
err = -EPROBE_DEFER;
of_node_put(ddc);
return err;
}
of_node_put(ddc);
}
output->hpd_gpio = of_get_named_gpio_flags(output->of_node,
"nvidia,hpd-gpio", 0,
&flags);
if (gpio_is_valid(output->hpd_gpio)) {
unsigned long flags;
err = gpio_request_one(output->hpd_gpio, GPIOF_DIR_IN,
"HDMI hotplug detect");
if (err < 0) {
dev_err(output->dev, "gpio_request_one(): %d\n", err);
return err;
}
err = gpio_to_irq(output->hpd_gpio);
if (err < 0) {
dev_err(output->dev, "gpio_to_irq(): %d\n", err);
gpio_free(output->hpd_gpio);
return err;
}
output->hpd_irq = err;
flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
IRQF_ONESHOT;
err = request_threaded_irq(output->hpd_irq, NULL, hpd_irq,
flags, "hpd", output);
if (err < 0) {
dev_err(output->dev, "failed to request IRQ#%u: %d\n",
output->hpd_irq, err);
gpio_free(output->hpd_gpio);
return err;
}
output->connector.polled = DRM_CONNECTOR_POLL_HPD;
/*
* Disable the interrupt until the connector has been
* initialized to avoid a race in the hotplug interrupt
* handler.
*/
disable_irq(output->hpd_irq);
}
return 0;
}
int tegra_output_remove(struct tegra_output *output)
{
if (gpio_is_valid(output->hpd_gpio)) {
free_irq(output->hpd_irq, output);
gpio_free(output->hpd_gpio);
}
if (output->ddc)
put_device(&output->ddc->dev);
return 0;
}
int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
{
int connector, encoder;
switch (output->type) {
case TEGRA_OUTPUT_RGB:
connector = DRM_MODE_CONNECTOR_LVDS;
encoder = DRM_MODE_ENCODER_LVDS;
break;
case TEGRA_OUTPUT_HDMI:
connector = DRM_MODE_CONNECTOR_HDMIA;
encoder = DRM_MODE_ENCODER_TMDS;
break;
case TEGRA_OUTPUT_DSI:
connector = DRM_MODE_CONNECTOR_DSI;
encoder = DRM_MODE_ENCODER_DSI;
break;
case TEGRA_OUTPUT_EDP:
connector = DRM_MODE_CONNECTOR_eDP;
encoder = DRM_MODE_ENCODER_TMDS;
break;
default:
connector = DRM_MODE_CONNECTOR_Unknown;
encoder = DRM_MODE_ENCODER_NONE;
break;
}
drm_connector_init(drm, &output->connector, &connector_funcs,
connector);
drm_connector_helper_add(&output->connector, &connector_helper_funcs);
output->connector.dpms = DRM_MODE_DPMS_OFF;
if (output->panel)
drm_panel_attach(output->panel, &output->connector);
drm_encoder_init(drm, &output->encoder, &encoder_funcs, encoder);
drm_encoder_helper_add(&output->encoder, &encoder_helper_funcs);
drm_mode_connector_attach_encoder(&output->connector, &output->encoder);
drm_sysfs_connector_add(&output->connector);
switch (output->type) {
case TEGRA_OUTPUT_EDP:
output->encoder.possible_crtcs = 0x1;
break;
case TEGRA_OUTPUT_HDMI:
output->encoder.possible_crtcs = 0x2;
break;
default:
output->encoder.possible_crtcs = 0x3;
break;
}
/*
* The connector is now registered and ready to receive hotplug events
* so the hotplug interrupt can be enabled.
*/
if (gpio_is_valid(output->hpd_gpio))
enable_irq(output->hpd_irq);
return 0;
}
int tegra_output_exit(struct tegra_output *output)
{
/*
* The connector is going away, so the interrupt must be disabled to
* prevent the hotplug interrupt handler from potentially crashing.
*/
if (gpio_is_valid(output->hpd_gpio))
disable_irq(output->hpd_irq);
return 0;
}

325
drivers/gpu/drm/tegra/rgb.c Normal file
View File

@@ -0,0 +1,325 @@
/*
* Copyright (C) 2012 Avionic Design GmbH
* Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include "drm.h"
#include "dc.h"
struct tegra_rgb {
struct tegra_output output;
struct tegra_dc *dc;
bool enabled;
struct clk *clk_parent;
struct clk *clk;
};
static inline struct tegra_rgb *to_rgb(struct tegra_output *output)
{
return container_of(output, struct tegra_rgb, output);
}
struct reg_entry {
unsigned long offset;
unsigned long value;
};
static const struct reg_entry rgb_enable[] = {
{ DC_COM_PIN_OUTPUT_ENABLE(0), 0x00000000 },
{ DC_COM_PIN_OUTPUT_ENABLE(1), 0x00000000 },
{ DC_COM_PIN_OUTPUT_ENABLE(2), 0x00000000 },
{ DC_COM_PIN_OUTPUT_ENABLE(3), 0x00000000 },
{ DC_COM_PIN_OUTPUT_POLARITY(0), 0x00000000 },
{ DC_COM_PIN_OUTPUT_POLARITY(1), 0x01000000 },
{ DC_COM_PIN_OUTPUT_POLARITY(2), 0x00000000 },
{ DC_COM_PIN_OUTPUT_POLARITY(3), 0x00000000 },
{ DC_COM_PIN_OUTPUT_DATA(0), 0x00000000 },
{ DC_COM_PIN_OUTPUT_DATA(1), 0x00000000 },
{ DC_COM_PIN_OUTPUT_DATA(2), 0x00000000 },
{ DC_COM_PIN_OUTPUT_DATA(3), 0x00000000 },
{ DC_COM_PIN_OUTPUT_SELECT(0), 0x00000000 },
{ DC_COM_PIN_OUTPUT_SELECT(1), 0x00000000 },
{ DC_COM_PIN_OUTPUT_SELECT(2), 0x00000000 },
{ DC_COM_PIN_OUTPUT_SELECT(3), 0x00000000 },
{ DC_COM_PIN_OUTPUT_SELECT(4), 0x00210222 },
{ DC_COM_PIN_OUTPUT_SELECT(5), 0x00002200 },
{ DC_COM_PIN_OUTPUT_SELECT(6), 0x00020000 },
};
static const struct reg_entry rgb_disable[] = {
{ DC_COM_PIN_OUTPUT_SELECT(6), 0x00000000 },
{ DC_COM_PIN_OUTPUT_SELECT(5), 0x00000000 },
{ DC_COM_PIN_OUTPUT_SELECT(4), 0x00000000 },
{ DC_COM_PIN_OUTPUT_SELECT(3), 0x00000000 },
{ DC_COM_PIN_OUTPUT_SELECT(2), 0x00000000 },
{ DC_COM_PIN_OUTPUT_SELECT(1), 0x00000000 },
{ DC_COM_PIN_OUTPUT_SELECT(0), 0x00000000 },
{ DC_COM_PIN_OUTPUT_DATA(3), 0xaaaaaaaa },
{ DC_COM_PIN_OUTPUT_DATA(2), 0xaaaaaaaa },
{ DC_COM_PIN_OUTPUT_DATA(1), 0xaaaaaaaa },
{ DC_COM_PIN_OUTPUT_DATA(0), 0xaaaaaaaa },
{ DC_COM_PIN_OUTPUT_POLARITY(3), 0x00000000 },
{ DC_COM_PIN_OUTPUT_POLARITY(2), 0x00000000 },
{ DC_COM_PIN_OUTPUT_POLARITY(1), 0x00000000 },
{ DC_COM_PIN_OUTPUT_POLARITY(0), 0x00000000 },
{ DC_COM_PIN_OUTPUT_ENABLE(3), 0x55555555 },
{ DC_COM_PIN_OUTPUT_ENABLE(2), 0x55555555 },
{ DC_COM_PIN_OUTPUT_ENABLE(1), 0x55150005 },
{ DC_COM_PIN_OUTPUT_ENABLE(0), 0x55555555 },
};
static void tegra_dc_write_regs(struct tegra_dc *dc,
const struct reg_entry *table,
unsigned int num)
{
unsigned int i;
for (i = 0; i < num; i++)
tegra_dc_writel(dc, table[i].value, table[i].offset);
}
static int tegra_output_rgb_enable(struct tegra_output *output)
{
struct tegra_rgb *rgb = to_rgb(output);
unsigned long value;
if (rgb->enabled)
return 0;
tegra_dc_write_regs(rgb->dc, rgb_enable, ARRAY_SIZE(rgb_enable));
value = DE_SELECT_ACTIVE | DE_CONTROL_NORMAL;
tegra_dc_writel(rgb->dc, value, DC_DISP_DATA_ENABLE_OPTIONS);
/* XXX: parameterize? */
value = tegra_dc_readl(rgb->dc, DC_COM_PIN_OUTPUT_POLARITY(1));
value &= ~LVS_OUTPUT_POLARITY_LOW;
value &= ~LHS_OUTPUT_POLARITY_LOW;
tegra_dc_writel(rgb->dc, value, DC_COM_PIN_OUTPUT_POLARITY(1));
/* XXX: parameterize? */
value = DISP_DATA_FORMAT_DF1P1C | DISP_ALIGNMENT_MSB |
DISP_ORDER_RED_BLUE;
tegra_dc_writel(rgb->dc, value, DC_DISP_DISP_INTERFACE_CONTROL);
/* XXX: parameterize? */
value = SC0_H_QUALIFIER_NONE | SC1_H_QUALIFIER_NONE;
tegra_dc_writel(rgb->dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS);
value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_COMMAND);
value &= ~DISP_CTRL_MODE_MASK;
value |= DISP_CTRL_MODE_C_DISPLAY;
tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_COMMAND);
value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_POWER_CONTROL);
value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
rgb->enabled = true;
return 0;
}
static int tegra_output_rgb_disable(struct tegra_output *output)
{
struct tegra_rgb *rgb = to_rgb(output);
unsigned long value;
if (!rgb->enabled)
return 0;
value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_POWER_CONTROL);
value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_COMMAND);
value &= ~DISP_CTRL_MODE_MASK;
tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_COMMAND);
tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
tegra_dc_write_regs(rgb->dc, rgb_disable, ARRAY_SIZE(rgb_disable));
rgb->enabled = false;
return 0;
}
static int tegra_output_rgb_setup_clock(struct tegra_output *output,
struct clk *clk, unsigned long pclk,
unsigned int *div)
{
struct tegra_rgb *rgb = to_rgb(output);
int err;
err = clk_set_parent(clk, rgb->clk_parent);
if (err < 0) {
dev_err(output->dev, "failed to set parent: %d\n", err);
return err;
}
/*
* We may not want to change the frequency of the parent clock, since
* it may be a parent for other peripherals. This is due to the fact
* that on Tegra20 there's only a single clock dedicated to display
* (pll_d_out0), whereas later generations have a second one that can
* be used to independently drive a second output (pll_d2_out0).
*
* As a way to support multiple outputs on Tegra20 as well, pll_p is
* typically used as the parent clock for the display controllers.
* But this comes at a cost: pll_p is the parent of several other
* peripherals, so its frequency shouldn't change out of the blue.
*
* The best we can do at this point is to use the shift clock divider
* and hope that the desired frequency can be matched (or at least
* matched sufficiently close that the panel will still work).
*/
*div = ((clk_get_rate(clk) * 2) / pclk) - 2;
return 0;
}
static int tegra_output_rgb_check_mode(struct tegra_output *output,
struct drm_display_mode *mode,
enum drm_mode_status *status)
{
/*
* FIXME: For now, always assume that the mode is okay. There are
* unresolved issues with clk_round_rate(), which doesn't always
* reliably report whether a frequency can be set or not.
*/
*status = MODE_OK;
return 0;
}
static const struct tegra_output_ops rgb_ops = {
.enable = tegra_output_rgb_enable,
.disable = tegra_output_rgb_disable,
.setup_clock = tegra_output_rgb_setup_clock,
.check_mode = tegra_output_rgb_check_mode,
};
int tegra_dc_rgb_probe(struct tegra_dc *dc)
{
struct device_node *np;
struct tegra_rgb *rgb;
int err;
np = of_get_child_by_name(dc->dev->of_node, "rgb");
if (!np || !of_device_is_available(np))
return -ENODEV;
rgb = devm_kzalloc(dc->dev, sizeof(*rgb), GFP_KERNEL);
if (!rgb)
return -ENOMEM;
rgb->output.dev = dc->dev;
rgb->output.of_node = np;
rgb->dc = dc;
err = tegra_output_probe(&rgb->output);
if (err < 0)
return err;
rgb->clk = devm_clk_get(dc->dev, NULL);
if (IS_ERR(rgb->clk)) {
dev_err(dc->dev, "failed to get clock\n");
return PTR_ERR(rgb->clk);
}
rgb->clk_parent = devm_clk_get(dc->dev, "parent");
if (IS_ERR(rgb->clk_parent)) {
dev_err(dc->dev, "failed to get parent clock\n");
return PTR_ERR(rgb->clk_parent);
}
err = clk_set_parent(rgb->clk, rgb->clk_parent);
if (err < 0) {
dev_err(dc->dev, "failed to set parent clock: %d\n", err);
return err;
}
dc->rgb = &rgb->output;
return 0;
}
int tegra_dc_rgb_remove(struct tegra_dc *dc)
{
int err;
if (!dc->rgb)
return 0;
err = tegra_output_remove(dc->rgb);
if (err < 0)
return err;
return 0;
}
int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)
{
struct tegra_rgb *rgb = to_rgb(dc->rgb);
int err;
if (!dc->rgb)
return -ENODEV;
rgb->output.type = TEGRA_OUTPUT_RGB;
rgb->output.ops = &rgb_ops;
err = tegra_output_init(dc->base.dev, &rgb->output);
if (err < 0) {
dev_err(dc->dev, "output setup failed: %d\n", err);
return err;
}
/*
* By default, outputs can be associated with each display controller.
* RGB outputs are an exception, so we make sure they can be attached
* to only their parent display controller.
*/
rgb->output.encoder.possible_crtcs = drm_crtc_mask(&dc->base);
return 0;
}
int tegra_dc_rgb_exit(struct tegra_dc *dc)
{
if (dc->rgb) {
int err;
err = tegra_output_disable(dc->rgb);
if (err < 0) {
dev_err(dc->dev, "output failed to disable: %d\n", err);
return err;
}
err = tegra_output_exit(dc->rgb);
if (err < 0) {
dev_err(dc->dev, "output cleanup failed: %d\n", err);
return err;
}
dc->rgb = NULL;
}
return 0;
}

1975
drivers/gpu/drm/tegra/sor.c Normal file

File diff suppressed because it is too large Load Diff

292
drivers/gpu/drm/tegra/sor.h Normal file
View File

@@ -0,0 +1,292 @@
/*
* Copyright (C) 2013 NVIDIA Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef DRM_TEGRA_SOR_H
#define DRM_TEGRA_SOR_H
#define SOR_CTXSW 0x00
#define SOR_SUPER_STATE_0 0x01
#define SOR_SUPER_STATE_1 0x02
#define SOR_SUPER_STATE_ATTACHED (1 << 3)
#define SOR_SUPER_STATE_MODE_NORMAL (1 << 2)
#define SOR_SUPER_STATE_HEAD_MODE_MASK (3 << 0)
#define SOR_SUPER_STATE_HEAD_MODE_AWAKE (2 << 0)
#define SOR_SUPER_STATE_HEAD_MODE_SNOOZE (1 << 0)
#define SOR_SUPER_STATE_HEAD_MODE_SLEEP (0 << 0)
#define SOR_STATE_0 0x03
#define SOR_STATE_1 0x04
#define SOR_STATE_ASY_PIXELDEPTH_MASK (0xf << 17)
#define SOR_STATE_ASY_PIXELDEPTH_BPP_18_444 (0x2 << 17)
#define SOR_STATE_ASY_PIXELDEPTH_BPP_24_444 (0x5 << 17)
#define SOR_STATE_ASY_PIXELDEPTH_BPP_30_444 (0x6 << 17)
#define SOR_STATE_ASY_PIXELDEPTH_BPP_36_444 (0x8 << 17)
#define SOR_STATE_ASY_PIXELDEPTH_BPP_48_444 (0x9 << 17)
#define SOR_STATE_ASY_VSYNCPOL (1 << 13)
#define SOR_STATE_ASY_HSYNCPOL (1 << 12)
#define SOR_STATE_ASY_PROTOCOL_MASK (0xf << 8)
#define SOR_STATE_ASY_PROTOCOL_CUSTOM (0xf << 8)
#define SOR_STATE_ASY_PROTOCOL_DP_A (0x8 << 8)
#define SOR_STATE_ASY_PROTOCOL_DP_B (0x9 << 8)
#define SOR_STATE_ASY_PROTOCOL_SINGLE_TMDS_A (0x1 << 8)
#define SOR_STATE_ASY_PROTOCOL_LVDS (0x0 << 8)
#define SOR_STATE_ASY_CRC_MODE_MASK (0x3 << 6)
#define SOR_STATE_ASY_CRC_MODE_NON_ACTIVE (0x2 << 6)
#define SOR_STATE_ASY_CRC_MODE_COMPLETE (0x1 << 6)
#define SOR_STATE_ASY_CRC_MODE_ACTIVE (0x0 << 6)
#define SOR_STATE_ASY_SUBOWNER_MASK (0x3 << 4)
#define SOR_STATE_ASY_OWNER_MASK 0xf
#define SOR_STATE_ASY_OWNER(x) (((x) & 0xf) << 0)
#define SOR_HEAD_STATE_0(x) (0x05 + (x))
#define SOR_HEAD_STATE_1(x) (0x07 + (x))
#define SOR_HEAD_STATE_2(x) (0x09 + (x))
#define SOR_HEAD_STATE_3(x) (0x0b + (x))
#define SOR_HEAD_STATE_4(x) (0x0d + (x))
#define SOR_HEAD_STATE_5(x) (0x0f + (x))
#define SOR_CRC_CNTRL 0x11
#define SOR_CRC_CNTRL_ENABLE (1 << 0)
#define SOR_DP_DEBUG_MVID 0x12
#define SOR_CLK_CNTRL 0x13
#define SOR_CLK_CNTRL_DP_LINK_SPEED_MASK (0x1f << 2)
#define SOR_CLK_CNTRL_DP_LINK_SPEED(x) (((x) & 0x1f) << 2)
#define SOR_CLK_CNTRL_DP_LINK_SPEED_G1_62 (0x06 << 2)
#define SOR_CLK_CNTRL_DP_LINK_SPEED_G2_70 (0x0a << 2)
#define SOR_CLK_CNTRL_DP_LINK_SPEED_G5_40 (0x14 << 2)
#define SOR_CLK_CNTRL_DP_CLK_SEL_MASK (3 << 0)
#define SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_PCLK (0 << 0)
#define SOR_CLK_CNTRL_DP_CLK_SEL_DIFF_PCLK (1 << 0)
#define SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK (2 << 0)
#define SOR_CLK_CNTRL_DP_CLK_SEL_DIFF_DPCLK (3 << 0)
#define SOR_CAP 0x14
#define SOR_PWR 0x15
#define SOR_PWR_TRIGGER (1 << 31)
#define SOR_PWR_MODE_SAFE (1 << 28)
#define SOR_PWR_NORMAL_STATE_PU (1 << 0)
#define SOR_TEST 0x16
#define SOR_TEST_CRC_POST_SERIALIZE (1 << 23)
#define SOR_TEST_ATTACHED (1 << 10)
#define SOR_TEST_HEAD_MODE_MASK (3 << 8)
#define SOR_TEST_HEAD_MODE_AWAKE (2 << 8)
#define SOR_PLL_0 0x17
#define SOR_PLL_0_ICHPMP_MASK (0xf << 24)
#define SOR_PLL_0_ICHPMP(x) (((x) & 0xf) << 24)
#define SOR_PLL_0_VCOCAP_MASK (0xf << 8)
#define SOR_PLL_0_VCOCAP(x) (((x) & 0xf) << 8)
#define SOR_PLL_0_VCOCAP_RST SOR_PLL_0_VCOCAP(3)
#define SOR_PLL_0_PLLREG_MASK (0x3 << 6)
#define SOR_PLL_0_PLLREG_LEVEL(x) (((x) & 0x3) << 6)
#define SOR_PLL_0_PLLREG_LEVEL_V25 SOR_PLL_0_PLLREG_LEVEL(0)
#define SOR_PLL_0_PLLREG_LEVEL_V15 SOR_PLL_0_PLLREG_LEVEL(1)
#define SOR_PLL_0_PLLREG_LEVEL_V35 SOR_PLL_0_PLLREG_LEVEL(2)
#define SOR_PLL_0_PLLREG_LEVEL_V45 SOR_PLL_0_PLLREG_LEVEL(3)
#define SOR_PLL_0_PULLDOWN (1 << 5)
#define SOR_PLL_0_RESISTOR_EXT (1 << 4)
#define SOR_PLL_0_VCOPD (1 << 2)
#define SOR_PLL_0_POWER_OFF (1 << 0)
#define SOR_PLL_1 0x18
/* XXX: read-only bit? */
#define SOR_PLL1_LOADADJ_MASK (0xf << 20)
#define SOR_PLL1_LOADADJ(x) (((x) & 0xf) << 20)
#define SOR_PLL_1_TERM_COMPOUT (1 << 15)
#define SOR_PLL_1_TMDS_TERMADJ_MASK (0xf << 9)
#define SOR_PLL_1_TMDS_TERMADJ(x) (((x) & 0xf) << 9)
#define SOR_PLL_1_TMDS_TERM (1 << 8)
#define SOR_PLL_2 0x19
#define SOR_PLL_2_LVDS_ENABLE (1 << 25)
#define SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE (1 << 24)
#define SOR_PLL_2_PORT_POWERDOWN (1 << 23)
#define SOR_PLL_2_BANDGAP_POWERDOWN (1 << 22)
#define SOR_PLL_2_POWERDOWN_OVERRIDE (1 << 18)
#define SOR_PLL_2_SEQ_PLLCAPPD (1 << 17)
#define SOR_PLL_3 0x1a
#define SOR_PLL_3_PLL_VDD_MODE_V1_8 (0 << 13)
#define SOR_PLL_3_PLL_VDD_MODE_V3_3 (1 << 13)
#define SOR_CSTM 0x1b
#define SOR_CSTM_LVDS (1 << 16)
#define SOR_CSTM_LINK_ACT_B (1 << 15)
#define SOR_CSTM_LINK_ACT_A (1 << 14)
#define SOR_CSTM_UPPER (1 << 11)
#define SOR_LVDS 0x1c
#define SOR_CRC_A 0x1d
#define SOR_CRC_A_VALID (1 << 0)
#define SOR_CRC_A_RESET (1 << 0)
#define SOR_CRC_B 0x1e
#define SOR_BLANK 0x1f
#define SOR_SEQ_CTL 0x20
#define SOR_LANE_SEQ_CTL 0x21
#define SOR_LANE_SEQ_CTL_TRIGGER (1 << 31)
#define SOR_LANE_SEQ_CTL_SEQUENCE_UP (0 << 20)
#define SOR_LANE_SEQ_CTL_SEQUENCE_DOWN (1 << 20)
#define SOR_LANE_SEQ_CTL_POWER_STATE_UP (0 << 16)
#define SOR_LANE_SEQ_CTL_POWER_STATE_DOWN (1 << 16)
#define SOR_SEQ_INST(x) (0x22 + (x))
#define SOR_PWM_DIV 0x32
#define SOR_PWM_DIV_MASK 0xffffff
#define SOR_PWM_CTL 0x33
#define SOR_PWM_CTL_TRIGGER (1 << 31)
#define SOR_PWM_CTL_CLK_SEL (1 << 30)
#define SOR_PWM_CTL_DUTY_CYCLE_MASK 0xffffff
#define SOR_VCRC_A_0 0x34
#define SOR_VCRC_A_1 0x35
#define SOR_VCRC_B_0 0x36
#define SOR_VCRC_B_1 0x37
#define SOR_CCRC_A_0 0x38
#define SOR_CCRC_A_1 0x39
#define SOR_CCRC_B_0 0x3a
#define SOR_CCRC_B_1 0x3b
#define SOR_EDATA_A_0 0x3c
#define SOR_EDATA_A_1 0x3d
#define SOR_EDATA_B_0 0x3e
#define SOR_EDATA_B_1 0x3f
#define SOR_COUNT_A_0 0x40
#define SOR_COUNT_A_1 0x41
#define SOR_COUNT_B_0 0x42
#define SOR_COUNT_B_1 0x43
#define SOR_DEBUG_A_0 0x44
#define SOR_DEBUG_A_1 0x45
#define SOR_DEBUG_B_0 0x46
#define SOR_DEBUG_B_1 0x47
#define SOR_TRIG 0x48
#define SOR_MSCHECK 0x49
#define SOR_XBAR_CTRL 0x4a
#define SOR_XBAR_POL 0x4b
#define SOR_DP_LINKCTL_0 0x4c
#define SOR_DP_LINKCTL_LANE_COUNT_MASK (0x1f << 16)
#define SOR_DP_LINKCTL_LANE_COUNT(x) (((1 << (x)) - 1) << 16)
#define SOR_DP_LINKCTL_ENHANCED_FRAME (1 << 14)
#define SOR_DP_LINKCTL_TU_SIZE_MASK (0x7f << 2)
#define SOR_DP_LINKCTL_TU_SIZE(x) (((x) & 0x7f) << 2)
#define SOR_DP_LINKCTL_ENABLE (1 << 0)
#define SOR_DP_LINKCTL_1 0x4d
#define SOR_LANE_DRIVE_CURRENT_0 0x4e
#define SOR_LANE_DRIVE_CURRENT_1 0x4f
#define SOR_LANE4_DRIVE_CURRENT_0 0x50
#define SOR_LANE4_DRIVE_CURRENT_1 0x51
#define SOR_LANE_DRIVE_CURRENT_LANE3(x) (((x) & 0xff) << 24)
#define SOR_LANE_DRIVE_CURRENT_LANE2(x) (((x) & 0xff) << 16)
#define SOR_LANE_DRIVE_CURRENT_LANE1(x) (((x) & 0xff) << 8)
#define SOR_LANE_DRIVE_CURRENT_LANE0(x) (((x) & 0xff) << 0)
#define SOR_LANE_PREEMPHASIS_0 0x52
#define SOR_LANE_PREEMPHASIS_1 0x53
#define SOR_LANE4_PREEMPHASIS_0 0x54
#define SOR_LANE4_PREEMPHASIS_1 0x55
#define SOR_LANE_PREEMPHASIS_LANE3(x) (((x) & 0xff) << 24)
#define SOR_LANE_PREEMPHASIS_LANE2(x) (((x) & 0xff) << 16)
#define SOR_LANE_PREEMPHASIS_LANE1(x) (((x) & 0xff) << 8)
#define SOR_LANE_PREEMPHASIS_LANE0(x) (((x) & 0xff) << 0)
#define SOR_LANE_POST_CURSOR_0 0x56
#define SOR_LANE_POST_CURSOR_1 0x57
#define SOR_LANE_POST_CURSOR_LANE3(x) (((x) & 0xff) << 24)
#define SOR_LANE_POST_CURSOR_LANE2(x) (((x) & 0xff) << 16)
#define SOR_LANE_POST_CURSOR_LANE1(x) (((x) & 0xff) << 8)
#define SOR_LANE_POST_CURSOR_LANE0(x) (((x) & 0xff) << 0)
#define SOR_DP_CONFIG_0 0x58
#define SOR_DP_CONFIG_DISPARITY_NEGATIVE (1 << 31)
#define SOR_DP_CONFIG_ACTIVE_SYM_ENABLE (1 << 26)
#define SOR_DP_CONFIG_ACTIVE_SYM_POLARITY (1 << 24)
#define SOR_DP_CONFIG_ACTIVE_SYM_FRAC_MASK (0xf << 16)
#define SOR_DP_CONFIG_ACTIVE_SYM_FRAC(x) (((x) & 0xf) << 16)
#define SOR_DP_CONFIG_ACTIVE_SYM_COUNT_MASK (0x7f << 8)
#define SOR_DP_CONFIG_ACTIVE_SYM_COUNT(x) (((x) & 0x7f) << 8)
#define SOR_DP_CONFIG_WATERMARK_MASK (0x3f << 0)
#define SOR_DP_CONFIG_WATERMARK(x) (((x) & 0x3f) << 0)
#define SOR_DP_CONFIG_1 0x59
#define SOR_DP_MN_0 0x5a
#define SOR_DP_MN_1 0x5b
#define SOR_DP_PADCTL_0 0x5c
#define SOR_DP_PADCTL_PAD_CAL_PD (1 << 23)
#define SOR_DP_PADCTL_TX_PU_ENABLE (1 << 22)
#define SOR_DP_PADCTL_TX_PU_MASK (0xff << 8)
#define SOR_DP_PADCTL_TX_PU(x) (((x) & 0xff) << 8)
#define SOR_DP_PADCTL_CM_TXD_3 (1 << 7)
#define SOR_DP_PADCTL_CM_TXD_2 (1 << 6)
#define SOR_DP_PADCTL_CM_TXD_1 (1 << 5)
#define SOR_DP_PADCTL_CM_TXD_0 (1 << 4)
#define SOR_DP_PADCTL_PD_TXD_3 (1 << 3)
#define SOR_DP_PADCTL_PD_TXD_0 (1 << 2)
#define SOR_DP_PADCTL_PD_TXD_1 (1 << 1)
#define SOR_DP_PADCTL_PD_TXD_2 (1 << 0)
#define SOR_DP_PADCTL_1 0x5d
#define SOR_DP_DEBUG_0 0x5e
#define SOR_DP_DEBUG_1 0x5f
#define SOR_DP_SPARE_0 0x60
#define SOR_DP_SPARE_MACRO_SOR_CLK (1 << 2)
#define SOR_DP_SPARE_PANEL_INTERNAL (1 << 1)
#define SOR_DP_SPARE_SEQ_ENABLE (1 << 0)
#define SOR_DP_SPARE_1 0x61
#define SOR_DP_AUDIO_CTRL 0x62
#define SOR_DP_AUDIO_HBLANK_SYMBOLS 0x63
#define SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK (0x01ffff << 0)
#define SOR_DP_AUDIO_VBLANK_SYMBOLS 0x64
#define SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK (0x1fffff << 0)
#define SOR_DP_GENERIC_INFOFRAME_HEADER 0x65
#define SOR_DP_GENERIC_INFOFRAME_SUBPACK_0 0x66
#define SOR_DP_GENERIC_INFOFRAME_SUBPACK_1 0x67
#define SOR_DP_GENERIC_INFOFRAME_SUBPACK_2 0x68
#define SOR_DP_GENERIC_INFOFRAME_SUBPACK_3 0x69
#define SOR_DP_GENERIC_INFOFRAME_SUBPACK_4 0x6a
#define SOR_DP_GENERIC_INFOFRAME_SUBPACK_5 0x6b
#define SOR_DP_GENERIC_INFOFRAME_SUBPACK_6 0x6c
#define SOR_DP_TPG 0x6d
#define SOR_DP_TPG_CHANNEL_CODING (1 << 6)
#define SOR_DP_TPG_SCRAMBLER_MASK (3 << 4)
#define SOR_DP_TPG_SCRAMBLER_FIBONACCI (2 << 4)
#define SOR_DP_TPG_SCRAMBLER_GALIOS (1 << 4)
#define SOR_DP_TPG_SCRAMBLER_NONE (0 << 4)
#define SOR_DP_TPG_PATTERN_MASK (0xf << 0)
#define SOR_DP_TPG_PATTERN_HBR2 (0x8 << 0)
#define SOR_DP_TPG_PATTERN_CSTM (0x7 << 0)
#define SOR_DP_TPG_PATTERN_PRBS7 (0x6 << 0)
#define SOR_DP_TPG_PATTERN_SBLERRRATE (0x5 << 0)
#define SOR_DP_TPG_PATTERN_D102 (0x4 << 0)
#define SOR_DP_TPG_PATTERN_TRAIN3 (0x3 << 0)
#define SOR_DP_TPG_PATTERN_TRAIN2 (0x2 << 0)
#define SOR_DP_TPG_PATTERN_TRAIN1 (0x1 << 0)
#define SOR_DP_TPG_PATTERN_NONE (0x0 << 0)
#define SOR_DP_TPG_CONFIG 0x6e
#define SOR_DP_LQ_CSTM_0 0x6f
#define SOR_DP_LQ_CSTM_1 0x70
#define SOR_DP_LQ_CSTM_2 0x71
#endif