mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-29 23:24:11 +08:00
Main pull request for drm for 4.10 kernel
-----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJYT3qqAAoJEAx081l5xIa+dLMP/2dqBybSAeWlPmAwVenIHRtS KFNktISezFSY/LBcIP2mHkFJmjTKBMZFxWnyEJL9NmFUD1cS2WMyNnC1282h/+rD +P8Bsmzmt/daV4UTFxVDpzlmVlavAyakNi6FnSQfAfmf+3PB1yzU3gn8ld9pU/if h7KEp9fDn9eYZreTRfCUloI2yoVpD9d0DG3uaGDN/N0kGUnCC6TZT5ig5j2JO016 fYf/DqoYAk3ItWF9WK/uG7qJIGi37afCpQq+kbSSJk+p3HjJqu8JUe9jzqYdl7j9 26TGSY5o9WLhZkxDgbcCIJzcFJhMmXgMdhjil9lqaHmnNG5FPFU7g8DK1CZqbel9 m8+aRPn1EgxIahMgdl8NblW1pfO2Kco0tZmoP5vXx1uqhivd67h0hiQqp66WxOJd i2yMLncaCEv8M161CVEgtzuI5a7nCfaZv7J9ArzbkD/huBwu51IZgTs7Dz4njgvz VPB5FBTB/ZYteErUNoh6gjF0hLngWvvJSPvuzT+EFO7yypek0IJ28GTdbxYSP+jR 13697s5Itigf/D3KUdRRGsWRzyVVN9n+djkl//sy5ddL9eOlKSKEga4ujOUjTWaW hTvAxpK9GmJS/Iun5jIP6f75zDbi+e8FWUeB/OI2lPtnApaSKdXBTPXsco2RnTEV +G6XrH8IMEIsTxOk7hWU =7s/c -----END PGP SIGNATURE----- Merge tag 'drm-for-v4.10' of git://people.freedesktop.org/~airlied/linux into drm-misc-next Main pull request for drm for 4.10 kernel - resync drm-misc with full 4.10 state (2 new drivers) so that we can start pulling in all the refactorings for 4.11! Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
This commit is contained in:
commit
010f5b9f0d
112
Documentation/devicetree/bindings/display/amlogic,meson-vpu.txt
Normal file
112
Documentation/devicetree/bindings/display/amlogic,meson-vpu.txt
Normal file
@ -0,0 +1,112 @@
|
||||
Amlogic Meson Display Controller
|
||||
================================
|
||||
|
||||
The Amlogic Meson Display controller is composed of several components
|
||||
that are going to be documented below:
|
||||
|
||||
DMC|---------------VPU (Video Processing Unit)----------------|------HHI------|
|
||||
| vd1 _______ _____________ _________________ | |
|
||||
D |-------| |----| | | | | HDMI PLL |
|
||||
D | vd2 | VIU | | Video Post | | Video Encoders |<---|-----VCLK |
|
||||
R |-------| |----| Processing | | | | |
|
||||
| osd2 | | | |---| Enci ----------|----|-----VDAC------|
|
||||
R |-------| CSC |----| Scalers | | Encp ----------|----|----HDMI-TX----|
|
||||
A | osd1 | | | Blenders | | Encl ----------|----|---------------|
|
||||
M |-------|______|----|____________| |________________| | |
|
||||
___|__________________________________________________________|_______________|
|
||||
|
||||
|
||||
VIU: Video Input Unit
|
||||
---------------------
|
||||
|
||||
The Video Input Unit is in charge of the pixel scanout from the DDR memory.
|
||||
It fetches the frames addresses, stride and parameters from the "Canvas" memory.
|
||||
This part is also in charge of the CSC (Colorspace Conversion).
|
||||
It can handle 2 OSD Planes and 2 Video Planes.
|
||||
|
||||
VPP: Video Post Processing
|
||||
--------------------------
|
||||
|
||||
The Video Post Processing is in charge of the scaling and blending of the
|
||||
various planes into a single pixel stream.
|
||||
There is a special "pre-blending" used by the video planes with a dedicated
|
||||
scaler and a "post-blending" to merge with the OSD Planes.
|
||||
The OSD planes also have a dedicated scaler for one of the OSD.
|
||||
|
||||
VENC: Video Encoders
|
||||
--------------------
|
||||
|
||||
The VENC is composed of the multiple pixel encoders :
|
||||
- ENCI : Interlace Video encoder for CVBS and Interlace HDMI
|
||||
- ENCP : Progressive Video Encoder for HDMI
|
||||
- ENCL : LCD LVDS Encoder
|
||||
The VENC Unit gets a Pixel Clocks (VCLK) from a dedicated HDMI PLL and clock
|
||||
tree and provides the scanout clock to the VPP and VIU.
|
||||
The ENCI is connected to a single VDAC for Composite Output.
|
||||
The ENCI and ENCP are connected to an on-chip HDMI Transceiver.
|
||||
|
||||
Device Tree Bindings:
|
||||
---------------------
|
||||
|
||||
VPU: Video Processing Unit
|
||||
--------------------------
|
||||
|
||||
Required properties:
|
||||
- compatible: value should be different for each SoC family as :
|
||||
- GXBB (S905) : "amlogic,meson-gxbb-vpu"
|
||||
- GXL (S905X, S905D) : "amlogic,meson-gxl-vpu"
|
||||
- GXM (S912) : "amlogic,meson-gxm-vpu"
|
||||
followed by the common "amlogic,meson-gx-vpu"
|
||||
- reg: base address and size of he following memory-mapped regions :
|
||||
- vpu
|
||||
- hhi
|
||||
- dmc
|
||||
- reg-names: should contain the names of the previous memory regions
|
||||
- interrupts: should contain the VENC Vsync interrupt number
|
||||
|
||||
Required nodes:
|
||||
|
||||
The connections to the VPU output video ports are modeled using the OF graph
|
||||
bindings specified in Documentation/devicetree/bindings/graph.txt.
|
||||
|
||||
The following table lists for each supported model the port number
|
||||
corresponding to each VPU output.
|
||||
|
||||
Port 0 Port 1
|
||||
-----------------------------------------
|
||||
S905 (GXBB) CVBS VDAC HDMI-TX
|
||||
S905X (GXL) CVBS VDAC HDMI-TX
|
||||
S905D (GXL) CVBS VDAC HDMI-TX
|
||||
S912 (GXM) CVBS VDAC HDMI-TX
|
||||
|
||||
Example:
|
||||
|
||||
tv-connector {
|
||||
compatible = "composite-video-connector";
|
||||
|
||||
port {
|
||||
tv_connector_in: endpoint {
|
||||
remote-endpoint = <&cvbs_vdac_out>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
vpu: vpu@d0100000 {
|
||||
compatible = "amlogic,meson-gxbb-vpu";
|
||||
reg = <0x0 0xd0100000 0x0 0x100000>,
|
||||
<0x0 0xc883c000 0x0 0x1000>,
|
||||
<0x0 0xc8838000 0x0 0x1000>;
|
||||
reg-names = "vpu", "hhi", "dmc";
|
||||
interrupts = <GIC_SPI 3 IRQ_TYPE_EDGE_RISING>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* CVBS VDAC output port */
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
|
||||
cvbs_vdac_out: endpoint {
|
||||
remote-endpoint = <&tv_connector_in>;
|
||||
};
|
||||
};
|
||||
};
|
@ -43,6 +43,13 @@ Required properties for DPI:
|
||||
- port: Port node with a single endpoint connecting to the panel
|
||||
device, as defined in [1]
|
||||
|
||||
Required properties for VEC:
|
||||
- compatible: Should be "brcm,bcm2835-vec"
|
||||
- reg: Physical base address and length of the registers
|
||||
- clocks: The core clock the unit runs on
|
||||
- interrupts: The interrupt number
|
||||
See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
|
||||
|
||||
Required properties for V3D:
|
||||
- compatible: Should be "brcm,bcm2835-v3d"
|
||||
- reg: Physical base address and length of the V3D's registers
|
||||
@ -92,6 +99,13 @@ dpi: dpi@7e208000 {
|
||||
};
|
||||
};
|
||||
|
||||
vec: vec@7e806000 {
|
||||
compatible = "brcm,bcm2835-vec";
|
||||
reg = <0x7e806000 0x1000>;
|
||||
clocks = <&clocks BCM2835_CLOCK_VEC>;
|
||||
interrupts = <2 27>;
|
||||
};
|
||||
|
||||
v3d: v3d@7ec00000 {
|
||||
compatible = "brcm,bcm2835-v3d";
|
||||
reg = <0x7ec00000 0x1000>;
|
||||
|
@ -1,20 +1,57 @@
|
||||
* Freescale MXS LCD Interface (LCDIF)
|
||||
|
||||
New bindings:
|
||||
=============
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,<chip>-lcdif". Supported chips include
|
||||
imx23 and imx28.
|
||||
- reg: Address and length of the register set for lcdif
|
||||
- interrupts: Should contain lcdif interrupts
|
||||
- display : phandle to display node (see below for details)
|
||||
- compatible: Should be "fsl,imx23-lcdif" for i.MX23.
|
||||
Should be "fsl,imx28-lcdif" for i.MX28.
|
||||
Should be "fsl,imx6sx-lcdif" for i.MX6SX.
|
||||
- reg: Address and length of the register set for LCDIF
|
||||
- interrupts: Should contain LCDIF interrupt
|
||||
- clocks: A list of phandle + clock-specifier pairs, one for each
|
||||
entry in 'clock-names'.
|
||||
- clock-names: A list of clock names. For MXSFB it should contain:
|
||||
- "pix" for the LCDIF block clock
|
||||
- (MX6SX-only) "axi", "disp_axi" for the bus interface clock
|
||||
|
||||
Required sub-nodes:
|
||||
- port: The connection to an encoder chip.
|
||||
|
||||
Example:
|
||||
|
||||
lcdif1: display-controller@2220000 {
|
||||
compatible = "fsl,imx6sx-lcdif", "fsl,imx28-lcdif";
|
||||
reg = <0x02220000 0x4000>;
|
||||
interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clks IMX6SX_CLK_LCDIF1_PIX>,
|
||||
<&clks IMX6SX_CLK_LCDIF_APB>,
|
||||
<&clks IMX6SX_CLK_DISPLAY_AXI>;
|
||||
clock-names = "pix", "axi", "disp_axi";
|
||||
|
||||
port {
|
||||
parallel_out: endpoint {
|
||||
remote-endpoint = <&panel_in_parallel>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Deprecated bindings:
|
||||
====================
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,imx23-lcdif" for i.MX23.
|
||||
Should be "fsl,imx28-lcdif" for i.MX28.
|
||||
- reg: Address and length of the register set for LCDIF
|
||||
- interrupts: Should contain LCDIF interrupts
|
||||
- display: phandle to display node (see below for details)
|
||||
|
||||
* display node
|
||||
|
||||
Required properties:
|
||||
- bits-per-pixel : <16> for RGB565, <32> for RGB888/666.
|
||||
- bus-width : number of data lines. Could be <8>, <16>, <18> or <24>.
|
||||
- bits-per-pixel: <16> for RGB565, <32> for RGB888/666.
|
||||
- bus-width: number of data lines. Could be <8>, <16>, <18> or <24>.
|
||||
|
||||
Required sub-node:
|
||||
- display-timings : Refer to binding doc display-timing.txt for details.
|
||||
- display-timings: Refer to binding doc display-timing.txt for details.
|
||||
|
||||
Examples:
|
||||
|
||||
|
@ -0,0 +1,7 @@
|
||||
AU Optronics Corporation 13.3" FHD (1920x1080) TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "auo,g133han01"
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
@ -0,0 +1,7 @@
|
||||
AU Optronics Corporation 18.5" FHD (1920x1080) TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "auo,g185han01"
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
@ -0,0 +1,7 @@
|
||||
AU Optronics Corporation 21.5" FHD (1920x1080) color TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "auo,t215hvn01"
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
@ -0,0 +1,7 @@
|
||||
Chunghwa Picture Tubes Ltd. 7" WXGA TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "chunghwa,claa070wp03xg"
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
@ -0,0 +1,7 @@
|
||||
New Vision Display 7.0" 800 RGB x 480 TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "nvd,9128"
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
@ -0,0 +1,36 @@
|
||||
Sharp 15" LQ150X1LG11 XGA TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "sharp,lq150x1lg11"
|
||||
- power-supply: regulator to provide the VCC supply voltage (3.3 volts)
|
||||
|
||||
Optional properties:
|
||||
- backlight: phandle of the backlight device
|
||||
- rlud-gpios: a single GPIO for the RL/UD (rotate 180 degrees) pin.
|
||||
- sellvds-gpios: a single GPIO for the SELLVDS pin.
|
||||
|
||||
If rlud-gpios and/or sellvds-gpios are not specified, the RL/UD and/or SELLVDS
|
||||
pins are assumed to be handled appropriately by the hardware.
|
||||
|
||||
Example:
|
||||
|
||||
backlight: backlight {
|
||||
compatible = "pwm-backlight";
|
||||
pwms = <&pwm 0 100000>; /* VBR */
|
||||
|
||||
brightness-levels = <0 20 40 60 80 100>;
|
||||
default-brightness-level = <2>;
|
||||
|
||||
power-supply = <&vdd_12v_reg>; /* VDD */
|
||||
enable-gpios = <&gpio 42 GPIO_ACTIVE_HIGH>; /* XSTABY */
|
||||
};
|
||||
|
||||
panel {
|
||||
compatible = "sharp,lq150x1lg11";
|
||||
|
||||
power-supply = <&vcc_3v3_reg>; /* VCC */
|
||||
|
||||
backlight = <&backlight>;
|
||||
rlud-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>; /* RL/UD */
|
||||
sellvds-gpios = <&gpio 18 GPIO_ACTIVE_HIGH>; /* SELLVDS */
|
||||
};
|
@ -187,6 +187,7 @@ netgear NETGEAR
|
||||
netlogic Broadcom Corporation (formerly NetLogic Microsystems)
|
||||
netxeon Shenzhen Netxeon Technology CO., LTD
|
||||
newhaven Newhaven Display International
|
||||
nvd New Vision Display
|
||||
nintendo Nintendo
|
||||
nokia Nokia
|
||||
nuvoton Nuvoton Technology Corporation
|
||||
|
15
MAINTAINERS
15
MAINTAINERS
@ -4121,6 +4121,15 @@ S: Supported
|
||||
F: drivers/gpu/drm/sun4i/
|
||||
F: Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
|
||||
|
||||
DRM DRIVERS FOR AMLOGIC SOCS
|
||||
M: Neil Armstrong <narmstrong@baylibre.com>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
L: linux-amlogic@lists.infradead.org
|
||||
W: http://linux-meson.com/
|
||||
S: Supported
|
||||
F: drivers/gpu/drm/meson/
|
||||
F: Documentation/devicetree/bindings/display/amlogic,meson-vpu.txt
|
||||
|
||||
DRM DRIVERS FOR EXYNOS
|
||||
M: Inki Dae <inki.dae@samsung.com>
|
||||
M: Joonyoung Shim <jy0922.shim@samsung.com>
|
||||
@ -8319,6 +8328,12 @@ T: git git://linuxtv.org/mkrufky/tuners.git
|
||||
S: Maintained
|
||||
F: drivers/media/tuners/mxl5007t.*
|
||||
|
||||
MXSFB DRM DRIVER
|
||||
M: Marek Vasut <marex@denx.de>
|
||||
S: Supported
|
||||
F: drivers/gpu/drm/mxsfb/
|
||||
F: Documentation/devicetree/bindings/display/mxsfb-drm.txt
|
||||
|
||||
MYRICOM MYRI-10G 10GbE DRIVER (MYRI10GE)
|
||||
M: Hyong-Youb Kim <hykim@myri.com>
|
||||
L: netdev@vger.kernel.org
|
||||
|
@ -240,6 +240,10 @@ source "drivers/gpu/drm/mediatek/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/zte/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/mxsfb/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/meson/Kconfig"
|
||||
|
||||
# Keep legacy drivers last
|
||||
|
||||
menuconfig DRM_LEGACY
|
||||
|
@ -81,6 +81,7 @@ obj-$(CONFIG_DRM_TEGRA) += tegra/
|
||||
obj-$(CONFIG_DRM_STI) += sti/
|
||||
obj-$(CONFIG_DRM_IMX) += imx/
|
||||
obj-$(CONFIG_DRM_MEDIATEK) += mediatek/
|
||||
obj-$(CONFIG_DRM_MESON) += meson/
|
||||
obj-y += i2c/
|
||||
obj-y += panel/
|
||||
obj-y += bridge/
|
||||
@ -89,3 +90,4 @@ obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/
|
||||
obj-$(CONFIG_DRM_ARCPGU)+= arc/
|
||||
obj-y += hisilicon/
|
||||
obj-$(CONFIG_DRM_ZTE) += zte/
|
||||
obj-$(CONFIG_DRM_MXSFB) += mxsfb/
|
||||
|
@ -842,6 +842,8 @@ struct amdgpu_gfx_funcs {
|
||||
uint64_t (*get_gpu_clock_counter)(struct amdgpu_device *adev);
|
||||
void (*select_se_sh)(struct amdgpu_device *adev, u32 se_num, u32 sh_num, u32 instance);
|
||||
void (*read_wave_data)(struct amdgpu_device *adev, uint32_t simd, uint32_t wave, uint32_t *dst, int *no_fields);
|
||||
void (*read_wave_vgprs)(struct amdgpu_device *adev, uint32_t simd, uint32_t wave, uint32_t thread, uint32_t start, uint32_t size, uint32_t *dst);
|
||||
void (*read_wave_sgprs)(struct amdgpu_device *adev, uint32_t simd, uint32_t wave, uint32_t start, uint32_t size, uint32_t *dst);
|
||||
};
|
||||
|
||||
struct amdgpu_gfx {
|
||||
@ -1330,6 +1332,7 @@ struct amdgpu_device {
|
||||
|
||||
/* BIOS */
|
||||
uint8_t *bios;
|
||||
uint32_t bios_size;
|
||||
bool is_atom_bios;
|
||||
struct amdgpu_bo *stollen_vga_memory;
|
||||
uint32_t bios_scratch[AMDGPU_BIOS_NUM_SCRATCH];
|
||||
@ -1679,8 +1682,6 @@ uint32_t amdgpu_ttm_tt_pte_flags(struct amdgpu_device *adev, struct ttm_tt *ttm,
|
||||
void amdgpu_vram_location(struct amdgpu_device *adev, struct amdgpu_mc *mc, u64 base);
|
||||
void amdgpu_gtt_location(struct amdgpu_device *adev, struct amdgpu_mc *mc);
|
||||
void amdgpu_ttm_set_active_vram_size(struct amdgpu_device *adev, u64 size);
|
||||
u64 amdgpu_ttm_get_gtt_mem_size(struct amdgpu_device *adev);
|
||||
int amdgpu_ttm_global_init(struct amdgpu_device *adev);
|
||||
int amdgpu_ttm_init(struct amdgpu_device *adev);
|
||||
void amdgpu_ttm_fini(struct amdgpu_device *adev);
|
||||
void amdgpu_program_register_sequence(struct amdgpu_device *adev,
|
||||
|
@ -74,6 +74,7 @@ static bool igp_read_bios_from_vram(struct amdgpu_device *adev)
|
||||
iounmap(bios);
|
||||
return false;
|
||||
}
|
||||
adev->bios_size = size;
|
||||
memcpy_fromio(adev->bios, bios, size);
|
||||
iounmap(bios);
|
||||
return true;
|
||||
@ -103,6 +104,7 @@ bool amdgpu_read_bios(struct amdgpu_device *adev)
|
||||
pci_unmap_rom(adev->pdev, bios);
|
||||
return false;
|
||||
}
|
||||
adev->bios_size = size;
|
||||
memcpy_fromio(adev->bios, bios, size);
|
||||
pci_unmap_rom(adev->pdev, bios);
|
||||
return true;
|
||||
@ -135,6 +137,7 @@ static bool amdgpu_read_bios_from_rom(struct amdgpu_device *adev)
|
||||
DRM_ERROR("no memory to allocate for BIOS\n");
|
||||
return false;
|
||||
}
|
||||
adev->bios_size = len;
|
||||
|
||||
/* read complete BIOS */
|
||||
return amdgpu_asic_read_bios_from_rom(adev, adev->bios, len);
|
||||
@ -159,6 +162,7 @@ static bool amdgpu_read_platform_bios(struct amdgpu_device *adev)
|
||||
if (adev->bios == NULL) {
|
||||
return false;
|
||||
}
|
||||
adev->bios_size = size;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -273,6 +277,7 @@ static bool amdgpu_atrm_get_bios(struct amdgpu_device *adev)
|
||||
kfree(adev->bios);
|
||||
return false;
|
||||
}
|
||||
adev->bios_size = size;
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
@ -334,6 +339,7 @@ static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev)
|
||||
}
|
||||
|
||||
adev->bios = kmemdup(&vbios->VbiosContent, vhdr->ImageLength, GFP_KERNEL);
|
||||
adev->bios_size = vhdr->ImageLength;
|
||||
ret = !!adev->bios;
|
||||
|
||||
out_unmap:
|
||||
|
@ -723,7 +723,7 @@ static uint16_t amdgpu_get_firmware_version(struct cgs_device *cgs_device,
|
||||
enum cgs_ucode_id type)
|
||||
{
|
||||
CGS_FUNC_ADEV;
|
||||
uint16_t fw_version;
|
||||
uint16_t fw_version = 0;
|
||||
|
||||
switch (type) {
|
||||
case CGS_UCODE_ID_SDMA0:
|
||||
@ -753,9 +753,11 @@ static uint16_t amdgpu_get_firmware_version(struct cgs_device *cgs_device,
|
||||
case CGS_UCODE_ID_RLC_G:
|
||||
fw_version = adev->gfx.rlc_fw_version;
|
||||
break;
|
||||
case CGS_UCODE_ID_STORAGE:
|
||||
break;
|
||||
default:
|
||||
DRM_ERROR("firmware type %d do not have version\n", type);
|
||||
fw_version = 0;
|
||||
break;
|
||||
}
|
||||
return fw_version;
|
||||
}
|
||||
|
@ -451,7 +451,7 @@ static int amdgpu_cs_validate(void *param, struct amdgpu_bo *bo)
|
||||
return r;
|
||||
|
||||
if (bo->shadow)
|
||||
r = amdgpu_cs_bo_validate(p, bo);
|
||||
r = amdgpu_cs_bo_validate(p, bo->shadow);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
@ -1470,20 +1470,26 @@ static int amdgpu_fini(struct amdgpu_device *adev)
|
||||
amdgpu_wb_fini(adev);
|
||||
amdgpu_vram_scratch_fini(adev);
|
||||
}
|
||||
/* ungate blocks before hw fini so that we can shutdown the blocks safely */
|
||||
r = adev->ip_blocks[i].version->funcs->set_clockgating_state((void *)adev,
|
||||
AMD_CG_STATE_UNGATE);
|
||||
if (r) {
|
||||
DRM_ERROR("set_clockgating_state(ungate) of IP block <%s> failed %d\n",
|
||||
adev->ip_blocks[i].version->funcs->name, r);
|
||||
return r;
|
||||
|
||||
if (adev->ip_blocks[i].version->type != AMD_IP_BLOCK_TYPE_UVD &&
|
||||
adev->ip_blocks[i].version->type != AMD_IP_BLOCK_TYPE_VCE) {
|
||||
/* ungate blocks before hw fini so that we can shutdown the blocks safely */
|
||||
r = adev->ip_blocks[i].version->funcs->set_clockgating_state((void *)adev,
|
||||
AMD_CG_STATE_UNGATE);
|
||||
if (r) {
|
||||
DRM_ERROR("set_clockgating_state(ungate) of IP block <%s> failed %d\n",
|
||||
adev->ip_blocks[i].version->funcs->name, r);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
r = adev->ip_blocks[i].version->funcs->hw_fini((void *)adev);
|
||||
/* XXX handle errors */
|
||||
if (r) {
|
||||
DRM_DEBUG("hw_fini of IP block <%s> failed %d\n",
|
||||
adev->ip_blocks[i].version->funcs->name, r);
|
||||
}
|
||||
|
||||
adev->ip_blocks[i].status.hw = false;
|
||||
}
|
||||
|
||||
@ -2973,6 +2979,66 @@ static ssize_t amdgpu_debugfs_wave_read(struct file *f, char __user *buf,
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t amdgpu_debugfs_gpr_read(struct file *f, char __user *buf,
|
||||
size_t size, loff_t *pos)
|
||||
{
|
||||
struct amdgpu_device *adev = f->f_inode->i_private;
|
||||
int r;
|
||||
ssize_t result = 0;
|
||||
uint32_t offset, se, sh, cu, wave, simd, thread, bank, *data;
|
||||
|
||||
if (size & 3 || *pos & 3)
|
||||
return -EINVAL;
|
||||
|
||||
/* decode offset */
|
||||
offset = (*pos & 0xFFF); /* in dwords */
|
||||
se = ((*pos >> 12) & 0xFF);
|
||||
sh = ((*pos >> 20) & 0xFF);
|
||||
cu = ((*pos >> 28) & 0xFF);
|
||||
wave = ((*pos >> 36) & 0xFF);
|
||||
simd = ((*pos >> 44) & 0xFF);
|
||||
thread = ((*pos >> 52) & 0xFF);
|
||||
bank = ((*pos >> 60) & 1);
|
||||
|
||||
data = kmalloc_array(1024, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
/* switch to the specific se/sh/cu */
|
||||
mutex_lock(&adev->grbm_idx_mutex);
|
||||
amdgpu_gfx_select_se_sh(adev, se, sh, cu);
|
||||
|
||||
if (bank == 0) {
|
||||
if (adev->gfx.funcs->read_wave_vgprs)
|
||||
adev->gfx.funcs->read_wave_vgprs(adev, simd, wave, thread, offset, size>>2, data);
|
||||
} else {
|
||||
if (adev->gfx.funcs->read_wave_sgprs)
|
||||
adev->gfx.funcs->read_wave_sgprs(adev, simd, wave, offset, size>>2, data);
|
||||
}
|
||||
|
||||
amdgpu_gfx_select_se_sh(adev, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF);
|
||||
mutex_unlock(&adev->grbm_idx_mutex);
|
||||
|
||||
while (size) {
|
||||
uint32_t value;
|
||||
|
||||
value = data[offset++];
|
||||
r = put_user(value, (uint32_t *)buf);
|
||||
if (r) {
|
||||
result = r;
|
||||
goto err;
|
||||
}
|
||||
|
||||
result += 4;
|
||||
buf += 4;
|
||||
size -= 4;
|
||||
}
|
||||
|
||||
err:
|
||||
kfree(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
static const struct file_operations amdgpu_debugfs_regs_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = amdgpu_debugfs_regs_read,
|
||||
@ -3015,6 +3081,11 @@ static const struct file_operations amdgpu_debugfs_wave_fops = {
|
||||
.read = amdgpu_debugfs_wave_read,
|
||||
.llseek = default_llseek
|
||||
};
|
||||
static const struct file_operations amdgpu_debugfs_gpr_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = amdgpu_debugfs_gpr_read,
|
||||
.llseek = default_llseek
|
||||
};
|
||||
|
||||
static const struct file_operations *debugfs_regs[] = {
|
||||
&amdgpu_debugfs_regs_fops,
|
||||
@ -3024,6 +3095,7 @@ static const struct file_operations *debugfs_regs[] = {
|
||||
&amdgpu_debugfs_gca_config_fops,
|
||||
&amdgpu_debugfs_sensors_fops,
|
||||
&amdgpu_debugfs_wave_fops,
|
||||
&amdgpu_debugfs_gpr_fops,
|
||||
};
|
||||
|
||||
static const char *debugfs_regs_names[] = {
|
||||
@ -3034,6 +3106,7 @@ static const char *debugfs_regs_names[] = {
|
||||
"amdgpu_gca_config",
|
||||
"amdgpu_sensors",
|
||||
"amdgpu_wave",
|
||||
"amdgpu_gpr",
|
||||
};
|
||||
|
||||
static int amdgpu_debugfs_regs_init(struct amdgpu_device *adev)
|
||||
|
@ -187,7 +187,7 @@ int amdgpu_crtc_page_flip_target(struct drm_crtc *crtc,
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
r = amdgpu_bo_pin_restricted(new_abo, AMDGPU_GEM_DOMAIN_VRAM, 0, 0, &base);
|
||||
r = amdgpu_bo_pin(new_abo, AMDGPU_GEM_DOMAIN_VRAM, &base);
|
||||
if (unlikely(r != 0)) {
|
||||
r = -EINVAL;
|
||||
DRM_ERROR("failed to pin new abo buffer before flip\n");
|
||||
|
@ -171,7 +171,7 @@ static int amdgpufb_create_pinned_object(struct amdgpu_fbdev *rfbdev,
|
||||
}
|
||||
|
||||
|
||||
ret = amdgpu_bo_pin_restricted(abo, AMDGPU_GEM_DOMAIN_VRAM, 0, 0, NULL);
|
||||
ret = amdgpu_bo_pin(abo, AMDGPU_GEM_DOMAIN_VRAM, NULL);
|
||||
if (ret) {
|
||||
amdgpu_bo_unreserve(abo);
|
||||
goto out_unref;
|
||||
|
@ -164,8 +164,10 @@ static int amdgpu_gtt_mgr_new(struct ttm_mem_type_manager *man,
|
||||
spin_unlock(&mgr->lock);
|
||||
|
||||
node = kzalloc(sizeof(*node), GFP_KERNEL);
|
||||
if (!node)
|
||||
return -ENOMEM;
|
||||
if (!node) {
|
||||
r = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
node->start = AMDGPU_BO_INVALID_OFFSET;
|
||||
node->size = mem->num_pages;
|
||||
@ -176,12 +178,20 @@ static int amdgpu_gtt_mgr_new(struct ttm_mem_type_manager *man,
|
||||
if (unlikely(r)) {
|
||||
kfree(node);
|
||||
mem->mm_node = NULL;
|
||||
r = 0;
|
||||
goto err_out;
|
||||
}
|
||||
} else {
|
||||
mem->start = node->start;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_out:
|
||||
spin_lock(&mgr->lock);
|
||||
mgr->available += mem->num_pages;
|
||||
spin_unlock(&mgr->lock);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -544,6 +544,32 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file
|
||||
return copy_to_user(out, &vce_clk_table,
|
||||
min((size_t)size, sizeof(vce_clk_table))) ? -EFAULT : 0;
|
||||
}
|
||||
case AMDGPU_INFO_VBIOS: {
|
||||
uint32_t bios_size = adev->bios_size;
|
||||
|
||||
switch (info->vbios_info.type) {
|
||||
case AMDGPU_INFO_VBIOS_SIZE:
|
||||
return copy_to_user(out, &bios_size,
|
||||
min((size_t)size, sizeof(bios_size)))
|
||||
? -EFAULT : 0;
|
||||
case AMDGPU_INFO_VBIOS_IMAGE: {
|
||||
uint8_t *bios;
|
||||
uint32_t bios_offset = info->vbios_info.offset;
|
||||
|
||||
if (bios_offset >= bios_size)
|
||||
return -EINVAL;
|
||||
|
||||
bios = adev->bios + bios_offset;
|
||||
return copy_to_user(out, bios,
|
||||
min((size_t)size, (size_t)(bios_size - bios_offset)))
|
||||
? -EFAULT : 0;
|
||||
}
|
||||
default:
|
||||
DRM_DEBUG_KMS("Invalid request %d\n",
|
||||
info->vbios_info.type);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
default:
|
||||
DRM_DEBUG_KMS("Invalid request %d\n", info->query);
|
||||
return -EINVAL;
|
||||
|
@ -34,7 +34,6 @@
|
||||
#include <ttm/ttm_placement.h>
|
||||
#include <ttm/ttm_module.h>
|
||||
#include <ttm/ttm_page_alloc.h>
|
||||
#include <ttm/ttm_memory.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/amdgpu_drm.h>
|
||||
#include <linux/seq_file.h>
|
||||
@ -65,7 +64,7 @@ static void amdgpu_ttm_mem_global_release(struct drm_global_reference *ref)
|
||||
ttm_mem_global_release(ref->object);
|
||||
}
|
||||
|
||||
int amdgpu_ttm_global_init(struct amdgpu_device *adev)
|
||||
static int amdgpu_ttm_global_init(struct amdgpu_device *adev)
|
||||
{
|
||||
struct drm_global_reference *global_ref;
|
||||
struct amdgpu_ring *ring;
|
||||
@ -1151,6 +1150,10 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
|
||||
unsigned i, j;
|
||||
int r;
|
||||
|
||||
r = amdgpu_ttm_global_init(adev);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
/* No others user of address space so set it to 0 */
|
||||
r = ttm_bo_device_init(&adev->mman.bdev,
|
||||
adev->mman.bo_global_ref.ref.object,
|
||||
@ -1650,8 +1653,3 @@ static void amdgpu_ttm_debugfs_fini(struct amdgpu_device *adev)
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
u64 amdgpu_ttm_get_gtt_mem_size(struct amdgpu_device *adev)
|
||||
{
|
||||
return ttm_get_kernel_zone_memory_size(adev->mman.mem_global_ref.object);
|
||||
}
|
||||
|
@ -6083,7 +6083,7 @@ ci_dpm_debugfs_print_current_performance_level(struct amdgpu_device *adev,
|
||||
activity_percent = activity_percent > 100 ? 100 : activity_percent;
|
||||
}
|
||||
|
||||
seq_printf(m, "uvd %sabled\n", pi->uvd_enabled ? "en" : "dis");
|
||||
seq_printf(m, "uvd %sabled\n", pi->uvd_power_gated ? "dis" : "en");
|
||||
seq_printf(m, "vce %sabled\n", rps->vce_active ? "en" : "dis");
|
||||
seq_printf(m, "power level avg sclk: %u mclk: %u\n",
|
||||
sclk, mclk);
|
||||
|
@ -2493,6 +2493,9 @@ static int dce_v10_0_cursor_move_locked(struct drm_crtc *crtc,
|
||||
struct amdgpu_device *adev = crtc->dev->dev_private;
|
||||
int xorigin = 0, yorigin = 0;
|
||||
|
||||
amdgpu_crtc->cursor_x = x;
|
||||
amdgpu_crtc->cursor_y = y;
|
||||
|
||||
/* avivo cursor are offset into the total surface */
|
||||
x += crtc->x;
|
||||
y += crtc->y;
|
||||
@ -2509,11 +2512,6 @@ static int dce_v10_0_cursor_move_locked(struct drm_crtc *crtc,
|
||||
|
||||
WREG32(mmCUR_POSITION + amdgpu_crtc->crtc_offset, (x << 16) | y);
|
||||
WREG32(mmCUR_HOT_SPOT + amdgpu_crtc->crtc_offset, (xorigin << 16) | yorigin);
|
||||
WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset,
|
||||
((amdgpu_crtc->cursor_width - 1) << 16) | (amdgpu_crtc->cursor_height - 1));
|
||||
|
||||
amdgpu_crtc->cursor_x = x;
|
||||
amdgpu_crtc->cursor_y = y;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2539,6 +2537,7 @@ static int dce_v10_0_crtc_cursor_set2(struct drm_crtc *crtc,
|
||||
int32_t hot_y)
|
||||
{
|
||||
struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
|
||||
struct amdgpu_device *adev = crtc->dev->dev_private;
|
||||
struct drm_gem_object *obj;
|
||||
struct amdgpu_bo *aobj;
|
||||
int ret;
|
||||
@ -2577,9 +2576,6 @@ static int dce_v10_0_crtc_cursor_set2(struct drm_crtc *crtc,
|
||||
return ret;
|
||||
}
|
||||
|
||||
amdgpu_crtc->cursor_width = width;
|
||||
amdgpu_crtc->cursor_height = height;
|
||||
|
||||
dce_v10_0_lock_cursor(crtc, true);
|
||||
|
||||
if (hot_x != amdgpu_crtc->cursor_hot_x ||
|
||||
@ -2595,6 +2591,14 @@ static int dce_v10_0_crtc_cursor_set2(struct drm_crtc *crtc,
|
||||
amdgpu_crtc->cursor_hot_y = hot_y;
|
||||
}
|
||||
|
||||
if (width != amdgpu_crtc->cursor_width ||
|
||||
height != amdgpu_crtc->cursor_height) {
|
||||
WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset,
|
||||
(width - 1) << 16 | (height - 1));
|
||||
amdgpu_crtc->cursor_width = width;
|
||||
amdgpu_crtc->cursor_height = height;
|
||||
}
|
||||
|
||||
dce_v10_0_show_cursor(crtc);
|
||||
dce_v10_0_lock_cursor(crtc, false);
|
||||
|
||||
@ -2616,6 +2620,7 @@ unpin:
|
||||
static void dce_v10_0_cursor_reset(struct drm_crtc *crtc)
|
||||
{
|
||||
struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
|
||||
struct amdgpu_device *adev = crtc->dev->dev_private;
|
||||
|
||||
if (amdgpu_crtc->cursor_bo) {
|
||||
dce_v10_0_lock_cursor(crtc, true);
|
||||
@ -2623,6 +2628,10 @@ static void dce_v10_0_cursor_reset(struct drm_crtc *crtc)
|
||||
dce_v10_0_cursor_move_locked(crtc, amdgpu_crtc->cursor_x,
|
||||
amdgpu_crtc->cursor_y);
|
||||
|
||||
WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset,
|
||||
(amdgpu_crtc->cursor_width - 1) << 16 |
|
||||
(amdgpu_crtc->cursor_height - 1));
|
||||
|
||||
dce_v10_0_show_cursor(crtc);
|
||||
|
||||
dce_v10_0_lock_cursor(crtc, false);
|
||||
|
@ -2509,6 +2509,9 @@ static int dce_v11_0_cursor_move_locked(struct drm_crtc *crtc,
|
||||
struct amdgpu_device *adev = crtc->dev->dev_private;
|
||||
int xorigin = 0, yorigin = 0;
|
||||
|
||||
amdgpu_crtc->cursor_x = x;
|
||||
amdgpu_crtc->cursor_y = y;
|
||||
|
||||
/* avivo cursor are offset into the total surface */
|
||||
x += crtc->x;
|
||||
y += crtc->y;
|
||||
@ -2525,11 +2528,6 @@ static int dce_v11_0_cursor_move_locked(struct drm_crtc *crtc,
|
||||
|
||||
WREG32(mmCUR_POSITION + amdgpu_crtc->crtc_offset, (x << 16) | y);
|
||||
WREG32(mmCUR_HOT_SPOT + amdgpu_crtc->crtc_offset, (xorigin << 16) | yorigin);
|
||||
WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset,
|
||||
((amdgpu_crtc->cursor_width - 1) << 16) | (amdgpu_crtc->cursor_height - 1));
|
||||
|
||||
amdgpu_crtc->cursor_x = x;
|
||||
amdgpu_crtc->cursor_y = y;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2555,6 +2553,7 @@ static int dce_v11_0_crtc_cursor_set2(struct drm_crtc *crtc,
|
||||
int32_t hot_y)
|
||||
{
|
||||
struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
|
||||
struct amdgpu_device *adev = crtc->dev->dev_private;
|
||||
struct drm_gem_object *obj;
|
||||
struct amdgpu_bo *aobj;
|
||||
int ret;
|
||||
@ -2593,9 +2592,6 @@ static int dce_v11_0_crtc_cursor_set2(struct drm_crtc *crtc,
|
||||
return ret;
|
||||
}
|
||||
|
||||
amdgpu_crtc->cursor_width = width;
|
||||
amdgpu_crtc->cursor_height = height;
|
||||
|
||||
dce_v11_0_lock_cursor(crtc, true);
|
||||
|
||||
if (hot_x != amdgpu_crtc->cursor_hot_x ||
|
||||
@ -2611,6 +2607,14 @@ static int dce_v11_0_crtc_cursor_set2(struct drm_crtc *crtc,
|
||||
amdgpu_crtc->cursor_hot_y = hot_y;
|
||||
}
|
||||
|
||||
if (width != amdgpu_crtc->cursor_width ||
|
||||
height != amdgpu_crtc->cursor_height) {
|
||||
WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset,
|
||||
(width - 1) << 16 | (height - 1));
|
||||
amdgpu_crtc->cursor_width = width;
|
||||
amdgpu_crtc->cursor_height = height;
|
||||
}
|
||||
|
||||
dce_v11_0_show_cursor(crtc);
|
||||
dce_v11_0_lock_cursor(crtc, false);
|
||||
|
||||
@ -2632,6 +2636,7 @@ unpin:
|
||||
static void dce_v11_0_cursor_reset(struct drm_crtc *crtc)
|
||||
{
|
||||
struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
|
||||
struct amdgpu_device *adev = crtc->dev->dev_private;
|
||||
|
||||
if (amdgpu_crtc->cursor_bo) {
|
||||
dce_v11_0_lock_cursor(crtc, true);
|
||||
@ -2639,6 +2644,10 @@ static void dce_v11_0_cursor_reset(struct drm_crtc *crtc)
|
||||
dce_v11_0_cursor_move_locked(crtc, amdgpu_crtc->cursor_x,
|
||||
amdgpu_crtc->cursor_y);
|
||||
|
||||
WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset,
|
||||
(amdgpu_crtc->cursor_width - 1) << 16 |
|
||||
(amdgpu_crtc->cursor_height - 1));
|
||||
|
||||
dce_v11_0_show_cursor(crtc);
|
||||
|
||||
dce_v11_0_lock_cursor(crtc, false);
|
||||
|
@ -460,9 +460,8 @@ static void dce_v6_0_resume_mc_access(struct amdgpu_device *adev,
|
||||
for (i = 0; i < adev->mode_info.num_crtc; i++) {
|
||||
if (save->crtc_enabled[i]) {
|
||||
tmp = RREG32(mmMASTER_UPDATE_MODE + crtc_offsets[i]);
|
||||
if ((tmp & 0x7) != 3) {
|
||||
if ((tmp & 0x7) != 0) {
|
||||
tmp &= ~0x7;
|
||||
tmp |= 0x3;
|
||||
WREG32(mmMASTER_UPDATE_MODE + crtc_offsets[i], tmp);
|
||||
}
|
||||
tmp = RREG32(mmGRPH_UPDATE + crtc_offsets[i]);
|
||||
@ -1860,7 +1859,8 @@ static int dce_v6_0_cursor_move_locked(struct drm_crtc *crtc,
|
||||
struct amdgpu_device *adev = crtc->dev->dev_private;
|
||||
int xorigin = 0, yorigin = 0;
|
||||
|
||||
int w = amdgpu_crtc->cursor_width;
|
||||
amdgpu_crtc->cursor_x = x;
|
||||
amdgpu_crtc->cursor_y = y;
|
||||
|
||||
/* avivo cursor are offset into the total surface */
|
||||
x += crtc->x;
|
||||
@ -1878,11 +1878,7 @@ static int dce_v6_0_cursor_move_locked(struct drm_crtc *crtc,
|
||||
|
||||
WREG32(mmCUR_POSITION + amdgpu_crtc->crtc_offset, (x << 16) | y);
|
||||
WREG32(mmCUR_HOT_SPOT + amdgpu_crtc->crtc_offset, (xorigin << 16) | yorigin);
|
||||
WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset,
|
||||
((w - 1) << 16) | (amdgpu_crtc->cursor_height - 1));
|
||||
|
||||
amdgpu_crtc->cursor_x = x;
|
||||
amdgpu_crtc->cursor_y = y;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1907,6 +1903,7 @@ static int dce_v6_0_crtc_cursor_set2(struct drm_crtc *crtc,
|
||||
int32_t hot_y)
|
||||
{
|
||||
struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
|
||||
struct amdgpu_device *adev = crtc->dev->dev_private;
|
||||
struct drm_gem_object *obj;
|
||||
struct amdgpu_bo *aobj;
|
||||
int ret;
|
||||
@ -1945,12 +1942,11 @@ static int dce_v6_0_crtc_cursor_set2(struct drm_crtc *crtc,
|
||||
return ret;
|
||||
}
|
||||
|
||||
amdgpu_crtc->cursor_width = width;
|
||||
amdgpu_crtc->cursor_height = height;
|
||||
|
||||
dce_v6_0_lock_cursor(crtc, true);
|
||||
|
||||
if (hot_x != amdgpu_crtc->cursor_hot_x ||
|
||||
if (width != amdgpu_crtc->cursor_width ||
|
||||
height != amdgpu_crtc->cursor_height ||
|
||||
hot_x != amdgpu_crtc->cursor_hot_x ||
|
||||
hot_y != amdgpu_crtc->cursor_hot_y) {
|
||||
int x, y;
|
||||
|
||||
@ -1959,10 +1955,20 @@ static int dce_v6_0_crtc_cursor_set2(struct drm_crtc *crtc,
|
||||
|
||||
dce_v6_0_cursor_move_locked(crtc, x, y);
|
||||
|
||||
amdgpu_crtc->cursor_width = width;
|
||||
amdgpu_crtc->cursor_height = height;
|
||||
amdgpu_crtc->cursor_hot_x = hot_x;
|
||||
amdgpu_crtc->cursor_hot_y = hot_y;
|
||||
}
|
||||
|
||||
if (width != amdgpu_crtc->cursor_width ||
|
||||
height != amdgpu_crtc->cursor_height) {
|
||||
WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset,
|
||||
(width - 1) << 16 | (height - 1));
|
||||
amdgpu_crtc->cursor_width = width;
|
||||
amdgpu_crtc->cursor_height = height;
|
||||
}
|
||||
|
||||
dce_v6_0_show_cursor(crtc);
|
||||
dce_v6_0_lock_cursor(crtc, false);
|
||||
|
||||
@ -1984,6 +1990,7 @@ unpin:
|
||||
static void dce_v6_0_cursor_reset(struct drm_crtc *crtc)
|
||||
{
|
||||
struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
|
||||
struct amdgpu_device *adev = crtc->dev->dev_private;
|
||||
|
||||
if (amdgpu_crtc->cursor_bo) {
|
||||
dce_v6_0_lock_cursor(crtc, true);
|
||||
@ -1991,6 +1998,10 @@ static void dce_v6_0_cursor_reset(struct drm_crtc *crtc)
|
||||
dce_v6_0_cursor_move_locked(crtc, amdgpu_crtc->cursor_x,
|
||||
amdgpu_crtc->cursor_y);
|
||||
|
||||
WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset,
|
||||
(amdgpu_crtc->cursor_width - 1) << 16 |
|
||||
(amdgpu_crtc->cursor_height - 1));
|
||||
|
||||
dce_v6_0_show_cursor(crtc);
|
||||
dce_v6_0_lock_cursor(crtc, false);
|
||||
}
|
||||
|
@ -2344,6 +2344,9 @@ static int dce_v8_0_cursor_move_locked(struct drm_crtc *crtc,
|
||||
struct amdgpu_device *adev = crtc->dev->dev_private;
|
||||
int xorigin = 0, yorigin = 0;
|
||||
|
||||
amdgpu_crtc->cursor_x = x;
|
||||
amdgpu_crtc->cursor_y = y;
|
||||
|
||||
/* avivo cursor are offset into the total surface */
|
||||
x += crtc->x;
|
||||
y += crtc->y;
|
||||
@ -2360,11 +2363,6 @@ static int dce_v8_0_cursor_move_locked(struct drm_crtc *crtc,
|
||||
|
||||
WREG32(mmCUR_POSITION + amdgpu_crtc->crtc_offset, (x << 16) | y);
|
||||
WREG32(mmCUR_HOT_SPOT + amdgpu_crtc->crtc_offset, (xorigin << 16) | yorigin);
|
||||
WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset,
|
||||
((amdgpu_crtc->cursor_width - 1) << 16) | (amdgpu_crtc->cursor_height - 1));
|
||||
|
||||
amdgpu_crtc->cursor_x = x;
|
||||
amdgpu_crtc->cursor_y = y;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2390,6 +2388,7 @@ static int dce_v8_0_crtc_cursor_set2(struct drm_crtc *crtc,
|
||||
int32_t hot_y)
|
||||
{
|
||||
struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
|
||||
struct amdgpu_device *adev = crtc->dev->dev_private;
|
||||
struct drm_gem_object *obj;
|
||||
struct amdgpu_bo *aobj;
|
||||
int ret;
|
||||
@ -2428,9 +2427,6 @@ static int dce_v8_0_crtc_cursor_set2(struct drm_crtc *crtc,
|
||||
return ret;
|
||||
}
|
||||
|
||||
amdgpu_crtc->cursor_width = width;
|
||||
amdgpu_crtc->cursor_height = height;
|
||||
|
||||
dce_v8_0_lock_cursor(crtc, true);
|
||||
|
||||
if (hot_x != amdgpu_crtc->cursor_hot_x ||
|
||||
@ -2442,10 +2438,20 @@ static int dce_v8_0_crtc_cursor_set2(struct drm_crtc *crtc,
|
||||
|
||||
dce_v8_0_cursor_move_locked(crtc, x, y);
|
||||
|
||||
amdgpu_crtc->cursor_width = width;
|
||||
amdgpu_crtc->cursor_height = height;
|
||||
amdgpu_crtc->cursor_hot_x = hot_x;
|
||||
amdgpu_crtc->cursor_hot_y = hot_y;
|
||||
}
|
||||
|
||||
if (width != amdgpu_crtc->cursor_width ||
|
||||
height != amdgpu_crtc->cursor_height) {
|
||||
WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset,
|
||||
(width - 1) << 16 | (height - 1));
|
||||
amdgpu_crtc->cursor_width = width;
|
||||
amdgpu_crtc->cursor_height = height;
|
||||
}
|
||||
|
||||
dce_v8_0_show_cursor(crtc);
|
||||
dce_v8_0_lock_cursor(crtc, false);
|
||||
|
||||
@ -2467,6 +2473,7 @@ unpin:
|
||||
static void dce_v8_0_cursor_reset(struct drm_crtc *crtc)
|
||||
{
|
||||
struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
|
||||
struct amdgpu_device *adev = crtc->dev->dev_private;
|
||||
|
||||
if (amdgpu_crtc->cursor_bo) {
|
||||
dce_v8_0_lock_cursor(crtc, true);
|
||||
@ -2474,6 +2481,10 @@ static void dce_v8_0_cursor_reset(struct drm_crtc *crtc)
|
||||
dce_v8_0_cursor_move_locked(crtc, amdgpu_crtc->cursor_x,
|
||||
amdgpu_crtc->cursor_y);
|
||||
|
||||
WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset,
|
||||
(amdgpu_crtc->cursor_width - 1) << 16 |
|
||||
(amdgpu_crtc->cursor_height - 1));
|
||||
|
||||
dce_v8_0_show_cursor(crtc);
|
||||
|
||||
dce_v8_0_lock_cursor(crtc, false);
|
||||
|
@ -2827,6 +2827,21 @@ static uint32_t wave_read_ind(struct amdgpu_device *adev, uint32_t simd, uint32_
|
||||
return RREG32(mmSQ_IND_DATA);
|
||||
}
|
||||
|
||||
static void wave_read_regs(struct amdgpu_device *adev, uint32_t simd,
|
||||
uint32_t wave, uint32_t thread,
|
||||
uint32_t regno, uint32_t num, uint32_t *out)
|
||||
{
|
||||
WREG32(mmSQ_IND_INDEX,
|
||||
(wave << SQ_IND_INDEX__WAVE_ID__SHIFT) |
|
||||
(simd << SQ_IND_INDEX__SIMD_ID__SHIFT) |
|
||||
(regno << SQ_IND_INDEX__INDEX__SHIFT) |
|
||||
(thread << SQ_IND_INDEX__THREAD_ID__SHIFT) |
|
||||
(SQ_IND_INDEX__FORCE_READ_MASK) |
|
||||
(SQ_IND_INDEX__AUTO_INCR_MASK));
|
||||
while (num--)
|
||||
*(out++) = RREG32(mmSQ_IND_DATA);
|
||||
}
|
||||
|
||||
static void gfx_v6_0_read_wave_data(struct amdgpu_device *adev, uint32_t simd, uint32_t wave, uint32_t *dst, int *no_fields)
|
||||
{
|
||||
/* type 0 wave data */
|
||||
@ -2851,10 +2866,20 @@ static void gfx_v6_0_read_wave_data(struct amdgpu_device *adev, uint32_t simd, u
|
||||
dst[(*no_fields)++] = wave_read_ind(adev, simd, wave, ixSQ_WAVE_M0);
|
||||
}
|
||||
|
||||
static void gfx_v6_0_read_wave_sgprs(struct amdgpu_device *adev, uint32_t simd,
|
||||
uint32_t wave, uint32_t start,
|
||||
uint32_t size, uint32_t *dst)
|
||||
{
|
||||
wave_read_regs(
|
||||
adev, simd, wave, 0,
|
||||
start + SQIND_WAVE_SGPRS_OFFSET, size, dst);
|
||||
}
|
||||
|
||||
static const struct amdgpu_gfx_funcs gfx_v6_0_gfx_funcs = {
|
||||
.get_gpu_clock_counter = &gfx_v6_0_get_gpu_clock_counter,
|
||||
.select_se_sh = &gfx_v6_0_select_se_sh,
|
||||
.read_wave_data = &gfx_v6_0_read_wave_data,
|
||||
.read_wave_sgprs = &gfx_v6_0_read_wave_sgprs,
|
||||
};
|
||||
|
||||
static int gfx_v6_0_early_init(void *handle)
|
||||
|
@ -4380,6 +4380,21 @@ static uint32_t wave_read_ind(struct amdgpu_device *adev, uint32_t simd, uint32_
|
||||
return RREG32(mmSQ_IND_DATA);
|
||||
}
|
||||
|
||||
static void wave_read_regs(struct amdgpu_device *adev, uint32_t simd,
|
||||
uint32_t wave, uint32_t thread,
|
||||
uint32_t regno, uint32_t num, uint32_t *out)
|
||||
{
|
||||
WREG32(mmSQ_IND_INDEX,
|
||||
(wave << SQ_IND_INDEX__WAVE_ID__SHIFT) |
|
||||
(simd << SQ_IND_INDEX__SIMD_ID__SHIFT) |
|
||||
(regno << SQ_IND_INDEX__INDEX__SHIFT) |
|
||||
(thread << SQ_IND_INDEX__THREAD_ID__SHIFT) |
|
||||
(SQ_IND_INDEX__FORCE_READ_MASK) |
|
||||
(SQ_IND_INDEX__AUTO_INCR_MASK));
|
||||
while (num--)
|
||||
*(out++) = RREG32(mmSQ_IND_DATA);
|
||||
}
|
||||
|
||||
static void gfx_v7_0_read_wave_data(struct amdgpu_device *adev, uint32_t simd, uint32_t wave, uint32_t *dst, int *no_fields)
|
||||
{
|
||||
/* type 0 wave data */
|
||||
@ -4404,10 +4419,20 @@ static void gfx_v7_0_read_wave_data(struct amdgpu_device *adev, uint32_t simd, u
|
||||
dst[(*no_fields)++] = wave_read_ind(adev, simd, wave, ixSQ_WAVE_M0);
|
||||
}
|
||||
|
||||
static void gfx_v7_0_read_wave_sgprs(struct amdgpu_device *adev, uint32_t simd,
|
||||
uint32_t wave, uint32_t start,
|
||||
uint32_t size, uint32_t *dst)
|
||||
{
|
||||
wave_read_regs(
|
||||
adev, simd, wave, 0,
|
||||
start + SQIND_WAVE_SGPRS_OFFSET, size, dst);
|
||||
}
|
||||
|
||||
static const struct amdgpu_gfx_funcs gfx_v7_0_gfx_funcs = {
|
||||
.get_gpu_clock_counter = &gfx_v7_0_get_gpu_clock_counter,
|
||||
.select_se_sh = &gfx_v7_0_select_se_sh,
|
||||
.read_wave_data = &gfx_v7_0_read_wave_data,
|
||||
.read_wave_sgprs = &gfx_v7_0_read_wave_sgprs,
|
||||
};
|
||||
|
||||
static const struct amdgpu_rlc_funcs gfx_v7_0_rlc_funcs = {
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "amdgpu.h"
|
||||
#include "amdgpu_gfx.h"
|
||||
#include "vi.h"
|
||||
#include "vi_structs.h"
|
||||
#include "vid.h"
|
||||
#include "amdgpu_ucode.h"
|
||||
#include "amdgpu_atombios.h"
|
||||
@ -167,6 +168,7 @@ static const u32 golden_settings_tonga_a11[] =
|
||||
mmPA_SC_ENHANCE, 0xffffffff, 0x20000001,
|
||||
mmPA_SC_FIFO_DEPTH_CNTL, 0x000003ff, 0x000000fc,
|
||||
mmPA_SC_LINE_STIPPLE_STATE, 0x0000ff0f, 0x00000000,
|
||||
mmRLC_CGCG_CGLS_CTRL, 0x00000003, 0x0000003c,
|
||||
mmSQ_RANDOM_WAVE_PRI, 0x001fffff, 0x000006fd,
|
||||
mmTA_CNTL_AUX, 0x000f000f, 0x000b0000,
|
||||
mmTCC_CTRL, 0x00100000, 0xf31fff7f,
|
||||
@ -1371,7 +1373,7 @@ static int gfx_v8_0_mec_init(struct amdgpu_device *adev)
|
||||
|
||||
if (adev->gfx.mec.hpd_eop_obj == NULL) {
|
||||
r = amdgpu_bo_create(adev,
|
||||
adev->gfx.mec.num_mec *adev->gfx.mec.num_pipe * MEC_HPD_SIZE * 2,
|
||||
adev->gfx.mec.num_queue * MEC_HPD_SIZE,
|
||||
PAGE_SIZE, true,
|
||||
AMDGPU_GEM_DOMAIN_GTT, 0, NULL, NULL,
|
||||
&adev->gfx.mec.hpd_eop_obj);
|
||||
@ -1400,7 +1402,7 @@ static int gfx_v8_0_mec_init(struct amdgpu_device *adev)
|
||||
return r;
|
||||
}
|
||||
|
||||
memset(hpd, 0, adev->gfx.mec.num_mec *adev->gfx.mec.num_pipe * MEC_HPD_SIZE * 2);
|
||||
memset(hpd, 0, adev->gfx.mec.num_queue * MEC_HPD_SIZE);
|
||||
|
||||
amdgpu_bo_kunmap(adev->gfx.mec.hpd_eop_obj);
|
||||
amdgpu_bo_unreserve(adev->gfx.mec.hpd_eop_obj);
|
||||
@ -4469,267 +4471,6 @@ static int gfx_v8_0_cp_compute_load_microcode(struct amdgpu_device *adev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct vi_mqd {
|
||||
uint32_t header; /* ordinal0 */
|
||||
uint32_t compute_dispatch_initiator; /* ordinal1 */
|
||||
uint32_t compute_dim_x; /* ordinal2 */
|
||||
uint32_t compute_dim_y; /* ordinal3 */
|
||||
uint32_t compute_dim_z; /* ordinal4 */
|
||||
uint32_t compute_start_x; /* ordinal5 */
|
||||
uint32_t compute_start_y; /* ordinal6 */
|
||||
uint32_t compute_start_z; /* ordinal7 */
|
||||
uint32_t compute_num_thread_x; /* ordinal8 */
|
||||
uint32_t compute_num_thread_y; /* ordinal9 */
|
||||
uint32_t compute_num_thread_z; /* ordinal10 */
|
||||
uint32_t compute_pipelinestat_enable; /* ordinal11 */
|
||||
uint32_t compute_perfcount_enable; /* ordinal12 */
|
||||
uint32_t compute_pgm_lo; /* ordinal13 */
|
||||
uint32_t compute_pgm_hi; /* ordinal14 */
|
||||
uint32_t compute_tba_lo; /* ordinal15 */
|
||||
uint32_t compute_tba_hi; /* ordinal16 */
|
||||
uint32_t compute_tma_lo; /* ordinal17 */
|
||||
uint32_t compute_tma_hi; /* ordinal18 */
|
||||
uint32_t compute_pgm_rsrc1; /* ordinal19 */
|
||||
uint32_t compute_pgm_rsrc2; /* ordinal20 */
|
||||
uint32_t compute_vmid; /* ordinal21 */
|
||||
uint32_t compute_resource_limits; /* ordinal22 */
|
||||
uint32_t compute_static_thread_mgmt_se0; /* ordinal23 */
|
||||
uint32_t compute_static_thread_mgmt_se1; /* ordinal24 */
|
||||
uint32_t compute_tmpring_size; /* ordinal25 */
|
||||
uint32_t compute_static_thread_mgmt_se2; /* ordinal26 */
|
||||
uint32_t compute_static_thread_mgmt_se3; /* ordinal27 */
|
||||
uint32_t compute_restart_x; /* ordinal28 */
|
||||
uint32_t compute_restart_y; /* ordinal29 */
|
||||
uint32_t compute_restart_z; /* ordinal30 */
|
||||
uint32_t compute_thread_trace_enable; /* ordinal31 */
|
||||
uint32_t compute_misc_reserved; /* ordinal32 */
|
||||
uint32_t compute_dispatch_id; /* ordinal33 */
|
||||
uint32_t compute_threadgroup_id; /* ordinal34 */
|
||||
uint32_t compute_relaunch; /* ordinal35 */
|
||||
uint32_t compute_wave_restore_addr_lo; /* ordinal36 */
|
||||
uint32_t compute_wave_restore_addr_hi; /* ordinal37 */
|
||||
uint32_t compute_wave_restore_control; /* ordinal38 */
|
||||
uint32_t reserved9; /* ordinal39 */
|
||||
uint32_t reserved10; /* ordinal40 */
|
||||
uint32_t reserved11; /* ordinal41 */
|
||||
uint32_t reserved12; /* ordinal42 */
|
||||
uint32_t reserved13; /* ordinal43 */
|
||||
uint32_t reserved14; /* ordinal44 */
|
||||
uint32_t reserved15; /* ordinal45 */
|
||||
uint32_t reserved16; /* ordinal46 */
|
||||
uint32_t reserved17; /* ordinal47 */
|
||||
uint32_t reserved18; /* ordinal48 */
|
||||
uint32_t reserved19; /* ordinal49 */
|
||||
uint32_t reserved20; /* ordinal50 */
|
||||
uint32_t reserved21; /* ordinal51 */
|
||||
uint32_t reserved22; /* ordinal52 */
|
||||
uint32_t reserved23; /* ordinal53 */
|
||||
uint32_t reserved24; /* ordinal54 */
|
||||
uint32_t reserved25; /* ordinal55 */
|
||||
uint32_t reserved26; /* ordinal56 */
|
||||
uint32_t reserved27; /* ordinal57 */
|
||||
uint32_t reserved28; /* ordinal58 */
|
||||
uint32_t reserved29; /* ordinal59 */
|
||||
uint32_t reserved30; /* ordinal60 */
|
||||
uint32_t reserved31; /* ordinal61 */
|
||||
uint32_t reserved32; /* ordinal62 */
|
||||
uint32_t reserved33; /* ordinal63 */
|
||||
uint32_t reserved34; /* ordinal64 */
|
||||
uint32_t compute_user_data_0; /* ordinal65 */
|
||||
uint32_t compute_user_data_1; /* ordinal66 */
|
||||
uint32_t compute_user_data_2; /* ordinal67 */
|
||||
uint32_t compute_user_data_3; /* ordinal68 */
|
||||
uint32_t compute_user_data_4; /* ordinal69 */
|
||||
uint32_t compute_user_data_5; /* ordinal70 */
|
||||
uint32_t compute_user_data_6; /* ordinal71 */
|
||||
uint32_t compute_user_data_7; /* ordinal72 */
|
||||
uint32_t compute_user_data_8; /* ordinal73 */
|
||||
uint32_t compute_user_data_9; /* ordinal74 */
|
||||
uint32_t compute_user_data_10; /* ordinal75 */
|
||||
uint32_t compute_user_data_11; /* ordinal76 */
|
||||
uint32_t compute_user_data_12; /* ordinal77 */
|
||||
uint32_t compute_user_data_13; /* ordinal78 */
|
||||
uint32_t compute_user_data_14; /* ordinal79 */
|
||||
uint32_t compute_user_data_15; /* ordinal80 */
|
||||
uint32_t cp_compute_csinvoc_count_lo; /* ordinal81 */
|
||||
uint32_t cp_compute_csinvoc_count_hi; /* ordinal82 */
|
||||
uint32_t reserved35; /* ordinal83 */
|
||||
uint32_t reserved36; /* ordinal84 */
|
||||
uint32_t reserved37; /* ordinal85 */
|
||||
uint32_t cp_mqd_query_time_lo; /* ordinal86 */
|
||||
uint32_t cp_mqd_query_time_hi; /* ordinal87 */
|
||||
uint32_t cp_mqd_connect_start_time_lo; /* ordinal88 */
|
||||
uint32_t cp_mqd_connect_start_time_hi; /* ordinal89 */
|
||||
uint32_t cp_mqd_connect_end_time_lo; /* ordinal90 */
|
||||
uint32_t cp_mqd_connect_end_time_hi; /* ordinal91 */
|
||||
uint32_t cp_mqd_connect_end_wf_count; /* ordinal92 */
|
||||
uint32_t cp_mqd_connect_end_pq_rptr; /* ordinal93 */
|
||||
uint32_t cp_mqd_connect_end_pq_wptr; /* ordinal94 */
|
||||
uint32_t cp_mqd_connect_end_ib_rptr; /* ordinal95 */
|
||||
uint32_t reserved38; /* ordinal96 */
|
||||
uint32_t reserved39; /* ordinal97 */
|
||||
uint32_t cp_mqd_save_start_time_lo; /* ordinal98 */
|
||||
uint32_t cp_mqd_save_start_time_hi; /* ordinal99 */
|
||||
uint32_t cp_mqd_save_end_time_lo; /* ordinal100 */
|
||||
uint32_t cp_mqd_save_end_time_hi; /* ordinal101 */
|
||||
uint32_t cp_mqd_restore_start_time_lo; /* ordinal102 */
|
||||
uint32_t cp_mqd_restore_start_time_hi; /* ordinal103 */
|
||||
uint32_t cp_mqd_restore_end_time_lo; /* ordinal104 */
|
||||
uint32_t cp_mqd_restore_end_time_hi; /* ordinal105 */
|
||||
uint32_t reserved40; /* ordinal106 */
|
||||
uint32_t reserved41; /* ordinal107 */
|
||||
uint32_t gds_cs_ctxsw_cnt0; /* ordinal108 */
|
||||
uint32_t gds_cs_ctxsw_cnt1; /* ordinal109 */
|
||||
uint32_t gds_cs_ctxsw_cnt2; /* ordinal110 */
|
||||
uint32_t gds_cs_ctxsw_cnt3; /* ordinal111 */
|
||||
uint32_t reserved42; /* ordinal112 */
|
||||
uint32_t reserved43; /* ordinal113 */
|
||||
uint32_t cp_pq_exe_status_lo; /* ordinal114 */
|
||||
uint32_t cp_pq_exe_status_hi; /* ordinal115 */
|
||||
uint32_t cp_packet_id_lo; /* ordinal116 */
|
||||
uint32_t cp_packet_id_hi; /* ordinal117 */
|
||||
uint32_t cp_packet_exe_status_lo; /* ordinal118 */
|
||||
uint32_t cp_packet_exe_status_hi; /* ordinal119 */
|
||||
uint32_t gds_save_base_addr_lo; /* ordinal120 */
|
||||
uint32_t gds_save_base_addr_hi; /* ordinal121 */
|
||||
uint32_t gds_save_mask_lo; /* ordinal122 */
|
||||
uint32_t gds_save_mask_hi; /* ordinal123 */
|
||||
uint32_t ctx_save_base_addr_lo; /* ordinal124 */
|
||||
uint32_t ctx_save_base_addr_hi; /* ordinal125 */
|
||||
uint32_t reserved44; /* ordinal126 */
|
||||
uint32_t reserved45; /* ordinal127 */
|
||||
uint32_t cp_mqd_base_addr_lo; /* ordinal128 */
|
||||
uint32_t cp_mqd_base_addr_hi; /* ordinal129 */
|
||||
uint32_t cp_hqd_active; /* ordinal130 */
|
||||
uint32_t cp_hqd_vmid; /* ordinal131 */
|
||||
uint32_t cp_hqd_persistent_state; /* ordinal132 */
|
||||
uint32_t cp_hqd_pipe_priority; /* ordinal133 */
|
||||
uint32_t cp_hqd_queue_priority; /* ordinal134 */
|
||||
uint32_t cp_hqd_quantum; /* ordinal135 */
|
||||
uint32_t cp_hqd_pq_base_lo; /* ordinal136 */
|
||||
uint32_t cp_hqd_pq_base_hi; /* ordinal137 */
|
||||
uint32_t cp_hqd_pq_rptr; /* ordinal138 */
|
||||
uint32_t cp_hqd_pq_rptr_report_addr_lo; /* ordinal139 */
|
||||
uint32_t cp_hqd_pq_rptr_report_addr_hi; /* ordinal140 */
|
||||
uint32_t cp_hqd_pq_wptr_poll_addr; /* ordinal141 */
|
||||
uint32_t cp_hqd_pq_wptr_poll_addr_hi; /* ordinal142 */
|
||||
uint32_t cp_hqd_pq_doorbell_control; /* ordinal143 */
|
||||
uint32_t cp_hqd_pq_wptr; /* ordinal144 */
|
||||
uint32_t cp_hqd_pq_control; /* ordinal145 */
|
||||
uint32_t cp_hqd_ib_base_addr_lo; /* ordinal146 */
|
||||
uint32_t cp_hqd_ib_base_addr_hi; /* ordinal147 */
|
||||
uint32_t cp_hqd_ib_rptr; /* ordinal148 */
|
||||
uint32_t cp_hqd_ib_control; /* ordinal149 */
|
||||
uint32_t cp_hqd_iq_timer; /* ordinal150 */
|
||||
uint32_t cp_hqd_iq_rptr; /* ordinal151 */
|
||||
uint32_t cp_hqd_dequeue_request; /* ordinal152 */
|
||||
uint32_t cp_hqd_dma_offload; /* ordinal153 */
|
||||
uint32_t cp_hqd_sema_cmd; /* ordinal154 */
|
||||
uint32_t cp_hqd_msg_type; /* ordinal155 */
|
||||
uint32_t cp_hqd_atomic0_preop_lo; /* ordinal156 */
|
||||
uint32_t cp_hqd_atomic0_preop_hi; /* ordinal157 */
|
||||
uint32_t cp_hqd_atomic1_preop_lo; /* ordinal158 */
|
||||
uint32_t cp_hqd_atomic1_preop_hi; /* ordinal159 */
|
||||
uint32_t cp_hqd_hq_status0; /* ordinal160 */
|
||||
uint32_t cp_hqd_hq_control0; /* ordinal161 */
|
||||
uint32_t cp_mqd_control; /* ordinal162 */
|
||||
uint32_t cp_hqd_hq_status1; /* ordinal163 */
|
||||
uint32_t cp_hqd_hq_control1; /* ordinal164 */
|
||||
uint32_t cp_hqd_eop_base_addr_lo; /* ordinal165 */
|
||||
uint32_t cp_hqd_eop_base_addr_hi; /* ordinal166 */
|
||||
uint32_t cp_hqd_eop_control; /* ordinal167 */
|
||||
uint32_t cp_hqd_eop_rptr; /* ordinal168 */
|
||||
uint32_t cp_hqd_eop_wptr; /* ordinal169 */
|
||||
uint32_t cp_hqd_eop_done_events; /* ordinal170 */
|
||||
uint32_t cp_hqd_ctx_save_base_addr_lo; /* ordinal171 */
|
||||
uint32_t cp_hqd_ctx_save_base_addr_hi; /* ordinal172 */
|
||||
uint32_t cp_hqd_ctx_save_control; /* ordinal173 */
|
||||
uint32_t cp_hqd_cntl_stack_offset; /* ordinal174 */
|
||||
uint32_t cp_hqd_cntl_stack_size; /* ordinal175 */
|
||||
uint32_t cp_hqd_wg_state_offset; /* ordinal176 */
|
||||
uint32_t cp_hqd_ctx_save_size; /* ordinal177 */
|
||||
uint32_t cp_hqd_gds_resource_state; /* ordinal178 */
|
||||
uint32_t cp_hqd_error; /* ordinal179 */
|
||||
uint32_t cp_hqd_eop_wptr_mem; /* ordinal180 */
|
||||
uint32_t cp_hqd_eop_dones; /* ordinal181 */
|
||||
uint32_t reserved46; /* ordinal182 */
|
||||
uint32_t reserved47; /* ordinal183 */
|
||||
uint32_t reserved48; /* ordinal184 */
|
||||
uint32_t reserved49; /* ordinal185 */
|
||||
uint32_t reserved50; /* ordinal186 */
|
||||
uint32_t reserved51; /* ordinal187 */
|
||||
uint32_t reserved52; /* ordinal188 */
|
||||
uint32_t reserved53; /* ordinal189 */
|
||||
uint32_t reserved54; /* ordinal190 */
|
||||
uint32_t reserved55; /* ordinal191 */
|
||||
uint32_t iqtimer_pkt_header; /* ordinal192 */
|
||||
uint32_t iqtimer_pkt_dw0; /* ordinal193 */
|
||||
uint32_t iqtimer_pkt_dw1; /* ordinal194 */
|
||||
uint32_t iqtimer_pkt_dw2; /* ordinal195 */
|
||||
uint32_t iqtimer_pkt_dw3; /* ordinal196 */
|
||||
uint32_t iqtimer_pkt_dw4; /* ordinal197 */
|
||||
uint32_t iqtimer_pkt_dw5; /* ordinal198 */
|
||||
uint32_t iqtimer_pkt_dw6; /* ordinal199 */
|
||||
uint32_t iqtimer_pkt_dw7; /* ordinal200 */
|
||||
uint32_t iqtimer_pkt_dw8; /* ordinal201 */
|
||||
uint32_t iqtimer_pkt_dw9; /* ordinal202 */
|
||||
uint32_t iqtimer_pkt_dw10; /* ordinal203 */
|
||||
uint32_t iqtimer_pkt_dw11; /* ordinal204 */
|
||||
uint32_t iqtimer_pkt_dw12; /* ordinal205 */
|
||||
uint32_t iqtimer_pkt_dw13; /* ordinal206 */
|
||||
uint32_t iqtimer_pkt_dw14; /* ordinal207 */
|
||||
uint32_t iqtimer_pkt_dw15; /* ordinal208 */
|
||||
uint32_t iqtimer_pkt_dw16; /* ordinal209 */
|
||||
uint32_t iqtimer_pkt_dw17; /* ordinal210 */
|
||||
uint32_t iqtimer_pkt_dw18; /* ordinal211 */
|
||||
uint32_t iqtimer_pkt_dw19; /* ordinal212 */
|
||||
uint32_t iqtimer_pkt_dw20; /* ordinal213 */
|
||||
uint32_t iqtimer_pkt_dw21; /* ordinal214 */
|
||||
uint32_t iqtimer_pkt_dw22; /* ordinal215 */
|
||||
uint32_t iqtimer_pkt_dw23; /* ordinal216 */
|
||||
uint32_t iqtimer_pkt_dw24; /* ordinal217 */
|
||||
uint32_t iqtimer_pkt_dw25; /* ordinal218 */
|
||||
uint32_t iqtimer_pkt_dw26; /* ordinal219 */
|
||||
uint32_t iqtimer_pkt_dw27; /* ordinal220 */
|
||||
uint32_t iqtimer_pkt_dw28; /* ordinal221 */
|
||||
uint32_t iqtimer_pkt_dw29; /* ordinal222 */
|
||||
uint32_t iqtimer_pkt_dw30; /* ordinal223 */
|
||||
uint32_t iqtimer_pkt_dw31; /* ordinal224 */
|
||||
uint32_t reserved56; /* ordinal225 */
|
||||
uint32_t reserved57; /* ordinal226 */
|
||||
uint32_t reserved58; /* ordinal227 */
|
||||
uint32_t set_resources_header; /* ordinal228 */
|
||||
uint32_t set_resources_dw1; /* ordinal229 */
|
||||
uint32_t set_resources_dw2; /* ordinal230 */
|
||||
uint32_t set_resources_dw3; /* ordinal231 */
|
||||
uint32_t set_resources_dw4; /* ordinal232 */
|
||||
uint32_t set_resources_dw5; /* ordinal233 */
|
||||
uint32_t set_resources_dw6; /* ordinal234 */
|
||||
uint32_t set_resources_dw7; /* ordinal235 */
|
||||
uint32_t reserved59; /* ordinal236 */
|
||||
uint32_t reserved60; /* ordinal237 */
|
||||
uint32_t reserved61; /* ordinal238 */
|
||||
uint32_t reserved62; /* ordinal239 */
|
||||
uint32_t reserved63; /* ordinal240 */
|
||||
uint32_t reserved64; /* ordinal241 */
|
||||
uint32_t reserved65; /* ordinal242 */
|
||||
uint32_t reserved66; /* ordinal243 */
|
||||
uint32_t reserved67; /* ordinal244 */
|
||||
uint32_t reserved68; /* ordinal245 */
|
||||
uint32_t reserved69; /* ordinal246 */
|
||||
uint32_t reserved70; /* ordinal247 */
|
||||
uint32_t reserved71; /* ordinal248 */
|
||||
uint32_t reserved72; /* ordinal249 */
|
||||
uint32_t reserved73; /* ordinal250 */
|
||||
uint32_t reserved74; /* ordinal251 */
|
||||
uint32_t reserved75; /* ordinal252 */
|
||||
uint32_t reserved76; /* ordinal253 */
|
||||
uint32_t reserved77; /* ordinal254 */
|
||||
uint32_t reserved78; /* ordinal255 */
|
||||
|
||||
uint32_t reserved_t[256]; /* Reserve 256 dword buffer used by ucode */
|
||||
};
|
||||
|
||||
static void gfx_v8_0_cp_compute_fini(struct amdgpu_device *adev)
|
||||
{
|
||||
int i, r;
|
||||
@ -4763,34 +4504,7 @@ static int gfx_v8_0_cp_compute_resume(struct amdgpu_device *adev)
|
||||
u32 *buf;
|
||||
struct vi_mqd *mqd;
|
||||
|
||||
/* init the pipes */
|
||||
mutex_lock(&adev->srbm_mutex);
|
||||
for (i = 0; i < (adev->gfx.mec.num_pipe * adev->gfx.mec.num_mec); i++) {
|
||||
int me = (i < 4) ? 1 : 2;
|
||||
int pipe = (i < 4) ? i : (i - 4);
|
||||
|
||||
eop_gpu_addr = adev->gfx.mec.hpd_eop_gpu_addr + (i * MEC_HPD_SIZE);
|
||||
eop_gpu_addr >>= 8;
|
||||
|
||||
vi_srbm_select(adev, me, pipe, 0, 0);
|
||||
|
||||
/* write the EOP addr */
|
||||
WREG32(mmCP_HQD_EOP_BASE_ADDR, eop_gpu_addr);
|
||||
WREG32(mmCP_HQD_EOP_BASE_ADDR_HI, upper_32_bits(eop_gpu_addr));
|
||||
|
||||
/* set the VMID assigned */
|
||||
WREG32(mmCP_HQD_VMID, 0);
|
||||
|
||||
/* set the EOP size, register value is 2^(EOP_SIZE+1) dwords */
|
||||
tmp = RREG32(mmCP_HQD_EOP_CONTROL);
|
||||
tmp = REG_SET_FIELD(tmp, CP_HQD_EOP_CONTROL, EOP_SIZE,
|
||||
(order_base_2(MEC_HPD_SIZE / 4) - 1));
|
||||
WREG32(mmCP_HQD_EOP_CONTROL, tmp);
|
||||
}
|
||||
vi_srbm_select(adev, 0, 0, 0, 0);
|
||||
mutex_unlock(&adev->srbm_mutex);
|
||||
|
||||
/* init the queues. Just two for now. */
|
||||
/* init the queues. */
|
||||
for (i = 0; i < adev->gfx.num_compute_rings; i++) {
|
||||
struct amdgpu_ring *ring = &adev->gfx.compute_ring[i];
|
||||
|
||||
@ -4842,6 +4556,22 @@ static int gfx_v8_0_cp_compute_resume(struct amdgpu_device *adev)
|
||||
ring->pipe,
|
||||
ring->queue, 0);
|
||||
|
||||
eop_gpu_addr = adev->gfx.mec.hpd_eop_gpu_addr + (i * MEC_HPD_SIZE);
|
||||
eop_gpu_addr >>= 8;
|
||||
|
||||
/* write the EOP addr */
|
||||
WREG32(mmCP_HQD_EOP_BASE_ADDR, eop_gpu_addr);
|
||||
WREG32(mmCP_HQD_EOP_BASE_ADDR_HI, upper_32_bits(eop_gpu_addr));
|
||||
|
||||
/* set the VMID assigned */
|
||||
WREG32(mmCP_HQD_VMID, 0);
|
||||
|
||||
/* set the EOP size, register value is 2^(EOP_SIZE+1) dwords */
|
||||
tmp = RREG32(mmCP_HQD_EOP_CONTROL);
|
||||
tmp = REG_SET_FIELD(tmp, CP_HQD_EOP_CONTROL, EOP_SIZE,
|
||||
(order_base_2(MEC_HPD_SIZE / 4) - 1));
|
||||
WREG32(mmCP_HQD_EOP_CONTROL, tmp);
|
||||
|
||||
/* disable wptr polling */
|
||||
tmp = RREG32(mmCP_PQ_WPTR_POLL_CNTL);
|
||||
tmp = REG_SET_FIELD(tmp, CP_PQ_WPTR_POLL_CNTL, EN, 0);
|
||||
@ -4925,9 +4655,9 @@ static int gfx_v8_0_cp_compute_resume(struct amdgpu_device *adev)
|
||||
|
||||
/* only used if CP_PQ_WPTR_POLL_CNTL.CP_PQ_WPTR_POLL_CNTL__EN_MASK=1 */
|
||||
wb_gpu_addr = adev->wb.gpu_addr + (ring->wptr_offs * 4);
|
||||
mqd->cp_hqd_pq_wptr_poll_addr = wb_gpu_addr & 0xfffffffc;
|
||||
mqd->cp_hqd_pq_wptr_poll_addr_lo = wb_gpu_addr & 0xfffffffc;
|
||||
mqd->cp_hqd_pq_wptr_poll_addr_hi = upper_32_bits(wb_gpu_addr) & 0xffff;
|
||||
WREG32(mmCP_HQD_PQ_WPTR_POLL_ADDR, mqd->cp_hqd_pq_wptr_poll_addr);
|
||||
WREG32(mmCP_HQD_PQ_WPTR_POLL_ADDR, mqd->cp_hqd_pq_wptr_poll_addr_lo);
|
||||
WREG32(mmCP_HQD_PQ_WPTR_POLL_ADDR_HI,
|
||||
mqd->cp_hqd_pq_wptr_poll_addr_hi);
|
||||
|
||||
@ -5098,6 +4828,10 @@ static int gfx_v8_0_hw_fini(void *handle)
|
||||
|
||||
amdgpu_irq_put(adev, &adev->gfx.priv_reg_irq, 0);
|
||||
amdgpu_irq_put(adev, &adev->gfx.priv_inst_irq, 0);
|
||||
if (amdgpu_sriov_vf(adev)) {
|
||||
pr_debug("For SRIOV client, shouldn't do anything.\n");
|
||||
return 0;
|
||||
}
|
||||
gfx_v8_0_cp_enable(adev, false);
|
||||
gfx_v8_0_rlc_stop(adev);
|
||||
gfx_v8_0_cp_compute_fini(adev);
|
||||
@ -5450,6 +5184,21 @@ static uint32_t wave_read_ind(struct amdgpu_device *adev, uint32_t simd, uint32_
|
||||
return RREG32(mmSQ_IND_DATA);
|
||||
}
|
||||
|
||||
static void wave_read_regs(struct amdgpu_device *adev, uint32_t simd,
|
||||
uint32_t wave, uint32_t thread,
|
||||
uint32_t regno, uint32_t num, uint32_t *out)
|
||||
{
|
||||
WREG32(mmSQ_IND_INDEX,
|
||||
(wave << SQ_IND_INDEX__WAVE_ID__SHIFT) |
|
||||
(simd << SQ_IND_INDEX__SIMD_ID__SHIFT) |
|
||||
(regno << SQ_IND_INDEX__INDEX__SHIFT) |
|
||||
(thread << SQ_IND_INDEX__THREAD_ID__SHIFT) |
|
||||
(SQ_IND_INDEX__FORCE_READ_MASK) |
|
||||
(SQ_IND_INDEX__AUTO_INCR_MASK));
|
||||
while (num--)
|
||||
*(out++) = RREG32(mmSQ_IND_DATA);
|
||||
}
|
||||
|
||||
static void gfx_v8_0_read_wave_data(struct amdgpu_device *adev, uint32_t simd, uint32_t wave, uint32_t *dst, int *no_fields)
|
||||
{
|
||||
/* type 0 wave data */
|
||||
@ -5474,11 +5223,21 @@ static void gfx_v8_0_read_wave_data(struct amdgpu_device *adev, uint32_t simd, u
|
||||
dst[(*no_fields)++] = wave_read_ind(adev, simd, wave, ixSQ_WAVE_M0);
|
||||
}
|
||||
|
||||
static void gfx_v8_0_read_wave_sgprs(struct amdgpu_device *adev, uint32_t simd,
|
||||
uint32_t wave, uint32_t start,
|
||||
uint32_t size, uint32_t *dst)
|
||||
{
|
||||
wave_read_regs(
|
||||
adev, simd, wave, 0,
|
||||
start + SQIND_WAVE_SGPRS_OFFSET, size, dst);
|
||||
}
|
||||
|
||||
|
||||
static const struct amdgpu_gfx_funcs gfx_v8_0_gfx_funcs = {
|
||||
.get_gpu_clock_counter = &gfx_v8_0_get_gpu_clock_counter,
|
||||
.select_se_sh = &gfx_v8_0_select_se_sh,
|
||||
.read_wave_data = &gfx_v8_0_read_wave_data,
|
||||
.read_wave_sgprs = &gfx_v8_0_read_wave_sgprs,
|
||||
};
|
||||
|
||||
static int gfx_v8_0_early_init(void *handle)
|
||||
@ -5930,29 +5689,24 @@ static void gfx_v8_0_update_coarse_grain_clock_gating(struct amdgpu_device *adev
|
||||
adev->gfx.rlc.funcs->enter_safe_mode(adev);
|
||||
|
||||
if (enable && (adev->cg_flags & AMD_CG_SUPPORT_GFX_CGCG)) {
|
||||
/* 1 enable cntx_empty_int_enable/cntx_busy_int_enable/
|
||||
* Cmp_busy/GFX_Idle interrupts
|
||||
*/
|
||||
gfx_v8_0_enable_gui_idle_interrupt(adev, true);
|
||||
|
||||
temp1 = data1 = RREG32(mmRLC_CGTT_MGCG_OVERRIDE);
|
||||
data1 &= ~RLC_CGTT_MGCG_OVERRIDE__CGCG_MASK;
|
||||
if (temp1 != data1)
|
||||
WREG32(mmRLC_CGTT_MGCG_OVERRIDE, data1);
|
||||
|
||||
/* 2 wait for RLC_SERDES_CU_MASTER & RLC_SERDES_NONCU_MASTER idle */
|
||||
/* : wait for RLC_SERDES_CU_MASTER & RLC_SERDES_NONCU_MASTER idle */
|
||||
gfx_v8_0_wait_for_rlc_serdes(adev);
|
||||
|
||||
/* 3 - clear cgcg override */
|
||||
/* 2 - clear cgcg override */
|
||||
gfx_v8_0_send_serdes_cmd(adev, BPM_REG_CGCG_OVERRIDE, CLE_BPM_SERDES_CMD);
|
||||
|
||||
/* wait for RLC_SERDES_CU_MASTER & RLC_SERDES_NONCU_MASTER idle */
|
||||
gfx_v8_0_wait_for_rlc_serdes(adev);
|
||||
|
||||
/* 4 - write cmd to set CGLS */
|
||||
/* 3 - write cmd to set CGLS */
|
||||
gfx_v8_0_send_serdes_cmd(adev, BPM_REG_CGLS_EN, SET_BPM_SERDES_CMD);
|
||||
|
||||
/* 5 - enable cgcg */
|
||||
/* 4 - enable cgcg */
|
||||
data |= RLC_CGCG_CGLS_CTRL__CGCG_EN_MASK;
|
||||
|
||||
if (adev->cg_flags & AMD_CG_SUPPORT_GFX_CGLS) {
|
||||
@ -5970,6 +5724,11 @@ static void gfx_v8_0_update_coarse_grain_clock_gating(struct amdgpu_device *adev
|
||||
|
||||
if (temp != data)
|
||||
WREG32(mmRLC_CGCG_CGLS_CTRL, data);
|
||||
|
||||
/* 5 enable cntx_empty_int_enable/cntx_busy_int_enable/
|
||||
* Cmp_busy/GFX_Idle interrupts
|
||||
*/
|
||||
gfx_v8_0_enable_gui_idle_interrupt(adev, true);
|
||||
} else {
|
||||
/* disable cntx_empty_int_enable & GFX Idle interrupt */
|
||||
gfx_v8_0_enable_gui_idle_interrupt(adev, false);
|
||||
|
@ -335,7 +335,7 @@ static int gmc_v6_0_mc_init(struct amdgpu_device *adev)
|
||||
* size equal to the 1024 or vram, whichever is larger.
|
||||
*/
|
||||
if (amdgpu_gart_size == -1)
|
||||
adev->mc.gtt_size = amdgpu_ttm_get_gtt_mem_size(adev);
|
||||
adev->mc.gtt_size = max((1024ULL << 20), adev->mc.mc_vram_size);
|
||||
else
|
||||
adev->mc.gtt_size = (uint64_t)amdgpu_gart_size << 20;
|
||||
|
||||
@ -795,11 +795,6 @@ static int gmc_v6_0_sw_init(void *handle)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = amdgpu_ttm_global_init(adev);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = gmc_v6_0_mc_init(adev);
|
||||
if (r)
|
||||
return r;
|
||||
|
@ -385,7 +385,7 @@ static int gmc_v7_0_mc_init(struct amdgpu_device *adev)
|
||||
* size equal to the 1024 or vram, whichever is larger.
|
||||
*/
|
||||
if (amdgpu_gart_size == -1)
|
||||
adev->mc.gtt_size = amdgpu_ttm_get_gtt_mem_size(adev);
|
||||
adev->mc.gtt_size = max((1024ULL << 20), adev->mc.mc_vram_size);
|
||||
else
|
||||
adev->mc.gtt_size = (uint64_t)amdgpu_gart_size << 20;
|
||||
|
||||
@ -945,11 +945,6 @@ static int gmc_v7_0_sw_init(void *handle)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = amdgpu_ttm_global_init(adev);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = gmc_v7_0_mc_init(adev);
|
||||
if (r)
|
||||
return r;
|
||||
|
@ -472,7 +472,7 @@ static int gmc_v8_0_mc_init(struct amdgpu_device *adev)
|
||||
* size equal to the 1024 or vram, whichever is larger.
|
||||
*/
|
||||
if (amdgpu_gart_size == -1)
|
||||
adev->mc.gtt_size = amdgpu_ttm_get_gtt_mem_size(adev);
|
||||
adev->mc.gtt_size = max((1024ULL << 20), adev->mc.mc_vram_size);
|
||||
else
|
||||
adev->mc.gtt_size = (uint64_t)amdgpu_gart_size << 20;
|
||||
|
||||
@ -952,11 +952,6 @@ static int gmc_v8_0_sw_init(void *handle)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = amdgpu_ttm_global_init(adev);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = gmc_v8_0_mc_init(adev);
|
||||
if (r)
|
||||
return r;
|
||||
|
@ -3506,6 +3506,7 @@ static void si_apply_state_adjust_rules(struct amdgpu_device *adev,
|
||||
(adev->pdev->revision == 0x80) ||
|
||||
(adev->pdev->revision == 0x81) ||
|
||||
(adev->pdev->revision == 0x83) ||
|
||||
(adev->pdev->revision == 0x87) ||
|
||||
(adev->pdev->device == 0x6604) ||
|
||||
(adev->pdev->device == 0x6605)) {
|
||||
max_sclk = 75000;
|
||||
@ -7715,6 +7716,7 @@ static int si_dpm_init_microcode(struct amdgpu_device *adev)
|
||||
(adev->pdev->revision == 0x80) ||
|
||||
(adev->pdev->revision == 0x81) ||
|
||||
(adev->pdev->revision == 0x83) ||
|
||||
(adev->pdev->revision == 0x87) ||
|
||||
(adev->pdev->device == 0x6604) ||
|
||||
(adev->pdev->device == 0x6605))
|
||||
chip_name = "oland_k";
|
||||
|
@ -640,7 +640,7 @@ static void uvd_v5_0_enable_clock_gating(struct amdgpu_device *adev, bool enable
|
||||
UVD_SUVD_CGC_GATE__SDB_MASK;
|
||||
|
||||
if (enable) {
|
||||
data3 |= (UVD_CGC_GATE__SYS_MASK |
|
||||
data3 |= (UVD_CGC_GATE__SYS_MASK |
|
||||
UVD_CGC_GATE__UDEC_MASK |
|
||||
UVD_CGC_GATE__MPEG2_MASK |
|
||||
UVD_CGC_GATE__RBC_MASK |
|
||||
@ -656,9 +656,11 @@ static void uvd_v5_0_enable_clock_gating(struct amdgpu_device *adev, bool enable
|
||||
UVD_CGC_GATE__UDEC_DB_MASK |
|
||||
UVD_CGC_GATE__UDEC_MP_MASK |
|
||||
UVD_CGC_GATE__WCB_MASK |
|
||||
UVD_CGC_GATE__VCPU_MASK |
|
||||
UVD_CGC_GATE__JPEG_MASK |
|
||||
UVD_CGC_GATE__SCPU_MASK);
|
||||
/* only in pg enabled, we can gate clock to vcpu*/
|
||||
if (adev->pg_flags & AMD_PG_SUPPORT_UVD)
|
||||
data3 |= UVD_CGC_GATE__VCPU_MASK;
|
||||
data3 &= ~UVD_CGC_GATE__REGS_MASK;
|
||||
data1 |= suvd_flags;
|
||||
} else {
|
||||
|
@ -42,6 +42,10 @@ static void uvd_v6_0_set_irq_funcs(struct amdgpu_device *adev);
|
||||
static int uvd_v6_0_start(struct amdgpu_device *adev);
|
||||
static void uvd_v6_0_stop(struct amdgpu_device *adev);
|
||||
static void uvd_v6_0_set_sw_clock_gating(struct amdgpu_device *adev);
|
||||
static int uvd_v6_0_set_clockgating_state(void *handle,
|
||||
enum amd_clockgating_state state);
|
||||
static void uvd_v6_0_enable_mgcg(struct amdgpu_device *adev,
|
||||
bool enable);
|
||||
|
||||
/**
|
||||
* uvd_v6_0_ring_get_rptr - get read pointer
|
||||
@ -151,8 +155,6 @@ static int uvd_v6_0_hw_init(void *handle)
|
||||
uint32_t tmp;
|
||||
int r;
|
||||
|
||||
amdgpu_asic_set_uvd_clocks(adev, 10000, 10000);
|
||||
|
||||
r = uvd_v6_0_start(adev);
|
||||
if (r)
|
||||
goto done;
|
||||
@ -395,11 +397,11 @@ static int uvd_v6_0_start(struct amdgpu_device *adev)
|
||||
lmi_swap_cntl = 0;
|
||||
mp_swap_cntl = 0;
|
||||
|
||||
amdgpu_asic_set_uvd_clocks(adev, 10000, 10000);
|
||||
uvd_v6_0_set_clockgating_state(adev, AMD_CG_STATE_UNGATE);
|
||||
uvd_v6_0_enable_mgcg(adev, true);
|
||||
uvd_v6_0_mc_resume(adev);
|
||||
|
||||
/* disable clock gating */
|
||||
WREG32_FIELD(UVD_CGC_CTRL, DYN_CLOCK_MODE, 0);
|
||||
|
||||
/* disable interupt */
|
||||
WREG32_FIELD(UVD_MASTINT_EN, VCPU_EN, 0);
|
||||
|
||||
@ -838,22 +840,72 @@ static int uvd_v6_0_process_interrupt(struct amdgpu_device *adev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void uvd_v6_0_enable_clock_gating(struct amdgpu_device *adev, bool enable)
|
||||
{
|
||||
uint32_t data1, data3;
|
||||
|
||||
data1 = RREG32(mmUVD_SUVD_CGC_GATE);
|
||||
data3 = RREG32(mmUVD_CGC_GATE);
|
||||
|
||||
data1 |= UVD_SUVD_CGC_GATE__SRE_MASK |
|
||||
UVD_SUVD_CGC_GATE__SIT_MASK |
|
||||
UVD_SUVD_CGC_GATE__SMP_MASK |
|
||||
UVD_SUVD_CGC_GATE__SCM_MASK |
|
||||
UVD_SUVD_CGC_GATE__SDB_MASK |
|
||||
UVD_SUVD_CGC_GATE__SRE_H264_MASK |
|
||||
UVD_SUVD_CGC_GATE__SRE_HEVC_MASK |
|
||||
UVD_SUVD_CGC_GATE__SIT_H264_MASK |
|
||||
UVD_SUVD_CGC_GATE__SIT_HEVC_MASK |
|
||||
UVD_SUVD_CGC_GATE__SCM_H264_MASK |
|
||||
UVD_SUVD_CGC_GATE__SCM_HEVC_MASK |
|
||||
UVD_SUVD_CGC_GATE__SDB_H264_MASK |
|
||||
UVD_SUVD_CGC_GATE__SDB_HEVC_MASK;
|
||||
|
||||
if (enable) {
|
||||
data3 |= (UVD_CGC_GATE__SYS_MASK |
|
||||
UVD_CGC_GATE__UDEC_MASK |
|
||||
UVD_CGC_GATE__MPEG2_MASK |
|
||||
UVD_CGC_GATE__RBC_MASK |
|
||||
UVD_CGC_GATE__LMI_MC_MASK |
|
||||
UVD_CGC_GATE__LMI_UMC_MASK |
|
||||
UVD_CGC_GATE__IDCT_MASK |
|
||||
UVD_CGC_GATE__MPRD_MASK |
|
||||
UVD_CGC_GATE__MPC_MASK |
|
||||
UVD_CGC_GATE__LBSI_MASK |
|
||||
UVD_CGC_GATE__LRBBM_MASK |
|
||||
UVD_CGC_GATE__UDEC_RE_MASK |
|
||||
UVD_CGC_GATE__UDEC_CM_MASK |
|
||||
UVD_CGC_GATE__UDEC_IT_MASK |
|
||||
UVD_CGC_GATE__UDEC_DB_MASK |
|
||||
UVD_CGC_GATE__UDEC_MP_MASK |
|
||||
UVD_CGC_GATE__WCB_MASK |
|
||||
UVD_CGC_GATE__JPEG_MASK |
|
||||
UVD_CGC_GATE__SCPU_MASK |
|
||||
UVD_CGC_GATE__JPEG2_MASK);
|
||||
/* only in pg enabled, we can gate clock to vcpu*/
|
||||
if (adev->pg_flags & AMD_PG_SUPPORT_UVD)
|
||||
data3 |= UVD_CGC_GATE__VCPU_MASK;
|
||||
|
||||
data3 &= ~UVD_CGC_GATE__REGS_MASK;
|
||||
} else {
|
||||
data3 = 0;
|
||||
}
|
||||
|
||||
WREG32(mmUVD_SUVD_CGC_GATE, data1);
|
||||
WREG32(mmUVD_CGC_GATE, data3);
|
||||
}
|
||||
|
||||
static void uvd_v6_0_set_sw_clock_gating(struct amdgpu_device *adev)
|
||||
{
|
||||
uint32_t data, data1, data2, suvd_flags;
|
||||
uint32_t data, data2;
|
||||
|
||||
data = RREG32(mmUVD_CGC_CTRL);
|
||||
data1 = RREG32(mmUVD_SUVD_CGC_GATE);
|
||||
data2 = RREG32(mmUVD_SUVD_CGC_CTRL);
|
||||
|
||||
|
||||
data &= ~(UVD_CGC_CTRL__CLK_OFF_DELAY_MASK |
|
||||
UVD_CGC_CTRL__CLK_GATE_DLY_TIMER_MASK);
|
||||
|
||||
suvd_flags = UVD_SUVD_CGC_GATE__SRE_MASK |
|
||||
UVD_SUVD_CGC_GATE__SIT_MASK |
|
||||
UVD_SUVD_CGC_GATE__SMP_MASK |
|
||||
UVD_SUVD_CGC_GATE__SCM_MASK |
|
||||
UVD_SUVD_CGC_GATE__SDB_MASK;
|
||||
|
||||
data |= UVD_CGC_CTRL__DYN_CLOCK_MODE_MASK |
|
||||
(1 << REG_FIELD_SHIFT(UVD_CGC_CTRL, CLK_GATE_DLY_TIMER)) |
|
||||
@ -886,11 +938,8 @@ static void uvd_v6_0_set_sw_clock_gating(struct amdgpu_device *adev)
|
||||
UVD_SUVD_CGC_CTRL__SMP_MODE_MASK |
|
||||
UVD_SUVD_CGC_CTRL__SCM_MODE_MASK |
|
||||
UVD_SUVD_CGC_CTRL__SDB_MODE_MASK);
|
||||
data1 |= suvd_flags;
|
||||
|
||||
WREG32(mmUVD_CGC_CTRL, data);
|
||||
WREG32(mmUVD_CGC_GATE, 0);
|
||||
WREG32(mmUVD_SUVD_CGC_GATE, data1);
|
||||
WREG32(mmUVD_SUVD_CGC_CTRL, data2);
|
||||
}
|
||||
|
||||
@ -937,6 +986,32 @@ static void uvd_v6_0_set_hw_clock_gating(struct amdgpu_device *adev)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void uvd_v6_0_enable_mgcg(struct amdgpu_device *adev,
|
||||
bool enable)
|
||||
{
|
||||
u32 orig, data;
|
||||
|
||||
if (enable && (adev->cg_flags & AMD_CG_SUPPORT_UVD_MGCG)) {
|
||||
data = RREG32_UVD_CTX(ixUVD_CGC_MEM_CTRL);
|
||||
data |= 0xfff;
|
||||
WREG32_UVD_CTX(ixUVD_CGC_MEM_CTRL, data);
|
||||
|
||||
orig = data = RREG32(mmUVD_CGC_CTRL);
|
||||
data |= UVD_CGC_CTRL__DYN_CLOCK_MODE_MASK;
|
||||
if (orig != data)
|
||||
WREG32(mmUVD_CGC_CTRL, data);
|
||||
} else {
|
||||
data = RREG32_UVD_CTX(ixUVD_CGC_MEM_CTRL);
|
||||
data &= ~0xfff;
|
||||
WREG32_UVD_CTX(ixUVD_CGC_MEM_CTRL, data);
|
||||
|
||||
orig = data = RREG32(mmUVD_CGC_CTRL);
|
||||
data &= ~UVD_CGC_CTRL__DYN_CLOCK_MODE_MASK;
|
||||
if (orig != data)
|
||||
WREG32(mmUVD_CGC_CTRL, data);
|
||||
}
|
||||
}
|
||||
|
||||
static int uvd_v6_0_set_clockgating_state(void *handle,
|
||||
enum amd_clockgating_state state)
|
||||
{
|
||||
@ -947,17 +1022,17 @@ static int uvd_v6_0_set_clockgating_state(void *handle,
|
||||
return 0;
|
||||
|
||||
if (enable) {
|
||||
/* disable HW gating and enable Sw gating */
|
||||
uvd_v6_0_set_sw_clock_gating(adev);
|
||||
} else {
|
||||
/* wait for STATUS to clear */
|
||||
if (uvd_v6_0_wait_for_idle(handle))
|
||||
return -EBUSY;
|
||||
|
||||
uvd_v6_0_enable_clock_gating(adev, true);
|
||||
/* enable HW gates because UVD is idle */
|
||||
/* uvd_v6_0_set_hw_clock_gating(adev); */
|
||||
} else {
|
||||
/* disable HW gating and enable Sw gating */
|
||||
uvd_v6_0_enable_clock_gating(adev, false);
|
||||
}
|
||||
|
||||
uvd_v6_0_set_sw_clock_gating(adev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -134,7 +134,7 @@ static void vce_v3_0_set_vce_sw_clock_gating(struct amdgpu_device *adev,
|
||||
accessible but the firmware will throttle the clocks on the
|
||||
fly as necessary.
|
||||
*/
|
||||
if (gated) {
|
||||
if (!gated) {
|
||||
data = RREG32(mmVCE_CLOCK_GATING_B);
|
||||
data |= 0x1ff;
|
||||
data &= ~0xef0000;
|
||||
|
@ -937,12 +937,14 @@ static int vi_common_early_init(void *handle)
|
||||
adev->external_rev_id = adev->rev_id + 0x14;
|
||||
break;
|
||||
case CHIP_POLARIS11:
|
||||
adev->cg_flags = AMD_CG_SUPPORT_UVD_MGCG;
|
||||
adev->cg_flags = AMD_CG_SUPPORT_UVD_MGCG |
|
||||
AMD_CG_SUPPORT_VCE_MGCG;
|
||||
adev->pg_flags = 0;
|
||||
adev->external_rev_id = adev->rev_id + 0x5A;
|
||||
break;
|
||||
case CHIP_POLARIS10:
|
||||
adev->cg_flags = AMD_CG_SUPPORT_UVD_MGCG;
|
||||
adev->cg_flags = AMD_CG_SUPPORT_UVD_MGCG |
|
||||
AMD_CG_SUPPORT_VCE_MGCG;
|
||||
adev->pg_flags = 0;
|
||||
adev->external_rev_id = adev->rev_id + 0x50;
|
||||
break;
|
||||
|
@ -1004,12 +1004,12 @@ int amd_powerplay_reset(void *handle)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hw_init_power_state_table(instance->hwmgr);
|
||||
|
||||
if ((amdgpu_dpm == 0)
|
||||
|| cgs_is_virtualization_enabled(instance->smu_mgr->device))
|
||||
return 0;
|
||||
|
||||
hw_init_power_state_table(instance->hwmgr);
|
||||
|
||||
if (eventmgr == NULL || eventmgr->pp_eventmgr_init == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -154,7 +154,7 @@ int pem_task_powerdown_vce_tasks(struct pp_eventmgr *eventmgr, struct pem_event_
|
||||
|
||||
int pem_task_disable_clock_power_gatings_tasks(struct pp_eventmgr *eventmgr, struct pem_event_data *event_data)
|
||||
{
|
||||
/* TODO */
|
||||
phm_disable_clock_power_gatings(eventmgr->hwmgr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ static int phm_run_table(struct pp_hwmgr *hwmgr,
|
||||
phm_table_function *function;
|
||||
|
||||
if (rt_table->function_list == NULL) {
|
||||
printk(KERN_INFO "[ powerplay ] this function not implement!\n");
|
||||
pr_debug("[ powerplay ] this function not implement!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -209,6 +209,19 @@ int phm_enable_clock_power_gatings(struct pp_hwmgr *hwmgr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int phm_disable_clock_power_gatings(struct pp_hwmgr *hwmgr)
|
||||
{
|
||||
PHM_FUNC_CHECK(hwmgr);
|
||||
|
||||
if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
|
||||
PHM_PlatformCaps_TablelessHardwareInterface)) {
|
||||
if (NULL != hwmgr->hwmgr_func->disable_clock_power_gating)
|
||||
return hwmgr->hwmgr_func->disable_clock_power_gating(hwmgr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int phm_display_configuration_changed(struct pp_hwmgr *hwmgr)
|
||||
{
|
||||
PHM_FUNC_CHECK(hwmgr);
|
||||
|
@ -334,6 +334,7 @@ struct phm_clocks {
|
||||
uint32_t clock[MAX_NUM_CLOCKS];
|
||||
};
|
||||
|
||||
extern int phm_disable_clock_power_gatings(struct pp_hwmgr *hwmgr);
|
||||
extern int phm_enable_clock_power_gatings(struct pp_hwmgr *hwmgr);
|
||||
extern int phm_powergate_uvd(struct pp_hwmgr *hwmgr, bool gate);
|
||||
extern int phm_powergate_vce(struct pp_hwmgr *hwmgr, bool gate);
|
||||
|
@ -1958,6 +1958,12 @@ int fiji_thermal_setup_fan_table(struct pp_hwmgr *hwmgr)
|
||||
int res;
|
||||
uint64_t tmp64;
|
||||
|
||||
if (hwmgr->thermal_controller.fanInfo.bNoFan) {
|
||||
phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
|
||||
PHM_PlatformCaps_MicrocodeFanControl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (smu_data->smu7_data.fan_table_start == 0) {
|
||||
phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
|
||||
PHM_PlatformCaps_MicrocodeFanControl);
|
||||
|
@ -2006,6 +2006,12 @@ int iceland_thermal_setup_fan_table(struct pp_hwmgr *hwmgr)
|
||||
if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl))
|
||||
return 0;
|
||||
|
||||
if (hwmgr->thermal_controller.fanInfo.bNoFan) {
|
||||
phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
|
||||
PHM_PlatformCaps_MicrocodeFanControl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (0 == smu7_data->fan_table_start) {
|
||||
phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl);
|
||||
return 0;
|
||||
|
@ -1885,6 +1885,12 @@ int polaris10_thermal_setup_fan_table(struct pp_hwmgr *hwmgr)
|
||||
int res;
|
||||
uint64_t tmp64;
|
||||
|
||||
if (hwmgr->thermal_controller.fanInfo.bNoFan) {
|
||||
phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
|
||||
PHM_PlatformCaps_MicrocodeFanControl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (smu_data->smu7_data.fan_table_start == 0) {
|
||||
phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
|
||||
PHM_PlatformCaps_MicrocodeFanControl);
|
||||
|
@ -366,12 +366,16 @@ static int smu7_populate_single_firmware_entry(struct pp_smumgr *smumgr,
|
||||
&info);
|
||||
|
||||
if (!result) {
|
||||
entry->version = info.version;
|
||||
entry->version = info.fw_version;
|
||||
entry->id = (uint16_t)fw_type;
|
||||
entry->image_addr_high = smu_upper_32_bits(info.mc_addr);
|
||||
entry->image_addr_low = smu_lower_32_bits(info.mc_addr);
|
||||
entry->meta_data_addr_high = 0;
|
||||
entry->meta_data_addr_low = 0;
|
||||
|
||||
/* digest need be excluded out */
|
||||
if (cgs_is_virtualization_enabled(smumgr->device))
|
||||
info.image_size -= 20;
|
||||
entry->data_size_byte = info.image_size;
|
||||
entry->num_register_entries = 0;
|
||||
}
|
||||
@ -403,8 +407,14 @@ int smu7_request_smu_load_fw(struct pp_smumgr *smumgr)
|
||||
0x0);
|
||||
|
||||
if (smumgr->chip_id > CHIP_TOPAZ) { /* add support for Topaz */
|
||||
smu7_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_SMU_DRAM_ADDR_HI, smu_data->smu_buffer.mc_addr_high);
|
||||
smu7_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_SMU_DRAM_ADDR_LO, smu_data->smu_buffer.mc_addr_low);
|
||||
if (!cgs_is_virtualization_enabled(smumgr->device)) {
|
||||
smu7_send_msg_to_smc_with_parameter(smumgr,
|
||||
PPSMC_MSG_SMU_DRAM_ADDR_HI,
|
||||
smu_data->smu_buffer.mc_addr_high);
|
||||
smu7_send_msg_to_smc_with_parameter(smumgr,
|
||||
PPSMC_MSG_SMU_DRAM_ADDR_LO,
|
||||
smu_data->smu_buffer.mc_addr_low);
|
||||
}
|
||||
fw_to_load = UCODE_ID_RLC_G_MASK
|
||||
+ UCODE_ID_SDMA0_MASK
|
||||
+ UCODE_ID_SDMA1_MASK
|
||||
@ -539,7 +549,6 @@ int smu7_init(struct pp_smumgr *smumgr)
|
||||
smu_data = (struct smu7_smumgr *)(smumgr->backend);
|
||||
smu_data->header_buffer.data_size =
|
||||
((sizeof(struct SMU_DRAMData_TOC) / 4096) + 1) * 4096;
|
||||
smu_data->smu_buffer.data_size = 200*4096;
|
||||
|
||||
/* Allocate FW image data structure and header buffer and
|
||||
* send the header buffer address to SMU */
|
||||
@ -562,6 +571,10 @@ int smu7_init(struct pp_smumgr *smumgr)
|
||||
(cgs_handle_t)smu_data->header_buffer.handle);
|
||||
return -EINVAL);
|
||||
|
||||
if (cgs_is_virtualization_enabled(smumgr->device))
|
||||
return 0;
|
||||
|
||||
smu_data->smu_buffer.data_size = 200*4096;
|
||||
smu_allocate_memory(smumgr->device,
|
||||
smu_data->smu_buffer.data_size,
|
||||
CGS_GPU_MEM_TYPE__VISIBLE_CONTIG_FB,
|
||||
|
@ -2496,6 +2496,12 @@ int tonga_thermal_setup_fan_table(struct pp_hwmgr *hwmgr)
|
||||
PHM_PlatformCaps_MicrocodeFanControl))
|
||||
return 0;
|
||||
|
||||
if (hwmgr->thermal_controller.fanInfo.bNoFan) {
|
||||
phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
|
||||
PHM_PlatformCaps_MicrocodeFanControl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (0 == smu_data->smu7_data.fan_table_start) {
|
||||
phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
|
||||
PHM_PlatformCaps_MicrocodeFanControl);
|
||||
|
@ -1087,12 +1087,38 @@ int drm_atomic_connector_set_property(struct drm_connector *connector,
|
||||
* now?) atomic writes to DPMS property:
|
||||
*/
|
||||
return -EINVAL;
|
||||
} else if (property == config->tv_select_subconnector_property) {
|
||||
state->tv.subconnector = val;
|
||||
} else if (property == config->tv_left_margin_property) {
|
||||
state->tv.margins.left = val;
|
||||
} else if (property == config->tv_right_margin_property) {
|
||||
state->tv.margins.right = val;
|
||||
} else if (property == config->tv_top_margin_property) {
|
||||
state->tv.margins.top = val;
|
||||
} else if (property == config->tv_bottom_margin_property) {
|
||||
state->tv.margins.bottom = val;
|
||||
} else if (property == config->tv_mode_property) {
|
||||
state->tv.mode = val;
|
||||
} else if (property == config->tv_brightness_property) {
|
||||
state->tv.brightness = val;
|
||||
} else if (property == config->tv_contrast_property) {
|
||||
state->tv.contrast = val;
|
||||
} else if (property == config->tv_flicker_reduction_property) {
|
||||
state->tv.flicker_reduction = val;
|
||||
} else if (property == config->tv_overscan_property) {
|
||||
state->tv.overscan = val;
|
||||
} else if (property == config->tv_saturation_property) {
|
||||
state->tv.saturation = val;
|
||||
} else if (property == config->tv_hue_property) {
|
||||
state->tv.hue = val;
|
||||
} else if (connector->funcs->atomic_set_property) {
|
||||
return connector->funcs->atomic_set_property(connector,
|
||||
state, property, val);
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_connector_set_property);
|
||||
|
||||
@ -1135,6 +1161,30 @@ drm_atomic_connector_get_property(struct drm_connector *connector,
|
||||
*val = (state->crtc) ? state->crtc->base.id : 0;
|
||||
} else if (property == config->dpms_property) {
|
||||
*val = connector->dpms;
|
||||
} else if (property == config->tv_select_subconnector_property) {
|
||||
*val = state->tv.subconnector;
|
||||
} else if (property == config->tv_left_margin_property) {
|
||||
*val = state->tv.margins.left;
|
||||
} else if (property == config->tv_right_margin_property) {
|
||||
*val = state->tv.margins.right;
|
||||
} else if (property == config->tv_top_margin_property) {
|
||||
*val = state->tv.margins.top;
|
||||
} else if (property == config->tv_bottom_margin_property) {
|
||||
*val = state->tv.margins.bottom;
|
||||
} else if (property == config->tv_mode_property) {
|
||||
*val = state->tv.mode;
|
||||
} else if (property == config->tv_brightness_property) {
|
||||
*val = state->tv.brightness;
|
||||
} else if (property == config->tv_contrast_property) {
|
||||
*val = state->tv.contrast;
|
||||
} else if (property == config->tv_flicker_reduction_property) {
|
||||
*val = state->tv.flicker_reduction;
|
||||
} else if (property == config->tv_overscan_property) {
|
||||
*val = state->tv.overscan;
|
||||
} else if (property == config->tv_saturation_property) {
|
||||
*val = state->tv.saturation;
|
||||
} else if (property == config->tv_hue_property) {
|
||||
*val = state->tv.hue;
|
||||
} else if (connector->funcs->atomic_get_property) {
|
||||
return connector->funcs->atomic_get_property(connector,
|
||||
state, property, val);
|
||||
|
@ -650,6 +650,62 @@ void drm_dev_unref(struct drm_device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dev_unref);
|
||||
|
||||
static int create_compat_control_link(struct drm_device *dev)
|
||||
{
|
||||
struct drm_minor *minor;
|
||||
char *name;
|
||||
int ret;
|
||||
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return 0;
|
||||
|
||||
minor = *drm_minor_get_slot(dev, DRM_MINOR_PRIMARY);
|
||||
if (!minor)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Some existing userspace out there uses the existing of the controlD*
|
||||
* sysfs files to figure out whether it's a modeset driver. It only does
|
||||
* readdir, hence a symlink is sufficient (and the least confusing
|
||||
* option). Otherwise controlD* is entirely unused.
|
||||
*
|
||||
* Old controlD chardev have been allocated in the range
|
||||
* 64-127.
|
||||
*/
|
||||
name = kasprintf(GFP_KERNEL, "controlD%d", minor->index + 64);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = sysfs_create_link(minor->kdev->kobj.parent,
|
||||
&minor->kdev->kobj,
|
||||
name);
|
||||
|
||||
kfree(name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void remove_compat_control_link(struct drm_device *dev)
|
||||
{
|
||||
struct drm_minor *minor;
|
||||
char *name;
|
||||
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return;
|
||||
|
||||
minor = *drm_minor_get_slot(dev, DRM_MINOR_PRIMARY);
|
||||
if (!minor)
|
||||
return;
|
||||
|
||||
name = kasprintf(GFP_KERNEL, "controlD%d", minor->index);
|
||||
if (!name)
|
||||
return;
|
||||
|
||||
sysfs_remove_link(minor->kdev->kobj.parent, name);
|
||||
|
||||
kfree(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_dev_register - Register DRM device
|
||||
* @dev: Device to register
|
||||
@ -688,6 +744,10 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags)
|
||||
if (ret)
|
||||
goto err_minors;
|
||||
|
||||
ret = create_compat_control_link(dev);
|
||||
if (ret)
|
||||
goto err_minors;
|
||||
|
||||
if (dev->driver->load) {
|
||||
ret = dev->driver->load(dev, flags);
|
||||
if (ret)
|
||||
@ -701,6 +761,7 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags)
|
||||
goto out_unlock;
|
||||
|
||||
err_minors:
|
||||
remove_compat_control_link(dev);
|
||||
drm_minor_unregister(dev, DRM_MINOR_PRIMARY);
|
||||
drm_minor_unregister(dev, DRM_MINOR_RENDER);
|
||||
drm_minor_unregister(dev, DRM_MINOR_CONTROL);
|
||||
@ -741,6 +802,7 @@ void drm_dev_unregister(struct drm_device *dev)
|
||||
list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head)
|
||||
drm_legacy_rmmap(dev, r_list->map);
|
||||
|
||||
remove_compat_control_link(dev);
|
||||
drm_minor_unregister(dev, DRM_MINOR_PRIMARY);
|
||||
drm_minor_unregister(dev, DRM_MINOR_RENDER);
|
||||
drm_minor_unregister(dev, DRM_MINOR_CONTROL);
|
||||
|
@ -79,7 +79,7 @@ static unsigned int drm_num_planes(struct drm_device *dev)
|
||||
* Zero on success, error code on failure.
|
||||
*/
|
||||
int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
|
||||
unsigned long possible_crtcs,
|
||||
uint32_t possible_crtcs,
|
||||
const struct drm_plane_funcs *funcs,
|
||||
const uint32_t *formats, unsigned int format_count,
|
||||
enum drm_plane_type type,
|
||||
@ -196,7 +196,7 @@ void drm_plane_unregister_all(struct drm_device *dev)
|
||||
* Zero on success, error code on failure.
|
||||
*/
|
||||
int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
|
||||
unsigned long possible_crtcs,
|
||||
uint32_t possible_crtcs,
|
||||
const struct drm_plane_funcs *funcs,
|
||||
const uint32_t *formats, unsigned int format_count,
|
||||
bool is_primary)
|
||||
|
@ -3,6 +3,5 @@ fsl-dcu-drm-y := fsl_dcu_drm_drv.o \
|
||||
fsl_dcu_drm_rgb.o \
|
||||
fsl_dcu_drm_plane.o \
|
||||
fsl_dcu_drm_crtc.o \
|
||||
fsl_dcu_drm_fbdev.o \
|
||||
fsl_tcon.o
|
||||
obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu-drm.o
|
||||
|
@ -32,6 +32,9 @@
|
||||
#include "fsl_dcu_drm_drv.h"
|
||||
#include "fsl_tcon.h"
|
||||
|
||||
static int legacyfb_depth = 24;
|
||||
module_param(legacyfb_depth, int, 0444);
|
||||
|
||||
static bool fsl_dcu_drm_is_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
if (reg == DCU_INT_STATUS || reg == DCU_UPDATE_MODE)
|
||||
@ -85,7 +88,18 @@ static int fsl_dcu_load(struct drm_device *dev, unsigned long flags)
|
||||
goto done;
|
||||
dev->irq_enabled = true;
|
||||
|
||||
fsl_dcu_fbdev_init(dev);
|
||||
if (legacyfb_depth != 16 && legacyfb_depth != 24 &&
|
||||
legacyfb_depth != 32) {
|
||||
dev_warn(dev->dev,
|
||||
"Invalid legacyfb_depth. Defaulting to 24bpp\n");
|
||||
legacyfb_depth = 24;
|
||||
}
|
||||
fsl_dev->fbdev = drm_fbdev_cma_init(dev, legacyfb_depth, 1, 1);
|
||||
if (IS_ERR(fsl_dev->fbdev)) {
|
||||
ret = PTR_ERR(fsl_dev->fbdev);
|
||||
fsl_dev->fbdev = NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
return 0;
|
||||
done:
|
||||
@ -106,6 +120,7 @@ static int fsl_dcu_unload(struct drm_device *dev)
|
||||
{
|
||||
struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
|
||||
|
||||
drm_crtc_force_disable_all(dev);
|
||||
drm_kms_helper_poll_fini(dev);
|
||||
|
||||
if (fsl_dev->fbdev)
|
||||
@ -332,11 +347,6 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
|
||||
fsl_dev->soc = id->data;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "could not get memory IO resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base)) {
|
||||
ret = PTR_ERR(base);
|
||||
@ -346,7 +356,7 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
|
||||
fsl_dev->irq = platform_get_irq(pdev, 0);
|
||||
if (fsl_dev->irq < 0) {
|
||||
dev_err(dev, "failed to get irq\n");
|
||||
return -ENXIO;
|
||||
return fsl_dev->irq;
|
||||
}
|
||||
|
||||
fsl_dev->regmap = devm_regmap_init_mmio(dev, base,
|
||||
@ -424,9 +434,9 @@ static int fsl_dcu_drm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct fsl_dcu_drm_device *fsl_dev = platform_get_drvdata(pdev);
|
||||
|
||||
drm_put_dev(fsl_dev->drm);
|
||||
clk_disable_unprepare(fsl_dev->clk);
|
||||
clk_unregister(fsl_dev->pix_clk);
|
||||
drm_put_dev(fsl_dev->drm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -197,7 +197,6 @@ struct fsl_dcu_drm_device {
|
||||
struct drm_atomic_state *state;
|
||||
};
|
||||
|
||||
void fsl_dcu_fbdev_init(struct drm_device *dev);
|
||||
int fsl_dcu_drm_modeset_init(struct fsl_dcu_drm_device *fsl_dev);
|
||||
|
||||
#endif /* __FSL_DCU_DRM_DRV_H__ */
|
||||
|
@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* Freescale DCU drm device driver
|
||||
*
|
||||
* 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 <drm/drm_fb_cma_helper.h>
|
||||
|
||||
#include "fsl_dcu_drm_drv.h"
|
||||
|
||||
/* initialize fbdev helper */
|
||||
void fsl_dcu_fbdev_init(struct drm_device *dev)
|
||||
{
|
||||
struct fsl_dcu_drm_device *fsl_dev = dev_get_drvdata(dev->dev);
|
||||
|
||||
fsl_dev->fbdev = drm_fbdev_cma_init(dev, 24, 1, 1);
|
||||
}
|
@ -361,6 +361,8 @@ static inline void intel_vgpu_write_pci_bar(struct intel_vgpu *vgpu,
|
||||
* leave the bit 3 - bit 0 unchanged.
|
||||
*/
|
||||
*pval = (val & GENMASK(31, 4)) | (*pval & GENMASK(3, 0));
|
||||
} else {
|
||||
*pval = val;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,8 +177,8 @@ static int dispatch_workload(struct intel_vgpu_workload *workload)
|
||||
rq = i915_gem_request_alloc(dev_priv->engine[ring_id], shadow_ctx);
|
||||
if (IS_ERR(rq)) {
|
||||
gvt_err("fail to allocate gem request\n");
|
||||
workload->status = PTR_ERR(rq);
|
||||
return workload->status;
|
||||
ret = PTR_ERR(rq);
|
||||
goto out;
|
||||
}
|
||||
|
||||
gvt_dbg_sched("ring id %d get i915 gem request %p\n", ring_id, rq);
|
||||
@ -212,7 +212,8 @@ out:
|
||||
if (ret)
|
||||
workload->status = ret;
|
||||
|
||||
i915_add_request_no_flush(rq);
|
||||
if (!IS_ERR_OR_NULL(rq))
|
||||
i915_add_request_no_flush(rq);
|
||||
mutex_unlock(&dev_priv->drm.struct_mutex);
|
||||
return ret;
|
||||
}
|
||||
@ -460,7 +461,8 @@ complete:
|
||||
|
||||
complete_current_workload(gvt, ring_id);
|
||||
|
||||
i915_gem_request_put(fetch_and_zero(&workload->req));
|
||||
if (workload->req)
|
||||
i915_gem_request_put(fetch_and_zero(&workload->req));
|
||||
|
||||
if (need_force_wake)
|
||||
intel_uncore_forcewake_put(gvt->dev_priv,
|
||||
|
@ -378,6 +378,7 @@ struct intel_vgpu *intel_gvt_create_vgpu(struct intel_gvt *gvt,
|
||||
struct intel_vgpu *vgpu;
|
||||
|
||||
param.handle = 0;
|
||||
param.primary = 1;
|
||||
param.low_gm_sz = type->low_gm_size;
|
||||
param.high_gm_sz = type->high_gm_size;
|
||||
param.fence_sz = type->fence;
|
||||
|
@ -935,27 +935,6 @@ static int i915_gem_fence_regs_info(struct seq_file *m, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i915_hws_info(struct seq_file *m, void *data)
|
||||
{
|
||||
struct drm_info_node *node = m->private;
|
||||
struct drm_i915_private *dev_priv = node_to_i915(node);
|
||||
struct intel_engine_cs *engine;
|
||||
const u32 *hws;
|
||||
int i;
|
||||
|
||||
engine = dev_priv->engine[(uintptr_t)node->info_ent->data];
|
||||
hws = engine->status_page.page_addr;
|
||||
if (hws == NULL)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < 4096 / sizeof(u32) / 4; i += 4) {
|
||||
seq_printf(m, "0x%08x: 0x%08x 0x%08x 0x%08x 0x%08x\n",
|
||||
i * 4,
|
||||
hws[i], hws[i + 1], hws[i + 2], hws[i + 3]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR)
|
||||
|
||||
static ssize_t
|
||||
@ -1047,7 +1026,7 @@ i915_next_seqno_get(void *data, u64 *val)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = data;
|
||||
|
||||
*val = atomic_read(&dev_priv->gt.global_timeline.next_seqno);
|
||||
*val = 1 + atomic_read(&dev_priv->gt.global_timeline.next_seqno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -5403,10 +5382,6 @@ static const struct drm_info_list i915_debugfs_list[] = {
|
||||
{"i915_gem_seqno", i915_gem_seqno_info, 0},
|
||||
{"i915_gem_fence_regs", i915_gem_fence_regs_info, 0},
|
||||
{"i915_gem_interrupt", i915_interrupt_info, 0},
|
||||
{"i915_gem_hws", i915_hws_info, 0, (void *)RCS},
|
||||
{"i915_gem_hws_blt", i915_hws_info, 0, (void *)BCS},
|
||||
{"i915_gem_hws_bsd", i915_hws_info, 0, (void *)VCS},
|
||||
{"i915_gem_hws_vebox", i915_hws_info, 0, (void *)VECS},
|
||||
{"i915_gem_batch_pool", i915_gem_batch_pool_info, 0},
|
||||
{"i915_guc_info", i915_guc_info, 0},
|
||||
{"i915_guc_load_status", i915_guc_load_status_info, 0},
|
||||
|
@ -2764,6 +2764,8 @@ void i915_gem_reset(struct drm_i915_private *dev_priv)
|
||||
|
||||
static void nop_submit_request(struct drm_i915_gem_request *request)
|
||||
{
|
||||
i915_gem_request_submit(request);
|
||||
intel_engine_init_global_seqno(request->engine, request->global_seqno);
|
||||
}
|
||||
|
||||
static void i915_gem_cleanup_engine(struct intel_engine_cs *engine)
|
||||
|
@ -200,8 +200,8 @@ static void i915_gem_request_retire(struct drm_i915_gem_request *request)
|
||||
struct i915_gem_active *active, *next;
|
||||
|
||||
lockdep_assert_held(&request->i915->drm.struct_mutex);
|
||||
GEM_BUG_ON(!i915_sw_fence_done(&request->submit));
|
||||
GEM_BUG_ON(!i915_sw_fence_done(&request->execute));
|
||||
GEM_BUG_ON(!i915_sw_fence_signaled(&request->submit));
|
||||
GEM_BUG_ON(!i915_sw_fence_signaled(&request->execute));
|
||||
GEM_BUG_ON(!i915_gem_request_completed(request));
|
||||
GEM_BUG_ON(!request->i915->gt.active_requests);
|
||||
|
||||
@ -445,11 +445,17 @@ void i915_gem_request_submit(struct drm_i915_gem_request *request)
|
||||
static int __i915_sw_fence_call
|
||||
submit_notify(struct i915_sw_fence *fence, enum i915_sw_fence_notify state)
|
||||
{
|
||||
if (state == FENCE_COMPLETE) {
|
||||
struct drm_i915_gem_request *request =
|
||||
container_of(fence, typeof(*request), submit);
|
||||
struct drm_i915_gem_request *request =
|
||||
container_of(fence, typeof(*request), submit);
|
||||
|
||||
switch (state) {
|
||||
case FENCE_COMPLETE:
|
||||
request->engine->submit_request(request);
|
||||
break;
|
||||
|
||||
case FENCE_FREE:
|
||||
i915_gem_request_put(request);
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
@ -458,6 +464,18 @@ submit_notify(struct i915_sw_fence *fence, enum i915_sw_fence_notify state)
|
||||
static int __i915_sw_fence_call
|
||||
execute_notify(struct i915_sw_fence *fence, enum i915_sw_fence_notify state)
|
||||
{
|
||||
struct drm_i915_gem_request *request =
|
||||
container_of(fence, typeof(*request), execute);
|
||||
|
||||
switch (state) {
|
||||
case FENCE_COMPLETE:
|
||||
break;
|
||||
|
||||
case FENCE_FREE:
|
||||
i915_gem_request_put(request);
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
@ -545,8 +563,10 @@ i915_gem_request_alloc(struct intel_engine_cs *engine,
|
||||
req->timeline->fence_context,
|
||||
__timeline_get_seqno(req->timeline->common));
|
||||
|
||||
i915_sw_fence_init(&req->submit, submit_notify);
|
||||
i915_sw_fence_init(&req->execute, execute_notify);
|
||||
/* We bump the ref for the fence chain */
|
||||
i915_sw_fence_init(&i915_gem_request_get(req)->submit, submit_notify);
|
||||
i915_sw_fence_init(&i915_gem_request_get(req)->execute, execute_notify);
|
||||
|
||||
/* Ensure that the execute fence completes after the submit fence -
|
||||
* as we complete the execute fence from within the submit fence
|
||||
* callback, its completion would otherwise be visible first.
|
||||
|
@ -75,6 +75,11 @@ int i915_sw_fence_await_reservation(struct i915_sw_fence *fence,
|
||||
unsigned long timeout,
|
||||
gfp_t gfp);
|
||||
|
||||
static inline bool i915_sw_fence_signaled(const struct i915_sw_fence *fence)
|
||||
{
|
||||
return atomic_read(&fence->pending) <= 0;
|
||||
}
|
||||
|
||||
static inline bool i915_sw_fence_done(const struct i915_sw_fence *fence)
|
||||
{
|
||||
return atomic_read(&fence->pending) < 0;
|
||||
|
@ -351,10 +351,13 @@ hsw_hdmi_audio_config_update(struct intel_crtc *intel_crtc, enum port port,
|
||||
|
||||
I915_WRITE(HSW_AUD_CFG(pipe), tmp);
|
||||
|
||||
/*
|
||||
* Let's disable "Enable CTS or M Prog bit"
|
||||
* and let HW calculate the value
|
||||
*/
|
||||
tmp = I915_READ(HSW_AUD_M_CTS_ENABLE(pipe));
|
||||
tmp &= ~AUD_CONFIG_M_MASK;
|
||||
tmp &= ~AUD_M_CTS_M_PROG_ENABLE;
|
||||
tmp &= ~AUD_M_CTS_M_VALUE_INDEX;
|
||||
tmp |= AUD_M_CTS_M_PROG_ENABLE;
|
||||
I915_WRITE(HSW_AUD_M_CTS_ENABLE(pipe), tmp);
|
||||
}
|
||||
|
||||
|
@ -12028,7 +12028,6 @@ static void intel_mmio_flip_work_func(struct work_struct *w)
|
||||
to_intel_framebuffer(crtc->base.primary->fb);
|
||||
struct drm_i915_gem_object *obj = intel_fb->obj;
|
||||
|
||||
i915_gem_object_wait_priority(obj, 0, I915_PRIORITY_DISPLAY);
|
||||
WARN_ON(i915_gem_object_wait(obj, 0, MAX_SCHEDULE_TIMEOUT, NULL) < 0);
|
||||
|
||||
intel_pipe_update_start(crtc);
|
||||
@ -12284,6 +12283,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
|
||||
i915_add_request_no_flush(request);
|
||||
}
|
||||
|
||||
i915_gem_object_wait_priority(obj, 0, I915_PRIORITY_DISPLAY);
|
||||
i915_gem_track_fb(intel_fb_obj(old_fb), obj,
|
||||
to_intel_plane(primary)->frontbuffer_bit);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
@ -13995,8 +13995,9 @@ static int intel_modeset_checks(struct drm_atomic_state *state)
|
||||
|
||||
DRM_DEBUG_KMS("New cdclk calculated to be atomic %u, actual %u\n",
|
||||
intel_state->cdclk, intel_state->dev_cdclk);
|
||||
} else
|
||||
} else {
|
||||
to_intel_atomic_state(state)->cdclk = dev_priv->atomic_cdclk_freq;
|
||||
}
|
||||
|
||||
intel_modeset_clear_plls(state);
|
||||
|
||||
@ -14097,8 +14098,9 @@ static int intel_atomic_check(struct drm_device *dev,
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
} else
|
||||
intel_state->cdclk = dev_priv->cdclk_freq;
|
||||
} else {
|
||||
intel_state->cdclk = dev_priv->atomic_cdclk_freq;
|
||||
}
|
||||
|
||||
ret = drm_atomic_helper_check_planes(dev, state);
|
||||
if (ret)
|
||||
@ -16485,6 +16487,7 @@ int intel_modeset_init(struct drm_device *dev)
|
||||
|
||||
intel_update_czclk(dev_priv);
|
||||
intel_update_cdclk(dev_priv);
|
||||
dev_priv->atomic_cdclk_freq = dev_priv->cdclk_freq;
|
||||
|
||||
intel_shared_dpll_init(dev);
|
||||
|
||||
|
@ -3851,10 +3851,10 @@ static void skl_write_wm_level(struct drm_i915_private *dev_priv,
|
||||
I915_WRITE(reg, val);
|
||||
}
|
||||
|
||||
void skl_write_plane_wm(struct intel_crtc *intel_crtc,
|
||||
const struct skl_plane_wm *wm,
|
||||
const struct skl_ddb_allocation *ddb,
|
||||
int plane)
|
||||
static void skl_write_plane_wm(struct intel_crtc *intel_crtc,
|
||||
const struct skl_plane_wm *wm,
|
||||
const struct skl_ddb_allocation *ddb,
|
||||
int plane)
|
||||
{
|
||||
struct drm_crtc *crtc = &intel_crtc->base;
|
||||
struct drm_device *dev = crtc->dev;
|
||||
@ -3875,9 +3875,9 @@ void skl_write_plane_wm(struct intel_crtc *intel_crtc,
|
||||
&ddb->y_plane[pipe][plane]);
|
||||
}
|
||||
|
||||
void skl_write_cursor_wm(struct intel_crtc *intel_crtc,
|
||||
const struct skl_plane_wm *wm,
|
||||
const struct skl_ddb_allocation *ddb)
|
||||
static void skl_write_cursor_wm(struct intel_crtc *intel_crtc,
|
||||
const struct skl_plane_wm *wm,
|
||||
const struct skl_ddb_allocation *ddb)
|
||||
{
|
||||
struct drm_crtc *crtc = &intel_crtc->base;
|
||||
struct drm_device *dev = crtc->dev;
|
||||
|
9
drivers/gpu/drm/meson/Kconfig
Normal file
9
drivers/gpu/drm/meson/Kconfig
Normal file
@ -0,0 +1,9 @@
|
||||
config DRM_MESON
|
||||
tristate "DRM Support for Amlogic Meson Display Controller"
|
||||
depends on DRM && OF && (ARM || ARM64)
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_KMS_CMA_HELPER
|
||||
select DRM_GEM_CMA_HELPER
|
||||
select VIDEOMODE_HELPERS
|
||||
select REGMAP_MMIO
|
4
drivers/gpu/drm/meson/Makefile
Normal file
4
drivers/gpu/drm/meson/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
meson-y := meson_drv.o meson_plane.o meson_crtc.o meson_venc_cvbs.o
|
||||
meson-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_canvas.o
|
||||
|
||||
obj-$(CONFIG_DRM_MESON) += meson.o
|
68
drivers/gpu/drm/meson/meson_canvas.c
Normal file
68
drivers/gpu/drm/meson/meson_canvas.c
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include "meson_drv.h"
|
||||
#include "meson_canvas.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
/*
|
||||
* CANVAS is a memory zone where physical memory frames information
|
||||
* are stored for the VIU to scanout.
|
||||
*/
|
||||
|
||||
/* DMC Registers */
|
||||
#define DMC_CAV_LUT_DATAL 0x48 /* 0x12 offset in data sheet */
|
||||
#define CANVAS_WIDTH_LBIT 29
|
||||
#define CANVAS_WIDTH_LWID 3
|
||||
#define DMC_CAV_LUT_DATAH 0x4c /* 0x13 offset in data sheet */
|
||||
#define CANVAS_WIDTH_HBIT 0
|
||||
#define CANVAS_HEIGHT_BIT 9
|
||||
#define CANVAS_BLKMODE_BIT 24
|
||||
#define DMC_CAV_LUT_ADDR 0x50 /* 0x14 offset in data sheet */
|
||||
#define CANVAS_LUT_WR_EN (0x2 << 8)
|
||||
#define CANVAS_LUT_RD_EN (0x1 << 8)
|
||||
|
||||
void meson_canvas_setup(struct meson_drm *priv,
|
||||
uint32_t canvas_index, uint32_t addr,
|
||||
uint32_t stride, uint32_t height,
|
||||
unsigned int wrap,
|
||||
unsigned int blkmode)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
regmap_write(priv->dmc, DMC_CAV_LUT_DATAL,
|
||||
(((addr + 7) >> 3)) |
|
||||
(((stride + 7) >> 3) << CANVAS_WIDTH_LBIT));
|
||||
|
||||
regmap_write(priv->dmc, DMC_CAV_LUT_DATAH,
|
||||
((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) <<
|
||||
CANVAS_WIDTH_HBIT) |
|
||||
(height << CANVAS_HEIGHT_BIT) |
|
||||
(wrap << 22) |
|
||||
(blkmode << CANVAS_BLKMODE_BIT));
|
||||
|
||||
regmap_write(priv->dmc, DMC_CAV_LUT_ADDR,
|
||||
CANVAS_LUT_WR_EN | canvas_index);
|
||||
|
||||
/* Force a read-back to make sure everything is flushed. */
|
||||
regmap_read(priv->dmc, DMC_CAV_LUT_DATAH, &val);
|
||||
}
|
42
drivers/gpu/drm/meson/meson_canvas.h
Normal file
42
drivers/gpu/drm/meson/meson_canvas.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Canvas LUT Memory */
|
||||
|
||||
#ifndef __MESON_CANVAS_H
|
||||
#define __MESON_CANVAS_H
|
||||
|
||||
#define MESON_CANVAS_ID_OSD1 0x4e
|
||||
|
||||
/* Canvas configuration. */
|
||||
#define MESON_CANVAS_WRAP_NONE 0x00
|
||||
#define MESON_CANVAS_WRAP_X 0x01
|
||||
#define MESON_CANVAS_WRAP_Y 0x02
|
||||
|
||||
#define MESON_CANVAS_BLKMODE_LINEAR 0x00
|
||||
#define MESON_CANVAS_BLKMODE_32x32 0x01
|
||||
#define MESON_CANVAS_BLKMODE_64x64 0x02
|
||||
|
||||
void meson_canvas_setup(struct meson_drm *priv,
|
||||
uint32_t canvas_index, uint32_t addr,
|
||||
uint32_t stride, uint32_t height,
|
||||
unsigned int wrap,
|
||||
unsigned int blkmode);
|
||||
|
||||
#endif /* __MESON_CANVAS_H */
|
208
drivers/gpu/drm/meson/meson_crtc.c
Normal file
208
drivers/gpu/drm/meson/meson_crtc.c
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Written by:
|
||||
* Jasper St. Pierre <jstpierre@mecheye.net>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_flip_work.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
|
||||
#include "meson_crtc.h"
|
||||
#include "meson_plane.h"
|
||||
#include "meson_vpp.h"
|
||||
#include "meson_viu.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
/* CRTC definition */
|
||||
|
||||
struct meson_crtc {
|
||||
struct drm_crtc base;
|
||||
struct drm_pending_vblank_event *event;
|
||||
struct meson_drm *priv;
|
||||
};
|
||||
#define to_meson_crtc(x) container_of(x, struct meson_crtc, base)
|
||||
|
||||
/* CRTC */
|
||||
|
||||
static const struct drm_crtc_funcs meson_crtc_funcs = {
|
||||
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
|
||||
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
|
||||
.destroy = drm_crtc_cleanup,
|
||||
.page_flip = drm_atomic_helper_page_flip,
|
||||
.reset = drm_atomic_helper_crtc_reset,
|
||||
.set_config = drm_atomic_helper_set_config,
|
||||
};
|
||||
|
||||
static void meson_crtc_enable(struct drm_crtc *crtc)
|
||||
{
|
||||
struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
|
||||
struct drm_plane *plane = meson_crtc->priv->primary_plane;
|
||||
struct meson_drm *priv = meson_crtc->priv;
|
||||
|
||||
/* Enable VPP Postblend */
|
||||
writel(plane->state->crtc_w,
|
||||
priv->io_base + _REG(VPP_POSTBLEND_H_SIZE));
|
||||
|
||||
writel_bits_relaxed(VPP_POSTBLEND_ENABLE, VPP_POSTBLEND_ENABLE,
|
||||
priv->io_base + _REG(VPP_MISC));
|
||||
|
||||
priv->viu.osd1_enabled = true;
|
||||
}
|
||||
|
||||
static void meson_crtc_disable(struct drm_crtc *crtc)
|
||||
{
|
||||
struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
|
||||
struct meson_drm *priv = meson_crtc->priv;
|
||||
|
||||
priv->viu.osd1_enabled = false;
|
||||
|
||||
/* Disable VPP Postblend */
|
||||
writel_bits_relaxed(VPP_POSTBLEND_ENABLE, 0,
|
||||
priv->io_base + _REG(VPP_MISC));
|
||||
|
||||
if (crtc->state->event && !crtc->state->active) {
|
||||
spin_lock_irq(&crtc->dev->event_lock);
|
||||
drm_crtc_send_vblank_event(crtc, crtc->state->event);
|
||||
spin_unlock_irq(&crtc->dev->event_lock);
|
||||
|
||||
crtc->state->event = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void meson_crtc_atomic_begin(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *state)
|
||||
{
|
||||
struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
|
||||
unsigned long flags;
|
||||
|
||||
if (crtc->state->event) {
|
||||
WARN_ON(drm_crtc_vblank_get(crtc) != 0);
|
||||
|
||||
spin_lock_irqsave(&crtc->dev->event_lock, flags);
|
||||
meson_crtc->event = crtc->state->event;
|
||||
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
|
||||
crtc->state->event = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void meson_crtc_atomic_flush(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_crtc_state)
|
||||
{
|
||||
struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
|
||||
struct meson_drm *priv = meson_crtc->priv;
|
||||
|
||||
if (priv->viu.osd1_enabled)
|
||||
priv->viu.osd1_commit = true;
|
||||
}
|
||||
|
||||
static const struct drm_crtc_helper_funcs meson_crtc_helper_funcs = {
|
||||
.enable = meson_crtc_enable,
|
||||
.disable = meson_crtc_disable,
|
||||
.atomic_begin = meson_crtc_atomic_begin,
|
||||
.atomic_flush = meson_crtc_atomic_flush,
|
||||
};
|
||||
|
||||
void meson_crtc_irq(struct meson_drm *priv)
|
||||
{
|
||||
struct meson_crtc *meson_crtc = to_meson_crtc(priv->crtc);
|
||||
unsigned long flags;
|
||||
|
||||
/* Update the OSD registers */
|
||||
if (priv->viu.osd1_enabled && priv->viu.osd1_commit) {
|
||||
writel_relaxed(priv->viu.osd1_ctrl_stat,
|
||||
priv->io_base + _REG(VIU_OSD1_CTRL_STAT));
|
||||
writel_relaxed(priv->viu.osd1_blk0_cfg[0],
|
||||
priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W0));
|
||||
writel_relaxed(priv->viu.osd1_blk0_cfg[1],
|
||||
priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W1));
|
||||
writel_relaxed(priv->viu.osd1_blk0_cfg[2],
|
||||
priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W2));
|
||||
writel_relaxed(priv->viu.osd1_blk0_cfg[3],
|
||||
priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W3));
|
||||
writel_relaxed(priv->viu.osd1_blk0_cfg[4],
|
||||
priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W4));
|
||||
|
||||
/* If output is interlace, make use of the Scaler */
|
||||
if (priv->viu.osd1_interlace) {
|
||||
struct drm_plane *plane = priv->primary_plane;
|
||||
struct drm_plane_state *state = plane->state;
|
||||
struct drm_rect dest = {
|
||||
.x1 = state->crtc_x,
|
||||
.y1 = state->crtc_y,
|
||||
.x2 = state->crtc_x + state->crtc_w,
|
||||
.y2 = state->crtc_y + state->crtc_h,
|
||||
};
|
||||
|
||||
meson_vpp_setup_interlace_vscaler_osd1(priv, &dest);
|
||||
} else
|
||||
meson_vpp_disable_interlace_vscaler_osd1(priv);
|
||||
|
||||
/* Enable OSD1 */
|
||||
writel_bits_relaxed(VPP_OSD1_POSTBLEND, VPP_OSD1_POSTBLEND,
|
||||
priv->io_base + _REG(VPP_MISC));
|
||||
|
||||
priv->viu.osd1_commit = false;
|
||||
}
|
||||
|
||||
drm_crtc_handle_vblank(priv->crtc);
|
||||
|
||||
spin_lock_irqsave(&priv->drm->event_lock, flags);
|
||||
if (meson_crtc->event) {
|
||||
drm_crtc_send_vblank_event(priv->crtc, meson_crtc->event);
|
||||
drm_crtc_vblank_put(priv->crtc);
|
||||
meson_crtc->event = NULL;
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->drm->event_lock, flags);
|
||||
}
|
||||
|
||||
int meson_crtc_create(struct meson_drm *priv)
|
||||
{
|
||||
struct meson_crtc *meson_crtc;
|
||||
struct drm_crtc *crtc;
|
||||
int ret;
|
||||
|
||||
meson_crtc = devm_kzalloc(priv->drm->dev, sizeof(*meson_crtc),
|
||||
GFP_KERNEL);
|
||||
if (!meson_crtc)
|
||||
return -ENOMEM;
|
||||
|
||||
meson_crtc->priv = priv;
|
||||
crtc = &meson_crtc->base;
|
||||
ret = drm_crtc_init_with_planes(priv->drm, crtc,
|
||||
priv->primary_plane, NULL,
|
||||
&meson_crtc_funcs, "meson_crtc");
|
||||
if (ret) {
|
||||
dev_err(priv->drm->dev, "Failed to init CRTC\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_crtc_helper_add(crtc, &meson_crtc_helper_funcs);
|
||||
|
||||
priv->crtc = crtc;
|
||||
|
||||
return 0;
|
||||
}
|
32
drivers/gpu/drm/meson/meson_crtc.h
Normal file
32
drivers/gpu/drm/meson/meson_crtc.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Written by:
|
||||
* Jasper St. Pierre <jstpierre@mecheye.net>
|
||||
*/
|
||||
|
||||
#ifndef __MESON_CRTC_H
|
||||
#define __MESON_CRTC_H
|
||||
|
||||
#include "meson_drv.h"
|
||||
|
||||
int meson_crtc_create(struct meson_drm *priv);
|
||||
|
||||
void meson_crtc_irq(struct meson_drm *priv);
|
||||
|
||||
#endif /* __MESON_CRTC_H */
|
343
drivers/gpu/drm/meson/meson_drv.c
Normal file
343
drivers/gpu/drm/meson/meson_drv.c
Normal file
@ -0,0 +1,343 @@
|
||||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Written by:
|
||||
* Jasper St. Pierre <jstpierre@mecheye.net>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_graph.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_flip_work.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_rect.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
|
||||
#include "meson_drv.h"
|
||||
#include "meson_plane.h"
|
||||
#include "meson_crtc.h"
|
||||
#include "meson_venc_cvbs.h"
|
||||
|
||||
#include "meson_vpp.h"
|
||||
#include "meson_viu.h"
|
||||
#include "meson_venc.h"
|
||||
#include "meson_canvas.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
#define DRIVER_NAME "meson"
|
||||
#define DRIVER_DESC "Amlogic Meson DRM driver"
|
||||
|
||||
/*
|
||||
* Video Processing Unit
|
||||
*
|
||||
* VPU Handles the Global Video Processing, it includes management of the
|
||||
* clocks gates, blocks reset lines and power domains.
|
||||
*
|
||||
* What is missing :
|
||||
* - Full reset of entire video processing HW blocks
|
||||
* - Scaling and setup of the VPU clock
|
||||
* - Bus clock gates
|
||||
* - Powering up video processing HW blocks
|
||||
* - Powering Up HDMI controller and PHY
|
||||
*/
|
||||
|
||||
static void meson_fb_output_poll_changed(struct drm_device *dev)
|
||||
{
|
||||
struct meson_drm *priv = dev->dev_private;
|
||||
|
||||
drm_fbdev_cma_hotplug_event(priv->fbdev);
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_funcs meson_mode_config_funcs = {
|
||||
.output_poll_changed = meson_fb_output_poll_changed,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
.fb_create = drm_fb_cma_create,
|
||||
};
|
||||
|
||||
static int meson_enable_vblank(struct drm_device *dev, unsigned int crtc)
|
||||
{
|
||||
struct meson_drm *priv = dev->dev_private;
|
||||
|
||||
meson_venc_enable_vsync(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void meson_disable_vblank(struct drm_device *dev, unsigned int crtc)
|
||||
{
|
||||
struct meson_drm *priv = dev->dev_private;
|
||||
|
||||
meson_venc_disable_vsync(priv);
|
||||
}
|
||||
|
||||
static irqreturn_t meson_irq(int irq, void *arg)
|
||||
{
|
||||
struct drm_device *dev = arg;
|
||||
struct meson_drm *priv = dev->dev_private;
|
||||
|
||||
(void)readl_relaxed(priv->io_base + _REG(VENC_INTFLAG));
|
||||
|
||||
meson_crtc_irq(priv);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct file_operations fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
#endif
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = no_llseek,
|
||||
.mmap = drm_gem_cma_mmap,
|
||||
};
|
||||
|
||||
static struct drm_driver meson_driver = {
|
||||
.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM |
|
||||
DRIVER_MODESET | DRIVER_PRIME |
|
||||
DRIVER_ATOMIC,
|
||||
|
||||
/* Vblank */
|
||||
.enable_vblank = meson_enable_vblank,
|
||||
.disable_vblank = meson_disable_vblank,
|
||||
.get_vblank_counter = drm_vblank_no_hw_counter,
|
||||
|
||||
/* IRQ */
|
||||
.irq_handler = meson_irq,
|
||||
|
||||
/* PRIME Ops */
|
||||
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
|
||||
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
|
||||
.gem_prime_import = drm_gem_prime_import,
|
||||
.gem_prime_export = drm_gem_prime_export,
|
||||
.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
|
||||
.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
|
||||
.gem_prime_vmap = drm_gem_cma_prime_vmap,
|
||||
.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
|
||||
.gem_prime_mmap = drm_gem_cma_prime_mmap,
|
||||
|
||||
/* GEM Ops */
|
||||
.dumb_create = drm_gem_cma_dumb_create,
|
||||
.dumb_destroy = drm_gem_dumb_destroy,
|
||||
.dumb_map_offset = drm_gem_cma_dumb_map_offset,
|
||||
.gem_free_object_unlocked = drm_gem_cma_free_object,
|
||||
.gem_vm_ops = &drm_gem_cma_vm_ops,
|
||||
|
||||
/* Misc */
|
||||
.fops = &fops,
|
||||
.name = DRIVER_NAME,
|
||||
.desc = DRIVER_DESC,
|
||||
.date = "20161109",
|
||||
.major = 1,
|
||||
.minor = 0,
|
||||
};
|
||||
|
||||
static bool meson_vpu_has_available_connectors(struct device *dev)
|
||||
{
|
||||
struct device_node *ep, *remote;
|
||||
|
||||
/* Parses each endpoint and check if remote exists */
|
||||
for_each_endpoint_of_node(dev->of_node, ep) {
|
||||
/* If the endpoint node exists, consider it enabled */
|
||||
remote = of_graph_get_remote_port(ep);
|
||||
if (remote)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct regmap_config meson_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = 0x1000,
|
||||
};
|
||||
|
||||
static int meson_drv_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct meson_drm *priv;
|
||||
struct drm_device *drm;
|
||||
struct resource *res;
|
||||
void __iomem *regs;
|
||||
int ret;
|
||||
|
||||
/* Checks if an output connector is available */
|
||||
if (!meson_vpu_has_available_connectors(dev)) {
|
||||
dev_err(dev, "No output connector available\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
drm = drm_dev_alloc(&meson_driver, dev);
|
||||
if (IS_ERR(drm))
|
||||
return PTR_ERR(drm);
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
ret = -ENOMEM;
|
||||
goto free_drm;
|
||||
}
|
||||
drm->dev_private = priv;
|
||||
priv->drm = drm;
|
||||
priv->dev = dev;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu");
|
||||
regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
||||
priv->io_base = regs;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hhi");
|
||||
/* Simply ioremap since it may be a shared register zone */
|
||||
regs = devm_ioremap(dev, res->start, resource_size(res));
|
||||
if (!regs)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
priv->hhi = devm_regmap_init_mmio(dev, regs,
|
||||
&meson_regmap_config);
|
||||
if (IS_ERR(priv->hhi)) {
|
||||
dev_err(&pdev->dev, "Couldn't create the HHI regmap\n");
|
||||
return PTR_ERR(priv->hhi);
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmc");
|
||||
/* Simply ioremap since it may be a shared register zone */
|
||||
regs = devm_ioremap(dev, res->start, resource_size(res));
|
||||
if (!regs)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
priv->dmc = devm_regmap_init_mmio(dev, regs,
|
||||
&meson_regmap_config);
|
||||
if (IS_ERR(priv->dmc)) {
|
||||
dev_err(&pdev->dev, "Couldn't create the DMC regmap\n");
|
||||
return PTR_ERR(priv->dmc);
|
||||
}
|
||||
|
||||
priv->vsync_irq = platform_get_irq(pdev, 0);
|
||||
|
||||
drm_vblank_init(drm, 1);
|
||||
drm_mode_config_init(drm);
|
||||
|
||||
/* Encoder Initialization */
|
||||
|
||||
ret = meson_venc_cvbs_create(priv);
|
||||
if (ret)
|
||||
goto free_drm;
|
||||
|
||||
/* Hardware Initialization */
|
||||
|
||||
meson_venc_init(priv);
|
||||
meson_vpp_init(priv);
|
||||
meson_viu_init(priv);
|
||||
|
||||
ret = meson_plane_create(priv);
|
||||
if (ret)
|
||||
goto free_drm;
|
||||
|
||||
ret = meson_crtc_create(priv);
|
||||
if (ret)
|
||||
goto free_drm;
|
||||
|
||||
ret = drm_irq_install(drm, priv->vsync_irq);
|
||||
if (ret)
|
||||
goto free_drm;
|
||||
|
||||
drm_mode_config_reset(drm);
|
||||
drm->mode_config.max_width = 8192;
|
||||
drm->mode_config.max_height = 8192;
|
||||
drm->mode_config.funcs = &meson_mode_config_funcs;
|
||||
|
||||
priv->fbdev = drm_fbdev_cma_init(drm, 32,
|
||||
drm->mode_config.num_crtc,
|
||||
drm->mode_config.num_connector);
|
||||
if (IS_ERR(priv->fbdev)) {
|
||||
ret = PTR_ERR(priv->fbdev);
|
||||
goto free_drm;
|
||||
}
|
||||
|
||||
drm_kms_helper_poll_init(drm);
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
ret = drm_dev_register(drm, 0);
|
||||
if (ret)
|
||||
goto free_drm;
|
||||
|
||||
return 0;
|
||||
|
||||
free_drm:
|
||||
drm_dev_unref(drm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int meson_drv_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct drm_device *drm = dev_get_drvdata(&pdev->dev);
|
||||
struct meson_drm *priv = drm->dev_private;
|
||||
|
||||
drm_dev_unregister(drm);
|
||||
drm_kms_helper_poll_fini(drm);
|
||||
drm_fbdev_cma_fini(priv->fbdev);
|
||||
drm_mode_config_cleanup(drm);
|
||||
drm_vblank_cleanup(drm);
|
||||
drm_dev_unref(drm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id dt_match[] = {
|
||||
{ .compatible = "amlogic,meson-gxbb-vpu" },
|
||||
{ .compatible = "amlogic,meson-gxl-vpu" },
|
||||
{ .compatible = "amlogic,meson-gxm-vpu" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dt_match);
|
||||
|
||||
static struct platform_driver meson_drm_platform_driver = {
|
||||
.probe = meson_drv_probe,
|
||||
.remove = meson_drv_remove,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = dt_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(meson_drm_platform_driver);
|
||||
|
||||
MODULE_AUTHOR("Jasper St. Pierre <jstpierre@mecheye.net>");
|
||||
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
59
drivers/gpu/drm/meson/meson_drv.h
Normal file
59
drivers/gpu/drm/meson/meson_drv.h
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MESON_DRV_H
|
||||
#define __MESON_DRV_H
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of.h>
|
||||
#include <drm/drmP.h>
|
||||
|
||||
struct meson_drm {
|
||||
struct device *dev;
|
||||
void __iomem *io_base;
|
||||
struct regmap *hhi;
|
||||
struct regmap *dmc;
|
||||
int vsync_irq;
|
||||
|
||||
struct drm_device *drm;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_fbdev_cma *fbdev;
|
||||
struct drm_plane *primary_plane;
|
||||
|
||||
/* Components Data */
|
||||
struct {
|
||||
bool osd1_enabled;
|
||||
bool osd1_interlace;
|
||||
bool osd1_commit;
|
||||
uint32_t osd1_ctrl_stat;
|
||||
uint32_t osd1_blk0_cfg[5];
|
||||
} viu;
|
||||
|
||||
struct {
|
||||
unsigned int current_mode;
|
||||
} venc;
|
||||
};
|
||||
|
||||
static inline int meson_vpu_is_compatible(struct meson_drm *priv,
|
||||
const char *compat)
|
||||
{
|
||||
return of_device_is_compatible(priv->dev->of_node, compat);
|
||||
}
|
||||
|
||||
#endif /* __MESON_DRV_H */
|
230
drivers/gpu/drm/meson/meson_plane.c
Normal file
230
drivers/gpu/drm/meson/meson_plane.c
Normal file
@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Written by:
|
||||
* Jasper St. Pierre <jstpierre@mecheye.net>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_rect.h>
|
||||
|
||||
#include "meson_plane.h"
|
||||
#include "meson_vpp.h"
|
||||
#include "meson_viu.h"
|
||||
#include "meson_canvas.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
struct meson_plane {
|
||||
struct drm_plane base;
|
||||
struct meson_drm *priv;
|
||||
};
|
||||
#define to_meson_plane(x) container_of(x, struct meson_plane, base)
|
||||
|
||||
static int meson_plane_atomic_check(struct drm_plane *plane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct drm_crtc_state *crtc_state;
|
||||
struct drm_rect clip = { 0, };
|
||||
|
||||
crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
|
||||
if (IS_ERR(crtc_state))
|
||||
return PTR_ERR(crtc_state);
|
||||
|
||||
clip.x2 = crtc_state->mode.hdisplay;
|
||||
clip.y2 = crtc_state->mode.vdisplay;
|
||||
|
||||
return drm_plane_helper_check_state(state, &clip,
|
||||
DRM_PLANE_HELPER_NO_SCALING,
|
||||
DRM_PLANE_HELPER_NO_SCALING,
|
||||
true, true);
|
||||
}
|
||||
|
||||
/* Takes a fixed 16.16 number and converts it to integer. */
|
||||
static inline int64_t fixed16_to_int(int64_t value)
|
||||
{
|
||||
return value >> 16;
|
||||
}
|
||||
|
||||
static void meson_plane_atomic_update(struct drm_plane *plane,
|
||||
struct drm_plane_state *old_state)
|
||||
{
|
||||
struct meson_plane *meson_plane = to_meson_plane(plane);
|
||||
struct drm_plane_state *state = plane->state;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
struct meson_drm *priv = meson_plane->priv;
|
||||
struct drm_gem_cma_object *gem;
|
||||
struct drm_rect src = {
|
||||
.x1 = (state->src_x),
|
||||
.y1 = (state->src_y),
|
||||
.x2 = (state->src_x + state->src_w),
|
||||
.y2 = (state->src_y + state->src_h),
|
||||
};
|
||||
struct drm_rect dest = {
|
||||
.x1 = state->crtc_x,
|
||||
.y1 = state->crtc_y,
|
||||
.x2 = state->crtc_x + state->crtc_w,
|
||||
.y2 = state->crtc_y + state->crtc_h,
|
||||
};
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* Update Coordinates
|
||||
* Update Formats
|
||||
* Update Buffer
|
||||
* Enable Plane
|
||||
*/
|
||||
spin_lock_irqsave(&priv->drm->event_lock, flags);
|
||||
|
||||
/* Enable OSD and BLK0, set max global alpha */
|
||||
priv->viu.osd1_ctrl_stat = OSD_ENABLE |
|
||||
(0xFF << OSD_GLOBAL_ALPHA_SHIFT) |
|
||||
OSD_BLK0_ENABLE;
|
||||
|
||||
/* Set up BLK0 to point to the right canvas */
|
||||
priv->viu.osd1_blk0_cfg[0] = ((MESON_CANVAS_ID_OSD1 << OSD_CANVAS_SEL) |
|
||||
OSD_ENDIANNESS_LE);
|
||||
|
||||
/* On GXBB, Use the old non-HDR RGB2YUV converter */
|
||||
if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu"))
|
||||
priv->viu.osd1_blk0_cfg[0] |= OSD_OUTPUT_COLOR_RGB;
|
||||
|
||||
switch (fb->pixel_format) {
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
/* For XRGB, replace the pixel's alpha by 0xFF */
|
||||
writel_bits_relaxed(OSD_REPLACE_EN, OSD_REPLACE_EN,
|
||||
priv->io_base + _REG(VIU_OSD1_CTRL_STAT2));
|
||||
priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_32 |
|
||||
OSD_COLOR_MATRIX_32_ARGB;
|
||||
break;
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
/* For ARGB, use the pixel's alpha */
|
||||
writel_bits_relaxed(OSD_REPLACE_EN, 0,
|
||||
priv->io_base + _REG(VIU_OSD1_CTRL_STAT2));
|
||||
priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_32 |
|
||||
OSD_COLOR_MATRIX_32_ARGB;
|
||||
break;
|
||||
case DRM_FORMAT_RGB888:
|
||||
priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_24 |
|
||||
OSD_COLOR_MATRIX_24_RGB;
|
||||
break;
|
||||
case DRM_FORMAT_RGB565:
|
||||
priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_16 |
|
||||
OSD_COLOR_MATRIX_16_RGB565;
|
||||
break;
|
||||
};
|
||||
|
||||
if (state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) {
|
||||
priv->viu.osd1_interlace = true;
|
||||
|
||||
dest.y1 /= 2;
|
||||
dest.y2 /= 2;
|
||||
} else
|
||||
priv->viu.osd1_interlace = false;
|
||||
|
||||
/*
|
||||
* The format of these registers is (x2 << 16 | x1),
|
||||
* where x2 is exclusive.
|
||||
* e.g. +30x1920 would be (1919 << 16) | 30
|
||||
*/
|
||||
priv->viu.osd1_blk0_cfg[1] = ((fixed16_to_int(src.x2) - 1) << 16) |
|
||||
fixed16_to_int(src.x1);
|
||||
priv->viu.osd1_blk0_cfg[2] = ((fixed16_to_int(src.y2) - 1) << 16) |
|
||||
fixed16_to_int(src.y1);
|
||||
priv->viu.osd1_blk0_cfg[3] = ((dest.x2 - 1) << 16) | dest.x1;
|
||||
priv->viu.osd1_blk0_cfg[4] = ((dest.y2 - 1) << 16) | dest.y1;
|
||||
|
||||
/* Update Canvas with buffer address */
|
||||
gem = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
|
||||
meson_canvas_setup(priv, MESON_CANVAS_ID_OSD1,
|
||||
gem->paddr, fb->pitches[0],
|
||||
fb->height, MESON_CANVAS_WRAP_NONE,
|
||||
MESON_CANVAS_BLKMODE_LINEAR);
|
||||
|
||||
spin_unlock_irqrestore(&priv->drm->event_lock, flags);
|
||||
}
|
||||
|
||||
static void meson_plane_atomic_disable(struct drm_plane *plane,
|
||||
struct drm_plane_state *old_state)
|
||||
{
|
||||
struct meson_plane *meson_plane = to_meson_plane(plane);
|
||||
struct meson_drm *priv = meson_plane->priv;
|
||||
|
||||
/* Disable OSD1 */
|
||||
writel_bits_relaxed(VPP_OSD1_POSTBLEND, 0,
|
||||
priv->io_base + _REG(VPP_MISC));
|
||||
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs meson_plane_helper_funcs = {
|
||||
.atomic_check = meson_plane_atomic_check,
|
||||
.atomic_disable = meson_plane_atomic_disable,
|
||||
.atomic_update = meson_plane_atomic_update,
|
||||
};
|
||||
|
||||
static const struct drm_plane_funcs meson_plane_funcs = {
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
.destroy = drm_plane_cleanup,
|
||||
.reset = drm_atomic_helper_plane_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
|
||||
};
|
||||
|
||||
static const uint32_t supported_drm_formats[] = {
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_RGB888,
|
||||
DRM_FORMAT_RGB565,
|
||||
};
|
||||
|
||||
int meson_plane_create(struct meson_drm *priv)
|
||||
{
|
||||
struct meson_plane *meson_plane;
|
||||
struct drm_plane *plane;
|
||||
|
||||
meson_plane = devm_kzalloc(priv->drm->dev, sizeof(*meson_plane),
|
||||
GFP_KERNEL);
|
||||
if (!meson_plane)
|
||||
return -ENOMEM;
|
||||
|
||||
meson_plane->priv = priv;
|
||||
plane = &meson_plane->base;
|
||||
|
||||
drm_universal_plane_init(priv->drm, plane, 0xFF,
|
||||
&meson_plane_funcs,
|
||||
supported_drm_formats,
|
||||
ARRAY_SIZE(supported_drm_formats),
|
||||
DRM_PLANE_TYPE_PRIMARY, "meson_primary_plane");
|
||||
|
||||
drm_plane_helper_add(plane, &meson_plane_helper_funcs);
|
||||
|
||||
priv->primary_plane = plane;
|
||||
|
||||
return 0;
|
||||
}
|
30
drivers/gpu/drm/meson/meson_plane.h
Normal file
30
drivers/gpu/drm/meson/meson_plane.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Written by:
|
||||
* Jasper St. Pierre <jstpierre@mecheye.net>
|
||||
*/
|
||||
|
||||
#ifndef __MESON_PLANE_H
|
||||
#define __MESON_PLANE_H
|
||||
|
||||
#include "meson_drv.h"
|
||||
|
||||
int meson_plane_create(struct meson_drm *priv);
|
||||
|
||||
#endif /* __MESON_PLANE_H */
|
1395
drivers/gpu/drm/meson/meson_registers.h
Normal file
1395
drivers/gpu/drm/meson/meson_registers.h
Normal file
File diff suppressed because it is too large
Load Diff
167
drivers/gpu/drm/meson/meson_vclk.c
Normal file
167
drivers/gpu/drm/meson/meson_vclk.c
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <drm/drmP.h>
|
||||
#include "meson_drv.h"
|
||||
#include "meson_vclk.h"
|
||||
|
||||
/*
|
||||
* VCLK is the "Pixel Clock" frequency generator from a dedicated PLL.
|
||||
* We handle the following encodings :
|
||||
* - CVBS 27MHz generator via the VCLK2 to the VENCI and VDAC blocks
|
||||
*
|
||||
* What is missing :
|
||||
* - HDMI Pixel Clocks generation
|
||||
*/
|
||||
|
||||
/* HHI Registers */
|
||||
#define HHI_VID_PLL_CLK_DIV 0x1a0 /* 0x68 offset in data sheet */
|
||||
#define VID_PLL_EN BIT(19)
|
||||
#define VID_PLL_BYPASS BIT(18)
|
||||
#define VID_PLL_PRESET BIT(15)
|
||||
#define HHI_VIID_CLK_DIV 0x128 /* 0x4a offset in data sheet */
|
||||
#define VCLK2_DIV_MASK 0xff
|
||||
#define VCLK2_DIV_EN BIT(16)
|
||||
#define VCLK2_DIV_RESET BIT(17)
|
||||
#define CTS_VDAC_SEL_MASK (0xf << 28)
|
||||
#define CTS_VDAC_SEL_SHIFT 28
|
||||
#define HHI_VIID_CLK_CNTL 0x12c /* 0x4b offset in data sheet */
|
||||
#define VCLK2_EN BIT(19)
|
||||
#define VCLK2_SEL_MASK (0x7 << 16)
|
||||
#define VCLK2_SEL_SHIFT 16
|
||||
#define VCLK2_SOFT_RESET BIT(15)
|
||||
#define VCLK2_DIV1_EN BIT(0)
|
||||
#define HHI_VID_CLK_DIV 0x164 /* 0x59 offset in data sheet */
|
||||
#define CTS_ENCI_SEL_MASK (0xf << 28)
|
||||
#define CTS_ENCI_SEL_SHIFT 28
|
||||
#define HHI_VID_CLK_CNTL2 0x194 /* 0x65 offset in data sheet */
|
||||
#define CTS_ENCI_EN BIT(0)
|
||||
#define CTS_VDAC_EN BIT(4)
|
||||
|
||||
#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */
|
||||
#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */
|
||||
|
||||
#define HHI_HDMI_PLL_CNTL 0x320 /* 0xc8 offset in data sheet */
|
||||
#define HHI_HDMI_PLL_CNTL2 0x324 /* 0xc9 offset in data sheet */
|
||||
#define HHI_HDMI_PLL_CNTL3 0x328 /* 0xca offset in data sheet */
|
||||
#define HHI_HDMI_PLL_CNTL4 0x32C /* 0xcb offset in data sheet */
|
||||
#define HHI_HDMI_PLL_CNTL5 0x330 /* 0xcc offset in data sheet */
|
||||
#define HHI_HDMI_PLL_CNTL6 0x334 /* 0xcd offset in data sheet */
|
||||
|
||||
#define HDMI_PLL_RESET BIT(28)
|
||||
#define HDMI_PLL_LOCK BIT(31)
|
||||
|
||||
/*
|
||||
* Setup VCLK2 for 27MHz, and enable clocks for ENCI and VDAC
|
||||
*
|
||||
* TOFIX: Refactor into table to also handle HDMI frequency and paths
|
||||
*/
|
||||
static void meson_venci_cvbs_clock_config(struct meson_drm *priv)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
/* Setup PLL to output 1.485GHz */
|
||||
if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) {
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800023d);
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00404e00);
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091);
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c);
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980);
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55);
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x4800023d);
|
||||
} else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
|
||||
meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) {
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x4000027b);
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb300);
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0xa6212844);
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c4d000c);
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729);
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500);
|
||||
|
||||
/* Reset PLL */
|
||||
regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
|
||||
HDMI_PLL_RESET, HDMI_PLL_RESET);
|
||||
regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
|
||||
HDMI_PLL_RESET, 0);
|
||||
}
|
||||
|
||||
/* Poll for lock bit */
|
||||
regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, val,
|
||||
(val & HDMI_PLL_LOCK), 10, 0);
|
||||
|
||||
/* Disable VCLK2 */
|
||||
regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL, VCLK2_EN, 0);
|
||||
|
||||
/* Disable vid_pll output clock */
|
||||
regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, VID_PLL_EN, 0);
|
||||
regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, VID_PLL_PRESET, 0);
|
||||
/* Enable vid_pll bypass to HDMI pll */
|
||||
regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV,
|
||||
VID_PLL_BYPASS, VID_PLL_BYPASS);
|
||||
/* Enable the vid_pll output clock */
|
||||
regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV,
|
||||
VID_PLL_EN, VID_PLL_EN);
|
||||
|
||||
/* Setup the VCLK2 divider value to achieve 27MHz */
|
||||
regmap_update_bits(priv->hhi, HHI_VIID_CLK_DIV,
|
||||
VCLK2_DIV_MASK, (55 - 1));
|
||||
|
||||
/* select vid_pll for vclk2 */
|
||||
regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL,
|
||||
VCLK2_SEL_MASK, (4 << VCLK2_SEL_SHIFT));
|
||||
/* enable vclk2 gate */
|
||||
regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL, VCLK2_EN, VCLK2_EN);
|
||||
|
||||
/* select vclk_div1 for enci */
|
||||
regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV,
|
||||
CTS_ENCI_SEL_MASK, (8 << CTS_ENCI_SEL_SHIFT));
|
||||
/* select vclk_div1 for vdac */
|
||||
regmap_update_bits(priv->hhi, HHI_VIID_CLK_DIV,
|
||||
CTS_VDAC_SEL_MASK, (8 << CTS_VDAC_SEL_SHIFT));
|
||||
|
||||
/* release vclk2_div_reset and enable vclk2_div */
|
||||
regmap_update_bits(priv->hhi, HHI_VIID_CLK_DIV,
|
||||
VCLK2_DIV_EN | VCLK2_DIV_RESET, VCLK2_DIV_EN);
|
||||
|
||||
/* enable vclk2_div1 gate */
|
||||
regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL,
|
||||
VCLK2_DIV1_EN, VCLK2_DIV1_EN);
|
||||
|
||||
/* reset vclk2 */
|
||||
regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL,
|
||||
VCLK2_SOFT_RESET, VCLK2_SOFT_RESET);
|
||||
regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL,
|
||||
VCLK2_SOFT_RESET, 0);
|
||||
|
||||
/* enable enci_clk */
|
||||
regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL2,
|
||||
CTS_ENCI_EN, CTS_ENCI_EN);
|
||||
/* enable vdac_clk */
|
||||
regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL2,
|
||||
CTS_VDAC_EN, CTS_VDAC_EN);
|
||||
}
|
||||
|
||||
void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
|
||||
unsigned int freq)
|
||||
{
|
||||
if (target == MESON_VCLK_TARGET_CVBS && freq == MESON_VCLK_CVBS)
|
||||
meson_venci_cvbs_clock_config(priv);
|
||||
}
|
34
drivers/gpu/drm/meson/meson_vclk.h
Normal file
34
drivers/gpu/drm/meson/meson_vclk.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Video Clock */
|
||||
|
||||
#ifndef __MESON_VCLK_H
|
||||
#define __MESON_VCLK_H
|
||||
|
||||
enum {
|
||||
MESON_VCLK_TARGET_CVBS = 0,
|
||||
};
|
||||
|
||||
/* 27MHz is the CVBS Pixel Clock */
|
||||
#define MESON_VCLK_CVBS 27000
|
||||
|
||||
void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
|
||||
unsigned int freq);
|
||||
|
||||
#endif /* __MESON_VCLK_H */
|
254
drivers/gpu/drm/meson/meson_venc.c
Normal file
254
drivers/gpu/drm/meson/meson_venc.c
Normal file
@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <drm/drmP.h>
|
||||
#include "meson_drv.h"
|
||||
#include "meson_venc.h"
|
||||
#include "meson_vpp.h"
|
||||
#include "meson_vclk.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
/*
|
||||
* VENC Handle the pixels encoding to the output formats.
|
||||
* We handle the following encodings :
|
||||
* - CVBS Encoding via the ENCI encoder and VDAC digital to analog converter
|
||||
*
|
||||
* What is missing :
|
||||
* - TMDS/HDMI Encoding via ENCI_DIV and ENCP
|
||||
* - Setup of more clock rates for HDMI modes
|
||||
* - LCD Panel encoding via ENCL
|
||||
* - TV Panel encoding via ENCT
|
||||
*/
|
||||
|
||||
struct meson_cvbs_enci_mode meson_cvbs_enci_pal = {
|
||||
.mode_tag = MESON_VENC_MODE_CVBS_PAL,
|
||||
.hso_begin = 3,
|
||||
.hso_end = 129,
|
||||
.vso_even = 3,
|
||||
.vso_odd = 260,
|
||||
.macv_max_amp = 7,
|
||||
.video_prog_mode = 0xff,
|
||||
.video_mode = 0x13,
|
||||
.sch_adjust = 0x28,
|
||||
.yc_delay = 0x343,
|
||||
.pixel_start = 251,
|
||||
.pixel_end = 1691,
|
||||
.top_field_line_start = 22,
|
||||
.top_field_line_end = 310,
|
||||
.bottom_field_line_start = 23,
|
||||
.bottom_field_line_end = 311,
|
||||
.video_saturation = 9,
|
||||
.video_contrast = 0,
|
||||
.video_brightness = 0,
|
||||
.video_hue = 0,
|
||||
.analog_sync_adj = 0x8080,
|
||||
};
|
||||
|
||||
struct meson_cvbs_enci_mode meson_cvbs_enci_ntsc = {
|
||||
.mode_tag = MESON_VENC_MODE_CVBS_NTSC,
|
||||
.hso_begin = 5,
|
||||
.hso_end = 129,
|
||||
.vso_even = 3,
|
||||
.vso_odd = 260,
|
||||
.macv_max_amp = 0xb,
|
||||
.video_prog_mode = 0xf0,
|
||||
.video_mode = 0x8,
|
||||
.sch_adjust = 0x20,
|
||||
.yc_delay = 0x333,
|
||||
.pixel_start = 227,
|
||||
.pixel_end = 1667,
|
||||
.top_field_line_start = 18,
|
||||
.top_field_line_end = 258,
|
||||
.bottom_field_line_start = 19,
|
||||
.bottom_field_line_end = 259,
|
||||
.video_saturation = 18,
|
||||
.video_contrast = 3,
|
||||
.video_brightness = 0,
|
||||
.video_hue = 0,
|
||||
.analog_sync_adj = 0x9c00,
|
||||
};
|
||||
|
||||
void meson_venci_cvbs_mode_set(struct meson_drm *priv,
|
||||
struct meson_cvbs_enci_mode *mode)
|
||||
{
|
||||
if (mode->mode_tag == priv->venc.current_mode)
|
||||
return;
|
||||
|
||||
/* CVBS Filter settings */
|
||||
writel_relaxed(0x12, priv->io_base + _REG(ENCI_CFILT_CTRL));
|
||||
writel_relaxed(0x12, priv->io_base + _REG(ENCI_CFILT_CTRL2));
|
||||
|
||||
/* Digital Video Select : Interlace, clk27 clk, external */
|
||||
writel_relaxed(0, priv->io_base + _REG(VENC_DVI_SETTING));
|
||||
|
||||
/* Reset Video Mode */
|
||||
writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_MODE));
|
||||
writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_MODE_ADV));
|
||||
|
||||
/* Horizontal sync signal output */
|
||||
writel_relaxed(mode->hso_begin,
|
||||
priv->io_base + _REG(ENCI_SYNC_HSO_BEGIN));
|
||||
writel_relaxed(mode->hso_end,
|
||||
priv->io_base + _REG(ENCI_SYNC_HSO_END));
|
||||
|
||||
/* Vertical Sync lines */
|
||||
writel_relaxed(mode->vso_even,
|
||||
priv->io_base + _REG(ENCI_SYNC_VSO_EVNLN));
|
||||
writel_relaxed(mode->vso_odd,
|
||||
priv->io_base + _REG(ENCI_SYNC_VSO_ODDLN));
|
||||
|
||||
/* Macrovision max amplitude change */
|
||||
writel_relaxed(0x8100 + mode->macv_max_amp,
|
||||
priv->io_base + _REG(ENCI_MACV_MAX_AMP));
|
||||
|
||||
/* Video mode */
|
||||
writel_relaxed(mode->video_prog_mode,
|
||||
priv->io_base + _REG(VENC_VIDEO_PROG_MODE));
|
||||
writel_relaxed(mode->video_mode,
|
||||
priv->io_base + _REG(ENCI_VIDEO_MODE));
|
||||
|
||||
/* Advanced Video Mode :
|
||||
* Demux shifting 0x2
|
||||
* Blank line end at line17/22
|
||||
* High bandwidth Luma Filter
|
||||
* Low bandwidth Chroma Filter
|
||||
* Bypass luma low pass filter
|
||||
* No macrovision on CSYNC
|
||||
*/
|
||||
writel_relaxed(0x26, priv->io_base + _REG(ENCI_VIDEO_MODE_ADV));
|
||||
|
||||
writel(mode->sch_adjust, priv->io_base + _REG(ENCI_VIDEO_SCH));
|
||||
|
||||
/* Sync mode : MASTER Master mode, free run, send HSO/VSO out */
|
||||
writel_relaxed(0x07, priv->io_base + _REG(ENCI_SYNC_MODE));
|
||||
|
||||
/* 0x3 Y, C, and Component Y delay */
|
||||
writel_relaxed(mode->yc_delay, priv->io_base + _REG(ENCI_YC_DELAY));
|
||||
|
||||
/* Timings */
|
||||
writel_relaxed(mode->pixel_start,
|
||||
priv->io_base + _REG(ENCI_VFIFO2VD_PIXEL_START));
|
||||
writel_relaxed(mode->pixel_end,
|
||||
priv->io_base + _REG(ENCI_VFIFO2VD_PIXEL_END));
|
||||
|
||||
writel_relaxed(mode->top_field_line_start,
|
||||
priv->io_base + _REG(ENCI_VFIFO2VD_LINE_TOP_START));
|
||||
writel_relaxed(mode->top_field_line_end,
|
||||
priv->io_base + _REG(ENCI_VFIFO2VD_LINE_TOP_END));
|
||||
|
||||
writel_relaxed(mode->bottom_field_line_start,
|
||||
priv->io_base + _REG(ENCI_VFIFO2VD_LINE_BOT_START));
|
||||
writel_relaxed(mode->bottom_field_line_end,
|
||||
priv->io_base + _REG(ENCI_VFIFO2VD_LINE_BOT_END));
|
||||
|
||||
/* Internal Venc, Internal VIU Sync, Internal Vencoder */
|
||||
writel_relaxed(0, priv->io_base + _REG(VENC_SYNC_ROUTE));
|
||||
|
||||
/* UNreset Interlaced TV Encoder */
|
||||
writel_relaxed(0, priv->io_base + _REG(ENCI_DBG_PX_RST));
|
||||
|
||||
/* Enable Vfifo2vd, Y_Cb_Y_Cr select */
|
||||
writel_relaxed(0x4e01, priv->io_base + _REG(ENCI_VFIFO2VD_CTL));
|
||||
|
||||
/* Power UP Dacs */
|
||||
writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_SETTING));
|
||||
|
||||
/* Video Upsampling */
|
||||
writel_relaxed(0x0061, priv->io_base + _REG(VENC_UPSAMPLE_CTRL0));
|
||||
writel_relaxed(0x4061, priv->io_base + _REG(VENC_UPSAMPLE_CTRL1));
|
||||
writel_relaxed(0x5061, priv->io_base + _REG(VENC_UPSAMPLE_CTRL2));
|
||||
|
||||
/* Select Interlace Y DACs */
|
||||
writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL0));
|
||||
writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL1));
|
||||
writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL2));
|
||||
writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL3));
|
||||
writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL4));
|
||||
writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL5));
|
||||
|
||||
/* Select ENCI for VIU */
|
||||
meson_vpp_setup_mux(priv, MESON_VIU_VPP_MUX_ENCI);
|
||||
|
||||
/* Enable ENCI FIFO */
|
||||
writel_relaxed(0x2000, priv->io_base + _REG(VENC_VDAC_FIFO_CTRL));
|
||||
|
||||
/* Select ENCI DACs 0, 1, 4, and 5 */
|
||||
writel_relaxed(0x11, priv->io_base + _REG(ENCI_DACSEL_0));
|
||||
writel_relaxed(0x11, priv->io_base + _REG(ENCI_DACSEL_1));
|
||||
|
||||
/* Interlace video enable */
|
||||
writel_relaxed(1, priv->io_base + _REG(ENCI_VIDEO_EN));
|
||||
|
||||
/* Configure Video Saturation / Contrast / Brightness / Hue */
|
||||
writel_relaxed(mode->video_saturation,
|
||||
priv->io_base + _REG(ENCI_VIDEO_SAT));
|
||||
writel_relaxed(mode->video_contrast,
|
||||
priv->io_base + _REG(ENCI_VIDEO_CONT));
|
||||
writel_relaxed(mode->video_brightness,
|
||||
priv->io_base + _REG(ENCI_VIDEO_BRIGHT));
|
||||
writel_relaxed(mode->video_hue,
|
||||
priv->io_base + _REG(ENCI_VIDEO_HUE));
|
||||
|
||||
/* Enable DAC0 Filter */
|
||||
writel_relaxed(0x1, priv->io_base + _REG(VENC_VDAC_DAC0_FILT_CTRL0));
|
||||
writel_relaxed(0xfc48, priv->io_base + _REG(VENC_VDAC_DAC0_FILT_CTRL1));
|
||||
|
||||
/* 0 in Macrovision register 0 */
|
||||
writel_relaxed(0, priv->io_base + _REG(ENCI_MACV_N0));
|
||||
|
||||
/* Analog Synchronization and color burst value adjust */
|
||||
writel_relaxed(mode->analog_sync_adj,
|
||||
priv->io_base + _REG(ENCI_SYNC_ADJ));
|
||||
|
||||
/* Setup 27MHz vclk2 for ENCI and VDAC */
|
||||
meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS, MESON_VCLK_CVBS);
|
||||
|
||||
priv->venc.current_mode = mode->mode_tag;
|
||||
}
|
||||
|
||||
/* Returns the current ENCI field polarity */
|
||||
unsigned int meson_venci_get_field(struct meson_drm *priv)
|
||||
{
|
||||
return readl_relaxed(priv->io_base + _REG(ENCI_INFO_READ)) & BIT(29);
|
||||
}
|
||||
|
||||
void meson_venc_enable_vsync(struct meson_drm *priv)
|
||||
{
|
||||
writel_relaxed(2, priv->io_base + _REG(VENC_INTCTRL));
|
||||
}
|
||||
|
||||
void meson_venc_disable_vsync(struct meson_drm *priv)
|
||||
{
|
||||
writel_relaxed(0, priv->io_base + _REG(VENC_INTCTRL));
|
||||
}
|
||||
|
||||
void meson_venc_init(struct meson_drm *priv)
|
||||
{
|
||||
/* Disable all encoders */
|
||||
writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_EN));
|
||||
writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN));
|
||||
writel_relaxed(0, priv->io_base + _REG(ENCL_VIDEO_EN));
|
||||
|
||||
/* Disable VSync IRQ */
|
||||
meson_venc_disable_vsync(priv);
|
||||
|
||||
priv->venc.current_mode = MESON_VENC_MODE_NONE;
|
||||
}
|
72
drivers/gpu/drm/meson/meson_venc.h
Normal file
72
drivers/gpu/drm/meson/meson_venc.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Video Encoders
|
||||
* - ENCI : Interlace Video Encoder
|
||||
* - ENCI_DVI : Interlace Video Encoder for DVI/HDMI
|
||||
* - ENCP : Progressive Video Encoder
|
||||
*/
|
||||
|
||||
#ifndef __MESON_VENC_H
|
||||
#define __MESON_VENC_H
|
||||
|
||||
enum {
|
||||
MESON_VENC_MODE_NONE = 0,
|
||||
MESON_VENC_MODE_CVBS_PAL,
|
||||
MESON_VENC_MODE_CVBS_NTSC,
|
||||
};
|
||||
|
||||
struct meson_cvbs_enci_mode {
|
||||
unsigned int mode_tag;
|
||||
unsigned int hso_begin; /* HSO begin position */
|
||||
unsigned int hso_end; /* HSO end position */
|
||||
unsigned int vso_even; /* VSO even line */
|
||||
unsigned int vso_odd; /* VSO odd line */
|
||||
unsigned int macv_max_amp; /* Macrovision max amplitude */
|
||||
unsigned int video_prog_mode;
|
||||
unsigned int video_mode;
|
||||
unsigned int sch_adjust;
|
||||
unsigned int yc_delay;
|
||||
unsigned int pixel_start;
|
||||
unsigned int pixel_end;
|
||||
unsigned int top_field_line_start;
|
||||
unsigned int top_field_line_end;
|
||||
unsigned int bottom_field_line_start;
|
||||
unsigned int bottom_field_line_end;
|
||||
unsigned int video_saturation;
|
||||
unsigned int video_contrast;
|
||||
unsigned int video_brightness;
|
||||
unsigned int video_hue;
|
||||
unsigned int analog_sync_adj;
|
||||
};
|
||||
|
||||
/* CVBS Timings and Parameters */
|
||||
extern struct meson_cvbs_enci_mode meson_cvbs_enci_pal;
|
||||
extern struct meson_cvbs_enci_mode meson_cvbs_enci_ntsc;
|
||||
|
||||
void meson_venci_cvbs_mode_set(struct meson_drm *priv,
|
||||
struct meson_cvbs_enci_mode *mode);
|
||||
unsigned int meson_venci_get_field(struct meson_drm *priv);
|
||||
|
||||
void meson_venc_enable_vsync(struct meson_drm *priv);
|
||||
void meson_venc_disable_vsync(struct meson_drm *priv);
|
||||
|
||||
void meson_venc_init(struct meson_drm *priv);
|
||||
|
||||
#endif /* __MESON_VENC_H */
|
293
drivers/gpu/drm/meson/meson_venc_cvbs.c
Normal file
293
drivers/gpu/drm/meson/meson_venc_cvbs.c
Normal file
@ -0,0 +1,293 @@
|
||||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Written by:
|
||||
* Jasper St. Pierre <jstpierre@mecheye.net>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_graph.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
|
||||
#include "meson_venc_cvbs.h"
|
||||
#include "meson_venc.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
/* HHI VDAC Registers */
|
||||
#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */
|
||||
#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */
|
||||
|
||||
struct meson_venc_cvbs {
|
||||
struct drm_encoder encoder;
|
||||
struct drm_connector connector;
|
||||
struct meson_drm *priv;
|
||||
};
|
||||
#define encoder_to_meson_venc_cvbs(x) \
|
||||
container_of(x, struct meson_venc_cvbs, encoder)
|
||||
|
||||
#define connector_to_meson_venc_cvbs(x) \
|
||||
container_of(x, struct meson_venc_cvbs, connector)
|
||||
|
||||
/* Supported Modes */
|
||||
|
||||
struct meson_cvbs_mode meson_cvbs_modes[MESON_CVBS_MODES_COUNT] = {
|
||||
{ /* PAL */
|
||||
.enci = &meson_cvbs_enci_pal,
|
||||
.mode = {
|
||||
DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500,
|
||||
720, 732, 795, 864, 0, 576, 580, 586, 625, 0,
|
||||
DRM_MODE_FLAG_INTERLACE),
|
||||
.vrefresh = 50,
|
||||
.picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
|
||||
},
|
||||
},
|
||||
{ /* NTSC */
|
||||
.enci = &meson_cvbs_enci_ntsc,
|
||||
.mode = {
|
||||
DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500,
|
||||
720, 739, 801, 858, 0, 480, 488, 494, 525, 0,
|
||||
DRM_MODE_FLAG_INTERLACE),
|
||||
.vrefresh = 60,
|
||||
.picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/* Connector */
|
||||
|
||||
static void meson_cvbs_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
meson_cvbs_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
/* FIXME: Add load-detect or jack-detect if possible */
|
||||
return connector_status_connected;
|
||||
}
|
||||
|
||||
static int meson_cvbs_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_display_mode *mode;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) {
|
||||
struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i];
|
||||
|
||||
mode = drm_mode_duplicate(dev, &meson_mode->mode);
|
||||
if (!mode) {
|
||||
DRM_ERROR("Failed to create a new display mode\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
drm_mode_probed_add(connector, mode);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int meson_cvbs_connector_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
/* Validate the modes added in get_modes */
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs meson_cvbs_connector_funcs = {
|
||||
.dpms = drm_atomic_helper_connector_dpms,
|
||||
.detect = meson_cvbs_connector_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = meson_cvbs_connector_destroy,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static const
|
||||
struct drm_connector_helper_funcs meson_cvbs_connector_helper_funcs = {
|
||||
.get_modes = meson_cvbs_connector_get_modes,
|
||||
.mode_valid = meson_cvbs_connector_mode_valid,
|
||||
};
|
||||
|
||||
/* Encoder */
|
||||
|
||||
static void meson_venc_cvbs_encoder_destroy(struct drm_encoder *encoder)
|
||||
{
|
||||
drm_encoder_cleanup(encoder);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_funcs meson_venc_cvbs_encoder_funcs = {
|
||||
.destroy = meson_venc_cvbs_encoder_destroy,
|
||||
};
|
||||
|
||||
static int meson_venc_cvbs_encoder_atomic_check(struct drm_encoder *encoder,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) {
|
||||
struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i];
|
||||
|
||||
if (drm_mode_equal(&crtc_state->mode, &meson_mode->mode))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void meson_venc_cvbs_encoder_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct meson_venc_cvbs *meson_venc_cvbs =
|
||||
encoder_to_meson_venc_cvbs(encoder);
|
||||
struct meson_drm *priv = meson_venc_cvbs->priv;
|
||||
|
||||
/* Disable CVBS VDAC */
|
||||
regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0);
|
||||
regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0);
|
||||
}
|
||||
|
||||
static void meson_venc_cvbs_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct meson_venc_cvbs *meson_venc_cvbs =
|
||||
encoder_to_meson_venc_cvbs(encoder);
|
||||
struct meson_drm *priv = meson_venc_cvbs->priv;
|
||||
|
||||
/* VDAC0 source is not from ATV */
|
||||
writel_bits_relaxed(BIT(5), 0, priv->io_base + _REG(VENC_VDAC_DACSEL0));
|
||||
|
||||
if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu"))
|
||||
regmap_write(priv->hhi, HHI_VDAC_CNTL0, 1);
|
||||
else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
|
||||
meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
|
||||
regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0xf0001);
|
||||
|
||||
regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0);
|
||||
}
|
||||
|
||||
static void meson_venc_cvbs_encoder_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct meson_venc_cvbs *meson_venc_cvbs =
|
||||
encoder_to_meson_venc_cvbs(encoder);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) {
|
||||
struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i];
|
||||
|
||||
if (drm_mode_equal(mode, &meson_mode->mode)) {
|
||||
meson_venci_cvbs_mode_set(meson_venc_cvbs->priv,
|
||||
meson_mode->enci);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs
|
||||
meson_venc_cvbs_encoder_helper_funcs = {
|
||||
.atomic_check = meson_venc_cvbs_encoder_atomic_check,
|
||||
.disable = meson_venc_cvbs_encoder_disable,
|
||||
.enable = meson_venc_cvbs_encoder_enable,
|
||||
.mode_set = meson_venc_cvbs_encoder_mode_set,
|
||||
};
|
||||
|
||||
static bool meson_venc_cvbs_connector_is_available(struct meson_drm *priv)
|
||||
{
|
||||
struct device_node *ep, *remote;
|
||||
|
||||
/* CVBS VDAC output is on the first port, first endpoint */
|
||||
ep = of_graph_get_endpoint_by_regs(priv->dev->of_node, 0, 0);
|
||||
if (!ep)
|
||||
return false;
|
||||
|
||||
|
||||
/* If the endpoint node exists, consider it enabled */
|
||||
remote = of_graph_get_remote_port(ep);
|
||||
if (remote) {
|
||||
of_node_put(ep);
|
||||
return true;
|
||||
}
|
||||
|
||||
of_node_put(ep);
|
||||
of_node_put(remote);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int meson_venc_cvbs_create(struct meson_drm *priv)
|
||||
{
|
||||
struct drm_device *drm = priv->drm;
|
||||
struct meson_venc_cvbs *meson_venc_cvbs;
|
||||
struct drm_connector *connector;
|
||||
struct drm_encoder *encoder;
|
||||
int ret;
|
||||
|
||||
if (!meson_venc_cvbs_connector_is_available(priv)) {
|
||||
dev_info(drm->dev, "CVBS Output connector not available\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
meson_venc_cvbs = devm_kzalloc(priv->dev, sizeof(*meson_venc_cvbs),
|
||||
GFP_KERNEL);
|
||||
if (!meson_venc_cvbs)
|
||||
return -ENOMEM;
|
||||
|
||||
meson_venc_cvbs->priv = priv;
|
||||
encoder = &meson_venc_cvbs->encoder;
|
||||
connector = &meson_venc_cvbs->connector;
|
||||
|
||||
/* Connector */
|
||||
|
||||
drm_connector_helper_add(connector,
|
||||
&meson_cvbs_connector_helper_funcs);
|
||||
|
||||
ret = drm_connector_init(drm, connector, &meson_cvbs_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_Composite);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "Failed to init CVBS connector\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
connector->interlace_allowed = 1;
|
||||
|
||||
/* Encoder */
|
||||
|
||||
drm_encoder_helper_add(encoder, &meson_venc_cvbs_encoder_helper_funcs);
|
||||
|
||||
ret = drm_encoder_init(drm, encoder, &meson_venc_cvbs_encoder_funcs,
|
||||
DRM_MODE_ENCODER_TVDAC, "meson_venc_cvbs");
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "Failed to init CVBS encoder\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
encoder->possible_crtcs = BIT(0);
|
||||
|
||||
drm_mode_connector_attach_encoder(connector, encoder);
|
||||
|
||||
return 0;
|
||||
}
|
41
drivers/gpu/drm/meson/meson_venc_cvbs.h
Normal file
41
drivers/gpu/drm/meson/meson_venc_cvbs.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Written by:
|
||||
* Jasper St. Pierre <jstpierre@mecheye.net>
|
||||
*/
|
||||
|
||||
#ifndef __MESON_VENC_CVBS_H
|
||||
#define __MESON_VENC_CVBS_H
|
||||
|
||||
#include "meson_drv.h"
|
||||
#include "meson_venc.h"
|
||||
|
||||
struct meson_cvbs_mode {
|
||||
struct meson_cvbs_enci_mode *enci;
|
||||
struct drm_display_mode mode;
|
||||
};
|
||||
|
||||
#define MESON_CVBS_MODES_COUNT 2
|
||||
|
||||
/* Modes supported by the CVBS output */
|
||||
extern struct meson_cvbs_mode meson_cvbs_modes[MESON_CVBS_MODES_COUNT];
|
||||
|
||||
int meson_venc_cvbs_create(struct meson_drm *priv);
|
||||
|
||||
#endif /* __MESON_VENC_CVBS_H */
|
331
drivers/gpu/drm/meson/meson_viu.c
Normal file
331
drivers/gpu/drm/meson/meson_viu.c
Normal file
@ -0,0 +1,331 @@
|
||||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <drm/drmP.h>
|
||||
#include "meson_drv.h"
|
||||
#include "meson_viu.h"
|
||||
#include "meson_vpp.h"
|
||||
#include "meson_venc.h"
|
||||
#include "meson_canvas.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
/*
|
||||
* VIU Handles the Pixel scanout and the basic Colorspace conversions
|
||||
* We handle the following features :
|
||||
* - OSD1 RGB565/RGB888/xRGB8888 scanout
|
||||
* - RGB conversion to x/cb/cr
|
||||
* - Progressive or Interlace buffer scanout
|
||||
* - OSD1 Commit on Vsync
|
||||
* - HDR OSD matrix for GXL/GXM
|
||||
*
|
||||
* What is missing :
|
||||
* - BGR888/xBGR8888/BGRx8888/BGRx8888 modes
|
||||
* - YUV4:2:2 Y0CbY1Cr scanout
|
||||
* - Conversion to YUV 4:4:4 from 4:2:2 input
|
||||
* - Colorkey Alpha matching
|
||||
* - Big endian scanout
|
||||
* - X/Y reverse scanout
|
||||
* - Global alpha setup
|
||||
* - OSD2 support, would need interlace switching on vsync
|
||||
* - OSD1 full scaling to support TV overscan
|
||||
*/
|
||||
|
||||
/* OSD csc defines */
|
||||
|
||||
enum viu_matrix_sel_e {
|
||||
VIU_MATRIX_OSD_EOTF = 0,
|
||||
VIU_MATRIX_OSD,
|
||||
};
|
||||
|
||||
enum viu_lut_sel_e {
|
||||
VIU_LUT_OSD_EOTF = 0,
|
||||
VIU_LUT_OSD_OETF,
|
||||
};
|
||||
|
||||
#define COEFF_NORM(a) ((int)((((a) * 2048.0) + 1) / 2))
|
||||
#define MATRIX_5X3_COEF_SIZE 24
|
||||
|
||||
#define EOTF_COEFF_NORM(a) ((int)((((a) * 4096.0) + 1) / 2))
|
||||
#define EOTF_COEFF_SIZE 10
|
||||
#define EOTF_COEFF_RIGHTSHIFT 1
|
||||
|
||||
static int RGB709_to_YUV709l_coeff[MATRIX_5X3_COEF_SIZE] = {
|
||||
0, 0, 0, /* pre offset */
|
||||
COEFF_NORM(0.181873), COEFF_NORM(0.611831), COEFF_NORM(0.061765),
|
||||
COEFF_NORM(-0.100251), COEFF_NORM(-0.337249), COEFF_NORM(0.437500),
|
||||
COEFF_NORM(0.437500), COEFF_NORM(-0.397384), COEFF_NORM(-0.040116),
|
||||
0, 0, 0, /* 10'/11'/12' */
|
||||
0, 0, 0, /* 20'/21'/22' */
|
||||
64, 512, 512, /* offset */
|
||||
0, 0, 0 /* mode, right_shift, clip_en */
|
||||
};
|
||||
|
||||
/* eotf matrix: bypass */
|
||||
static int eotf_bypass_coeff[EOTF_COEFF_SIZE] = {
|
||||
EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0),
|
||||
EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0),
|
||||
EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0),
|
||||
EOTF_COEFF_RIGHTSHIFT /* right shift */
|
||||
};
|
||||
|
||||
void meson_viu_set_osd_matrix(struct meson_drm *priv,
|
||||
enum viu_matrix_sel_e m_select,
|
||||
int *m, bool csc_on)
|
||||
{
|
||||
if (m_select == VIU_MATRIX_OSD) {
|
||||
/* osd matrix, VIU_MATRIX_0 */
|
||||
writel(((m[0] & 0xfff) << 16) | (m[1] & 0xfff),
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET0_1));
|
||||
writel(m[2] & 0xfff,
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET2));
|
||||
writel(((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff),
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_COEF00_01));
|
||||
writel(((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff),
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_COEF02_10));
|
||||
writel(((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff),
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_COEF11_12));
|
||||
writel(((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff),
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_COEF20_21));
|
||||
|
||||
if (m[21]) {
|
||||
writel(((m[11] & 0x1fff) << 16) | (m[12] & 0x1fff),
|
||||
priv->io_base +
|
||||
_REG(VIU_OSD1_MATRIX_COEF22_30));
|
||||
writel(((m[13] & 0x1fff) << 16) | (m[14] & 0x1fff),
|
||||
priv->io_base +
|
||||
_REG(VIU_OSD1_MATRIX_COEF31_32));
|
||||
writel(((m[15] & 0x1fff) << 16) | (m[16] & 0x1fff),
|
||||
priv->io_base +
|
||||
_REG(VIU_OSD1_MATRIX_COEF40_41));
|
||||
writel(m[17] & 0x1fff, priv->io_base +
|
||||
_REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
|
||||
} else
|
||||
writel((m[11] & 0x1fff) << 16, priv->io_base +
|
||||
_REG(VIU_OSD1_MATRIX_COEF22_30));
|
||||
|
||||
writel(((m[18] & 0xfff) << 16) | (m[19] & 0xfff),
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET0_1));
|
||||
writel(m[20] & 0xfff,
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET2));
|
||||
|
||||
writel_bits_relaxed(3 << 30, m[21] << 30,
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
|
||||
writel_bits_relaxed(7 << 16, m[22] << 16,
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
|
||||
|
||||
/* 23 reserved for clipping control */
|
||||
writel_bits_relaxed(BIT(0), csc_on ? BIT(0) : 0,
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL));
|
||||
writel_bits_relaxed(BIT(1), 0,
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL));
|
||||
} else if (m_select == VIU_MATRIX_OSD_EOTF) {
|
||||
int i;
|
||||
|
||||
/* osd eotf matrix, VIU_MATRIX_OSD_EOTF */
|
||||
for (i = 0; i < 5; i++)
|
||||
writel(((m[i * 2] & 0x1fff) << 16) |
|
||||
(m[i * 2 + 1] & 0x1fff), priv->io_base +
|
||||
_REG(VIU_OSD1_EOTF_CTL + i + 1));
|
||||
|
||||
writel_bits_relaxed(BIT(30), csc_on ? BIT(30) : 0,
|
||||
priv->io_base + _REG(VIU_OSD1_EOTF_CTL));
|
||||
writel_bits_relaxed(BIT(31), csc_on ? BIT(31) : 0,
|
||||
priv->io_base + _REG(VIU_OSD1_EOTF_CTL));
|
||||
}
|
||||
}
|
||||
|
||||
#define OSD_EOTF_LUT_SIZE 33
|
||||
#define OSD_OETF_LUT_SIZE 41
|
||||
|
||||
void meson_viu_set_osd_lut(struct meson_drm *priv, enum viu_lut_sel_e lut_sel,
|
||||
unsigned int *r_map, unsigned int *g_map,
|
||||
unsigned int *b_map,
|
||||
bool csc_on)
|
||||
{
|
||||
unsigned int addr_port;
|
||||
unsigned int data_port;
|
||||
unsigned int ctrl_port;
|
||||
int i;
|
||||
|
||||
if (lut_sel == VIU_LUT_OSD_EOTF) {
|
||||
addr_port = VIU_OSD1_EOTF_LUT_ADDR_PORT;
|
||||
data_port = VIU_OSD1_EOTF_LUT_DATA_PORT;
|
||||
ctrl_port = VIU_OSD1_EOTF_CTL;
|
||||
} else if (lut_sel == VIU_LUT_OSD_OETF) {
|
||||
addr_port = VIU_OSD1_OETF_LUT_ADDR_PORT;
|
||||
data_port = VIU_OSD1_OETF_LUT_DATA_PORT;
|
||||
ctrl_port = VIU_OSD1_OETF_CTL;
|
||||
} else
|
||||
return;
|
||||
|
||||
if (lut_sel == VIU_LUT_OSD_OETF) {
|
||||
writel(0, priv->io_base + _REG(addr_port));
|
||||
|
||||
for (i = 0; i < 20; i++)
|
||||
writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16),
|
||||
priv->io_base + _REG(data_port));
|
||||
|
||||
writel(r_map[OSD_OETF_LUT_SIZE - 1] | (g_map[0] << 16),
|
||||
priv->io_base + _REG(data_port));
|
||||
|
||||
for (i = 0; i < 20; i++)
|
||||
writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16),
|
||||
priv->io_base + _REG(data_port));
|
||||
|
||||
for (i = 0; i < 20; i++)
|
||||
writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16),
|
||||
priv->io_base + _REG(data_port));
|
||||
|
||||
writel(b_map[OSD_OETF_LUT_SIZE - 1],
|
||||
priv->io_base + _REG(data_port));
|
||||
|
||||
if (csc_on)
|
||||
writel_bits_relaxed(0x7 << 29, 7 << 29,
|
||||
priv->io_base + _REG(ctrl_port));
|
||||
else
|
||||
writel_bits_relaxed(0x7 << 29, 0,
|
||||
priv->io_base + _REG(ctrl_port));
|
||||
} else if (lut_sel == VIU_LUT_OSD_EOTF) {
|
||||
writel(0, priv->io_base + _REG(addr_port));
|
||||
|
||||
for (i = 0; i < 20; i++)
|
||||
writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16),
|
||||
priv->io_base + _REG(data_port));
|
||||
|
||||
writel(r_map[OSD_EOTF_LUT_SIZE - 1] | (g_map[0] << 16),
|
||||
priv->io_base + _REG(data_port));
|
||||
|
||||
for (i = 0; i < 20; i++)
|
||||
writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16),
|
||||
priv->io_base + _REG(data_port));
|
||||
|
||||
for (i = 0; i < 20; i++)
|
||||
writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16),
|
||||
priv->io_base + _REG(data_port));
|
||||
|
||||
writel(b_map[OSD_EOTF_LUT_SIZE - 1],
|
||||
priv->io_base + _REG(data_port));
|
||||
|
||||
if (csc_on)
|
||||
writel_bits_relaxed(7 << 27, 7 << 27,
|
||||
priv->io_base + _REG(ctrl_port));
|
||||
else
|
||||
writel_bits_relaxed(7 << 27, 0,
|
||||
priv->io_base + _REG(ctrl_port));
|
||||
|
||||
writel_bits_relaxed(BIT(31), BIT(31),
|
||||
priv->io_base + _REG(ctrl_port));
|
||||
}
|
||||
}
|
||||
|
||||
/* eotf lut: linear */
|
||||
static unsigned int eotf_33_linear_mapping[OSD_EOTF_LUT_SIZE] = {
|
||||
0x0000, 0x0200, 0x0400, 0x0600,
|
||||
0x0800, 0x0a00, 0x0c00, 0x0e00,
|
||||
0x1000, 0x1200, 0x1400, 0x1600,
|
||||
0x1800, 0x1a00, 0x1c00, 0x1e00,
|
||||
0x2000, 0x2200, 0x2400, 0x2600,
|
||||
0x2800, 0x2a00, 0x2c00, 0x2e00,
|
||||
0x3000, 0x3200, 0x3400, 0x3600,
|
||||
0x3800, 0x3a00, 0x3c00, 0x3e00,
|
||||
0x4000
|
||||
};
|
||||
|
||||
/* osd oetf lut: linear */
|
||||
static unsigned int oetf_41_linear_mapping[OSD_OETF_LUT_SIZE] = {
|
||||
0, 0, 0, 0,
|
||||
0, 32, 64, 96,
|
||||
128, 160, 196, 224,
|
||||
256, 288, 320, 352,
|
||||
384, 416, 448, 480,
|
||||
512, 544, 576, 608,
|
||||
640, 672, 704, 736,
|
||||
768, 800, 832, 864,
|
||||
896, 928, 960, 992,
|
||||
1023, 1023, 1023, 1023,
|
||||
1023
|
||||
};
|
||||
|
||||
static void meson_viu_load_matrix(struct meson_drm *priv)
|
||||
{
|
||||
/* eotf lut bypass */
|
||||
meson_viu_set_osd_lut(priv, VIU_LUT_OSD_EOTF,
|
||||
eotf_33_linear_mapping, /* R */
|
||||
eotf_33_linear_mapping, /* G */
|
||||
eotf_33_linear_mapping, /* B */
|
||||
false);
|
||||
|
||||
/* eotf matrix bypass */
|
||||
meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD_EOTF,
|
||||
eotf_bypass_coeff,
|
||||
false);
|
||||
|
||||
/* oetf lut bypass */
|
||||
meson_viu_set_osd_lut(priv, VIU_LUT_OSD_OETF,
|
||||
oetf_41_linear_mapping, /* R */
|
||||
oetf_41_linear_mapping, /* G */
|
||||
oetf_41_linear_mapping, /* B */
|
||||
false);
|
||||
|
||||
/* osd matrix RGB709 to YUV709 limit */
|
||||
meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD,
|
||||
RGB709_to_YUV709l_coeff,
|
||||
true);
|
||||
}
|
||||
|
||||
void meson_viu_init(struct meson_drm *priv)
|
||||
{
|
||||
uint32_t reg;
|
||||
|
||||
/* Disable OSDs */
|
||||
writel_bits_relaxed(BIT(0) | BIT(21), 0,
|
||||
priv->io_base + _REG(VIU_OSD1_CTRL_STAT));
|
||||
writel_bits_relaxed(BIT(0) | BIT(21), 0,
|
||||
priv->io_base + _REG(VIU_OSD2_CTRL_STAT));
|
||||
|
||||
/* On GXL/GXM, Use the 10bit HDR conversion matrix */
|
||||
if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
|
||||
meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
|
||||
meson_viu_load_matrix(priv);
|
||||
|
||||
/* Initialize OSD1 fifo control register */
|
||||
reg = BIT(0) | /* Urgent DDR request priority */
|
||||
(4 << 5) | /* hold_fifo_lines */
|
||||
(3 << 10) | /* burst length 64 */
|
||||
(32 << 12) | /* fifo_depth_val: 32*8=256 */
|
||||
(2 << 22) | /* 4 words in 1 burst */
|
||||
(2 << 24);
|
||||
writel_relaxed(reg, priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT));
|
||||
writel_relaxed(reg, priv->io_base + _REG(VIU_OSD2_FIFO_CTRL_STAT));
|
||||
|
||||
/* Set OSD alpha replace value */
|
||||
writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT,
|
||||
0xff << OSD_REPLACE_SHIFT,
|
||||
priv->io_base + _REG(VIU_OSD1_CTRL_STAT2));
|
||||
writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT,
|
||||
0xff << OSD_REPLACE_SHIFT,
|
||||
priv->io_base + _REG(VIU_OSD2_CTRL_STAT2));
|
||||
|
||||
priv->viu.osd1_enabled = false;
|
||||
priv->viu.osd1_commit = false;
|
||||
priv->viu.osd1_interlace = false;
|
||||
}
|
64
drivers/gpu/drm/meson/meson_viu.h
Normal file
64
drivers/gpu/drm/meson/meson_viu.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Video Input Unit */
|
||||
|
||||
#ifndef __MESON_VIU_H
|
||||
#define __MESON_VIU_H
|
||||
|
||||
/* OSDx_BLKx_CFG */
|
||||
#define OSD_CANVAS_SEL 16
|
||||
|
||||
#define OSD_ENDIANNESS_LE BIT(15)
|
||||
#define OSD_ENDIANNESS_BE (0)
|
||||
|
||||
#define OSD_BLK_MODE_422 (0x03 << 8)
|
||||
#define OSD_BLK_MODE_16 (0x04 << 8)
|
||||
#define OSD_BLK_MODE_32 (0x05 << 8)
|
||||
#define OSD_BLK_MODE_24 (0x07 << 8)
|
||||
|
||||
#define OSD_OUTPUT_COLOR_RGB BIT(7)
|
||||
#define OSD_OUTPUT_COLOR_YUV (0)
|
||||
|
||||
#define OSD_COLOR_MATRIX_32_RGBA (0x00 << 2)
|
||||
#define OSD_COLOR_MATRIX_32_ARGB (0x01 << 2)
|
||||
#define OSD_COLOR_MATRIX_32_ABGR (0x02 << 2)
|
||||
#define OSD_COLOR_MATRIX_32_BGRA (0x03 << 2)
|
||||
|
||||
#define OSD_COLOR_MATRIX_24_RGB (0x00 << 2)
|
||||
|
||||
#define OSD_COLOR_MATRIX_16_RGB655 (0x00 << 2)
|
||||
#define OSD_COLOR_MATRIX_16_RGB565 (0x04 << 2)
|
||||
|
||||
#define OSD_INTERLACE_ENABLED BIT(1)
|
||||
#define OSD_INTERLACE_ODD BIT(0)
|
||||
#define OSD_INTERLACE_EVEN (0)
|
||||
|
||||
/* OSDx_CTRL_STAT */
|
||||
#define OSD_ENABLE BIT(21)
|
||||
#define OSD_BLK0_ENABLE BIT(0)
|
||||
|
||||
#define OSD_GLOBAL_ALPHA_SHIFT 12
|
||||
|
||||
/* OSDx_CTRL_STAT2 */
|
||||
#define OSD_REPLACE_EN BIT(14)
|
||||
#define OSD_REPLACE_SHIFT 6
|
||||
|
||||
void meson_viu_init(struct meson_drm *priv);
|
||||
|
||||
#endif /* __MESON_VIU_H */
|
162
drivers/gpu/drm/meson/meson_vpp.c
Normal file
162
drivers/gpu/drm/meson/meson_vpp.c
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <drm/drmP.h>
|
||||
#include "meson_drv.h"
|
||||
#include "meson_vpp.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
/*
|
||||
* VPP Handles all the Post Processing after the Scanout from the VIU
|
||||
* We handle the following post processings :
|
||||
* - Postblend : Blends the OSD1 only
|
||||
* We exclude OSD2, VS1, VS1 and Preblend output
|
||||
* - Vertical OSD Scaler for OSD1 only, we disable vertical scaler and
|
||||
* use it only for interlace scanout
|
||||
* - Intermediate FIFO with default Amlogic values
|
||||
*
|
||||
* What is missing :
|
||||
* - Preblend for video overlay pre-scaling
|
||||
* - OSD2 support for cursor framebuffer
|
||||
* - Video pre-scaling before postblend
|
||||
* - Full Vertical/Horizontal OSD scaling to support TV overscan
|
||||
* - HDR conversion
|
||||
*/
|
||||
|
||||
void meson_vpp_setup_mux(struct meson_drm *priv, unsigned int mux)
|
||||
{
|
||||
writel(mux, priv->io_base + _REG(VPU_VIU_VENC_MUX_CTRL));
|
||||
}
|
||||
|
||||
/*
|
||||
* When the output is interlaced, the OSD must switch between
|
||||
* each field using the INTERLACE_SEL_ODD (0) of VIU_OSD1_BLK0_CFG_W0
|
||||
* at each vsync.
|
||||
* But the vertical scaler can provide such funtionnality if
|
||||
* is configured for 2:1 scaling with interlace options enabled.
|
||||
*/
|
||||
void meson_vpp_setup_interlace_vscaler_osd1(struct meson_drm *priv,
|
||||
struct drm_rect *input)
|
||||
{
|
||||
writel_relaxed(BIT(3) /* Enable scaler */ |
|
||||
BIT(2), /* Select OSD1 */
|
||||
priv->io_base + _REG(VPP_OSD_SC_CTRL0));
|
||||
|
||||
writel_relaxed(((drm_rect_width(input) - 1) << 16) |
|
||||
(drm_rect_height(input) - 1),
|
||||
priv->io_base + _REG(VPP_OSD_SCI_WH_M1));
|
||||
/* 2:1 scaling */
|
||||
writel_relaxed(((input->x1) << 16) | (input->x2),
|
||||
priv->io_base + _REG(VPP_OSD_SCO_H_START_END));
|
||||
writel_relaxed(((input->y1 >> 1) << 16) | (input->y2 >> 1),
|
||||
priv->io_base + _REG(VPP_OSD_SCO_V_START_END));
|
||||
|
||||
/* 2:1 scaling values */
|
||||
writel_relaxed(BIT(16), priv->io_base + _REG(VPP_OSD_VSC_INI_PHASE));
|
||||
writel_relaxed(BIT(25), priv->io_base + _REG(VPP_OSD_VSC_PHASE_STEP));
|
||||
|
||||
writel_relaxed(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0));
|
||||
|
||||
writel_relaxed((4 << 0) /* osd_vsc_bank_length */ |
|
||||
(4 << 3) /* osd_vsc_top_ini_rcv_num0 */ |
|
||||
(1 << 8) /* osd_vsc_top_rpt_p0_num0 */ |
|
||||
(6 << 11) /* osd_vsc_bot_ini_rcv_num0 */ |
|
||||
(2 << 16) /* osd_vsc_bot_rpt_p0_num0 */ |
|
||||
BIT(23) /* osd_prog_interlace */ |
|
||||
BIT(24), /* Enable vertical scaler */
|
||||
priv->io_base + _REG(VPP_OSD_VSC_CTRL0));
|
||||
}
|
||||
|
||||
void meson_vpp_disable_interlace_vscaler_osd1(struct meson_drm *priv)
|
||||
{
|
||||
writel_relaxed(0, priv->io_base + _REG(VPP_OSD_SC_CTRL0));
|
||||
writel_relaxed(0, priv->io_base + _REG(VPP_OSD_VSC_CTRL0));
|
||||
writel_relaxed(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0));
|
||||
}
|
||||
|
||||
static unsigned int vpp_filter_coefs_4point_bspline[] = {
|
||||
0x15561500, 0x14561600, 0x13561700, 0x12561800,
|
||||
0x11551a00, 0x11541b00, 0x10541c00, 0x0f541d00,
|
||||
0x0f531e00, 0x0e531f00, 0x0d522100, 0x0c522200,
|
||||
0x0b522300, 0x0b512400, 0x0a502600, 0x0a4f2700,
|
||||
0x094e2900, 0x084e2a00, 0x084d2b00, 0x074c2c01,
|
||||
0x074b2d01, 0x064a2f01, 0x06493001, 0x05483201,
|
||||
0x05473301, 0x05463401, 0x04453601, 0x04433702,
|
||||
0x04423802, 0x03413a02, 0x03403b02, 0x033f3c02,
|
||||
0x033d3d03
|
||||
};
|
||||
|
||||
static void meson_vpp_write_scaling_filter_coefs(struct meson_drm *priv,
|
||||
const unsigned int *coefs,
|
||||
bool is_horizontal)
|
||||
{
|
||||
int i;
|
||||
|
||||
writel_relaxed(is_horizontal ? BIT(8) : 0,
|
||||
priv->io_base + _REG(VPP_OSD_SCALE_COEF_IDX));
|
||||
for (i = 0; i < 33; i++)
|
||||
writel_relaxed(coefs[i],
|
||||
priv->io_base + _REG(VPP_OSD_SCALE_COEF));
|
||||
}
|
||||
|
||||
void meson_vpp_init(struct meson_drm *priv)
|
||||
{
|
||||
/* set dummy data default YUV black */
|
||||
if (meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
|
||||
writel_relaxed(0x108080, priv->io_base + _REG(VPP_DUMMY_DATA1));
|
||||
else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu")) {
|
||||
writel_bits_relaxed(0xff << 16, 0xff << 16,
|
||||
priv->io_base + _REG(VIU_MISC_CTRL1));
|
||||
writel_relaxed(0x20000, priv->io_base + _REG(VPP_DOLBY_CTRL));
|
||||
writel_relaxed(0x1020080,
|
||||
priv->io_base + _REG(VPP_DUMMY_DATA1));
|
||||
}
|
||||
|
||||
/* Initialize vpu fifo control registers */
|
||||
writel_relaxed(readl_relaxed(priv->io_base + _REG(VPP_OFIFO_SIZE)) |
|
||||
0x77f, priv->io_base + _REG(VPP_OFIFO_SIZE));
|
||||
writel_relaxed(0x08080808, priv->io_base + _REG(VPP_HOLD_LINES));
|
||||
|
||||
/* Turn off preblend */
|
||||
writel_bits_relaxed(VPP_PREBLEND_ENABLE, 0,
|
||||
priv->io_base + _REG(VPP_MISC));
|
||||
|
||||
/* Turn off POSTBLEND */
|
||||
writel_bits_relaxed(VPP_POSTBLEND_ENABLE, 0,
|
||||
priv->io_base + _REG(VPP_MISC));
|
||||
|
||||
/* Force all planes off */
|
||||
writel_bits_relaxed(VPP_OSD1_POSTBLEND | VPP_OSD2_POSTBLEND |
|
||||
VPP_VD1_POSTBLEND | VPP_VD2_POSTBLEND, 0,
|
||||
priv->io_base + _REG(VPP_MISC));
|
||||
|
||||
/* Disable Scalers */
|
||||
writel_relaxed(0, priv->io_base + _REG(VPP_OSD_SC_CTRL0));
|
||||
writel_relaxed(0, priv->io_base + _REG(VPP_OSD_VSC_CTRL0));
|
||||
writel_relaxed(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0));
|
||||
|
||||
/* Write in the proper filter coefficients. */
|
||||
meson_vpp_write_scaling_filter_coefs(priv,
|
||||
vpp_filter_coefs_4point_bspline, false);
|
||||
meson_vpp_write_scaling_filter_coefs(priv,
|
||||
vpp_filter_coefs_4point_bspline, true);
|
||||
}
|
35
drivers/gpu/drm/meson/meson_vpp.h
Normal file
35
drivers/gpu/drm/meson/meson_vpp.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Video Post Process */
|
||||
|
||||
#ifndef __MESON_VPP_H
|
||||
#define __MESON_VPP_H
|
||||
|
||||
/* Mux VIU/VPP to ENCI */
|
||||
#define MESON_VIU_VPP_MUX_ENCI 0x5
|
||||
|
||||
void meson_vpp_setup_mux(struct meson_drm *priv, unsigned int mux);
|
||||
|
||||
void meson_vpp_setup_interlace_vscaler_osd1(struct meson_drm *priv,
|
||||
struct drm_rect *input);
|
||||
void meson_vpp_disable_interlace_vscaler_osd1(struct meson_drm *priv);
|
||||
|
||||
void meson_vpp_init(struct meson_drm *priv);
|
||||
|
||||
#endif /* __MESON_VPP_H */
|
19
drivers/gpu/drm/mxsfb/Kconfig
Normal file
19
drivers/gpu/drm/mxsfb/Kconfig
Normal file
@ -0,0 +1,19 @@
|
||||
config DRM_MXS
|
||||
bool
|
||||
help
|
||||
Choose this option to select drivers for MXS FB devices
|
||||
|
||||
config DRM_MXSFB
|
||||
tristate "i.MX23/i.MX28/i.MX6SX MXSFB LCD controller"
|
||||
depends on DRM && OF
|
||||
depends on COMMON_CLK
|
||||
select DRM_MXS
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_KMS_FB_HELPER
|
||||
select DRM_KMS_CMA_HELPER
|
||||
select DRM_PANEL
|
||||
help
|
||||
Choose this option if you have an i.MX23/i.MX28/i.MX6SX MXSFB
|
||||
LCD controller.
|
||||
|
||||
If M is selected the module will be called mxsfb.
|
2
drivers/gpu/drm/mxsfb/Makefile
Normal file
2
drivers/gpu/drm/mxsfb/Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
mxsfb-y := mxsfb_drv.o mxsfb_crtc.o mxsfb_out.o
|
||||
obj-$(CONFIG_DRM_MXSFB) += mxsfb.o
|
241
drivers/gpu/drm/mxsfb/mxsfb_crtc.c
Normal file
241
drivers/gpu/drm/mxsfb/mxsfb_crtc.c
Normal file
@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Marek Vasut <marex@denx.de>
|
||||
*
|
||||
* This code is based on drivers/video/fbdev/mxsfb.c :
|
||||
* Copyright (C) 2010 Juergen Beisert, Pengutronix
|
||||
* Copyright (C) 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
* 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 <drm/drmP.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drm_simple_kms_helper.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/platform_data/simplefb.h>
|
||||
#include <video/videomode.h>
|
||||
|
||||
#include "mxsfb_drv.h"
|
||||
#include "mxsfb_regs.h"
|
||||
|
||||
static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val)
|
||||
{
|
||||
return (val & mxsfb->devdata->hs_wdth_mask) <<
|
||||
mxsfb->devdata->hs_wdth_shift;
|
||||
}
|
||||
|
||||
/* Setup the MXSFB registers for decoding the pixels out of the framebuffer */
|
||||
static int mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
struct drm_crtc *crtc = &mxsfb->pipe.crtc;
|
||||
struct drm_device *drm = crtc->dev;
|
||||
const u32 format = crtc->primary->state->fb->pixel_format;
|
||||
u32 ctrl, ctrl1;
|
||||
|
||||
ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER;
|
||||
|
||||
/*
|
||||
* WARNING: The bus width, CTRL_SET_BUS_WIDTH(), is configured to
|
||||
* match the selected mode here. This differs from the original
|
||||
* MXSFB driver, which had the option to configure the bus width
|
||||
* to arbitrary value. This limitation should not pose an issue.
|
||||
*/
|
||||
|
||||
/* CTRL1 contains IRQ config and status bits, preserve those. */
|
||||
ctrl1 = readl(mxsfb->base + LCDC_CTRL1);
|
||||
ctrl1 &= CTRL1_CUR_FRAME_DONE_IRQ_EN | CTRL1_CUR_FRAME_DONE_IRQ;
|
||||
|
||||
switch (format) {
|
||||
case DRM_FORMAT_RGB565:
|
||||
dev_dbg(drm->dev, "Setting up RGB565 mode\n");
|
||||
ctrl |= CTRL_SET_BUS_WIDTH(STMLCDIF_16BIT);
|
||||
ctrl |= CTRL_SET_WORD_LENGTH(0);
|
||||
ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0xf);
|
||||
break;
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
dev_dbg(drm->dev, "Setting up XRGB8888 mode\n");
|
||||
ctrl |= CTRL_SET_BUS_WIDTH(STMLCDIF_24BIT);
|
||||
ctrl |= CTRL_SET_WORD_LENGTH(3);
|
||||
/* Do not use packed pixels = one pixel per word instead. */
|
||||
ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0x7);
|
||||
break;
|
||||
default:
|
||||
dev_err(drm->dev, "Unhandled pixel format %08x\n", format);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel(ctrl1, mxsfb->base + LCDC_CTRL1);
|
||||
writel(ctrl, mxsfb->base + LCDC_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mxsfb_enable_controller(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
if (mxsfb->clk_disp_axi)
|
||||
clk_prepare_enable(mxsfb->clk_disp_axi);
|
||||
clk_prepare_enable(mxsfb->clk);
|
||||
mxsfb_enable_axi_clk(mxsfb);
|
||||
|
||||
/* If it was disabled, re-enable the mode again */
|
||||
writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_SET);
|
||||
|
||||
/* Enable the SYNC signals first, then the DMA engine */
|
||||
reg = readl(mxsfb->base + LCDC_VDCTRL4);
|
||||
reg |= VDCTRL4_SYNC_SIGNALS_ON;
|
||||
writel(reg, mxsfb->base + LCDC_VDCTRL4);
|
||||
|
||||
writel(CTRL_RUN, mxsfb->base + LCDC_CTRL + REG_SET);
|
||||
}
|
||||
|
||||
static void mxsfb_disable_controller(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
/*
|
||||
* Even if we disable the controller here, it will still continue
|
||||
* until its FIFOs are running out of data
|
||||
*/
|
||||
writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_CLR);
|
||||
|
||||
readl_poll_timeout(mxsfb->base + LCDC_CTRL, reg, !(reg & CTRL_RUN),
|
||||
0, 1000);
|
||||
|
||||
reg = readl(mxsfb->base + LCDC_VDCTRL4);
|
||||
reg &= ~VDCTRL4_SYNC_SIGNALS_ON;
|
||||
writel(reg, mxsfb->base + LCDC_VDCTRL4);
|
||||
|
||||
mxsfb_disable_axi_clk(mxsfb);
|
||||
|
||||
clk_disable_unprepare(mxsfb->clk);
|
||||
if (mxsfb->clk_disp_axi)
|
||||
clk_disable_unprepare(mxsfb->clk_disp_axi);
|
||||
}
|
||||
|
||||
static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
struct drm_display_mode *m = &mxsfb->pipe.crtc.state->adjusted_mode;
|
||||
const u32 bus_flags = mxsfb->connector.display_info.bus_flags;
|
||||
u32 vdctrl0, vsync_pulse_len, hsync_pulse_len;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* It seems, you can't re-program the controller if it is still
|
||||
* running. This may lead to shifted pictures (FIFO issue?), so
|
||||
* first stop the controller and drain its FIFOs.
|
||||
*/
|
||||
mxsfb_enable_axi_clk(mxsfb);
|
||||
|
||||
/* Clear the FIFOs */
|
||||
writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET);
|
||||
|
||||
err = mxsfb_set_pixel_fmt(mxsfb);
|
||||
if (err)
|
||||
return;
|
||||
|
||||
clk_set_rate(mxsfb->clk, m->crtc_clock * 1000);
|
||||
|
||||
writel(TRANSFER_COUNT_SET_VCOUNT(m->crtc_vdisplay) |
|
||||
TRANSFER_COUNT_SET_HCOUNT(m->crtc_hdisplay),
|
||||
mxsfb->base + mxsfb->devdata->transfer_count);
|
||||
|
||||
vsync_pulse_len = m->crtc_vsync_end - m->crtc_vsync_start;
|
||||
|
||||
vdctrl0 = VDCTRL0_ENABLE_PRESENT | /* Always in DOTCLOCK mode */
|
||||
VDCTRL0_VSYNC_PERIOD_UNIT |
|
||||
VDCTRL0_VSYNC_PULSE_WIDTH_UNIT |
|
||||
VDCTRL0_SET_VSYNC_PULSE_WIDTH(vsync_pulse_len);
|
||||
if (m->flags & DRM_MODE_FLAG_PHSYNC)
|
||||
vdctrl0 |= VDCTRL0_HSYNC_ACT_HIGH;
|
||||
if (m->flags & DRM_MODE_FLAG_PVSYNC)
|
||||
vdctrl0 |= VDCTRL0_VSYNC_ACT_HIGH;
|
||||
if (bus_flags & DRM_BUS_FLAG_DE_HIGH)
|
||||
vdctrl0 |= VDCTRL0_ENABLE_ACT_HIGH;
|
||||
if (bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE)
|
||||
vdctrl0 |= VDCTRL0_DOTCLK_ACT_FALLING;
|
||||
|
||||
writel(vdctrl0, mxsfb->base + LCDC_VDCTRL0);
|
||||
|
||||
/* Frame length in lines. */
|
||||
writel(m->crtc_vtotal, mxsfb->base + LCDC_VDCTRL1);
|
||||
|
||||
/* Line length in units of clocks or pixels. */
|
||||
hsync_pulse_len = m->crtc_hsync_end - m->crtc_hsync_start;
|
||||
writel(set_hsync_pulse_width(mxsfb, hsync_pulse_len) |
|
||||
VDCTRL2_SET_HSYNC_PERIOD(m->crtc_htotal),
|
||||
mxsfb->base + LCDC_VDCTRL2);
|
||||
|
||||
writel(SET_HOR_WAIT_CNT(m->crtc_hblank_end - m->crtc_hsync_end) |
|
||||
SET_VERT_WAIT_CNT(m->crtc_vblank_end - m->crtc_vsync_end),
|
||||
mxsfb->base + LCDC_VDCTRL3);
|
||||
|
||||
writel(SET_DOTCLK_H_VALID_DATA_CNT(m->hdisplay),
|
||||
mxsfb->base + LCDC_VDCTRL4);
|
||||
|
||||
mxsfb_disable_axi_clk(mxsfb);
|
||||
}
|
||||
|
||||
void mxsfb_crtc_enable(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
mxsfb_crtc_mode_set_nofb(mxsfb);
|
||||
mxsfb_enable_controller(mxsfb);
|
||||
}
|
||||
|
||||
void mxsfb_crtc_disable(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
mxsfb_disable_controller(mxsfb);
|
||||
}
|
||||
|
||||
void mxsfb_plane_atomic_update(struct mxsfb_drm_private *mxsfb,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct drm_simple_display_pipe *pipe = &mxsfb->pipe;
|
||||
struct drm_crtc *crtc = &pipe->crtc;
|
||||
struct drm_framebuffer *fb = pipe->plane.state->fb;
|
||||
struct drm_pending_vblank_event *event;
|
||||
struct drm_gem_cma_object *gem;
|
||||
|
||||
if (!crtc)
|
||||
return;
|
||||
|
||||
spin_lock_irq(&crtc->dev->event_lock);
|
||||
event = crtc->state->event;
|
||||
if (event) {
|
||||
crtc->state->event = NULL;
|
||||
|
||||
if (drm_crtc_vblank_get(crtc) == 0) {
|
||||
drm_crtc_arm_vblank_event(crtc, event);
|
||||
} else {
|
||||
drm_crtc_send_vblank_event(crtc, event);
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&crtc->dev->event_lock);
|
||||
|
||||
if (!fb)
|
||||
return;
|
||||
|
||||
gem = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
|
||||
mxsfb_enable_axi_clk(mxsfb);
|
||||
writel(gem->paddr, mxsfb->base + mxsfb->devdata->next_buf);
|
||||
mxsfb_disable_axi_clk(mxsfb);
|
||||
}
|
444
drivers/gpu/drm/mxsfb/mxsfb_drv.c
Normal file
444
drivers/gpu/drm/mxsfb/mxsfb_drv.c
Normal file
@ -0,0 +1,444 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Marek Vasut <marex@denx.de>
|
||||
*
|
||||
* This code is based on drivers/video/fbdev/mxsfb.c :
|
||||
* Copyright (C) 2010 Juergen Beisert, Pengutronix
|
||||
* Copyright (C) 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
* 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/spinlock.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reservation.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_simple_kms_helper.h>
|
||||
|
||||
#include "mxsfb_drv.h"
|
||||
#include "mxsfb_regs.h"
|
||||
|
||||
enum mxsfb_devtype {
|
||||
MXSFB_V3,
|
||||
MXSFB_V4,
|
||||
};
|
||||
|
||||
static const struct mxsfb_devdata mxsfb_devdata[] = {
|
||||
[MXSFB_V3] = {
|
||||
.transfer_count = LCDC_V3_TRANSFER_COUNT,
|
||||
.cur_buf = LCDC_V3_CUR_BUF,
|
||||
.next_buf = LCDC_V3_NEXT_BUF,
|
||||
.debug0 = LCDC_V3_DEBUG0,
|
||||
.hs_wdth_mask = 0xff,
|
||||
.hs_wdth_shift = 24,
|
||||
.ipversion = 3,
|
||||
},
|
||||
[MXSFB_V4] = {
|
||||
.transfer_count = LCDC_V4_TRANSFER_COUNT,
|
||||
.cur_buf = LCDC_V4_CUR_BUF,
|
||||
.next_buf = LCDC_V4_NEXT_BUF,
|
||||
.debug0 = LCDC_V4_DEBUG0,
|
||||
.hs_wdth_mask = 0x3fff,
|
||||
.hs_wdth_shift = 18,
|
||||
.ipversion = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static const uint32_t mxsfb_formats[] = {
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_RGB565
|
||||
};
|
||||
|
||||
static struct mxsfb_drm_private *
|
||||
drm_pipe_to_mxsfb_drm_private(struct drm_simple_display_pipe *pipe)
|
||||
{
|
||||
return container_of(pipe, struct mxsfb_drm_private, pipe);
|
||||
}
|
||||
|
||||
void mxsfb_enable_axi_clk(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
if (mxsfb->clk_axi)
|
||||
clk_prepare_enable(mxsfb->clk_axi);
|
||||
}
|
||||
|
||||
void mxsfb_disable_axi_clk(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
if (mxsfb->clk_axi)
|
||||
clk_disable_unprepare(mxsfb->clk_axi);
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_funcs mxsfb_mode_config_funcs = {
|
||||
.fb_create = drm_fb_cma_create,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
};
|
||||
|
||||
static void mxsfb_pipe_enable(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_crtc_state *crtc_state)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
|
||||
|
||||
mxsfb_crtc_enable(mxsfb);
|
||||
}
|
||||
|
||||
static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
|
||||
|
||||
mxsfb_crtc_disable(mxsfb);
|
||||
}
|
||||
|
||||
static void mxsfb_pipe_update(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_plane_state *plane_state)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
|
||||
|
||||
mxsfb_plane_atomic_update(mxsfb, plane_state);
|
||||
}
|
||||
|
||||
static int mxsfb_pipe_prepare_fb(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_plane_state *plane_state)
|
||||
{
|
||||
return drm_fb_cma_prepare_fb(&pipe->plane, plane_state);
|
||||
}
|
||||
|
||||
struct drm_simple_display_pipe_funcs mxsfb_funcs = {
|
||||
.enable = mxsfb_pipe_enable,
|
||||
.disable = mxsfb_pipe_disable,
|
||||
.update = mxsfb_pipe_update,
|
||||
.prepare_fb = mxsfb_pipe_prepare_fb,
|
||||
};
|
||||
|
||||
static int mxsfb_load(struct drm_device *drm, unsigned long flags)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(drm->dev);
|
||||
struct mxsfb_drm_private *mxsfb;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
mxsfb = devm_kzalloc(&pdev->dev, sizeof(*mxsfb), GFP_KERNEL);
|
||||
if (!mxsfb)
|
||||
return -ENOMEM;
|
||||
|
||||
drm->dev_private = mxsfb;
|
||||
mxsfb->devdata = &mxsfb_devdata[pdev->id_entry->driver_data];
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
mxsfb->base = devm_ioremap_resource(drm->dev, res);
|
||||
if (IS_ERR(mxsfb->base))
|
||||
return PTR_ERR(mxsfb->base);
|
||||
|
||||
mxsfb->clk = devm_clk_get(drm->dev, NULL);
|
||||
if (IS_ERR(mxsfb->clk))
|
||||
return PTR_ERR(mxsfb->clk);
|
||||
|
||||
mxsfb->clk_axi = devm_clk_get(drm->dev, "axi");
|
||||
if (IS_ERR(mxsfb->clk_axi))
|
||||
mxsfb->clk_axi = NULL;
|
||||
|
||||
mxsfb->clk_disp_axi = devm_clk_get(drm->dev, "disp_axi");
|
||||
if (IS_ERR(mxsfb->clk_disp_axi))
|
||||
mxsfb->clk_disp_axi = NULL;
|
||||
|
||||
ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_enable(drm->dev);
|
||||
|
||||
ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
|
||||
if (ret < 0) {
|
||||
dev_err(drm->dev, "Failed to initialise vblank\n");
|
||||
goto err_vblank;
|
||||
}
|
||||
|
||||
/* Modeset init */
|
||||
drm_mode_config_init(drm);
|
||||
|
||||
ret = mxsfb_create_output(drm);
|
||||
if (ret < 0) {
|
||||
dev_err(drm->dev, "Failed to create outputs\n");
|
||||
goto err_vblank;
|
||||
}
|
||||
|
||||
ret = drm_simple_display_pipe_init(drm, &mxsfb->pipe, &mxsfb_funcs,
|
||||
mxsfb_formats, ARRAY_SIZE(mxsfb_formats),
|
||||
&mxsfb->connector);
|
||||
if (ret < 0) {
|
||||
dev_err(drm->dev, "Cannot setup simple display pipe\n");
|
||||
goto err_vblank;
|
||||
}
|
||||
|
||||
ret = drm_panel_attach(mxsfb->panel, &mxsfb->connector);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "Cannot connect panel\n");
|
||||
goto err_vblank;
|
||||
}
|
||||
|
||||
drm->mode_config.min_width = MXSFB_MIN_XRES;
|
||||
drm->mode_config.min_height = MXSFB_MIN_YRES;
|
||||
drm->mode_config.max_width = MXSFB_MAX_XRES;
|
||||
drm->mode_config.max_height = MXSFB_MAX_YRES;
|
||||
drm->mode_config.funcs = &mxsfb_mode_config_funcs;
|
||||
|
||||
drm_mode_config_reset(drm);
|
||||
|
||||
pm_runtime_get_sync(drm->dev);
|
||||
ret = drm_irq_install(drm, platform_get_irq(pdev, 0));
|
||||
pm_runtime_put_sync(drm->dev);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(drm->dev, "Failed to install IRQ handler\n");
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
drm_kms_helper_poll_init(drm);
|
||||
|
||||
mxsfb->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_crtc,
|
||||
drm->mode_config.num_connector);
|
||||
if (IS_ERR(mxsfb->fbdev)) {
|
||||
mxsfb->fbdev = NULL;
|
||||
dev_err(drm->dev, "Failed to init FB CMA area\n");
|
||||
goto err_cma;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, drm);
|
||||
|
||||
drm_helper_hpd_irq_event(drm);
|
||||
|
||||
return 0;
|
||||
|
||||
err_cma:
|
||||
drm_irq_uninstall(drm);
|
||||
err_irq:
|
||||
drm_panel_detach(mxsfb->panel);
|
||||
err_vblank:
|
||||
pm_runtime_disable(drm->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mxsfb_unload(struct drm_device *drm)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = drm->dev_private;
|
||||
|
||||
if (mxsfb->fbdev)
|
||||
drm_fbdev_cma_fini(mxsfb->fbdev);
|
||||
|
||||
drm_kms_helper_poll_fini(drm);
|
||||
drm_mode_config_cleanup(drm);
|
||||
drm_vblank_cleanup(drm);
|
||||
|
||||
pm_runtime_get_sync(drm->dev);
|
||||
drm_irq_uninstall(drm);
|
||||
pm_runtime_put_sync(drm->dev);
|
||||
|
||||
drm->dev_private = NULL;
|
||||
|
||||
pm_runtime_disable(drm->dev);
|
||||
}
|
||||
|
||||
static void mxsfb_lastclose(struct drm_device *drm)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = drm->dev_private;
|
||||
|
||||
drm_fbdev_cma_restore_mode(mxsfb->fbdev);
|
||||
}
|
||||
|
||||
static int mxsfb_enable_vblank(struct drm_device *drm, unsigned int crtc)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = drm->dev_private;
|
||||
|
||||
/* Clear and enable VBLANK IRQ */
|
||||
mxsfb_enable_axi_clk(mxsfb);
|
||||
writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
|
||||
writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_SET);
|
||||
mxsfb_disable_axi_clk(mxsfb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mxsfb_disable_vblank(struct drm_device *drm, unsigned int crtc)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = drm->dev_private;
|
||||
|
||||
/* Disable and clear VBLANK IRQ */
|
||||
mxsfb_enable_axi_clk(mxsfb);
|
||||
writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_CLR);
|
||||
writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
|
||||
mxsfb_disable_axi_clk(mxsfb);
|
||||
}
|
||||
|
||||
static void mxsfb_irq_preinstall(struct drm_device *drm)
|
||||
{
|
||||
mxsfb_disable_vblank(drm, 0);
|
||||
}
|
||||
|
||||
static irqreturn_t mxsfb_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct drm_device *drm = data;
|
||||
struct mxsfb_drm_private *mxsfb = drm->dev_private;
|
||||
u32 reg;
|
||||
|
||||
mxsfb_enable_axi_clk(mxsfb);
|
||||
|
||||
reg = readl(mxsfb->base + LCDC_CTRL1);
|
||||
|
||||
if (reg & CTRL1_CUR_FRAME_DONE_IRQ)
|
||||
drm_crtc_handle_vblank(&mxsfb->pipe.crtc);
|
||||
|
||||
writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
|
||||
|
||||
mxsfb_disable_axi_clk(mxsfb);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct file_operations fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
#endif
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = noop_llseek,
|
||||
.mmap = drm_gem_cma_mmap,
|
||||
};
|
||||
|
||||
static struct drm_driver mxsfb_driver = {
|
||||
.driver_features = DRIVER_GEM | DRIVER_MODESET |
|
||||
DRIVER_PRIME | DRIVER_ATOMIC |
|
||||
DRIVER_HAVE_IRQ,
|
||||
.lastclose = mxsfb_lastclose,
|
||||
.irq_handler = mxsfb_irq_handler,
|
||||
.irq_preinstall = mxsfb_irq_preinstall,
|
||||
.irq_uninstall = mxsfb_irq_preinstall,
|
||||
.get_vblank_counter = drm_vblank_no_hw_counter,
|
||||
.enable_vblank = mxsfb_enable_vblank,
|
||||
.disable_vblank = mxsfb_disable_vblank,
|
||||
.gem_free_object = drm_gem_cma_free_object,
|
||||
.gem_vm_ops = &drm_gem_cma_vm_ops,
|
||||
.dumb_create = drm_gem_cma_dumb_create,
|
||||
.dumb_map_offset = drm_gem_cma_dumb_map_offset,
|
||||
.dumb_destroy = drm_gem_dumb_destroy,
|
||||
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
|
||||
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
|
||||
.gem_prime_export = drm_gem_prime_export,
|
||||
.gem_prime_import = drm_gem_prime_import,
|
||||
.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
|
||||
.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
|
||||
.gem_prime_vmap = drm_gem_cma_prime_vmap,
|
||||
.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
|
||||
.gem_prime_mmap = drm_gem_cma_prime_mmap,
|
||||
.fops = &fops,
|
||||
.name = "mxsfb-drm",
|
||||
.desc = "MXSFB Controller DRM",
|
||||
.date = "20160824",
|
||||
.major = 1,
|
||||
.minor = 0,
|
||||
};
|
||||
|
||||
static const struct platform_device_id mxsfb_devtype[] = {
|
||||
{ .name = "imx23-fb", .driver_data = MXSFB_V3, },
|
||||
{ .name = "imx28-fb", .driver_data = MXSFB_V4, },
|
||||
{ .name = "imx6sx-fb", .driver_data = MXSFB_V4, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, mxsfb_devtype);
|
||||
|
||||
static const struct of_device_id mxsfb_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx23-lcdif", .data = &mxsfb_devtype[0], },
|
||||
{ .compatible = "fsl,imx28-lcdif", .data = &mxsfb_devtype[1], },
|
||||
{ .compatible = "fsl,imx6sx-lcdif", .data = &mxsfb_devtype[2], },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mxsfb_dt_ids);
|
||||
|
||||
static int mxsfb_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct drm_device *drm;
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(mxsfb_dt_ids, &pdev->dev);
|
||||
int ret;
|
||||
|
||||
if (!pdev->dev.of_node)
|
||||
return -ENODEV;
|
||||
|
||||
if (of_id)
|
||||
pdev->id_entry = of_id->data;
|
||||
|
||||
drm = drm_dev_alloc(&mxsfb_driver, &pdev->dev);
|
||||
if (!drm)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = mxsfb_load(drm, 0);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
ret = drm_dev_register(drm, 0);
|
||||
if (ret)
|
||||
goto err_unload;
|
||||
|
||||
return 0;
|
||||
|
||||
err_unload:
|
||||
mxsfb_unload(drm);
|
||||
err_free:
|
||||
drm_dev_unref(drm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxsfb_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct drm_device *drm = platform_get_drvdata(pdev);
|
||||
|
||||
drm_dev_unregister(drm);
|
||||
mxsfb_unload(drm);
|
||||
drm_dev_unref(drm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mxsfb_platform_driver = {
|
||||
.probe = mxsfb_probe,
|
||||
.remove = mxsfb_remove,
|
||||
.id_table = mxsfb_devtype,
|
||||
.driver = {
|
||||
.name = "mxsfb",
|
||||
.of_match_table = mxsfb_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(mxsfb_platform_driver);
|
||||
|
||||
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
|
||||
MODULE_DESCRIPTION("Freescale MXS DRM/KMS driver");
|
||||
MODULE_LICENSE("GPL");
|
54
drivers/gpu/drm/mxsfb/mxsfb_drv.h
Normal file
54
drivers/gpu/drm/mxsfb/mxsfb_drv.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Marek Vasut <marex@denx.de>
|
||||
*
|
||||
* i.MX23/i.MX28/i.MX6SX MXSFB LCD controller driver.
|
||||
*
|
||||
* 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.
|
||||
* 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 __MXSFB_DRV_H__
|
||||
#define __MXSFB_DRV_H__
|
||||
|
||||
struct mxsfb_devdata {
|
||||
unsigned int transfer_count;
|
||||
unsigned int cur_buf;
|
||||
unsigned int next_buf;
|
||||
unsigned int debug0;
|
||||
unsigned int hs_wdth_mask;
|
||||
unsigned int hs_wdth_shift;
|
||||
unsigned int ipversion;
|
||||
};
|
||||
|
||||
struct mxsfb_drm_private {
|
||||
const struct mxsfb_devdata *devdata;
|
||||
|
||||
void __iomem *base; /* registers */
|
||||
struct clk *clk;
|
||||
struct clk *clk_axi;
|
||||
struct clk *clk_disp_axi;
|
||||
|
||||
struct drm_simple_display_pipe pipe;
|
||||
struct drm_connector connector;
|
||||
struct drm_panel *panel;
|
||||
struct drm_fbdev_cma *fbdev;
|
||||
};
|
||||
|
||||
int mxsfb_setup_crtc(struct drm_device *dev);
|
||||
int mxsfb_create_output(struct drm_device *dev);
|
||||
|
||||
void mxsfb_enable_axi_clk(struct mxsfb_drm_private *mxsfb);
|
||||
void mxsfb_disable_axi_clk(struct mxsfb_drm_private *mxsfb);
|
||||
|
||||
void mxsfb_crtc_enable(struct mxsfb_drm_private *mxsfb);
|
||||
void mxsfb_crtc_disable(struct mxsfb_drm_private *mxsfb);
|
||||
void mxsfb_plane_atomic_update(struct mxsfb_drm_private *mxsfb,
|
||||
struct drm_plane_state *state);
|
||||
|
||||
#endif /* __MXSFB_DRV_H__ */
|
131
drivers/gpu/drm/mxsfb/mxsfb_out.c
Normal file
131
drivers/gpu/drm/mxsfb/mxsfb_out.c
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Marek Vasut <marex@denx.de>
|
||||
*
|
||||
* 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.
|
||||
* 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/of_graph.h>
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drm_simple_kms_helper.h>
|
||||
#include <drm/drmP.h>
|
||||
|
||||
#include "mxsfb_drv.h"
|
||||
|
||||
static struct mxsfb_drm_private *
|
||||
drm_connector_to_mxsfb_drm_private(struct drm_connector *connector)
|
||||
{
|
||||
return container_of(connector, struct mxsfb_drm_private, connector);
|
||||
}
|
||||
|
||||
static int mxsfb_panel_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb =
|
||||
drm_connector_to_mxsfb_drm_private(connector);
|
||||
|
||||
if (mxsfb->panel)
|
||||
return mxsfb->panel->funcs->get_modes(mxsfb->panel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct
|
||||
drm_connector_helper_funcs mxsfb_panel_connector_helper_funcs = {
|
||||
.get_modes = mxsfb_panel_get_modes,
|
||||
};
|
||||
|
||||
static enum drm_connector_status
|
||||
mxsfb_panel_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb =
|
||||
drm_connector_to_mxsfb_drm_private(connector);
|
||||
|
||||
if (mxsfb->panel)
|
||||
return connector_status_connected;
|
||||
|
||||
return connector_status_disconnected;
|
||||
}
|
||||
|
||||
static void mxsfb_panel_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb =
|
||||
drm_connector_to_mxsfb_drm_private(connector);
|
||||
|
||||
if (mxsfb->panel)
|
||||
drm_panel_detach(mxsfb->panel);
|
||||
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs mxsfb_panel_connector_funcs = {
|
||||
.dpms = drm_atomic_helper_connector_dpms,
|
||||
.detect = mxsfb_panel_connector_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = mxsfb_panel_connector_destroy,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static int mxsfb_attach_endpoint(struct drm_device *drm,
|
||||
const struct of_endpoint *ep)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = drm->dev_private;
|
||||
struct device_node *np;
|
||||
struct drm_panel *panel;
|
||||
int ret = -EPROBE_DEFER;
|
||||
|
||||
np = of_graph_get_remote_port_parent(ep->local_node);
|
||||
panel = of_drm_find_panel(np);
|
||||
of_node_put(np);
|
||||
|
||||
if (!panel)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
mxsfb->connector.dpms = DRM_MODE_DPMS_OFF;
|
||||
mxsfb->connector.polled = 0;
|
||||
drm_connector_helper_add(&mxsfb->connector,
|
||||
&mxsfb_panel_connector_helper_funcs);
|
||||
ret = drm_connector_init(drm, &mxsfb->connector,
|
||||
&mxsfb_panel_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_Unknown);
|
||||
if (!ret)
|
||||
mxsfb->panel = panel;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mxsfb_create_output(struct drm_device *drm)
|
||||
{
|
||||
struct device_node *ep_np = NULL;
|
||||
struct of_endpoint ep;
|
||||
int ret;
|
||||
|
||||
for_each_endpoint_of_node(drm->dev->of_node, ep_np) {
|
||||
ret = of_graph_parse_endpoint(ep_np, &ep);
|
||||
if (!ret)
|
||||
ret = mxsfb_attach_endpoint(drm, &ep);
|
||||
|
||||
if (ret) {
|
||||
of_node_put(ep_np);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
114
drivers/gpu/drm/mxsfb/mxsfb_regs.h
Normal file
114
drivers/gpu/drm/mxsfb/mxsfb_regs.h
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Juergen Beisert, Pengutronix
|
||||
* Copyright (C) 2016 Marek Vasut <marex@denx.de>
|
||||
*
|
||||
* i.MX23/i.MX28/i.MX6SX MXSFB LCD controller driver.
|
||||
*
|
||||
* 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.
|
||||
* 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 __MXSFB_REGS_H__
|
||||
#define __MXSFB_REGS_H__
|
||||
|
||||
#define REG_SET 4
|
||||
#define REG_CLR 8
|
||||
|
||||
#define LCDC_CTRL 0x00
|
||||
#define LCDC_CTRL1 0x10
|
||||
#define LCDC_V3_TRANSFER_COUNT 0x20
|
||||
#define LCDC_V4_TRANSFER_COUNT 0x30
|
||||
#define LCDC_V4_CUR_BUF 0x40
|
||||
#define LCDC_V4_NEXT_BUF 0x50
|
||||
#define LCDC_V3_CUR_BUF 0x30
|
||||
#define LCDC_V3_NEXT_BUF 0x40
|
||||
#define LCDC_VDCTRL0 0x70
|
||||
#define LCDC_VDCTRL1 0x80
|
||||
#define LCDC_VDCTRL2 0x90
|
||||
#define LCDC_VDCTRL3 0xa0
|
||||
#define LCDC_VDCTRL4 0xb0
|
||||
#define LCDC_V4_DEBUG0 0x1d0
|
||||
#define LCDC_V3_DEBUG0 0x1f0
|
||||
|
||||
#define CTRL_SFTRST (1 << 31)
|
||||
#define CTRL_CLKGATE (1 << 30)
|
||||
#define CTRL_BYPASS_COUNT (1 << 19)
|
||||
#define CTRL_VSYNC_MODE (1 << 18)
|
||||
#define CTRL_DOTCLK_MODE (1 << 17)
|
||||
#define CTRL_DATA_SELECT (1 << 16)
|
||||
#define CTRL_SET_BUS_WIDTH(x) (((x) & 0x3) << 10)
|
||||
#define CTRL_GET_BUS_WIDTH(x) (((x) >> 10) & 0x3)
|
||||
#define CTRL_SET_WORD_LENGTH(x) (((x) & 0x3) << 8)
|
||||
#define CTRL_GET_WORD_LENGTH(x) (((x) >> 8) & 0x3)
|
||||
#define CTRL_MASTER (1 << 5)
|
||||
#define CTRL_DF16 (1 << 3)
|
||||
#define CTRL_DF18 (1 << 2)
|
||||
#define CTRL_DF24 (1 << 1)
|
||||
#define CTRL_RUN (1 << 0)
|
||||
|
||||
#define CTRL1_FIFO_CLEAR (1 << 21)
|
||||
#define CTRL1_SET_BYTE_PACKAGING(x) (((x) & 0xf) << 16)
|
||||
#define CTRL1_GET_BYTE_PACKAGING(x) (((x) >> 16) & 0xf)
|
||||
#define CTRL1_CUR_FRAME_DONE_IRQ_EN (1 << 13)
|
||||
#define CTRL1_CUR_FRAME_DONE_IRQ (1 << 9)
|
||||
|
||||
#define TRANSFER_COUNT_SET_VCOUNT(x) (((x) & 0xffff) << 16)
|
||||
#define TRANSFER_COUNT_GET_VCOUNT(x) (((x) >> 16) & 0xffff)
|
||||
#define TRANSFER_COUNT_SET_HCOUNT(x) ((x) & 0xffff)
|
||||
#define TRANSFER_COUNT_GET_HCOUNT(x) ((x) & 0xffff)
|
||||
|
||||
#define VDCTRL0_ENABLE_PRESENT (1 << 28)
|
||||
#define VDCTRL0_VSYNC_ACT_HIGH (1 << 27)
|
||||
#define VDCTRL0_HSYNC_ACT_HIGH (1 << 26)
|
||||
#define VDCTRL0_DOTCLK_ACT_FALLING (1 << 25)
|
||||
#define VDCTRL0_ENABLE_ACT_HIGH (1 << 24)
|
||||
#define VDCTRL0_VSYNC_PERIOD_UNIT (1 << 21)
|
||||
#define VDCTRL0_VSYNC_PULSE_WIDTH_UNIT (1 << 20)
|
||||
#define VDCTRL0_HALF_LINE (1 << 19)
|
||||
#define VDCTRL0_HALF_LINE_MODE (1 << 18)
|
||||
#define VDCTRL0_SET_VSYNC_PULSE_WIDTH(x) ((x) & 0x3ffff)
|
||||
#define VDCTRL0_GET_VSYNC_PULSE_WIDTH(x) ((x) & 0x3ffff)
|
||||
|
||||
#define VDCTRL2_SET_HSYNC_PERIOD(x) ((x) & 0x3ffff)
|
||||
#define VDCTRL2_GET_HSYNC_PERIOD(x) ((x) & 0x3ffff)
|
||||
|
||||
#define VDCTRL3_MUX_SYNC_SIGNALS (1 << 29)
|
||||
#define VDCTRL3_VSYNC_ONLY (1 << 28)
|
||||
#define SET_HOR_WAIT_CNT(x) (((x) & 0xfff) << 16)
|
||||
#define GET_HOR_WAIT_CNT(x) (((x) >> 16) & 0xfff)
|
||||
#define SET_VERT_WAIT_CNT(x) ((x) & 0xffff)
|
||||
#define GET_VERT_WAIT_CNT(x) ((x) & 0xffff)
|
||||
|
||||
#define VDCTRL4_SET_DOTCLK_DLY(x) (((x) & 0x7) << 29) /* v4 only */
|
||||
#define VDCTRL4_GET_DOTCLK_DLY(x) (((x) >> 29) & 0x7) /* v4 only */
|
||||
#define VDCTRL4_SYNC_SIGNALS_ON (1 << 18)
|
||||
#define SET_DOTCLK_H_VALID_DATA_CNT(x) ((x) & 0x3ffff)
|
||||
|
||||
#define DEBUG0_HSYNC (1 < 26)
|
||||
#define DEBUG0_VSYNC (1 < 25)
|
||||
|
||||
#define MXSFB_MIN_XRES 120
|
||||
#define MXSFB_MIN_YRES 120
|
||||
#define MXSFB_MAX_XRES 0xffff
|
||||
#define MXSFB_MAX_YRES 0xffff
|
||||
|
||||
#define RED 0
|
||||
#define GREEN 1
|
||||
#define BLUE 2
|
||||
#define TRANSP 3
|
||||
|
||||
#define STMLCDIF_8BIT 1 /* pixel data bus to the display is of 8 bit width */
|
||||
#define STMLCDIF_16BIT 0 /* pixel data bus to the display is of 16 bit width */
|
||||
#define STMLCDIF_18BIT 2 /* pixel data bus to the display is of 18 bit width */
|
||||
#define STMLCDIF_24BIT 3 /* pixel data bus to the display is of 24 bit width */
|
||||
|
||||
#define MXSFB_SYNC_DATA_ENABLE_HIGH_ACT (1 << 6)
|
||||
#define MXSFB_SYNC_DOTCLK_FALLING_ACT (1 << 7) /* negative edge sampling */
|
||||
|
||||
#endif /* __MXSFB_REGS_H__ */
|
@ -30,12 +30,37 @@
|
||||
* Register locations derived from NVClock by Roderick Colenbrander
|
||||
*/
|
||||
|
||||
#include <linux/apple-gmux.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/idr.h>
|
||||
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_reg.h"
|
||||
#include "nouveau_encoder.h"
|
||||
|
||||
static struct ida bl_ida;
|
||||
#define BL_NAME_SIZE 15 // 12 for name + 2 for digits + 1 for '\0'
|
||||
|
||||
struct backlight_connector {
|
||||
struct list_head head;
|
||||
int id;
|
||||
};
|
||||
|
||||
static bool
|
||||
nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE], struct backlight_connector
|
||||
*connector)
|
||||
{
|
||||
const int nb = ida_simple_get(&bl_ida, 0, 0, GFP_KERNEL);
|
||||
if (nb < 0 || nb >= 100)
|
||||
return false;
|
||||
if (nb > 0)
|
||||
snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight%d", nb);
|
||||
else
|
||||
snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight");
|
||||
connector->id = nb;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
nv40_get_intensity(struct backlight_device *bd)
|
||||
{
|
||||
@ -74,6 +99,8 @@ nv40_backlight_init(struct drm_connector *connector)
|
||||
struct nvif_object *device = &drm->device.object;
|
||||
struct backlight_properties props;
|
||||
struct backlight_device *bd;
|
||||
struct backlight_connector bl_connector;
|
||||
char backlight_name[BL_NAME_SIZE];
|
||||
|
||||
if (!(nvif_rd32(device, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK))
|
||||
return 0;
|
||||
@ -81,10 +108,19 @@ nv40_backlight_init(struct drm_connector *connector)
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.type = BACKLIGHT_RAW;
|
||||
props.max_brightness = 31;
|
||||
bd = backlight_device_register("nv_backlight", connector->kdev, drm,
|
||||
if (!nouveau_get_backlight_name(backlight_name, &bl_connector)) {
|
||||
NV_ERROR(drm, "Failed to retrieve a unique name for the backlight interface\n");
|
||||
return 0;
|
||||
}
|
||||
bd = backlight_device_register(backlight_name , connector->kdev, drm,
|
||||
&nv40_bl_ops, &props);
|
||||
if (IS_ERR(bd))
|
||||
|
||||
if (IS_ERR(bd)) {
|
||||
if (bl_connector.id > 0)
|
||||
ida_simple_remove(&bl_ida, bl_connector.id);
|
||||
return PTR_ERR(bd);
|
||||
}
|
||||
list_add(&bl_connector.head, &drm->bl_connectors);
|
||||
drm->backlight = bd;
|
||||
bd->props.brightness = nv40_get_intensity(bd);
|
||||
backlight_update_status(bd);
|
||||
@ -182,6 +218,8 @@ nv50_backlight_init(struct drm_connector *connector)
|
||||
struct backlight_properties props;
|
||||
struct backlight_device *bd;
|
||||
const struct backlight_ops *ops;
|
||||
struct backlight_connector bl_connector;
|
||||
char backlight_name[BL_NAME_SIZE];
|
||||
|
||||
nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS);
|
||||
if (!nv_encoder) {
|
||||
@ -203,11 +241,20 @@ nv50_backlight_init(struct drm_connector *connector)
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.type = BACKLIGHT_RAW;
|
||||
props.max_brightness = 100;
|
||||
bd = backlight_device_register("nv_backlight", connector->kdev,
|
||||
if (!nouveau_get_backlight_name(backlight_name, &bl_connector)) {
|
||||
NV_ERROR(drm, "Failed to retrieve a unique name for the backlight interface\n");
|
||||
return 0;
|
||||
}
|
||||
bd = backlight_device_register(backlight_name , connector->kdev,
|
||||
nv_encoder, ops, &props);
|
||||
if (IS_ERR(bd))
|
||||
return PTR_ERR(bd);
|
||||
|
||||
if (IS_ERR(bd)) {
|
||||
if (bl_connector.id > 0)
|
||||
ida_simple_remove(&bl_ida, bl_connector.id);
|
||||
return PTR_ERR(bd);
|
||||
}
|
||||
|
||||
list_add(&bl_connector.head, &drm->bl_connectors);
|
||||
drm->backlight = bd;
|
||||
bd->props.brightness = bd->ops->get_brightness(bd);
|
||||
backlight_update_status(bd);
|
||||
@ -221,6 +268,13 @@ nouveau_backlight_init(struct drm_device *dev)
|
||||
struct nvif_device *device = &drm->device;
|
||||
struct drm_connector *connector;
|
||||
|
||||
if (apple_gmux_present()) {
|
||||
NV_INFO(drm, "Apple GMUX detected: not registering Nouveau backlight interface\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&drm->bl_connectors);
|
||||
|
||||
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
||||
if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS &&
|
||||
connector->connector_type != DRM_MODE_CONNECTOR_eDP)
|
||||
@ -247,9 +301,27 @@ void
|
||||
nouveau_backlight_exit(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct backlight_connector *connector;
|
||||
|
||||
list_for_each_entry(connector, &drm->bl_connectors, head) {
|
||||
if (connector->id >= 0)
|
||||
ida_simple_remove(&bl_ida, connector->id);
|
||||
}
|
||||
|
||||
if (drm->backlight) {
|
||||
backlight_device_unregister(drm->backlight);
|
||||
drm->backlight = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_backlight_ctor(void)
|
||||
{
|
||||
ida_init(&bl_ida);
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_backlight_dtor(void)
|
||||
{
|
||||
ida_destroy(&bl_ida);
|
||||
}
|
||||
|
@ -1209,6 +1209,7 @@ nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem)
|
||||
nvbo->page_shift != vma->vm->mmu->lpg_shift)) {
|
||||
nvkm_vm_map(vma, new_mem->mm_node);
|
||||
} else {
|
||||
WARN_ON(ttm_bo_wait(bo, false, false));
|
||||
nvkm_vm_unmap(vma);
|
||||
}
|
||||
}
|
||||
|
@ -91,6 +91,8 @@ int nouveau_crtc_set_config(struct drm_mode_set *set);
|
||||
#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
|
||||
extern int nouveau_backlight_init(struct drm_device *);
|
||||
extern void nouveau_backlight_exit(struct drm_device *);
|
||||
extern void nouveau_backlight_ctor(void);
|
||||
extern void nouveau_backlight_dtor(void);
|
||||
#else
|
||||
static inline int
|
||||
nouveau_backlight_init(struct drm_device *dev)
|
||||
@ -101,6 +103,14 @@ nouveau_backlight_init(struct drm_device *dev)
|
||||
static inline void
|
||||
nouveau_backlight_exit(struct drm_device *dev) {
|
||||
}
|
||||
|
||||
static inline void
|
||||
nouveau_backlight_ctor(void) {
|
||||
}
|
||||
|
||||
static inline void
|
||||
nouveau_backlight_dtor(void) {
|
||||
}
|
||||
#endif
|
||||
|
||||
struct drm_framebuffer *
|
||||
|
@ -1122,6 +1122,7 @@ nouveau_drm_init(void)
|
||||
#endif
|
||||
|
||||
nouveau_register_dsm_handler();
|
||||
nouveau_backlight_ctor();
|
||||
return drm_pci_init(&driver_pci, &nouveau_drm_pci_driver);
|
||||
}
|
||||
|
||||
@ -1132,6 +1133,7 @@ nouveau_drm_exit(void)
|
||||
return;
|
||||
|
||||
drm_pci_exit(&driver_pci, &nouveau_drm_pci_driver);
|
||||
nouveau_backlight_dtor();
|
||||
nouveau_unregister_dsm_handler();
|
||||
|
||||
#ifdef CONFIG_NOUVEAU_PLATFORM_DRIVER
|
||||
|
@ -163,6 +163,7 @@ struct nouveau_drm {
|
||||
struct nvbios vbios;
|
||||
struct nouveau_display *display;
|
||||
struct backlight_device *backlight;
|
||||
struct list_head bl_connectors;
|
||||
struct work_struct hpd_work;
|
||||
#ifdef CONFIG_ACPI
|
||||
struct notifier_block acpi_nb;
|
||||
|
@ -1726,6 +1726,11 @@ nv50_head_core_set(struct nv50_head *head, struct nv50_head_atom *asyh)
|
||||
evo_data(push, asyh->core.handle);
|
||||
evo_mthd(push, 0x08c0 + head->base.index * 0x400, 1);
|
||||
evo_data(push, (asyh->core.y << 16) | asyh->core.x);
|
||||
/* EVO will complain with INVALID_STATE if we have an
|
||||
* active cursor and (re)specify HeadSetContextDmaIso
|
||||
* without also updating HeadSetOffsetCursor.
|
||||
*/
|
||||
asyh->set.curs = asyh->curs.visible;
|
||||
} else
|
||||
if (core->base.user.oclass < GF110_DISP_CORE_CHANNEL_DMA) {
|
||||
evo_mthd(push, 0x0860 + head->base.index * 0x400, 1);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user