mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
drm: Add NVIDIA Tegra20 support
This commit adds a KMS driver for the Tegra20 SoC. This includes basic support for host1x and the two display controllers found on the Tegra20 SoC. Each display controller can drive a separate RGB/LVDS output. Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de> Tested-by: Stephen Warren <swarren@nvidia.com> Acked-by: Mark Zhang <markz@nvidia.com> Reviewed-by: Mark Zhang <markz@nvidia.com> Tested-by: Mark Zhang <markz@nvidia.com> Tested-and-acked-by: Alexandre Courbot <acourbot@nvidia.com> Acked-by: Terje Bergstrom <tbergstrom@nvidia.com> Tested-by: Terje Bergstrom <tbergstrom@nvidia.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
This commit is contained in:
committed by
Dave Airlie
parent
b27b6d328a
commit
d8f4a9eda0
@@ -0,0 +1,191 @@
|
||||
NVIDIA Tegra host1x
|
||||
|
||||
Required properties:
|
||||
- compatible: "nvidia,tegra<chip>-host1x"
|
||||
- reg: Physical base address and length of the controller's registers.
|
||||
- interrupts: The interrupt outputs from the controller.
|
||||
- #address-cells: The number of cells used to represent physical base addresses
|
||||
in the host1x address space. Should be 1.
|
||||
- #size-cells: The number of cells used to represent the size of an address
|
||||
range in the host1x address space. Should be 1.
|
||||
- ranges: The mapping of the host1x address space to the CPU address space.
|
||||
|
||||
The host1x top-level node defines a number of children, each representing one
|
||||
of the following host1x client modules:
|
||||
|
||||
- mpe: video encoder
|
||||
|
||||
Required properties:
|
||||
- compatible: "nvidia,tegra<chip>-mpe"
|
||||
- reg: Physical base address and length of the controller's registers.
|
||||
- interrupts: The interrupt outputs from the controller.
|
||||
|
||||
- vi: video input
|
||||
|
||||
Required properties:
|
||||
- compatible: "nvidia,tegra<chip>-vi"
|
||||
- reg: Physical base address and length of the controller's registers.
|
||||
- interrupts: The interrupt outputs from the controller.
|
||||
|
||||
- epp: encoder pre-processor
|
||||
|
||||
Required properties:
|
||||
- compatible: "nvidia,tegra<chip>-epp"
|
||||
- reg: Physical base address and length of the controller's registers.
|
||||
- interrupts: The interrupt outputs from the controller.
|
||||
|
||||
- isp: image signal processor
|
||||
|
||||
Required properties:
|
||||
- compatible: "nvidia,tegra<chip>-isp"
|
||||
- reg: Physical base address and length of the controller's registers.
|
||||
- interrupts: The interrupt outputs from the controller.
|
||||
|
||||
- gr2d: 2D graphics engine
|
||||
|
||||
Required properties:
|
||||
- compatible: "nvidia,tegra<chip>-gr2d"
|
||||
- reg: Physical base address and length of the controller's registers.
|
||||
- interrupts: The interrupt outputs from the controller.
|
||||
|
||||
- gr3d: 3D graphics engine
|
||||
|
||||
Required properties:
|
||||
- compatible: "nvidia,tegra<chip>-gr3d"
|
||||
- reg: Physical base address and length of the controller's registers.
|
||||
|
||||
- dc: display controller
|
||||
|
||||
Required properties:
|
||||
- compatible: "nvidia,tegra<chip>-dc"
|
||||
- reg: Physical base address and length of the controller's registers.
|
||||
- interrupts: The interrupt outputs from the controller.
|
||||
|
||||
Each display controller node has a child node, named "rgb", that represents
|
||||
the RGB output associated with the controller. It can take the following
|
||||
optional properties:
|
||||
- nvidia,ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
|
||||
- nvidia,hpd-gpio: specifies a GPIO used for hotplug detection
|
||||
- nvidia,edid: supplies a binary EDID blob
|
||||
|
||||
- hdmi: High Definition Multimedia Interface
|
||||
|
||||
Required properties:
|
||||
- compatible: "nvidia,tegra<chip>-hdmi"
|
||||
- reg: Physical base address and length of the controller's registers.
|
||||
- interrupts: The interrupt outputs from the controller.
|
||||
- vdd-supply: regulator for supply voltage
|
||||
- pll-supply: regulator for PLL
|
||||
|
||||
Optional properties:
|
||||
- nvidia,ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
|
||||
- nvidia,hpd-gpio: specifies a GPIO used for hotplug detection
|
||||
- nvidia,edid: supplies a binary EDID blob
|
||||
|
||||
- tvo: TV encoder output
|
||||
|
||||
Required properties:
|
||||
- compatible: "nvidia,tegra<chip>-tvo"
|
||||
- reg: Physical base address and length of the controller's registers.
|
||||
- interrupts: The interrupt outputs from the controller.
|
||||
|
||||
- dsi: display serial interface
|
||||
|
||||
Required properties:
|
||||
- compatible: "nvidia,tegra<chip>-dsi"
|
||||
- reg: Physical base address and length of the controller's registers.
|
||||
|
||||
Example:
|
||||
|
||||
/ {
|
||||
...
|
||||
|
||||
host1x {
|
||||
compatible = "nvidia,tegra20-host1x", "simple-bus";
|
||||
reg = <0x50000000 0x00024000>;
|
||||
interrupts = <0 65 0x04 /* mpcore syncpt */
|
||||
0 67 0x04>; /* mpcore general */
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
ranges = <0x54000000 0x54000000 0x04000000>;
|
||||
|
||||
mpe {
|
||||
compatible = "nvidia,tegra20-mpe";
|
||||
reg = <0x54040000 0x00040000>;
|
||||
interrupts = <0 68 0x04>;
|
||||
};
|
||||
|
||||
vi {
|
||||
compatible = "nvidia,tegra20-vi";
|
||||
reg = <0x54080000 0x00040000>;
|
||||
interrupts = <0 69 0x04>;
|
||||
};
|
||||
|
||||
epp {
|
||||
compatible = "nvidia,tegra20-epp";
|
||||
reg = <0x540c0000 0x00040000>;
|
||||
interrupts = <0 70 0x04>;
|
||||
};
|
||||
|
||||
isp {
|
||||
compatible = "nvidia,tegra20-isp";
|
||||
reg = <0x54100000 0x00040000>;
|
||||
interrupts = <0 71 0x04>;
|
||||
};
|
||||
|
||||
gr2d {
|
||||
compatible = "nvidia,tegra20-gr2d";
|
||||
reg = <0x54140000 0x00040000>;
|
||||
interrupts = <0 72 0x04>;
|
||||
};
|
||||
|
||||
gr3d {
|
||||
compatible = "nvidia,tegra20-gr3d";
|
||||
reg = <0x54180000 0x00040000>;
|
||||
};
|
||||
|
||||
dc@54200000 {
|
||||
compatible = "nvidia,tegra20-dc";
|
||||
reg = <0x54200000 0x00040000>;
|
||||
interrupts = <0 73 0x04>;
|
||||
|
||||
rgb {
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
||||
dc@54240000 {
|
||||
compatible = "nvidia,tegra20-dc";
|
||||
reg = <0x54240000 0x00040000>;
|
||||
interrupts = <0 74 0x04>;
|
||||
|
||||
rgb {
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
||||
hdmi {
|
||||
compatible = "nvidia,tegra20-hdmi";
|
||||
reg = <0x54280000 0x00040000>;
|
||||
interrupts = <0 75 0x04>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
tvo {
|
||||
compatible = "nvidia,tegra20-tvo";
|
||||
reg = <0x542c0000 0x00040000>;
|
||||
interrupts = <0 76 0x04>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
dsi {
|
||||
compatible = "nvidia,tegra20-dsi";
|
||||
reg = <0x54300000 0x00040000>;
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
};
|
||||
@@ -210,3 +210,5 @@ source "drivers/gpu/drm/mgag200/Kconfig"
|
||||
source "drivers/gpu/drm/cirrus/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/shmobile/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/tegra/Kconfig"
|
||||
|
||||
@@ -48,4 +48,5 @@ obj-$(CONFIG_DRM_GMA500) += gma500/
|
||||
obj-$(CONFIG_DRM_UDL) += udl/
|
||||
obj-$(CONFIG_DRM_AST) += ast/
|
||||
obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
|
||||
obj-$(CONFIG_DRM_TEGRA) += tegra/
|
||||
obj-y += i2c/
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
config DRM_TEGRA
|
||||
tristate "NVIDIA Tegra DRM"
|
||||
depends on DRM && OF && ARCH_TEGRA
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_GEM_CMA_HELPER
|
||||
select DRM_KMS_CMA_HELPER
|
||||
select FB_CFB_FILLRECT
|
||||
select FB_CFB_COPYAREA
|
||||
select FB_CFB_IMAGEBLIT
|
||||
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_DEBUG
|
||||
bool "NVIDIA Tegra DRM debug support"
|
||||
help
|
||||
Say yes here to enable debugging support.
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,7 @@
|
||||
ccflags-y := -Iinclude/drm
|
||||
ccflags-$(CONFIG_DRM_TEGRA_DEBUG) += -DDEBUG
|
||||
|
||||
tegra-drm-y := drm.o fb.o dc.o host1x.o
|
||||
tegra-drm-y += output.o rgb.o
|
||||
|
||||
obj-$(CONFIG_DRM_TEGRA) += tegra-drm.o
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
* 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 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 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 GENERAL_UPDATE (1 << 8)
|
||||
#define WIN_A_UPDATE (1 << 9)
|
||||
#define WIN_B_UPDATE (1 << 10)
|
||||
#define WIN_C_UPDATE (1 << 11)
|
||||
#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 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 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 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_WIN_WIN_OPTIONS 0x700
|
||||
#define COLOR_EXPAND (1 << 6)
|
||||
#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_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_BLEND32WIN_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_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 */
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#include <mach/clk.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <asm/dma-iommu.h>
|
||||
|
||||
#include "drm.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
|
||||
|
||||
static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
|
||||
{
|
||||
struct device *dev = drm->dev;
|
||||
struct host1x *host1x;
|
||||
int err;
|
||||
|
||||
host1x = dev_get_drvdata(dev);
|
||||
drm->dev_private = host1x;
|
||||
host1x->drm = drm;
|
||||
|
||||
drm_mode_config_init(drm);
|
||||
|
||||
err = host1x_drm_init(host1x, drm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = tegra_drm_fb_init(drm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
drm_kms_helper_poll_init(drm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_drm_unload(struct drm_device *drm)
|
||||
{
|
||||
drm_kms_helper_poll_fini(drm);
|
||||
tegra_drm_fb_exit(drm);
|
||||
|
||||
drm_mode_config_cleanup(drm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra_drm_lastclose(struct drm_device *drm)
|
||||
{
|
||||
struct host1x *host1x = drm->dev_private;
|
||||
|
||||
drm_fbdev_cma_restore_mode(host1x->fbdev);
|
||||
}
|
||||
|
||||
static struct drm_ioctl_desc tegra_drm_ioctls[] = {
|
||||
};
|
||||
|
||||
static const struct file_operations tegra_drm_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.mmap = drm_gem_cma_mmap,
|
||||
.poll = drm_poll,
|
||||
.fasync = drm_fasync,
|
||||
.read = drm_read,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
#endif
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
struct drm_driver tegra_drm_driver = {
|
||||
.driver_features = DRIVER_BUS_PLATFORM | DRIVER_MODESET | DRIVER_GEM,
|
||||
.load = tegra_drm_load,
|
||||
.unload = tegra_drm_unload,
|
||||
.open = tegra_drm_open,
|
||||
.lastclose = tegra_drm_lastclose,
|
||||
|
||||
.gem_free_object = drm_gem_cma_free_object,
|
||||
.gem_vm_ops = &drm_gem_cma_vm_ops,
|
||||
.dumb_create = drm_gem_cma_dumb_create,
|
||||
.dumb_map_offset = drm_gem_cma_dumb_map_offset,
|
||||
.dumb_destroy = drm_gem_cma_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,
|
||||
};
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* 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_DRM_H
|
||||
#define TEGRA_DRM_H 1
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_fixed.h>
|
||||
|
||||
struct tegra_framebuffer {
|
||||
struct drm_framebuffer base;
|
||||
struct drm_gem_cma_object *obj;
|
||||
};
|
||||
|
||||
static inline struct tegra_framebuffer *to_tegra_fb(struct drm_framebuffer *fb)
|
||||
{
|
||||
return container_of(fb, struct tegra_framebuffer, base);
|
||||
}
|
||||
|
||||
struct host1x {
|
||||
struct drm_device *drm;
|
||||
struct device *dev;
|
||||
void __iomem *regs;
|
||||
struct clk *clk;
|
||||
int syncpt;
|
||||
int irq;
|
||||
|
||||
struct mutex drm_clients_lock;
|
||||
struct list_head drm_clients;
|
||||
struct list_head drm_active;
|
||||
|
||||
struct mutex clients_lock;
|
||||
struct list_head clients;
|
||||
|
||||
struct drm_fbdev_cma *fbdev;
|
||||
struct tegra_framebuffer fb;
|
||||
};
|
||||
|
||||
struct host1x_client;
|
||||
|
||||
struct host1x_client_ops {
|
||||
int (*drm_init)(struct host1x_client *client, struct drm_device *drm);
|
||||
int (*drm_exit)(struct host1x_client *client);
|
||||
};
|
||||
|
||||
struct host1x_client {
|
||||
struct host1x *host1x;
|
||||
struct device *dev;
|
||||
|
||||
const struct host1x_client_ops *ops;
|
||||
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
extern int host1x_drm_init(struct host1x *host1x, struct drm_device *drm);
|
||||
extern int host1x_drm_exit(struct host1x *host1x);
|
||||
|
||||
extern int host1x_register_client(struct host1x *host1x,
|
||||
struct host1x_client *client);
|
||||
extern int host1x_unregister_client(struct host1x *host1x,
|
||||
struct host1x_client *client);
|
||||
|
||||
struct tegra_output;
|
||||
|
||||
struct tegra_dc {
|
||||
struct host1x_client client;
|
||||
|
||||
struct host1x *host1x;
|
||||
struct device *dev;
|
||||
|
||||
struct drm_crtc base;
|
||||
int pipe;
|
||||
|
||||
struct clk *clk;
|
||||
|
||||
void __iomem *regs;
|
||||
int irq;
|
||||
|
||||
struct tegra_output *rgb;
|
||||
|
||||
struct list_head list;
|
||||
|
||||
struct drm_info_list *debugfs_files;
|
||||
struct drm_minor *minor;
|
||||
struct dentry *debugfs;
|
||||
};
|
||||
|
||||
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 container_of(crtc, struct tegra_dc, base);
|
||||
}
|
||||
|
||||
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_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);
|
||||
int (*check_mode)(struct tegra_output *output,
|
||||
struct drm_display_mode *mode,
|
||||
enum drm_mode_status *status);
|
||||
};
|
||||
|
||||
enum tegra_output_type {
|
||||
TEGRA_OUTPUT_RGB,
|
||||
};
|
||||
|
||||
struct tegra_output {
|
||||
struct device_node *of_node;
|
||||
struct device *dev;
|
||||
|
||||
const struct tegra_output_ops *ops;
|
||||
enum tegra_output_type type;
|
||||
|
||||
struct i2c_adapter *ddc;
|
||||
const struct edid *edid;
|
||||
unsigned int hpd_irq;
|
||||
int hpd_gpio;
|
||||
|
||||
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)
|
||||
{
|
||||
if (output && output->ops && output->ops->setup_clock)
|
||||
return output->ops->setup_clock(output, clk, pclk);
|
||||
|
||||
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 rgb.c */
|
||||
extern int tegra_dc_rgb_probe(struct tegra_dc *dc);
|
||||
extern int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc);
|
||||
extern int tegra_dc_rgb_exit(struct tegra_dc *dc);
|
||||
|
||||
/* from output.c */
|
||||
extern int tegra_output_parse_dt(struct tegra_output *output);
|
||||
extern int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
|
||||
extern int tegra_output_exit(struct tegra_output *output);
|
||||
|
||||
/* from gem.c */
|
||||
extern struct tegra_gem_object *tegra_gem_alloc(struct drm_device *drm,
|
||||
size_t size);
|
||||
extern int tegra_gem_handle_create(struct drm_device *drm,
|
||||
struct drm_file *file, size_t size,
|
||||
unsigned long flags, uint32_t *handle);
|
||||
extern int tegra_gem_dumb_create(struct drm_file *file, struct drm_device *drm,
|
||||
struct drm_mode_create_dumb *args);
|
||||
extern int tegra_gem_dumb_map_offset(struct drm_file *file,
|
||||
struct drm_device *drm, uint32_t handle,
|
||||
uint64_t *offset);
|
||||
extern int tegra_gem_dumb_destroy(struct drm_file *file,
|
||||
struct drm_device *drm, uint32_t handle);
|
||||
extern int tegra_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
|
||||
extern int tegra_gem_init_object(struct drm_gem_object *obj);
|
||||
extern void tegra_gem_free_object(struct drm_gem_object *obj);
|
||||
extern struct vm_operations_struct tegra_gem_vm_ops;
|
||||
|
||||
/* from fb.c */
|
||||
extern int tegra_drm_fb_init(struct drm_device *drm);
|
||||
extern void tegra_drm_fb_exit(struct drm_device *drm);
|
||||
|
||||
extern struct platform_driver tegra_host1x_driver;
|
||||
extern struct platform_driver tegra_dc_driver;
|
||||
extern struct drm_driver tegra_drm_driver;
|
||||
|
||||
#endif /* TEGRA_DRM_H */
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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 "drm.h"
|
||||
|
||||
static void tegra_drm_fb_output_poll_changed(struct drm_device *drm)
|
||||
{
|
||||
struct host1x *host1x = drm->dev_private;
|
||||
|
||||
drm_fbdev_cma_hotplug_event(host1x->fbdev);
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
|
||||
.fb_create = drm_fb_cma_create,
|
||||
.output_poll_changed = tegra_drm_fb_output_poll_changed,
|
||||
};
|
||||
|
||||
int tegra_drm_fb_init(struct drm_device *drm)
|
||||
{
|
||||
struct host1x *host1x = drm->dev_private;
|
||||
struct drm_fbdev_cma *fbdev;
|
||||
|
||||
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;
|
||||
|
||||
fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_crtc,
|
||||
drm->mode_config.num_connector);
|
||||
if (IS_ERR(fbdev))
|
||||
return PTR_ERR(fbdev);
|
||||
|
||||
#ifndef CONFIG_FRAMEBUFFER_CONSOLE
|
||||
drm_fbdev_cma_restore_mode(fbdev);
|
||||
#endif
|
||||
|
||||
host1x->fbdev = fbdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tegra_drm_fb_exit(struct drm_device *drm)
|
||||
{
|
||||
struct host1x *host1x = drm->dev_private;
|
||||
|
||||
drm_fbdev_cma_fini(host1x->fbdev);
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* 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 <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "drm.h"
|
||||
|
||||
struct host1x_drm_client {
|
||||
struct host1x_client *client;
|
||||
struct device_node *np;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static int host1x_add_drm_client(struct host1x *host1x, struct device_node *np)
|
||||
{
|
||||
struct host1x_drm_client *client;
|
||||
|
||||
client = kzalloc(sizeof(*client), GFP_KERNEL);
|
||||
if (!client)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&client->list);
|
||||
client->np = of_node_get(np);
|
||||
|
||||
list_add_tail(&client->list, &host1x->drm_clients);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int host1x_activate_drm_client(struct host1x *host1x,
|
||||
struct host1x_drm_client *drm,
|
||||
struct host1x_client *client)
|
||||
{
|
||||
mutex_lock(&host1x->drm_clients_lock);
|
||||
list_del_init(&drm->list);
|
||||
list_add_tail(&drm->list, &host1x->drm_active);
|
||||
drm->client = client;
|
||||
mutex_unlock(&host1x->drm_clients_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int host1x_remove_drm_client(struct host1x *host1x,
|
||||
struct host1x_drm_client *client)
|
||||
{
|
||||
mutex_lock(&host1x->drm_clients_lock);
|
||||
list_del_init(&client->list);
|
||||
mutex_unlock(&host1x->drm_clients_lock);
|
||||
|
||||
of_node_put(client->np);
|
||||
kfree(client);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int host1x_parse_dt(struct host1x *host1x)
|
||||
{
|
||||
static const char * const compat[] = {
|
||||
"nvidia,tegra20-dc",
|
||||
};
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(compat); i++) {
|
||||
struct device_node *np;
|
||||
|
||||
for_each_child_of_node(host1x->dev->of_node, np) {
|
||||
if (of_device_is_compatible(np, compat[i]) &&
|
||||
of_device_is_available(np)) {
|
||||
err = host1x_add_drm_client(host1x, np);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_host1x_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct host1x *host1x;
|
||||
struct resource *regs;
|
||||
int err;
|
||||
|
||||
host1x = devm_kzalloc(&pdev->dev, sizeof(*host1x), GFP_KERNEL);
|
||||
if (!host1x)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&host1x->drm_clients_lock);
|
||||
INIT_LIST_HEAD(&host1x->drm_clients);
|
||||
INIT_LIST_HEAD(&host1x->drm_active);
|
||||
mutex_init(&host1x->clients_lock);
|
||||
INIT_LIST_HEAD(&host1x->clients);
|
||||
host1x->dev = &pdev->dev;
|
||||
|
||||
err = host1x_parse_dt(host1x);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "failed to parse DT: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
host1x->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(host1x->clk))
|
||||
return PTR_ERR(host1x->clk);
|
||||
|
||||
err = clk_prepare_enable(host1x->clk);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!regs) {
|
||||
err = -ENXIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = platform_get_irq(pdev, 0);
|
||||
if (err < 0)
|
||||
goto err;
|
||||
|
||||
host1x->syncpt = err;
|
||||
|
||||
err = platform_get_irq(pdev, 1);
|
||||
if (err < 0)
|
||||
goto err;
|
||||
|
||||
host1x->irq = err;
|
||||
|
||||
host1x->regs = devm_request_and_ioremap(&pdev->dev, regs);
|
||||
if (!host1x->regs) {
|
||||
err = -EADDRNOTAVAIL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, host1x);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
clk_disable_unprepare(host1x->clk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tegra_host1x_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct host1x *host1x = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(host1x->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int host1x_drm_init(struct host1x *host1x, struct drm_device *drm)
|
||||
{
|
||||
struct host1x_client *client;
|
||||
|
||||
mutex_lock(&host1x->clients_lock);
|
||||
|
||||
list_for_each_entry(client, &host1x->clients, list) {
|
||||
if (client->ops && client->ops->drm_init) {
|
||||
int err = client->ops->drm_init(client, drm);
|
||||
if (err < 0) {
|
||||
dev_err(host1x->dev,
|
||||
"DRM setup failed for %s: %d\n",
|
||||
dev_name(client->dev), err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&host1x->clients_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int host1x_drm_exit(struct host1x *host1x)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(host1x->dev);
|
||||
struct host1x_client *client;
|
||||
|
||||
if (!host1x->drm)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&host1x->clients_lock);
|
||||
|
||||
list_for_each_entry_reverse(client, &host1x->clients, list) {
|
||||
if (client->ops && client->ops->drm_exit) {
|
||||
int err = client->ops->drm_exit(client);
|
||||
if (err < 0) {
|
||||
dev_err(host1x->dev,
|
||||
"DRM cleanup failed for %s: %d\n",
|
||||
dev_name(client->dev), err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&host1x->clients_lock);
|
||||
|
||||
drm_platform_exit(&tegra_drm_driver, pdev);
|
||||
host1x->drm = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int host1x_register_client(struct host1x *host1x, struct host1x_client *client)
|
||||
{
|
||||
struct host1x_drm_client *drm, *tmp;
|
||||
int err;
|
||||
|
||||
mutex_lock(&host1x->clients_lock);
|
||||
list_add_tail(&client->list, &host1x->clients);
|
||||
mutex_unlock(&host1x->clients_lock);
|
||||
|
||||
list_for_each_entry_safe(drm, tmp, &host1x->drm_clients, list)
|
||||
if (drm->np == client->dev->of_node)
|
||||
host1x_activate_drm_client(host1x, drm, client);
|
||||
|
||||
if (list_empty(&host1x->drm_clients)) {
|
||||
struct platform_device *pdev = to_platform_device(host1x->dev);
|
||||
|
||||
err = drm_platform_init(&tegra_drm_driver, pdev);
|
||||
if (err < 0) {
|
||||
dev_err(host1x->dev, "drm_platform_init(): %d\n", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int host1x_unregister_client(struct host1x *host1x,
|
||||
struct host1x_client *client)
|
||||
{
|
||||
struct host1x_drm_client *drm, *tmp;
|
||||
int err;
|
||||
|
||||
list_for_each_entry_safe(drm, tmp, &host1x->drm_active, list) {
|
||||
if (drm->client == client) {
|
||||
err = host1x_drm_exit(host1x);
|
||||
if (err < 0) {
|
||||
dev_err(host1x->dev, "host1x_drm_exit(): %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
host1x_remove_drm_client(host1x, drm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_lock(&host1x->clients_lock);
|
||||
list_del_init(&client->list);
|
||||
mutex_unlock(&host1x->clients_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id tegra_host1x_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra20-host1x", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tegra_host1x_of_match);
|
||||
|
||||
struct platform_driver tegra_host1x_driver = {
|
||||
.driver = {
|
||||
.name = "tegra-host1x",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = tegra_host1x_of_match,
|
||||
},
|
||||
.probe = tegra_host1x_probe,
|
||||
.remove = tegra_host1x_remove,
|
||||
};
|
||||
|
||||
static int __init tegra_host1x_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = platform_driver_register(&tegra_host1x_driver);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = platform_driver_register(&tegra_dc_driver);
|
||||
if (err < 0)
|
||||
goto unregister_host1x;
|
||||
|
||||
return 0;
|
||||
|
||||
unregister_host1x:
|
||||
platform_driver_unregister(&tegra_host1x_driver);
|
||||
return err;
|
||||
}
|
||||
module_init(tegra_host1x_init);
|
||||
|
||||
static void __exit tegra_host1x_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&tegra_dc_driver);
|
||||
platform_driver_unregister(&tegra_host1x_driver);
|
||||
}
|
||||
module_exit(tegra_host1x_exit);
|
||||
|
||||
MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
|
||||
MODULE_DESCRIPTION("NVIDIA Tegra DRM driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* 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/module.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_i2c.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);
|
||||
|
||||
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 (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 (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
|
||||
status = connector_status_connected;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void tegra_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.detect = tegra_connector_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = tegra_connector_destroy,
|
||||
};
|
||||
|
||||
static void tegra_encoder_destroy(struct drm_encoder *encoder)
|
||||
{
|
||||
drm_encoder_cleanup(encoder);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_funcs encoder_funcs = {
|
||||
.destroy = tegra_encoder_destroy,
|
||||
};
|
||||
|
||||
static void tegra_encoder_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
static void tegra_encoder_commit(struct drm_encoder *encoder)
|
||||
{
|
||||
}
|
||||
|
||||
static void tegra_encoder_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted)
|
||||
{
|
||||
struct tegra_output *output = encoder_to_output(encoder);
|
||||
int err;
|
||||
|
||||
err = tegra_output_enable(output);
|
||||
if (err < 0)
|
||||
dev_err(encoder->dev->dev, "tegra_output_enable(): %d\n", err);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
|
||||
.dpms = tegra_encoder_dpms,
|
||||
.mode_fixup = tegra_encoder_mode_fixup,
|
||||
.prepare = tegra_encoder_prepare,
|
||||
.commit = tegra_encoder_commit,
|
||||
.mode_set = tegra_encoder_mode_set,
|
||||
};
|
||||
|
||||
static irqreturn_t hpd_irq(int irq, void *data)
|
||||
{
|
||||
struct tegra_output *output = data;
|
||||
|
||||
drm_helper_hpd_irq_event(output->connector.dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int tegra_output_parse_dt(struct tegra_output *output)
|
||||
{
|
||||
enum of_gpio_flags flags;
|
||||
struct device_node *ddc;
|
||||
size_t size;
|
||||
int err;
|
||||
|
||||
if (!output->of_node)
|
||||
output->of_node = output->dev->of_node;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (!output->edid && !output->ddc)
|
||||
return -ENODEV;
|
||||
|
||||
output->hpd_gpio = of_get_named_gpio_flags(output->of_node,
|
||||
"nvidia,hpd-gpio", 0,
|
||||
&flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
|
||||
{
|
||||
int connector, encoder, err;
|
||||
|
||||
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);
|
||||
goto free_hpd;
|
||||
}
|
||||
|
||||
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);
|
||||
goto free_hpd;
|
||||
}
|
||||
|
||||
output->connector.polled = DRM_CONNECTOR_POLL_HPD;
|
||||
}
|
||||
|
||||
switch (output->type) {
|
||||
case TEGRA_OUTPUT_RGB:
|
||||
connector = DRM_MODE_CONNECTOR_LVDS;
|
||||
encoder = DRM_MODE_ENCODER_LVDS;
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
output->encoder.possible_crtcs = 0x3;
|
||||
|
||||
return 0;
|
||||
|
||||
free_hpd:
|
||||
gpio_free(output->hpd_gpio);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int tegra_output_exit(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;
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* 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 <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "drm.h"
|
||||
#include "dc.h"
|
||||
|
||||
struct tegra_rgb {
|
||||
struct tegra_output output;
|
||||
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_dc *dc = to_tegra_dc(output->encoder.crtc);
|
||||
|
||||
tegra_dc_write_regs(dc, rgb_enable, ARRAY_SIZE(rgb_enable));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_output_rgb_disable(struct tegra_output *output)
|
||||
{
|
||||
struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
|
||||
|
||||
tegra_dc_write_regs(dc, rgb_disable, ARRAY_SIZE(rgb_disable));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_output_rgb_setup_clock(struct tegra_output *output,
|
||||
struct clk *clk, unsigned long pclk)
|
||||
{
|
||||
struct tegra_rgb *rgb = to_rgb(output);
|
||||
|
||||
return clk_set_parent(clk, rgb->clk_parent);
|
||||
}
|
||||
|
||||
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->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;
|
||||
}
|
||||
|
||||
rgb->output.dev = dc->dev;
|
||||
rgb->output.of_node = np;
|
||||
|
||||
err = tegra_output_parse_dt(&rgb->output);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
dc->rgb = &rgb->output;
|
||||
|
||||
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 = 1 << dc->pipe;
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user