Merge remote-tracking branch 'airlied/drm-next' into HEAD

Backmerge drm-next after the big s/crtc->fb/crtc->primary->fb/
cocinelle patch to avoid endless amounts of conflict hilarity in my
-next queue for 3.16.

Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
This commit is contained in:
Daniel Vetter 2014-04-09 14:33:47 +02:00
commit 8cbf320209
376 changed files with 22318 additions and 9199 deletions

View File

@ -1194,7 +1194,7 @@ int max_width, max_height;</synopsis>
pointer to CRTC functions.
</para>
</sect3>
<sect3>
<sect3 id="drm-kms-crtcops">
<title>CRTC Operations</title>
<sect4>
<title>Set Configuration</title>
@ -1335,15 +1335,47 @@ int max_width, max_height;</synopsis>
optionally scale it to a destination size. The result is then blended
with or overlayed on top of a CRTC.
</para>
<para>
The DRM core recognizes three types of planes:
<itemizedlist>
<listitem>
DRM_PLANE_TYPE_PRIMARY represents a "main" plane for a CRTC. Primary
planes are the planes operated upon by by CRTC modesetting and flipping
operations described in <xref linkend="drm-kms-crtcops"/>.
</listitem>
<listitem>
DRM_PLANE_TYPE_CURSOR represents a "cursor" plane for a CRTC. Cursor
planes are the planes operated upon by the DRM_IOCTL_MODE_CURSOR and
DRM_IOCTL_MODE_CURSOR2 ioctls.
</listitem>
<listitem>
DRM_PLANE_TYPE_OVERLAY represents all non-primary, non-cursor planes.
Some drivers refer to these types of planes as "sprites" internally.
</listitem>
</itemizedlist>
For compatibility with legacy userspace, only overlay planes are made
available to userspace by default. Userspace clients may set the
DRM_CLIENT_CAP_UNIVERSAL_PLANES client capability bit to indicate that
they wish to receive a universal plane list containing all plane types.
</para>
<sect3>
<title>Plane Initialization</title>
<para>
Planes are optional. To create a plane, a KMS drivers allocates and
To create a plane, a KMS drivers allocates and
zeroes an instances of struct <structname>drm_plane</structname>
(possibly as part of a larger structure) and registers it with a call
to <function>drm_plane_init</function>. The function takes a bitmask
to <function>drm_universal_plane_init</function>. The function takes a bitmask
of the CRTCs that can be associated with the plane, a pointer to the
plane functions and a list of format supported formats.
plane functions, a list of format supported formats, and the type of
plane (primary, cursor, or overlay) being initialized.
</para>
<para>
Cursor and overlay planes are optional. All drivers should provide
one primary plane per CRTC (although this requirement may change in
the future); drivers that do not wish to provide special handling for
primary planes may make use of the helper functions described in
<xref linkend="drm-kms-planehelpers"/> to create and register a
primary plane with standard capabilities.
</para>
</sect3>
<sect3>
@ -1774,7 +1806,7 @@ void intel_crt_init(struct drm_device *dev)
<sect1>
<title>Mode Setting Helper Functions</title>
<para>
The CRTC, encoder and connector functions provided by the drivers
The plane, CRTC, encoder and connector functions provided by the drivers
implement the DRM API. They're called by the DRM core and ioctl handlers
to handle device state changes and configuration request. As implementing
those functions often requires logic not specific to drivers, mid-layer
@ -1782,8 +1814,8 @@ void intel_crt_init(struct drm_device *dev)
</para>
<para>
The DRM core contains one mid-layer implementation. The mid-layer provides
implementations of several CRTC, encoder and connector functions (called
from the top of the mid-layer) that pre-process requests and call
implementations of several plane, CRTC, encoder and connector functions
(called from the top of the mid-layer) that pre-process requests and call
lower-level functions provided by the driver (at the bottom of the
mid-layer). For instance, the
<function>drm_crtc_helper_set_config</function> function can be used to
@ -2293,6 +2325,10 @@ void intel_crt_init(struct drm_device *dev)
!Iinclude/linux/hdmi.h
!Edrivers/video/hdmi.c
</sect2>
<sect2>
<title id="drm-kms-planehelpers">Plane Helper Reference</title>
!Edrivers/gpu/drm/drm_plane_helper.c Plane Helpers
</sect2>
</sect1>
<!-- Internals: kms properties -->

View File

@ -0,0 +1,27 @@
ptn3460 bridge bindings
Required properties:
- compatible: "nxp,ptn3460"
- reg: i2c address of the bridge
- powerdown-gpio: OF device-tree gpio specification
- reset-gpio: OF device-tree gpio specification
- edid-emulation: The EDID emulation entry to use
+-------+------------+------------------+
| Value | Resolution | Description |
| 0 | 1024x768 | NXP Generic |
| 1 | 1920x1080 | NXP Generic |
| 2 | 1920x1080 | NXP Generic |
| 3 | 1600x900 | Samsung LTM200KT |
| 4 | 1920x1080 | Samsung LTM230HT |
| 5 | 1366x768 | NXP Generic |
| 6 | 1600x900 | ChiMei M215HGE |
+-------+------------+------------------+
Example:
lvds-bridge@20 {
compatible = "nxp,ptn3460";
reg = <0x20>;
powerdown-gpio = <&gpy2 5 1 0 0>;
reset-gpio = <&gpx1 5 1 0 0>;
edid-emulation = <5>;
};

View File

@ -190,6 +190,48 @@ of the following host1x client modules:
- nvidia,edid: supplies a binary EDID blob
- nvidia,panel: phandle of a display panel
- sor: serial output resource
Required properties:
- compatible: "nvidia,tegra124-sor"
- reg: Physical base address and length of the controller's registers.
- interrupts: The interrupt outputs from the controller.
- clocks: Must contain an entry for each entry in clock-names.
See ../clocks/clock-bindings.txt for details.
- clock-names: Must include the following entries:
- sor: clock input for the SOR hardware
- parent: input for the pixel clock
- dp: reference clock for the SOR clock
- safe: safe reference for the SOR clock during power up
- resets: Must contain an entry for each entry in reset-names.
See ../reset/reset.txt for details.
- reset-names: Must include the following entries:
- sor
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
- nvidia,panel: phandle of a display panel
Optional properties when driving an eDP output:
- nvidia,dpaux: phandle to a DispayPort AUX interface
- dpaux: DisplayPort AUX interface
- compatible: "nvidia,tegra124-dpaux"
- reg: Physical base address and length of the controller's registers.
- interrupts: The interrupt outputs from the controller.
- clocks: Must contain an entry for each entry in clock-names.
See ../clocks/clock-bindings.txt for details.
- clock-names: Must include the following entries:
- dpaux: clock input for the DPAUX hardware
- parent: reference clock
- resets: Must contain an entry for each entry in reset-names.
See ../reset/reset.txt for details.
- reset-names: Must include the following entries:
- dpaux
- vdd-supply: phandle of a supply that powers the DisplayPort link
Example:
/ {

View File

@ -0,0 +1,7 @@
LG Corporation 7" WXGA TFT LCD panel
Required properties:
- compatible: should be "lg,ld070wx3-sl01"
This binding is compatible with the simple-panel binding, which is specified
in simple-panel.txt in this directory.

View File

@ -0,0 +1,7 @@
LG Corporation 5" HD TFT LCD panel
Required properties:
- compatible: should be "lg,lh500wx1-sd03"
This binding is compatible with the simple-panel binding, which is specified
in simple-panel.txt in this directory.

View File

@ -0,0 +1,7 @@
LG 12.9" (2560x1700 pixels) TFT LCD panel
Required properties:
- compatible: should be "lg,lp129qe"
This binding is compatible with the simple-panel binding, which is specified
in simple-panel.txt in this directory.

View File

@ -0,0 +1,66 @@
Samsung LD9040 AMOLED LCD parallel RGB panel with SPI control bus
Required properties:
- compatible: "samsung,ld9040"
- reg: address of the panel on SPI bus
- vdd3-supply: core voltage supply
- vci-supply: voltage supply for analog circuits
- reset-gpios: a GPIO spec for the reset pin
- display-timings: timings for the connected panel according to [1]
The panel must obey rules for SPI slave device specified in document [2].
Optional properties:
- power-on-delay: delay after turning regulators on [ms]
- reset-delay: delay after reset sequence [ms]
- panel-width-mm: physical panel width [mm]
- panel-height-mm: physical panel height [mm]
The device node can contain one 'port' child node with one child
'endpoint' node, according to the bindings defined in [3]. This
node should describe panel's video bus.
[1]: Documentation/devicetree/bindings/video/display-timing.txt
[2]: Documentation/devicetree/bindings/spi/spi-bus.txt
[3]: Documentation/devicetree/bindings/media/video-interfaces.txt
Example:
lcd@0 {
compatible = "samsung,ld9040";
reg = <0>;
vdd3-supply = <&ldo7_reg>;
vci-supply = <&ldo17_reg>;
reset-gpios = <&gpy4 5 0>;
spi-max-frequency = <1200000>;
spi-cpol;
spi-cpha;
power-on-delay = <10>;
reset-delay = <10>;
panel-width-mm = <90>;
panel-height-mm = <154>;
display-timings {
timing {
clock-frequency = <23492370>;
hactive = <480>;
vactive = <800>;
hback-porch = <16>;
hfront-porch = <16>;
vback-porch = <2>;
vfront-porch = <28>;
hsync-len = <2>;
vsync-len = <1>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <0>;
pixelclk-active = <0>;
};
};
port {
lcd_ep: endpoint {
remote-endpoint = <&fimd_dpi_ep>;
};
};
};

View File

@ -0,0 +1,56 @@
Samsung S6E8AA0 AMOLED LCD 5.3 inch panel
Required properties:
- compatible: "samsung,s6e8aa0"
- reg: the virtual channel number of a DSI peripheral
- vdd3-supply: core voltage supply
- vci-supply: voltage supply for analog circuits
- reset-gpios: a GPIO spec for the reset pin
- display-timings: timings for the connected panel as described by [1]
Optional properties:
- power-on-delay: delay after turning regulators on [ms]
- reset-delay: delay after reset sequence [ms]
- init-delay: delay after initialization sequence [ms]
- panel-width-mm: physical panel width [mm]
- panel-height-mm: physical panel height [mm]
- flip-horizontal: boolean to flip image horizontally
- flip-vertical: boolean to flip image vertically
The device node can contain one 'port' child node with one child
'endpoint' node, according to the bindings defined in [2]. This
node should describe panel's video bus.
[1]: Documentation/devicetree/bindings/video/display-timing.txt
[2]: Documentation/devicetree/bindings/media/video-interfaces.txt
Example:
panel {
compatible = "samsung,s6e8aa0";
reg = <0>;
vdd3-supply = <&vcclcd_reg>;
vci-supply = <&vlcd_reg>;
reset-gpios = <&gpy4 5 0>;
power-on-delay= <50>;
reset-delay = <100>;
init-delay = <100>;
panel-width-mm = <58>;
panel-height-mm = <103>;
flip-horizontal;
flip-vertical;
display-timings {
timing0: timing-0 {
clock-frequency = <57153600>;
hactive = <720>;
vactive = <1280>;
hfront-porch = <5>;
hback-porch = <5>;
hsync-len = <5>;
vfront-porch = <13>;
vback-porch = <1>;
vsync-len = <2>;
};
};
};

View File

@ -49,6 +49,8 @@ Required properties for dp-controller:
-samsung,lane-count:
number of lanes supported by the panel.
LANE_COUNT1 = 1, LANE_COUNT2 = 2, LANE_COUNT4 = 4
- display-timings: timings for the connected panel as described by
Documentation/devicetree/bindings/video/display-timing.txt
Optional properties for dp-controller:
-interlaced:
@ -84,4 +86,19 @@ Board Specific portion:
samsung,color-depth = <1>;
samsung,link-rate = <0x0a>;
samsung,lane-count = <4>;
display-timings {
native-mode = <&lcd_timing>;
lcd_timing: 1366x768 {
clock-frequency = <70589280>;
hactive = <1366>;
vactive = <768>;
hfront-porch = <40>;
hback-porch = <40>;
hsync-len = <32>;
vback-porch = <10>;
vfront-porch = <12>;
vsync-len = <6>;
};
};
};

View File

@ -0,0 +1,80 @@
Exynos MIPI DSI Master
Required properties:
- compatible: "samsung,exynos4210-mipi-dsi"
- reg: physical base address and length of the registers set for the device
- interrupts: should contain DSI interrupt
- clocks: list of clock specifiers, must contain an entry for each required
entry in clock-names
- clock-names: should include "bus_clk"and "pll_clk" entries
- phys: list of phy specifiers, must contain an entry for each required
entry in phy-names
- phy-names: should include "dsim" entry
- vddcore-supply: MIPI DSIM Core voltage supply (e.g. 1.1V)
- vddio-supply: MIPI DSIM I/O and PLL voltage supply (e.g. 1.8V)
- samsung,pll-clock-frequency: specifies frequency of the "pll_clk" clock
- #address-cells, #size-cells: should be set respectively to <1> and <0>
according to DSI host bindings (see MIPI DSI bindings [1])
Optional properties:
- samsung,power-domain: a phandle to DSIM power domain node
Child nodes:
Should contain DSI peripheral nodes (see MIPI DSI bindings [1]).
Video interfaces:
Device node can contain video interface port nodes according to [2].
The following are properties specific to those nodes:
port node:
- reg: (required) can be 0 for input RGB/I80 port or 1 for DSI port;
endpoint node of DSI port (reg = 1):
- samsung,burst-clock-frequency: specifies DSI frequency in high-speed burst
mode
- samsung,esc-clock-frequency: specifies DSI frequency in escape mode
[1]: Documentation/devicetree/bindings/mipi/dsi/mipi-dsi-bus.txt
[2]: Documentation/devicetree/bindings/media/video-interfaces.txt
Example:
dsi@11C80000 {
compatible = "samsung,exynos4210-mipi-dsi";
reg = <0x11C80000 0x10000>;
interrupts = <0 79 0>;
clocks = <&clock 286>, <&clock 143>;
clock-names = "bus_clk", "pll_clk";
phys = <&mipi_phy 1>;
phy-names = "dsim";
vddcore-supply = <&vusb_reg>;
vddio-supply = <&vmipi_reg>;
samsung,power-domain = <&pd_lcd0>;
#address-cells = <1>;
#size-cells = <0>;
samsung,pll-clock-frequency = <24000000>;
panel@1 {
reg = <0>;
...
port {
panel_ep: endpoint {
remote-endpoint = <&dsi_ep>;
};
};
};
ports {
#address-cells = <1>;
#size-cells = <0>;
port@1 {
dsi_ep: endpoint {
reg = <0>;
samsung,burst-clock-frequency = <500000000>;
samsung,esc-clock-frequency = <20000000>;
remote-endpoint = <&panel_ep>;
};
};
};
};

View File

@ -25,6 +25,9 @@ Required properties:
sclk_pixel.
- clock-names: aliases as per driver requirements for above clock IDs:
"hdmi", "sclk_hdmi", "sclk_pixel", "sclk_hdmiphy" and "mout_hdmi".
- ddc: phandle to the hdmi ddc node
- phy: phandle to the hdmi phy node
Example:
hdmi {
@ -32,4 +35,6 @@ Example:
reg = <0x14530000 0x100000>;
interrupts = <0 95 0>;
hpd-gpio = <&gpx3 7 1>;
ddc = <&hdmi_ddc_node>;
phy = <&hdmi_phy_node>;
};

View File

@ -39,6 +39,23 @@ Required properties:
Optional Properties:
- samsung,power-domain: a phandle to FIMD power domain node.
- samsung,invert-vden: video enable signal is inverted
- samsung,invert-vclk: video clock signal is inverted
- display-timings: timing settings for FIMD, as described in document [1].
Can be used in case timings cannot be provided otherwise
or to override timings provided by the panel.
The device node can contain 'port' child nodes according to the bindings defined
in [2]. The following are properties specific to those nodes:
- reg: (required) port index, can be:
0 - for CAMIF0 input,
1 - for CAMIF1 input,
2 - for CAMIF2 input,
3 - for parallel output,
4 - for write-back interface
[1]: Documentation/devicetree/bindings/video/display-timing.txt
[2]: Documentation/devicetree/bindings/media/video-interfaces.txt
Example:

View File

@ -2866,6 +2866,16 @@ F: drivers/gpu/drm/radeon/
F: include/drm/radeon*
F: include/uapi/drm/radeon*
DRM PANEL DRIVERS
M: Thierry Reding <thierry.reding@gmail.com>
L: dri-devel@lists.freedesktop.org
T: git git://anongit.freedesktop.org/tegra/linux.git
S: Maintained
F: drivers/gpu/drm/drm_panel.c
F: drivers/gpu/drm/panel/
F: include/drm/drm_panel.h
F: Documentation/devicetree/bindings/panel/
INTEL DRM DRIVERS (excluding Poulsbo, Moorestown and derivative chipsets)
M: Daniel Vetter <daniel.vetter@ffwll.ch>
M: Jani Nikula <jani.nikula@linux.intel.com>
@ -3393,12 +3403,6 @@ S: Maintained
F: drivers/extcon/
F: Documentation/extcon/
EXYNOS DP DRIVER
M: Jingoo Han <jg1.han@samsung.com>
L: linux-fbdev@vger.kernel.org
S: Maintained
F: drivers/video/exynos/exynos_dp*
EXYNOS MIPI DISPLAY DRIVERS
M: Inki Dae <inki.dae@samsung.com>
M: Donghwa Lee <dh09.lee@samsung.com>

View File

@ -104,6 +104,20 @@
reg = <0x10010000 0x400>;
};
dsi_0: dsi@11C80000 {
compatible = "samsung,exynos4210-mipi-dsi";
reg = <0x11C80000 0x10000>;
interrupts = <0 79 0>;
samsung,power-domain = <&pd_lcd0>;
phys = <&mipi_phy 1>;
phy-names = "dsim";
clocks = <&clock 286>, <&clock 143>;
clock-names = "bus_clk", "pll_clk";
status = "disabled";
#address-cells = <1>;
#size-cells = <0>;
};
camera {
compatible = "samsung,fimc", "simple-bus";
status = "disabled";

View File

@ -353,6 +353,67 @@
};
};
dsi_0: dsi@11C80000 {
vddcore-supply = <&vusb_reg>;
vddio-supply = <&vmipi_reg>;
samsung,pll-clock-frequency = <24000000>;
status = "okay";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@1 {
reg = <1>;
dsi_out: endpoint {
remote-endpoint = <&dsi_in>;
samsung,burst-clock-frequency = <500000000>;
samsung,esc-clock-frequency = <20000000>;
};
};
};
panel@0 {
reg = <0>;
compatible = "samsung,s6e8aa0";
vdd3-supply = <&vcclcd_reg>;
vci-supply = <&vlcd_reg>;
reset-gpios = <&gpy4 5 0>;
power-on-delay= <50>;
reset-delay = <100>;
init-delay = <100>;
flip-horizontal;
flip-vertical;
panel-width-mm = <58>;
panel-height-mm = <103>;
display-timings {
timing-0 {
clock-frequency = <57153600>;
hactive = <720>;
vactive = <1280>;
hfront-porch = <5>;
hback-porch = <5>;
hsync-len = <5>;
vfront-porch = <13>;
vback-porch = <1>;
vsync-len = <2>;
};
};
port {
dsi_in: endpoint {
remote-endpoint = <&dsi_out>;
};
};
};
};
fimd@11c00000 {
status = "okay";
};
camera {
pinctrl-names = "default";
pinctrl-0 = <>;

View File

@ -345,6 +345,70 @@
};
};
spi-lcd {
compatible = "spi-gpio";
#address-cells = <1>;
#size-cells = <0>;
gpio-sck = <&gpy3 1 0>;
gpio-mosi = <&gpy3 3 0>;
num-chipselects = <1>;
cs-gpios = <&gpy4 3 0>;
lcd@0 {
compatible = "samsung,ld9040";
reg = <0>;
vdd3-supply = <&ldo7_reg>;
vci-supply = <&ldo17_reg>;
reset-gpios = <&gpy4 5 0>;
spi-max-frequency = <1200000>;
spi-cpol;
spi-cpha;
power-on-delay = <10>;
reset-delay = <10>;
panel-width-mm = <90>;
panel-height-mm = <154>;
display-timings {
timing {
clock-frequency = <23492370>;
hactive = <480>;
vactive = <800>;
hback-porch = <16>;
hfront-porch = <16>;
vback-porch = <2>;
vfront-porch = <28>;
hsync-len = <2>;
vsync-len = <1>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <0>;
pixelclk-active = <0>;
};
};
port {
lcd_ep: endpoint {
remote-endpoint = <&fimd_dpi_ep>;
};
};
};
};
fimd: fimd@11c00000 {
pinctrl-0 = <&lcd_clk>, <&lcd_data24>;
pinctrl-names = "default";
status = "okay";
samsung,invert-vden;
samsung,invert-vclk;
#address-cells = <1>;
#size-cells = <0>;
port@3 {
reg = <3>;
fimd_dpi_ep: endpoint {
remote-endpoint = <&lcd_ep>;
};
};
};
pwm@139D0000 {
compatible = "samsung,s5p6440-pwm";
status = "okay";

View File

@ -71,6 +71,15 @@
enable-active-high;
};
lcd_vdd3_reg: voltage-regulator-2 {
compatible = "regulator-fixed";
regulator-name = "LCD_VDD_2.2V";
regulator-min-microvolt = <2200000>;
regulator-max-microvolt = <2200000>;
gpio = <&gpc0 1 0>;
enable-active-high;
};
/* More to come */
};
@ -511,6 +520,67 @@
};
};
dsi_0: dsi@11C80000 {
vddcore-supply = <&ldo8_reg>;
vddio-supply = <&ldo10_reg>;
samsung,pll-clock-frequency = <24000000>;
status = "okay";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@1 {
reg = <1>;
dsi_out: endpoint {
remote-endpoint = <&dsi_in>;
samsung,burst-clock-frequency = <500000000>;
samsung,esc-clock-frequency = <20000000>;
};
};
};
panel@0 {
compatible = "samsung,s6e8aa0";
reg = <0>;
vdd3-supply = <&lcd_vdd3_reg>;
vci-supply = <&ldo25_reg>;
reset-gpios = <&gpy4 5 0>;
power-on-delay= <50>;
reset-delay = <100>;
init-delay = <100>;
flip-horizontal;
flip-vertical;
panel-width-mm = <58>;
panel-height-mm = <103>;
display-timings {
timing-0 {
clock-frequency = <0>;
hactive = <720>;
vactive = <1280>;
hfront-porch = <5>;
hback-porch = <5>;
hsync-len = <5>;
vfront-porch = <13>;
vback-porch = <1>;
vsync-len = <2>;
};
};
port {
dsi_in: endpoint {
remote-endpoint = <&dsi_out>;
};
};
};
};
fimd@11c00000 {
status = "okay";
};
camera {
pinctrl-0 = <&cam_port_b_clk_active>;
pinctrl-names = "default";

View File

@ -199,3 +199,5 @@ source "drivers/gpu/drm/msm/Kconfig"
source "drivers/gpu/drm/tegra/Kconfig"
source "drivers/gpu/drm/panel/Kconfig"
source "drivers/gpu/drm/bridge/Kconfig"

View File

@ -13,7 +13,8 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
drm_crtc.o drm_modes.o drm_edid.o \
drm_info.o drm_debugfs.o drm_encoder_slave.o \
drm_trace_points.o drm_global.o drm_prime.o \
drm_rect.o drm_vma_manager.o drm_flip_work.o
drm_rect.o drm_vma_manager.o drm_flip_work.o \
drm_plane_helper.o
drm-$(CONFIG_COMPAT) += drm_ioc32.o
drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
@ -63,3 +64,4 @@ obj-$(CONFIG_DRM_MSM) += msm/
obj-$(CONFIG_DRM_TEGRA) += tegra/
obj-y += i2c/
obj-y += panel/
obj-y += bridge/

View File

@ -478,11 +478,12 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
unsigned i;
bool interlaced;
drm_framebuffer_reference(crtc->fb);
drm_framebuffer_reference(crtc->primary->fb);
interlaced = !!(adj->flags & DRM_MODE_FLAG_INTERLACE);
i = armada_drm_crtc_calc_fb(dcrtc->crtc.fb, x, y, regs, interlaced);
i = armada_drm_crtc_calc_fb(dcrtc->crtc.primary->fb,
x, y, regs, interlaced);
rm = adj->crtc_hsync_start - adj->crtc_hdisplay;
lm = adj->crtc_htotal - adj->crtc_hsync_end;
@ -567,10 +568,10 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
}
val = CFG_GRA_ENA | CFG_GRA_HSMOOTH;
val |= CFG_GRA_FMT(drm_fb_to_armada_fb(dcrtc->crtc.fb)->fmt);
val |= CFG_GRA_MOD(drm_fb_to_armada_fb(dcrtc->crtc.fb)->mod);
val |= CFG_GRA_FMT(drm_fb_to_armada_fb(dcrtc->crtc.primary->fb)->fmt);
val |= CFG_GRA_MOD(drm_fb_to_armada_fb(dcrtc->crtc.primary->fb)->mod);
if (drm_fb_to_armada_fb(dcrtc->crtc.fb)->fmt > CFG_420)
if (drm_fb_to_armada_fb(dcrtc->crtc.primary->fb)->fmt > CFG_420)
val |= CFG_PALETTE_ENA;
if (interlaced)
@ -608,7 +609,7 @@ static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
struct armada_regs regs[4];
unsigned i;
i = armada_drm_crtc_calc_fb(crtc->fb, crtc->x, crtc->y, regs,
i = armada_drm_crtc_calc_fb(crtc->primary->fb, crtc->x, crtc->y, regs,
dcrtc->interlaced);
armada_reg_queue_end(regs, i);
@ -616,7 +617,7 @@ static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
wait_event(dcrtc->frame_wait, !dcrtc->frame_work);
/* Take a reference to the new fb as we're using it */
drm_framebuffer_reference(crtc->fb);
drm_framebuffer_reference(crtc->primary->fb);
/* Update the base in the CRTC */
armada_drm_crtc_update_regs(dcrtc, regs);
@ -637,7 +638,7 @@ static void armada_drm_crtc_disable(struct drm_crtc *crtc)
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
armada_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
armada_drm_crtc_finish_fb(dcrtc, crtc->fb, true);
armada_drm_crtc_finish_fb(dcrtc, crtc->primary->fb, true);
/* Power down most RAMs and FIFOs */
writel_relaxed(CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
@ -678,6 +679,7 @@ static void armada_load_cursor_argb(void __iomem *base, uint32_t *pix,
base + LCD_SPU_SRAM_WRDAT);
writel_relaxed(addr | SRAM_WRITE,
base + LCD_SPU_SRAM_CTRL);
readl_relaxed(base + LCD_SPU_HWC_OVSA_HPXL_VLN);
addr += 1;
if ((addr & 0x00ff) == 0)
addr += 0xf00;
@ -904,7 +906,7 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
int ret;
/* We don't support changing the pixel format */
if (fb->pixel_format != crtc->fb->pixel_format)
if (fb->pixel_format != crtc->primary->fb->pixel_format)
return -EINVAL;
work = kmalloc(sizeof(*work), GFP_KERNEL);
@ -912,7 +914,7 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
return -ENOMEM;
work->event = event;
work->old_fb = dcrtc->crtc.fb;
work->old_fb = dcrtc->crtc.primary->fb;
i = armada_drm_crtc_calc_fb(fb, crtc->x, crtc->y, work->regs,
dcrtc->interlaced);
@ -941,7 +943,7 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
* will _not_ drop that reference on successful return from this
* function. Simply mark this new framebuffer as the current one.
*/
dcrtc->crtc.fb = fb;
dcrtc->crtc.primary->fb = fb;
/*
* Finally, if the display is blanked, we won't receive an

View File

@ -81,7 +81,7 @@ static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mo
u32 refresh_rate_index = 0, mode_id, color_index, refresh_rate;
u32 hborder, vborder;
switch (crtc->fb->bits_per_pixel) {
switch (crtc->primary->fb->bits_per_pixel) {
case 8:
vbios_mode->std_table = &vbios_stdtable[VGAModeIndex];
color_index = VGAModeIndex - 1;
@ -176,7 +176,7 @@ static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mo
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x8e, mode_id & 0xff);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x91, 0xa8);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x92, crtc->fb->bits_per_pixel);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x92, crtc->primary->fb->bits_per_pixel);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x93, adjusted_mode->clock / 1000);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x94, adjusted_mode->crtc_hdisplay);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x95, adjusted_mode->crtc_hdisplay >> 8);
@ -340,7 +340,7 @@ static void ast_set_offset_reg(struct drm_crtc *crtc)
u16 offset;
offset = crtc->fb->pitches[0] >> 3;
offset = crtc->primary->fb->pitches[0] >> 3;
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x13, (offset & 0xff));
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xb0, (offset >> 8) & 0x3f);
}
@ -365,7 +365,7 @@ static void ast_set_ext_reg(struct drm_crtc *crtc, struct drm_display_mode *mode
struct ast_private *ast = crtc->dev->dev_private;
u8 jregA0 = 0, jregA3 = 0, jregA8 = 0;
switch (crtc->fb->bits_per_pixel) {
switch (crtc->primary->fb->bits_per_pixel) {
case 8:
jregA0 = 0x70;
jregA3 = 0x01;
@ -418,7 +418,7 @@ static void ast_set_sync_reg(struct drm_device *dev, struct drm_display_mode *mo
static bool ast_set_dac_reg(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct ast_vbios_mode_info *vbios_mode)
{
switch (crtc->fb->bits_per_pixel) {
switch (crtc->primary->fb->bits_per_pixel) {
case 8:
break;
default:
@ -490,7 +490,7 @@ static int ast_crtc_do_set_base(struct drm_crtc *crtc,
ast_bo_unreserve(bo);
}
ast_fb = to_ast_framebuffer(crtc->fb);
ast_fb = to_ast_framebuffer(crtc->primary->fb);
obj = ast_fb->obj;
bo = gem_to_ast_bo(obj);

View File

@ -62,10 +62,10 @@ static int bochs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
}
}
if (WARN_ON(crtc->fb == NULL))
if (WARN_ON(crtc->primary->fb == NULL))
return -EINVAL;
bochs_fb = to_bochs_framebuffer(crtc->fb);
bochs_fb = to_bochs_framebuffer(crtc->primary->fb);
bo = gem_to_bochs_bo(bochs_fb->obj);
ret = ttm_bo_reserve(&bo->bo, true, false, false, 0);
if (ret)

View File

@ -0,0 +1,5 @@
config DRM_PTN3460
tristate "PTN3460 DP/LVDS bridge"
depends on DRM
select DRM_KMS_HELPER
---help---

View File

@ -0,0 +1,3 @@
ccflags-y := -Iinclude/drm
obj-$(CONFIG_DRM_PTN3460) += ptn3460.o

View File

@ -0,0 +1,350 @@
/*
* NXP PTN3460 DP/LVDS bridge driver
*
* Copyright (C) 2013 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include "drmP.h"
#include "drm_edid.h"
#include "drm_crtc.h"
#include "drm_crtc_helper.h"
#include "bridge/ptn3460.h"
#define PTN3460_EDID_ADDR 0x0
#define PTN3460_EDID_EMULATION_ADDR 0x84
#define PTN3460_EDID_ENABLE_EMULATION 0
#define PTN3460_EDID_EMULATION_SELECTION 1
#define PTN3460_EDID_SRAM_LOAD_ADDR 0x85
struct ptn3460_bridge {
struct drm_connector connector;
struct i2c_client *client;
struct drm_encoder *encoder;
struct drm_bridge *bridge;
struct edid *edid;
int gpio_pd_n;
int gpio_rst_n;
u32 edid_emulation;
bool enabled;
};
static int ptn3460_read_bytes(struct ptn3460_bridge *ptn_bridge, char addr,
u8 *buf, int len)
{
int ret;
ret = i2c_master_send(ptn_bridge->client, &addr, 1);
if (ret <= 0) {
DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
return ret;
}
ret = i2c_master_recv(ptn_bridge->client, buf, len);
if (ret <= 0) {
DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret);
return ret;
}
return 0;
}
static int ptn3460_write_byte(struct ptn3460_bridge *ptn_bridge, char addr,
char val)
{
int ret;
char buf[2];
buf[0] = addr;
buf[1] = val;
ret = i2c_master_send(ptn_bridge->client, buf, ARRAY_SIZE(buf));
if (ret <= 0) {
DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
return ret;
}
return 0;
}
static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge)
{
int ret;
char val;
/* Load the selected edid into SRAM (accessed at PTN3460_EDID_ADDR) */
ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_SRAM_LOAD_ADDR,
ptn_bridge->edid_emulation);
if (ret) {
DRM_ERROR("Failed to transfer edid to sram, ret=%d\n", ret);
return ret;
}
/* Enable EDID emulation and select the desired EDID */
val = 1 << PTN3460_EDID_ENABLE_EMULATION |
ptn_bridge->edid_emulation << PTN3460_EDID_EMULATION_SELECTION;
ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_EMULATION_ADDR, val);
if (ret) {
DRM_ERROR("Failed to write edid value, ret=%d\n", ret);
return ret;
}
return 0;
}
static void ptn3460_pre_enable(struct drm_bridge *bridge)
{
struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
int ret;
if (ptn_bridge->enabled)
return;
if (gpio_is_valid(ptn_bridge->gpio_pd_n))
gpio_set_value(ptn_bridge->gpio_pd_n, 1);
if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
gpio_set_value(ptn_bridge->gpio_rst_n, 0);
udelay(10);
gpio_set_value(ptn_bridge->gpio_rst_n, 1);
}
/*
* There's a bug in the PTN chip where it falsely asserts hotplug before
* it is fully functional. We're forced to wait for the maximum start up
* time specified in the chip's datasheet to make sure we're really up.
*/
msleep(90);
ret = ptn3460_select_edid(ptn_bridge);
if (ret)
DRM_ERROR("Select edid failed ret=%d\n", ret);
ptn_bridge->enabled = true;
}
static void ptn3460_enable(struct drm_bridge *bridge)
{
}
static void ptn3460_disable(struct drm_bridge *bridge)
{
struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
if (!ptn_bridge->enabled)
return;
ptn_bridge->enabled = false;
if (gpio_is_valid(ptn_bridge->gpio_rst_n))
gpio_set_value(ptn_bridge->gpio_rst_n, 1);
if (gpio_is_valid(ptn_bridge->gpio_pd_n))
gpio_set_value(ptn_bridge->gpio_pd_n, 0);
}
static void ptn3460_post_disable(struct drm_bridge *bridge)
{
}
void ptn3460_bridge_destroy(struct drm_bridge *bridge)
{
struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
drm_bridge_cleanup(bridge);
if (gpio_is_valid(ptn_bridge->gpio_pd_n))
gpio_free(ptn_bridge->gpio_pd_n);
if (gpio_is_valid(ptn_bridge->gpio_rst_n))
gpio_free(ptn_bridge->gpio_rst_n);
/* Nothing else to free, we've got devm allocated memory */
}
struct drm_bridge_funcs ptn3460_bridge_funcs = {
.pre_enable = ptn3460_pre_enable,
.enable = ptn3460_enable,
.disable = ptn3460_disable,
.post_disable = ptn3460_post_disable,
.destroy = ptn3460_bridge_destroy,
};
int ptn3460_get_modes(struct drm_connector *connector)
{
struct ptn3460_bridge *ptn_bridge;
u8 *edid;
int ret, num_modes;
bool power_off;
ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
if (ptn_bridge->edid)
return drm_add_edid_modes(connector, ptn_bridge->edid);
power_off = !ptn_bridge->enabled;
ptn3460_pre_enable(ptn_bridge->bridge);
edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
if (!edid) {
DRM_ERROR("Failed to allocate edid\n");
return 0;
}
ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid,
EDID_LENGTH);
if (ret) {
kfree(edid);
num_modes = 0;
goto out;
}
ptn_bridge->edid = (struct edid *)edid;
drm_mode_connector_update_edid_property(connector, ptn_bridge->edid);
num_modes = drm_add_edid_modes(connector, ptn_bridge->edid);
out:
if (power_off)
ptn3460_disable(ptn_bridge->bridge);
return num_modes;
}
static int ptn3460_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
return MODE_OK;
}
struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector)
{
struct ptn3460_bridge *ptn_bridge;
ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
return ptn_bridge->encoder;
}
struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = {
.get_modes = ptn3460_get_modes,
.mode_valid = ptn3460_mode_valid,
.best_encoder = ptn3460_best_encoder,
};
enum drm_connector_status ptn3460_detect(struct drm_connector *connector,
bool force)
{
return connector_status_connected;
}
void ptn3460_connector_destroy(struct drm_connector *connector)
{
drm_connector_cleanup(connector);
}
struct drm_connector_funcs ptn3460_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = ptn3460_detect,
.destroy = ptn3460_connector_destroy,
};
int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
struct i2c_client *client, struct device_node *node)
{
int ret;
struct drm_bridge *bridge;
struct ptn3460_bridge *ptn_bridge;
bridge = devm_kzalloc(dev->dev, sizeof(*bridge), GFP_KERNEL);
if (!bridge) {
DRM_ERROR("Failed to allocate drm bridge\n");
return -ENOMEM;
}
ptn_bridge = devm_kzalloc(dev->dev, sizeof(*ptn_bridge), GFP_KERNEL);
if (!ptn_bridge) {
DRM_ERROR("Failed to allocate ptn bridge\n");
return -ENOMEM;
}
ptn_bridge->client = client;
ptn_bridge->encoder = encoder;
ptn_bridge->bridge = bridge;
ptn_bridge->gpio_pd_n = of_get_named_gpio(node, "powerdown-gpio", 0);
if (gpio_is_valid(ptn_bridge->gpio_pd_n)) {
ret = gpio_request_one(ptn_bridge->gpio_pd_n,
GPIOF_OUT_INIT_HIGH, "PTN3460_PD_N");
if (ret) {
DRM_ERROR("Request powerdown-gpio failed (%d)\n", ret);
return ret;
}
}
ptn_bridge->gpio_rst_n = of_get_named_gpio(node, "reset-gpio", 0);
if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
/*
* Request the reset pin low to avoid the bridge being
* initialized prematurely
*/
ret = gpio_request_one(ptn_bridge->gpio_rst_n,
GPIOF_OUT_INIT_LOW, "PTN3460_RST_N");
if (ret) {
DRM_ERROR("Request reset-gpio failed (%d)\n", ret);
gpio_free(ptn_bridge->gpio_pd_n);
return ret;
}
}
ret = of_property_read_u32(node, "edid-emulation",
&ptn_bridge->edid_emulation);
if (ret) {
DRM_ERROR("Can't read edid emulation value\n");
goto err;
}
ret = drm_bridge_init(dev, bridge, &ptn3460_bridge_funcs);
if (ret) {
DRM_ERROR("Failed to initialize bridge with drm\n");
goto err;
}
bridge->driver_private = ptn_bridge;
encoder->bridge = bridge;
ret = drm_connector_init(dev, &ptn_bridge->connector,
&ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
if (ret) {
DRM_ERROR("Failed to initialize connector with drm\n");
goto err;
}
drm_connector_helper_add(&ptn_bridge->connector,
&ptn3460_connector_helper_funcs);
drm_sysfs_connector_add(&ptn_bridge->connector);
drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder);
return 0;
err:
if (gpio_is_valid(ptn_bridge->gpio_pd_n))
gpio_free(ptn_bridge->gpio_pd_n);
if (gpio_is_valid(ptn_bridge->gpio_rst_n))
gpio_free(ptn_bridge->gpio_rst_n);
return ret;
}
EXPORT_SYMBOL(ptn3460_init);

View File

@ -149,7 +149,7 @@ static int cirrus_crtc_do_set_base(struct drm_crtc *crtc,
cirrus_bo_unreserve(bo);
}
cirrus_fb = to_cirrus_framebuffer(crtc->fb);
cirrus_fb = to_cirrus_framebuffer(crtc->primary->fb);
obj = cirrus_fb->obj;
bo = gem_to_cirrus_bo(obj);
@ -268,7 +268,7 @@ static int cirrus_crtc_mode_set(struct drm_crtc *crtc,
sr07 = RREG8(SEQ_DATA);
sr07 &= 0xe0;
hdr = 0;
switch (crtc->fb->bits_per_pixel) {
switch (crtc->primary->fb->bits_per_pixel) {
case 8:
sr07 |= 0x11;
break;
@ -291,13 +291,13 @@ static int cirrus_crtc_mode_set(struct drm_crtc *crtc,
WREG_SEQ(0x7, sr07);
/* Program the pitch */
tmp = crtc->fb->pitches[0] / 8;
tmp = crtc->primary->fb->pitches[0] / 8;
WREG_CRT(VGA_CRTC_OFFSET, tmp);
/* Enable extended blanking and pitch bits, and enable full memory */
tmp = 0x22;
tmp |= (crtc->fb->pitches[0] >> 7) & 0x10;
tmp |= (crtc->fb->pitches[0] >> 6) & 0x40;
tmp |= (crtc->primary->fb->pitches[0] >> 7) & 0x10;
tmp |= (crtc->primary->fb->pitches[0] >> 6) & 0x40;
WREG_CRT(0x1b, tmp);
/* Enable high-colour modes */

View File

@ -121,6 +121,13 @@ static const struct drm_prop_enum_list drm_dpms_enum_list[] =
DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list)
static const struct drm_prop_enum_list drm_plane_type_enum_list[] =
{
{ DRM_PLANE_TYPE_OVERLAY, "Overlay" },
{ DRM_PLANE_TYPE_PRIMARY, "Primary" },
{ DRM_PLANE_TYPE_CURSOR, "Cursor" },
};
/*
* Optional properties
*/
@ -662,7 +669,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
drm_modeset_lock_all(dev);
/* remove from any CRTC */
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
if (crtc->fb == fb) {
if (crtc->primary->fb == fb) {
/* should turn off the crtc */
memset(&set, 0, sizeof(struct drm_mode_set));
set.crtc = crtc;
@ -685,9 +692,12 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
EXPORT_SYMBOL(drm_framebuffer_remove);
/**
* drm_crtc_init - Initialise a new CRTC object
* drm_crtc_init_with_planes - Initialise a new CRTC object with
* specified primary and cursor planes.
* @dev: DRM device
* @crtc: CRTC object to init
* @primary: Primary plane for CRTC
* @cursor: Cursor plane for CRTC
* @funcs: callbacks for the new CRTC
*
* Inits a new object created as base part of a driver crtc object.
@ -695,8 +705,10 @@ EXPORT_SYMBOL(drm_framebuffer_remove);
* Returns:
* Zero on success, error code on failure.
*/
int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
const struct drm_crtc_funcs *funcs)
int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
struct drm_plane *primary,
void *cursor,
const struct drm_crtc_funcs *funcs)
{
int ret;
@ -717,12 +729,16 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
dev->mode_config.num_crtc++;
crtc->primary = primary;
if (primary)
primary->possible_crtcs = 1 << drm_crtc_index(crtc);
out:
drm_modeset_unlock_all(dev);
return ret;
}
EXPORT_SYMBOL(drm_crtc_init);
EXPORT_SYMBOL(drm_crtc_init_with_planes);
/**
* drm_crtc_cleanup - Clean up the core crtc usage
@ -1000,26 +1016,25 @@ void drm_encoder_cleanup(struct drm_encoder *encoder)
EXPORT_SYMBOL(drm_encoder_cleanup);
/**
* drm_plane_init - Initialise a new plane object
* drm_universal_plane_init - Initialize a new universal plane object
* @dev: DRM device
* @plane: plane object to init
* @possible_crtcs: bitmask of possible CRTCs
* @funcs: callbacks for the new plane
* @formats: array of supported formats (%DRM_FORMAT_*)
* @format_count: number of elements in @formats
* @priv: plane is private (hidden from userspace)?
* @type: type of plane (overlay, primary, cursor)
*
* Inits a preallocate plane object created as base part of a driver plane
* object.
* Initializes a plane object of type @type.
*
* Returns:
* Zero on success, error code on failure.
*/
int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
unsigned long possible_crtcs,
const struct drm_plane_funcs *funcs,
const uint32_t *formats, uint32_t format_count,
bool priv)
int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
unsigned long possible_crtcs,
const struct drm_plane_funcs *funcs,
const uint32_t *formats, uint32_t format_count,
enum drm_plane_type type)
{
int ret;
@ -1044,23 +1059,53 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
memcpy(plane->format_types, formats, format_count * sizeof(uint32_t));
plane->format_count = format_count;
plane->possible_crtcs = possible_crtcs;
plane->type = type;
/* private planes are not exposed to userspace, but depending on
* display hardware, might be convenient to allow sharing programming
* for the scanout engine with the crtc implementation.
*/
if (!priv) {
list_add_tail(&plane->head, &dev->mode_config.plane_list);
dev->mode_config.num_plane++;
} else {
INIT_LIST_HEAD(&plane->head);
}
list_add_tail(&plane->head, &dev->mode_config.plane_list);
dev->mode_config.num_total_plane++;
if (plane->type == DRM_PLANE_TYPE_OVERLAY)
dev->mode_config.num_overlay_plane++;
drm_object_attach_property(&plane->base,
dev->mode_config.plane_type_property,
plane->type);
out:
drm_modeset_unlock_all(dev);
return ret;
}
EXPORT_SYMBOL(drm_universal_plane_init);
/**
* drm_plane_init - Initialize a legacy plane
* @dev: DRM device
* @plane: plane object to init
* @possible_crtcs: bitmask of possible CRTCs
* @funcs: callbacks for the new plane
* @formats: array of supported formats (%DRM_FORMAT_*)
* @format_count: number of elements in @formats
* @is_primary: plane type (primary vs overlay)
*
* Legacy API to initialize a DRM plane.
*
* New drivers should call drm_universal_plane_init() instead.
*
* Returns:
* Zero on success, error code on failure.
*/
int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
unsigned long possible_crtcs,
const struct drm_plane_funcs *funcs,
const uint32_t *formats, uint32_t format_count,
bool is_primary)
{
enum drm_plane_type type;
type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
return drm_universal_plane_init(dev, plane, possible_crtcs, funcs,
formats, format_count, type);
}
EXPORT_SYMBOL(drm_plane_init);
/**
@ -1078,11 +1123,13 @@ void drm_plane_cleanup(struct drm_plane *plane)
drm_modeset_lock_all(dev);
kfree(plane->format_types);
drm_mode_object_put(dev, &plane->base);
/* if not added to a list, it must be a private plane */
if (!list_empty(&plane->head)) {
list_del(&plane->head);
dev->mode_config.num_plane--;
}
BUG_ON(list_empty(&plane->head));
list_del(&plane->head);
dev->mode_config.num_total_plane--;
if (plane->type == DRM_PLANE_TYPE_OVERLAY)
dev->mode_config.num_overlay_plane--;
drm_modeset_unlock_all(dev);
}
EXPORT_SYMBOL(drm_plane_cleanup);
@ -1134,6 +1181,21 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
return 0;
}
static int drm_mode_create_standard_plane_properties(struct drm_device *dev)
{
struct drm_property *type;
/*
* Standard properties (apply to all planes)
*/
type = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
"type", drm_plane_type_enum_list,
ARRAY_SIZE(drm_plane_type_enum_list));
dev->mode_config.plane_type_property = type;
return 0;
}
/**
* drm_mode_create_dvi_i_properties - create DVI-I specific connector properties
* @dev: DRM device
@ -1492,9 +1554,9 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
mutex_unlock(&file_priv->fbs_lock);
drm_modeset_lock_all(dev);
mode_group = &file_priv->master->minor->mode_group;
if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
if (!drm_is_primary_client(file_priv)) {
mode_group = NULL;
list_for_each(lh, &dev->mode_config.crtc_list)
crtc_count++;
@ -1505,6 +1567,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
encoder_count++;
} else {
mode_group = &file_priv->master->minor->mode_group;
crtc_count = mode_group->num_crtcs;
connector_count = mode_group->num_connectors;
encoder_count = mode_group->num_encoders;
@ -1519,7 +1582,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
if (card_res->count_crtcs >= crtc_count) {
copied = 0;
crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr;
if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
if (!mode_group) {
list_for_each_entry(crtc, &dev->mode_config.crtc_list,
head) {
DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
@ -1546,7 +1609,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
if (card_res->count_encoders >= encoder_count) {
copied = 0;
encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr;
if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
if (!mode_group) {
list_for_each_entry(encoder,
&dev->mode_config.encoder_list,
head) {
@ -1577,7 +1640,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
if (card_res->count_connectors >= connector_count) {
copied = 0;
connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr;
if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
if (!mode_group) {
list_for_each_entry(connector,
&dev->mode_config.connector_list,
head) {
@ -1651,8 +1714,8 @@ int drm_mode_getcrtc(struct drm_device *dev,
crtc_resp->x = crtc->x;
crtc_resp->y = crtc->y;
crtc_resp->gamma_size = crtc->gamma_size;
if (crtc->fb)
crtc_resp->fb_id = crtc->fb->base.id;
if (crtc->primary->fb)
crtc_resp->fb_id = crtc->primary->fb->base.id;
else
crtc_resp->fb_id = 0;
@ -1896,6 +1959,7 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,
struct drm_plane *plane;
uint32_t __user *plane_ptr;
int copied = 0, ret = 0;
unsigned num_planes;
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return -EINVAL;
@ -1903,15 +1967,28 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,
drm_modeset_lock_all(dev);
config = &dev->mode_config;
if (file_priv->universal_planes)
num_planes = config->num_total_plane;
else
num_planes = config->num_overlay_plane;
/*
* This ioctl is called twice, once to determine how much space is
* needed, and the 2nd time to fill it.
*/
if (config->num_plane &&
(plane_resp->count_planes >= config->num_plane)) {
if (num_planes &&
(plane_resp->count_planes >= num_planes)) {
plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr;
list_for_each_entry(plane, &config->plane_list, head) {
/*
* Unless userspace set the 'universal planes'
* capability bit, only advertise overlays.
*/
if (plane->type != DRM_PLANE_TYPE_OVERLAY &&
!file_priv->universal_planes)
continue;
if (put_user(plane->base.id, plane_ptr + copied)) {
ret = -EFAULT;
goto out;
@ -1919,7 +1996,7 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,
copied++;
}
}
plane_resp->count_planes = config->num_plane;
plane_resp->count_planes = num_planes;
out:
drm_modeset_unlock_all(dev);
@ -2155,19 +2232,21 @@ int drm_mode_set_config_internal(struct drm_mode_set *set)
* crtcs. Atomic modeset will have saner semantics ...
*/
list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head)
tmp->old_fb = tmp->fb;
tmp->old_fb = tmp->primary->fb;
fb = set->fb;
ret = crtc->funcs->set_config(set);
if (ret == 0) {
crtc->primary->crtc = crtc;
/* crtc->fb must be updated by ->set_config, enforces this. */
WARN_ON(fb != crtc->fb);
WARN_ON(fb != crtc->primary->fb);
}
list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
if (tmp->fb)
drm_framebuffer_reference(tmp->fb);
if (tmp->primary->fb)
drm_framebuffer_reference(tmp->primary->fb);
if (tmp->old_fb)
drm_framebuffer_unreference(tmp->old_fb);
}
@ -2176,14 +2255,19 @@ int drm_mode_set_config_internal(struct drm_mode_set *set)
}
EXPORT_SYMBOL(drm_mode_set_config_internal);
/*
* Checks that the framebuffer is big enough for the CRTC viewport
* (x, y, hdisplay, vdisplay)
/**
* drm_crtc_check_viewport - Checks that a framebuffer is big enough for the
* CRTC viewport
* @crtc: CRTC that framebuffer will be displayed on
* @x: x panning
* @y: y panning
* @mode: mode that framebuffer will be displayed under
* @fb: framebuffer to check size of
*/
static int drm_crtc_check_viewport(const struct drm_crtc *crtc,
int x, int y,
const struct drm_display_mode *mode,
const struct drm_framebuffer *fb)
int drm_crtc_check_viewport(const struct drm_crtc *crtc,
int x, int y,
const struct drm_display_mode *mode,
const struct drm_framebuffer *fb)
{
int hdisplay, vdisplay;
@ -2214,6 +2298,7 @@ static int drm_crtc_check_viewport(const struct drm_crtc *crtc,
return 0;
}
EXPORT_SYMBOL(drm_crtc_check_viewport);
/**
* drm_mode_setcrtc - set CRTC configuration
@ -2265,12 +2350,12 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
/* If we have a mode we need a framebuffer. */
/* If we pass -1, set the mode with the currently bound fb */
if (crtc_req->fb_id == -1) {
if (!crtc->fb) {
if (!crtc->primary->fb) {
DRM_DEBUG_KMS("CRTC doesn't have current FB\n");
ret = -EINVAL;
goto out;
}
fb = crtc->fb;
fb = crtc->primary->fb;
/* Make refcounting symmetric with the lookup path. */
drm_framebuffer_reference(fb);
} else {
@ -2846,7 +2931,8 @@ int drm_mode_getfb(struct drm_device *dev,
r->bpp = fb->bits_per_pixel;
r->pitch = fb->pitches[0];
if (fb->funcs->create_handle) {
if (file_priv->is_master || capable(CAP_SYS_ADMIN)) {
if (file_priv->is_master || capable(CAP_SYS_ADMIN) ||
drm_is_control_client(file_priv)) {
ret = fb->funcs->create_handle(fb, file_priv,
&r->handle);
} else {
@ -4063,7 +4149,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
crtc = obj_to_crtc(obj);
mutex_lock(&crtc->mutex);
if (crtc->fb == NULL) {
if (crtc->primary->fb == NULL) {
/* The framebuffer is currently unbound, presumably
* due to a hotplug event, that userspace has not
* yet discovered.
@ -4085,7 +4171,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
if (ret)
goto out;
if (crtc->fb->pixel_format != fb->pixel_format) {
if (crtc->primary->fb->pixel_format != fb->pixel_format) {
DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
ret = -EINVAL;
goto out;
@ -4118,7 +4204,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
(void (*) (struct drm_pending_event *)) kfree;
}
old_fb = crtc->fb;
old_fb = crtc->primary->fb;
ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags);
if (ret) {
if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
@ -4136,7 +4222,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
* Failing to do so will screw with the reference counting
* on framebuffers.
*/
WARN_ON(crtc->fb != fb);
WARN_ON(crtc->primary->fb != fb);
/* Unref only the old framebuffer. */
fb = NULL;
}
@ -4525,6 +4611,7 @@ void drm_mode_config_init(struct drm_device *dev)
drm_modeset_lock_all(dev);
drm_mode_create_standard_connector_properties(dev);
drm_mode_create_standard_plane_properties(dev);
drm_modeset_unlock_all(dev);
/* Just to be sure */
@ -4532,6 +4619,8 @@ void drm_mode_config_init(struct drm_device *dev)
dev->mode_config.num_connector = 0;
dev->mode_config.num_crtc = 0;
dev->mode_config.num_encoder = 0;
dev->mode_config.num_overlay_plane = 0;
dev->mode_config.num_total_plane = 0;
}
EXPORT_SYMBOL(drm_mode_config_init);

View File

@ -278,17 +278,7 @@ drm_encoder_disable(struct drm_encoder *encoder)
encoder->bridge->funcs->post_disable(encoder->bridge);
}
/**
* drm_helper_disable_unused_functions - disable unused objects
* @dev: DRM device
*
* This function walks through the entire mode setting configuration of @dev. It
* will remove any crtc links of unused encoders and encoder links of
* disconnected connectors. Then it will disable all unused encoders and crtcs
* either by calling their disable callback if available or by calling their
* dpms callback with DRM_MODE_DPMS_OFF.
*/
void drm_helper_disable_unused_functions(struct drm_device *dev)
static void __drm_helper_disable_unused_functions(struct drm_device *dev)
{
struct drm_encoder *encoder;
struct drm_connector *connector;
@ -299,8 +289,6 @@ void drm_helper_disable_unused_functions(struct drm_device *dev)
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (!connector->encoder)
continue;
if (connector->status == connector_status_disconnected)
connector->encoder = NULL;
}
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
@ -319,10 +307,27 @@ void drm_helper_disable_unused_functions(struct drm_device *dev)
(*crtc_funcs->disable)(crtc);
else
(*crtc_funcs->dpms)(crtc, DRM_MODE_DPMS_OFF);
crtc->fb = NULL;
crtc->primary->fb = NULL;
}
}
}
/**
* drm_helper_disable_unused_functions - disable unused objects
* @dev: DRM device
*
* This function walks through the entire mode setting configuration of @dev. It
* will remove any crtc links of unused encoders and encoder links of
* disconnected connectors. Then it will disable all unused encoders and crtcs
* either by calling their disable callback if available or by calling their
* dpms callback with DRM_MODE_DPMS_OFF.
*/
void drm_helper_disable_unused_functions(struct drm_device *dev)
{
drm_modeset_lock_all(dev);
__drm_helper_disable_unused_functions(dev);
drm_modeset_unlock_all(dev);
}
EXPORT_SYMBOL(drm_helper_disable_unused_functions);
/*
@ -552,7 +557,7 @@ drm_crtc_helper_disable(struct drm_crtc *crtc)
}
}
drm_helper_disable_unused_functions(dev);
__drm_helper_disable_unused_functions(dev);
return 0;
}
@ -646,19 +651,19 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
save_set.mode = &set->crtc->mode;
save_set.x = set->crtc->x;
save_set.y = set->crtc->y;
save_set.fb = set->crtc->fb;
save_set.fb = set->crtc->primary->fb;
/* We should be able to check here if the fb has the same properties
* and then just flip_or_move it */
if (set->crtc->fb != set->fb) {
if (set->crtc->primary->fb != set->fb) {
/* If we have no fb then treat it as a full mode set */
if (set->crtc->fb == NULL) {
if (set->crtc->primary->fb == NULL) {
DRM_DEBUG_KMS("crtc has no fb, full mode set\n");
mode_changed = true;
} else if (set->fb == NULL) {
mode_changed = true;
} else if (set->fb->pixel_format !=
set->crtc->fb->pixel_format) {
set->crtc->primary->fb->pixel_format) {
mode_changed = true;
} else
fb_changed = true;
@ -688,12 +693,13 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
if (new_encoder == NULL)
/* don't break so fail path works correct */
fail = 1;
break;
if (connector->dpms != DRM_MODE_DPMS_ON) {
DRM_DEBUG_KMS("connector dpms not on, full mode switch\n");
mode_changed = true;
}
break;
}
}
@ -759,13 +765,13 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
DRM_DEBUG_KMS("attempting to set mode from"
" userspace\n");
drm_mode_debug_printmodeline(set->mode);
set->crtc->fb = set->fb;
set->crtc->primary->fb = set->fb;
if (!drm_crtc_helper_set_mode(set->crtc, set->mode,
set->x, set->y,
save_set.fb)) {
DRM_ERROR("failed to set mode on [CRTC:%d]\n",
set->crtc->base.id);
set->crtc->fb = save_set.fb;
set->crtc->primary->fb = save_set.fb;
ret = -EINVAL;
goto fail;
}
@ -776,17 +782,17 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON);
}
}
drm_helper_disable_unused_functions(dev);
__drm_helper_disable_unused_functions(dev);
} else if (fb_changed) {
set->crtc->x = set->x;
set->crtc->y = set->y;
set->crtc->fb = set->fb;
set->crtc->primary->fb = set->fb;
ret = crtc_funcs->mode_set_base(set->crtc,
set->x, set->y, save_set.fb);
if (ret != 0) {
set->crtc->x = save_set.x;
set->crtc->y = save_set.y;
set->crtc->fb = save_set.fb;
set->crtc->primary->fb = save_set.fb;
goto fail;
}
}
@ -976,13 +982,14 @@ void drm_helper_resume_force_mode(struct drm_device *dev)
int encoder_dpms;
bool ret;
drm_modeset_lock_all(dev);
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
if (!crtc->enabled)
continue;
ret = drm_crtc_helper_set_mode(crtc, &crtc->mode,
crtc->x, crtc->y, crtc->fb);
crtc->x, crtc->y, crtc->primary->fb);
/* Restoring the old config should never fail! */
if (ret == false)
@ -1009,7 +1016,8 @@ void drm_helper_resume_force_mode(struct drm_device *dev)
}
/* disable the unused connectors while restoring the modesetting */
drm_helper_disable_unused_functions(dev);
__drm_helper_disable_unused_functions(dev);
drm_modeset_unlock_all(dev);
}
EXPORT_SYMBOL(drm_helper_resume_force_mode);

View File

@ -386,11 +386,11 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
return err;
}
if (err < size)
return -EPROTO;
switch (msg.reply & DP_AUX_NATIVE_REPLY_MASK) {
case DP_AUX_NATIVE_REPLY_ACK:
if (err < size)
return -EPROTO;
return err;
case DP_AUX_NATIVE_REPLY_NACK:
@ -402,7 +402,7 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
}
}
DRM_ERROR("too many retries, giving up\n");
DRM_DEBUG_KMS("too many retries, giving up\n");
return -EIO;
}
@ -599,8 +599,6 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
return err;
}
if (err < msg->size)
return -EPROTO;
switch (msg->reply & DP_AUX_NATIVE_REPLY_MASK) {
case DP_AUX_NATIVE_REPLY_ACK:
@ -639,6 +637,8 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
* Both native ACK and I2C ACK replies received. We
* can assume the transfer was successful.
*/
if (err < msg->size)
return -EPROTO;
return 0;
case DP_AUX_I2C_REPLY_NACK:
@ -656,7 +656,7 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
}
}
DRM_ERROR("too many retries, giving up\n");
DRM_DEBUG_KMS("too many retries, giving up\n");
return -EREMOTEIO;
}

View File

@ -285,6 +285,45 @@ static int drm_version(struct drm_device *dev, void *data,
return err;
}
/**
* drm_ioctl_permit - Check ioctl permissions against caller
*
* @flags: ioctl permission flags.
* @file_priv: Pointer to struct drm_file identifying the caller.
*
* Checks whether the caller is allowed to run an ioctl with the
* indicated permissions. If so, returns zero. Otherwise returns an
* error code suitable for ioctl return.
*/
static int drm_ioctl_permit(u32 flags, struct drm_file *file_priv)
{
/* ROOT_ONLY is only for CAP_SYS_ADMIN */
if (unlikely((flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)))
return -EACCES;
/* AUTH is only for authenticated or render client */
if (unlikely((flags & DRM_AUTH) && !drm_is_render_client(file_priv) &&
!file_priv->authenticated))
return -EACCES;
/* MASTER is only for master or control clients */
if (unlikely((flags & DRM_MASTER) && !file_priv->is_master &&
!drm_is_control_client(file_priv)))
return -EACCES;
/* Control clients must be explicitly allowed */
if (unlikely(!(flags & DRM_CONTROL_ALLOW) &&
drm_is_control_client(file_priv)))
return -EACCES;
/* Render clients must be explicitly allowed */
if (unlikely(!(flags & DRM_RENDER_ALLOW) &&
drm_is_render_client(file_priv)))
return -EACCES;
return 0;
}
/**
* Called whenever a process performs an ioctl on /dev/drm.
*
@ -350,52 +389,51 @@ long drm_ioctl(struct file *filp,
/* Do not trust userspace, use our own definition */
func = ioctl->func;
if (!func) {
if (unlikely(!func)) {
DRM_DEBUG("no function\n");
retcode = -EINVAL;
} else if (((ioctl->flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)) ||
((ioctl->flags & DRM_AUTH) && !drm_is_render_client(file_priv) && !file_priv->authenticated) ||
((ioctl->flags & DRM_MASTER) && !file_priv->is_master) ||
(!(ioctl->flags & DRM_CONTROL_ALLOW) && (file_priv->minor->type == DRM_MINOR_CONTROL)) ||
(!(ioctl->flags & DRM_RENDER_ALLOW) && drm_is_render_client(file_priv))) {
retcode = -EACCES;
} else {
if (cmd & (IOC_IN | IOC_OUT)) {
if (asize <= sizeof(stack_kdata)) {
kdata = stack_kdata;
} else {
kdata = kmalloc(asize, GFP_KERNEL);
if (!kdata) {
retcode = -ENOMEM;
goto err_i1;
}
}
if (asize > usize)
memset(kdata + usize, 0, asize - usize);
}
goto err_i1;
}
if (cmd & IOC_IN) {
if (copy_from_user(kdata, (void __user *)arg,
usize) != 0) {
retcode = -EFAULT;
retcode = drm_ioctl_permit(ioctl->flags, file_priv);
if (unlikely(retcode))
goto err_i1;
if (cmd & (IOC_IN | IOC_OUT)) {
if (asize <= sizeof(stack_kdata)) {
kdata = stack_kdata;
} else {
kdata = kmalloc(asize, GFP_KERNEL);
if (!kdata) {
retcode = -ENOMEM;
goto err_i1;
}
} else
memset(kdata, 0, usize);
if (ioctl->flags & DRM_UNLOCKED)
retcode = func(dev, kdata, file_priv);
else {
mutex_lock(&drm_global_mutex);
retcode = func(dev, kdata, file_priv);
mutex_unlock(&drm_global_mutex);
}
if (asize > usize)
memset(kdata + usize, 0, asize - usize);
}
if (cmd & IOC_OUT) {
if (copy_to_user((void __user *)arg, kdata,
usize) != 0)
retcode = -EFAULT;
if (cmd & IOC_IN) {
if (copy_from_user(kdata, (void __user *)arg,
usize) != 0) {
retcode = -EFAULT;
goto err_i1;
}
} else
memset(kdata, 0, usize);
if (ioctl->flags & DRM_UNLOCKED)
retcode = func(dev, kdata, file_priv);
else {
mutex_lock(&drm_global_mutex);
retcode = func(dev, kdata, file_priv);
mutex_unlock(&drm_global_mutex);
}
if (cmd & IOC_OUT) {
if (copy_to_user((void __user *)arg, kdata,
usize) != 0)
retcode = -EFAULT;
}
err_i1:
@ -412,3 +450,21 @@ long drm_ioctl(struct file *filp,
return retcode;
}
EXPORT_SYMBOL(drm_ioctl);
/**
* drm_ioctl_flags - Check for core ioctl and return ioctl permission flags
*
* @nr: Ioctl number.
* @flags: Where to return the ioctl permission flags
*/
bool drm_ioctl_flags(unsigned int nr, unsigned int *flags)
{
if ((nr >= DRM_COMMAND_END && nr < DRM_CORE_IOCTL_COUNT) ||
(nr < DRM_COMMAND_BASE)) {
*flags = drm_ioctls[nr].flags;
return true;
}
return false;
}
EXPORT_SYMBOL(drm_ioctl_flags);

View File

@ -232,7 +232,7 @@ static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc)
list_for_each_entry(c, &dev->mode_config.crtc_list, head) {
if (crtc->base.id == c->base.id)
return c->fb;
return c->primary->fb;
}
return NULL;
@ -291,7 +291,8 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
drm_warn_on_modeset_not_all_locked(dev);
list_for_each_entry(plane, &dev->mode_config.plane_list, head)
drm_plane_force_disable(plane);
if (plane->type != DRM_PLANE_TYPE_PRIMARY)
drm_plane_force_disable(plane);
for (i = 0; i < fb_helper->crtc_count; i++) {
struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
@ -365,9 +366,9 @@ static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper)
return false;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
if (crtc->fb)
if (crtc->primary->fb)
crtcs_bound++;
if (crtc->fb == fb_helper->fb)
if (crtc->primary->fb == fb_helper->fb)
bound++;
}
@ -1158,6 +1159,7 @@ struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *f
{
struct drm_cmdline_mode *cmdline_mode;
struct drm_display_mode *mode = NULL;
bool prefer_non_interlace;
cmdline_mode = &fb_helper_conn->cmdline_mode;
if (cmdline_mode->specified == false)
@ -1169,6 +1171,8 @@ struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *f
if (cmdline_mode->rb || cmdline_mode->margins)
goto create_mode;
prefer_non_interlace = !cmdline_mode->interlace;
again:
list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
/* check width/height */
if (mode->hdisplay != cmdline_mode->xres ||
@ -1183,10 +1187,18 @@ struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *f
if (cmdline_mode->interlace) {
if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
continue;
} else if (prefer_non_interlace) {
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
continue;
}
return mode;
}
if (prefer_non_interlace) {
prefer_non_interlace = false;
goto again;
}
create_mode:
mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev,
cmdline_mode);
@ -1536,9 +1548,11 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
drm_fb_helper_parse_command_line(fb_helper);
mutex_lock(&dev->mode_config.mutex);
count = drm_fb_helper_probe_connector_modes(fb_helper,
dev->mode_config.max_width,
dev->mode_config.max_height);
mutex_unlock(&dev->mode_config.mutex);
/*
* we shouldn't end up with no modes here.
*/

View File

@ -231,12 +231,11 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
/* if there is no current master make this fd it, but do not create
* any master object for render clients */
mutex_lock(&dev->struct_mutex);
if (!priv->minor->master && !drm_is_render_client(priv)) {
mutex_lock(&dev->master_mutex);
if (drm_is_primary_client(priv) && !priv->minor->master) {
/* create a new master */
priv->minor->master = drm_master_create(priv->minor);
if (!priv->minor->master) {
mutex_unlock(&dev->struct_mutex);
ret = -ENOMEM;
goto out_close;
}
@ -244,37 +243,31 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
priv->is_master = 1;
/* take another reference for the copy in the local file priv */
priv->master = drm_master_get(priv->minor->master);
priv->authenticated = 1;
mutex_unlock(&dev->struct_mutex);
if (dev->driver->master_create) {
ret = dev->driver->master_create(dev, priv->master);
if (ret) {
mutex_lock(&dev->struct_mutex);
/* drop both references if this fails */
drm_master_put(&priv->minor->master);
drm_master_put(&priv->master);
mutex_unlock(&dev->struct_mutex);
goto out_close;
}
}
mutex_lock(&dev->struct_mutex);
if (dev->driver->master_set) {
ret = dev->driver->master_set(dev, priv, true);
if (ret) {
/* drop both references if this fails */
drm_master_put(&priv->minor->master);
drm_master_put(&priv->master);
mutex_unlock(&dev->struct_mutex);
goto out_close;
}
}
} else if (!drm_is_render_client(priv)) {
} else if (drm_is_primary_client(priv)) {
/* get a reference to the master */
priv->master = drm_master_get(priv->minor->master);
}
mutex_unlock(&dev->struct_mutex);
mutex_unlock(&dev->master_mutex);
mutex_lock(&dev->struct_mutex);
list_add(&priv->lhead, &dev->filelist);
@ -302,6 +295,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
return 0;
out_close:
mutex_unlock(&dev->master_mutex);
if (dev->driver->postclose)
dev->driver->postclose(dev, priv);
out_prime_destroy:
@ -489,11 +483,13 @@ int drm_release(struct inode *inode, struct file *filp)
}
mutex_unlock(&dev->ctxlist_mutex);
mutex_lock(&dev->struct_mutex);
mutex_lock(&dev->master_mutex);
if (file_priv->is_master) {
struct drm_master *master = file_priv->master;
struct drm_file *temp;
mutex_lock(&dev->struct_mutex);
list_for_each_entry(temp, &dev->filelist, lhead) {
if ((temp->master == file_priv->master) &&
(temp != file_priv))
@ -512,6 +508,7 @@ int drm_release(struct inode *inode, struct file *filp)
master->lock.file_priv = NULL;
wake_up_interruptible_all(&master->lock.lock_queue);
}
mutex_unlock(&dev->struct_mutex);
if (file_priv->minor->master == file_priv->master) {
/* drop the reference held my the minor */
@ -521,10 +518,13 @@ int drm_release(struct inode *inode, struct file *filp)
}
}
/* drop the reference held my the file priv */
/* drop the master reference held by the file priv */
if (file_priv->master)
drm_master_put(&file_priv->master);
file_priv->is_master = 0;
mutex_unlock(&dev->master_mutex);
mutex_lock(&dev->struct_mutex);
list_del(&file_priv->lhead);
mutex_unlock(&dev->struct_mutex);

View File

@ -234,8 +234,17 @@ static int drm_gem_cma_mmap_obj(struct drm_gem_cma_object *cma_obj,
{
int ret;
ret = remap_pfn_range(vma, vma->vm_start, cma_obj->paddr >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot);
/*
* Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the
* vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map
* the whole buffer.
*/
vma->vm_flags &= ~VM_PFNMAP;
vma->vm_pgoff = 0;
ret = dma_mmap_writecombine(cma_obj->base.dev->dev, vma,
cma_obj->vaddr, cma_obj->paddr,
vma->vm_end - vma->vm_start);
if (ret)
drm_gem_vm_close(vma);
@ -273,9 +282,9 @@ void drm_gem_cma_describe(struct drm_gem_cma_object *cma_obj, struct seq_file *m
off = drm_vma_node_start(&obj->vma_node);
seq_printf(m, "%2d (%2d) %08llx %08Zx %p %d",
seq_printf(m, "%2d (%2d) %08llx %pad %p %d",
obj->name, obj->refcount.refcount.counter,
off, cma_obj->paddr, cma_obj->vaddr, obj->size);
off, &cma_obj->paddr, cma_obj->vaddr, obj->size);
seq_printf(m, "\n");
}
@ -323,7 +332,7 @@ drm_gem_cma_prime_import_sg_table(struct drm_device *dev, size_t size,
cma_obj->paddr = sg_dma_address(sgt->sgl);
cma_obj->sgt = sgt;
DRM_DEBUG_PRIME("dma_addr = 0x%x, size = %zu\n", cma_obj->paddr, size);
DRM_DEBUG_PRIME("dma_addr = %pad, size = %zu\n", &cma_obj->paddr, size);
return &cma_obj->base;
}

View File

@ -328,6 +328,13 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
return -EINVAL;
file_priv->stereo_allowed = req->value;
break;
case DRM_CLIENT_CAP_UNIVERSAL_PLANES:
if (!drm_universal_planes)
return -EINVAL;
if (req->value > 1)
return -EINVAL;
file_priv->universal_planes = req->value;
break;
default:
return -EINVAL;
}

View File

@ -142,8 +142,12 @@ int mipi_dsi_host_register(struct mipi_dsi_host *host)
{
struct device_node *node;
for_each_available_child_of_node(host->dev->of_node, node)
for_each_available_child_of_node(host->dev->of_node, node) {
/* skip nodes without reg property */
if (!of_find_property(node, "reg", NULL))
continue;
of_mipi_dsi_device_add(host, node);
}
return 0;
}

View File

@ -82,6 +82,10 @@
* this to implement guard pages between incompatible caching domains in the
* graphics TT.
*
* Two behaviors are supported for searching and allocating: bottom-up and top-down.
* The default is bottom-up. Top-down allocation can be used if the memory area
* has different restrictions, or just to reduce fragmentation.
*
* Finally iteration helpers to walk all nodes and all holes are provided as are
* some basic allocator dumpers for debugging.
*/
@ -102,7 +106,8 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_
static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
struct drm_mm_node *node,
unsigned long size, unsigned alignment,
unsigned long color)
unsigned long color,
enum drm_mm_allocator_flags flags)
{
struct drm_mm *mm = hole_node->mm;
unsigned long hole_start = drm_mm_hole_node_start(hole_node);
@ -115,12 +120,22 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
if (mm->color_adjust)
mm->color_adjust(hole_node, color, &adj_start, &adj_end);
if (flags & DRM_MM_CREATE_TOP)
adj_start = adj_end - size;
if (alignment) {
unsigned tmp = adj_start % alignment;
if (tmp)
adj_start += alignment - tmp;
if (tmp) {
if (flags & DRM_MM_CREATE_TOP)
adj_start -= tmp;
else
adj_start += alignment - tmp;
}
}
BUG_ON(adj_start < hole_start);
BUG_ON(adj_end > hole_end);
if (adj_start == hole_start) {
hole_node->hole_follows = 0;
list_del(&hole_node->hole_stack);
@ -205,7 +220,8 @@ EXPORT_SYMBOL(drm_mm_reserve_node);
* @size: size of the allocation
* @alignment: alignment of the allocation
* @color: opaque tag value to use for this node
* @flags: flags to fine-tune the allocation
* @sflags: flags to fine-tune the allocation search
* @aflags: flags to fine-tune the allocation behavior
*
* The preallocated node must be cleared to 0.
*
@ -215,16 +231,17 @@ EXPORT_SYMBOL(drm_mm_reserve_node);
int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node,
unsigned long size, unsigned alignment,
unsigned long color,
enum drm_mm_search_flags flags)
enum drm_mm_search_flags sflags,
enum drm_mm_allocator_flags aflags)
{
struct drm_mm_node *hole_node;
hole_node = drm_mm_search_free_generic(mm, size, alignment,
color, flags);
color, sflags);
if (!hole_node)
return -ENOSPC;
drm_mm_insert_helper(hole_node, node, size, alignment, color);
drm_mm_insert_helper(hole_node, node, size, alignment, color, aflags);
return 0;
}
EXPORT_SYMBOL(drm_mm_insert_node_generic);
@ -233,7 +250,8 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
struct drm_mm_node *node,
unsigned long size, unsigned alignment,
unsigned long color,
unsigned long start, unsigned long end)
unsigned long start, unsigned long end,
enum drm_mm_allocator_flags flags)
{
struct drm_mm *mm = hole_node->mm;
unsigned long hole_start = drm_mm_hole_node_start(hole_node);
@ -248,13 +266,20 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
if (adj_end > end)
adj_end = end;
if (flags & DRM_MM_CREATE_TOP)
adj_start = adj_end - size;
if (mm->color_adjust)
mm->color_adjust(hole_node, color, &adj_start, &adj_end);
if (alignment) {
unsigned tmp = adj_start % alignment;
if (tmp)
adj_start += alignment - tmp;
if (tmp) {
if (flags & DRM_MM_CREATE_TOP)
adj_start -= tmp;
else
adj_start += alignment - tmp;
}
}
if (adj_start == hole_start) {
@ -271,6 +296,8 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
INIT_LIST_HEAD(&node->hole_stack);
list_add(&node->node_list, &hole_node->node_list);
BUG_ON(node->start < start);
BUG_ON(node->start < adj_start);
BUG_ON(node->start + node->size > adj_end);
BUG_ON(node->start + node->size > end);
@ -290,7 +317,8 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
* @color: opaque tag value to use for this node
* @start: start of the allowed range for this node
* @end: end of the allowed range for this node
* @flags: flags to fine-tune the allocation
* @sflags: flags to fine-tune the allocation search
* @aflags: flags to fine-tune the allocation behavior
*
* The preallocated node must be cleared to 0.
*
@ -298,21 +326,23 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
* 0 on success, -ENOSPC if there's no suitable hole.
*/
int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *node,
unsigned long size, unsigned alignment, unsigned long color,
unsigned long size, unsigned alignment,
unsigned long color,
unsigned long start, unsigned long end,
enum drm_mm_search_flags flags)
enum drm_mm_search_flags sflags,
enum drm_mm_allocator_flags aflags)
{
struct drm_mm_node *hole_node;
hole_node = drm_mm_search_free_in_range_generic(mm,
size, alignment, color,
start, end, flags);
start, end, sflags);
if (!hole_node)
return -ENOSPC;
drm_mm_insert_helper_range(hole_node, node,
size, alignment, color,
start, end);
start, end, aflags);
return 0;
}
EXPORT_SYMBOL(drm_mm_insert_node_in_range_generic);
@ -391,7 +421,10 @@ static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
best = NULL;
best_size = ~0UL;
drm_mm_for_each_hole(entry, mm, adj_start, adj_end) {
__drm_mm_for_each_hole(entry, mm, adj_start, adj_end,
flags & DRM_MM_SEARCH_BELOW) {
unsigned long hole_size = adj_end - adj_start;
if (mm->color_adjust) {
mm->color_adjust(entry, color, &adj_start, &adj_end);
if (adj_end <= adj_start)
@ -404,9 +437,9 @@ static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
if (!(flags & DRM_MM_SEARCH_BEST))
return entry;
if (entry->size < best_size) {
if (hole_size < best_size) {
best = entry;
best_size = entry->size;
best_size = hole_size;
}
}
@ -432,7 +465,10 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_
best = NULL;
best_size = ~0UL;
drm_mm_for_each_hole(entry, mm, adj_start, adj_end) {
__drm_mm_for_each_hole(entry, mm, adj_start, adj_end,
flags & DRM_MM_SEARCH_BELOW) {
unsigned long hole_size = adj_end - adj_start;
if (adj_start < start)
adj_start = start;
if (adj_end > end)
@ -450,9 +486,9 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_
if (!(flags & DRM_MM_SEARCH_BEST))
return entry;
if (entry->size < best_size) {
if (hole_size < best_size) {
best = entry;
best_size = entry->size;
best_size = hole_size;
}
}

View File

@ -0,0 +1,333 @@
/*
* Copyright (C) 2014 Intel Corporation
*
* DRM universal plane helper functions
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <linux/list.h>
#include <drm/drmP.h>
#include <drm/drm_rect.h>
#define SUBPIXEL_MASK 0xffff
/*
* This is the minimal list of formats that seem to be safe for modeset use
* with all current DRM drivers. Most hardware can actually support more
* formats than this and drivers may specify a more accurate list when
* creating the primary plane. However drivers that still call
* drm_plane_init() will use this minimal format list as the default.
*/
const static uint32_t safe_modeset_formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
};
/*
* Returns the connectors currently associated with a CRTC. This function
* should be called twice: once with a NULL connector list to retrieve
* the list size, and once with the properly allocated list to be filled in.
*/
static int get_connectors_for_crtc(struct drm_crtc *crtc,
struct drm_connector **connector_list,
int num_connectors)
{
struct drm_device *dev = crtc->dev;
struct drm_connector *connector;
int count = 0;
list_for_each_entry(connector, &dev->mode_config.connector_list, head)
if (connector->encoder && connector->encoder->crtc == crtc) {
if (connector_list != NULL && count < num_connectors)
*(connector_list++) = connector;
count++;
}
return count;
}
/**
* drm_primary_helper_update() - Helper for primary plane update
* @plane: plane object to update
* @crtc: owning CRTC of owning plane
* @fb: framebuffer to flip onto plane
* @crtc_x: x offset of primary plane on crtc
* @crtc_y: y offset of primary plane on crtc
* @crtc_w: width of primary plane rectangle on crtc
* @crtc_h: height of primary plane rectangle on crtc
* @src_x: x offset of @fb for panning
* @src_y: y offset of @fb for panning
* @src_w: width of source rectangle in @fb
* @src_h: height of source rectangle in @fb
*
* Provides a default plane update handler for primary planes. This is handler
* is called in response to a userspace SetPlane operation on the plane with a
* non-NULL framebuffer. We call the driver's modeset handler to update the
* framebuffer.
*
* SetPlane() on a primary plane of a disabled CRTC is not supported, and will
* return an error.
*
* Note that we make some assumptions about hardware limitations that may not be
* true for all hardware --
* 1) Primary plane cannot be repositioned.
* 2) Primary plane cannot be scaled.
* 3) Primary plane must cover the entire CRTC.
* 4) Subpixel positioning is not supported.
* Drivers for hardware that don't have these restrictions can provide their
* own implementation rather than using this helper.
*
* RETURNS:
* Zero on success, error code on failure
*/
int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_framebuffer *fb,
int crtc_x, int crtc_y,
unsigned int crtc_w, unsigned int crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h)
{
struct drm_mode_set set = {
.crtc = crtc,
.fb = fb,
.mode = &crtc->mode,
.x = src_x >> 16,
.y = src_y >> 16,
};
struct drm_rect dest = {
.x1 = crtc_x,
.y1 = crtc_y,
.x2 = crtc_x + crtc_w,
.y2 = crtc_y + crtc_h,
};
struct drm_rect clip = {
.x2 = crtc->mode.hdisplay,
.y2 = crtc->mode.vdisplay,
};
struct drm_connector **connector_list;
struct drm_framebuffer *tmpfb;
int num_connectors, ret;
if (!crtc->enabled) {
DRM_DEBUG_KMS("Cannot update primary plane of a disabled CRTC.\n");
return -EINVAL;
}
/* Disallow subpixel positioning */
if ((src_x | src_y | src_w | src_h) & SUBPIXEL_MASK) {
DRM_DEBUG_KMS("Primary plane does not support subpixel positioning\n");
return -EINVAL;
}
/* Primary planes are locked to their owning CRTC */
if (plane->possible_crtcs != drm_crtc_mask(crtc)) {
DRM_DEBUG_KMS("Cannot change primary plane CRTC\n");
return -EINVAL;
}
/* Disallow scaling */
if (crtc_w != src_w || crtc_h != src_h) {
DRM_DEBUG_KMS("Can't scale primary plane\n");
return -EINVAL;
}
/* Make sure primary plane covers entire CRTC */
drm_rect_intersect(&dest, &clip);
if (dest.x1 != 0 || dest.y1 != 0 ||
dest.x2 != crtc->mode.hdisplay || dest.y2 != crtc->mode.vdisplay) {
DRM_DEBUG_KMS("Primary plane must cover entire CRTC\n");
return -EINVAL;
}
/* Framebuffer must be big enough to cover entire plane */
ret = drm_crtc_check_viewport(crtc, crtc_x, crtc_y, &crtc->mode, fb);
if (ret)
return ret;
/* Find current connectors for CRTC */
num_connectors = get_connectors_for_crtc(crtc, NULL, 0);
BUG_ON(num_connectors == 0);
connector_list = kzalloc(num_connectors * sizeof(*connector_list),
GFP_KERNEL);
if (!connector_list)
return -ENOMEM;
get_connectors_for_crtc(crtc, connector_list, num_connectors);
set.connectors = connector_list;
set.num_connectors = num_connectors;
/*
* set_config() adjusts crtc->primary->fb; however the DRM setplane
* code that called us expects to handle the framebuffer update and
* reference counting; save and restore the current fb before
* calling it.
*
* N.B., we call set_config() directly here rather than using
* drm_mode_set_config_internal. We're reprogramming the same
* connectors that were already in use, so we shouldn't need the extra
* cross-CRTC fb refcounting to accomodate stealing connectors.
* drm_mode_setplane() already handles the basic refcounting for the
* framebuffers involved in this operation.
*/
tmpfb = plane->fb;
ret = crtc->funcs->set_config(&set);
plane->fb = tmpfb;
kfree(connector_list);
return ret;
}
EXPORT_SYMBOL(drm_primary_helper_update);
/**
* drm_primary_helper_disable() - Helper for primary plane disable
* @plane: plane to disable
*
* Provides a default plane disable handler for primary planes. This is handler
* is called in response to a userspace SetPlane operation on the plane with a
* NULL framebuffer parameter. We call the driver's modeset handler with a NULL
* framebuffer to disable the CRTC if no other planes are currently enabled.
* If other planes are still enabled on the same CRTC, we return -EBUSY.
*
* Note that some hardware may be able to disable the primary plane without
* disabling the whole CRTC. Drivers for such hardware should provide their
* own disable handler that disables just the primary plane (and they'll likely
* need to provide their own update handler as well to properly re-enable a
* disabled primary plane).
*
* RETURNS:
* Zero on success, error code on failure
*/
int drm_primary_helper_disable(struct drm_plane *plane)
{
struct drm_plane *p;
struct drm_mode_set set = {
.crtc = plane->crtc,
.fb = NULL,
};
if (plane->crtc == NULL || plane->fb == NULL)
/* Already disabled */
return 0;
list_for_each_entry(p, &plane->dev->mode_config.plane_list, head)
if (p != plane && p->fb) {
DRM_DEBUG_KMS("Cannot disable primary plane while other planes are still active on CRTC.\n");
return -EBUSY;
}
/*
* N.B. We call set_config() directly here rather than
* drm_mode_set_config_internal() since drm_mode_setplane() already
* handles the basic refcounting and we don't need the special
* cross-CRTC refcounting (no chance of stealing connectors from
* other CRTC's with this update).
*/
return plane->crtc->funcs->set_config(&set);
}
EXPORT_SYMBOL(drm_primary_helper_disable);
/**
* drm_primary_helper_destroy() - Helper for primary plane destruction
* @plane: plane to destroy
*
* Provides a default plane destroy handler for primary planes. This handler
* is called during CRTC destruction. We disable the primary plane, remove
* it from the DRM plane list, and deallocate the plane structure.
*/
void drm_primary_helper_destroy(struct drm_plane *plane)
{
plane->funcs->disable_plane(plane);
drm_plane_cleanup(plane);
kfree(plane);
}
EXPORT_SYMBOL(drm_primary_helper_destroy);
const struct drm_plane_funcs drm_primary_helper_funcs = {
.update_plane = drm_primary_helper_update,
.disable_plane = drm_primary_helper_disable,
.destroy = drm_primary_helper_destroy,
};
EXPORT_SYMBOL(drm_primary_helper_funcs);
/**
* drm_primary_helper_create_plane() - Create a generic primary plane
* @dev: drm device
* @formats: pixel formats supported, or NULL for a default safe list
* @num_formats: size of @formats; ignored if @formats is NULL
*
* Allocates and initializes a primary plane that can be used with the primary
* plane helpers. Drivers that wish to use driver-specific plane structures or
* provide custom handler functions may perform their own allocation and
* initialization rather than calling this function.
*/
struct drm_plane *drm_primary_helper_create_plane(struct drm_device *dev,
const uint32_t *formats,
int num_formats)
{
struct drm_plane *primary;
int ret;
primary = kzalloc(sizeof(*primary), GFP_KERNEL);
if (primary == NULL) {
DRM_DEBUG_KMS("Failed to allocate primary plane\n");
return NULL;
}
if (formats == NULL) {
formats = safe_modeset_formats;
num_formats = ARRAY_SIZE(safe_modeset_formats);
}
/* possible_crtc's will be filled in later by crtc_init */
ret = drm_plane_init(dev, primary, 0, &drm_primary_helper_funcs,
formats, num_formats,
DRM_PLANE_TYPE_PRIMARY);
if (ret) {
kfree(primary);
primary = NULL;
}
return primary;
}
EXPORT_SYMBOL(drm_primary_helper_create_plane);
/**
* drm_crtc_init - Legacy CRTC initialization function
* @dev: DRM device
* @crtc: CRTC object to init
* @funcs: callbacks for the new CRTC
*
* Initialize a CRTC object with a default helper-provided primary plane and no
* cursor plane.
*
* Returns:
* Zero on success, error code on failure.
*/
int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
const struct drm_crtc_funcs *funcs)
{
struct drm_plane *primary;
primary = drm_primary_helper_create_plane(dev, NULL, 0);
return drm_crtc_init_with_planes(dev, crtc, primary, NULL, funcs);
}
EXPORT_SYMBOL(drm_crtc_init);

View File

@ -45,6 +45,10 @@ EXPORT_SYMBOL(drm_debug);
unsigned int drm_rnodes = 0; /* 1 to enable experimental render nodes API */
EXPORT_SYMBOL(drm_rnodes);
/* 1 to allow user space to request universal planes (experimental) */
unsigned int drm_universal_planes = 0;
EXPORT_SYMBOL(drm_universal_planes);
unsigned int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */
EXPORT_SYMBOL(drm_vblank_offdelay);
@ -68,6 +72,7 @@ MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
module_param_named(debug, drm_debug, int, 0600);
module_param_named(rnodes, drm_rnodes, int, 0600);
module_param_named(universal_planes, drm_universal_planes, int, 0600);
module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
@ -97,26 +102,18 @@ int drm_err(const char *func, const char *format, ...)
}
EXPORT_SYMBOL(drm_err);
void drm_ut_debug_printk(unsigned int request_level,
const char *prefix,
const char *function_name,
const char *format, ...)
void drm_ut_debug_printk(const char *function_name, const char *format, ...)
{
struct va_format vaf;
va_list args;
if (drm_debug & request_level) {
va_start(args, format);
vaf.fmt = format;
vaf.va = &args;
va_start(args, format);
vaf.fmt = format;
vaf.va = &args;
if (function_name)
printk(KERN_DEBUG "[%s:%s], %pV", prefix,
function_name, &vaf);
else
printk(KERN_DEBUG "%pV", &vaf);
va_end(args);
}
printk(KERN_DEBUG "[" DRM_NAME ":%s] %pV", function_name, &vaf);
va_end(args);
}
EXPORT_SYMBOL(drm_ut_debug_printk);
@ -135,8 +132,6 @@ struct drm_master *drm_master_create(struct drm_minor *minor)
INIT_LIST_HEAD(&master->magicfree);
master->minor = minor;
list_add_tail(&master->head, &minor->master_list);
return master;
}
@ -154,8 +149,7 @@ static void drm_master_destroy(struct kref *kref)
struct drm_device *dev = master->minor->dev;
struct drm_map_list *r_list, *list_temp;
list_del(&master->head);
mutex_lock(&dev->struct_mutex);
if (dev->driver->master_destroy)
dev->driver->master_destroy(dev, master);
@ -183,6 +177,7 @@ static void drm_master_destroy(struct kref *kref)
drm_ht_remove(&master->magiclist);
mutex_unlock(&dev->struct_mutex);
kfree(master);
}
@ -198,19 +193,20 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data,
{
int ret = 0;
mutex_lock(&dev->master_mutex);
if (file_priv->is_master)
return 0;
goto out_unlock;
if (file_priv->minor->master && file_priv->minor->master != file_priv->master)
return -EINVAL;
if (file_priv->minor->master) {
ret = -EINVAL;
goto out_unlock;
}
if (!file_priv->master)
return -EINVAL;
if (!file_priv->master) {
ret = -EINVAL;
goto out_unlock;
}
if (file_priv->minor->master)
return -EINVAL;
mutex_lock(&dev->struct_mutex);
file_priv->minor->master = drm_master_get(file_priv->master);
file_priv->is_master = 1;
if (dev->driver->master_set) {
@ -220,27 +216,33 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data,
drm_master_put(&file_priv->minor->master);
}
}
mutex_unlock(&dev->struct_mutex);
out_unlock:
mutex_unlock(&dev->master_mutex);
return ret;
}
int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
int ret = -EINVAL;
mutex_lock(&dev->master_mutex);
if (!file_priv->is_master)
return -EINVAL;
goto out_unlock;
if (!file_priv->minor->master)
return -EINVAL;
goto out_unlock;
mutex_lock(&dev->struct_mutex);
ret = 0;
if (dev->driver->master_drop)
dev->driver->master_drop(dev, file_priv, false);
drm_master_put(&file_priv->minor->master);
file_priv->is_master = 0;
mutex_unlock(&dev->struct_mutex);
return 0;
out_unlock:
mutex_unlock(&dev->master_mutex);
return ret;
}
/*
@ -281,7 +283,6 @@ static int drm_minor_alloc(struct drm_device *dev, unsigned int type)
minor->type = type;
minor->dev = dev;
INIT_LIST_HEAD(&minor->master_list);
*drm_minor_get_slot(dev, type) = minor;
return 0;
@ -572,6 +573,7 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
spin_lock_init(&dev->event_lock);
mutex_init(&dev->struct_mutex);
mutex_init(&dev->ctxlist_mutex);
mutex_init(&dev->master_mutex);
dev->anon_inode = drm_fs_inode_new();
if (IS_ERR(dev->anon_inode)) {
@ -625,6 +627,7 @@ err_minors:
drm_minor_free(dev, DRM_MINOR_CONTROL);
drm_fs_inode_free(dev->anon_inode);
err_free:
mutex_destroy(&dev->master_mutex);
kfree(dev);
return NULL;
}
@ -646,6 +649,8 @@ static void drm_dev_release(struct kref *ref)
drm_minor_free(dev, DRM_MINOR_CONTROL);
kfree(dev->devname);
mutex_destroy(&dev->master_mutex);
kfree(dev);
}

View File

@ -31,6 +31,30 @@ config DRM_EXYNOS_FIMD
help
Choose this option if you want to use Exynos FIMD for DRM.
config DRM_EXYNOS_DPI
bool "EXYNOS DRM parallel output support"
depends on DRM_EXYNOS
select DRM_PANEL
default n
help
This enables support for Exynos parallel output.
config DRM_EXYNOS_DSI
bool "EXYNOS DRM MIPI-DSI driver support"
depends on DRM_EXYNOS
select DRM_MIPI_DSI
select DRM_PANEL
default n
help
This enables support for Exynos MIPI-DSI device.
config DRM_EXYNOS_DP
bool "EXYNOS DRM DP driver support"
depends on DRM_EXYNOS && ARCH_EXYNOS
default DRM_EXYNOS
help
This enables support for DP device.
config DRM_EXYNOS_HDMI
bool "Exynos DRM HDMI"
depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_TV

View File

@ -3,7 +3,7 @@
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/exynos
exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \
exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o \
exynos_drm_crtc.o exynos_drm_fbdev.o exynos_drm_fb.o \
exynos_drm_buf.o exynos_drm_gem.o exynos_drm_core.o \
exynos_drm_plane.o
@ -11,9 +11,10 @@ exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \
exynosdrm-$(CONFIG_DRM_EXYNOS_IOMMU) += exynos_drm_iommu.o
exynosdrm-$(CONFIG_DRM_EXYNOS_DMABUF) += exynos_drm_dmabuf.o
exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o
exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o \
exynos_ddc.o exynos_hdmiphy.o \
exynos_drm_hdmi.o
exynosdrm-$(CONFIG_DRM_EXYNOS_DPI) += exynos_drm_dpi.o
exynosdrm-$(CONFIG_DRM_EXYNOS_DSI) += exynos_drm_dsi.o
exynosdrm-$(CONFIG_DRM_EXYNOS_DP) += exynos_dp_core.o exynos_dp_reg.o
exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o
exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o
exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o
exynosdrm-$(CONFIG_DRM_EXYNOS_IPP) += exynos_drm_ipp.o

View File

@ -12,7 +12,6 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
@ -20,9 +19,25 @@
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <video/of_display_timing.h>
#include <video/of_videomode.h>
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/bridge/ptn3460.h>
#include "exynos_drm_drv.h"
#include "exynos_dp_core.h"
#define ctx_from_connector(c) container_of(c, struct exynos_dp_device, \
connector)
struct bridge_init {
struct i2c_client *client;
struct device_node *node;
};
static int exynos_dp_init_dp(struct exynos_dp_device *dp)
{
exynos_dp_reset(dp);
@ -893,6 +908,214 @@ static void exynos_dp_hotplug(struct work_struct *work)
dev_err(dp->dev, "unable to config video\n");
}
static enum drm_connector_status exynos_dp_detect(
struct drm_connector *connector, bool force)
{
return connector_status_connected;
}
static void exynos_dp_connector_destroy(struct drm_connector *connector)
{
}
static struct drm_connector_funcs exynos_dp_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = exynos_dp_detect,
.destroy = exynos_dp_connector_destroy,
};
static int exynos_dp_get_modes(struct drm_connector *connector)
{
struct exynos_dp_device *dp = ctx_from_connector(connector);
struct drm_display_mode *mode;
mode = drm_mode_create(connector->dev);
if (!mode) {
DRM_ERROR("failed to create a new display mode.\n");
return 0;
}
drm_display_mode_from_videomode(&dp->panel.vm, mode);
mode->width_mm = dp->panel.width_mm;
mode->height_mm = dp->panel.height_mm;
connector->display_info.width_mm = mode->width_mm;
connector->display_info.height_mm = mode->height_mm;
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_set_name(mode);
drm_mode_probed_add(connector, mode);
return 1;
}
static int exynos_dp_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
return MODE_OK;
}
static struct drm_encoder *exynos_dp_best_encoder(
struct drm_connector *connector)
{
struct exynos_dp_device *dp = ctx_from_connector(connector);
return dp->encoder;
}
static struct drm_connector_helper_funcs exynos_dp_connector_helper_funcs = {
.get_modes = exynos_dp_get_modes,
.mode_valid = exynos_dp_mode_valid,
.best_encoder = exynos_dp_best_encoder,
};
static int exynos_dp_initialize(struct exynos_drm_display *display,
struct drm_device *drm_dev)
{
struct exynos_dp_device *dp = display->ctx;
dp->drm_dev = drm_dev;
return 0;
}
static bool find_bridge(const char *compat, struct bridge_init *bridge)
{
bridge->client = NULL;
bridge->node = of_find_compatible_node(NULL, NULL, compat);
if (!bridge->node)
return false;
bridge->client = of_find_i2c_device_by_node(bridge->node);
if (!bridge->client)
return false;
return true;
}
/* returns the number of bridges attached */
static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
struct drm_encoder *encoder)
{
struct bridge_init bridge;
int ret;
if (find_bridge("nxp,ptn3460", &bridge)) {
ret = ptn3460_init(dev, encoder, bridge.client, bridge.node);
if (!ret)
return 1;
}
return 0;
}
static int exynos_dp_create_connector(struct exynos_drm_display *display,
struct drm_encoder *encoder)
{
struct exynos_dp_device *dp = display->ctx;
struct drm_connector *connector = &dp->connector;
int ret;
dp->encoder = encoder;
/* Pre-empt DP connector creation if there's a bridge */
ret = exynos_drm_attach_lcd_bridge(dp->drm_dev, encoder);
if (ret)
return 0;
connector->polled = DRM_CONNECTOR_POLL_HPD;
ret = drm_connector_init(dp->drm_dev, connector,
&exynos_dp_connector_funcs, DRM_MODE_CONNECTOR_eDP);
if (ret) {
DRM_ERROR("Failed to initialize connector with drm\n");
return ret;
}
drm_connector_helper_add(connector, &exynos_dp_connector_helper_funcs);
drm_sysfs_connector_add(connector);
drm_mode_connector_attach_encoder(connector, encoder);
return 0;
}
static void exynos_dp_phy_init(struct exynos_dp_device *dp)
{
if (dp->phy) {
phy_power_on(dp->phy);
} else if (dp->phy_addr) {
u32 reg;
reg = __raw_readl(dp->phy_addr);
reg |= dp->enable_mask;
__raw_writel(reg, dp->phy_addr);
}
}
static void exynos_dp_phy_exit(struct exynos_dp_device *dp)
{
if (dp->phy) {
phy_power_off(dp->phy);
} else if (dp->phy_addr) {
u32 reg;
reg = __raw_readl(dp->phy_addr);
reg &= ~(dp->enable_mask);
__raw_writel(reg, dp->phy_addr);
}
}
static void exynos_dp_poweron(struct exynos_dp_device *dp)
{
if (dp->dpms_mode == DRM_MODE_DPMS_ON)
return;
clk_prepare_enable(dp->clock);
exynos_dp_phy_init(dp);
exynos_dp_init_dp(dp);
enable_irq(dp->irq);
}
static void exynos_dp_poweroff(struct exynos_dp_device *dp)
{
if (dp->dpms_mode != DRM_MODE_DPMS_ON)
return;
disable_irq(dp->irq);
flush_work(&dp->hotplug_work);
exynos_dp_phy_exit(dp);
clk_disable_unprepare(dp->clock);
}
static void exynos_dp_dpms(struct exynos_drm_display *display, int mode)
{
struct exynos_dp_device *dp = display->ctx;
switch (mode) {
case DRM_MODE_DPMS_ON:
exynos_dp_poweron(dp);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
exynos_dp_poweroff(dp);
break;
default:
break;
};
dp->dpms_mode = mode;
}
static struct exynos_drm_display_ops exynos_dp_display_ops = {
.initialize = exynos_dp_initialize,
.create_connector = exynos_dp_create_connector,
.dpms = exynos_dp_dpms,
};
static struct exynos_drm_display exynos_dp_display = {
.type = EXYNOS_DISPLAY_TYPE_LCD,
.ops = &exynos_dp_display_ops,
};
static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev)
{
struct device_node *dp_node = dev->of_node;
@ -994,30 +1217,17 @@ err:
return ret;
}
static void exynos_dp_phy_init(struct exynos_dp_device *dp)
static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp)
{
if (dp->phy) {
phy_power_on(dp->phy);
} else if (dp->phy_addr) {
u32 reg;
int ret;
reg = __raw_readl(dp->phy_addr);
reg |= dp->enable_mask;
__raw_writel(reg, dp->phy_addr);
}
}
static void exynos_dp_phy_exit(struct exynos_dp_device *dp)
{
if (dp->phy) {
phy_power_off(dp->phy);
} else if (dp->phy_addr) {
u32 reg;
reg = __raw_readl(dp->phy_addr);
reg &= ~(dp->enable_mask);
__raw_writel(reg, dp->phy_addr);
ret = of_get_videomode(dp->dev->of_node, &dp->panel.vm,
OF_USE_NATIVE_MODE);
if (ret) {
DRM_ERROR("failed: of_get_videomode() : %d\n", ret);
return ret;
}
return 0;
}
static int exynos_dp_probe(struct platform_device *pdev)
@ -1035,6 +1245,7 @@ static int exynos_dp_probe(struct platform_device *pdev)
}
dp->dev = &pdev->dev;
dp->dpms_mode = DRM_MODE_DPMS_OFF;
dp->video_info = exynos_dp_dt_parse_pdata(&pdev->dev);
if (IS_ERR(dp->video_info))
@ -1044,6 +1255,10 @@ static int exynos_dp_probe(struct platform_device *pdev)
if (ret)
return ret;
ret = exynos_dp_dt_parse_panel(dp);
if (ret)
return ret;
dp->clock = devm_clk_get(&pdev->dev, "dp");
if (IS_ERR(dp->clock)) {
dev_err(&pdev->dev, "failed to get clock\n");
@ -1076,22 +1291,22 @@ static int exynos_dp_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to request irq\n");
return ret;
}
disable_irq(dp->irq);
platform_set_drvdata(pdev, dp);
exynos_dp_display.ctx = dp;
platform_set_drvdata(pdev, &exynos_dp_display);
exynos_drm_display_register(&exynos_dp_display);
return 0;
}
static int exynos_dp_remove(struct platform_device *pdev)
{
struct exynos_dp_device *dp = platform_get_drvdata(pdev);
flush_work(&dp->hotplug_work);
exynos_dp_phy_exit(dp);
clk_disable_unprepare(dp->clock);
struct exynos_drm_display *display = platform_get_drvdata(pdev);
exynos_dp_dpms(display, DRM_MODE_DPMS_OFF);
exynos_drm_display_unregister(&exynos_dp_display);
return 0;
}
@ -1099,31 +1314,19 @@ static int exynos_dp_remove(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP
static int exynos_dp_suspend(struct device *dev)
{
struct exynos_dp_device *dp = dev_get_drvdata(dev);
disable_irq(dp->irq);
flush_work(&dp->hotplug_work);
exynos_dp_phy_exit(dp);
clk_disable_unprepare(dp->clock);
struct platform_device *pdev = to_platform_device(dev);
struct exynos_drm_display *display = platform_get_drvdata(pdev);
exynos_dp_dpms(display, DRM_MODE_DPMS_OFF);
return 0;
}
static int exynos_dp_resume(struct device *dev)
{
struct exynos_dp_device *dp = dev_get_drvdata(dev);
exynos_dp_phy_init(dp);
clk_prepare_enable(dp->clock);
exynos_dp_init_dp(dp);
enable_irq(dp->irq);
struct platform_device *pdev = to_platform_device(dev);
struct exynos_drm_display *display = platform_get_drvdata(pdev);
exynos_dp_dpms(display, DRM_MODE_DPMS_ON);
return 0;
}
#endif
@ -1136,9 +1339,8 @@ static const struct of_device_id exynos_dp_match[] = {
{ .compatible = "samsung,exynos5-dp" },
{},
};
MODULE_DEVICE_TABLE(of, exynos_dp_match);
static struct platform_driver exynos_dp_driver = {
struct platform_driver dp_driver = {
.probe = exynos_dp_probe,
.remove = exynos_dp_remove,
.driver = {
@ -1149,8 +1351,6 @@ static struct platform_driver exynos_dp_driver = {
},
};
module_platform_driver(exynos_dp_driver);
MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
MODULE_DESCRIPTION("Samsung SoC DP Driver");
MODULE_LICENSE("GPL");

View File

@ -13,6 +13,9 @@
#ifndef _EXYNOS_DP_CORE_H
#define _EXYNOS_DP_CORE_H
#include <drm/drm_crtc.h>
#include <drm/exynos_drm.h>
#define DP_TIMEOUT_LOOP_COUNT 100
#define MAX_CR_LOOP 5
#define MAX_EQ_LOOP 5
@ -142,6 +145,9 @@ struct link_train {
struct exynos_dp_device {
struct device *dev;
struct drm_device *drm_dev;
struct drm_connector connector;
struct drm_encoder *encoder;
struct clk *clock;
unsigned int irq;
void __iomem *reg_base;
@ -152,6 +158,9 @@ struct exynos_dp_device {
struct link_train link_train;
struct work_struct hotplug_work;
struct phy *phy;
int dpms_mode;
struct exynos_drm_panel_info panel;
};
/* exynos_dp_reg.c */

View File

@ -23,27 +23,20 @@
drm_connector)
struct exynos_drm_connector {
struct drm_connector drm_connector;
uint32_t encoder_id;
struct exynos_drm_manager *manager;
uint32_t dpms;
struct drm_connector drm_connector;
uint32_t encoder_id;
struct exynos_drm_display *display;
};
static int exynos_drm_connector_get_modes(struct drm_connector *connector)
{
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
struct exynos_drm_manager *manager = exynos_connector->manager;
struct exynos_drm_display_ops *display_ops = manager->display_ops;
struct exynos_drm_display *display = exynos_connector->display;
struct edid *edid = NULL;
unsigned int count = 0;
int ret;
if (!display_ops) {
DRM_DEBUG_KMS("display_ops is null.\n");
return 0;
}
/*
* if get_edid() exists then get_edid() callback of hdmi side
* is called to get edid data through i2c interface else
@ -52,8 +45,8 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
* P.S. in case of lcd panel, count is always 1 if success
* because lcd panel has only one mode.
*/
if (display_ops->get_edid) {
edid = display_ops->get_edid(manager->dev, connector);
if (display->ops->get_edid) {
edid = display->ops->get_edid(display, connector);
if (IS_ERR_OR_NULL(edid)) {
ret = PTR_ERR(edid);
edid = NULL;
@ -76,8 +69,8 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
return 0;
}
if (display_ops->get_panel)
panel = display_ops->get_panel(manager->dev);
if (display->ops->get_panel)
panel = display->ops->get_panel(display);
else {
drm_mode_destroy(connector->dev, mode);
return 0;
@ -106,20 +99,20 @@ static int exynos_drm_connector_mode_valid(struct drm_connector *connector,
{
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
struct exynos_drm_manager *manager = exynos_connector->manager;
struct exynos_drm_display_ops *display_ops = manager->display_ops;
struct exynos_drm_display *display = exynos_connector->display;
int ret = MODE_BAD;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (display_ops && display_ops->check_mode)
if (!display_ops->check_mode(manager->dev, mode))
if (display->ops->check_mode)
if (!display->ops->check_mode(display, mode))
ret = MODE_OK;
return ret;
}
struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector)
static struct drm_encoder *exynos_drm_best_encoder(
struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct exynos_drm_connector *exynos_connector =
@ -146,48 +139,12 @@ static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
.best_encoder = exynos_drm_best_encoder,
};
void exynos_drm_display_power(struct drm_connector *connector, int mode)
{
struct drm_encoder *encoder = exynos_drm_best_encoder(connector);
struct exynos_drm_connector *exynos_connector;
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
struct exynos_drm_display_ops *display_ops = manager->display_ops;
exynos_connector = to_exynos_connector(connector);
if (exynos_connector->dpms == mode) {
DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
return;
}
if (display_ops && display_ops->power_on)
display_ops->power_on(manager->dev, mode);
exynos_connector->dpms = mode;
}
static void exynos_drm_connector_dpms(struct drm_connector *connector,
int mode)
{
/*
* in case that drm_crtc_helper_set_mode() is called,
* encoder/crtc->funcs->dpms() will be just returned
* because they already were DRM_MODE_DPMS_ON so only
* exynos_drm_display_power() will be called.
*/
drm_helper_connector_dpms(connector, mode);
exynos_drm_display_power(connector, mode);
}
static int exynos_drm_connector_fill_modes(struct drm_connector *connector,
unsigned int max_width, unsigned int max_height)
{
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
struct exynos_drm_manager *manager = exynos_connector->manager;
struct exynos_drm_manager_ops *ops = manager->ops;
struct exynos_drm_display *display = exynos_connector->display;
unsigned int width, height;
width = max_width;
@ -197,8 +154,8 @@ static int exynos_drm_connector_fill_modes(struct drm_connector *connector,
* if specific driver want to find desired_mode using maxmum
* resolution then get max width and height from that driver.
*/
if (ops && ops->get_max_resol)
ops->get_max_resol(manager->dev, &width, &height);
if (display->ops->get_max_resol)
display->ops->get_max_resol(display, &width, &height);
return drm_helper_probe_single_connector_modes(connector, width,
height);
@ -210,13 +167,11 @@ exynos_drm_connector_detect(struct drm_connector *connector, bool force)
{
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
struct exynos_drm_manager *manager = exynos_connector->manager;
struct exynos_drm_display_ops *display_ops =
manager->display_ops;
struct exynos_drm_display *display = exynos_connector->display;
enum drm_connector_status status = connector_status_disconnected;
if (display_ops && display_ops->is_connected) {
if (display_ops->is_connected(manager->dev))
if (display->ops->is_connected) {
if (display->ops->is_connected(display))
status = connector_status_connected;
else
status = connector_status_disconnected;
@ -236,7 +191,7 @@ static void exynos_drm_connector_destroy(struct drm_connector *connector)
}
static struct drm_connector_funcs exynos_connector_funcs = {
.dpms = exynos_drm_connector_dpms,
.dpms = drm_helper_connector_dpms,
.fill_modes = exynos_drm_connector_fill_modes,
.detect = exynos_drm_connector_detect,
.destroy = exynos_drm_connector_destroy,
@ -246,7 +201,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
struct drm_encoder *encoder)
{
struct exynos_drm_connector *exynos_connector;
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
struct exynos_drm_display *display = exynos_drm_get_display(encoder);
struct drm_connector *connector;
int type;
int err;
@ -257,7 +212,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
connector = &exynos_connector->drm_connector;
switch (manager->display_ops->type) {
switch (display->type) {
case EXYNOS_DISPLAY_TYPE_HDMI:
type = DRM_MODE_CONNECTOR_HDMIA;
connector->interlace_allowed = true;
@ -280,8 +235,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
goto err_connector;
exynos_connector->encoder_id = encoder->base.id;
exynos_connector->manager = manager;
exynos_connector->dpms = DRM_MODE_DPMS_OFF;
exynos_connector->display = display;
connector->dpms = DRM_MODE_DPMS_OFF;
connector->encoder = encoder;

View File

@ -17,8 +17,4 @@
struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
struct drm_encoder *encoder);
struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector);
void exynos_drm_display_power(struct drm_connector *connector, int mode);
#endif

View File

@ -14,43 +14,42 @@
#include <drm/drmP.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_encoder.h"
#include "exynos_drm_connector.h"
#include "exynos_drm_fbdev.h"
static LIST_HEAD(exynos_drm_subdrv_list);
static LIST_HEAD(exynos_drm_manager_list);
static LIST_HEAD(exynos_drm_display_list);
static int exynos_drm_create_enc_conn(struct drm_device *dev,
struct exynos_drm_subdrv *subdrv)
struct exynos_drm_display *display)
{
struct drm_encoder *encoder;
struct drm_connector *connector;
struct exynos_drm_manager *manager;
int ret;
unsigned long possible_crtcs = 0;
subdrv->manager->dev = subdrv->dev;
/* Find possible crtcs for this display */
list_for_each_entry(manager, &exynos_drm_manager_list, list)
if (manager->type == display->type)
possible_crtcs |= 1 << manager->pipe;
/* create and initialize a encoder for this sub driver. */
encoder = exynos_drm_encoder_create(dev, subdrv->manager,
(1 << MAX_CRTC) - 1);
encoder = exynos_drm_encoder_create(dev, display, possible_crtcs);
if (!encoder) {
DRM_ERROR("failed to create encoder\n");
return -EFAULT;
}
/*
* create and initialize a connector for this sub driver and
* attach the encoder created above to the connector.
*/
connector = exynos_drm_connector_create(dev, encoder);
if (!connector) {
DRM_ERROR("failed to create connector\n");
ret = -EFAULT;
display->encoder = encoder;
ret = display->ops->create_connector(display, encoder);
if (ret) {
DRM_ERROR("failed to create connector ret = %d\n", ret);
goto err_destroy_encoder;
}
subdrv->encoder = encoder;
subdrv->connector = connector;
return 0;
err_destroy_encoder:
@ -58,21 +57,6 @@ err_destroy_encoder:
return ret;
}
static void exynos_drm_destroy_enc_conn(struct exynos_drm_subdrv *subdrv)
{
if (subdrv->encoder) {
struct drm_encoder *encoder = subdrv->encoder;
encoder->funcs->destroy(encoder);
subdrv->encoder = NULL;
}
if (subdrv->connector) {
struct drm_connector *connector = subdrv->connector;
connector->funcs->destroy(connector);
subdrv->connector = NULL;
}
}
static int exynos_drm_subdrv_probe(struct drm_device *dev,
struct exynos_drm_subdrv *subdrv)
{
@ -104,10 +88,98 @@ static void exynos_drm_subdrv_remove(struct drm_device *dev,
subdrv->remove(dev, subdrv->dev);
}
int exynos_drm_initialize_managers(struct drm_device *dev)
{
struct exynos_drm_manager *manager, *n;
int ret, pipe = 0;
list_for_each_entry(manager, &exynos_drm_manager_list, list) {
if (manager->ops->initialize) {
ret = manager->ops->initialize(manager, dev, pipe);
if (ret) {
DRM_ERROR("Mgr init [%d] failed with %d\n",
manager->type, ret);
goto err;
}
}
manager->drm_dev = dev;
manager->pipe = pipe++;
ret = exynos_drm_crtc_create(manager);
if (ret) {
DRM_ERROR("CRTC create [%d] failed with %d\n",
manager->type, ret);
goto err;
}
}
return 0;
err:
list_for_each_entry_safe(manager, n, &exynos_drm_manager_list, list) {
if (pipe-- > 0)
exynos_drm_manager_unregister(manager);
else
list_del(&manager->list);
}
return ret;
}
void exynos_drm_remove_managers(struct drm_device *dev)
{
struct exynos_drm_manager *manager, *n;
list_for_each_entry_safe(manager, n, &exynos_drm_manager_list, list)
exynos_drm_manager_unregister(manager);
}
int exynos_drm_initialize_displays(struct drm_device *dev)
{
struct exynos_drm_display *display, *n;
int ret, initialized = 0;
list_for_each_entry(display, &exynos_drm_display_list, list) {
if (display->ops->initialize) {
ret = display->ops->initialize(display, dev);
if (ret) {
DRM_ERROR("Display init [%d] failed with %d\n",
display->type, ret);
goto err;
}
}
initialized++;
ret = exynos_drm_create_enc_conn(dev, display);
if (ret) {
DRM_ERROR("Encoder create [%d] failed with %d\n",
display->type, ret);
goto err;
}
}
return 0;
err:
list_for_each_entry_safe(display, n, &exynos_drm_display_list, list) {
if (initialized-- > 0)
exynos_drm_display_unregister(display);
else
list_del(&display->list);
}
return ret;
}
void exynos_drm_remove_displays(struct drm_device *dev)
{
struct exynos_drm_display *display, *n;
list_for_each_entry_safe(display, n, &exynos_drm_display_list, list)
exynos_drm_display_unregister(display);
}
int exynos_drm_device_register(struct drm_device *dev)
{
struct exynos_drm_subdrv *subdrv, *n;
unsigned int fine_cnt = 0;
int err;
if (!dev)
@ -120,30 +192,8 @@ int exynos_drm_device_register(struct drm_device *dev)
list_del(&subdrv->list);
continue;
}
/*
* if manager is null then it means that this sub driver
* doesn't need encoder and connector.
*/
if (!subdrv->manager) {
fine_cnt++;
continue;
}
err = exynos_drm_create_enc_conn(dev, subdrv);
if (err) {
DRM_DEBUG("failed to create encoder and connector.\n");
exynos_drm_subdrv_remove(dev, subdrv);
list_del(&subdrv->list);
continue;
}
fine_cnt++;
}
if (!fine_cnt)
return -EINVAL;
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_device_register);
@ -159,13 +209,44 @@ int exynos_drm_device_unregister(struct drm_device *dev)
list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
exynos_drm_subdrv_remove(dev, subdrv);
exynos_drm_destroy_enc_conn(subdrv);
}
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_device_unregister);
int exynos_drm_manager_register(struct exynos_drm_manager *manager)
{
BUG_ON(!manager->ops);
list_add_tail(&manager->list, &exynos_drm_manager_list);
return 0;
}
int exynos_drm_manager_unregister(struct exynos_drm_manager *manager)
{
if (manager->ops->remove)
manager->ops->remove(manager);
list_del(&manager->list);
return 0;
}
int exynos_drm_display_register(struct exynos_drm_display *display)
{
BUG_ON(!display->ops);
list_add_tail(&display->list, &exynos_drm_display_list);
return 0;
}
int exynos_drm_display_unregister(struct exynos_drm_display *display)
{
if (display->ops->remove)
display->ops->remove(display);
list_del(&display->list);
return 0;
}
int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
{
if (!subdrv)

View File

@ -33,6 +33,7 @@ enum exynos_crtc_mode {
*
* @drm_crtc: crtc object.
* @drm_plane: pointer of private plane object for this crtc
* @manager: the manager associated with this crtc
* @pipe: a crtc index created at load() with a new crtc object creation
* and the crtc object would be set to private->crtc array
* to get a crtc object corresponding to this pipe from private->crtc
@ -46,6 +47,7 @@ enum exynos_crtc_mode {
struct exynos_drm_crtc {
struct drm_crtc drm_crtc;
struct drm_plane *plane;
struct exynos_drm_manager *manager;
unsigned int pipe;
unsigned int dpms;
enum exynos_crtc_mode mode;
@ -56,6 +58,7 @@ struct exynos_drm_crtc {
static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct exynos_drm_manager *manager = exynos_crtc->manager;
DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
@ -71,7 +74,9 @@ static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
drm_vblank_off(crtc->dev, exynos_crtc->pipe);
}
exynos_drm_fn_encoder(crtc, &mode, exynos_drm_encoder_crtc_dpms);
if (manager->ops->dpms)
manager->ops->dpms(manager, mode);
exynos_crtc->dpms = mode;
}
@ -83,9 +88,15 @@ static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)
static void exynos_drm_crtc_commit(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct exynos_drm_manager *manager = exynos_crtc->manager;
exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
exynos_plane_commit(exynos_crtc->plane);
if (manager->ops->commit)
manager->ops->commit(manager);
exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_ON);
}
@ -94,7 +105,12 @@ exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
/* drm framework doesn't check NULL */
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct exynos_drm_manager *manager = exynos_crtc->manager;
if (manager->ops->mode_fixup)
return manager->ops->mode_fixup(manager, mode, adjusted_mode);
return true;
}
@ -104,10 +120,10 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_framebuffer *old_fb)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct exynos_drm_manager *manager = exynos_crtc->manager;
struct drm_plane *plane = exynos_crtc->plane;
unsigned int crtc_w;
unsigned int crtc_h;
int pipe = exynos_crtc->pipe;
int ret;
/*
@ -116,18 +132,19 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
*/
memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
crtc_w = crtc->fb->width - x;
crtc_h = crtc->fb->height - y;
crtc_w = crtc->primary->fb->width - x;
crtc_h = crtc->primary->fb->height - y;
ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h,
if (manager->ops->mode_set)
manager->ops->mode_set(manager, &crtc->mode);
ret = exynos_plane_mode_set(plane, crtc, crtc->primary->fb, 0, 0, crtc_w, crtc_h,
x, y, crtc_w, crtc_h);
if (ret)
return ret;
plane->crtc = crtc;
plane->fb = crtc->fb;
exynos_drm_fn_encoder(crtc, &pipe, exynos_drm_encoder_crtc_pipe);
plane->fb = crtc->primary->fb;
return 0;
}
@ -147,10 +164,10 @@ static int exynos_drm_crtc_mode_set_commit(struct drm_crtc *crtc, int x, int y,
return -EPERM;
}
crtc_w = crtc->fb->width - x;
crtc_h = crtc->fb->height - y;
crtc_w = crtc->primary->fb->width - x;
crtc_h = crtc->primary->fb->height - y;
ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h,
ret = exynos_plane_mode_set(plane, crtc, crtc->primary->fb, 0, 0, crtc_w, crtc_h,
x, y, crtc_w, crtc_h);
if (ret)
return ret;
@ -168,10 +185,19 @@ static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
static void exynos_drm_crtc_disable(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct drm_plane *plane;
int ret;
exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_OFF);
exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
drm_for_each_legacy_plane(plane, &crtc->dev->mode_config.plane_list) {
if (plane->crtc != crtc)
continue;
ret = plane->funcs->disable_plane(plane);
if (ret)
DRM_ERROR("Failed to disable plane %d\n", ret);
}
}
static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
@ -192,7 +218,7 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
struct drm_device *dev = crtc->dev;
struct exynos_drm_private *dev_priv = dev->dev_private;
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct drm_framebuffer *old_fb = crtc->fb;
struct drm_framebuffer *old_fb = crtc->primary->fb;
int ret = -EINVAL;
/* when the page flip is requested, crtc's dpms should be on */
@ -223,11 +249,11 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
atomic_set(&exynos_crtc->pending_flip, 1);
spin_unlock_irq(&dev->event_lock);
crtc->fb = fb;
crtc->primary->fb = fb;
ret = exynos_drm_crtc_mode_set_commit(crtc, crtc->x, crtc->y,
NULL);
if (ret) {
crtc->fb = old_fb;
crtc->primary->fb = old_fb;
spin_lock_irq(&dev->event_lock);
drm_vblank_put(dev, exynos_crtc->pipe);
@ -318,21 +344,24 @@ static void exynos_drm_crtc_attach_mode_property(struct drm_crtc *crtc)
drm_object_attach_property(&crtc->base, prop, 0);
}
int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
int exynos_drm_crtc_create(struct exynos_drm_manager *manager)
{
struct exynos_drm_crtc *exynos_crtc;
struct exynos_drm_private *private = dev->dev_private;
struct exynos_drm_private *private = manager->drm_dev->dev_private;
struct drm_crtc *crtc;
exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL);
if (!exynos_crtc)
return -ENOMEM;
exynos_crtc->pipe = nr;
exynos_crtc->dpms = DRM_MODE_DPMS_OFF;
init_waitqueue_head(&exynos_crtc->pending_flip_queue);
atomic_set(&exynos_crtc->pending_flip, 0);
exynos_crtc->plane = exynos_plane_init(dev, 1 << nr, true);
exynos_crtc->dpms = DRM_MODE_DPMS_OFF;
exynos_crtc->manager = manager;
exynos_crtc->pipe = manager->pipe;
exynos_crtc->plane = exynos_plane_init(manager->drm_dev,
1 << manager->pipe, true);
if (!exynos_crtc->plane) {
kfree(exynos_crtc);
return -ENOMEM;
@ -340,9 +369,9 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
crtc = &exynos_crtc->drm_crtc;
private->crtc[nr] = crtc;
private->crtc[manager->pipe] = crtc;
drm_crtc_init(dev, crtc, &exynos_crtc_funcs);
drm_crtc_init(manager->drm_dev, crtc, &exynos_crtc_funcs);
drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs);
exynos_drm_crtc_attach_mode_property(crtc);
@ -350,39 +379,41 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
return 0;
}
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc)
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
{
struct exynos_drm_private *private = dev->dev_private;
struct exynos_drm_crtc *exynos_crtc =
to_exynos_crtc(private->crtc[crtc]);
to_exynos_crtc(private->crtc[pipe]);
struct exynos_drm_manager *manager = exynos_crtc->manager;
if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
return -EPERM;
exynos_drm_fn_encoder(private->crtc[crtc], &crtc,
exynos_drm_enable_vblank);
if (manager->ops->enable_vblank)
manager->ops->enable_vblank(manager);
return 0;
}
void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc)
void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
{
struct exynos_drm_private *private = dev->dev_private;
struct exynos_drm_crtc *exynos_crtc =
to_exynos_crtc(private->crtc[crtc]);
to_exynos_crtc(private->crtc[pipe]);
struct exynos_drm_manager *manager = exynos_crtc->manager;
if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
return;
exynos_drm_fn_encoder(private->crtc[crtc], &crtc,
exynos_drm_disable_vblank);
if (manager->ops->disable_vblank)
manager->ops->disable_vblank(manager);
}
void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc)
void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)
{
struct exynos_drm_private *dev_priv = dev->dev_private;
struct drm_pending_vblank_event *e, *t;
struct drm_crtc *drm_crtc = dev_priv->crtc[crtc];
struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(drm_crtc);
unsigned long flags;
@ -391,15 +422,71 @@ void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc)
list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list,
base.link) {
/* if event's pipe isn't same as crtc then ignore it. */
if (crtc != e->pipe)
if (pipe != e->pipe)
continue;
list_del(&e->base.link);
drm_send_vblank_event(dev, -1, e);
drm_vblank_put(dev, crtc);
drm_vblank_put(dev, pipe);
atomic_set(&exynos_crtc->pending_flip, 0);
wake_up(&exynos_crtc->pending_flip_queue);
}
spin_unlock_irqrestore(&dev->event_lock, flags);
}
void exynos_drm_crtc_plane_mode_set(struct drm_crtc *crtc,
struct exynos_drm_overlay *overlay)
{
struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager;
if (manager->ops->win_mode_set)
manager->ops->win_mode_set(manager, overlay);
}
void exynos_drm_crtc_plane_commit(struct drm_crtc *crtc, int zpos)
{
struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager;
if (manager->ops->win_commit)
manager->ops->win_commit(manager, zpos);
}
void exynos_drm_crtc_plane_enable(struct drm_crtc *crtc, int zpos)
{
struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager;
if (manager->ops->win_enable)
manager->ops->win_enable(manager, zpos);
}
void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos)
{
struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager;
if (manager->ops->win_disable)
manager->ops->win_disable(manager, zpos);
}
void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb)
{
struct exynos_drm_manager *manager;
struct drm_device *dev = fb->dev;
struct drm_crtc *crtc;
/*
* make sure that overlay data are updated to real hardware
* for all encoders.
*/
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
manager = to_exynos_crtc(crtc)->manager;
/*
* wait for vblank interrupt
* - this makes sure that overlay data are updated to
* real hardware.
*/
if (manager->ops->wait_for_vblank)
manager->ops->wait_for_vblank(manager);
}
}

View File

@ -15,9 +15,21 @@
#ifndef _EXYNOS_DRM_CRTC_H_
#define _EXYNOS_DRM_CRTC_H_
int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr);
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc);
void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc);
void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc);
struct drm_device;
struct drm_crtc;
struct exynos_drm_manager;
struct exynos_drm_overlay;
int exynos_drm_crtc_create(struct exynos_drm_manager *manager);
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe);
void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe);
void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe);
void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb);
void exynos_drm_crtc_plane_mode_set(struct drm_crtc *crtc,
struct exynos_drm_overlay *overlay);
void exynos_drm_crtc_plane_commit(struct drm_crtc *crtc, int zpos);
void exynos_drm_crtc_plane_enable(struct drm_crtc *crtc, int zpos);
void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos);
#endif

View File

@ -0,0 +1,339 @@
/*
* Exynos DRM Parallel output support.
*
* Copyright (c) 2014 Samsung Electronics Co., Ltd
*
* Contacts: Andrzej Hajda <a.hajda@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_panel.h>
#include <linux/regulator/consumer.h>
#include <video/of_videomode.h>
#include <video/videomode.h>
#include "exynos_drm_drv.h"
struct exynos_dpi {
struct device *dev;
struct device_node *panel_node;
struct drm_panel *panel;
struct drm_connector connector;
struct drm_encoder *encoder;
struct videomode *vm;
int dpms_mode;
};
#define connector_to_dpi(c) container_of(c, struct exynos_dpi, connector)
static enum drm_connector_status
exynos_dpi_detect(struct drm_connector *connector, bool force)
{
struct exynos_dpi *ctx = connector_to_dpi(connector);
/* panels supported only by boot-loader are always connected */
if (!ctx->panel_node)
return connector_status_connected;
if (!ctx->panel) {
ctx->panel = of_drm_find_panel(ctx->panel_node);
if (ctx->panel)
drm_panel_attach(ctx->panel, &ctx->connector);
}
if (ctx->panel)
return connector_status_connected;
return connector_status_disconnected;
}
static void exynos_dpi_connector_destroy(struct drm_connector *connector)
{
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
}
static struct drm_connector_funcs exynos_dpi_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.detect = exynos_dpi_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = exynos_dpi_connector_destroy,
};
static int exynos_dpi_get_modes(struct drm_connector *connector)
{
struct exynos_dpi *ctx = connector_to_dpi(connector);
/* fimd timings gets precedence over panel modes */
if (ctx->vm) {
struct drm_display_mode *mode;
mode = drm_mode_create(connector->dev);
if (!mode) {
DRM_ERROR("failed to create a new display mode\n");
return 0;
}
drm_display_mode_from_videomode(ctx->vm, mode);
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(connector, mode);
return 1;
}
if (ctx->panel)
return ctx->panel->funcs->get_modes(ctx->panel);
return 0;
}
static int exynos_dpi_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
return MODE_OK;
}
static struct drm_encoder *
exynos_dpi_best_encoder(struct drm_connector *connector)
{
struct exynos_dpi *ctx = connector_to_dpi(connector);
return ctx->encoder;
}
static struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = {
.get_modes = exynos_dpi_get_modes,
.mode_valid = exynos_dpi_mode_valid,
.best_encoder = exynos_dpi_best_encoder,
};
static int exynos_dpi_create_connector(struct exynos_drm_display *display,
struct drm_encoder *encoder)
{
struct exynos_dpi *ctx = display->ctx;
struct drm_connector *connector = &ctx->connector;
int ret;
ctx->encoder = encoder;
if (ctx->panel_node)
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
else
connector->polled = DRM_CONNECTOR_POLL_HPD;
ret = drm_connector_init(encoder->dev, connector,
&exynos_dpi_connector_funcs,
DRM_MODE_CONNECTOR_VGA);
if (ret) {
DRM_ERROR("failed to initialize connector with drm\n");
return ret;
}
drm_connector_helper_add(connector, &exynos_dpi_connector_helper_funcs);
drm_sysfs_connector_add(connector);
drm_mode_connector_attach_encoder(connector, encoder);
return 0;
}
static void exynos_dpi_poweron(struct exynos_dpi *ctx)
{
if (ctx->panel)
drm_panel_enable(ctx->panel);
}
static void exynos_dpi_poweroff(struct exynos_dpi *ctx)
{
if (ctx->panel)
drm_panel_disable(ctx->panel);
}
static void exynos_dpi_dpms(struct exynos_drm_display *display, int mode)
{
struct exynos_dpi *ctx = display->ctx;
switch (mode) {
case DRM_MODE_DPMS_ON:
if (ctx->dpms_mode != DRM_MODE_DPMS_ON)
exynos_dpi_poweron(ctx);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
if (ctx->dpms_mode == DRM_MODE_DPMS_ON)
exynos_dpi_poweroff(ctx);
break;
default:
break;
};
ctx->dpms_mode = mode;
}
static struct exynos_drm_display_ops exynos_dpi_display_ops = {
.create_connector = exynos_dpi_create_connector,
.dpms = exynos_dpi_dpms
};
static struct exynos_drm_display exynos_dpi_display = {
.type = EXYNOS_DISPLAY_TYPE_LCD,
.ops = &exynos_dpi_display_ops,
};
/* of_* functions will be removed after merge of of_graph patches */
static struct device_node *
of_get_child_by_name_reg(struct device_node *parent, const char *name, u32 reg)
{
struct device_node *np;
for_each_child_of_node(parent, np) {
u32 r;
if (!np->name || of_node_cmp(np->name, name))
continue;
if (of_property_read_u32(np, "reg", &r) < 0)
r = 0;
if (reg == r)
break;
}
return np;
}
static struct device_node *of_graph_get_port_by_reg(struct device_node *parent,
u32 reg)
{
struct device_node *ports, *port;
ports = of_get_child_by_name(parent, "ports");
if (ports)
parent = ports;
port = of_get_child_by_name_reg(parent, "port", reg);
of_node_put(ports);
return port;
}
static struct device_node *
of_graph_get_endpoint_by_reg(struct device_node *port, u32 reg)
{
return of_get_child_by_name_reg(port, "endpoint", reg);
}
static struct device_node *
of_graph_get_remote_port_parent(const struct device_node *node)
{
struct device_node *np;
unsigned int depth;
np = of_parse_phandle(node, "remote-endpoint", 0);
/* Walk 3 levels up only if there is 'ports' node. */
for (depth = 3; depth && np; depth--) {
np = of_get_next_parent(np);
if (depth == 2 && of_node_cmp(np->name, "ports"))
break;
}
return np;
}
enum {
FIMD_PORT_IN0,
FIMD_PORT_IN1,
FIMD_PORT_IN2,
FIMD_PORT_RGB,
FIMD_PORT_WRB,
};
static struct device_node *exynos_dpi_of_find_panel_node(struct device *dev)
{
struct device_node *np, *ep;
np = of_graph_get_port_by_reg(dev->of_node, FIMD_PORT_RGB);
if (!np)
return NULL;
ep = of_graph_get_endpoint_by_reg(np, 0);
of_node_put(np);
if (!ep)
return NULL;
np = of_graph_get_remote_port_parent(ep);
of_node_put(ep);
return np;
}
static int exynos_dpi_parse_dt(struct exynos_dpi *ctx)
{
struct device *dev = ctx->dev;
struct device_node *dn = dev->of_node;
struct device_node *np;
ctx->panel_node = exynos_dpi_of_find_panel_node(dev);
np = of_get_child_by_name(dn, "display-timings");
if (np) {
struct videomode *vm;
int ret;
of_node_put(np);
vm = devm_kzalloc(dev, sizeof(*ctx->vm), GFP_KERNEL);
if (!vm)
return -ENOMEM;
ret = of_get_videomode(dn, vm, 0);
if (ret < 0)
return ret;
ctx->vm = vm;
return 0;
}
if (!ctx->panel_node)
return -EINVAL;
return 0;
}
int exynos_dpi_probe(struct device *dev)
{
struct exynos_dpi *ctx;
int ret;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->dev = dev;
exynos_dpi_display.ctx = ctx;
ctx->dpms_mode = DRM_MODE_DPMS_OFF;
ret = exynos_dpi_parse_dt(ctx);
if (ret < 0)
return ret;
exynos_drm_display_register(&exynos_dpi_display);
return 0;
}
int exynos_dpi_remove(struct device *dev)
{
exynos_dpi_dpms(&exynos_dpi_display, DRM_MODE_DPMS_OFF);
exynos_drm_display_unregister(&exynos_dpi_display);
return 0;
}

View File

@ -11,6 +11,7 @@
* option) any later version.
*/
#include <linux/pm_runtime.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
@ -53,6 +54,7 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
return -ENOMEM;
INIT_LIST_HEAD(&private->pageflip_event_list);
dev_set_drvdata(dev->dev, dev);
dev->dev_private = (void *)private;
/*
@ -64,38 +66,36 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
ret = drm_create_iommu_mapping(dev);
if (ret < 0) {
DRM_ERROR("failed to create iommu mapping.\n");
goto err_crtc;
goto err_free_private;
}
drm_mode_config_init(dev);
/* init kms poll for handling hpd */
drm_kms_helper_poll_init(dev);
exynos_drm_mode_config_init(dev);
/*
* EXYNOS4 is enough to have two CRTCs and each crtc would be used
* without dependency of hardware.
*/
for (nr = 0; nr < MAX_CRTC; nr++) {
ret = exynos_drm_crtc_create(dev, nr);
if (ret)
goto err_release_iommu_mapping;
}
ret = exynos_drm_initialize_managers(dev);
if (ret)
goto err_mode_config_cleanup;
for (nr = 0; nr < MAX_PLANE; nr++) {
struct drm_plane *plane;
unsigned int possible_crtcs = (1 << MAX_CRTC) - 1;
unsigned long possible_crtcs = (1 << MAX_CRTC) - 1;
plane = exynos_plane_init(dev, possible_crtcs, false);
if (!plane)
goto err_release_iommu_mapping;
goto err_manager_cleanup;
}
ret = exynos_drm_initialize_displays(dev);
if (ret)
goto err_manager_cleanup;
/* init kms poll for handling hpd */
drm_kms_helper_poll_init(dev);
ret = drm_vblank_init(dev, MAX_CRTC);
if (ret)
goto err_release_iommu_mapping;
goto err_display_cleanup;
/*
* probe sub drivers such as display controller and hdmi driver,
@ -109,30 +109,25 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
/* setup possible_clones. */
exynos_drm_encoder_setup(dev);
/*
* create and configure fb helper and also exynos specific
* fbdev object.
*/
ret = exynos_drm_fbdev_init(dev);
if (ret) {
DRM_ERROR("failed to initialize drm fbdev\n");
goto err_drm_device;
}
drm_vblank_offdelay = VBLANK_OFF_DELAY;
platform_set_drvdata(dev->platformdev, dev);
/* force connectors detection */
drm_helper_hpd_irq_event(dev);
return 0;
err_drm_device:
exynos_drm_device_unregister(dev);
err_vblank:
drm_vblank_cleanup(dev);
err_release_iommu_mapping:
drm_release_iommu_mapping(dev);
err_crtc:
err_display_cleanup:
exynos_drm_remove_displays(dev);
err_manager_cleanup:
exynos_drm_remove_managers(dev);
err_mode_config_cleanup:
drm_mode_config_cleanup(dev);
drm_release_iommu_mapping(dev);
err_free_private:
kfree(private);
return ret;
@ -144,6 +139,8 @@ static int exynos_drm_unload(struct drm_device *dev)
exynos_drm_device_unregister(dev);
drm_vblank_cleanup(dev);
drm_kms_helper_poll_fini(dev);
exynos_drm_remove_displays(dev);
exynos_drm_remove_managers(dev);
drm_mode_config_cleanup(dev);
drm_release_iommu_mapping(dev);
@ -158,6 +155,41 @@ static const struct file_operations exynos_drm_gem_fops = {
.mmap = exynos_drm_gem_mmap_buffer,
};
static int exynos_drm_suspend(struct drm_device *dev, pm_message_t state)
{
struct drm_connector *connector;
drm_modeset_lock_all(dev);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
int old_dpms = connector->dpms;
if (connector->funcs->dpms)
connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
/* Set the old mode back to the connector for resume */
connector->dpms = old_dpms;
}
drm_modeset_unlock_all(dev);
return 0;
}
static int exynos_drm_resume(struct drm_device *dev)
{
struct drm_connector *connector;
drm_modeset_lock_all(dev);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->funcs->dpms)
connector->funcs->dpms(connector, connector->dpms);
}
drm_helper_resume_force_mode(dev);
drm_modeset_unlock_all(dev);
return 0;
}
static int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
{
struct drm_exynos_file_private *file_priv;
@ -295,6 +327,8 @@ static struct drm_driver exynos_drm_driver = {
DRIVER_GEM | DRIVER_PRIME,
.load = exynos_drm_load,
.unload = exynos_drm_unload,
.suspend = exynos_drm_suspend,
.resume = exynos_drm_resume,
.open = exynos_drm_open,
.preclose = exynos_drm_preclose,
.lastclose = exynos_drm_lastclose,
@ -329,6 +363,9 @@ static int exynos_drm_platform_probe(struct platform_device *pdev)
if (ret)
return ret;
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
return drm_platform_init(&exynos_drm_driver, pdev);
}
@ -339,12 +376,67 @@ static int exynos_drm_platform_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int exynos_drm_sys_suspend(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
pm_message_t message;
if (pm_runtime_suspended(dev))
return 0;
message.event = PM_EVENT_SUSPEND;
return exynos_drm_suspend(drm_dev, message);
}
static int exynos_drm_sys_resume(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
if (pm_runtime_suspended(dev))
return 0;
return exynos_drm_resume(drm_dev);
}
#endif
#ifdef CONFIG_PM_RUNTIME
static int exynos_drm_runtime_suspend(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
pm_message_t message;
if (pm_runtime_suspended(dev))
return 0;
message.event = PM_EVENT_SUSPEND;
return exynos_drm_suspend(drm_dev, message);
}
static int exynos_drm_runtime_resume(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
if (!pm_runtime_suspended(dev))
return 0;
return exynos_drm_resume(drm_dev);
}
#endif
static const struct dev_pm_ops exynos_drm_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(exynos_drm_sys_suspend, exynos_drm_sys_resume)
SET_RUNTIME_PM_OPS(exynos_drm_runtime_suspend,
exynos_drm_runtime_resume, NULL)
};
static struct platform_driver exynos_drm_platform_driver = {
.probe = exynos_drm_platform_probe,
.remove = exynos_drm_platform_remove,
.driver = {
.owner = THIS_MODULE,
.name = "exynos-drm",
.pm = &exynos_drm_pm_ops,
},
};
@ -352,6 +444,18 @@ static int __init exynos_drm_init(void)
{
int ret;
#ifdef CONFIG_DRM_EXYNOS_DP
ret = platform_driver_register(&dp_driver);
if (ret < 0)
goto out_dp;
#endif
#ifdef CONFIG_DRM_EXYNOS_DSI
ret = platform_driver_register(&dsi_driver);
if (ret < 0)
goto out_dsi;
#endif
#ifdef CONFIG_DRM_EXYNOS_FIMD
ret = platform_driver_register(&fimd_driver);
if (ret < 0)
@ -365,13 +469,6 @@ static int __init exynos_drm_init(void)
ret = platform_driver_register(&mixer_driver);
if (ret < 0)
goto out_mixer;
ret = platform_driver_register(&exynos_drm_common_hdmi_driver);
if (ret < 0)
goto out_common_hdmi;
ret = exynos_platform_device_hdmi_register();
if (ret < 0)
goto out_common_hdmi_dev;
#endif
#ifdef CONFIG_DRM_EXYNOS_VIDI
@ -464,10 +561,6 @@ out_vidi:
#endif
#ifdef CONFIG_DRM_EXYNOS_HDMI
exynos_platform_device_hdmi_unregister();
out_common_hdmi_dev:
platform_driver_unregister(&exynos_drm_common_hdmi_driver);
out_common_hdmi:
platform_driver_unregister(&mixer_driver);
out_mixer:
platform_driver_unregister(&hdmi_driver);
@ -477,6 +570,16 @@ out_hdmi:
#ifdef CONFIG_DRM_EXYNOS_FIMD
platform_driver_unregister(&fimd_driver);
out_fimd:
#endif
#ifdef CONFIG_DRM_EXYNOS_DSI
platform_driver_unregister(&dsi_driver);
out_dsi:
#endif
#ifdef CONFIG_DRM_EXYNOS_DP
platform_driver_unregister(&dp_driver);
out_dp:
#endif
return ret;
}
@ -509,8 +612,6 @@ static void __exit exynos_drm_exit(void)
#endif
#ifdef CONFIG_DRM_EXYNOS_HDMI
exynos_platform_device_hdmi_unregister();
platform_driver_unregister(&exynos_drm_common_hdmi_driver);
platform_driver_unregister(&mixer_driver);
platform_driver_unregister(&hdmi_driver);
#endif
@ -522,6 +623,14 @@ static void __exit exynos_drm_exit(void)
#ifdef CONFIG_DRM_EXYNOS_FIMD
platform_driver_unregister(&fimd_driver);
#endif
#ifdef CONFIG_DRM_EXYNOS_DSI
platform_driver_unregister(&dsi_driver);
#endif
#ifdef CONFIG_DRM_EXYNOS_DP
platform_driver_unregister(&dp_driver);
#endif
}
module_init(exynos_drm_init);

View File

@ -53,22 +53,6 @@ enum exynos_drm_output_type {
EXYNOS_DISPLAY_TYPE_VIDI,
};
/*
* Exynos drm overlay ops structure.
*
* @mode_set: copy drm overlay info to hw specific overlay info.
* @commit: apply hardware specific overlay data to registers.
* @enable: enable hardware specific overlay.
* @disable: disable hardware specific overlay.
*/
struct exynos_drm_overlay_ops {
void (*mode_set)(struct device *subdrv_dev,
struct exynos_drm_overlay *overlay);
void (*commit)(struct device *subdrv_dev, int zpos);
void (*enable)(struct device *subdrv_dev, int zpos);
void (*disable)(struct device *subdrv_dev, int zpos);
};
/*
* Exynos drm common overlay structure.
*
@ -138,77 +122,110 @@ struct exynos_drm_overlay {
* Exynos DRM Display Structure.
* - this structure is common to analog tv, digital tv and lcd panel.
*
* @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI.
* @is_connected: check for that display is connected or not.
* @get_edid: get edid modes from display driver.
* @get_panel: get panel object from display driver.
* @initialize: initializes the display with drm_dev
* @remove: cleans up the display for removal
* @mode_fixup: fix mode data comparing to hw specific display mode.
* @mode_set: convert drm_display_mode to hw specific display mode and
* would be called by encoder->mode_set().
* @check_mode: check if mode is valid or not.
* @power_on: display device on or off.
* @dpms: display device on or off.
* @commit: apply changes to hw
*/
struct exynos_drm_display;
struct exynos_drm_display_ops {
int (*initialize)(struct exynos_drm_display *display,
struct drm_device *drm_dev);
int (*create_connector)(struct exynos_drm_display *display,
struct drm_encoder *encoder);
void (*remove)(struct exynos_drm_display *display);
void (*mode_fixup)(struct exynos_drm_display *display,
struct drm_connector *connector,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
void (*mode_set)(struct exynos_drm_display *display,
struct drm_display_mode *mode);
int (*check_mode)(struct exynos_drm_display *display,
struct drm_display_mode *mode);
void (*dpms)(struct exynos_drm_display *display, int mode);
void (*commit)(struct exynos_drm_display *display);
};
/*
* Exynos drm display structure, maps 1:1 with an encoder/connector
*
* @list: the list entry for this manager
* @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI.
* @encoder: encoder object this display maps to
* @connector: connector object this display maps to
* @ops: pointer to callbacks for exynos drm specific functionality
* @ctx: A pointer to the display's implementation specific context
*/
struct exynos_drm_display {
struct list_head list;
enum exynos_drm_output_type type;
bool (*is_connected)(struct device *dev);
struct edid *(*get_edid)(struct device *dev,
struct drm_connector *connector);
void *(*get_panel)(struct device *dev);
int (*check_mode)(struct device *dev, struct drm_display_mode *mode);
int (*power_on)(struct device *dev, int mode);
struct drm_encoder *encoder;
struct drm_connector *connector;
struct exynos_drm_display_ops *ops;
void *ctx;
};
/*
* Exynos drm manager ops
*
* @initialize: initializes the manager with drm_dev
* @remove: cleans up the manager for removal
* @dpms: control device power.
* @apply: set timing, vblank and overlay data to registers.
* @mode_fixup: fix mode data comparing to hw specific display mode.
* @mode_set: convert drm_display_mode to hw specific display mode and
* would be called by encoder->mode_set().
* @get_max_resol: get maximum resolution to specific hardware.
* @mode_fixup: fix mode data before applying it
* @mode_set: set the given mode to the manager
* @commit: set current hw specific display mode to hw.
* @enable_vblank: specific driver callback for enabling vblank interrupt.
* @disable_vblank: specific driver callback for disabling vblank interrupt.
* @wait_for_vblank: wait for vblank interrupt to make sure that
* hardware overlay is updated.
* @win_mode_set: copy drm overlay info to hw specific overlay info.
* @win_commit: apply hardware specific overlay data to registers.
* @win_enable: enable hardware specific overlay.
* @win_disable: disable hardware specific overlay.
*/
struct exynos_drm_manager;
struct exynos_drm_manager_ops {
void (*dpms)(struct device *subdrv_dev, int mode);
void (*apply)(struct device *subdrv_dev);
void (*mode_fixup)(struct device *subdrv_dev,
struct drm_connector *connector,
int (*initialize)(struct exynos_drm_manager *mgr,
struct drm_device *drm_dev, int pipe);
void (*remove)(struct exynos_drm_manager *mgr);
void (*dpms)(struct exynos_drm_manager *mgr, int mode);
bool (*mode_fixup)(struct exynos_drm_manager *mgr,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
void (*mode_set)(struct device *subdrv_dev, void *mode);
void (*get_max_resol)(struct device *subdrv_dev, unsigned int *width,
unsigned int *height);
void (*commit)(struct device *subdrv_dev);
int (*enable_vblank)(struct device *subdrv_dev);
void (*disable_vblank)(struct device *subdrv_dev);
void (*wait_for_vblank)(struct device *subdrv_dev);
void (*mode_set)(struct exynos_drm_manager *mgr,
const struct drm_display_mode *mode);
void (*commit)(struct exynos_drm_manager *mgr);
int (*enable_vblank)(struct exynos_drm_manager *mgr);
void (*disable_vblank)(struct exynos_drm_manager *mgr);
void (*wait_for_vblank)(struct exynos_drm_manager *mgr);
void (*win_mode_set)(struct exynos_drm_manager *mgr,
struct exynos_drm_overlay *overlay);
void (*win_commit)(struct exynos_drm_manager *mgr, int zpos);
void (*win_enable)(struct exynos_drm_manager *mgr, int zpos);
void (*win_disable)(struct exynos_drm_manager *mgr, int zpos);
};
/*
* Exynos drm common manager structure.
* Exynos drm common manager structure, maps 1:1 with a crtc
*
* @dev: pointer to device object for subdrv device driver.
* sub drivers such as display controller or hdmi driver,
* have their own device object.
* @ops: pointer to callbacks for exynos drm specific framebuffer.
* these callbacks should be set by specific drivers such fimd
* or hdmi driver and are used to control hardware global registers.
* @overlay_ops: pointer to callbacks for exynos drm specific framebuffer.
* these callbacks should be set by specific drivers such fimd
* or hdmi driver and are used to control hardware overlay reigsters.
* @display: pointer to callbacks for exynos drm specific framebuffer.
* these callbacks should be set by specific drivers such fimd
* or hdmi driver and are used to control display devices such as
* analog tv, digital tv and lcd panel and also get timing data for them.
* @list: the list entry for this manager
* @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI.
* @drm_dev: pointer to the drm device
* @pipe: the pipe number for this crtc/manager
* @ops: pointer to callbacks for exynos drm specific functionality
* @ctx: A pointer to the manager's implementation specific context
*/
struct exynos_drm_manager {
struct device *dev;
struct list_head list;
enum exynos_drm_output_type type;
struct drm_device *drm_dev;
int pipe;
struct exynos_drm_manager_ops *ops;
struct exynos_drm_overlay_ops *overlay_ops;
struct exynos_drm_display_ops *display_ops;
void *ctx;
};
struct exynos_drm_g2d_private {
@ -273,14 +290,11 @@ struct exynos_drm_private {
* by probe callback.
* @open: this would be called with drm device file open.
* @close: this would be called with drm device file close.
* @encoder: encoder object owned by this sub driver.
* @connector: connector object owned by this sub driver.
*/
struct exynos_drm_subdrv {
struct list_head list;
struct device *dev;
struct drm_device *drm_dev;
struct exynos_drm_manager *manager;
int (*probe)(struct drm_device *drm_dev, struct device *dev);
void (*remove)(struct drm_device *drm_dev, struct device *dev);
@ -288,9 +302,6 @@ struct exynos_drm_subdrv {
struct drm_file *file);
void (*close)(struct drm_device *drm_dev, struct device *dev,
struct drm_file *file);
struct drm_encoder *encoder;
struct drm_connector *connector;
};
/*
@ -305,6 +316,16 @@ int exynos_drm_device_register(struct drm_device *dev);
*/
int exynos_drm_device_unregister(struct drm_device *dev);
int exynos_drm_initialize_managers(struct drm_device *dev);
void exynos_drm_remove_managers(struct drm_device *dev);
int exynos_drm_initialize_displays(struct drm_device *dev);
void exynos_drm_remove_displays(struct drm_device *dev);
int exynos_drm_manager_register(struct exynos_drm_manager *manager);
int exynos_drm_manager_unregister(struct exynos_drm_manager *manager);
int exynos_drm_display_register(struct exynos_drm_display *display);
int exynos_drm_display_unregister(struct exynos_drm_display *display);
/*
* this function would be called by sub drivers such as display controller
* or hdmi driver to register this sub driver object to exynos drm driver
@ -340,6 +361,16 @@ int exynos_platform_device_ipp_register(void);
*/
void exynos_platform_device_ipp_unregister(void);
#ifdef CONFIG_DRM_EXYNOS_DPI
int exynos_dpi_probe(struct device *dev);
int exynos_dpi_remove(struct device *dev);
#else
static inline int exynos_dpi_probe(struct device *dev) { return 0; }
static inline int exynos_dpi_remove(struct device *dev) { return 0; }
#endif
extern struct platform_driver dp_driver;
extern struct platform_driver dsi_driver;
extern struct platform_driver fimd_driver;
extern struct platform_driver hdmi_driver;
extern struct platform_driver mixer_driver;

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,6 @@
#include "exynos_drm_drv.h"
#include "exynos_drm_encoder.h"
#include "exynos_drm_connector.h"
#define to_exynos_encoder(x) container_of(x, struct exynos_drm_encoder,\
drm_encoder)
@ -26,72 +25,22 @@
* exynos specific encoder structure.
*
* @drm_encoder: encoder object.
* @manager: specific encoder has its own manager to control a hardware
* appropriately and we can access a hardware drawing on this manager.
* @dpms: store the encoder dpms value.
* @updated: indicate whether overlay data updating is needed or not.
* @display: the display structure that maps to this encoder
*/
struct exynos_drm_encoder {
struct drm_crtc *old_crtc;
struct drm_encoder drm_encoder;
struct exynos_drm_manager *manager;
int dpms;
bool updated;
struct exynos_drm_display *display;
};
static void exynos_drm_connector_power(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct drm_connector *connector;
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (exynos_drm_best_encoder(connector) == encoder) {
DRM_DEBUG_KMS("connector[%d] dpms[%d]\n",
connector->base.id, mode);
exynos_drm_display_power(connector, mode);
}
}
}
static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
struct exynos_drm_manager_ops *manager_ops = manager->ops;
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
struct exynos_drm_display *display = exynos_encoder->display;
DRM_DEBUG_KMS("encoder dpms: %d\n", mode);
if (exynos_encoder->dpms == mode) {
DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
return;
}
mutex_lock(&dev->struct_mutex);
switch (mode) {
case DRM_MODE_DPMS_ON:
if (manager_ops && manager_ops->apply)
if (!exynos_encoder->updated)
manager_ops->apply(manager->dev);
exynos_drm_connector_power(encoder, mode);
exynos_encoder->dpms = mode;
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
exynos_drm_connector_power(encoder, mode);
exynos_encoder->dpms = mode;
exynos_encoder->updated = false;
break;
default:
DRM_ERROR("unspecified mode %d\n", mode);
break;
}
mutex_unlock(&dev->struct_mutex);
if (display->ops->dpms)
display->ops->dpms(display, mode);
}
static bool
@ -100,87 +49,31 @@ exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
struct exynos_drm_display *display = exynos_encoder->display;
struct drm_connector *connector;
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
struct exynos_drm_manager_ops *manager_ops = manager->ops;
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->encoder == encoder)
if (manager_ops && manager_ops->mode_fixup)
manager_ops->mode_fixup(manager->dev, connector,
mode, adjusted_mode);
if (connector->encoder != encoder)
continue;
if (display->ops->mode_fixup)
display->ops->mode_fixup(display, connector, mode,
adjusted_mode);
}
return true;
}
static void disable_plane_to_crtc(struct drm_device *dev,
struct drm_crtc *old_crtc,
struct drm_crtc *new_crtc)
{
struct drm_plane *plane;
/*
* if old_crtc isn't same as encoder->crtc then it means that
* user changed crtc id to another one so the plane to old_crtc
* should be disabled and plane->crtc should be set to new_crtc
* (encoder->crtc)
*/
list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
if (plane->crtc == old_crtc) {
/*
* do not change below call order.
*
* plane->funcs->disable_plane call checks
* if encoder->crtc is same as plane->crtc and if same
* then overlay_ops->disable callback will be called
* to diasble current hw overlay so plane->crtc should
* have new_crtc because new_crtc was set to
* encoder->crtc in advance.
*/
plane->crtc = new_crtc;
plane->funcs->disable_plane(plane);
}
}
}
static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
struct drm_connector *connector;
struct exynos_drm_manager *manager;
struct exynos_drm_manager_ops *manager_ops;
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
struct exynos_drm_display *display = exynos_encoder->display;
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->encoder == encoder) {
struct exynos_drm_encoder *exynos_encoder;
exynos_encoder = to_exynos_encoder(encoder);
if (exynos_encoder->old_crtc != encoder->crtc &&
exynos_encoder->old_crtc) {
/*
* disable a plane to old crtc and change
* crtc of the plane to new one.
*/
disable_plane_to_crtc(dev,
exynos_encoder->old_crtc,
encoder->crtc);
}
manager = exynos_drm_get_manager(encoder);
manager_ops = manager->ops;
if (manager_ops && manager_ops->mode_set)
manager_ops->mode_set(manager->dev,
adjusted_mode);
exynos_encoder->old_crtc = encoder->crtc;
}
}
if (display->ops->mode_set)
display->ops->mode_set(display, adjusted_mode);
}
static void exynos_drm_encoder_prepare(struct drm_encoder *encoder)
@ -191,53 +84,15 @@ static void exynos_drm_encoder_prepare(struct drm_encoder *encoder)
static void exynos_drm_encoder_commit(struct drm_encoder *encoder)
{
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
struct exynos_drm_manager *manager = exynos_encoder->manager;
struct exynos_drm_manager_ops *manager_ops = manager->ops;
struct exynos_drm_display *display = exynos_encoder->display;
if (manager_ops && manager_ops->commit)
manager_ops->commit(manager->dev);
if (display->ops->dpms)
display->ops->dpms(display, DRM_MODE_DPMS_ON);
/*
* this will avoid one issue that overlay data is updated to
* real hardware two times.
* And this variable will be used to check if the data was
* already updated or not by exynos_drm_encoder_dpms function.
*/
exynos_encoder->updated = true;
/*
* In case of setcrtc, there is no way to update encoder's dpms
* so update it here.
*/
exynos_encoder->dpms = DRM_MODE_DPMS_ON;
if (display->ops->commit)
display->ops->commit(display);
}
void exynos_drm_encoder_complete_scanout(struct drm_framebuffer *fb)
{
struct exynos_drm_encoder *exynos_encoder;
struct exynos_drm_manager_ops *ops;
struct drm_device *dev = fb->dev;
struct drm_encoder *encoder;
/*
* make sure that overlay data are updated to real hardware
* for all encoders.
*/
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
exynos_encoder = to_exynos_encoder(encoder);
ops = exynos_encoder->manager->ops;
/*
* wait for vblank interrupt
* - this makes sure that overlay data are updated to
* real hardware.
*/
if (ops->wait_for_vblank)
ops->wait_for_vblank(exynos_encoder->manager->dev);
}
}
static void exynos_drm_encoder_disable(struct drm_encoder *encoder)
{
struct drm_plane *plane;
@ -246,7 +101,7 @@ static void exynos_drm_encoder_disable(struct drm_encoder *encoder)
exynos_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
/* all planes connected to this encoder should be also disabled. */
list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
drm_for_each_legacy_plane(plane, &dev->mode_config.plane_list) {
if (plane->crtc == encoder->crtc)
plane->funcs->disable_plane(plane);
}
@ -263,10 +118,7 @@ static struct drm_encoder_helper_funcs exynos_encoder_helper_funcs = {
static void exynos_drm_encoder_destroy(struct drm_encoder *encoder)
{
struct exynos_drm_encoder *exynos_encoder =
to_exynos_encoder(encoder);
exynos_encoder->manager->pipe = -1;
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
drm_encoder_cleanup(encoder);
kfree(exynos_encoder);
@ -281,13 +133,12 @@ static unsigned int exynos_drm_encoder_clones(struct drm_encoder *encoder)
struct drm_encoder *clone;
struct drm_device *dev = encoder->dev;
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
struct exynos_drm_display_ops *display_ops =
exynos_encoder->manager->display_ops;
struct exynos_drm_display *display = exynos_encoder->display;
unsigned int clone_mask = 0;
int cnt = 0;
list_for_each_entry(clone, &dev->mode_config.encoder_list, head) {
switch (display_ops->type) {
switch (display->type) {
case EXYNOS_DISPLAY_TYPE_LCD:
case EXYNOS_DISPLAY_TYPE_HDMI:
case EXYNOS_DISPLAY_TYPE_VIDI:
@ -311,24 +162,20 @@ void exynos_drm_encoder_setup(struct drm_device *dev)
struct drm_encoder *
exynos_drm_encoder_create(struct drm_device *dev,
struct exynos_drm_manager *manager,
unsigned int possible_crtcs)
struct exynos_drm_display *display,
unsigned long possible_crtcs)
{
struct drm_encoder *encoder;
struct exynos_drm_encoder *exynos_encoder;
if (!manager || !possible_crtcs)
return NULL;
if (!manager->dev)
if (!possible_crtcs)
return NULL;
exynos_encoder = kzalloc(sizeof(*exynos_encoder), GFP_KERNEL);
if (!exynos_encoder)
return NULL;
exynos_encoder->dpms = DRM_MODE_DPMS_OFF;
exynos_encoder->manager = manager;
exynos_encoder->display = display;
encoder = &exynos_encoder->drm_encoder;
encoder->possible_crtcs = possible_crtcs;
@ -344,149 +191,7 @@ exynos_drm_encoder_create(struct drm_device *dev,
return encoder;
}
struct exynos_drm_manager *exynos_drm_get_manager(struct drm_encoder *encoder)
struct exynos_drm_display *exynos_drm_get_display(struct drm_encoder *encoder)
{
return to_exynos_encoder(encoder)->manager;
}
void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data,
void (*fn)(struct drm_encoder *, void *))
{
struct drm_device *dev = crtc->dev;
struct drm_encoder *encoder;
struct exynos_drm_private *private = dev->dev_private;
struct exynos_drm_manager *manager;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
/*
* if crtc is detached from encoder, check pipe,
* otherwise check crtc attached to encoder
*/
if (!encoder->crtc) {
manager = to_exynos_encoder(encoder)->manager;
if (manager->pipe < 0 ||
private->crtc[manager->pipe] != crtc)
continue;
} else {
if (encoder->crtc != crtc)
continue;
}
fn(encoder, data);
}
}
void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data)
{
struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager;
struct exynos_drm_manager_ops *manager_ops = manager->ops;
int crtc = *(int *)data;
if (manager->pipe != crtc)
return;
if (manager_ops->enable_vblank)
manager_ops->enable_vblank(manager->dev);
}
void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data)
{
struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager;
struct exynos_drm_manager_ops *manager_ops = manager->ops;
int crtc = *(int *)data;
if (manager->pipe != crtc)
return;
if (manager_ops->disable_vblank)
manager_ops->disable_vblank(manager->dev);
}
void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data)
{
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
struct exynos_drm_manager *manager = exynos_encoder->manager;
struct exynos_drm_manager_ops *manager_ops = manager->ops;
int mode = *(int *)data;
if (manager_ops && manager_ops->dpms)
manager_ops->dpms(manager->dev, mode);
/*
* if this condition is ok then it means that the crtc is already
* detached from encoder and last function for detaching is properly
* done, so clear pipe from manager to prevent repeated call.
*/
if (mode > DRM_MODE_DPMS_ON) {
if (!encoder->crtc)
manager->pipe = -1;
}
}
void exynos_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data)
{
struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager;
int pipe = *(int *)data;
/*
* when crtc is detached from encoder, this pipe is used
* to select manager operation
*/
manager->pipe = pipe;
}
void exynos_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data)
{
struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager;
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
struct exynos_drm_overlay *overlay = data;
if (overlay_ops && overlay_ops->mode_set)
overlay_ops->mode_set(manager->dev, overlay);
}
void exynos_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data)
{
struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager;
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
int zpos = DEFAULT_ZPOS;
if (data)
zpos = *(int *)data;
if (overlay_ops && overlay_ops->commit)
overlay_ops->commit(manager->dev, zpos);
}
void exynos_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data)
{
struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager;
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
int zpos = DEFAULT_ZPOS;
if (data)
zpos = *(int *)data;
if (overlay_ops && overlay_ops->enable)
overlay_ops->enable(manager->dev, zpos);
}
void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data)
{
struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager;
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
int zpos = DEFAULT_ZPOS;
if (data)
zpos = *(int *)data;
if (overlay_ops && overlay_ops->disable)
overlay_ops->disable(manager->dev, zpos);
return to_exynos_encoder(encoder)->display;
}

View File

@ -18,20 +18,8 @@ struct exynos_drm_manager;
void exynos_drm_encoder_setup(struct drm_device *dev);
struct drm_encoder *exynos_drm_encoder_create(struct drm_device *dev,
struct exynos_drm_manager *mgr,
unsigned int possible_crtcs);
struct exynos_drm_manager *
exynos_drm_get_manager(struct drm_encoder *encoder);
void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data,
void (*fn)(struct drm_encoder *, void *));
void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data);
void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_complete_scanout(struct drm_framebuffer *fb);
struct exynos_drm_display *mgr,
unsigned long possible_crtcs);
struct exynos_drm_display *exynos_drm_get_display(struct drm_encoder *encoder);
#endif

View File

@ -20,9 +20,10 @@
#include "exynos_drm_drv.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_fbdev.h"
#include "exynos_drm_gem.h"
#include "exynos_drm_iommu.h"
#include "exynos_drm_encoder.h"
#include "exynos_drm_crtc.h"
#define to_exynos_fb(x) container_of(x, struct exynos_drm_fb, fb)
@ -71,7 +72,7 @@ static void exynos_drm_fb_destroy(struct drm_framebuffer *fb)
unsigned int i;
/* make sure that overlay data are updated before relesing fb. */
exynos_drm_encoder_complete_scanout(fb);
exynos_drm_crtc_complete_scanout(fb);
drm_framebuffer_cleanup(fb);
@ -300,6 +301,8 @@ static void exynos_drm_output_poll_changed(struct drm_device *dev)
if (fb_helper)
drm_fb_helper_hotplug_event(fb_helper);
else
exynos_drm_fbdev_init(dev);
}
static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = {

View File

@ -90,7 +90,7 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
/* RGB formats use only one buffer */
buffer = exynos_drm_fb_buffer(fb, 0);
if (!buffer) {
DRM_LOG_KMS("buffer is null.\n");
DRM_DEBUG_KMS("buffer is null.\n");
return -EFAULT;
}
@ -237,6 +237,24 @@ static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
.fb_probe = exynos_drm_fbdev_create,
};
bool exynos_drm_fbdev_is_anything_connected(struct drm_device *dev)
{
struct drm_connector *connector;
bool ret = false;
mutex_lock(&dev->mode_config.mutex);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->status != connector_status_connected)
continue;
ret = true;
break;
}
mutex_unlock(&dev->mode_config.mutex);
return ret;
}
int exynos_drm_fbdev_init(struct drm_device *dev)
{
struct exynos_drm_fbdev *fbdev;
@ -248,6 +266,9 @@ int exynos_drm_fbdev_init(struct drm_device *dev)
if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
return 0;
if (!exynos_drm_fbdev_is_anything_connected(dev))
return 0;
fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
if (!fbdev)
return -ENOMEM;

View File

@ -62,7 +62,7 @@
/* FIMD has totally five hardware windows. */
#define WINDOWS_NR 5
#define get_fimd_context(dev) platform_get_drvdata(to_platform_device(dev))
#define get_fimd_manager(mgr) platform_get_drvdata(to_platform_device(dev))
struct fimd_driver_data {
unsigned int timing_base;
@ -105,20 +105,18 @@ struct fimd_win_data {
};
struct fimd_context {
struct exynos_drm_subdrv subdrv;
int irq;
struct drm_crtc *crtc;
struct device *dev;
struct drm_device *drm_dev;
struct clk *bus_clk;
struct clk *lcd_clk;
void __iomem *regs;
struct drm_display_mode mode;
struct fimd_win_data win_data[WINDOWS_NR];
unsigned int clkdiv;
unsigned int default_win;
unsigned long irq_flags;
u32 vidcon0;
u32 vidcon1;
bool suspended;
struct mutex lock;
int pipe;
wait_queue_head_t wait_vsync_queue;
atomic_t wait_vsync_event;
@ -145,153 +143,147 @@ static inline struct fimd_driver_data *drm_fimd_get_driver_data(
return (struct fimd_driver_data *)of_id->data;
}
static bool fimd_display_is_connected(struct device *dev)
static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
struct drm_device *drm_dev, int pipe)
{
/* TODO. */
struct fimd_context *ctx = mgr->ctx;
ctx->drm_dev = drm_dev;
ctx->pipe = pipe;
/*
* enable drm irq mode.
* - with irq_enabled = true, we can use the vblank feature.
*
* P.S. note that we wouldn't use drm irq handler but
* just specific driver own one instead because
* drm framework supports only one irq handler.
*/
drm_dev->irq_enabled = true;
/*
* with vblank_disable_allowed = true, vblank interrupt will be disabled
* by drm timer once a current process gives up ownership of
* vblank event.(after drm_vblank_put function is called)
*/
drm_dev->vblank_disable_allowed = true;
/* attach this sub driver to iommu mapping if supported. */
if (is_drm_iommu_supported(ctx->drm_dev))
drm_iommu_attach_device(ctx->drm_dev, ctx->dev);
return 0;
}
static void fimd_mgr_remove(struct exynos_drm_manager *mgr)
{
struct fimd_context *ctx = mgr->ctx;
/* detach this sub driver from iommu mapping if supported. */
if (is_drm_iommu_supported(ctx->drm_dev))
drm_iommu_detach_device(ctx->drm_dev, ctx->dev);
}
static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
const struct drm_display_mode *mode)
{
unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh;
u32 clkdiv;
/* Find the clock divider value that gets us closest to ideal_clk */
clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk);
return (clkdiv < 0x100) ? clkdiv : 0xff;
}
static bool fimd_mode_fixup(struct exynos_drm_manager *mgr,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
if (adjusted_mode->vrefresh == 0)
adjusted_mode->vrefresh = FIMD_DEFAULT_FRAMERATE;
return true;
}
static void *fimd_get_panel(struct device *dev)
static void fimd_mode_set(struct exynos_drm_manager *mgr,
const struct drm_display_mode *in_mode)
{
struct fimd_context *ctx = get_fimd_context(dev);
struct fimd_context *ctx = mgr->ctx;
return &ctx->panel;
drm_mode_copy(&ctx->mode, in_mode);
}
static int fimd_check_mode(struct device *dev, struct drm_display_mode *mode)
static void fimd_commit(struct exynos_drm_manager *mgr)
{
/* TODO. */
return 0;
}
static int fimd_display_power_on(struct device *dev, int mode)
{
/* TODO */
return 0;
}
static struct exynos_drm_display_ops fimd_display_ops = {
.type = EXYNOS_DISPLAY_TYPE_LCD,
.is_connected = fimd_display_is_connected,
.get_panel = fimd_get_panel,
.check_mode = fimd_check_mode,
.power_on = fimd_display_power_on,
};
static void fimd_dpms(struct device *subdrv_dev, int mode)
{
struct fimd_context *ctx = get_fimd_context(subdrv_dev);
DRM_DEBUG_KMS("%d\n", mode);
mutex_lock(&ctx->lock);
switch (mode) {
case DRM_MODE_DPMS_ON:
/*
* enable fimd hardware only if suspended status.
*
* P.S. fimd_dpms function would be called at booting time so
* clk_enable could be called double time.
*/
if (ctx->suspended)
pm_runtime_get_sync(subdrv_dev);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
if (!ctx->suspended)
pm_runtime_put_sync(subdrv_dev);
break;
default:
DRM_DEBUG_KMS("unspecified mode %d\n", mode);
break;
}
mutex_unlock(&ctx->lock);
}
static void fimd_apply(struct device *subdrv_dev)
{
struct fimd_context *ctx = get_fimd_context(subdrv_dev);
struct exynos_drm_manager *mgr = ctx->subdrv.manager;
struct exynos_drm_manager_ops *mgr_ops = mgr->ops;
struct exynos_drm_overlay_ops *ovl_ops = mgr->overlay_ops;
struct fimd_win_data *win_data;
int i;
for (i = 0; i < WINDOWS_NR; i++) {
win_data = &ctx->win_data[i];
if (win_data->enabled && (ovl_ops && ovl_ops->commit))
ovl_ops->commit(subdrv_dev, i);
}
if (mgr_ops && mgr_ops->commit)
mgr_ops->commit(subdrv_dev);
}
static void fimd_commit(struct device *dev)
{
struct fimd_context *ctx = get_fimd_context(dev);
struct exynos_drm_panel_info *panel = &ctx->panel;
struct videomode *vm = &panel->vm;
struct fimd_context *ctx = mgr->ctx;
struct drm_display_mode *mode = &ctx->mode;
struct fimd_driver_data *driver_data;
u32 val;
u32 val, clkdiv, vidcon1;
int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
driver_data = ctx->driver_data;
if (ctx->suspended)
return;
/* setup polarity values from machine code. */
writel(ctx->vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
/* nothing to do if we haven't set the mode yet */
if (mode->htotal == 0 || mode->vtotal == 0)
return;
/* setup polarity values */
vidcon1 = ctx->vidcon1;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
vidcon1 |= VIDCON1_INV_VSYNC;
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
vidcon1 |= VIDCON1_INV_HSYNC;
writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
/* setup vertical timing values. */
val = VIDTCON0_VBPD(vm->vback_porch - 1) |
VIDTCON0_VFPD(vm->vfront_porch - 1) |
VIDTCON0_VSPW(vm->vsync_len - 1);
vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
vbpd = mode->crtc_vtotal - mode->crtc_vsync_end;
vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay;
val = VIDTCON0_VBPD(vbpd - 1) |
VIDTCON0_VFPD(vfpd - 1) |
VIDTCON0_VSPW(vsync_len - 1);
writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
/* setup horizontal timing values. */
val = VIDTCON1_HBPD(vm->hback_porch - 1) |
VIDTCON1_HFPD(vm->hfront_porch - 1) |
VIDTCON1_HSPW(vm->hsync_len - 1);
hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
hbpd = mode->crtc_htotal - mode->crtc_hsync_end;
hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay;
val = VIDTCON1_HBPD(hbpd - 1) |
VIDTCON1_HFPD(hfpd - 1) |
VIDTCON1_HSPW(hsync_len - 1);
writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
/* setup horizontal and vertical display size. */
val = VIDTCON2_LINEVAL(vm->vactive - 1) |
VIDTCON2_HOZVAL(vm->hactive - 1) |
VIDTCON2_LINEVAL_E(vm->vactive - 1) |
VIDTCON2_HOZVAL_E(vm->hactive - 1);
val = VIDTCON2_LINEVAL(mode->vdisplay - 1) |
VIDTCON2_HOZVAL(mode->hdisplay - 1) |
VIDTCON2_LINEVAL_E(mode->vdisplay - 1) |
VIDTCON2_HOZVAL_E(mode->hdisplay - 1);
writel(val, ctx->regs + driver_data->timing_base + VIDTCON2);
/* setup clock source, clock divider, enable dma. */
val = ctx->vidcon0;
val &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR);
if (ctx->driver_data->has_clksel) {
val &= ~VIDCON0_CLKSEL_MASK;
val |= VIDCON0_CLKSEL_LCD;
}
if (ctx->clkdiv > 1)
val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR;
else
val &= ~VIDCON0_CLKDIR; /* 1:1 clock */
/*
* fields of register with prefix '_F' would be updated
* at vsync(same as dma start)
*/
val |= VIDCON0_ENVID | VIDCON0_ENVID_F;
val = VIDCON0_ENVID | VIDCON0_ENVID_F;
if (ctx->driver_data->has_clksel)
val |= VIDCON0_CLKSEL_LCD;
clkdiv = fimd_calc_clkdiv(ctx, mode);
if (clkdiv > 1)
val |= VIDCON0_CLKVAL_F(clkdiv - 1) | VIDCON0_CLKDIR;
writel(val, ctx->regs + VIDCON0);
}
static int fimd_enable_vblank(struct device *dev)
static int fimd_enable_vblank(struct exynos_drm_manager *mgr)
{
struct fimd_context *ctx = get_fimd_context(dev);
struct fimd_context *ctx = mgr->ctx;
u32 val;
if (ctx->suspended)
@ -314,9 +306,9 @@ static int fimd_enable_vblank(struct device *dev)
return 0;
}
static void fimd_disable_vblank(struct device *dev)
static void fimd_disable_vblank(struct exynos_drm_manager *mgr)
{
struct fimd_context *ctx = get_fimd_context(dev);
struct fimd_context *ctx = mgr->ctx;
u32 val;
if (ctx->suspended)
@ -332,9 +324,9 @@ static void fimd_disable_vblank(struct device *dev)
}
}
static void fimd_wait_for_vblank(struct device *dev)
static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr)
{
struct fimd_context *ctx = get_fimd_context(dev);
struct fimd_context *ctx = mgr->ctx;
if (ctx->suspended)
return;
@ -351,25 +343,16 @@ static void fimd_wait_for_vblank(struct device *dev)
DRM_DEBUG_KMS("vblank wait timed out.\n");
}
static struct exynos_drm_manager_ops fimd_manager_ops = {
.dpms = fimd_dpms,
.apply = fimd_apply,
.commit = fimd_commit,
.enable_vblank = fimd_enable_vblank,
.disable_vblank = fimd_disable_vblank,
.wait_for_vblank = fimd_wait_for_vblank,
};
static void fimd_win_mode_set(struct device *dev,
struct exynos_drm_overlay *overlay)
static void fimd_win_mode_set(struct exynos_drm_manager *mgr,
struct exynos_drm_overlay *overlay)
{
struct fimd_context *ctx = get_fimd_context(dev);
struct fimd_context *ctx = mgr->ctx;
struct fimd_win_data *win_data;
int win;
unsigned long offset;
if (!overlay) {
dev_err(dev, "overlay is NULL\n");
DRM_ERROR("overlay is NULL\n");
return;
}
@ -409,9 +392,8 @@ static void fimd_win_mode_set(struct device *dev,
overlay->fb_width, overlay->crtc_width);
}
static void fimd_win_set_pixfmt(struct device *dev, unsigned int win)
static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)
{
struct fimd_context *ctx = get_fimd_context(dev);
struct fimd_win_data *win_data = &ctx->win_data[win];
unsigned long val;
@ -467,9 +449,8 @@ static void fimd_win_set_pixfmt(struct device *dev, unsigned int win)
writel(val, ctx->regs + WINCON(win));
}
static void fimd_win_set_colkey(struct device *dev, unsigned int win)
static void fimd_win_set_colkey(struct fimd_context *ctx, unsigned int win)
{
struct fimd_context *ctx = get_fimd_context(dev);
unsigned int keycon0 = 0, keycon1 = 0;
keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F |
@ -508,9 +489,9 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx,
writel(val, ctx->regs + reg);
}
static void fimd_win_commit(struct device *dev, int zpos)
static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos)
{
struct fimd_context *ctx = get_fimd_context(dev);
struct fimd_context *ctx = mgr->ctx;
struct fimd_win_data *win_data;
int win = zpos;
unsigned long val, alpha, size;
@ -528,6 +509,12 @@ static void fimd_win_commit(struct device *dev, int zpos)
win_data = &ctx->win_data[win];
/* If suspended, enable this on resume */
if (ctx->suspended) {
win_data->resume = true;
return;
}
/*
* SHADOWCON/PRTCON register is used for enabling timing.
*
@ -605,11 +592,11 @@ static void fimd_win_commit(struct device *dev, int zpos)
DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val);
}
fimd_win_set_pixfmt(dev, win);
fimd_win_set_pixfmt(ctx, win);
/* hardware window 0 doesn't support color key. */
if (win != 0)
fimd_win_set_colkey(dev, win);
fimd_win_set_colkey(ctx, win);
/* wincon */
val = readl(ctx->regs + WINCON(win));
@ -628,9 +615,9 @@ static void fimd_win_commit(struct device *dev, int zpos)
win_data->enabled = true;
}
static void fimd_win_disable(struct device *dev, int zpos)
static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
{
struct fimd_context *ctx = get_fimd_context(dev);
struct fimd_context *ctx = mgr->ctx;
struct fimd_win_data *win_data;
int win = zpos;
u32 val;
@ -669,132 +656,6 @@ static void fimd_win_disable(struct device *dev, int zpos)
win_data->enabled = false;
}
static struct exynos_drm_overlay_ops fimd_overlay_ops = {
.mode_set = fimd_win_mode_set,
.commit = fimd_win_commit,
.disable = fimd_win_disable,
};
static struct exynos_drm_manager fimd_manager = {
.pipe = -1,
.ops = &fimd_manager_ops,
.overlay_ops = &fimd_overlay_ops,
.display_ops = &fimd_display_ops,
};
static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
{
struct fimd_context *ctx = (struct fimd_context *)dev_id;
struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
struct drm_device *drm_dev = subdrv->drm_dev;
struct exynos_drm_manager *manager = subdrv->manager;
u32 val;
val = readl(ctx->regs + VIDINTCON1);
if (val & VIDINTCON1_INT_FRAME)
/* VSYNC interrupt */
writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1);
/* check the crtc is detached already from encoder */
if (manager->pipe < 0)
goto out;
drm_handle_vblank(drm_dev, manager->pipe);
exynos_drm_crtc_finish_pageflip(drm_dev, manager->pipe);
/* set wait vsync event to zero and wake up queue. */
if (atomic_read(&ctx->wait_vsync_event)) {
atomic_set(&ctx->wait_vsync_event, 0);
wake_up(&ctx->wait_vsync_queue);
}
out:
return IRQ_HANDLED;
}
static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
{
/*
* enable drm irq mode.
* - with irq_enabled = true, we can use the vblank feature.
*
* P.S. note that we wouldn't use drm irq handler but
* just specific driver own one instead because
* drm framework supports only one irq handler.
*/
drm_dev->irq_enabled = true;
/*
* with vblank_disable_allowed = true, vblank interrupt will be disabled
* by drm timer once a current process gives up ownership of
* vblank event.(after drm_vblank_put function is called)
*/
drm_dev->vblank_disable_allowed = true;
/* attach this sub driver to iommu mapping if supported. */
if (is_drm_iommu_supported(drm_dev))
drm_iommu_attach_device(drm_dev, dev);
return 0;
}
static void fimd_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
{
/* detach this sub driver from iommu mapping if supported. */
if (is_drm_iommu_supported(drm_dev))
drm_iommu_detach_device(drm_dev, dev);
}
static int fimd_configure_clocks(struct fimd_context *ctx, struct device *dev)
{
struct videomode *vm = &ctx->panel.vm;
unsigned long clk;
ctx->bus_clk = devm_clk_get(dev, "fimd");
if (IS_ERR(ctx->bus_clk)) {
dev_err(dev, "failed to get bus clock\n");
return PTR_ERR(ctx->bus_clk);
}
ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd");
if (IS_ERR(ctx->lcd_clk)) {
dev_err(dev, "failed to get lcd clock\n");
return PTR_ERR(ctx->lcd_clk);
}
clk = clk_get_rate(ctx->lcd_clk);
if (clk == 0) {
dev_err(dev, "error getting sclk_fimd clock rate\n");
return -EINVAL;
}
if (vm->pixelclock == 0) {
unsigned long c;
c = vm->hactive + vm->hback_porch + vm->hfront_porch +
vm->hsync_len;
c *= vm->vactive + vm->vback_porch + vm->vfront_porch +
vm->vsync_len;
vm->pixelclock = c * FIMD_DEFAULT_FRAMERATE;
if (vm->pixelclock == 0) {
dev_err(dev, "incorrect display timings\n");
return -EINVAL;
}
dev_warn(dev, "pixel clock recalculated to %luHz (%dHz frame rate)\n",
vm->pixelclock, FIMD_DEFAULT_FRAMERATE);
}
ctx->clkdiv = DIV_ROUND_UP(clk, vm->pixelclock);
if (ctx->clkdiv > 256) {
dev_warn(dev, "calculated pixel clock divider too high (%u), lowered to 256\n",
ctx->clkdiv);
ctx->clkdiv = 256;
}
vm->pixelclock = clk / ctx->clkdiv;
DRM_DEBUG_KMS("pixel clock = %lu, clkdiv = %d\n", vm->pixelclock,
ctx->clkdiv);
return 0;
}
static void fimd_clear_win(struct fimd_context *ctx, int win)
{
writel(0, ctx->regs + WINCON(win));
@ -808,45 +669,24 @@ static void fimd_clear_win(struct fimd_context *ctx, int win)
fimd_shadow_protect_win(ctx, win, false);
}
static int fimd_clock(struct fimd_context *ctx, bool enable)
static void fimd_window_suspend(struct exynos_drm_manager *mgr)
{
if (enable) {
int ret;
ret = clk_prepare_enable(ctx->bus_clk);
if (ret < 0)
return ret;
ret = clk_prepare_enable(ctx->lcd_clk);
if (ret < 0) {
clk_disable_unprepare(ctx->bus_clk);
return ret;
}
} else {
clk_disable_unprepare(ctx->lcd_clk);
clk_disable_unprepare(ctx->bus_clk);
}
return 0;
}
static void fimd_window_suspend(struct device *dev)
{
struct fimd_context *ctx = get_fimd_context(dev);
struct fimd_context *ctx = mgr->ctx;
struct fimd_win_data *win_data;
int i;
for (i = 0; i < WINDOWS_NR; i++) {
win_data = &ctx->win_data[i];
win_data->resume = win_data->enabled;
fimd_win_disable(dev, i);
if (win_data->enabled)
fimd_win_disable(mgr, i);
}
fimd_wait_for_vblank(dev);
fimd_wait_for_vblank(mgr);
}
static void fimd_window_resume(struct device *dev)
static void fimd_window_resume(struct exynos_drm_manager *mgr)
{
struct fimd_context *ctx = get_fimd_context(dev);
struct fimd_context *ctx = mgr->ctx;
struct fimd_win_data *win_data;
int i;
@ -857,62 +697,162 @@ static void fimd_window_resume(struct device *dev)
}
}
static int fimd_activate(struct fimd_context *ctx, bool enable)
static void fimd_apply(struct exynos_drm_manager *mgr)
{
struct device *dev = ctx->subdrv.dev;
if (enable) {
int ret;
struct fimd_context *ctx = mgr->ctx;
struct fimd_win_data *win_data;
int i;
ret = fimd_clock(ctx, true);
if (ret < 0)
return ret;
ctx->suspended = false;
/* if vblank was enabled status, enable it again. */
if (test_and_clear_bit(0, &ctx->irq_flags))
fimd_enable_vblank(dev);
fimd_window_resume(dev);
} else {
fimd_window_suspend(dev);
fimd_clock(ctx, false);
ctx->suspended = true;
for (i = 0; i < WINDOWS_NR; i++) {
win_data = &ctx->win_data[i];
if (win_data->enabled)
fimd_win_commit(mgr, i);
}
fimd_commit(mgr);
}
static int fimd_poweron(struct exynos_drm_manager *mgr)
{
struct fimd_context *ctx = mgr->ctx;
int ret;
if (!ctx->suspended)
return 0;
ctx->suspended = false;
pm_runtime_get_sync(ctx->dev);
ret = clk_prepare_enable(ctx->bus_clk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret);
goto bus_clk_err;
}
ret = clk_prepare_enable(ctx->lcd_clk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret);
goto lcd_clk_err;
}
/* if vblank was enabled status, enable it again. */
if (test_and_clear_bit(0, &ctx->irq_flags)) {
ret = fimd_enable_vblank(mgr);
if (ret) {
DRM_ERROR("Failed to re-enable vblank [%d]\n", ret);
goto enable_vblank_err;
}
}
fimd_window_resume(mgr);
fimd_apply(mgr);
return 0;
enable_vblank_err:
clk_disable_unprepare(ctx->lcd_clk);
lcd_clk_err:
clk_disable_unprepare(ctx->bus_clk);
bus_clk_err:
ctx->suspended = true;
return ret;
}
static int fimd_poweroff(struct exynos_drm_manager *mgr)
{
struct fimd_context *ctx = mgr->ctx;
if (ctx->suspended)
return 0;
/*
* We need to make sure that all windows are disabled before we
* suspend that connector. Otherwise we might try to scan from
* a destroyed buffer later.
*/
fimd_window_suspend(mgr);
clk_disable_unprepare(ctx->lcd_clk);
clk_disable_unprepare(ctx->bus_clk);
pm_runtime_put_sync(ctx->dev);
ctx->suspended = true;
return 0;
}
static int fimd_get_platform_data(struct fimd_context *ctx, struct device *dev)
static void fimd_dpms(struct exynos_drm_manager *mgr, int mode)
{
struct videomode *vm;
int ret;
DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
vm = &ctx->panel.vm;
ret = of_get_videomode(dev->of_node, vm, OF_USE_NATIVE_MODE);
if (ret) {
DRM_ERROR("failed: of_get_videomode() : %d\n", ret);
return ret;
switch (mode) {
case DRM_MODE_DPMS_ON:
fimd_poweron(mgr);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
fimd_poweroff(mgr);
break;
default:
DRM_DEBUG_KMS("unspecified mode %d\n", mode);
break;
}
}
if (vm->flags & DISPLAY_FLAGS_VSYNC_LOW)
ctx->vidcon1 |= VIDCON1_INV_VSYNC;
if (vm->flags & DISPLAY_FLAGS_HSYNC_LOW)
ctx->vidcon1 |= VIDCON1_INV_HSYNC;
if (vm->flags & DISPLAY_FLAGS_DE_LOW)
ctx->vidcon1 |= VIDCON1_INV_VDEN;
if (vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
ctx->vidcon1 |= VIDCON1_INV_VCLK;
static struct exynos_drm_manager_ops fimd_manager_ops = {
.initialize = fimd_mgr_initialize,
.remove = fimd_mgr_remove,
.dpms = fimd_dpms,
.mode_fixup = fimd_mode_fixup,
.mode_set = fimd_mode_set,
.commit = fimd_commit,
.enable_vblank = fimd_enable_vblank,
.disable_vblank = fimd_disable_vblank,
.wait_for_vblank = fimd_wait_for_vblank,
.win_mode_set = fimd_win_mode_set,
.win_commit = fimd_win_commit,
.win_disable = fimd_win_disable,
};
return 0;
static struct exynos_drm_manager fimd_manager = {
.type = EXYNOS_DISPLAY_TYPE_LCD,
.ops = &fimd_manager_ops,
};
static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
{
struct fimd_context *ctx = (struct fimd_context *)dev_id;
u32 val;
val = readl(ctx->regs + VIDINTCON1);
if (val & VIDINTCON1_INT_FRAME)
/* VSYNC interrupt */
writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1);
/* check the crtc is detached already from encoder */
if (ctx->pipe < 0 || !ctx->drm_dev)
goto out;
drm_handle_vblank(ctx->drm_dev, ctx->pipe);
exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
/* set wait vsync event to zero and wake up queue. */
if (atomic_read(&ctx->wait_vsync_event)) {
atomic_set(&ctx->wait_vsync_event, 0);
wake_up(&ctx->wait_vsync_queue);
}
out:
return IRQ_HANDLED;
}
static int fimd_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct fimd_context *ctx;
struct exynos_drm_subdrv *subdrv;
struct resource *res;
int win;
int ret = -EINVAL;
@ -924,13 +864,25 @@ static int fimd_probe(struct platform_device *pdev)
if (!ctx)
return -ENOMEM;
ret = fimd_get_platform_data(ctx, dev);
if (ret)
return ret;
ctx->dev = dev;
ctx->suspended = true;
ret = fimd_configure_clocks(ctx, dev);
if (ret)
return ret;
if (of_property_read_bool(dev->of_node, "samsung,invert-vden"))
ctx->vidcon1 |= VIDCON1_INV_VDEN;
if (of_property_read_bool(dev->of_node, "samsung,invert-vclk"))
ctx->vidcon1 |= VIDCON1_INV_VCLK;
ctx->bus_clk = devm_clk_get(dev, "fimd");
if (IS_ERR(ctx->bus_clk)) {
dev_err(dev, "failed to get bus clock\n");
return PTR_ERR(ctx->bus_clk);
}
ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd");
if (IS_ERR(ctx->lcd_clk)) {
dev_err(dev, "failed to get lcd clock\n");
return PTR_ERR(ctx->lcd_clk);
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@ -944,9 +896,7 @@ static int fimd_probe(struct platform_device *pdev)
return -ENXIO;
}
ctx->irq = res->start;
ret = devm_request_irq(dev, ctx->irq, fimd_irq_handler,
ret = devm_request_irq(dev, res->start, fimd_irq_handler,
0, "drm_fimd", ctx);
if (ret) {
dev_err(dev, "irq request failed.\n");
@ -957,120 +907,42 @@ static int fimd_probe(struct platform_device *pdev)
init_waitqueue_head(&ctx->wait_vsync_queue);
atomic_set(&ctx->wait_vsync_event, 0);
subdrv = &ctx->subdrv;
platform_set_drvdata(pdev, &fimd_manager);
subdrv->dev = dev;
subdrv->manager = &fimd_manager;
subdrv->probe = fimd_subdrv_probe;
subdrv->remove = fimd_subdrv_remove;
fimd_manager.ctx = ctx;
exynos_drm_manager_register(&fimd_manager);
mutex_init(&ctx->lock);
platform_set_drvdata(pdev, ctx);
exynos_dpi_probe(ctx->dev);
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
for (win = 0; win < WINDOWS_NR; win++)
fimd_clear_win(ctx, win);
exynos_drm_subdrv_register(subdrv);
return 0;
}
static int fimd_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct fimd_context *ctx = platform_get_drvdata(pdev);
struct exynos_drm_manager *mgr = platform_get_drvdata(pdev);
exynos_drm_subdrv_unregister(&ctx->subdrv);
exynos_dpi_remove(&pdev->dev);
if (ctx->suspended)
goto out;
exynos_drm_manager_unregister(&fimd_manager);
pm_runtime_set_suspended(dev);
pm_runtime_put_sync(dev);
fimd_dpms(mgr, DRM_MODE_DPMS_OFF);
out:
pm_runtime_disable(dev);
pm_runtime_disable(&pdev->dev);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int fimd_suspend(struct device *dev)
{
struct fimd_context *ctx = get_fimd_context(dev);
/*
* do not use pm_runtime_suspend(). if pm_runtime_suspend() is
* called here, an error would be returned by that interface
* because the usage_count of pm runtime is more than 1.
*/
if (!pm_runtime_suspended(dev))
return fimd_activate(ctx, false);
return 0;
}
static int fimd_resume(struct device *dev)
{
struct fimd_context *ctx = get_fimd_context(dev);
/*
* if entered to sleep when lcd panel was on, the usage_count
* of pm runtime would still be 1 so in this case, fimd driver
* should be on directly not drawing on pm runtime interface.
*/
if (!pm_runtime_suspended(dev)) {
int ret;
ret = fimd_activate(ctx, true);
if (ret < 0)
return ret;
/*
* in case of dpms on(standby), fimd_apply function will
* be called by encoder's dpms callback to update fimd's
* registers but in case of sleep wakeup, it's not.
* so fimd_apply function should be called at here.
*/
fimd_apply(dev);
}
return 0;
}
#endif
#ifdef CONFIG_PM_RUNTIME
static int fimd_runtime_suspend(struct device *dev)
{
struct fimd_context *ctx = get_fimd_context(dev);
return fimd_activate(ctx, false);
}
static int fimd_runtime_resume(struct device *dev)
{
struct fimd_context *ctx = get_fimd_context(dev);
return fimd_activate(ctx, true);
}
#endif
static const struct dev_pm_ops fimd_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(fimd_suspend, fimd_resume)
SET_RUNTIME_PM_OPS(fimd_runtime_suspend, fimd_runtime_resume, NULL)
};
struct platform_driver fimd_driver = {
.probe = fimd_probe,
.remove = fimd_remove,
.driver = {
.name = "exynos4-fb",
.owner = THIS_MODULE,
.pm = &fimd_pm_ops,
.of_match_table = fimd_driver_dt_match,
},
};

View File

@ -1,439 +0,0 @@
/*
* Copyright (C) 2011 Samsung Electronics Co.Ltd
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Seung-Woo Kim <sw0312.kim@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <drm/drmP.h>
#include <linux/kernel.h>
#include <linux/wait.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_hdmi.h"
#define to_context(dev) platform_get_drvdata(to_platform_device(dev))
#define to_subdrv(dev) to_context(dev)
#define get_ctx_from_subdrv(subdrv) container_of(subdrv,\
struct drm_hdmi_context, subdrv);
/* platform device pointer for common drm hdmi device. */
static struct platform_device *exynos_drm_hdmi_pdev;
/* Common hdmi subdrv needs to access the hdmi and mixer though context.
* These should be initialied by the repective drivers */
static struct exynos_drm_hdmi_context *hdmi_ctx;
static struct exynos_drm_hdmi_context *mixer_ctx;
/* these callback points shoud be set by specific drivers. */
static struct exynos_hdmi_ops *hdmi_ops;
static struct exynos_mixer_ops *mixer_ops;
struct drm_hdmi_context {
struct exynos_drm_subdrv subdrv;
struct exynos_drm_hdmi_context *hdmi_ctx;
struct exynos_drm_hdmi_context *mixer_ctx;
bool enabled[MIXER_WIN_NR];
};
int exynos_platform_device_hdmi_register(void)
{
struct platform_device *pdev;
if (exynos_drm_hdmi_pdev)
return -EEXIST;
pdev = platform_device_register_simple(
"exynos-drm-hdmi", -1, NULL, 0);
if (IS_ERR(pdev))
return PTR_ERR(pdev);
exynos_drm_hdmi_pdev = pdev;
return 0;
}
void exynos_platform_device_hdmi_unregister(void)
{
if (exynos_drm_hdmi_pdev) {
platform_device_unregister(exynos_drm_hdmi_pdev);
exynos_drm_hdmi_pdev = NULL;
}
}
void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx)
{
if (ctx)
hdmi_ctx = ctx;
}
void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx)
{
if (ctx)
mixer_ctx = ctx;
}
void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops)
{
if (ops)
hdmi_ops = ops;
}
void exynos_mixer_ops_register(struct exynos_mixer_ops *ops)
{
if (ops)
mixer_ops = ops;
}
static bool drm_hdmi_is_connected(struct device *dev)
{
struct drm_hdmi_context *ctx = to_context(dev);
if (hdmi_ops && hdmi_ops->is_connected)
return hdmi_ops->is_connected(ctx->hdmi_ctx->ctx);
return false;
}
static struct edid *drm_hdmi_get_edid(struct device *dev,
struct drm_connector *connector)
{
struct drm_hdmi_context *ctx = to_context(dev);
if (hdmi_ops && hdmi_ops->get_edid)
return hdmi_ops->get_edid(ctx->hdmi_ctx->ctx, connector);
return NULL;
}
static int drm_hdmi_check_mode(struct device *dev,
struct drm_display_mode *mode)
{
struct drm_hdmi_context *ctx = to_context(dev);
int ret = 0;
/*
* Both, mixer and hdmi should be able to handle the requested mode.
* If any of the two fails, return mode as BAD.
*/
if (mixer_ops && mixer_ops->check_mode)
ret = mixer_ops->check_mode(ctx->mixer_ctx->ctx, mode);
if (ret)
return ret;
if (hdmi_ops && hdmi_ops->check_mode)
return hdmi_ops->check_mode(ctx->hdmi_ctx->ctx, mode);
return 0;
}
static int drm_hdmi_power_on(struct device *dev, int mode)
{
struct drm_hdmi_context *ctx = to_context(dev);
if (hdmi_ops && hdmi_ops->power_on)
return hdmi_ops->power_on(ctx->hdmi_ctx->ctx, mode);
return 0;
}
static struct exynos_drm_display_ops drm_hdmi_display_ops = {
.type = EXYNOS_DISPLAY_TYPE_HDMI,
.is_connected = drm_hdmi_is_connected,
.get_edid = drm_hdmi_get_edid,
.check_mode = drm_hdmi_check_mode,
.power_on = drm_hdmi_power_on,
};
static int drm_hdmi_enable_vblank(struct device *subdrv_dev)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
struct exynos_drm_manager *manager = subdrv->manager;
if (mixer_ops && mixer_ops->enable_vblank)
return mixer_ops->enable_vblank(ctx->mixer_ctx->ctx,
manager->pipe);
return 0;
}
static void drm_hdmi_disable_vblank(struct device *subdrv_dev)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
if (mixer_ops && mixer_ops->disable_vblank)
return mixer_ops->disable_vblank(ctx->mixer_ctx->ctx);
}
static void drm_hdmi_wait_for_vblank(struct device *subdrv_dev)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
if (mixer_ops && mixer_ops->wait_for_vblank)
mixer_ops->wait_for_vblank(ctx->mixer_ctx->ctx);
}
static void drm_hdmi_mode_fixup(struct device *subdrv_dev,
struct drm_connector *connector,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_display_mode *m;
int mode_ok;
drm_mode_set_crtcinfo(adjusted_mode, 0);
mode_ok = drm_hdmi_check_mode(subdrv_dev, adjusted_mode);
/* just return if user desired mode exists. */
if (mode_ok == 0)
return;
/*
* otherwise, find the most suitable mode among modes and change it
* to adjusted_mode.
*/
list_for_each_entry(m, &connector->modes, head) {
mode_ok = drm_hdmi_check_mode(subdrv_dev, m);
if (mode_ok == 0) {
struct drm_mode_object base;
struct list_head head;
DRM_INFO("desired mode doesn't exist so\n");
DRM_INFO("use the most suitable mode among modes.\n");
DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n",
m->hdisplay, m->vdisplay, m->vrefresh);
/* preserve display mode header while copying. */
head = adjusted_mode->head;
base = adjusted_mode->base;
memcpy(adjusted_mode, m, sizeof(*m));
adjusted_mode->head = head;
adjusted_mode->base = base;
break;
}
}
}
static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
if (hdmi_ops && hdmi_ops->mode_set)
hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
}
static void drm_hdmi_get_max_resol(struct device *subdrv_dev,
unsigned int *width, unsigned int *height)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
if (hdmi_ops && hdmi_ops->get_max_resol)
hdmi_ops->get_max_resol(ctx->hdmi_ctx->ctx, width, height);
}
static void drm_hdmi_commit(struct device *subdrv_dev)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
if (hdmi_ops && hdmi_ops->commit)
hdmi_ops->commit(ctx->hdmi_ctx->ctx);
}
static void drm_hdmi_dpms(struct device *subdrv_dev, int mode)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
if (mixer_ops && mixer_ops->dpms)
mixer_ops->dpms(ctx->mixer_ctx->ctx, mode);
if (hdmi_ops && hdmi_ops->dpms)
hdmi_ops->dpms(ctx->hdmi_ctx->ctx, mode);
}
static void drm_hdmi_apply(struct device *subdrv_dev)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
int i;
for (i = 0; i < MIXER_WIN_NR; i++) {
if (!ctx->enabled[i])
continue;
if (mixer_ops && mixer_ops->win_commit)
mixer_ops->win_commit(ctx->mixer_ctx->ctx, i);
}
if (hdmi_ops && hdmi_ops->commit)
hdmi_ops->commit(ctx->hdmi_ctx->ctx);
}
static struct exynos_drm_manager_ops drm_hdmi_manager_ops = {
.dpms = drm_hdmi_dpms,
.apply = drm_hdmi_apply,
.enable_vblank = drm_hdmi_enable_vblank,
.disable_vblank = drm_hdmi_disable_vblank,
.wait_for_vblank = drm_hdmi_wait_for_vblank,
.mode_fixup = drm_hdmi_mode_fixup,
.mode_set = drm_hdmi_mode_set,
.get_max_resol = drm_hdmi_get_max_resol,
.commit = drm_hdmi_commit,
};
static void drm_mixer_mode_set(struct device *subdrv_dev,
struct exynos_drm_overlay *overlay)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
if (mixer_ops && mixer_ops->win_mode_set)
mixer_ops->win_mode_set(ctx->mixer_ctx->ctx, overlay);
}
static void drm_mixer_commit(struct device *subdrv_dev, int zpos)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos;
if (win < 0 || win >= MIXER_WIN_NR) {
DRM_ERROR("mixer window[%d] is wrong\n", win);
return;
}
if (mixer_ops && mixer_ops->win_commit)
mixer_ops->win_commit(ctx->mixer_ctx->ctx, win);
ctx->enabled[win] = true;
}
static void drm_mixer_disable(struct device *subdrv_dev, int zpos)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos;
if (win < 0 || win >= MIXER_WIN_NR) {
DRM_ERROR("mixer window[%d] is wrong\n", win);
return;
}
if (mixer_ops && mixer_ops->win_disable)
mixer_ops->win_disable(ctx->mixer_ctx->ctx, win);
ctx->enabled[win] = false;
}
static struct exynos_drm_overlay_ops drm_hdmi_overlay_ops = {
.mode_set = drm_mixer_mode_set,
.commit = drm_mixer_commit,
.disable = drm_mixer_disable,
};
static struct exynos_drm_manager hdmi_manager = {
.pipe = -1,
.ops = &drm_hdmi_manager_ops,
.overlay_ops = &drm_hdmi_overlay_ops,
.display_ops = &drm_hdmi_display_ops,
};
static int hdmi_subdrv_probe(struct drm_device *drm_dev,
struct device *dev)
{
struct exynos_drm_subdrv *subdrv = to_subdrv(dev);
struct drm_hdmi_context *ctx;
if (!hdmi_ctx) {
DRM_ERROR("hdmi context not initialized.\n");
return -EFAULT;
}
if (!mixer_ctx) {
DRM_ERROR("mixer context not initialized.\n");
return -EFAULT;
}
ctx = get_ctx_from_subdrv(subdrv);
if (!ctx) {
DRM_ERROR("no drm hdmi context.\n");
return -EFAULT;
}
ctx->hdmi_ctx = hdmi_ctx;
ctx->mixer_ctx = mixer_ctx;
ctx->hdmi_ctx->drm_dev = drm_dev;
ctx->mixer_ctx->drm_dev = drm_dev;
if (mixer_ops->iommu_on)
mixer_ops->iommu_on(ctx->mixer_ctx->ctx, true);
return 0;
}
static void hdmi_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
{
struct drm_hdmi_context *ctx;
struct exynos_drm_subdrv *subdrv = to_subdrv(dev);
ctx = get_ctx_from_subdrv(subdrv);
if (mixer_ops->iommu_on)
mixer_ops->iommu_on(ctx->mixer_ctx->ctx, false);
}
static int exynos_drm_hdmi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct exynos_drm_subdrv *subdrv;
struct drm_hdmi_context *ctx;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
subdrv = &ctx->subdrv;
subdrv->dev = dev;
subdrv->manager = &hdmi_manager;
subdrv->probe = hdmi_subdrv_probe;
subdrv->remove = hdmi_subdrv_remove;
platform_set_drvdata(pdev, subdrv);
exynos_drm_subdrv_register(subdrv);
return 0;
}
static int exynos_drm_hdmi_remove(struct platform_device *pdev)
{
struct drm_hdmi_context *ctx = platform_get_drvdata(pdev);
exynos_drm_subdrv_unregister(&ctx->subdrv);
return 0;
}
struct platform_driver exynos_drm_common_hdmi_driver = {
.probe = exynos_drm_hdmi_probe,
.remove = exynos_drm_hdmi_remove,
.driver = {
.name = "exynos-drm-hdmi",
.owner = THIS_MODULE,
},
};

View File

@ -1,67 +0,0 @@
/* exynos_drm_hdmi.h
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authoer: Inki Dae <inki.dae@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#ifndef _EXYNOS_DRM_HDMI_H_
#define _EXYNOS_DRM_HDMI_H_
#define MIXER_WIN_NR 3
#define MIXER_DEFAULT_WIN 0
/*
* exynos hdmi common context structure.
*
* @drm_dev: pointer to drm_device.
* @ctx: pointer to the context of specific device driver.
* this context should be hdmi_context or mixer_context.
*/
struct exynos_drm_hdmi_context {
struct drm_device *drm_dev;
void *ctx;
};
struct exynos_hdmi_ops {
/* display */
bool (*is_connected)(void *ctx);
struct edid *(*get_edid)(void *ctx,
struct drm_connector *connector);
int (*check_mode)(void *ctx, struct drm_display_mode *mode);
int (*power_on)(void *ctx, int mode);
/* manager */
void (*mode_set)(void *ctx, struct drm_display_mode *mode);
void (*get_max_resol)(void *ctx, unsigned int *width,
unsigned int *height);
void (*commit)(void *ctx);
void (*dpms)(void *ctx, int mode);
};
struct exynos_mixer_ops {
/* manager */
int (*iommu_on)(void *ctx, bool enable);
int (*enable_vblank)(void *ctx, int pipe);
void (*disable_vblank)(void *ctx);
void (*wait_for_vblank)(void *ctx);
void (*dpms)(void *ctx, int mode);
/* overlay */
void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay);
void (*win_commit)(void *ctx, int zpos);
void (*win_disable)(void *ctx, int zpos);
/* display */
int (*check_mode)(void *ctx, struct drm_display_mode *mode);
};
void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx);
void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx);
void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops);
void exynos_mixer_ops_register(struct exynos_mixer_ops *ops);
#endif

View File

@ -13,7 +13,7 @@
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_encoder.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_gem.h"
#include "exynos_drm_plane.h"
@ -87,7 +87,7 @@ int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
struct exynos_drm_gem_buf *buffer = exynos_drm_fb_buffer(fb, i);
if (!buffer) {
DRM_LOG_KMS("buffer is null\n");
DRM_DEBUG_KMS("buffer is null\n");
return -EFAULT;
}
@ -139,7 +139,7 @@ int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
overlay->crtc_x, overlay->crtc_y,
overlay->crtc_width, overlay->crtc_height);
exynos_drm_fn_encoder(crtc, overlay, exynos_drm_encoder_plane_mode_set);
exynos_drm_crtc_plane_mode_set(crtc, overlay);
return 0;
}
@ -149,8 +149,7 @@ void exynos_plane_commit(struct drm_plane *plane)
struct exynos_plane *exynos_plane = to_exynos_plane(plane);
struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
exynos_drm_encoder_plane_commit);
exynos_drm_crtc_plane_commit(plane->crtc, overlay->zpos);
}
void exynos_plane_dpms(struct drm_plane *plane, int mode)
@ -162,17 +161,13 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode)
if (exynos_plane->enabled)
return;
exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
exynos_drm_encoder_plane_enable);
exynos_drm_crtc_plane_enable(plane->crtc, overlay->zpos);
exynos_plane->enabled = true;
} else {
if (!exynos_plane->enabled)
return;
exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
exynos_drm_encoder_plane_disable);
exynos_drm_crtc_plane_disable(plane->crtc, overlay->zpos);
exynos_plane->enabled = false;
}
}
@ -259,7 +254,7 @@ static void exynos_plane_attach_zpos_property(struct drm_plane *plane)
}
struct drm_plane *exynos_plane_init(struct drm_device *dev,
unsigned int possible_crtcs, bool priv)
unsigned long possible_crtcs, bool priv)
{
struct exynos_plane *exynos_plane;
int err;

View File

@ -17,4 +17,4 @@ int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
void exynos_plane_commit(struct drm_plane *plane);
void exynos_plane_dpms(struct drm_plane *plane, int mode);
struct drm_plane *exynos_plane_init(struct drm_device *dev,
unsigned int possible_crtcs, bool priv);
unsigned long possible_crtcs, bool priv);

View File

@ -28,7 +28,9 @@
/* vidi has totally three virtual windows. */
#define WINDOWS_NR 3
#define get_vidi_context(dev) platform_get_drvdata(to_platform_device(dev))
#define get_vidi_mgr(dev) platform_get_drvdata(to_platform_device(dev))
#define ctx_from_connector(c) container_of(c, struct vidi_context, \
connector)
struct vidi_win_data {
unsigned int offset_x;
@ -45,8 +47,10 @@ struct vidi_win_data {
};
struct vidi_context {
struct exynos_drm_subdrv subdrv;
struct drm_device *drm_dev;
struct drm_crtc *crtc;
struct drm_encoder *encoder;
struct drm_connector connector;
struct vidi_win_data win_data[WINDOWS_NR];
struct edid *raw_edid;
unsigned int clkdiv;
@ -58,6 +62,7 @@ struct vidi_context {
bool direct_vblank;
struct work_struct work;
struct mutex lock;
int pipe;
};
static const char fake_edid_info[] = {
@ -85,126 +90,34 @@ static const char fake_edid_info[] = {
0x00, 0x00, 0x00, 0x06
};
static bool vidi_display_is_connected(struct device *dev)
static void vidi_apply(struct exynos_drm_manager *mgr)
{
struct vidi_context *ctx = get_vidi_context(dev);
/*
* connection request would come from user side
* to do hotplug through specific ioctl.
*/
return ctx->connected ? true : false;
}
static struct edid *vidi_get_edid(struct device *dev,
struct drm_connector *connector)
{
struct vidi_context *ctx = get_vidi_context(dev);
struct edid *edid;
/*
* the edid data comes from user side and it would be set
* to ctx->raw_edid through specific ioctl.
*/
if (!ctx->raw_edid) {
DRM_DEBUG_KMS("raw_edid is null.\n");
return ERR_PTR(-EFAULT);
}
edid = drm_edid_duplicate(ctx->raw_edid);
if (!edid) {
DRM_DEBUG_KMS("failed to allocate edid\n");
return ERR_PTR(-ENOMEM);
}
return edid;
}
static void *vidi_get_panel(struct device *dev)
{
/* TODO. */
return NULL;
}
static int vidi_check_mode(struct device *dev, struct drm_display_mode *mode)
{
/* TODO. */
return 0;
}
static int vidi_display_power_on(struct device *dev, int mode)
{
/* TODO */
return 0;
}
static struct exynos_drm_display_ops vidi_display_ops = {
.type = EXYNOS_DISPLAY_TYPE_VIDI,
.is_connected = vidi_display_is_connected,
.get_edid = vidi_get_edid,
.get_panel = vidi_get_panel,
.check_mode = vidi_check_mode,
.power_on = vidi_display_power_on,
};
static void vidi_dpms(struct device *subdrv_dev, int mode)
{
struct vidi_context *ctx = get_vidi_context(subdrv_dev);
DRM_DEBUG_KMS("%d\n", mode);
mutex_lock(&ctx->lock);
switch (mode) {
case DRM_MODE_DPMS_ON:
/* TODO. */
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
/* TODO. */
break;
default:
DRM_DEBUG_KMS("unspecified mode %d\n", mode);
break;
}
mutex_unlock(&ctx->lock);
}
static void vidi_apply(struct device *subdrv_dev)
{
struct vidi_context *ctx = get_vidi_context(subdrv_dev);
struct exynos_drm_manager *mgr = ctx->subdrv.manager;
struct vidi_context *ctx = mgr->ctx;
struct exynos_drm_manager_ops *mgr_ops = mgr->ops;
struct exynos_drm_overlay_ops *ovl_ops = mgr->overlay_ops;
struct vidi_win_data *win_data;
int i;
for (i = 0; i < WINDOWS_NR; i++) {
win_data = &ctx->win_data[i];
if (win_data->enabled && (ovl_ops && ovl_ops->commit))
ovl_ops->commit(subdrv_dev, i);
if (win_data->enabled && (mgr_ops && mgr_ops->win_commit))
mgr_ops->win_commit(mgr, i);
}
if (mgr_ops && mgr_ops->commit)
mgr_ops->commit(subdrv_dev);
mgr_ops->commit(mgr);
}
static void vidi_commit(struct device *dev)
static void vidi_commit(struct exynos_drm_manager *mgr)
{
struct vidi_context *ctx = get_vidi_context(dev);
struct vidi_context *ctx = mgr->ctx;
if (ctx->suspended)
return;
}
static int vidi_enable_vblank(struct device *dev)
static int vidi_enable_vblank(struct exynos_drm_manager *mgr)
{
struct vidi_context *ctx = get_vidi_context(dev);
struct vidi_context *ctx = mgr->ctx;
if (ctx->suspended)
return -EPERM;
@ -217,16 +130,16 @@ static int vidi_enable_vblank(struct device *dev)
/*
* in case of page flip request, vidi_finish_pageflip function
* will not be called because direct_vblank is true and then
* that function will be called by overlay_ops->commit callback
* that function will be called by manager_ops->win_commit callback
*/
schedule_work(&ctx->work);
return 0;
}
static void vidi_disable_vblank(struct device *dev)
static void vidi_disable_vblank(struct exynos_drm_manager *mgr)
{
struct vidi_context *ctx = get_vidi_context(dev);
struct vidi_context *ctx = mgr->ctx;
if (ctx->suspended)
return;
@ -235,24 +148,16 @@ static void vidi_disable_vblank(struct device *dev)
ctx->vblank_on = false;
}
static struct exynos_drm_manager_ops vidi_manager_ops = {
.dpms = vidi_dpms,
.apply = vidi_apply,
.commit = vidi_commit,
.enable_vblank = vidi_enable_vblank,
.disable_vblank = vidi_disable_vblank,
};
static void vidi_win_mode_set(struct device *dev,
struct exynos_drm_overlay *overlay)
static void vidi_win_mode_set(struct exynos_drm_manager *mgr,
struct exynos_drm_overlay *overlay)
{
struct vidi_context *ctx = get_vidi_context(dev);
struct vidi_context *ctx = mgr->ctx;
struct vidi_win_data *win_data;
int win;
unsigned long offset;
if (!overlay) {
dev_err(dev, "overlay is NULL\n");
DRM_ERROR("overlay is NULL\n");
return;
}
@ -296,9 +201,9 @@ static void vidi_win_mode_set(struct device *dev,
overlay->fb_width, overlay->crtc_width);
}
static void vidi_win_commit(struct device *dev, int zpos)
static void vidi_win_commit(struct exynos_drm_manager *mgr, int zpos)
{
struct vidi_context *ctx = get_vidi_context(dev);
struct vidi_context *ctx = mgr->ctx;
struct vidi_win_data *win_data;
int win = zpos;
@ -321,9 +226,9 @@ static void vidi_win_commit(struct device *dev, int zpos)
schedule_work(&ctx->work);
}
static void vidi_win_disable(struct device *dev, int zpos)
static void vidi_win_disable(struct exynos_drm_manager *mgr, int zpos)
{
struct vidi_context *ctx = get_vidi_context(dev);
struct vidi_context *ctx = mgr->ctx;
struct vidi_win_data *win_data;
int win = zpos;
@ -339,27 +244,107 @@ static void vidi_win_disable(struct device *dev, int zpos)
/* TODO. */
}
static struct exynos_drm_overlay_ops vidi_overlay_ops = {
.mode_set = vidi_win_mode_set,
.commit = vidi_win_commit,
.disable = vidi_win_disable,
static int vidi_power_on(struct exynos_drm_manager *mgr, bool enable)
{
struct vidi_context *ctx = mgr->ctx;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (enable != false && enable != true)
return -EINVAL;
if (enable) {
ctx->suspended = false;
/* if vblank was enabled status, enable it again. */
if (test_and_clear_bit(0, &ctx->irq_flags))
vidi_enable_vblank(mgr);
vidi_apply(mgr);
} else {
ctx->suspended = true;
}
return 0;
}
static void vidi_dpms(struct exynos_drm_manager *mgr, int mode)
{
struct vidi_context *ctx = mgr->ctx;
DRM_DEBUG_KMS("%d\n", mode);
mutex_lock(&ctx->lock);
switch (mode) {
case DRM_MODE_DPMS_ON:
vidi_power_on(mgr, true);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
vidi_power_on(mgr, false);
break;
default:
DRM_DEBUG_KMS("unspecified mode %d\n", mode);
break;
}
mutex_unlock(&ctx->lock);
}
static int vidi_mgr_initialize(struct exynos_drm_manager *mgr,
struct drm_device *drm_dev, int pipe)
{
struct vidi_context *ctx = mgr->ctx;
DRM_ERROR("vidi initialize ct=%p dev=%p pipe=%d\n", ctx, drm_dev, pipe);
ctx->drm_dev = drm_dev;
ctx->pipe = pipe;
/*
* enable drm irq mode.
* - with irq_enabled = 1, we can use the vblank feature.
*
* P.S. note that we wouldn't use drm irq handler but
* just specific driver own one instead because
* drm framework supports only one irq handler.
*/
drm_dev->irq_enabled = 1;
/*
* with vblank_disable_allowed = 1, vblank interrupt will be disabled
* by drm timer once a current process gives up ownership of
* vblank event.(after drm_vblank_put function is called)
*/
drm_dev->vblank_disable_allowed = 1;
return 0;
}
static struct exynos_drm_manager_ops vidi_manager_ops = {
.initialize = vidi_mgr_initialize,
.dpms = vidi_dpms,
.commit = vidi_commit,
.enable_vblank = vidi_enable_vblank,
.disable_vblank = vidi_disable_vblank,
.win_mode_set = vidi_win_mode_set,
.win_commit = vidi_win_commit,
.win_disable = vidi_win_disable,
};
static struct exynos_drm_manager vidi_manager = {
.pipe = -1,
.ops = &vidi_manager_ops,
.overlay_ops = &vidi_overlay_ops,
.display_ops = &vidi_display_ops,
.type = EXYNOS_DISPLAY_TYPE_VIDI,
.ops = &vidi_manager_ops,
};
static void vidi_fake_vblank_handler(struct work_struct *work)
{
struct vidi_context *ctx = container_of(work, struct vidi_context,
work);
struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
struct exynos_drm_manager *manager = subdrv->manager;
if (manager->pipe < 0)
if (ctx->pipe < 0)
return;
/* refresh rate is about 50Hz. */
@ -368,7 +353,7 @@ static void vidi_fake_vblank_handler(struct work_struct *work)
mutex_lock(&ctx->lock);
if (ctx->direct_vblank) {
drm_handle_vblank(subdrv->drm_dev, manager->pipe);
drm_handle_vblank(ctx->drm_dev, ctx->pipe);
ctx->direct_vblank = false;
mutex_unlock(&ctx->lock);
return;
@ -376,61 +361,15 @@ static void vidi_fake_vblank_handler(struct work_struct *work)
mutex_unlock(&ctx->lock);
exynos_drm_crtc_finish_pageflip(subdrv->drm_dev, manager->pipe);
}
static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
{
/*
* enable drm irq mode.
* - with irq_enabled = true, we can use the vblank feature.
*
* P.S. note that we wouldn't use drm irq handler but
* just specific driver own one instead because
* drm framework supports only one irq handler.
*/
drm_dev->irq_enabled = true;
/*
* with vblank_disable_allowed = true, vblank interrupt will be disabled
* by drm timer once a current process gives up ownership of
* vblank event.(after drm_vblank_put function is called)
*/
drm_dev->vblank_disable_allowed = true;
return 0;
}
static void vidi_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
{
/* TODO. */
}
static int vidi_power_on(struct vidi_context *ctx, bool enable)
{
struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
struct device *dev = subdrv->dev;
if (enable) {
ctx->suspended = false;
/* if vblank was enabled status, enable it again. */
if (test_and_clear_bit(0, &ctx->irq_flags))
vidi_enable_vblank(dev);
vidi_apply(dev);
} else {
ctx->suspended = true;
}
return 0;
exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
}
static int vidi_show_connection(struct device *dev,
struct device_attribute *attr, char *buf)
{
int rc;
struct vidi_context *ctx = get_vidi_context(dev);
struct exynos_drm_manager *mgr = get_vidi_mgr(dev);
struct vidi_context *ctx = mgr->ctx;
mutex_lock(&ctx->lock);
@ -445,7 +384,8 @@ static int vidi_store_connection(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct vidi_context *ctx = get_vidi_context(dev);
struct exynos_drm_manager *mgr = get_vidi_mgr(dev);
struct vidi_context *ctx = mgr->ctx;
int ret;
ret = kstrtoint(buf, 0, &ctx->connected);
@ -467,7 +407,7 @@ static int vidi_store_connection(struct device *dev,
DRM_DEBUG_KMS("requested connection.\n");
drm_helper_hpd_irq_event(ctx->subdrv.drm_dev);
drm_helper_hpd_irq_event(ctx->drm_dev);
return len;
}
@ -480,8 +420,7 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
{
struct vidi_context *ctx = NULL;
struct drm_encoder *encoder;
struct exynos_drm_manager *manager;
struct exynos_drm_display_ops *display_ops;
struct exynos_drm_display *display;
struct drm_exynos_vidi_connection *vidi = data;
if (!vidi) {
@ -496,11 +435,10 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
list_for_each_entry(encoder, &drm_dev->mode_config.encoder_list,
head) {
manager = exynos_drm_get_manager(encoder);
display_ops = manager->display_ops;
display = exynos_drm_get_display(encoder);
if (display_ops->type == EXYNOS_DISPLAY_TYPE_VIDI) {
ctx = get_vidi_context(manager->dev);
if (display->type == EXYNOS_DISPLAY_TYPE_VIDI) {
ctx = display->ctx;
break;
}
}
@ -539,16 +477,119 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
}
ctx->connected = vidi->connection;
drm_helper_hpd_irq_event(ctx->subdrv.drm_dev);
drm_helper_hpd_irq_event(ctx->drm_dev);
return 0;
}
static enum drm_connector_status vidi_detect(struct drm_connector *connector,
bool force)
{
struct vidi_context *ctx = ctx_from_connector(connector);
/*
* connection request would come from user side
* to do hotplug through specific ioctl.
*/
return ctx->connected ? connector_status_connected :
connector_status_disconnected;
}
static void vidi_connector_destroy(struct drm_connector *connector)
{
}
static struct drm_connector_funcs vidi_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = vidi_detect,
.destroy = vidi_connector_destroy,
};
static int vidi_get_modes(struct drm_connector *connector)
{
struct vidi_context *ctx = ctx_from_connector(connector);
struct edid *edid;
int edid_len;
/*
* the edid data comes from user side and it would be set
* to ctx->raw_edid through specific ioctl.
*/
if (!ctx->raw_edid) {
DRM_DEBUG_KMS("raw_edid is null.\n");
return -EFAULT;
}
edid_len = (1 + ctx->raw_edid->extensions) * EDID_LENGTH;
edid = kmemdup(ctx->raw_edid, edid_len, GFP_KERNEL);
if (!edid) {
DRM_DEBUG_KMS("failed to allocate edid\n");
return -ENOMEM;
}
drm_mode_connector_update_edid_property(connector, edid);
return drm_add_edid_modes(connector, edid);
}
static int vidi_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
return MODE_OK;
}
static struct drm_encoder *vidi_best_encoder(struct drm_connector *connector)
{
struct vidi_context *ctx = ctx_from_connector(connector);
return ctx->encoder;
}
static struct drm_connector_helper_funcs vidi_connector_helper_funcs = {
.get_modes = vidi_get_modes,
.mode_valid = vidi_mode_valid,
.best_encoder = vidi_best_encoder,
};
static int vidi_create_connector(struct exynos_drm_display *display,
struct drm_encoder *encoder)
{
struct vidi_context *ctx = display->ctx;
struct drm_connector *connector = &ctx->connector;
int ret;
ctx->encoder = encoder;
connector->polled = DRM_CONNECTOR_POLL_HPD;
ret = drm_connector_init(ctx->drm_dev, connector,
&vidi_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL);
if (ret) {
DRM_ERROR("Failed to initialize connector with drm\n");
return ret;
}
drm_connector_helper_add(connector, &vidi_connector_helper_funcs);
drm_sysfs_connector_add(connector);
drm_mode_connector_attach_encoder(connector, encoder);
return 0;
}
static struct exynos_drm_display_ops vidi_display_ops = {
.create_connector = vidi_create_connector,
};
static struct exynos_drm_display vidi_display = {
.type = EXYNOS_DISPLAY_TYPE_VIDI,
.ops = &vidi_display_ops,
};
static int vidi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct vidi_context *ctx;
struct exynos_drm_subdrv *subdrv;
int ret;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
@ -559,21 +600,19 @@ static int vidi_probe(struct platform_device *pdev)
INIT_WORK(&ctx->work, vidi_fake_vblank_handler);
subdrv = &ctx->subdrv;
subdrv->dev = dev;
subdrv->manager = &vidi_manager;
subdrv->probe = vidi_subdrv_probe;
subdrv->remove = vidi_subdrv_remove;
vidi_manager.ctx = ctx;
vidi_display.ctx = ctx;
mutex_init(&ctx->lock);
platform_set_drvdata(pdev, ctx);
platform_set_drvdata(pdev, &vidi_manager);
ret = device_create_file(dev, &dev_attr_connection);
if (ret < 0)
DRM_INFO("failed to create connection sysfs.\n");
exynos_drm_subdrv_register(subdrv);
exynos_drm_manager_register(&vidi_manager);
exynos_drm_display_register(&vidi_display);
return 0;
}
@ -582,7 +621,8 @@ static int vidi_remove(struct platform_device *pdev)
{
struct vidi_context *ctx = platform_get_drvdata(pdev);
exynos_drm_subdrv_unregister(&ctx->subdrv);
exynos_drm_display_unregister(&vidi_display);
exynos_drm_manager_unregister(&vidi_manager);
if (ctx->raw_edid != (struct edid *)fake_edid_info) {
kfree(ctx->raw_edid);
@ -592,32 +632,11 @@ static int vidi_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int vidi_suspend(struct device *dev)
{
struct vidi_context *ctx = get_vidi_context(dev);
return vidi_power_on(ctx, false);
}
static int vidi_resume(struct device *dev)
{
struct vidi_context *ctx = get_vidi_context(dev);
return vidi_power_on(ctx, true);
}
#endif
static const struct dev_pm_ops vidi_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(vidi_suspend, vidi_resume)
};
struct platform_driver vidi_driver = {
.probe = vidi_probe,
.remove = vidi_remove,
.driver = {
.name = "exynos-drm-vidi",
.owner = THIS_MODULE,
.pm = &vidi_pm_ops,
},
};

View File

@ -33,38 +33,42 @@
#include <linux/regulator/consumer.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/i2c.h>
#include <linux/of_gpio.h>
#include <linux/hdmi.h>
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_hdmi.h"
#include "exynos_hdmi.h"
#include "exynos_mixer.h"
#include <linux/gpio.h>
#include <media/s5p_hdmi.h>
#define MAX_WIDTH 1920
#define MAX_HEIGHT 1080
#define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev))
#define get_hdmi_display(dev) platform_get_drvdata(to_platform_device(dev))
#define ctx_from_connector(c) container_of(c, struct hdmi_context, connector)
/* AVI header and aspect ratio */
#define HDMI_AVI_VERSION 0x02
#define HDMI_AVI_LENGTH 0x0D
#define AVI_PIC_ASPECT_RATIO_16_9 (2 << 4)
#define AVI_SAME_AS_PIC_ASPECT_RATIO 8
/* AUI header info */
#define HDMI_AUI_VERSION 0x01
#define HDMI_AUI_LENGTH 0x0A
#define AVI_SAME_AS_PIC_ASPECT_RATIO 0x8
#define AVI_4_3_CENTER_RATIO 0x9
#define AVI_16_9_CENTER_RATIO 0xa
enum hdmi_type {
HDMI_TYPE13,
HDMI_TYPE14,
};
struct hdmi_driver_data {
unsigned int type;
unsigned int is_apb_phy:1;
};
struct hdmi_resources {
struct clk *hdmi;
struct clk *sclk_hdmi;
@ -162,6 +166,7 @@ struct hdmi_v14_conf {
struct hdmi_conf_regs {
int pixel_clock;
int cea_video_id;
enum hdmi_picture_aspect aspect_ratio;
union {
struct hdmi_v13_conf v13_conf;
struct hdmi_v14_conf v14_conf;
@ -171,16 +176,17 @@ struct hdmi_conf_regs {
struct hdmi_context {
struct device *dev;
struct drm_device *drm_dev;
struct drm_connector connector;
struct drm_encoder *encoder;
bool hpd;
bool powered;
bool dvi_mode;
struct mutex hdmi_mutex;
void __iomem *regs;
void *parent_ctx;
int irq;
struct i2c_client *ddc_port;
struct i2c_adapter *ddc_adpt;
struct i2c_client *hdmiphy_port;
/* current hdmiphy conf regs */
@ -198,6 +204,14 @@ struct hdmiphy_config {
u8 conf[32];
};
struct hdmi_driver_data exynos4212_hdmi_driver_data = {
.type = HDMI_TYPE14,
};
struct hdmi_driver_data exynos5_hdmi_driver_data = {
.type = HDMI_TYPE14,
};
/* list of phy config settings */
static const struct hdmiphy_config hdmiphy_v13_configs[] = {
{
@ -302,6 +316,24 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 71000000,
.conf = {
0x01, 0x91, 0x1e, 0x15, 0x40, 0x3c, 0xce, 0x08,
0x04, 0x20, 0xb2, 0xd8, 0x45, 0xa0, 0xac, 0x80,
0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
0x54, 0xad, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 73250000,
.conf = {
0x01, 0xd1, 0x1f, 0x15, 0x40, 0x18, 0xe9, 0x08,
0x02, 0xa0, 0xb7, 0xd8, 0x45, 0xa0, 0xac, 0x80,
0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
0x54, 0xa8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 74176000,
.conf = {
@ -329,6 +361,15 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 88750000,
.conf = {
0x01, 0x91, 0x25, 0x17, 0x40, 0x30, 0xfe, 0x08,
0x06, 0x20, 0xde, 0xd8, 0x45, 0xa0, 0xac, 0x80,
0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
0x54, 0x8a, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 106500000,
.conf = {
@ -347,6 +388,24 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 115500000,
.conf = {
0x01, 0xd1, 0x30, 0x1a, 0x40, 0x40, 0x10, 0x04,
0x04, 0xa0, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80,
0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
0x54, 0xaa, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 119000000,
.conf = {
0x01, 0x91, 0x32, 0x14, 0x40, 0x60, 0xd8, 0x08,
0x06, 0x20, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
0x54, 0x9d, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 146250000,
.conf = {
@ -668,7 +727,6 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata,
{
u32 hdr_sum;
u8 chksum;
u32 aspect_ratio;
u32 mod;
u32 vic;
@ -697,10 +755,28 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata,
AVI_ACTIVE_FORMAT_VALID |
AVI_UNDERSCANNED_DISPLAY_VALID);
aspect_ratio = AVI_PIC_ASPECT_RATIO_16_9;
hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2), aspect_ratio |
AVI_SAME_AS_PIC_ASPECT_RATIO);
/*
* Set the aspect ratio as per the mode, mentioned in
* Table 9 AVI InfoFrame Data Byte 2 of CEA-861-D Standard
*/
switch (hdata->mode_conf.aspect_ratio) {
case HDMI_PICTURE_ASPECT_4_3:
hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2),
hdata->mode_conf.aspect_ratio |
AVI_4_3_CENTER_RATIO);
break;
case HDMI_PICTURE_ASPECT_16_9:
hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2),
hdata->mode_conf.aspect_ratio |
AVI_16_9_CENTER_RATIO);
break;
case HDMI_PICTURE_ASPECT_NONE:
default:
hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2),
hdata->mode_conf.aspect_ratio |
AVI_SAME_AS_PIC_ASPECT_RATIO);
break;
}
vic = hdata->mode_conf.cea_video_id;
hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(4), vic);
@ -728,31 +804,46 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata,
}
}
static bool hdmi_is_connected(void *ctx)
static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
bool force)
{
struct hdmi_context *hdata = ctx;
struct hdmi_context *hdata = ctx_from_connector(connector);
return hdata->hpd;
return hdata->hpd ? connector_status_connected :
connector_status_disconnected;
}
static struct edid *hdmi_get_edid(void *ctx, struct drm_connector *connector)
static void hdmi_connector_destroy(struct drm_connector *connector)
{
struct edid *raw_edid;
struct hdmi_context *hdata = ctx;
}
if (!hdata->ddc_port)
return ERR_PTR(-ENODEV);
static struct drm_connector_funcs hdmi_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = hdmi_detect,
.destroy = hdmi_connector_destroy,
};
raw_edid = drm_get_edid(connector, hdata->ddc_port->adapter);
if (!raw_edid)
return ERR_PTR(-ENODEV);
static int hdmi_get_modes(struct drm_connector *connector)
{
struct hdmi_context *hdata = ctx_from_connector(connector);
struct edid *edid;
hdata->dvi_mode = !drm_detect_hdmi_monitor(raw_edid);
if (!hdata->ddc_adpt)
return -ENODEV;
edid = drm_get_edid(connector, hdata->ddc_adpt);
if (!edid)
return -ENODEV;
hdata->dvi_mode = !drm_detect_hdmi_monitor(edid);
DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n",
(hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"),
raw_edid->width_cm, raw_edid->height_cm);
edid->width_cm, edid->height_cm);
return raw_edid;
drm_mode_connector_update_edid_property(connector, edid);
return drm_add_edid_modes(connector, edid);
}
static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
@ -777,9 +868,10 @@ static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
return -EINVAL;
}
static int hdmi_check_mode(void *ctx, struct drm_display_mode *mode)
static int hdmi_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct hdmi_context *hdata = ctx;
struct hdmi_context *hdata = ctx_from_connector(connector);
int ret;
DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
@ -787,12 +879,103 @@ static int hdmi_check_mode(void *ctx, struct drm_display_mode *mode)
(mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
false, mode->clock * 1000);
ret = mixer_check_mode(mode);
if (ret)
return MODE_BAD;
ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
if (ret < 0)
return MODE_BAD;
return MODE_OK;
}
static struct drm_encoder *hdmi_best_encoder(struct drm_connector *connector)
{
struct hdmi_context *hdata = ctx_from_connector(connector);
return hdata->encoder;
}
static struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
.get_modes = hdmi_get_modes,
.mode_valid = hdmi_mode_valid,
.best_encoder = hdmi_best_encoder,
};
static int hdmi_create_connector(struct exynos_drm_display *display,
struct drm_encoder *encoder)
{
struct hdmi_context *hdata = display->ctx;
struct drm_connector *connector = &hdata->connector;
int ret;
hdata->encoder = encoder;
connector->interlace_allowed = true;
connector->polled = DRM_CONNECTOR_POLL_HPD;
ret = drm_connector_init(hdata->drm_dev, connector,
&hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA);
if (ret) {
DRM_ERROR("Failed to initialize connector with drm\n");
return ret;
}
drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
drm_sysfs_connector_add(connector);
drm_mode_connector_attach_encoder(connector, encoder);
return 0;
}
static int hdmi_initialize(struct exynos_drm_display *display,
struct drm_device *drm_dev)
{
struct hdmi_context *hdata = display->ctx;
hdata->drm_dev = drm_dev;
return 0;
}
static void hdmi_mode_fixup(struct exynos_drm_display *display,
struct drm_connector *connector,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_display_mode *m;
int mode_ok;
DRM_DEBUG_KMS("%s\n", __FILE__);
drm_mode_set_crtcinfo(adjusted_mode, 0);
mode_ok = hdmi_mode_valid(connector, adjusted_mode);
/* just return if user desired mode exists. */
if (mode_ok == MODE_OK)
return;
/*
* otherwise, find the most suitable mode among modes and change it
* to adjusted_mode.
*/
list_for_each_entry(m, &connector->modes, head) {
mode_ok = hdmi_mode_valid(connector, m);
if (mode_ok == MODE_OK) {
DRM_INFO("desired mode doesn't exist so\n");
DRM_INFO("use the most suitable mode among modes.\n");
DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n",
m->hdisplay, m->vdisplay, m->vrefresh);
drm_mode_copy(adjusted_mode, m);
break;
}
}
}
static void hdmi_set_acr(u32 freq, u8 *acr)
{
u32 n, cts;
@ -1421,6 +1604,7 @@ static void hdmi_v13_mode_set(struct hdmi_context *hdata,
hdata->mode_conf.cea_video_id =
drm_match_cea_mode((struct drm_display_mode *)m);
hdata->mode_conf.pixel_clock = m->clock * 1000;
hdata->mode_conf.aspect_ratio = m->picture_aspect_ratio;
hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);
hdmi_set_reg(core->h_v_line, 3, (m->htotal << 12) | m->vtotal);
@ -1517,6 +1701,7 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
hdata->mode_conf.cea_video_id =
drm_match_cea_mode((struct drm_display_mode *)m);
hdata->mode_conf.pixel_clock = m->clock * 1000;
hdata->mode_conf.aspect_ratio = m->picture_aspect_ratio;
hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);
hdmi_set_reg(core->v_line, 2, m->vtotal);
@ -1618,9 +1803,10 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
hdmi_set_reg(tg->tg_3d, 1, 0x0);
}
static void hdmi_mode_set(void *ctx, struct drm_display_mode *mode)
static void hdmi_mode_set(struct exynos_drm_display *display,
struct drm_display_mode *mode)
{
struct hdmi_context *hdata = ctx;
struct hdmi_context *hdata = display->ctx;
struct drm_display_mode *m = mode;
DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n",
@ -1634,16 +1820,9 @@ static void hdmi_mode_set(void *ctx, struct drm_display_mode *mode)
hdmi_v14_mode_set(hdata, mode);
}
static void hdmi_get_max_resol(void *ctx, unsigned int *width,
unsigned int *height)
static void hdmi_commit(struct exynos_drm_display *display)
{
*width = MAX_WIDTH;
*height = MAX_HEIGHT;
}
static void hdmi_commit(void *ctx)
{
struct hdmi_context *hdata = ctx;
struct hdmi_context *hdata = display->ctx;
mutex_lock(&hdata->hdmi_mutex);
if (!hdata->powered) {
@ -1655,8 +1834,9 @@ static void hdmi_commit(void *ctx)
hdmi_conf_apply(hdata);
}
static void hdmi_poweron(struct hdmi_context *hdata)
static void hdmi_poweron(struct exynos_drm_display *display)
{
struct hdmi_context *hdata = display->ctx;
struct hdmi_resources *res = &hdata->res;
mutex_lock(&hdata->hdmi_mutex);
@ -1669,6 +1849,8 @@ static void hdmi_poweron(struct hdmi_context *hdata)
mutex_unlock(&hdata->hdmi_mutex);
pm_runtime_get_sync(hdata->dev);
if (regulator_bulk_enable(res->regul_count, res->regul_bulk))
DRM_DEBUG_KMS("failed to enable regulator bulk\n");
@ -1677,10 +1859,12 @@ static void hdmi_poweron(struct hdmi_context *hdata)
clk_prepare_enable(res->sclk_hdmi);
hdmiphy_poweron(hdata);
hdmi_commit(display);
}
static void hdmi_poweroff(struct hdmi_context *hdata)
static void hdmi_poweroff(struct exynos_drm_display *display)
{
struct hdmi_context *hdata = display->ctx;
struct hdmi_resources *res = &hdata->res;
mutex_lock(&hdata->hdmi_mutex);
@ -1700,30 +1884,27 @@ static void hdmi_poweroff(struct hdmi_context *hdata)
clk_disable_unprepare(res->hdmiphy);
regulator_bulk_disable(res->regul_count, res->regul_bulk);
mutex_lock(&hdata->hdmi_mutex);
pm_runtime_put_sync(hdata->dev);
mutex_lock(&hdata->hdmi_mutex);
hdata->powered = false;
out:
mutex_unlock(&hdata->hdmi_mutex);
}
static void hdmi_dpms(void *ctx, int mode)
static void hdmi_dpms(struct exynos_drm_display *display, int mode)
{
struct hdmi_context *hdata = ctx;
DRM_DEBUG_KMS("mode %d\n", mode);
switch (mode) {
case DRM_MODE_DPMS_ON:
if (pm_runtime_suspended(hdata->dev))
pm_runtime_get_sync(hdata->dev);
hdmi_poweron(display);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
if (!pm_runtime_suspended(hdata->dev))
pm_runtime_put_sync(hdata->dev);
hdmi_poweroff(display);
break;
default:
DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
@ -1731,30 +1912,30 @@ static void hdmi_dpms(void *ctx, int mode)
}
}
static struct exynos_hdmi_ops hdmi_ops = {
/* display */
.is_connected = hdmi_is_connected,
.get_edid = hdmi_get_edid,
.check_mode = hdmi_check_mode,
/* manager */
static struct exynos_drm_display_ops hdmi_display_ops = {
.initialize = hdmi_initialize,
.create_connector = hdmi_create_connector,
.mode_fixup = hdmi_mode_fixup,
.mode_set = hdmi_mode_set,
.get_max_resol = hdmi_get_max_resol,
.commit = hdmi_commit,
.dpms = hdmi_dpms,
.commit = hdmi_commit,
};
static struct exynos_drm_display hdmi_display = {
.type = EXYNOS_DISPLAY_TYPE_HDMI,
.ops = &hdmi_display_ops,
};
static irqreturn_t hdmi_irq_thread(int irq, void *arg)
{
struct exynos_drm_hdmi_context *ctx = arg;
struct hdmi_context *hdata = ctx->ctx;
struct hdmi_context *hdata = arg;
mutex_lock(&hdata->hdmi_mutex);
hdata->hpd = gpio_get_value(hdata->hpd_gpio);
mutex_unlock(&hdata->hdmi_mutex);
if (ctx->drm_dev)
drm_helper_hpd_irq_event(ctx->drm_dev);
if (hdata->drm_dev)
drm_helper_hpd_irq_event(hdata->drm_dev);
return IRQ_HANDLED;
}
@ -1830,20 +2011,6 @@ fail:
return -ENODEV;
}
static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy;
void hdmi_attach_ddc_client(struct i2c_client *ddc)
{
if (ddc)
hdmi_ddc = ddc;
}
void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy)
{
if (hdmiphy)
hdmi_hdmiphy = hdmiphy;
}
static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata
(struct device *dev)
{
@ -1871,10 +2038,10 @@ err_data:
static struct of_device_id hdmi_match_types[] = {
{
.compatible = "samsung,exynos5-hdmi",
.data = (void *)HDMI_TYPE14,
.data = &exynos5_hdmi_driver_data,
}, {
.compatible = "samsung,exynos4212-hdmi",
.data = (void *)HDMI_TYPE14,
.data = &exynos4212_hdmi_driver_data,
}, {
/* end node */
}
@ -1883,11 +2050,12 @@ static struct of_device_id hdmi_match_types[] = {
static int hdmi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct exynos_drm_hdmi_context *drm_hdmi_ctx;
struct hdmi_context *hdata;
struct s5p_hdmi_platform_data *pdata;
struct resource *res;
const struct of_device_id *match;
struct device_node *ddc_node, *phy_node;
struct hdmi_driver_data *drv_data;
int ret;
if (!dev->of_node)
@ -1897,25 +2065,20 @@ static int hdmi_probe(struct platform_device *pdev)
if (!pdata)
return -EINVAL;
drm_hdmi_ctx = devm_kzalloc(dev, sizeof(*drm_hdmi_ctx), GFP_KERNEL);
if (!drm_hdmi_ctx)
return -ENOMEM;
hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
if (!hdata)
return -ENOMEM;
mutex_init(&hdata->hdmi_mutex);
drm_hdmi_ctx->ctx = (void *)hdata;
hdata->parent_ctx = (void *)drm_hdmi_ctx;
platform_set_drvdata(pdev, drm_hdmi_ctx);
platform_set_drvdata(pdev, &hdmi_display);
match = of_match_node(hdmi_match_types, dev->of_node);
if (!match)
return -ENODEV;
hdata->type = (enum hdmi_type)match->data;
drv_data = (struct hdmi_driver_data *)match->data;
hdata->type = drv_data->type;
hdata->hpd_gpio = pdata->hpd_gpio;
hdata->dev = dev;
@ -1938,21 +2101,34 @@ static int hdmi_probe(struct platform_device *pdev)
}
/* DDC i2c driver */
if (i2c_add_driver(&ddc_driver)) {
DRM_ERROR("failed to register ddc i2c driver\n");
return -ENOENT;
ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
if (!ddc_node) {
DRM_ERROR("Failed to find ddc node in device tree\n");
return -ENODEV;
}
hdata->ddc_adpt = of_find_i2c_adapter_by_node(ddc_node);
if (!hdata->ddc_adpt) {
DRM_ERROR("Failed to get ddc i2c adapter by node\n");
return -ENODEV;
}
hdata->ddc_port = hdmi_ddc;
/* Not support APB PHY yet. */
if (drv_data->is_apb_phy)
return -EPERM;
/* hdmiphy i2c driver */
if (i2c_add_driver(&hdmiphy_driver)) {
DRM_ERROR("failed to register hdmiphy i2c driver\n");
ret = -ENOENT;
phy_node = of_parse_phandle(dev->of_node, "phy", 0);
if (!phy_node) {
DRM_ERROR("Failed to find hdmiphy node in device tree\n");
ret = -ENODEV;
goto err_ddc;
}
hdata->hdmiphy_port = of_find_i2c_device_by_node(phy_node);
if (!hdata->hdmiphy_port) {
DRM_ERROR("Failed to get hdmi phy i2c client from node\n");
ret = -ENODEV;
goto err_ddc;
}
hdata->hdmiphy_port = hdmi_hdmiphy;
hdata->irq = gpio_to_irq(hdata->hpd_gpio);
if (hdata->irq < 0) {
@ -1966,119 +2142,45 @@ static int hdmi_probe(struct platform_device *pdev)
ret = devm_request_threaded_irq(dev, hdata->irq, NULL,
hdmi_irq_thread, IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"hdmi", drm_hdmi_ctx);
"hdmi", hdata);
if (ret) {
DRM_ERROR("failed to register hdmi interrupt\n");
goto err_hdmiphy;
}
/* Attach HDMI Driver to common hdmi. */
exynos_hdmi_drv_attach(drm_hdmi_ctx);
/* register specific callbacks to common hdmi. */
exynos_hdmi_ops_register(&hdmi_ops);
pm_runtime_enable(dev);
hdmi_display.ctx = hdata;
exynos_drm_display_register(&hdmi_display);
return 0;
err_hdmiphy:
i2c_del_driver(&hdmiphy_driver);
put_device(&hdata->hdmiphy_port->dev);
err_ddc:
i2c_del_driver(&ddc_driver);
put_device(&hdata->ddc_adpt->dev);
return ret;
}
static int hdmi_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct exynos_drm_display *display = get_hdmi_display(dev);
struct hdmi_context *hdata = display->ctx;
pm_runtime_disable(dev);
/* hdmiphy i2c driver */
i2c_del_driver(&hdmiphy_driver);
/* DDC i2c driver */
i2c_del_driver(&ddc_driver);
put_device(&hdata->hdmiphy_port->dev);
put_device(&hdata->ddc_adpt->dev);
pm_runtime_disable(&pdev->dev);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int hdmi_suspend(struct device *dev)
{
struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
struct hdmi_context *hdata = ctx->ctx;
disable_irq(hdata->irq);
hdata->hpd = false;
if (ctx->drm_dev)
drm_helper_hpd_irq_event(ctx->drm_dev);
if (pm_runtime_suspended(dev)) {
DRM_DEBUG_KMS("Already suspended\n");
return 0;
}
hdmi_poweroff(hdata);
return 0;
}
static int hdmi_resume(struct device *dev)
{
struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
struct hdmi_context *hdata = ctx->ctx;
hdata->hpd = gpio_get_value(hdata->hpd_gpio);
enable_irq(hdata->irq);
if (!pm_runtime_suspended(dev)) {
DRM_DEBUG_KMS("Already resumed\n");
return 0;
}
hdmi_poweron(hdata);
return 0;
}
#endif
#ifdef CONFIG_PM_RUNTIME
static int hdmi_runtime_suspend(struct device *dev)
{
struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
struct hdmi_context *hdata = ctx->ctx;
hdmi_poweroff(hdata);
return 0;
}
static int hdmi_runtime_resume(struct device *dev)
{
struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
struct hdmi_context *hdata = ctx->ctx;
hdmi_poweron(hdata);
return 0;
}
#endif
static const struct dev_pm_ops hdmi_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(hdmi_suspend, hdmi_resume)
SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
};
struct platform_driver hdmi_driver = {
.probe = hdmi_probe,
.remove = hdmi_remove,
.driver = {
.name = "exynos-hdmi",
.owner = THIS_MODULE,
.pm = &hdmi_pm_ops,
.of_match_table = hdmi_match_types,
},
};

View File

@ -36,10 +36,13 @@
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_hdmi.h"
#include "exynos_drm_iommu.h"
#include "exynos_mixer.h"
#define get_mixer_context(dev) platform_get_drvdata(to_platform_device(dev))
#define get_mixer_manager(dev) platform_get_drvdata(to_platform_device(dev))
#define MIXER_WIN_NR 3
#define MIXER_DEFAULT_WIN 0
struct hdmi_win_data {
dma_addr_t dma_addr;
@ -82,6 +85,7 @@ enum mixer_version_id {
};
struct mixer_context {
struct platform_device *pdev;
struct device *dev;
struct drm_device *drm_dev;
int pipe;
@ -94,7 +98,6 @@ struct mixer_context {
struct mixer_resources mixer_res;
struct hdmi_win_data win_data[MIXER_WIN_NR];
enum mixer_version_id mxr_ver;
void *parent_ctx;
wait_queue_head_t wait_vsync_queue;
atomic_t wait_vsync_event;
};
@ -685,30 +688,195 @@ static void mixer_win_reset(struct mixer_context *ctx)
spin_unlock_irqrestore(&res->reg_slock, flags);
}
static int mixer_iommu_on(void *ctx, bool enable)
static irqreturn_t mixer_irq_handler(int irq, void *arg)
{
struct exynos_drm_hdmi_context *drm_hdmi_ctx;
struct mixer_context *mdata = ctx;
struct drm_device *drm_dev;
struct mixer_context *ctx = arg;
struct mixer_resources *res = &ctx->mixer_res;
u32 val, base, shadow;
drm_hdmi_ctx = mdata->parent_ctx;
drm_dev = drm_hdmi_ctx->drm_dev;
spin_lock(&res->reg_slock);
if (is_drm_iommu_supported(drm_dev)) {
if (enable)
return drm_iommu_attach_device(drm_dev, mdata->dev);
/* read interrupt status for handling and clearing flags for VSYNC */
val = mixer_reg_read(res, MXR_INT_STATUS);
drm_iommu_detach_device(drm_dev, mdata->dev);
/* handling VSYNC */
if (val & MXR_INT_STATUS_VSYNC) {
/* interlace scan need to check shadow register */
if (ctx->interlace) {
base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
if (base != shadow)
goto out;
base = mixer_reg_read(res, MXR_GRAPHIC_BASE(1));
shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1));
if (base != shadow)
goto out;
}
drm_handle_vblank(ctx->drm_dev, ctx->pipe);
exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
/* set wait vsync event to zero and wake up queue. */
if (atomic_read(&ctx->wait_vsync_event)) {
atomic_set(&ctx->wait_vsync_event, 0);
wake_up(&ctx->wait_vsync_queue);
}
}
out:
/* clear interrupts */
if (~val & MXR_INT_EN_VSYNC) {
/* vsync interrupt use different bit for read and clear */
val &= ~MXR_INT_EN_VSYNC;
val |= MXR_INT_CLEAR_VSYNC;
}
mixer_reg_write(res, MXR_INT_STATUS, val);
spin_unlock(&res->reg_slock);
return IRQ_HANDLED;
}
static int mixer_resources_init(struct mixer_context *mixer_ctx)
{
struct device *dev = &mixer_ctx->pdev->dev;
struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
struct resource *res;
int ret;
spin_lock_init(&mixer_res->reg_slock);
mixer_res->mixer = devm_clk_get(dev, "mixer");
if (IS_ERR(mixer_res->mixer)) {
dev_err(dev, "failed to get clock 'mixer'\n");
return -ENODEV;
}
mixer_res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
if (IS_ERR(mixer_res->sclk_hdmi)) {
dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
return -ENODEV;
}
res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(dev, "get memory resource failed.\n");
return -ENXIO;
}
mixer_res->mixer_regs = devm_ioremap(dev, res->start,
resource_size(res));
if (mixer_res->mixer_regs == NULL) {
dev_err(dev, "register mapping failed.\n");
return -ENXIO;
}
res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
dev_err(dev, "get interrupt resource failed.\n");
return -ENXIO;
}
ret = devm_request_irq(dev, res->start, mixer_irq_handler,
0, "drm_mixer", mixer_ctx);
if (ret) {
dev_err(dev, "request interrupt failed.\n");
return ret;
}
mixer_res->irq = res->start;
return 0;
}
static int mixer_enable_vblank(void *ctx, int pipe)
static int vp_resources_init(struct mixer_context *mixer_ctx)
{
struct mixer_context *mixer_ctx = ctx;
struct device *dev = &mixer_ctx->pdev->dev;
struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
struct resource *res;
mixer_res->vp = devm_clk_get(dev, "vp");
if (IS_ERR(mixer_res->vp)) {
dev_err(dev, "failed to get clock 'vp'\n");
return -ENODEV;
}
mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
if (IS_ERR(mixer_res->sclk_mixer)) {
dev_err(dev, "failed to get clock 'sclk_mixer'\n");
return -ENODEV;
}
mixer_res->sclk_dac = devm_clk_get(dev, "sclk_dac");
if (IS_ERR(mixer_res->sclk_dac)) {
dev_err(dev, "failed to get clock 'sclk_dac'\n");
return -ENODEV;
}
if (mixer_res->sclk_hdmi)
clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi);
res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 1);
if (res == NULL) {
dev_err(dev, "get memory resource failed.\n");
return -ENXIO;
}
mixer_res->vp_regs = devm_ioremap(dev, res->start,
resource_size(res));
if (mixer_res->vp_regs == NULL) {
dev_err(dev, "register mapping failed.\n");
return -ENXIO;
}
return 0;
}
static int mixer_initialize(struct exynos_drm_manager *mgr,
struct drm_device *drm_dev, int pipe)
{
int ret;
struct mixer_context *mixer_ctx = mgr->ctx;
mixer_ctx->drm_dev = drm_dev;
mixer_ctx->pipe = pipe;
/* acquire resources: regs, irqs, clocks */
ret = mixer_resources_init(mixer_ctx);
if (ret) {
DRM_ERROR("mixer_resources_init failed ret=%d\n", ret);
return ret;
}
if (mixer_ctx->vp_enabled) {
/* acquire vp resources: regs, irqs, clocks */
ret = vp_resources_init(mixer_ctx);
if (ret) {
DRM_ERROR("vp_resources_init failed ret=%d\n", ret);
return ret;
}
}
if (!is_drm_iommu_supported(mixer_ctx->drm_dev))
return 0;
return drm_iommu_attach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
}
static void mixer_mgr_remove(struct exynos_drm_manager *mgr)
{
struct mixer_context *mixer_ctx = mgr->ctx;
if (is_drm_iommu_supported(mixer_ctx->drm_dev))
drm_iommu_detach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
}
static int mixer_enable_vblank(struct exynos_drm_manager *mgr)
{
struct mixer_context *mixer_ctx = mgr->ctx;
struct mixer_resources *res = &mixer_ctx->mixer_res;
mixer_ctx->pipe = pipe;
if (!mixer_ctx->powered) {
mixer_ctx->int_en |= MXR_INT_EN_VSYNC;
return 0;
}
/* enable vsync interrupt */
mixer_reg_writemask(res, MXR_INT_EN, MXR_INT_EN_VSYNC,
@ -717,19 +885,19 @@ static int mixer_enable_vblank(void *ctx, int pipe)
return 0;
}
static void mixer_disable_vblank(void *ctx)
static void mixer_disable_vblank(struct exynos_drm_manager *mgr)
{
struct mixer_context *mixer_ctx = ctx;
struct mixer_context *mixer_ctx = mgr->ctx;
struct mixer_resources *res = &mixer_ctx->mixer_res;
/* disable vsync interrupt */
mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
}
static void mixer_win_mode_set(void *ctx,
struct exynos_drm_overlay *overlay)
static void mixer_win_mode_set(struct exynos_drm_manager *mgr,
struct exynos_drm_overlay *overlay)
{
struct mixer_context *mixer_ctx = ctx;
struct mixer_context *mixer_ctx = mgr->ctx;
struct hdmi_win_data *win_data;
int win;
@ -778,9 +946,10 @@ static void mixer_win_mode_set(void *ctx,
win_data->scan_flags = overlay->scan_flag;
}
static void mixer_win_commit(void *ctx, int win)
static void mixer_win_commit(struct exynos_drm_manager *mgr, int zpos)
{
struct mixer_context *mixer_ctx = ctx;
struct mixer_context *mixer_ctx = mgr->ctx;
int win = zpos == DEFAULT_ZPOS ? MIXER_DEFAULT_WIN : zpos;
DRM_DEBUG_KMS("win: %d\n", win);
@ -799,10 +968,11 @@ static void mixer_win_commit(void *ctx, int win)
mixer_ctx->win_data[win].enabled = true;
}
static void mixer_win_disable(void *ctx, int win)
static void mixer_win_disable(struct exynos_drm_manager *mgr, int zpos)
{
struct mixer_context *mixer_ctx = ctx;
struct mixer_context *mixer_ctx = mgr->ctx;
struct mixer_resources *res = &mixer_ctx->mixer_res;
int win = zpos == DEFAULT_ZPOS ? MIXER_DEFAULT_WIN : zpos;
unsigned long flags;
DRM_DEBUG_KMS("win: %d\n", win);
@ -826,32 +996,9 @@ static void mixer_win_disable(void *ctx, int win)
mixer_ctx->win_data[win].enabled = false;
}
static int mixer_check_mode(void *ctx, struct drm_display_mode *mode)
static void mixer_wait_for_vblank(struct exynos_drm_manager *mgr)
{
struct mixer_context *mixer_ctx = ctx;
u32 w, h;
w = mode->hdisplay;
h = mode->vdisplay;
DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d\n",
mode->hdisplay, mode->vdisplay, mode->vrefresh,
(mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
if (mixer_ctx->mxr_ver == MXR_VER_0_0_0_16 ||
mixer_ctx->mxr_ver == MXR_VER_128_0_0_184)
return 0;
if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) ||
(w >= 1024 && w <= 1280 && h >= 576 && h <= 720) ||
(w >= 1664 && w <= 1920 && h >= 936 && h <= 1080))
return 0;
return -EINVAL;
}
static void mixer_wait_for_vblank(void *ctx)
{
struct mixer_context *mixer_ctx = ctx;
struct mixer_context *mixer_ctx = mgr->ctx;
mutex_lock(&mixer_ctx->mixer_mutex);
if (!mixer_ctx->powered) {
@ -872,21 +1019,23 @@ static void mixer_wait_for_vblank(void *ctx)
DRM_DEBUG_KMS("vblank wait timed out.\n");
}
static void mixer_window_suspend(struct mixer_context *ctx)
static void mixer_window_suspend(struct exynos_drm_manager *mgr)
{
struct mixer_context *ctx = mgr->ctx;
struct hdmi_win_data *win_data;
int i;
for (i = 0; i < MIXER_WIN_NR; i++) {
win_data = &ctx->win_data[i];
win_data->resume = win_data->enabled;
mixer_win_disable(ctx, i);
mixer_win_disable(mgr, i);
}
mixer_wait_for_vblank(ctx);
mixer_wait_for_vblank(mgr);
}
static void mixer_window_resume(struct mixer_context *ctx)
static void mixer_window_resume(struct exynos_drm_manager *mgr)
{
struct mixer_context *ctx = mgr->ctx;
struct hdmi_win_data *win_data;
int i;
@ -894,11 +1043,14 @@ static void mixer_window_resume(struct mixer_context *ctx)
win_data = &ctx->win_data[i];
win_data->enabled = win_data->resume;
win_data->resume = false;
if (win_data->enabled)
mixer_win_commit(mgr, i);
}
}
static void mixer_poweron(struct mixer_context *ctx)
static void mixer_poweron(struct exynos_drm_manager *mgr)
{
struct mixer_context *ctx = mgr->ctx;
struct mixer_resources *res = &ctx->mixer_res;
mutex_lock(&ctx->mixer_mutex);
@ -909,6 +1061,8 @@ static void mixer_poweron(struct mixer_context *ctx)
ctx->powered = true;
mutex_unlock(&ctx->mixer_mutex);
pm_runtime_get_sync(ctx->dev);
clk_prepare_enable(res->mixer);
if (ctx->vp_enabled) {
clk_prepare_enable(res->vp);
@ -918,11 +1072,12 @@ static void mixer_poweron(struct mixer_context *ctx)
mixer_reg_write(res, MXR_INT_EN, ctx->int_en);
mixer_win_reset(ctx);
mixer_window_resume(ctx);
mixer_window_resume(mgr);
}
static void mixer_poweroff(struct mixer_context *ctx)
static void mixer_poweroff(struct exynos_drm_manager *mgr)
{
struct mixer_context *ctx = mgr->ctx;
struct mixer_resources *res = &ctx->mixer_res;
mutex_lock(&ctx->mixer_mutex);
@ -930,7 +1085,7 @@ static void mixer_poweroff(struct mixer_context *ctx)
goto out;
mutex_unlock(&ctx->mixer_mutex);
mixer_window_suspend(ctx);
mixer_window_suspend(mgr);
ctx->int_en = mixer_reg_read(res, MXR_INT_EN);
@ -940,6 +1095,8 @@ static void mixer_poweroff(struct mixer_context *ctx)
clk_disable_unprepare(res->sclk_mixer);
}
pm_runtime_put_sync(ctx->dev);
mutex_lock(&ctx->mixer_mutex);
ctx->powered = false;
@ -947,20 +1104,16 @@ out:
mutex_unlock(&ctx->mixer_mutex);
}
static void mixer_dpms(void *ctx, int mode)
static void mixer_dpms(struct exynos_drm_manager *mgr, int mode)
{
struct mixer_context *mixer_ctx = ctx;
switch (mode) {
case DRM_MODE_DPMS_ON:
if (pm_runtime_suspended(mixer_ctx->dev))
pm_runtime_get_sync(mixer_ctx->dev);
mixer_poweron(mgr);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
if (!pm_runtime_suspended(mixer_ctx->dev))
pm_runtime_put_sync(mixer_ctx->dev);
mixer_poweroff(mgr);
break;
default:
DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
@ -968,169 +1121,42 @@ static void mixer_dpms(void *ctx, int mode)
}
}
static struct exynos_mixer_ops mixer_ops = {
/* manager */
.iommu_on = mixer_iommu_on,
/* Only valid for Mixer version 16.0.33.0 */
int mixer_check_mode(struct drm_display_mode *mode)
{
u32 w, h;
w = mode->hdisplay;
h = mode->vdisplay;
DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d\n",
mode->hdisplay, mode->vdisplay, mode->vrefresh,
(mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) ||
(w >= 1024 && w <= 1280 && h >= 576 && h <= 720) ||
(w >= 1664 && w <= 1920 && h >= 936 && h <= 1080))
return 0;
return -EINVAL;
}
static struct exynos_drm_manager_ops mixer_manager_ops = {
.initialize = mixer_initialize,
.remove = mixer_mgr_remove,
.dpms = mixer_dpms,
.enable_vblank = mixer_enable_vblank,
.disable_vblank = mixer_disable_vblank,
.wait_for_vblank = mixer_wait_for_vblank,
.dpms = mixer_dpms,
/* overlay */
.win_mode_set = mixer_win_mode_set,
.win_commit = mixer_win_commit,
.win_disable = mixer_win_disable,
/* display */
.check_mode = mixer_check_mode,
};
static irqreturn_t mixer_irq_handler(int irq, void *arg)
{
struct exynos_drm_hdmi_context *drm_hdmi_ctx = arg;
struct mixer_context *ctx = drm_hdmi_ctx->ctx;
struct mixer_resources *res = &ctx->mixer_res;
u32 val, base, shadow;
spin_lock(&res->reg_slock);
/* read interrupt status for handling and clearing flags for VSYNC */
val = mixer_reg_read(res, MXR_INT_STATUS);
/* handling VSYNC */
if (val & MXR_INT_STATUS_VSYNC) {
/* interlace scan need to check shadow register */
if (ctx->interlace) {
base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
if (base != shadow)
goto out;
base = mixer_reg_read(res, MXR_GRAPHIC_BASE(1));
shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1));
if (base != shadow)
goto out;
}
drm_handle_vblank(drm_hdmi_ctx->drm_dev, ctx->pipe);
exynos_drm_crtc_finish_pageflip(drm_hdmi_ctx->drm_dev,
ctx->pipe);
/* set wait vsync event to zero and wake up queue. */
if (atomic_read(&ctx->wait_vsync_event)) {
atomic_set(&ctx->wait_vsync_event, 0);
wake_up(&ctx->wait_vsync_queue);
}
}
out:
/* clear interrupts */
if (~val & MXR_INT_EN_VSYNC) {
/* vsync interrupt use different bit for read and clear */
val &= ~MXR_INT_EN_VSYNC;
val |= MXR_INT_CLEAR_VSYNC;
}
mixer_reg_write(res, MXR_INT_STATUS, val);
spin_unlock(&res->reg_slock);
return IRQ_HANDLED;
}
static int mixer_resources_init(struct exynos_drm_hdmi_context *ctx,
struct platform_device *pdev)
{
struct mixer_context *mixer_ctx = ctx->ctx;
struct device *dev = &pdev->dev;
struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
struct resource *res;
int ret;
spin_lock_init(&mixer_res->reg_slock);
mixer_res->mixer = devm_clk_get(dev, "mixer");
if (IS_ERR(mixer_res->mixer)) {
dev_err(dev, "failed to get clock 'mixer'\n");
return -ENODEV;
}
mixer_res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
if (IS_ERR(mixer_res->sclk_hdmi)) {
dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
return -ENODEV;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(dev, "get memory resource failed.\n");
return -ENXIO;
}
mixer_res->mixer_regs = devm_ioremap(dev, res->start,
resource_size(res));
if (mixer_res->mixer_regs == NULL) {
dev_err(dev, "register mapping failed.\n");
return -ENXIO;
}
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
dev_err(dev, "get interrupt resource failed.\n");
return -ENXIO;
}
ret = devm_request_irq(dev, res->start, mixer_irq_handler,
0, "drm_mixer", ctx);
if (ret) {
dev_err(dev, "request interrupt failed.\n");
return ret;
}
mixer_res->irq = res->start;
return 0;
}
static int vp_resources_init(struct exynos_drm_hdmi_context *ctx,
struct platform_device *pdev)
{
struct mixer_context *mixer_ctx = ctx->ctx;
struct device *dev = &pdev->dev;
struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
struct resource *res;
mixer_res->vp = devm_clk_get(dev, "vp");
if (IS_ERR(mixer_res->vp)) {
dev_err(dev, "failed to get clock 'vp'\n");
return -ENODEV;
}
mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
if (IS_ERR(mixer_res->sclk_mixer)) {
dev_err(dev, "failed to get clock 'sclk_mixer'\n");
return -ENODEV;
}
mixer_res->sclk_dac = devm_clk_get(dev, "sclk_dac");
if (IS_ERR(mixer_res->sclk_dac)) {
dev_err(dev, "failed to get clock 'sclk_dac'\n");
return -ENODEV;
}
if (mixer_res->sclk_hdmi)
clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (res == NULL) {
dev_err(dev, "get memory resource failed.\n");
return -ENXIO;
}
mixer_res->vp_regs = devm_ioremap(dev, res->start,
resource_size(res));
if (mixer_res->vp_regs == NULL) {
dev_err(dev, "register mapping failed.\n");
return -ENXIO;
}
return 0;
}
static struct exynos_drm_manager mixer_manager = {
.type = EXYNOS_DISPLAY_TYPE_HDMI,
.ops = &mixer_manager_ops,
};
static struct mixer_drv_data exynos5420_mxr_drv_data = {
.version = MXR_VER_128_0_0_184,
@ -1177,21 +1203,16 @@ static struct of_device_id mixer_match_types[] = {
static int mixer_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct exynos_drm_hdmi_context *drm_hdmi_ctx;
struct mixer_context *ctx;
struct mixer_drv_data *drv;
int ret;
dev_info(dev, "probe start\n");
drm_hdmi_ctx = devm_kzalloc(dev, sizeof(*drm_hdmi_ctx),
GFP_KERNEL);
if (!drm_hdmi_ctx)
return -ENOMEM;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx) {
DRM_ERROR("failed to alloc mixer context.\n");
return -ENOMEM;
}
mutex_init(&ctx->mixer_mutex);
@ -1204,46 +1225,20 @@ static int mixer_probe(struct platform_device *pdev)
platform_get_device_id(pdev)->driver_data;
}
ctx->pdev = pdev;
ctx->dev = dev;
ctx->parent_ctx = (void *)drm_hdmi_ctx;
drm_hdmi_ctx->ctx = (void *)ctx;
ctx->vp_enabled = drv->is_vp_enabled;
ctx->mxr_ver = drv->version;
init_waitqueue_head(&ctx->wait_vsync_queue);
atomic_set(&ctx->wait_vsync_event, 0);
platform_set_drvdata(pdev, drm_hdmi_ctx);
/* acquire resources: regs, irqs, clocks */
ret = mixer_resources_init(drm_hdmi_ctx, pdev);
if (ret) {
DRM_ERROR("mixer_resources_init failed\n");
goto fail;
}
if (ctx->vp_enabled) {
/* acquire vp resources: regs, irqs, clocks */
ret = vp_resources_init(drm_hdmi_ctx, pdev);
if (ret) {
DRM_ERROR("vp_resources_init failed\n");
goto fail;
}
}
/* attach mixer driver to common hdmi. */
exynos_mixer_drv_attach(drm_hdmi_ctx);
/* register specific callback point to common hdmi. */
exynos_mixer_ops_register(&mixer_ops);
mixer_manager.ctx = ctx;
platform_set_drvdata(pdev, &mixer_manager);
exynos_drm_manager_register(&mixer_manager);
pm_runtime_enable(dev);
return 0;
fail:
dev_info(dev, "probe failed\n");
return ret;
}
static int mixer_remove(struct platform_device *pdev)
@ -1255,70 +1250,10 @@ static int mixer_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int mixer_suspend(struct device *dev)
{
struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
struct mixer_context *ctx = drm_hdmi_ctx->ctx;
if (pm_runtime_suspended(dev)) {
DRM_DEBUG_KMS("Already suspended\n");
return 0;
}
mixer_poweroff(ctx);
return 0;
}
static int mixer_resume(struct device *dev)
{
struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
struct mixer_context *ctx = drm_hdmi_ctx->ctx;
if (!pm_runtime_suspended(dev)) {
DRM_DEBUG_KMS("Already resumed\n");
return 0;
}
mixer_poweron(ctx);
return 0;
}
#endif
#ifdef CONFIG_PM_RUNTIME
static int mixer_runtime_suspend(struct device *dev)
{
struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
struct mixer_context *ctx = drm_hdmi_ctx->ctx;
mixer_poweroff(ctx);
return 0;
}
static int mixer_runtime_resume(struct device *dev)
{
struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
struct mixer_context *ctx = drm_hdmi_ctx->ctx;
mixer_poweron(ctx);
return 0;
}
#endif
static const struct dev_pm_ops mixer_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(mixer_suspend, mixer_resume)
SET_RUNTIME_PM_OPS(mixer_runtime_suspend, mixer_runtime_resume, NULL)
};
struct platform_driver mixer_driver = {
.driver = {
.name = "exynos-mixer",
.owner = THIS_MODULE,
.pm = &mixer_pm_ops,
.of_match_table = mixer_match_types,
},
.probe = mixer_probe,

View File

@ -0,0 +1,20 @@
/*
* Copyright (C) 2013 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _EXYNOS_MIXER_H_
#define _EXYNOS_MIXER_H_
/* This function returns 0 if the given timing is valid for the mixer */
int mixer_check_mode(struct drm_display_mode *mode);
#endif

View File

@ -13,9 +13,11 @@ gma500_gfx-y += \
intel_i2c.o \
intel_gmbus.o \
mmu.o \
blitter.o \
power.o \
psb_drv.o \
gma_display.o \
gma_device.o \
psb_intel_display.o \
psb_intel_lvds.o \
psb_intel_modes.o \

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2014, Patrik Jakobsson
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* Authors: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
*/
#include "psb_drv.h"
#include "blitter.h"
#include "psb_reg.h"
/* Wait for the blitter to be completely idle */
int gma_blt_wait_idle(struct drm_psb_private *dev_priv)
{
unsigned long stop = jiffies + HZ;
int busy = 1;
/* NOP for Cedarview */
if (IS_CDV(dev_priv->dev))
return 0;
/* First do a quick check */
if ((PSB_RSGX32(PSB_CR_2D_SOCIF) == _PSB_C2_SOCIF_EMPTY) &&
((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) & _PSB_C2B_STATUS_BUSY) == 0))
return 0;
do {
busy = (PSB_RSGX32(PSB_CR_2D_SOCIF) != _PSB_C2_SOCIF_EMPTY);
} while (busy && !time_after_eq(jiffies, stop));
if (busy)
return -EBUSY;
do {
busy = ((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) &
_PSB_C2B_STATUS_BUSY) != 0);
} while (busy && !time_after_eq(jiffies, stop));
/* If still busy, we probably have a hang */
return (busy) ? -EBUSY : 0;
}

View File

@ -0,0 +1,22 @@
/*
* Copyright (c) 2014, Patrik Jakobsson
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* Authors: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
*/
#ifndef __BLITTER_H
#define __BLITTER_H
extern int gma_blt_wait_idle(struct drm_psb_private *dev_priv);
#endif

View File

@ -26,6 +26,7 @@
#include "psb_intel_reg.h"
#include "intel_bios.h"
#include "cdv_device.h"
#include "gma_device.h"
#define VGA_SR_INDEX 0x3c4
#define VGA_SR_DATA 0x3c5
@ -426,43 +427,6 @@ static int cdv_power_up(struct drm_device *dev)
return 0;
}
/* FIXME ? - shared with Poulsbo */
static void cdv_get_core_freq(struct drm_device *dev)
{
uint32_t clock;
struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0);
struct drm_psb_private *dev_priv = dev->dev_private;
pci_write_config_dword(pci_root, 0xD0, 0xD0050300);
pci_read_config_dword(pci_root, 0xD4, &clock);
pci_dev_put(pci_root);
switch (clock & 0x07) {
case 0:
dev_priv->core_freq = 100;
break;
case 1:
dev_priv->core_freq = 133;
break;
case 2:
dev_priv->core_freq = 150;
break;
case 3:
dev_priv->core_freq = 178;
break;
case 4:
dev_priv->core_freq = 200;
break;
case 5:
case 6:
case 7:
dev_priv->core_freq = 266;
break;
default:
dev_priv->core_freq = 0;
}
}
static void cdv_hotplug_work_func(struct work_struct *work)
{
struct drm_psb_private *dev_priv = container_of(work, struct drm_psb_private,
@ -618,7 +582,7 @@ static int cdv_chip_setup(struct drm_device *dev)
if (pci_enable_msi(dev->pdev))
dev_warn(dev->dev, "Enabling MSI failed!\n");
dev_priv->regmap = cdv_regmap;
cdv_get_core_freq(dev);
gma_get_core_freq(dev);
psb_intel_opregion_init(dev);
psb_intel_init_bios(dev);
cdv_hotplug_enable(dev, false);

View File

@ -81,13 +81,6 @@ static int cdv_intel_crt_mode_valid(struct drm_connector *connector,
return MODE_OK;
}
static bool cdv_intel_crt_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
return true;
}
static void cdv_intel_crt_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
@ -224,7 +217,7 @@ static int cdv_intel_crt_set_property(struct drm_connector *connector,
static const struct drm_encoder_helper_funcs cdv_intel_crt_helper_funcs = {
.dpms = cdv_intel_crt_dpms,
.mode_fixup = cdv_intel_crt_mode_fixup,
.mode_fixup = gma_encoder_mode_fixup,
.prepare = gma_encoder_prepare,
.commit = gma_encoder_commit,
.mode_set = cdv_intel_crt_mode_set,

View File

@ -412,8 +412,11 @@ static bool cdv_intel_find_dp_pll(const struct gma_limit_t *limit,
int refclk,
struct gma_clock_t *best_clock)
{
struct gma_crtc *gma_crtc = to_gma_crtc(crtc);
struct gma_clock_t clock;
if (refclk == 27000) {
switch (refclk) {
case 27000:
if (target < 200000) {
clock.p1 = 2;
clock.p2 = 10;
@ -427,7 +430,9 @@ static bool cdv_intel_find_dp_pll(const struct gma_limit_t *limit,
clock.m1 = 0;
clock.m2 = 98;
}
} else if (refclk == 100000) {
break;
case 100000:
if (target < 200000) {
clock.p1 = 2;
clock.p2 = 10;
@ -441,12 +446,13 @@ static bool cdv_intel_find_dp_pll(const struct gma_limit_t *limit,
clock.m1 = 0;
clock.m2 = 133;
}
} else
break;
default:
return false;
clock.m = clock.m2 + 2;
clock.p = clock.p1 * clock.p2;
clock.vco = (refclk * clock.m) / clock.n;
clock.dot = clock.vco / clock.p;
}
gma_crtc->clock_funcs->clock(refclk, &clock);
memcpy(best_clock, &clock, sizeof(struct gma_clock_t));
return true;
}
@ -463,54 +469,11 @@ static bool cdv_intel_pipe_enabled(struct drm_device *dev, int pipe)
crtc = dev_priv->pipe_to_crtc_mapping[pipe];
gma_crtc = to_gma_crtc(crtc);
if (crtc->fb == NULL || !gma_crtc->active)
if (crtc->primary->fb == NULL || !gma_crtc->active)
return false;
return true;
}
static bool cdv_intel_single_pipe_active (struct drm_device *dev)
{
uint32_t pipe_enabled = 0;
if (cdv_intel_pipe_enabled(dev, 0))
pipe_enabled |= FIFO_PIPEA;
if (cdv_intel_pipe_enabled(dev, 1))
pipe_enabled |= FIFO_PIPEB;
DRM_DEBUG_KMS("pipe enabled %x\n", pipe_enabled);
if (pipe_enabled == FIFO_PIPEA || pipe_enabled == FIFO_PIPEB)
return true;
else
return false;
}
static bool is_pipeb_lvds(struct drm_device *dev, struct drm_crtc *crtc)
{
struct gma_crtc *gma_crtc = to_gma_crtc(crtc);
struct drm_mode_config *mode_config = &dev->mode_config;
struct drm_connector *connector;
if (gma_crtc->pipe != 1)
return false;
list_for_each_entry(connector, &mode_config->connector_list, head) {
struct gma_encoder *gma_encoder =
gma_attached_encoder(connector);
if (!connector->encoder
|| connector->encoder->crtc != crtc)
continue;
if (gma_encoder->type == INTEL_OUTPUT_LVDS)
return true;
}
return false;
}
void cdv_disable_sr(struct drm_device *dev)
{
if (REG_READ(FW_BLC_SELF) & FW_BLC_SELF_EN) {
@ -535,8 +498,10 @@ void cdv_disable_sr(struct drm_device *dev)
void cdv_update_wm(struct drm_device *dev, struct drm_crtc *crtc)
{
struct drm_psb_private *dev_priv = dev->dev_private;
struct gma_crtc *gma_crtc = to_gma_crtc(crtc);
if (cdv_intel_single_pipe_active(dev)) {
/* Is only one pipe enabled? */
if (cdv_intel_pipe_enabled(dev, 0) ^ cdv_intel_pipe_enabled(dev, 1)) {
u32 fw;
fw = REG_READ(DSPFW1);
@ -557,7 +522,9 @@ void cdv_update_wm(struct drm_device *dev, struct drm_crtc *crtc)
/* ignore FW4 */
if (is_pipeb_lvds(dev, crtc)) {
/* Is pipe b lvds ? */
if (gma_crtc->pipe == 1 &&
gma_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
REG_WRITE(DSPFW5, 0x00040330);
} else {
fw = (3 << DSP_PLANE_B_FIFO_WM1_SHIFT) |

View File

@ -1693,7 +1693,7 @@ done:
struct drm_crtc *crtc = encoder->base.crtc;
drm_crtc_helper_set_mode(crtc, &crtc->mode,
crtc->x, crtc->y,
crtc->fb);
crtc->primary->fb);
}
return 0;

View File

@ -89,13 +89,6 @@ static void cdv_hdmi_mode_set(struct drm_encoder *encoder,
REG_READ(hdmi_priv->hdmi_reg);
}
static bool cdv_hdmi_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
return true;
}
static void cdv_hdmi_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
@ -199,7 +192,7 @@ static int cdv_hdmi_set_property(struct drm_connector *connector,
crtc->saved_mode.vdisplay != 0) {
if (centre) {
if (!drm_crtc_helper_set_mode(encoder->crtc, &crtc->saved_mode,
encoder->crtc->x, encoder->crtc->y, encoder->crtc->fb))
encoder->crtc->x, encoder->crtc->y, encoder->crtc->primary->fb))
return -1;
} else {
struct drm_encoder_helper_funcs *helpers
@ -262,7 +255,7 @@ static void cdv_hdmi_destroy(struct drm_connector *connector)
static const struct drm_encoder_helper_funcs cdv_hdmi_helper_funcs = {
.dpms = cdv_hdmi_dpms,
.mode_fixup = cdv_hdmi_mode_fixup,
.mode_fixup = gma_encoder_mode_fixup,
.prepare = gma_encoder_prepare,
.mode_set = cdv_hdmi_mode_set,
.commit = gma_encoder_commit,

View File

@ -494,7 +494,7 @@ static int cdv_intel_lvds_set_property(struct drm_connector *connector,
&crtc->saved_mode,
encoder->crtc->x,
encoder->crtc->y,
encoder->crtc->fb))
encoder->crtc->primary->fb))
return -1;
}
} else if (!strcmp(property->name, "backlight") && encoder) {
@ -712,6 +712,7 @@ void cdv_intel_lvds_init(struct drm_device *dev,
* Attempt to get the fixed panel mode from DDC. Assume that the
* preferred mode is the right one.
*/
mutex_lock(&dev->mode_config.mutex);
psb_intel_ddc_get_modes(connector,
&gma_encoder->ddc_bus->adapter);
list_for_each_entry(scan, &connector->probed_modes, head) {
@ -772,10 +773,12 @@ void cdv_intel_lvds_init(struct drm_device *dev,
}
out:
mutex_unlock(&dev->mode_config.mutex);
drm_sysfs_connector_add(connector);
return;
failed_find:
mutex_unlock(&dev->mode_config.mutex);
printk(KERN_ERR "Failed find\n");
if (gma_encoder->ddc_bus)
psb_intel_i2c_destroy(gma_encoder->ddc_bus);

View File

@ -319,7 +319,7 @@ static struct gtt_range *psbfb_alloc(struct drm_device *dev, int aligned_size)
{
struct gtt_range *backing;
/* Begin by trying to use stolen memory backing */
backing = psb_gtt_alloc_range(dev, aligned_size, "fb", 1);
backing = psb_gtt_alloc_range(dev, aligned_size, "fb", 1, PAGE_SIZE);
if (backing) {
drm_gem_private_object_init(dev, &backing->gem, aligned_size);
return backing;

View File

@ -62,9 +62,6 @@ int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev,
int ret = 0;
struct drm_gem_object *obj;
if (!(dev->driver->driver_features & DRIVER_GEM))
return -ENODEV;
mutex_lock(&dev->struct_mutex);
/* GEM does all our handle to object mapping */
@ -98,8 +95,8 @@ unlock:
* it so that userspace can speak about it. This does the core work
* for the various methods that do/will create GEM objects for things
*/
static int psb_gem_create(struct drm_file *file,
struct drm_device *dev, uint64_t size, uint32_t *handlep)
int psb_gem_create(struct drm_file *file, struct drm_device *dev, u64 size,
u32 *handlep, int stolen, u32 align)
{
struct gtt_range *r;
int ret;
@ -109,7 +106,7 @@ static int psb_gem_create(struct drm_file *file,
/* Allocate our object - for now a direct gtt range which is not
stolen memory backed */
r = psb_gtt_alloc_range(dev, size, "gem", 0);
r = psb_gtt_alloc_range(dev, size, "gem", 0, PAGE_SIZE);
if (r == NULL) {
dev_err(dev->dev, "no memory for %lld byte GEM object\n", size);
return -ENOSPC;
@ -153,7 +150,8 @@ int psb_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
{
args->pitch = ALIGN(args->width * ((args->bpp + 7) / 8), 64);
args->size = args->pitch * args->height;
return psb_gem_create(file, dev, args->size, &args->handle);
return psb_gem_create(file, dev, args->size, &args->handle, 0,
PAGE_SIZE);
}
/**
@ -229,47 +227,3 @@ fail:
return VM_FAULT_SIGBUS;
}
}
static int psb_gem_create_stolen(struct drm_file *file, struct drm_device *dev,
int size, u32 *handle)
{
struct gtt_range *gtt = psb_gtt_alloc_range(dev, size, "gem", 1);
if (gtt == NULL)
return -ENOMEM;
drm_gem_private_object_init(dev, &gtt->gem, size);
if (drm_gem_handle_create(file, &gtt->gem, handle) == 0)
return 0;
drm_gem_object_release(&gtt->gem);
psb_gtt_free_range(dev, gtt);
return -ENOMEM;
}
/*
* GEM interfaces for our specific client
*/
int psb_gem_create_ioctl(struct drm_device *dev, void *data,
struct drm_file *file)
{
struct drm_psb_gem_create *args = data;
int ret;
if (args->flags & GMA_GEM_CREATE_STOLEN) {
ret = psb_gem_create_stolen(file, dev, args->size,
&args->handle);
if (ret == 0)
return 0;
/* Fall throguh */
args->flags &= ~GMA_GEM_CREATE_STOLEN;
}
return psb_gem_create(file, dev, args->size, &args->handle);
}
int psb_gem_mmap_ioctl(struct drm_device *dev, void *data,
struct drm_file *file)
{
struct drm_psb_gem_mmap *args = data;
return dev->driver->dumb_map_offset(file, dev,
args->handle, &args->offset);
}

View File

@ -0,0 +1,21 @@
/**************************************************************************
* Copyright (c) 2014 Patrik Jakobsson
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
**************************************************************************/
#ifndef _GEM_H
#define _GEM_H
extern int psb_gem_create(struct drm_file *file, struct drm_device *dev,
u64 size, u32 *handlep, int stolen, u32 align);
#endif

View File

@ -0,0 +1,56 @@
/**************************************************************************
* Copyright (c) 2011, Intel Corporation.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
**************************************************************************/
#include <drm/drmP.h>
#include "psb_drv.h"
void gma_get_core_freq(struct drm_device *dev)
{
uint32_t clock;
struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0);
struct drm_psb_private *dev_priv = dev->dev_private;
/*pci_write_config_dword(pci_root, 0xD4, 0x00C32004);*/
/*pci_write_config_dword(pci_root, 0xD0, 0xE0033000);*/
pci_write_config_dword(pci_root, 0xD0, 0xD0050300);
pci_read_config_dword(pci_root, 0xD4, &clock);
pci_dev_put(pci_root);
switch (clock & 0x07) {
case 0:
dev_priv->core_freq = 100;
break;
case 1:
dev_priv->core_freq = 133;
break;
case 2:
dev_priv->core_freq = 150;
break;
case 3:
dev_priv->core_freq = 178;
break;
case 4:
dev_priv->core_freq = 200;
break;
case 5:
case 6:
case 7:
dev_priv->core_freq = 266;
break;
default:
dev_priv->core_freq = 0;
}
}

View File

@ -0,0 +1,21 @@
/**************************************************************************
* Copyright (c) 2011, Intel Corporation.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
**************************************************************************/
#ifndef _GMA_DEVICE_H
#define _GMA_DEVICE_H
extern void gma_get_core_freq(struct drm_device *dev);
#endif

View File

@ -59,7 +59,7 @@ int gma_pipe_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_device *dev = crtc->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
struct gma_crtc *gma_crtc = to_gma_crtc(crtc);
struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb);
struct psb_framebuffer *psbfb = to_psb_fb(crtc->primary->fb);
int pipe = gma_crtc->pipe;
const struct psb_offset *map = &dev_priv->regmap[pipe];
unsigned long start, offset;
@ -70,7 +70,7 @@ int gma_pipe_set_base(struct drm_crtc *crtc, int x, int y,
return 0;
/* no fb bound */
if (!crtc->fb) {
if (!crtc->primary->fb) {
dev_err(dev->dev, "No FB bound\n");
goto gma_pipe_cleaner;
}
@ -81,19 +81,19 @@ int gma_pipe_set_base(struct drm_crtc *crtc, int x, int y,
if (ret < 0)
goto gma_pipe_set_base_exit;
start = psbfb->gtt->offset;
offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8);
offset = y * crtc->primary->fb->pitches[0] + x * (crtc->primary->fb->bits_per_pixel / 8);
REG_WRITE(map->stride, crtc->fb->pitches[0]);
REG_WRITE(map->stride, crtc->primary->fb->pitches[0]);
dspcntr = REG_READ(map->cntr);
dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
switch (crtc->fb->bits_per_pixel) {
switch (crtc->primary->fb->bits_per_pixel) {
case 8:
dspcntr |= DISPPLANE_8BPP;
break;
case 16:
if (crtc->fb->depth == 15)
if (crtc->primary->fb->depth == 15)
dspcntr |= DISPPLANE_15_16BPP;
else
dspcntr |= DISPPLANE_16BPP;
@ -485,6 +485,13 @@ int gma_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
return 0;
}
bool gma_encoder_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
return true;
}
bool gma_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
@ -511,8 +518,8 @@ void gma_crtc_disable(struct drm_crtc *crtc)
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
if (crtc->fb) {
gt = to_psb_fb(crtc->fb)->gtt;
if (crtc->primary->fb) {
gt = to_psb_fb(crtc->primary->fb)->gtt;
psb_gtt_unpin(gt);
}
}

View File

@ -90,6 +90,9 @@ extern void gma_crtc_restore(struct drm_crtc *crtc);
extern void gma_encoder_prepare(struct drm_encoder *encoder);
extern void gma_encoder_commit(struct drm_encoder *encoder);
extern void gma_encoder_destroy(struct drm_encoder *encoder);
extern bool gma_encoder_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
/* Common clock related functions */
extern const struct gma_limit_t *gma_limit(struct drm_crtc *crtc, int refclk);

View File

@ -22,6 +22,7 @@
#include <drm/drmP.h>
#include <linux/shmem_fs.h>
#include "psb_drv.h"
#include "blitter.h"
/*
@ -105,11 +106,13 @@ static int psb_gtt_insert(struct drm_device *dev, struct gtt_range *r,
/* Write our page entries into the GTT itself */
for (i = r->roll; i < r->npage; i++) {
pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0);
pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]),
PSB_MMU_CACHED_MEMORY);
iowrite32(pte, gtt_slot++);
}
for (i = 0; i < r->roll; i++) {
pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0);
pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]),
PSB_MMU_CACHED_MEMORY);
iowrite32(pte, gtt_slot++);
}
/* Make sure all the entries are set before we return */
@ -127,7 +130,7 @@ static int psb_gtt_insert(struct drm_device *dev, struct gtt_range *r,
* page table entries with the dummy page. This is protected via the gtt
* mutex which the caller must hold.
*/
static void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r)
void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r)
{
struct drm_psb_private *dev_priv = dev->dev_private;
u32 __iomem *gtt_slot;
@ -137,7 +140,8 @@ static void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r)
WARN_ON(r->stolen);
gtt_slot = psb_gtt_entry(dev, r);
pte = psb_gtt_mask_pte(page_to_pfn(dev_priv->scratch_page), 0);
pte = psb_gtt_mask_pte(page_to_pfn(dev_priv->scratch_page),
PSB_MMU_CACHED_MEMORY);
for (i = 0; i < r->npage; i++)
iowrite32(pte, gtt_slot++);
@ -176,11 +180,13 @@ void psb_gtt_roll(struct drm_device *dev, struct gtt_range *r, int roll)
gtt_slot = psb_gtt_entry(dev, r);
for (i = r->roll; i < r->npage; i++) {
pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0);
pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]),
PSB_MMU_CACHED_MEMORY);
iowrite32(pte, gtt_slot++);
}
for (i = 0; i < r->roll; i++) {
pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0);
pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]),
PSB_MMU_CACHED_MEMORY);
iowrite32(pte, gtt_slot++);
}
ioread32(gtt_slot - 1);
@ -240,6 +246,7 @@ int psb_gtt_pin(struct gtt_range *gt)
int ret = 0;
struct drm_device *dev = gt->gem.dev;
struct drm_psb_private *dev_priv = dev->dev_private;
u32 gpu_base = dev_priv->gtt.gatt_start;
mutex_lock(&dev_priv->gtt_mutex);
@ -252,6 +259,9 @@ int psb_gtt_pin(struct gtt_range *gt)
psb_gtt_detach_pages(gt);
goto out;
}
psb_mmu_insert_pages(psb_mmu_get_default_pd(dev_priv->mmu),
gt->pages, (gpu_base + gt->offset),
gt->npage, 0, 0, PSB_MMU_CACHED_MEMORY);
}
gt->in_gart++;
out:
@ -274,16 +284,30 @@ void psb_gtt_unpin(struct gtt_range *gt)
{
struct drm_device *dev = gt->gem.dev;
struct drm_psb_private *dev_priv = dev->dev_private;
u32 gpu_base = dev_priv->gtt.gatt_start;
int ret;
/* While holding the gtt_mutex no new blits can be initiated */
mutex_lock(&dev_priv->gtt_mutex);
/* Wait for any possible usage of the memory to be finished */
ret = gma_blt_wait_idle(dev_priv);
if (ret) {
DRM_ERROR("Failed to idle the blitter, unpin failed!");
goto out;
}
WARN_ON(!gt->in_gart);
gt->in_gart--;
if (gt->in_gart == 0 && gt->stolen == 0) {
psb_mmu_remove_pages(psb_mmu_get_default_pd(dev_priv->mmu),
(gpu_base + gt->offset), gt->npage, 0, 0);
psb_gtt_remove(dev, gt);
psb_gtt_detach_pages(gt);
}
out:
mutex_unlock(&dev_priv->gtt_mutex);
}
@ -306,7 +330,7 @@ void psb_gtt_unpin(struct gtt_range *gt)
* as in use.
*/
struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len,
const char *name, int backed)
const char *name, int backed, u32 align)
{
struct drm_psb_private *dev_priv = dev->dev_private;
struct gtt_range *gt;
@ -334,7 +358,7 @@ struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len,
/* Ensure this is set for non GEM objects */
gt->gem.dev = dev;
ret = allocate_resource(dev_priv->gtt_mem, &gt->resource,
len, start, end, PAGE_SIZE, NULL, NULL);
len, start, end, align, NULL, NULL);
if (ret == 0) {
gt->offset = gt->resource.start - r->start;
return gt;
@ -497,6 +521,7 @@ int psb_gtt_init(struct drm_device *dev, int resume)
if (!resume)
dev_priv->vram_addr = ioremap_wc(dev_priv->stolen_base,
stolen_size);
if (!dev_priv->vram_addr) {
dev_err(dev->dev, "Failure to map stolen base.\n");
ret = -ENOMEM;
@ -512,7 +537,7 @@ int psb_gtt_init(struct drm_device *dev, int resume)
dev_dbg(dev->dev, "Set up %d stolen pages starting at 0x%08x, GTT offset %dK\n",
num_pages, pfn_base << PAGE_SHIFT, 0);
for (i = 0; i < num_pages; ++i) {
pte = psb_gtt_mask_pte(pfn_base + i, 0);
pte = psb_gtt_mask_pte(pfn_base + i, PSB_MMU_CACHED_MEMORY);
iowrite32(pte, dev_priv->gtt_map + i);
}
@ -521,7 +546,7 @@ int psb_gtt_init(struct drm_device *dev, int resume)
*/
pfn_base = page_to_pfn(dev_priv->scratch_page);
pte = psb_gtt_mask_pte(pfn_base, 0);
pte = psb_gtt_mask_pte(pfn_base, PSB_MMU_CACHED_MEMORY);
for (; i < gtt_pages; ++i)
iowrite32(pte, dev_priv->gtt_map + i);

View File

@ -53,7 +53,8 @@ struct gtt_range {
};
extern struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len,
const char *name, int backed);
const char *name, int backed,
u32 align);
extern void psb_gtt_kref_put(struct gtt_range *gt);
extern void psb_gtt_free_range(struct drm_device *dev, struct gtt_range *gt);
extern int psb_gtt_pin(struct gtt_range *gt);

View File

@ -287,7 +287,7 @@ static int mdfld_dsi_connector_set_property(struct drm_connector *connector,
&gma_crtc->saved_mode,
encoder->crtc->x,
encoder->crtc->y,
encoder->crtc->fb))
encoder->crtc->primary->fb))
goto set_prop_error;
} else {
struct drm_encoder_helper_funcs *funcs =

View File

@ -166,7 +166,7 @@ static int mdfld__intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_device *dev = crtc->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
struct gma_crtc *gma_crtc = to_gma_crtc(crtc);
struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb);
struct psb_framebuffer *psbfb = to_psb_fb(crtc->primary->fb);
int pipe = gma_crtc->pipe;
const struct psb_offset *map = &dev_priv->regmap[pipe];
unsigned long start, offset;
@ -178,12 +178,12 @@ static int mdfld__intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
dev_dbg(dev->dev, "pipe = 0x%x.\n", pipe);
/* no fb bound */
if (!crtc->fb) {
if (!crtc->primary->fb) {
dev_dbg(dev->dev, "No FB bound\n");
return 0;
}
ret = check_fb(crtc->fb);
ret = check_fb(crtc->primary->fb);
if (ret)
return ret;
@ -196,18 +196,18 @@ static int mdfld__intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
return 0;
start = psbfb->gtt->offset;
offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8);
offset = y * crtc->primary->fb->pitches[0] + x * (crtc->primary->fb->bits_per_pixel / 8);
REG_WRITE(map->stride, crtc->fb->pitches[0]);
REG_WRITE(map->stride, crtc->primary->fb->pitches[0]);
dspcntr = REG_READ(map->cntr);
dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
switch (crtc->fb->bits_per_pixel) {
switch (crtc->primary->fb->bits_per_pixel) {
case 8:
dspcntr |= DISPPLANE_8BPP;
break;
case 16:
if (crtc->fb->depth == 15)
if (crtc->primary->fb->depth == 15)
dspcntr |= DISPPLANE_15_16BPP;
else
dspcntr |= DISPPLANE_16BPP;
@ -700,7 +700,7 @@ static int mdfld_crtc_mode_set(struct drm_crtc *crtc,
}
#endif
ret = check_fb(crtc->fb);
ret = check_fb(crtc->primary->fb);
if (ret)
return ret;

View File

@ -18,6 +18,7 @@
#include <drm/drmP.h>
#include "psb_drv.h"
#include "psb_reg.h"
#include "mmu.h"
/*
* Code for the SGX MMU:
@ -47,51 +48,6 @@
* but on average it should be fast.
*/
struct psb_mmu_driver {
/* protects driver- and pd structures. Always take in read mode
* before taking the page table spinlock.
*/
struct rw_semaphore sem;
/* protects page tables, directory tables and pt tables.
* and pt structures.
*/
spinlock_t lock;
atomic_t needs_tlbflush;
uint8_t __iomem *register_map;
struct psb_mmu_pd *default_pd;
/*uint32_t bif_ctrl;*/
int has_clflush;
int clflush_add;
unsigned long clflush_mask;
struct drm_psb_private *dev_priv;
};
struct psb_mmu_pd;
struct psb_mmu_pt {
struct psb_mmu_pd *pd;
uint32_t index;
uint32_t count;
struct page *p;
uint32_t *v;
};
struct psb_mmu_pd {
struct psb_mmu_driver *driver;
int hw_context;
struct psb_mmu_pt **tables;
struct page *p;
struct page *dummy_pt;
struct page *dummy_page;
uint32_t pd_mask;
uint32_t invalid_pde;
uint32_t invalid_pte;
};
static inline uint32_t psb_mmu_pt_index(uint32_t offset)
{
return (offset >> PSB_PTE_SHIFT) & 0x3FF;
@ -102,13 +58,13 @@ static inline uint32_t psb_mmu_pd_index(uint32_t offset)
return offset >> PSB_PDE_SHIFT;
}
#if defined(CONFIG_X86)
static inline void psb_clflush(void *addr)
{
__asm__ __volatile__("clflush (%0)\n" : : "r"(addr) : "memory");
}
static inline void psb_mmu_clflush(struct psb_mmu_driver *driver,
void *addr)
static inline void psb_mmu_clflush(struct psb_mmu_driver *driver, void *addr)
{
if (!driver->has_clflush)
return;
@ -117,62 +73,77 @@ static inline void psb_mmu_clflush(struct psb_mmu_driver *driver,
psb_clflush(addr);
mb();
}
#else
static void psb_page_clflush(struct psb_mmu_driver *driver, struct page* page)
static inline void psb_mmu_clflush(struct psb_mmu_driver *driver, void *addr)
{;
}
#endif
static void psb_mmu_flush_pd_locked(struct psb_mmu_driver *driver, int force)
{
uint32_t clflush_add = driver->clflush_add >> PAGE_SHIFT;
uint32_t clflush_count = PAGE_SIZE / clflush_add;
int i;
uint8_t *clf;
struct drm_device *dev = driver->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
clf = kmap_atomic(page);
mb();
for (i = 0; i < clflush_count; ++i) {
psb_clflush(clf);
clf += clflush_add;
if (atomic_read(&driver->needs_tlbflush) || force) {
uint32_t val = PSB_RSGX32(PSB_CR_BIF_CTRL);
PSB_WSGX32(val | _PSB_CB_CTRL_INVALDC, PSB_CR_BIF_CTRL);
/* Make sure data cache is turned off before enabling it */
wmb();
PSB_WSGX32(val & ~_PSB_CB_CTRL_INVALDC, PSB_CR_BIF_CTRL);
(void)PSB_RSGX32(PSB_CR_BIF_CTRL);
if (driver->msvdx_mmu_invaldc)
atomic_set(driver->msvdx_mmu_invaldc, 1);
}
mb();
kunmap_atomic(clf);
}
static void psb_pages_clflush(struct psb_mmu_driver *driver,
struct page *page[], unsigned long num_pages)
{
int i;
if (!driver->has_clflush)
return ;
for (i = 0; i < num_pages; i++)
psb_page_clflush(driver, *page++);
}
static void psb_mmu_flush_pd_locked(struct psb_mmu_driver *driver,
int force)
{
atomic_set(&driver->needs_tlbflush, 0);
}
#if 0
static void psb_mmu_flush_pd(struct psb_mmu_driver *driver, int force)
{
down_write(&driver->sem);
psb_mmu_flush_pd_locked(driver, force);
up_write(&driver->sem);
}
#endif
void psb_mmu_flush(struct psb_mmu_driver *driver, int rc_prot)
void psb_mmu_flush(struct psb_mmu_driver *driver)
{
if (rc_prot)
down_write(&driver->sem);
if (rc_prot)
up_write(&driver->sem);
struct drm_device *dev = driver->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
uint32_t val;
down_write(&driver->sem);
val = PSB_RSGX32(PSB_CR_BIF_CTRL);
if (atomic_read(&driver->needs_tlbflush))
PSB_WSGX32(val | _PSB_CB_CTRL_INVALDC, PSB_CR_BIF_CTRL);
else
PSB_WSGX32(val | _PSB_CB_CTRL_FLUSH, PSB_CR_BIF_CTRL);
/* Make sure data cache is turned off and MMU is flushed before
restoring bank interface control register */
wmb();
PSB_WSGX32(val & ~(_PSB_CB_CTRL_FLUSH | _PSB_CB_CTRL_INVALDC),
PSB_CR_BIF_CTRL);
(void)PSB_RSGX32(PSB_CR_BIF_CTRL);
atomic_set(&driver->needs_tlbflush, 0);
if (driver->msvdx_mmu_invaldc)
atomic_set(driver->msvdx_mmu_invaldc, 1);
up_write(&driver->sem);
}
void psb_mmu_set_pd_context(struct psb_mmu_pd *pd, int hw_context)
{
/*ttm_tt_cache_flush(&pd->p, 1);*/
psb_pages_clflush(pd->driver, &pd->p, 1);
struct drm_device *dev = pd->driver->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
uint32_t offset = (hw_context == 0) ? PSB_CR_BIF_DIR_LIST_BASE0 :
PSB_CR_BIF_DIR_LIST_BASE1 + hw_context * 4;
down_write(&pd->driver->sem);
PSB_WSGX32(page_to_pfn(pd->p) << PAGE_SHIFT, offset);
wmb();
psb_mmu_flush_pd_locked(pd->driver, 1);
pd->hw_context = hw_context;
@ -183,7 +154,6 @@ void psb_mmu_set_pd_context(struct psb_mmu_pd *pd, int hw_context)
static inline unsigned long psb_pd_addr_end(unsigned long addr,
unsigned long end)
{
addr = (addr + PSB_PDE_MASK + 1) & ~PSB_PDE_MASK;
return (addr < end) ? addr : end;
}
@ -223,12 +193,10 @@ struct psb_mmu_pd *psb_mmu_alloc_pd(struct psb_mmu_driver *driver,
goto out_err3;
if (!trap_pagefaults) {
pd->invalid_pde =
psb_mmu_mask_pte(page_to_pfn(pd->dummy_pt),
invalid_type);
pd->invalid_pte =
psb_mmu_mask_pte(page_to_pfn(pd->dummy_page),
invalid_type);
pd->invalid_pde = psb_mmu_mask_pte(page_to_pfn(pd->dummy_pt),
invalid_type);
pd->invalid_pte = psb_mmu_mask_pte(page_to_pfn(pd->dummy_page),
invalid_type);
} else {
pd->invalid_pde = 0;
pd->invalid_pte = 0;
@ -279,12 +247,16 @@ static void psb_mmu_free_pt(struct psb_mmu_pt *pt)
void psb_mmu_free_pagedir(struct psb_mmu_pd *pd)
{
struct psb_mmu_driver *driver = pd->driver;
struct drm_device *dev = driver->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
struct psb_mmu_pt *pt;
int i;
down_write(&driver->sem);
if (pd->hw_context != -1)
if (pd->hw_context != -1) {
PSB_WSGX32(0, PSB_CR_BIF_DIR_LIST_BASE0 + pd->hw_context * 4);
psb_mmu_flush_pd_locked(driver, 1);
}
/* Should take the spinlock here, but we don't need to do that
since we have the semaphore in write mode. */
@ -331,7 +303,7 @@ static struct psb_mmu_pt *psb_mmu_alloc_pt(struct psb_mmu_pd *pd)
for (i = 0; i < (PAGE_SIZE / sizeof(uint32_t)); ++i)
*ptes++ = pd->invalid_pte;
#if defined(CONFIG_X86)
if (pd->driver->has_clflush && pd->hw_context != -1) {
mb();
for (i = 0; i < clflush_count; ++i) {
@ -340,7 +312,7 @@ static struct psb_mmu_pt *psb_mmu_alloc_pt(struct psb_mmu_pd *pd)
}
mb();
}
#endif
kunmap_atomic(v);
spin_unlock(lock);
@ -351,7 +323,7 @@ static struct psb_mmu_pt *psb_mmu_alloc_pt(struct psb_mmu_pd *pd)
return pt;
}
static struct psb_mmu_pt *psb_mmu_pt_alloc_map_lock(struct psb_mmu_pd *pd,
struct psb_mmu_pt *psb_mmu_pt_alloc_map_lock(struct psb_mmu_pd *pd,
unsigned long addr)
{
uint32_t index = psb_mmu_pd_index(addr);
@ -383,7 +355,7 @@ static struct psb_mmu_pt *psb_mmu_pt_alloc_map_lock(struct psb_mmu_pd *pd,
kunmap_atomic((void *) v);
if (pd->hw_context != -1) {
psb_mmu_clflush(pd->driver, (void *) &v[index]);
psb_mmu_clflush(pd->driver, (void *)&v[index]);
atomic_set(&pd->driver->needs_tlbflush, 1);
}
}
@ -420,8 +392,7 @@ static void psb_mmu_pt_unmap_unlock(struct psb_mmu_pt *pt)
pd->tables[pt->index] = NULL;
if (pd->hw_context != -1) {
psb_mmu_clflush(pd->driver,
(void *) &v[pt->index]);
psb_mmu_clflush(pd->driver, (void *)&v[pt->index]);
atomic_set(&pd->driver->needs_tlbflush, 1);
}
kunmap_atomic(pt->v);
@ -432,8 +403,8 @@ static void psb_mmu_pt_unmap_unlock(struct psb_mmu_pt *pt)
spin_unlock(&pd->driver->lock);
}
static inline void psb_mmu_set_pte(struct psb_mmu_pt *pt,
unsigned long addr, uint32_t pte)
static inline void psb_mmu_set_pte(struct psb_mmu_pt *pt, unsigned long addr,
uint32_t pte)
{
pt->v[psb_mmu_pt_index(addr)] = pte;
}
@ -444,69 +415,50 @@ static inline void psb_mmu_invalidate_pte(struct psb_mmu_pt *pt,
pt->v[psb_mmu_pt_index(addr)] = pt->pd->invalid_pte;
}
void psb_mmu_mirror_gtt(struct psb_mmu_pd *pd,
uint32_t mmu_offset, uint32_t gtt_start,
uint32_t gtt_pages)
{
uint32_t *v;
uint32_t start = psb_mmu_pd_index(mmu_offset);
struct psb_mmu_driver *driver = pd->driver;
int num_pages = gtt_pages;
down_read(&driver->sem);
spin_lock(&driver->lock);
v = kmap_atomic(pd->p);
v += start;
while (gtt_pages--) {
*v++ = gtt_start | pd->pd_mask;
gtt_start += PAGE_SIZE;
}
/*ttm_tt_cache_flush(&pd->p, num_pages);*/
psb_pages_clflush(pd->driver, &pd->p, num_pages);
kunmap_atomic(v);
spin_unlock(&driver->lock);
if (pd->hw_context != -1)
atomic_set(&pd->driver->needs_tlbflush, 1);
up_read(&pd->driver->sem);
psb_mmu_flush_pd(pd->driver, 0);
}
struct psb_mmu_pd *psb_mmu_get_default_pd(struct psb_mmu_driver *driver)
{
struct psb_mmu_pd *pd;
/* down_read(&driver->sem); */
down_read(&driver->sem);
pd = driver->default_pd;
/* up_read(&driver->sem); */
up_read(&driver->sem);
return pd;
}
/* Returns the physical address of the PD shared by sgx/msvdx */
uint32_t psb_get_default_pd_addr(struct psb_mmu_driver *driver)
{
struct psb_mmu_pd *pd;
pd = psb_mmu_get_default_pd(driver);
return page_to_pfn(pd->p) << PAGE_SHIFT;
}
void psb_mmu_driver_takedown(struct psb_mmu_driver *driver)
{
struct drm_device *dev = driver->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
PSB_WSGX32(driver->bif_ctrl, PSB_CR_BIF_CTRL);
psb_mmu_free_pagedir(driver->default_pd);
kfree(driver);
}
struct psb_mmu_driver *psb_mmu_driver_init(uint8_t __iomem * registers,
int trap_pagefaults,
int invalid_type,
struct drm_psb_private *dev_priv)
struct psb_mmu_driver *psb_mmu_driver_init(struct drm_device *dev,
int trap_pagefaults,
int invalid_type,
atomic_t *msvdx_mmu_invaldc)
{
struct psb_mmu_driver *driver;
struct drm_psb_private *dev_priv = dev->dev_private;
driver = kmalloc(sizeof(*driver), GFP_KERNEL);
if (!driver)
return NULL;
driver->dev_priv = dev_priv;
driver->dev = dev;
driver->default_pd = psb_mmu_alloc_pd(driver, trap_pagefaults,
invalid_type);
if (!driver->default_pd)
@ -515,17 +467,24 @@ struct psb_mmu_driver *psb_mmu_driver_init(uint8_t __iomem * registers,
spin_lock_init(&driver->lock);
init_rwsem(&driver->sem);
down_write(&driver->sem);
driver->register_map = registers;
atomic_set(&driver->needs_tlbflush, 1);
driver->msvdx_mmu_invaldc = msvdx_mmu_invaldc;
driver->bif_ctrl = PSB_RSGX32(PSB_CR_BIF_CTRL);
PSB_WSGX32(driver->bif_ctrl | _PSB_CB_CTRL_CLEAR_FAULT,
PSB_CR_BIF_CTRL);
PSB_WSGX32(driver->bif_ctrl & ~_PSB_CB_CTRL_CLEAR_FAULT,
PSB_CR_BIF_CTRL);
driver->has_clflush = 0;
#if defined(CONFIG_X86)
if (boot_cpu_has(X86_FEATURE_CLFLSH)) {
uint32_t tfms, misc, cap0, cap4, clflush_size;
/*
* clflush size is determined at kernel setup for x86_64
* but not for i386. We have to do it here.
* clflush size is determined at kernel setup for x86_64 but not
* for i386. We have to do it here.
*/
cpuid(0x00000001, &tfms, &misc, &cap0, &cap4);
@ -536,6 +495,7 @@ struct psb_mmu_driver *psb_mmu_driver_init(uint8_t __iomem * registers,
driver->clflush_mask = driver->clflush_add - 1;
driver->clflush_mask = ~driver->clflush_mask;
}
#endif
up_write(&driver->sem);
return driver;
@ -545,9 +505,9 @@ out_err1:
return NULL;
}
static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd,
unsigned long address, uint32_t num_pages,
uint32_t desired_tile_stride,
#if defined(CONFIG_X86)
static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd, unsigned long address,
uint32_t num_pages, uint32_t desired_tile_stride,
uint32_t hw_tile_stride)
{
struct psb_mmu_pt *pt;
@ -561,11 +521,8 @@ static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd,
unsigned long clflush_add = pd->driver->clflush_add;
unsigned long clflush_mask = pd->driver->clflush_mask;
if (!pd->driver->has_clflush) {
/*ttm_tt_cache_flush(&pd->p, num_pages);*/
psb_pages_clflush(pd->driver, &pd->p, num_pages);
if (!pd->driver->has_clflush)
return;
}
if (hw_tile_stride)
rows = num_pages / desired_tile_stride;
@ -586,10 +543,8 @@ static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd,
if (!pt)
continue;
do {
psb_clflush(&pt->v
[psb_mmu_pt_index(addr)]);
} while (addr +=
clflush_add,
psb_clflush(&pt->v[psb_mmu_pt_index(addr)]);
} while (addr += clflush_add,
(addr & clflush_mask) < next);
psb_mmu_pt_unmap_unlock(pt);
@ -598,6 +553,14 @@ static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd,
}
mb();
}
#else
static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd, unsigned long address,
uint32_t num_pages, uint32_t desired_tile_stride,
uint32_t hw_tile_stride)
{
drm_ttm_cache_flush();
}
#endif
void psb_mmu_remove_pfn_sequence(struct psb_mmu_pd *pd,
unsigned long address, uint32_t num_pages)
@ -633,7 +596,7 @@ out:
up_read(&pd->driver->sem);
if (pd->hw_context != -1)
psb_mmu_flush(pd->driver, 0);
psb_mmu_flush(pd->driver);
return;
}
@ -660,7 +623,7 @@ void psb_mmu_remove_pages(struct psb_mmu_pd *pd, unsigned long address,
add = desired_tile_stride << PAGE_SHIFT;
row_add = hw_tile_stride << PAGE_SHIFT;
/* down_read(&pd->driver->sem); */
down_read(&pd->driver->sem);
/* Make sure we only need to flush this processor's cache */
@ -688,10 +651,10 @@ void psb_mmu_remove_pages(struct psb_mmu_pd *pd, unsigned long address,
psb_mmu_flush_ptes(pd, f_address, num_pages,
desired_tile_stride, hw_tile_stride);
/* up_read(&pd->driver->sem); */
up_read(&pd->driver->sem);
if (pd->hw_context != -1)
psb_mmu_flush(pd->driver, 0);
psb_mmu_flush(pd->driver);
}
int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd, uint32_t start_pfn,
@ -704,7 +667,7 @@ int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd, uint32_t start_pfn,
unsigned long end;
unsigned long next;
unsigned long f_address = address;
int ret = 0;
int ret = -ENOMEM;
down_read(&pd->driver->sem);
@ -726,6 +689,7 @@ int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd, uint32_t start_pfn,
psb_mmu_pt_unmap_unlock(pt);
} while (addr = next, next != end);
ret = 0;
out:
if (pd->hw_context != -1)
@ -734,15 +698,15 @@ out:
up_read(&pd->driver->sem);
if (pd->hw_context != -1)
psb_mmu_flush(pd->driver, 1);
psb_mmu_flush(pd->driver);
return ret;
return 0;
}
int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages,
unsigned long address, uint32_t num_pages,
uint32_t desired_tile_stride,
uint32_t hw_tile_stride, int type)
uint32_t desired_tile_stride, uint32_t hw_tile_stride,
int type)
{
struct psb_mmu_pt *pt;
uint32_t rows = 1;
@ -754,7 +718,7 @@ int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages,
unsigned long add;
unsigned long row_add;
unsigned long f_address = address;
int ret = 0;
int ret = -ENOMEM;
if (hw_tile_stride) {
if (num_pages % desired_tile_stride != 0)
@ -777,14 +741,11 @@ int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages,
do {
next = psb_pd_addr_end(addr, end);
pt = psb_mmu_pt_alloc_map_lock(pd, addr);
if (!pt) {
ret = -ENOMEM;
if (!pt)
goto out;
}
do {
pte =
psb_mmu_mask_pte(page_to_pfn(*pages++),
type);
pte = psb_mmu_mask_pte(page_to_pfn(*pages++),
type);
psb_mmu_set_pte(pt, addr, pte);
pt->count++;
} while (addr += PAGE_SIZE, addr < next);
@ -794,6 +755,8 @@ int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages,
address += row_add;
}
ret = 0;
out:
if (pd->hw_context != -1)
psb_mmu_flush_ptes(pd, f_address, num_pages,
@ -802,7 +765,7 @@ out:
up_read(&pd->driver->sem);
if (pd->hw_context != -1)
psb_mmu_flush(pd->driver, 1);
psb_mmu_flush(pd->driver);
return ret;
}

View File

@ -0,0 +1,93 @@
/**************************************************************************
* Copyright (c) 2007-2011, Intel Corporation.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
**************************************************************************/
#ifndef __MMU_H
#define __MMU_H
struct psb_mmu_driver {
/* protects driver- and pd structures. Always take in read mode
* before taking the page table spinlock.
*/
struct rw_semaphore sem;
/* protects page tables, directory tables and pt tables.
* and pt structures.
*/
spinlock_t lock;
atomic_t needs_tlbflush;
atomic_t *msvdx_mmu_invaldc;
struct psb_mmu_pd *default_pd;
uint32_t bif_ctrl;
int has_clflush;
int clflush_add;
unsigned long clflush_mask;
struct drm_device *dev;
};
struct psb_mmu_pd;
struct psb_mmu_pt {
struct psb_mmu_pd *pd;
uint32_t index;
uint32_t count;
struct page *p;
uint32_t *v;
};
struct psb_mmu_pd {
struct psb_mmu_driver *driver;
int hw_context;
struct psb_mmu_pt **tables;
struct page *p;
struct page *dummy_pt;
struct page *dummy_page;
uint32_t pd_mask;
uint32_t invalid_pde;
uint32_t invalid_pte;
};
extern struct psb_mmu_driver *psb_mmu_driver_init(struct drm_device *dev,
int trap_pagefaults,
int invalid_type,
atomic_t *msvdx_mmu_invaldc);
extern void psb_mmu_driver_takedown(struct psb_mmu_driver *driver);
extern struct psb_mmu_pd *psb_mmu_get_default_pd(struct psb_mmu_driver
*driver);
extern struct psb_mmu_pd *psb_mmu_alloc_pd(struct psb_mmu_driver *driver,
int trap_pagefaults,
int invalid_type);
extern void psb_mmu_free_pagedir(struct psb_mmu_pd *pd);
extern void psb_mmu_flush(struct psb_mmu_driver *driver);
extern void psb_mmu_remove_pfn_sequence(struct psb_mmu_pd *pd,
unsigned long address,
uint32_t num_pages);
extern int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd,
uint32_t start_pfn,
unsigned long address,
uint32_t num_pages, int type);
extern int psb_mmu_virtual_to_pfn(struct psb_mmu_pd *pd, uint32_t virtual,
unsigned long *pfn);
extern void psb_mmu_set_pd_context(struct psb_mmu_pd *pd, int hw_context);
extern int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages,
unsigned long address, uint32_t num_pages,
uint32_t desired_tile_stride,
uint32_t hw_tile_stride, int type);
extern void psb_mmu_remove_pages(struct psb_mmu_pd *pd,
unsigned long address, uint32_t num_pages,
uint32_t desired_tile_stride,
uint32_t hw_tile_stride);
#endif

View File

@ -599,7 +599,7 @@ static int oaktrail_pipe_set_base(struct drm_crtc *crtc,
struct drm_device *dev = crtc->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
struct gma_crtc *gma_crtc = to_gma_crtc(crtc);
struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb);
struct psb_framebuffer *psbfb = to_psb_fb(crtc->primary->fb);
int pipe = gma_crtc->pipe;
const struct psb_offset *map = &dev_priv->regmap[pipe];
unsigned long start, offset;
@ -608,7 +608,7 @@ static int oaktrail_pipe_set_base(struct drm_crtc *crtc,
int ret = 0;
/* no fb bound */
if (!crtc->fb) {
if (!crtc->primary->fb) {
dev_dbg(dev->dev, "No FB bound\n");
return 0;
}
@ -617,19 +617,19 @@ static int oaktrail_pipe_set_base(struct drm_crtc *crtc,
return 0;
start = psbfb->gtt->offset;
offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8);
offset = y * crtc->primary->fb->pitches[0] + x * (crtc->primary->fb->bits_per_pixel / 8);
REG_WRITE(map->stride, crtc->fb->pitches[0]);
REG_WRITE(map->stride, crtc->primary->fb->pitches[0]);
dspcntr = REG_READ(map->cntr);
dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
switch (crtc->fb->bits_per_pixel) {
switch (crtc->primary->fb->bits_per_pixel) {
case 8:
dspcntr |= DISPPLANE_8BPP;
break;
case 16:
if (crtc->fb->depth == 15)
if (crtc->primary->fb->depth == 15)
dspcntr |= DISPPLANE_15_16BPP;
else
dspcntr |= DISPPLANE_16BPP;

View File

@ -523,13 +523,6 @@ static int oaktrail_hdmi_mode_valid(struct drm_connector *connector,
return MODE_OK;
}
static bool oaktrail_hdmi_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
return true;
}
static enum drm_connector_status
oaktrail_hdmi_detect(struct drm_connector *connector, bool force)
{
@ -608,7 +601,7 @@ static void oaktrail_hdmi_destroy(struct drm_connector *connector)
static const struct drm_encoder_helper_funcs oaktrail_hdmi_helper_funcs = {
.dpms = oaktrail_hdmi_dpms,
.mode_fixup = oaktrail_hdmi_mode_fixup,
.mode_fixup = gma_encoder_mode_fixup,
.prepare = gma_encoder_prepare,
.mode_set = oaktrail_hdmi_mode_set,
.commit = gma_encoder_commit,

View File

@ -359,6 +359,7 @@ void oaktrail_lvds_init(struct drm_device *dev,
* if closed, act like it's not there for now
*/
mutex_lock(&dev->mode_config.mutex);
i2c_adap = i2c_get_adapter(dev_priv->ops->i2c_bus);
if (i2c_adap == NULL)
dev_err(dev->dev, "No ddc adapter available!\n");
@ -401,10 +402,14 @@ void oaktrail_lvds_init(struct drm_device *dev,
}
out:
mutex_unlock(&dev->mode_config.mutex);
drm_sysfs_connector_add(connector);
return;
failed_find:
mutex_unlock(&dev->mode_config.mutex);
dev_dbg(dev->dev, "No LVDS modes found, disabling.\n");
if (gma_encoder->ddc_bus)
psb_intel_i2c_destroy(gma_encoder->ddc_bus);

View File

@ -173,10 +173,13 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
return 0;
}
void psb_intel_opregion_asle_intr(struct drm_device *dev)
static void psb_intel_opregion_asle_work(struct work_struct *work)
{
struct drm_psb_private *dev_priv = dev->dev_private;
struct opregion_asle *asle = dev_priv->opregion.asle;
struct psb_intel_opregion *opregion =
container_of(work, struct psb_intel_opregion, asle_work);
struct drm_psb_private *dev_priv =
container_of(opregion, struct drm_psb_private, opregion);
struct opregion_asle *asle = opregion->asle;
u32 asle_stat = 0;
u32 asle_req;
@ -190,9 +193,18 @@ void psb_intel_opregion_asle_intr(struct drm_device *dev)
}
if (asle_req & ASLE_SET_BACKLIGHT)
asle_stat |= asle_set_backlight(dev, asle->bclp);
asle_stat |= asle_set_backlight(dev_priv->dev, asle->bclp);
asle->aslc = asle_stat;
}
void psb_intel_opregion_asle_intr(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
if (dev_priv->opregion.asle)
schedule_work(&dev_priv->opregion.asle_work);
}
#define ASLE_ALS_EN (1<<0)
@ -282,6 +294,8 @@ void psb_intel_opregion_fini(struct drm_device *dev)
unregister_acpi_notifier(&psb_intel_opregion_notifier);
}
cancel_work_sync(&opregion->asle_work);
/* just clear all opregion memory pointers now */
iounmap(opregion->header);
opregion->header = NULL;
@ -304,6 +318,9 @@ int psb_intel_opregion_setup(struct drm_device *dev)
DRM_DEBUG_DRIVER("ACPI Opregion not supported\n");
return -ENOTSUPP;
}
INIT_WORK(&opregion->asle_work, psb_intel_opregion_asle_work);
DRM_DEBUG("OpRegion detected at 0x%8x\n", opregion_phy);
base = acpi_os_ioremap(opregion_phy, 8*1024);
if (!base)

View File

@ -26,6 +26,7 @@
#include "psb_intel_reg.h"
#include "intel_bios.h"
#include "psb_device.h"
#include "gma_device.h"
static int psb_output_init(struct drm_device *dev)
{
@ -257,45 +258,6 @@ static int psb_power_up(struct drm_device *dev)
return 0;
}
static void psb_get_core_freq(struct drm_device *dev)
{
uint32_t clock;
struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0);
struct drm_psb_private *dev_priv = dev->dev_private;
/*pci_write_config_dword(pci_root, 0xD4, 0x00C32004);*/
/*pci_write_config_dword(pci_root, 0xD0, 0xE0033000);*/
pci_write_config_dword(pci_root, 0xD0, 0xD0050300);
pci_read_config_dword(pci_root, 0xD4, &clock);
pci_dev_put(pci_root);
switch (clock & 0x07) {
case 0:
dev_priv->core_freq = 100;
break;
case 1:
dev_priv->core_freq = 133;
break;
case 2:
dev_priv->core_freq = 150;
break;
case 3:
dev_priv->core_freq = 178;
break;
case 4:
dev_priv->core_freq = 200;
break;
case 5:
case 6:
case 7:
dev_priv->core_freq = 266;
break;
default:
dev_priv->core_freq = 0;
}
}
/* Poulsbo */
static const struct psb_offset psb_regmap[2] = {
{
@ -352,7 +314,7 @@ static int psb_chip_setup(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
dev_priv->regmap = psb_regmap;
psb_get_core_freq(dev);
gma_get_core_freq(dev);
gma_intel_setup_gmbus(dev);
psb_intel_opregion_init(dev);
psb_intel_init_bios(dev);

View File

@ -21,7 +21,6 @@
#include <drm/drmP.h>
#include <drm/drm.h>
#include <drm/gma_drm.h>
#include "psb_drv.h"
#include "framebuffer.h"
#include "psb_reg.h"
@ -37,56 +36,65 @@
#include <acpi/video.h>
#include <linux/module.h>
static int drm_psb_trap_pagefaults;
static int psb_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
MODULE_PARM_DESC(trap_pagefaults, "Error and reset on MMU pagefaults");
module_param_named(trap_pagefaults, drm_psb_trap_pagefaults, int, 0600);
static struct drm_driver driver;
static int psb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
/*
* The table below contains a mapping of the PCI vendor ID and the PCI Device ID
* to the different groups of PowerVR 5-series chip designs
*
* 0x8086 = Intel Corporation
*
* PowerVR SGX535 - Poulsbo - Intel GMA 500, Intel Atom Z5xx
* PowerVR SGX535 - Moorestown - Intel GMA 600
* PowerVR SGX535 - Oaktrail - Intel GMA 600, Intel Atom Z6xx, E6xx
* PowerVR SGX540 - Medfield - Intel Atom Z2460
* PowerVR SGX544MP2 - Medfield -
* PowerVR SGX545 - Cedartrail - Intel GMA 3600, Intel Atom D2500, N2600
* PowerVR SGX545 - Cedartrail - Intel GMA 3650, Intel Atom D2550, D2700,
* N2800
*/
static DEFINE_PCI_DEVICE_TABLE(pciidlist) = {
{ 0x8086, 0x8108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops },
{ 0x8086, 0x8109, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops },
#if defined(CONFIG_DRM_GMA600)
{ 0x8086, 0x4100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
{ 0x8086, 0x4101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
{ 0x8086, 0x4102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
{ 0x8086, 0x4103, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
{ 0x8086, 0x4104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
{ 0x8086, 0x4105, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
{ 0x8086, 0x4106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
{ 0x8086, 0x4107, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
/* Atom E620 */
{ 0x8086, 0x4108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
{ 0x8086, 0x4100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
{ 0x8086, 0x4101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
{ 0x8086, 0x4102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
{ 0x8086, 0x4103, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
{ 0x8086, 0x4104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
{ 0x8086, 0x4105, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
{ 0x8086, 0x4106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
{ 0x8086, 0x4107, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
{ 0x8086, 0x4108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
#endif
#if defined(CONFIG_DRM_MEDFIELD)
{0x8086, 0x0130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
{0x8086, 0x0131, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
{0x8086, 0x0132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
{0x8086, 0x0133, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
{0x8086, 0x0134, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
{0x8086, 0x0135, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
{0x8086, 0x0136, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
{0x8086, 0x0137, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
{ 0x8086, 0x0130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops },
{ 0x8086, 0x0131, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops },
{ 0x8086, 0x0132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops },
{ 0x8086, 0x0133, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops },
{ 0x8086, 0x0134, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops },
{ 0x8086, 0x0135, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops },
{ 0x8086, 0x0136, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops },
{ 0x8086, 0x0137, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops },
#endif
#if defined(CONFIG_DRM_GMA3600)
{ 0x8086, 0x0be0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
{ 0x8086, 0x0be1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
{ 0x8086, 0x0be2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
{ 0x8086, 0x0be3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
{ 0x8086, 0x0be4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
{ 0x8086, 0x0be5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
{ 0x8086, 0x0be6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
{ 0x8086, 0x0be7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
{ 0x8086, 0x0be8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
{ 0x8086, 0x0be9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
{ 0x8086, 0x0bea, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
{ 0x8086, 0x0beb, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
{ 0x8086, 0x0bec, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
{ 0x8086, 0x0bed, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
{ 0x8086, 0x0bee, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
{ 0x8086, 0x0bef, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
{ 0x8086, 0x0be0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
{ 0x8086, 0x0be1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
{ 0x8086, 0x0be2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
{ 0x8086, 0x0be3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
{ 0x8086, 0x0be4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
{ 0x8086, 0x0be5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
{ 0x8086, 0x0be6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
{ 0x8086, 0x0be7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
{ 0x8086, 0x0be8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
{ 0x8086, 0x0be9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
{ 0x8086, 0x0bea, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
{ 0x8086, 0x0beb, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
{ 0x8086, 0x0bec, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
{ 0x8086, 0x0bed, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
{ 0x8086, 0x0bee, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
{ 0x8086, 0x0bef, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
#endif
{ 0, }
};
@ -95,59 +103,10 @@ MODULE_DEVICE_TABLE(pci, pciidlist);
/*
* Standard IOCTLs.
*/
#define DRM_IOCTL_GMA_ADB \
DRM_IOWR(DRM_GMA_ADB + DRM_COMMAND_BASE, uint32_t)
#define DRM_IOCTL_GMA_MODE_OPERATION \
DRM_IOWR(DRM_GMA_MODE_OPERATION + DRM_COMMAND_BASE, \
struct drm_psb_mode_operation_arg)
#define DRM_IOCTL_GMA_STOLEN_MEMORY \
DRM_IOWR(DRM_GMA_STOLEN_MEMORY + DRM_COMMAND_BASE, \
struct drm_psb_stolen_memory_arg)
#define DRM_IOCTL_GMA_GAMMA \
DRM_IOWR(DRM_GMA_GAMMA + DRM_COMMAND_BASE, \
struct drm_psb_dpst_lut_arg)
#define DRM_IOCTL_GMA_DPST_BL \
DRM_IOWR(DRM_GMA_DPST_BL + DRM_COMMAND_BASE, \
uint32_t)
#define DRM_IOCTL_GMA_GET_PIPE_FROM_CRTC_ID \
DRM_IOWR(DRM_GMA_GET_PIPE_FROM_CRTC_ID + DRM_COMMAND_BASE, \
struct drm_psb_get_pipe_from_crtc_id_arg)
#define DRM_IOCTL_GMA_GEM_CREATE \
DRM_IOWR(DRM_GMA_GEM_CREATE + DRM_COMMAND_BASE, \
struct drm_psb_gem_create)
#define DRM_IOCTL_GMA_GEM_MMAP \
DRM_IOWR(DRM_GMA_GEM_MMAP + DRM_COMMAND_BASE, \
struct drm_psb_gem_mmap)
static int psb_adb_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
static int psb_mode_operation_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
static int psb_stolen_memory_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
static int psb_gamma_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
static int psb_dpst_bl_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
static const struct drm_ioctl_desc psb_ioctls[] = {
DRM_IOCTL_DEF_DRV(GMA_ADB, psb_adb_ioctl, DRM_AUTH),
DRM_IOCTL_DEF_DRV(GMA_MODE_OPERATION, psb_mode_operation_ioctl,
DRM_AUTH),
DRM_IOCTL_DEF_DRV(GMA_STOLEN_MEMORY, psb_stolen_memory_ioctl,
DRM_AUTH),
DRM_IOCTL_DEF_DRV(GMA_GAMMA, psb_gamma_ioctl, DRM_AUTH),
DRM_IOCTL_DEF_DRV(GMA_DPST_BL, psb_dpst_bl_ioctl, DRM_AUTH),
DRM_IOCTL_DEF_DRV(GMA_GET_PIPE_FROM_CRTC_ID,
psb_intel_get_pipe_from_crtc_id, 0),
DRM_IOCTL_DEF_DRV(GMA_GEM_CREATE, psb_gem_create_ioctl,
DRM_UNLOCKED | DRM_AUTH),
DRM_IOCTL_DEF_DRV(GMA_GEM_MMAP, psb_gem_mmap_ioctl,
DRM_UNLOCKED | DRM_AUTH),
};
static void psb_lastclose(struct drm_device *dev)
static void psb_driver_lastclose(struct drm_device *dev)
{
int ret;
struct drm_psb_private *dev_priv = dev->dev_private;
@ -169,19 +128,14 @@ static int psb_do_init(struct drm_device *dev)
uint32_t stolen_gtt;
int ret = -ENOMEM;
if (pg->mmu_gatt_start & 0x0FFFFFFF) {
dev_err(dev->dev, "Gatt must be 256M aligned. This is a bug.\n");
ret = -EINVAL;
goto out_err;
return -EINVAL;
}
stolen_gtt = (pg->stolen_size >> PAGE_SHIFT) * 4;
stolen_gtt = (stolen_gtt + PAGE_SIZE - 1) >> PAGE_SHIFT;
stolen_gtt =
(stolen_gtt < pg->gtt_pages) ? stolen_gtt : pg->gtt_pages;
stolen_gtt = (stolen_gtt < pg->gtt_pages) ? stolen_gtt : pg->gtt_pages;
dev_priv->gatt_free_offset = pg->mmu_gatt_start +
(stolen_gtt << PAGE_SHIFT) * 1024;
@ -192,23 +146,26 @@ static int psb_do_init(struct drm_device *dev)
PSB_WSGX32(0x00000000, PSB_CR_BIF_BANK0);
PSB_WSGX32(0x00000000, PSB_CR_BIF_BANK1);
PSB_RSGX32(PSB_CR_BIF_BANK1);
PSB_WSGX32(PSB_RSGX32(PSB_CR_BIF_CTRL) | _PSB_MMU_ER_MASK,
PSB_CR_BIF_CTRL);
/* Do not bypass any MMU access, let them pagefault instead */
PSB_WSGX32((PSB_RSGX32(PSB_CR_BIF_CTRL) & ~_PSB_MMU_ER_MASK),
PSB_CR_BIF_CTRL);
PSB_RSGX32(PSB_CR_BIF_CTRL);
psb_spank(dev_priv);
/* mmu_gatt ?? */
PSB_WSGX32(pg->gatt_start, PSB_CR_BIF_TWOD_REQ_BASE);
PSB_RSGX32(PSB_CR_BIF_TWOD_REQ_BASE); /* Post */
return 0;
out_err:
return ret;
}
static int psb_driver_unload(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
/* Kill vblank etc here */
/* TODO: Kill vblank etc here */
if (dev_priv) {
if (dev_priv->backlight_device)
@ -268,8 +225,7 @@ static int psb_driver_unload(struct drm_device *dev)
return 0;
}
static int psb_driver_load(struct drm_device *dev, unsigned long chipset)
static int psb_driver_load(struct drm_device *dev, unsigned long flags)
{
struct drm_psb_private *dev_priv;
unsigned long resource_start, resource_len;
@ -277,15 +233,19 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset)
int ret = -ENOMEM;
struct drm_connector *connector;
struct gma_encoder *gma_encoder;
struct psb_gtt *pg;
/* allocating and initializing driver private data */
dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL);
if (dev_priv == NULL)
return -ENOMEM;
dev_priv->ops = (struct psb_ops *)chipset;
dev_priv->ops = (struct psb_ops *)flags;
dev_priv->dev = dev;
dev->dev_private = (void *) dev_priv;
pg = &dev_priv->gtt;
pci_set_master(dev->pdev);
dev_priv->num_pipe = dev_priv->ops->pipes;
@ -347,9 +307,7 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset)
if (ret)
goto out_err;
dev_priv->mmu = psb_mmu_driver_init((void *)0,
drm_psb_trap_pagefaults, 0,
dev_priv);
dev_priv->mmu = psb_mmu_driver_init(dev, 1, 0, 0);
if (!dev_priv->mmu)
goto out_err;
@ -357,18 +315,27 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset)
if (!dev_priv->pf_pd)
goto out_err;
psb_mmu_set_pd_context(psb_mmu_get_default_pd(dev_priv->mmu), 0);
psb_mmu_set_pd_context(dev_priv->pf_pd, 1);
ret = psb_do_init(dev);
if (ret)
return ret;
/* Add stolen memory to SGX MMU */
down_read(&pg->sem);
ret = psb_mmu_insert_pfn_sequence(psb_mmu_get_default_pd(dev_priv->mmu),
dev_priv->stolen_base >> PAGE_SHIFT,
pg->gatt_start,
pg->stolen_size >> PAGE_SHIFT, 0);
up_read(&pg->sem);
psb_mmu_set_pd_context(psb_mmu_get_default_pd(dev_priv->mmu), 0);
psb_mmu_set_pd_context(dev_priv->pf_pd, 1);
PSB_WSGX32(0x20000000, PSB_CR_PDS_EXEC_BASE);
PSB_WSGX32(0x30000000, PSB_CR_BIF_3D_REQ_BASE);
acpi_video_register();
/* Setup vertical blanking handling */
ret = drm_vblank_init(dev, dev_priv->num_pipe);
if (ret)
goto out_err;
@ -390,9 +357,7 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset)
drm_irq_install(dev);
dev->vblank_disable_allowed = true;
dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
dev->driver->get_vblank_counter = psb_get_vblank_counter;
psb_modeset_init(dev);
@ -416,11 +381,11 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset)
return ret;
psb_intel_opregion_enable_asle(dev);
#if 0
/*enable runtime pm at last*/
/* Enable runtime pm at last */
pm_runtime_enable(&dev->pdev->dev);
pm_runtime_set_active(&dev->pdev->dev);
#endif
/*Intel drm driver load is done, continue doing pvr load*/
/* Intel drm driver load is done, continue doing pvr load */
return 0;
out_err:
psb_driver_unload(dev);
@ -442,161 +407,6 @@ static inline void get_brightness(struct backlight_device *bd)
#endif
}
static int psb_dpst_bl_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_psb_private *dev_priv = psb_priv(dev);
uint32_t *arg = data;
dev_priv->blc_adj2 = *arg;
get_brightness(dev_priv->backlight_device);
return 0;
}
static int psb_adb_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_psb_private *dev_priv = psb_priv(dev);
uint32_t *arg = data;
dev_priv->blc_adj1 = *arg;
get_brightness(dev_priv->backlight_device);
return 0;
}
static int psb_gamma_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_psb_dpst_lut_arg *lut_arg = data;
struct drm_mode_object *obj;
struct drm_crtc *crtc;
struct drm_connector *connector;
struct gma_crtc *gma_crtc;
int i = 0;
int32_t obj_id;
obj_id = lut_arg->output_id;
obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_CONNECTOR);
if (!obj) {
dev_dbg(dev->dev, "Invalid Connector object.\n");
return -ENOENT;
}
connector = obj_to_connector(obj);
crtc = connector->encoder->crtc;
gma_crtc = to_gma_crtc(crtc);
for (i = 0; i < 256; i++)
gma_crtc->lut_adj[i] = lut_arg->lut[i];
gma_crtc_load_lut(crtc);
return 0;
}
static int psb_mode_operation_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
uint32_t obj_id;
uint16_t op;
struct drm_mode_modeinfo *umode;
struct drm_display_mode *mode = NULL;
struct drm_psb_mode_operation_arg *arg;
struct drm_mode_object *obj;
struct drm_connector *connector;
struct drm_connector_helper_funcs *connector_funcs;
int ret = 0;
int resp = MODE_OK;
arg = (struct drm_psb_mode_operation_arg *)data;
obj_id = arg->obj_id;
op = arg->operation;
switch (op) {
case PSB_MODE_OPERATION_MODE_VALID:
umode = &arg->mode;
drm_modeset_lock_all(dev);
obj = drm_mode_object_find(dev, obj_id,
DRM_MODE_OBJECT_CONNECTOR);
if (!obj) {
ret = -ENOENT;
goto mode_op_out;
}
connector = obj_to_connector(obj);
mode = drm_mode_create(dev);
if (!mode) {
ret = -ENOMEM;
goto mode_op_out;
}
/* drm_crtc_convert_umode(mode, umode); */
{
mode->clock = umode->clock;
mode->hdisplay = umode->hdisplay;
mode->hsync_start = umode->hsync_start;
mode->hsync_end = umode->hsync_end;
mode->htotal = umode->htotal;
mode->hskew = umode->hskew;
mode->vdisplay = umode->vdisplay;
mode->vsync_start = umode->vsync_start;
mode->vsync_end = umode->vsync_end;
mode->vtotal = umode->vtotal;
mode->vscan = umode->vscan;
mode->vrefresh = umode->vrefresh;
mode->flags = umode->flags;
mode->type = umode->type;
strncpy(mode->name, umode->name, DRM_DISPLAY_MODE_LEN);
mode->name[DRM_DISPLAY_MODE_LEN-1] = 0;
}
connector_funcs = (struct drm_connector_helper_funcs *)
connector->helper_private;
if (connector_funcs->mode_valid) {
resp = connector_funcs->mode_valid(connector, mode);
arg->data = resp;
}
/*do some clean up work*/
if (mode)
drm_mode_destroy(dev, mode);
mode_op_out:
drm_modeset_unlock_all(dev);
return ret;
default:
dev_dbg(dev->dev, "Unsupported psb mode operation\n");
return -EOPNOTSUPP;
}
return 0;
}
static int psb_stolen_memory_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_psb_private *dev_priv = psb_priv(dev);
struct drm_psb_stolen_memory_arg *arg = data;
arg->base = dev_priv->stolen_base;
arg->size = dev_priv->vram_stolen_size;
return 0;
}
static int psb_driver_open(struct drm_device *dev, struct drm_file *priv)
{
return 0;
}
static void psb_driver_close(struct drm_device *dev, struct drm_file *priv)
{
}
static long psb_unlocked_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
@ -614,15 +424,21 @@ static long psb_unlocked_ioctl(struct file *filp, unsigned int cmd,
/* FIXME: do we need to wrap the other side of this */
}
/* When a client dies:
/*
* When a client dies:
* - Check for and clean up flipped page state
*/
static void psb_driver_preclose(struct drm_device *dev, struct drm_file *priv)
{
}
static void psb_remove(struct pci_dev *pdev)
static int psb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
return drm_get_pci_dev(pdev, ent, &driver);
}
static void psb_pci_remove(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
drm_put_dev(dev);
@ -657,11 +473,12 @@ static const struct file_operations psb_gem_fops = {
static struct drm_driver driver = {
.driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | \
DRIVER_MODESET | DRIVER_GEM ,
DRIVER_MODESET | DRIVER_GEM,
.load = psb_driver_load,
.unload = psb_driver_unload,
.lastclose = psb_driver_lastclose,
.preclose = psb_driver_preclose,
.ioctls = psb_ioctls,
.num_ioctls = DRM_ARRAY_SIZE(psb_ioctls),
.device_is_agp = psb_driver_device_is_agp,
.irq_preinstall = psb_irq_preinstall,
@ -671,40 +488,31 @@ static struct drm_driver driver = {
.enable_vblank = psb_enable_vblank,
.disable_vblank = psb_disable_vblank,
.get_vblank_counter = psb_get_vblank_counter,
.lastclose = psb_lastclose,
.open = psb_driver_open,
.preclose = psb_driver_preclose,
.postclose = psb_driver_close,
.gem_free_object = psb_gem_free_object,
.gem_vm_ops = &psb_gem_vm_ops,
.dumb_create = psb_gem_dumb_create,
.dumb_map_offset = psb_gem_dumb_map_gtt,
.dumb_destroy = drm_gem_dumb_destroy,
.ioctls = psb_ioctls,
.fops = &psb_gem_fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = PSB_DRM_DRIVER_DATE,
.major = PSB_DRM_DRIVER_MAJOR,
.minor = PSB_DRM_DRIVER_MINOR,
.patchlevel = PSB_DRM_DRIVER_PATCHLEVEL
.date = DRIVER_DATE,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
.patchlevel = DRIVER_PATCHLEVEL
};
static struct pci_driver psb_pci_driver = {
.name = DRIVER_NAME,
.id_table = pciidlist,
.probe = psb_probe,
.remove = psb_remove,
.driver = {
.pm = &psb_pm_ops,
}
.probe = psb_pci_probe,
.remove = psb_pci_remove,
.driver.pm = &psb_pm_ops,
};
static int psb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
return drm_get_pci_dev(pdev, ent, &driver);
}
static int __init psb_init(void)
{
return drm_pci_init(&driver, &psb_pci_driver);
@ -718,6 +526,6 @@ static void __exit psb_exit(void)
late_initcall(psb_init);
module_exit(psb_exit);
MODULE_AUTHOR("Alan Cox <alan@linux.intel.com> and others");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
MODULE_LICENSE(DRIVER_LICENSE);

View File

@ -33,6 +33,18 @@
#include "power.h"
#include "opregion.h"
#include "oaktrail.h"
#include "mmu.h"
#define DRIVER_AUTHOR "Alan Cox <alan@linux.intel.com> and others"
#define DRIVER_LICENSE "GPL"
#define DRIVER_NAME "gma500"
#define DRIVER_DESC "DRM driver for the Intel GMA500, GMA600, GMA3600, GMA3650"
#define DRIVER_DATE "20140314"
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
#define DRIVER_PATCHLEVEL 0
/* Append new drm mode definition here, align with libdrm definition */
#define DRM_MODE_SCALE_NO_SCALE 2
@ -49,21 +61,7 @@ enum {
#define IS_MFLD(dev) (((dev)->pdev->device & 0xfff8) == 0x0130)
#define IS_CDV(dev) (((dev)->pdev->device & 0xfff0) == 0x0be0)
/*
* Driver definitions
*/
#define DRIVER_NAME "gma500"
#define DRIVER_DESC "DRM driver for the Intel GMA500"
#define PSB_DRM_DRIVER_DATE "2011-06-06"
#define PSB_DRM_DRIVER_MAJOR 1
#define PSB_DRM_DRIVER_MINOR 0
#define PSB_DRM_DRIVER_PATCHLEVEL 0
/*
* Hardware offsets
*/
/* Hardware offsets */
#define PSB_VDC_OFFSET 0x00000000
#define PSB_VDC_SIZE 0x000080000
#define MRST_MMIO_SIZE 0x0000C0000
@ -71,16 +69,14 @@ enum {
#define PSB_SGX_SIZE 0x8000
#define PSB_SGX_OFFSET 0x00040000
#define MRST_SGX_OFFSET 0x00080000
/*
* PCI resource identifiers
*/
/* PCI resource identifiers */
#define PSB_MMIO_RESOURCE 0
#define PSB_AUX_RESOURCE 0
#define PSB_GATT_RESOURCE 2
#define PSB_GTT_RESOURCE 3
/*
* PCI configuration
*/
/* PCI configuration */
#define PSB_GMCH_CTRL 0x52
#define PSB_BSM 0x5C
#define _PSB_GMCH_ENABLED 0x4
@ -88,37 +84,29 @@ enum {
#define _PSB_PGETBL_ENABLED 0x00000001
#define PSB_SGX_2D_SLAVE_PORT 0x4000
/* To get rid of */
/* TODO: To get rid of */
#define PSB_TT_PRIV0_LIMIT (256*1024*1024)
#define PSB_TT_PRIV0_PLIMIT (PSB_TT_PRIV0_LIMIT >> PAGE_SHIFT)
/*
* SGX side MMU definitions (these can probably go)
*/
/* SGX side MMU definitions (these can probably go) */
/*
* Flags for external memory type field.
*/
/* Flags for external memory type field */
#define PSB_MMU_CACHED_MEMORY 0x0001 /* Bind to MMU only */
#define PSB_MMU_RO_MEMORY 0x0002 /* MMU RO memory */
#define PSB_MMU_WO_MEMORY 0x0004 /* MMU WO memory */
/*
* PTE's and PDE's
*/
/* PTE's and PDE's */
#define PSB_PDE_MASK 0x003FFFFF
#define PSB_PDE_SHIFT 22
#define PSB_PTE_SHIFT 12
/*
* Cache control
*/
/* Cache control */
#define PSB_PTE_VALID 0x0001 /* PTE / PDE valid */
#define PSB_PTE_WO 0x0002 /* Write only */
#define PSB_PTE_RO 0x0004 /* Read only */
#define PSB_PTE_CACHED 0x0008 /* CPU cache coherent */
/*
* VDC registers and bits
*/
/* VDC registers and bits */
#define PSB_MSVDX_CLOCKGATING 0x2064
#define PSB_TOPAZ_CLOCKGATING 0x2068
#define PSB_HWSTAM 0x2098
@ -265,6 +253,7 @@ struct psb_intel_opregion {
struct opregion_asle *asle;
void *vbt;
u32 __iomem *lid_state;
struct work_struct asle_work;
};
struct sdvo_device_mapping {
@ -283,10 +272,7 @@ struct intel_gmbus {
u32 reg0;
};
/*
* Register offset maps
*/
/* Register offset maps */
struct psb_offset {
u32 fp0;
u32 fp1;
@ -320,9 +306,7 @@ struct psb_offset {
* update the register cache instead.
*/
/*
* Common status for pipes.
*/
/* Common status for pipes */
struct psb_pipe {
u32 fp0;
u32 fp1;
@ -482,35 +466,24 @@ struct drm_psb_private {
struct psb_mmu_driver *mmu;
struct psb_mmu_pd *pf_pd;
/*
* Register base
*/
/* Register base */
uint8_t __iomem *sgx_reg;
uint8_t __iomem *vdc_reg;
uint8_t __iomem *aux_reg; /* Auxillary vdc pipe regs */
uint32_t gatt_free_offset;
/*
* Fencing / irq.
*/
/* Fencing / irq */
uint32_t vdc_irq_mask;
uint32_t pipestat[PSB_NUM_PIPE];
spinlock_t irqmask_lock;
/*
* Power
*/
/* Power */
bool suspended;
bool display_power;
int display_count;
/*
* Modesetting
*/
/* Modesetting */
struct psb_intel_mode_device mode_dev;
bool modeset; /* true if we have done the mode_device setup */
@ -518,15 +491,10 @@ struct drm_psb_private {
struct drm_crtc *pipe_to_crtc_mapping[PSB_NUM_PIPE];
uint32_t num_pipe;
/*
* OSPM info (Power management base) (can go ?)
*/
/* OSPM info (Power management base) (TODO: can go ?) */
uint32_t ospm_base;
/*
* Sizes info
*/
/* Sizes info */
u32 fuse_reg_value;
u32 video_device_fuse;
@ -546,9 +514,7 @@ struct drm_psb_private {
struct drm_property *broadcast_rgb_property;
struct drm_property *force_audio_property;
/*
* LVDS info
*/
/* LVDS info */
int backlight_duty_cycle; /* restore backlight to this value */
bool panel_wants_dither;
struct drm_display_mode *panel_fixed_mode;
@ -582,34 +548,23 @@ struct drm_psb_private {
/* Oaktrail HDMI state */
struct oaktrail_hdmi_dev *hdmi_priv;
/*
* Register state
*/
/* Register state */
struct psb_save_area regs;
/* MSI reg save */
uint32_t msi_addr;
uint32_t msi_data;
/*
* Hotplug handling
*/
/* Hotplug handling */
struct work_struct hotplug_work;
/*
* LID-Switch
*/
/* LID-Switch */
spinlock_t lid_lock;
struct timer_list lid_timer;
struct psb_intel_opregion opregion;
u32 lid_last_state;
/*
* Watchdog
*/
/* Watchdog */
uint32_t apm_reg;
uint16_t apm_base;
@ -629,9 +584,7 @@ struct drm_psb_private {
/* 2D acceleration */
spinlock_t lock_2d;
/*
* Panel brightness
*/
/* Panel brightness */
int brightness;
int brightness_adjusted;
@ -664,10 +617,7 @@ struct drm_psb_private {
};
/*
* Operations for each board type
*/
/* Operations for each board type */
struct psb_ops {
const char *name;
unsigned int accel_2d:1;
@ -713,8 +663,6 @@ struct psb_ops {
struct psb_mmu_driver;
extern int drm_crtc_probe_output_modes(struct drm_device *dev, int, int);
extern int drm_pick_crtcs(struct drm_device *dev);
@ -723,52 +671,7 @@ static inline struct drm_psb_private *psb_priv(struct drm_device *dev)
return (struct drm_psb_private *) dev->dev_private;
}
/*
* MMU stuff.
*/
extern struct psb_mmu_driver *psb_mmu_driver_init(uint8_t __iomem * registers,
int trap_pagefaults,
int invalid_type,
struct drm_psb_private *dev_priv);
extern void psb_mmu_driver_takedown(struct psb_mmu_driver *driver);
extern struct psb_mmu_pd *psb_mmu_get_default_pd(struct psb_mmu_driver
*driver);
extern void psb_mmu_mirror_gtt(struct psb_mmu_pd *pd, uint32_t mmu_offset,
uint32_t gtt_start, uint32_t gtt_pages);
extern struct psb_mmu_pd *psb_mmu_alloc_pd(struct psb_mmu_driver *driver,
int trap_pagefaults,
int invalid_type);
extern void psb_mmu_free_pagedir(struct psb_mmu_pd *pd);
extern void psb_mmu_flush(struct psb_mmu_driver *driver, int rc_prot);
extern void psb_mmu_remove_pfn_sequence(struct psb_mmu_pd *pd,
unsigned long address,
uint32_t num_pages);
extern int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd,
uint32_t start_pfn,
unsigned long address,
uint32_t num_pages, int type);
extern int psb_mmu_virtual_to_pfn(struct psb_mmu_pd *pd, uint32_t virtual,
unsigned long *pfn);
/*
* Enable / disable MMU for different requestors.
*/
extern void psb_mmu_set_pd_context(struct psb_mmu_pd *pd, int hw_context);
extern int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages,
unsigned long address, uint32_t num_pages,
uint32_t desired_tile_stride,
uint32_t hw_tile_stride, int type);
extern void psb_mmu_remove_pages(struct psb_mmu_pd *pd,
unsigned long address, uint32_t num_pages,
uint32_t desired_tile_stride,
uint32_t hw_tile_stride);
/*
*psb_irq.c
*/
/* psb_irq.c */
extern irqreturn_t psb_irq_handler(int irq, void *arg);
extern int psb_irq_enable_dpst(struct drm_device *dev);
extern int psb_irq_disable_dpst(struct drm_device *dev);
@ -791,24 +694,17 @@ psb_disable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask);
extern u32 psb_get_vblank_counter(struct drm_device *dev, int crtc);
/*
* framebuffer.c
*/
/* framebuffer.c */
extern int psbfb_probed(struct drm_device *dev);
extern int psbfb_remove(struct drm_device *dev,
struct drm_framebuffer *fb);
/*
* accel_2d.c
*/
/* accel_2d.c */
extern void psbfb_copyarea(struct fb_info *info,
const struct fb_copyarea *region);
extern int psbfb_sync(struct fb_info *info);
extern void psb_spank(struct drm_psb_private *dev_priv);
/*
* psb_reset.c
*/
/* psb_reset.c */
extern void psb_lid_timer_init(struct drm_psb_private *dev_priv);
extern void psb_lid_timer_takedown(struct drm_psb_private *dev_priv);
extern void psb_print_pagefault(struct drm_psb_private *dev_priv);
@ -867,9 +763,7 @@ extern const struct psb_ops mdfld_chip_ops;
/* cdv_device.c */
extern const struct psb_ops cdv_chip_ops;
/*
* Debug print bits setting
*/
/* Debug print bits setting */
#define PSB_D_GENERAL (1 << 0)
#define PSB_D_INIT (1 << 1)
#define PSB_D_IRQ (1 << 2)
@ -885,10 +779,7 @@ extern const struct psb_ops cdv_chip_ops;
extern int drm_idle_check_interval;
/*
* Utilities
*/
/* Utilities */
static inline u32 MRST_MSG_READ32(uint port, uint offset)
{
int mcr = (0xD0<<24) | (port << 16) | (offset << 8);

View File

@ -120,7 +120,7 @@ static int psb_intel_crtc_mode_set(struct drm_crtc *crtc,
const struct gma_limit_t *limit;
/* No scan out no play */
if (crtc->fb == NULL) {
if (crtc->primary->fb == NULL) {
crtc_funcs->mode_set_base(crtc, x, y, old_fb);
return 0;
}
@ -469,7 +469,8 @@ static void psb_intel_cursor_init(struct drm_device *dev,
/* Allocate 4 pages of stolen mem for a hardware cursor. That
* is enough for the 64 x 64 ARGB cursors we support.
*/
cursor_gt = psb_gtt_alloc_range(dev, 4 * PAGE_SIZE, "cursor", 1);
cursor_gt = psb_gtt_alloc_range(dev, 4 * PAGE_SIZE, "cursor", 1,
PAGE_SIZE);
if (!cursor_gt) {
gma_crtc->cursor_gt = NULL;
goto out;
@ -554,33 +555,6 @@ void psb_intel_crtc_init(struct drm_device *dev, int pipe,
gma_crtc->active = true;
}
int psb_intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_psb_private *dev_priv = dev->dev_private;
struct drm_psb_get_pipe_from_crtc_id_arg *pipe_from_crtc_id = data;
struct drm_mode_object *drmmode_obj;
struct gma_crtc *crtc;
if (!dev_priv) {
dev_err(dev->dev, "called with no initialization\n");
return -EINVAL;
}
drmmode_obj = drm_mode_object_find(dev, pipe_from_crtc_id->crtc_id,
DRM_MODE_OBJECT_CRTC);
if (!drmmode_obj) {
dev_err(dev->dev, "no such CRTC id\n");
return -ENOENT;
}
crtc = to_gma_crtc(obj_to_crtc(drmmode_obj));
pipe_from_crtc_id->pipe = crtc->pipe;
return 0;
}
struct drm_crtc *psb_intel_get_crtc_from_pipe(struct drm_device *dev, int pipe)
{
struct drm_crtc *crtc = NULL;

View File

@ -238,8 +238,6 @@ static inline struct gma_encoder *gma_attached_encoder(
extern struct drm_display_mode *psb_intel_crtc_mode_get(struct drm_device *dev,
struct drm_crtc *crtc);
extern int psb_intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern struct drm_crtc *psb_intel_get_crtc_from_pipe(struct drm_device *dev,
int pipe);
extern struct drm_connector *psb_intel_sdvo_find(struct drm_device *dev,

View File

@ -614,7 +614,7 @@ int psb_intel_lvds_set_property(struct drm_connector *connector,
&crtc->saved_mode,
encoder->crtc->x,
encoder->crtc->y,
encoder->crtc->fb))
encoder->crtc->primary->fb))
goto set_prop_error;
}
} else if (!strcmp(property->name, "backlight")) {
@ -777,6 +777,7 @@ void psb_intel_lvds_init(struct drm_device *dev,
* Attempt to get the fixed panel mode from DDC. Assume that the
* preferred mode is the right one.
*/
mutex_lock(&dev->mode_config.mutex);
psb_intel_ddc_get_modes(connector, &lvds_priv->ddc_bus->adapter);
list_for_each_entry(scan, &connector->probed_modes, head) {
if (scan->type & DRM_MODE_TYPE_PREFERRED) {
@ -827,10 +828,12 @@ void psb_intel_lvds_init(struct drm_device *dev,
* actually having one.
*/
out:
mutex_unlock(&dev->mode_config.mutex);
drm_sysfs_connector_add(connector);
return;
failed_find:
mutex_unlock(&dev->mode_config.mutex);
if (lvds_priv->ddc_bus)
psb_intel_i2c_destroy(lvds_priv->ddc_bus);
failed_ddc:

View File

@ -406,18 +406,18 @@ static void psb_intel_sdvo_debug_write(struct psb_intel_sdvo *psb_intel_sdvo, u8
DRM_DEBUG_KMS("%s: W: %02X ",
SDVO_NAME(psb_intel_sdvo), cmd);
for (i = 0; i < args_len; i++)
DRM_LOG_KMS("%02X ", ((u8 *)args)[i]);
DRM_DEBUG_KMS("%02X ", ((u8 *)args)[i]);
for (; i < 8; i++)
DRM_LOG_KMS(" ");
DRM_DEBUG_KMS(" ");
for (i = 0; i < ARRAY_SIZE(sdvo_cmd_names); i++) {
if (cmd == sdvo_cmd_names[i].cmd) {
DRM_LOG_KMS("(%s)", sdvo_cmd_names[i].name);
DRM_DEBUG_KMS("(%s)", sdvo_cmd_names[i].name);
break;
}
}
if (i == ARRAY_SIZE(sdvo_cmd_names))
DRM_LOG_KMS("(%02X)", cmd);
DRM_LOG_KMS("\n");
DRM_DEBUG_KMS("(%02X)", cmd);
DRM_DEBUG_KMS("\n");
}
static const char *cmd_status_names[] = {
@ -512,9 +512,9 @@ static bool psb_intel_sdvo_read_response(struct psb_intel_sdvo *psb_intel_sdvo,
}
if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP)
DRM_LOG_KMS("(%s)", cmd_status_names[status]);
DRM_DEBUG_KMS("(%s)", cmd_status_names[status]);
else
DRM_LOG_KMS("(??? %d)", status);
DRM_DEBUG_KMS("(??? %d)", status);
if (status != SDVO_CMD_STATUS_SUCCESS)
goto log_fail;
@ -525,13 +525,13 @@ static bool psb_intel_sdvo_read_response(struct psb_intel_sdvo *psb_intel_sdvo,
SDVO_I2C_RETURN_0 + i,
&((u8 *)response)[i]))
goto log_fail;
DRM_LOG_KMS(" %02X", ((u8 *)response)[i]);
DRM_DEBUG_KMS(" %02X", ((u8 *)response)[i]);
}
DRM_LOG_KMS("\n");
DRM_DEBUG_KMS("\n");
return true;
log_fail:
DRM_LOG_KMS("... failed\n");
DRM_DEBUG_KMS("... failed\n");
return false;
}
@ -1844,7 +1844,7 @@ done:
if (psb_intel_sdvo->base.base.crtc) {
struct drm_crtc *crtc = psb_intel_sdvo->base.base.crtc;
drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x,
crtc->y, crtc->fb);
crtc->y, crtc->primary->fb);
}
return 0;

View File

@ -200,11 +200,64 @@ static void psb_vdc_interrupt(struct drm_device *dev, uint32_t vdc_stat)
mid_pipe_event_handler(dev, 1);
}
/*
* SGX interrupt handler
*/
static void psb_sgx_interrupt(struct drm_device *dev, u32 stat_1, u32 stat_2)
{
struct drm_psb_private *dev_priv = dev->dev_private;
u32 val, addr;
int error = false;
if (stat_1 & _PSB_CE_TWOD_COMPLETE)
val = PSB_RSGX32(PSB_CR_2D_BLIT_STATUS);
if (stat_2 & _PSB_CE2_BIF_REQUESTER_FAULT) {
val = PSB_RSGX32(PSB_CR_BIF_INT_STAT);
addr = PSB_RSGX32(PSB_CR_BIF_FAULT);
if (val) {
if (val & _PSB_CBI_STAT_PF_N_RW)
DRM_ERROR("SGX MMU page fault:");
else
DRM_ERROR("SGX MMU read / write protection fault:");
if (val & _PSB_CBI_STAT_FAULT_CACHE)
DRM_ERROR("\tCache requestor");
if (val & _PSB_CBI_STAT_FAULT_TA)
DRM_ERROR("\tTA requestor");
if (val & _PSB_CBI_STAT_FAULT_VDM)
DRM_ERROR("\tVDM requestor");
if (val & _PSB_CBI_STAT_FAULT_2D)
DRM_ERROR("\t2D requestor");
if (val & _PSB_CBI_STAT_FAULT_PBE)
DRM_ERROR("\tPBE requestor");
if (val & _PSB_CBI_STAT_FAULT_TSP)
DRM_ERROR("\tTSP requestor");
if (val & _PSB_CBI_STAT_FAULT_ISP)
DRM_ERROR("\tISP requestor");
if (val & _PSB_CBI_STAT_FAULT_USSEPDS)
DRM_ERROR("\tUSSEPDS requestor");
if (val & _PSB_CBI_STAT_FAULT_HOST)
DRM_ERROR("\tHost requestor");
DRM_ERROR("\tMMU failing address is 0x%08x.\n",
(unsigned int)addr);
error = true;
}
}
/* Clear bits */
PSB_WSGX32(stat_1, PSB_CR_EVENT_HOST_CLEAR);
PSB_WSGX32(stat_2, PSB_CR_EVENT_HOST_CLEAR2);
PSB_RSGX32(PSB_CR_EVENT_HOST_CLEAR2);
}
irqreturn_t psb_irq_handler(int irq, void *arg)
{
struct drm_device *dev = arg;
struct drm_psb_private *dev_priv = dev->dev_private;
uint32_t vdc_stat, dsp_int = 0, sgx_int = 0, hotplug_int = 0;
u32 sgx_stat_1, sgx_stat_2;
int handled = 0;
spin_lock(&dev_priv->irqmask_lock);
@ -233,14 +286,9 @@ irqreturn_t psb_irq_handler(int irq, void *arg)
}
if (sgx_int) {
/* Not expected - we have it masked, shut it up */
u32 s, s2;
s = PSB_RSGX32(PSB_CR_EVENT_STATUS);
s2 = PSB_RSGX32(PSB_CR_EVENT_STATUS2);
PSB_WSGX32(s, PSB_CR_EVENT_HOST_CLEAR);
PSB_WSGX32(s2, PSB_CR_EVENT_HOST_CLEAR2);
/* if s & _PSB_CE_TWOD_COMPLETE we have 2D done but
we may as well poll even if we add that ! */
sgx_stat_1 = PSB_RSGX32(PSB_CR_EVENT_STATUS);
sgx_stat_2 = PSB_RSGX32(PSB_CR_EVENT_STATUS2);
psb_sgx_interrupt(dev, sgx_stat_1, sgx_stat_2);
handled = 1;
}
@ -269,8 +317,13 @@ void psb_irq_preinstall(struct drm_device *dev)
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
if (gma_power_is_on(dev))
if (gma_power_is_on(dev)) {
PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM);
PSB_WVDC32(0x00000000, PSB_INT_MASK_R);
PSB_WVDC32(0x00000000, PSB_INT_ENABLE_R);
PSB_WSGX32(0x00000000, PSB_CR_EVENT_HOST_ENABLE);
PSB_RSGX32(PSB_CR_EVENT_HOST_ENABLE);
}
if (dev->vblank[0].enabled)
dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEA_FLAG;
if (dev->vblank[1].enabled)
@ -286,7 +339,7 @@ void psb_irq_preinstall(struct drm_device *dev)
/* Revisit this area - want per device masks ? */
if (dev_priv->ops->hotplug)
dev_priv->vdc_irq_mask |= _PSB_IRQ_DISP_HOTSYNC;
dev_priv->vdc_irq_mask |= _PSB_IRQ_ASLE;
dev_priv->vdc_irq_mask |= _PSB_IRQ_ASLE | _PSB_IRQ_SGX_FLAG;
/* This register is safe even if display island is off */
PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R);
@ -295,12 +348,16 @@ void psb_irq_preinstall(struct drm_device *dev)
int psb_irq_postinstall(struct drm_device *dev)
{
struct drm_psb_private *dev_priv =
(struct drm_psb_private *) dev->dev_private;
struct drm_psb_private *dev_priv = dev->dev_private;
unsigned long irqflags;
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
/* Enable 2D and MMU fault interrupts */
PSB_WSGX32(_PSB_CE2_BIF_REQUESTER_FAULT, PSB_CR_EVENT_HOST_ENABLE2);
PSB_WSGX32(_PSB_CE_TWOD_COMPLETE, PSB_CR_EVENT_HOST_ENABLE);
PSB_RSGX32(PSB_CR_EVENT_HOST_ENABLE); /* Post */
/* This register is safe even if display island is off */
PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R);
PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM);

Some files were not shown because too many files have changed in this diff Show More