From 5843f4e02fbe86a59981e35adc6cabebee46fdc0 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 1 Nov 2017 15:20:04 +0100 Subject: drm/fourcc: Fix fourcc_mod_code() definition Avoid a compiler warnings when the val parameter is an expression. Reviewed-by: Daniel Vetter Signed-off-by: Thierry Reding --- include/uapi/drm/drm_fourcc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index 3ad838d3f93f..a76ed8f9e383 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -188,7 +188,7 @@ extern "C" { #define DRM_FORMAT_RESERVED ((1ULL << 56) - 1) #define fourcc_mod_code(vendor, val) \ - ((((__u64)DRM_FORMAT_MOD_VENDOR_## vendor) << 56) | (val & 0x00ffffffffffffffULL)) + ((((__u64)DRM_FORMAT_MOD_VENDOR_## vendor) << 56) | ((val) & 0x00ffffffffffffffULL)) /* * Format Modifier tokens: -- cgit v1.2.3 From 268892cb63a822315921a8dab48ac3e4abf7dd03 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 12 Oct 2017 16:39:20 +0200 Subject: drm/tegra: Sanitize format modifiers The existing format modifier definitions were merged prematurely, and recent work has unveiled that the definitions are suboptimal in several ways: - The format specifiers, except for one, are not Tegra specific, but the names don't reflect that. - The number space is split into two, reserving 32 bits for some "parameter" which most of the modifiers are not going to have. - Symbolic names for the modifiers are not using the standard DRM_FORMAT_MOD_* prefix, which makes them awkward to use. - The vendor prefix NV is somewhat ambiguous. Fortunately, nobody's started using these modifiers, so we can still fix the above issues. Do so by using the standard prefix. Also, remove TEGRA from the name of those modifiers that exist on NVIDIA GPUs as well. In case of the block linear modifiers, make the "parameter" smaller (4 bits, though only 6 values are valid) and don't let that leak into any of the other modifiers. Finally, also use the more canonical NVIDIA instead of the ambiguous NV prefix. Acked-by: Daniel Vetter Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/fb.c | 35 +++++++++++++++++++++++++++++------ include/uapi/drm/drm_fourcc.h | 36 +++++++++++++++++++----------------- 2 files changed, 48 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 80540c1c66dc..406e895d82cc 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -54,17 +54,40 @@ int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer, struct tegra_fb *fb = to_tegra_fb(framebuffer); uint64_t modifier = fb->base.modifier; - switch (fourcc_mod_tegra_mod(modifier)) { - case NV_FORMAT_MOD_TEGRA_TILED: + switch (modifier) { + case DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED: tiling->mode = TEGRA_BO_TILING_MODE_TILED; tiling->value = 0; break; - case NV_FORMAT_MOD_TEGRA_16BX2_BLOCK(0): + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0): tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; - tiling->value = fourcc_mod_tegra_param(modifier); - if (tiling->value > 5) - return -EINVAL; + tiling->value = 0; + break; + + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1): + tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; + tiling->value = 1; + break; + + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2): + tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; + tiling->value = 2; + break; + + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3): + tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; + tiling->value = 3; + break; + + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4): + tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; + tiling->value = 4; + break; + + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5): + tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; + tiling->value = 5; break; default: diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index a76ed8f9e383..e04613d30a13 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -178,7 +178,7 @@ extern "C" { #define DRM_FORMAT_MOD_VENDOR_NONE 0 #define DRM_FORMAT_MOD_VENDOR_INTEL 0x01 #define DRM_FORMAT_MOD_VENDOR_AMD 0x02 -#define DRM_FORMAT_MOD_VENDOR_NV 0x03 +#define DRM_FORMAT_MOD_VENDOR_NVIDIA 0x03 #define DRM_FORMAT_MOD_VENDOR_SAMSUNG 0x04 #define DRM_FORMAT_MOD_VENDOR_QCOM 0x05 #define DRM_FORMAT_MOD_VENDOR_VIVANTE 0x06 @@ -338,29 +338,17 @@ extern "C" { */ #define DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED fourcc_mod_code(VIVANTE, 4) -/* NVIDIA Tegra frame buffer modifiers */ - -/* - * Some modifiers take parameters, for example the number of vertical GOBs in - * a block. Reserve the lower 32 bits for parameters - */ -#define __fourcc_mod_tegra_mode_shift 32 -#define fourcc_mod_tegra_code(val, params) \ - fourcc_mod_code(NV, ((((__u64)val) << __fourcc_mod_tegra_mode_shift) | params)) -#define fourcc_mod_tegra_mod(m) \ - (m & ~((1ULL << __fourcc_mod_tegra_mode_shift) - 1)) -#define fourcc_mod_tegra_param(m) \ - (m & ((1ULL << __fourcc_mod_tegra_mode_shift) - 1)) +/* NVIDIA frame buffer modifiers */ /* * Tegra Tiled Layout, used by Tegra 2, 3 and 4. * * Pixels are arranged in simple tiles of 16 x 16 bytes. */ -#define NV_FORMAT_MOD_TEGRA_TILED fourcc_mod_tegra_code(1, 0) +#define DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED fourcc_mod_code(NVIDIA, 1) /* - * Tegra 16Bx2 Block Linear layout, used by TK1/TX1 + * 16Bx2 Block Linear layout, used by desktop GPUs, and Tegra K1 and later * * Pixels are arranged in 64x8 Groups Of Bytes (GOBs). GOBs are then stacked * vertically by a power of 2 (1 to 32 GOBs) to form a block. @@ -380,7 +368,21 @@ extern "C" { * Chapter 20 "Pixel Memory Formats" of the Tegra X1 TRM describes this format * in full detail. */ -#define NV_FORMAT_MOD_TEGRA_16BX2_BLOCK(v) fourcc_mod_tegra_code(2, v) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(v) \ + fourcc_mod_code(NVIDIA, 0x10 | ((v) & 0xf)) + +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_ONE_GOB \ + fourcc_mod_code(NVIDIA, 0x10) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_TWO_GOB \ + fourcc_mod_code(NVIDIA, 0x11) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_FOUR_GOB \ + fourcc_mod_code(NVIDIA, 0x12) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_EIGHT_GOB \ + fourcc_mod_code(NVIDIA, 0x13) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_SIXTEEN_GOB \ + fourcc_mod_code(NVIDIA, 0x14) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_THIRTYTWO_GOB \ + fourcc_mod_code(NVIDIA, 0x15) /* * Broadcom VC4 "T" format -- cgit v1.2.3 From 1f876c3fceadecbb8753e9a351ae3591d0de610e Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 31 Jul 2017 11:18:44 +0200 Subject: gpu: host1x: Rewrite conditional for better readability The current check is slightly difficult to read, rewrite it to improve that a little. Signed-off-by: Thierry Reding --- drivers/gpu/host1x/dev.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c index bf67c3aeb634..1f916b579e95 100644 --- a/drivers/gpu/host1x/dev.c +++ b/drivers/gpu/host1x/dev.c @@ -227,11 +227,13 @@ static int host1x_probe(struct platform_device *pdev) return -ENOMEM; err = iommu_attach_device(host->domain, &pdev->dev); - if (err == -ENODEV) { - iommu_domain_free(host->domain); - host->domain = NULL; - goto skip_iommu; - } else if (err) { + if (err) { + if (err == -ENODEV) { + iommu_domain_free(host->domain); + host->domain = NULL; + goto skip_iommu; + } + goto fail_free_domain; } -- cgit v1.2.3 From 8f7da1578e90bf1af30269dc6dd8806c96e9c683 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 8 Nov 2017 13:08:21 +0100 Subject: gpu: host1x: Cleanup on initialization failure When an error happens during the initialization of one of the sub- devices, make sure to properly cleanup all sub-devices that have been initialized up to that point. Signed-off-by: Thierry Reding --- drivers/gpu/host1x/bus.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c index 2e57c9cea696..88a3558b7916 100644 --- a/drivers/gpu/host1x/bus.c +++ b/drivers/gpu/host1x/bus.c @@ -211,8 +211,7 @@ int host1x_device_init(struct host1x_device *device) dev_err(&device->dev, "failed to initialize %s: %d\n", dev_name(client->dev), err); - mutex_unlock(&device->clients_lock); - return err; + goto teardown; } } } @@ -220,6 +219,14 @@ int host1x_device_init(struct host1x_device *device) mutex_unlock(&device->clients_lock); return 0; + +teardown: + list_for_each_entry_continue_reverse(client, &device->clients, list) + if (client->ops->exit) + client->ops->exit(client); + + mutex_unlock(&device->clients_lock); + return err; } EXPORT_SYMBOL(host1x_device_init); -- cgit v1.2.3 From 91cc07069291d83b3fa90615373640e028068aaf Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 30 Aug 2017 12:04:07 +0200 Subject: dt-bindings: display: tegra: Update SOR for Tegra186 Update the SOR bindings for Tegra186, in which a new property is required to identify the instance of the SOR interface and the clock tree has slightly changed as well. Signed-off-by: Thierry Reding --- .../bindings/display/tegra/nvidia,tegra20-host1x.txt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt index 844e0103fb0d..593be44a53c9 100644 --- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt +++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt @@ -206,21 +206,33 @@ of the following host1x client modules: - "nvidia,tegra132-sor": for Tegra132 - "nvidia,tegra210-sor": for Tegra210 - "nvidia,tegra210-sor1": for Tegra210 + - "nvidia,tegra186-sor": for Tegra186 + - "nvidia,tegra186-sor1": for Tegra186 - reg: Physical base address and length of the controller's registers. - interrupts: The interrupt outputs from the controller. - clocks: Must contain an entry for each entry in clock-names. See ../clocks/clock-bindings.txt for details. - clock-names: Must include the following entries: - sor: clock input for the SOR hardware - - source: source clock for the SOR clock + - out: SOR output clock - parent: input for the pixel clock - dp: reference clock for the SOR clock - safe: safe reference for the SOR clock during power up + + For Tegra186 and later: + - pad: SOR pad output clock (on Tegra186 and later) + + Obsolete: + - source: source clock for the SOR clock (obsolete, use "out" instead) + - resets: Must contain an entry for each entry in reset-names. See ../reset/reset.txt for details. - reset-names: Must include the following entries: - sor + Required properties on Tegra186 and later: + - nvidia,interface: index of the SOR interface + Optional properties: - nvidia,ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing - nvidia,hpd-gpio: specifies a GPIO used for hotplug detection -- cgit v1.2.3 From cf6824ac7261c5e2cd98bf7aafe5d132431e0d7c Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 10 Nov 2017 12:13:22 +0100 Subject: drm/tegra: dc: Move register definitions into a table After commit 67e04d1ab19b ("drm/tegra: dc: Trace register accesses"), the debugfs register dump implementation causes excessive stack usage and can result in build warnings. To fix this, move the register definitions into a table and iterate over the table while dumping the registers to debugfs. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 442 +++++++++++++++++++++++---------------------- 1 file changed, 224 insertions(+), 218 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index fc70351b9017..0c77473d6510 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1383,10 +1383,228 @@ static irqreturn_t tegra_dc_irq(int irq, void *data) return IRQ_HANDLED; } +#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name } + +static const struct debugfs_reg32 tegra_dc_regs[] = { + DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT), + DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL), + DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_ERROR), + DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT), + DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL), + DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_ERROR), + DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT), + DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL), + DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_ERROR), + DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT), + DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL), + DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_ERROR), + DEBUGFS_REG32(DC_CMD_CONT_SYNCPT_VSYNC), + DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND_OPTION0), + DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND), + DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE), + DEBUGFS_REG32(DC_CMD_DISPLAY_POWER_CONTROL), + DEBUGFS_REG32(DC_CMD_INT_STATUS), + DEBUGFS_REG32(DC_CMD_INT_MASK), + DEBUGFS_REG32(DC_CMD_INT_ENABLE), + DEBUGFS_REG32(DC_CMD_INT_TYPE), + DEBUGFS_REG32(DC_CMD_INT_POLARITY), + DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE1), + DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE2), + DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE3), + DEBUGFS_REG32(DC_CMD_STATE_ACCESS), + DEBUGFS_REG32(DC_CMD_STATE_CONTROL), + DEBUGFS_REG32(DC_CMD_DISPLAY_WINDOW_HEADER), + DEBUGFS_REG32(DC_CMD_REG_ACT_CONTROL), + DEBUGFS_REG32(DC_COM_CRC_CONTROL), + DEBUGFS_REG32(DC_COM_CRC_CHECKSUM), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(0)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(1)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(2)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(3)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(0)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(1)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(2)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(3)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(0)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(1)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(2)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(3)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(0)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(1)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(2)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(3)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(0)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(1)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(0)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(1)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(2)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(3)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(4)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(5)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(6)), + DEBUGFS_REG32(DC_COM_PIN_MISC_CONTROL), + DEBUGFS_REG32(DC_COM_PIN_PM0_CONTROL), + DEBUGFS_REG32(DC_COM_PIN_PM0_DUTY_CYCLE), + DEBUGFS_REG32(DC_COM_PIN_PM1_CONTROL), + DEBUGFS_REG32(DC_COM_PIN_PM1_DUTY_CYCLE), + DEBUGFS_REG32(DC_COM_SPI_CONTROL), + DEBUGFS_REG32(DC_COM_SPI_START_BYTE), + DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_AB), + DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_CD), + DEBUGFS_REG32(DC_COM_HSPI_CS_DC), + DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_A), + DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_B), + DEBUGFS_REG32(DC_COM_GPIO_CTRL), + DEBUGFS_REG32(DC_COM_GPIO_DEBOUNCE_COUNTER), + DEBUGFS_REG32(DC_COM_CRC_CHECKSUM_LATCHED), + DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS0), + DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS1), + DEBUGFS_REG32(DC_DISP_DISP_WIN_OPTIONS), + DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY), + DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER), + DEBUGFS_REG32(DC_DISP_DISP_TIMING_OPTIONS), + DEBUGFS_REG32(DC_DISP_REF_TO_SYNC), + DEBUGFS_REG32(DC_DISP_SYNC_WIDTH), + DEBUGFS_REG32(DC_DISP_BACK_PORCH), + DEBUGFS_REG32(DC_DISP_ACTIVE), + DEBUGFS_REG32(DC_DISP_FRONT_PORCH), + DEBUGFS_REG32(DC_DISP_H_PULSE0_CONTROL), + DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_A), + DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_B), + DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_C), + DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_D), + DEBUGFS_REG32(DC_DISP_H_PULSE1_CONTROL), + DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_A), + DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_B), + DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_C), + DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_D), + DEBUGFS_REG32(DC_DISP_H_PULSE2_CONTROL), + DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_A), + DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_B), + DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_C), + DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_D), + DEBUGFS_REG32(DC_DISP_V_PULSE0_CONTROL), + DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_A), + DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_B), + DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_C), + DEBUGFS_REG32(DC_DISP_V_PULSE1_CONTROL), + DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_A), + DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_B), + DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_C), + DEBUGFS_REG32(DC_DISP_V_PULSE2_CONTROL), + DEBUGFS_REG32(DC_DISP_V_PULSE2_POSITION_A), + DEBUGFS_REG32(DC_DISP_V_PULSE3_CONTROL), + DEBUGFS_REG32(DC_DISP_V_PULSE3_POSITION_A), + DEBUGFS_REG32(DC_DISP_M0_CONTROL), + DEBUGFS_REG32(DC_DISP_M1_CONTROL), + DEBUGFS_REG32(DC_DISP_DI_CONTROL), + DEBUGFS_REG32(DC_DISP_PP_CONTROL), + DEBUGFS_REG32(DC_DISP_PP_SELECT_A), + DEBUGFS_REG32(DC_DISP_PP_SELECT_B), + DEBUGFS_REG32(DC_DISP_PP_SELECT_C), + DEBUGFS_REG32(DC_DISP_PP_SELECT_D), + DEBUGFS_REG32(DC_DISP_DISP_CLOCK_CONTROL), + DEBUGFS_REG32(DC_DISP_DISP_INTERFACE_CONTROL), + DEBUGFS_REG32(DC_DISP_DISP_COLOR_CONTROL), + DEBUGFS_REG32(DC_DISP_SHIFT_CLOCK_OPTIONS), + DEBUGFS_REG32(DC_DISP_DATA_ENABLE_OPTIONS), + DEBUGFS_REG32(DC_DISP_SERIAL_INTERFACE_OPTIONS), + DEBUGFS_REG32(DC_DISP_LCD_SPI_OPTIONS), + DEBUGFS_REG32(DC_DISP_BORDER_COLOR), + DEBUGFS_REG32(DC_DISP_COLOR_KEY0_LOWER), + DEBUGFS_REG32(DC_DISP_COLOR_KEY0_UPPER), + DEBUGFS_REG32(DC_DISP_COLOR_KEY1_LOWER), + DEBUGFS_REG32(DC_DISP_COLOR_KEY1_UPPER), + DEBUGFS_REG32(DC_DISP_CURSOR_FOREGROUND), + DEBUGFS_REG32(DC_DISP_CURSOR_BACKGROUND), + DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR), + DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_NS), + DEBUGFS_REG32(DC_DISP_CURSOR_POSITION), + DEBUGFS_REG32(DC_DISP_CURSOR_POSITION_NS), + DEBUGFS_REG32(DC_DISP_INIT_SEQ_CONTROL), + DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_A), + DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_B), + DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_C), + DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_D), + DEBUGFS_REG32(DC_DISP_DC_MCCIF_FIFOCTRL), + DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0A_HYST), + DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0B_HYST), + DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1A_HYST), + DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1B_HYST), + DEBUGFS_REG32(DC_DISP_DAC_CRT_CTRL), + DEBUGFS_REG32(DC_DISP_DISP_MISC_CONTROL), + DEBUGFS_REG32(DC_DISP_SD_CONTROL), + DEBUGFS_REG32(DC_DISP_SD_CSC_COEFF), + DEBUGFS_REG32(DC_DISP_SD_LUT(0)), + DEBUGFS_REG32(DC_DISP_SD_LUT(1)), + DEBUGFS_REG32(DC_DISP_SD_LUT(2)), + DEBUGFS_REG32(DC_DISP_SD_LUT(3)), + DEBUGFS_REG32(DC_DISP_SD_LUT(4)), + DEBUGFS_REG32(DC_DISP_SD_LUT(5)), + DEBUGFS_REG32(DC_DISP_SD_LUT(6)), + DEBUGFS_REG32(DC_DISP_SD_LUT(7)), + DEBUGFS_REG32(DC_DISP_SD_LUT(8)), + DEBUGFS_REG32(DC_DISP_SD_FLICKER_CONTROL), + DEBUGFS_REG32(DC_DISP_DC_PIXEL_COUNT), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(0)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(1)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(2)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(3)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(4)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(5)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(6)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(7)), + DEBUGFS_REG32(DC_DISP_SD_BL_TF(0)), + DEBUGFS_REG32(DC_DISP_SD_BL_TF(1)), + DEBUGFS_REG32(DC_DISP_SD_BL_TF(2)), + DEBUGFS_REG32(DC_DISP_SD_BL_TF(3)), + DEBUGFS_REG32(DC_DISP_SD_BL_CONTROL), + DEBUGFS_REG32(DC_DISP_SD_HW_K_VALUES), + DEBUGFS_REG32(DC_DISP_SD_MAN_K_VALUES), + DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_HI), + DEBUGFS_REG32(DC_DISP_BLEND_CURSOR_CONTROL), + DEBUGFS_REG32(DC_WIN_WIN_OPTIONS), + DEBUGFS_REG32(DC_WIN_BYTE_SWAP), + DEBUGFS_REG32(DC_WIN_BUFFER_CONTROL), + DEBUGFS_REG32(DC_WIN_COLOR_DEPTH), + DEBUGFS_REG32(DC_WIN_POSITION), + DEBUGFS_REG32(DC_WIN_SIZE), + DEBUGFS_REG32(DC_WIN_PRESCALED_SIZE), + DEBUGFS_REG32(DC_WIN_H_INITIAL_DDA), + DEBUGFS_REG32(DC_WIN_V_INITIAL_DDA), + DEBUGFS_REG32(DC_WIN_DDA_INC), + DEBUGFS_REG32(DC_WIN_LINE_STRIDE), + DEBUGFS_REG32(DC_WIN_BUF_STRIDE), + DEBUGFS_REG32(DC_WIN_UV_BUF_STRIDE), + DEBUGFS_REG32(DC_WIN_BUFFER_ADDR_MODE), + DEBUGFS_REG32(DC_WIN_DV_CONTROL), + DEBUGFS_REG32(DC_WIN_BLEND_NOKEY), + DEBUGFS_REG32(DC_WIN_BLEND_1WIN), + DEBUGFS_REG32(DC_WIN_BLEND_2WIN_X), + DEBUGFS_REG32(DC_WIN_BLEND_2WIN_Y), + DEBUGFS_REG32(DC_WIN_BLEND_3WIN_XY), + DEBUGFS_REG32(DC_WIN_HP_FETCH_CONTROL), + DEBUGFS_REG32(DC_WINBUF_START_ADDR), + DEBUGFS_REG32(DC_WINBUF_START_ADDR_NS), + DEBUGFS_REG32(DC_WINBUF_START_ADDR_U), + DEBUGFS_REG32(DC_WINBUF_START_ADDR_U_NS), + DEBUGFS_REG32(DC_WINBUF_START_ADDR_V), + DEBUGFS_REG32(DC_WINBUF_START_ADDR_V_NS), + DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET), + DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET_NS), + DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET), + DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET_NS), + DEBUGFS_REG32(DC_WINBUF_UFLOW_STATUS), + DEBUGFS_REG32(DC_WINBUF_AD_UFLOW_STATUS), + DEBUGFS_REG32(DC_WINBUF_BD_UFLOW_STATUS), + DEBUGFS_REG32(DC_WINBUF_CD_UFLOW_STATUS), +}; + static int tegra_dc_show_regs(struct seq_file *s, void *data) { struct drm_info_node *node = s->private; struct tegra_dc *dc = node->info_ent->data; + unsigned int i; int err = 0; drm_modeset_lock(&dc->base.mutex, NULL); @@ -1396,224 +1614,12 @@ static int tegra_dc_show_regs(struct seq_file *s, void *data) goto unlock; } -#define DUMP_REG(name) \ - seq_printf(s, "%-40s %#05x %08x\n", #name, name, \ - tegra_dc_readl(dc, name)) - - DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT); - DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); - DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT_ERROR); - DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT); - DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL); - DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT_ERROR); - DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT); - DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL); - DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT_ERROR); - DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT); - DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL); - DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT_ERROR); - DUMP_REG(DC_CMD_CONT_SYNCPT_VSYNC); - DUMP_REG(DC_CMD_DISPLAY_COMMAND_OPTION0); - DUMP_REG(DC_CMD_DISPLAY_COMMAND); - DUMP_REG(DC_CMD_SIGNAL_RAISE); - DUMP_REG(DC_CMD_DISPLAY_POWER_CONTROL); - DUMP_REG(DC_CMD_INT_STATUS); - DUMP_REG(DC_CMD_INT_MASK); - DUMP_REG(DC_CMD_INT_ENABLE); - DUMP_REG(DC_CMD_INT_TYPE); - DUMP_REG(DC_CMD_INT_POLARITY); - DUMP_REG(DC_CMD_SIGNAL_RAISE1); - DUMP_REG(DC_CMD_SIGNAL_RAISE2); - DUMP_REG(DC_CMD_SIGNAL_RAISE3); - DUMP_REG(DC_CMD_STATE_ACCESS); - DUMP_REG(DC_CMD_STATE_CONTROL); - DUMP_REG(DC_CMD_DISPLAY_WINDOW_HEADER); - DUMP_REG(DC_CMD_REG_ACT_CONTROL); - DUMP_REG(DC_COM_CRC_CONTROL); - DUMP_REG(DC_COM_CRC_CHECKSUM); - DUMP_REG(DC_COM_PIN_OUTPUT_ENABLE(0)); - DUMP_REG(DC_COM_PIN_OUTPUT_ENABLE(1)); - DUMP_REG(DC_COM_PIN_OUTPUT_ENABLE(2)); - DUMP_REG(DC_COM_PIN_OUTPUT_ENABLE(3)); - DUMP_REG(DC_COM_PIN_OUTPUT_POLARITY(0)); - DUMP_REG(DC_COM_PIN_OUTPUT_POLARITY(1)); - DUMP_REG(DC_COM_PIN_OUTPUT_POLARITY(2)); - DUMP_REG(DC_COM_PIN_OUTPUT_POLARITY(3)); - DUMP_REG(DC_COM_PIN_OUTPUT_DATA(0)); - DUMP_REG(DC_COM_PIN_OUTPUT_DATA(1)); - DUMP_REG(DC_COM_PIN_OUTPUT_DATA(2)); - DUMP_REG(DC_COM_PIN_OUTPUT_DATA(3)); - DUMP_REG(DC_COM_PIN_INPUT_ENABLE(0)); - DUMP_REG(DC_COM_PIN_INPUT_ENABLE(1)); - DUMP_REG(DC_COM_PIN_INPUT_ENABLE(2)); - DUMP_REG(DC_COM_PIN_INPUT_ENABLE(3)); - DUMP_REG(DC_COM_PIN_INPUT_DATA(0)); - DUMP_REG(DC_COM_PIN_INPUT_DATA(1)); - DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(0)); - DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(1)); - DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(2)); - DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(3)); - DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(4)); - DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(5)); - DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(6)); - DUMP_REG(DC_COM_PIN_MISC_CONTROL); - DUMP_REG(DC_COM_PIN_PM0_CONTROL); - DUMP_REG(DC_COM_PIN_PM0_DUTY_CYCLE); - DUMP_REG(DC_COM_PIN_PM1_CONTROL); - DUMP_REG(DC_COM_PIN_PM1_DUTY_CYCLE); - DUMP_REG(DC_COM_SPI_CONTROL); - DUMP_REG(DC_COM_SPI_START_BYTE); - DUMP_REG(DC_COM_HSPI_WRITE_DATA_AB); - DUMP_REG(DC_COM_HSPI_WRITE_DATA_CD); - DUMP_REG(DC_COM_HSPI_CS_DC); - DUMP_REG(DC_COM_SCRATCH_REGISTER_A); - DUMP_REG(DC_COM_SCRATCH_REGISTER_B); - DUMP_REG(DC_COM_GPIO_CTRL); - DUMP_REG(DC_COM_GPIO_DEBOUNCE_COUNTER); - DUMP_REG(DC_COM_CRC_CHECKSUM_LATCHED); - DUMP_REG(DC_DISP_DISP_SIGNAL_OPTIONS0); - DUMP_REG(DC_DISP_DISP_SIGNAL_OPTIONS1); - DUMP_REG(DC_DISP_DISP_WIN_OPTIONS); - DUMP_REG(DC_DISP_DISP_MEM_HIGH_PRIORITY); - DUMP_REG(DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); - DUMP_REG(DC_DISP_DISP_TIMING_OPTIONS); - DUMP_REG(DC_DISP_REF_TO_SYNC); - DUMP_REG(DC_DISP_SYNC_WIDTH); - DUMP_REG(DC_DISP_BACK_PORCH); - DUMP_REG(DC_DISP_ACTIVE); - DUMP_REG(DC_DISP_FRONT_PORCH); - DUMP_REG(DC_DISP_H_PULSE0_CONTROL); - DUMP_REG(DC_DISP_H_PULSE0_POSITION_A); - DUMP_REG(DC_DISP_H_PULSE0_POSITION_B); - DUMP_REG(DC_DISP_H_PULSE0_POSITION_C); - DUMP_REG(DC_DISP_H_PULSE0_POSITION_D); - DUMP_REG(DC_DISP_H_PULSE1_CONTROL); - DUMP_REG(DC_DISP_H_PULSE1_POSITION_A); - DUMP_REG(DC_DISP_H_PULSE1_POSITION_B); - DUMP_REG(DC_DISP_H_PULSE1_POSITION_C); - DUMP_REG(DC_DISP_H_PULSE1_POSITION_D); - DUMP_REG(DC_DISP_H_PULSE2_CONTROL); - DUMP_REG(DC_DISP_H_PULSE2_POSITION_A); - DUMP_REG(DC_DISP_H_PULSE2_POSITION_B); - DUMP_REG(DC_DISP_H_PULSE2_POSITION_C); - DUMP_REG(DC_DISP_H_PULSE2_POSITION_D); - DUMP_REG(DC_DISP_V_PULSE0_CONTROL); - DUMP_REG(DC_DISP_V_PULSE0_POSITION_A); - DUMP_REG(DC_DISP_V_PULSE0_POSITION_B); - DUMP_REG(DC_DISP_V_PULSE0_POSITION_C); - DUMP_REG(DC_DISP_V_PULSE1_CONTROL); - DUMP_REG(DC_DISP_V_PULSE1_POSITION_A); - DUMP_REG(DC_DISP_V_PULSE1_POSITION_B); - DUMP_REG(DC_DISP_V_PULSE1_POSITION_C); - DUMP_REG(DC_DISP_V_PULSE2_CONTROL); - DUMP_REG(DC_DISP_V_PULSE2_POSITION_A); - DUMP_REG(DC_DISP_V_PULSE3_CONTROL); - DUMP_REG(DC_DISP_V_PULSE3_POSITION_A); - DUMP_REG(DC_DISP_M0_CONTROL); - DUMP_REG(DC_DISP_M1_CONTROL); - DUMP_REG(DC_DISP_DI_CONTROL); - DUMP_REG(DC_DISP_PP_CONTROL); - DUMP_REG(DC_DISP_PP_SELECT_A); - DUMP_REG(DC_DISP_PP_SELECT_B); - DUMP_REG(DC_DISP_PP_SELECT_C); - DUMP_REG(DC_DISP_PP_SELECT_D); - DUMP_REG(DC_DISP_DISP_CLOCK_CONTROL); - DUMP_REG(DC_DISP_DISP_INTERFACE_CONTROL); - DUMP_REG(DC_DISP_DISP_COLOR_CONTROL); - DUMP_REG(DC_DISP_SHIFT_CLOCK_OPTIONS); - DUMP_REG(DC_DISP_DATA_ENABLE_OPTIONS); - DUMP_REG(DC_DISP_SERIAL_INTERFACE_OPTIONS); - DUMP_REG(DC_DISP_LCD_SPI_OPTIONS); - DUMP_REG(DC_DISP_BORDER_COLOR); - DUMP_REG(DC_DISP_COLOR_KEY0_LOWER); - DUMP_REG(DC_DISP_COLOR_KEY0_UPPER); - DUMP_REG(DC_DISP_COLOR_KEY1_LOWER); - DUMP_REG(DC_DISP_COLOR_KEY1_UPPER); - DUMP_REG(DC_DISP_CURSOR_FOREGROUND); - DUMP_REG(DC_DISP_CURSOR_BACKGROUND); - DUMP_REG(DC_DISP_CURSOR_START_ADDR); - DUMP_REG(DC_DISP_CURSOR_START_ADDR_NS); - DUMP_REG(DC_DISP_CURSOR_POSITION); - DUMP_REG(DC_DISP_CURSOR_POSITION_NS); - DUMP_REG(DC_DISP_INIT_SEQ_CONTROL); - DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_A); - DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_B); - DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_C); - DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_D); - DUMP_REG(DC_DISP_DC_MCCIF_FIFOCTRL); - DUMP_REG(DC_DISP_MCCIF_DISPLAY0A_HYST); - DUMP_REG(DC_DISP_MCCIF_DISPLAY0B_HYST); - DUMP_REG(DC_DISP_MCCIF_DISPLAY1A_HYST); - DUMP_REG(DC_DISP_MCCIF_DISPLAY1B_HYST); - DUMP_REG(DC_DISP_DAC_CRT_CTRL); - DUMP_REG(DC_DISP_DISP_MISC_CONTROL); - DUMP_REG(DC_DISP_SD_CONTROL); - DUMP_REG(DC_DISP_SD_CSC_COEFF); - DUMP_REG(DC_DISP_SD_LUT(0)); - DUMP_REG(DC_DISP_SD_LUT(1)); - DUMP_REG(DC_DISP_SD_LUT(2)); - DUMP_REG(DC_DISP_SD_LUT(3)); - DUMP_REG(DC_DISP_SD_LUT(4)); - DUMP_REG(DC_DISP_SD_LUT(5)); - DUMP_REG(DC_DISP_SD_LUT(6)); - DUMP_REG(DC_DISP_SD_LUT(7)); - DUMP_REG(DC_DISP_SD_LUT(8)); - DUMP_REG(DC_DISP_SD_FLICKER_CONTROL); - DUMP_REG(DC_DISP_DC_PIXEL_COUNT); - DUMP_REG(DC_DISP_SD_HISTOGRAM(0)); - DUMP_REG(DC_DISP_SD_HISTOGRAM(1)); - DUMP_REG(DC_DISP_SD_HISTOGRAM(2)); - DUMP_REG(DC_DISP_SD_HISTOGRAM(3)); - DUMP_REG(DC_DISP_SD_HISTOGRAM(4)); - DUMP_REG(DC_DISP_SD_HISTOGRAM(5)); - DUMP_REG(DC_DISP_SD_HISTOGRAM(6)); - DUMP_REG(DC_DISP_SD_HISTOGRAM(7)); - DUMP_REG(DC_DISP_SD_BL_TF(0)); - DUMP_REG(DC_DISP_SD_BL_TF(1)); - DUMP_REG(DC_DISP_SD_BL_TF(2)); - DUMP_REG(DC_DISP_SD_BL_TF(3)); - DUMP_REG(DC_DISP_SD_BL_CONTROL); - DUMP_REG(DC_DISP_SD_HW_K_VALUES); - DUMP_REG(DC_DISP_SD_MAN_K_VALUES); - DUMP_REG(DC_DISP_CURSOR_START_ADDR_HI); - DUMP_REG(DC_DISP_BLEND_CURSOR_CONTROL); - DUMP_REG(DC_WIN_WIN_OPTIONS); - DUMP_REG(DC_WIN_BYTE_SWAP); - DUMP_REG(DC_WIN_BUFFER_CONTROL); - DUMP_REG(DC_WIN_COLOR_DEPTH); - DUMP_REG(DC_WIN_POSITION); - DUMP_REG(DC_WIN_SIZE); - DUMP_REG(DC_WIN_PRESCALED_SIZE); - DUMP_REG(DC_WIN_H_INITIAL_DDA); - DUMP_REG(DC_WIN_V_INITIAL_DDA); - DUMP_REG(DC_WIN_DDA_INC); - DUMP_REG(DC_WIN_LINE_STRIDE); - DUMP_REG(DC_WIN_BUF_STRIDE); - DUMP_REG(DC_WIN_UV_BUF_STRIDE); - DUMP_REG(DC_WIN_BUFFER_ADDR_MODE); - DUMP_REG(DC_WIN_DV_CONTROL); - DUMP_REG(DC_WIN_BLEND_NOKEY); - DUMP_REG(DC_WIN_BLEND_1WIN); - DUMP_REG(DC_WIN_BLEND_2WIN_X); - DUMP_REG(DC_WIN_BLEND_2WIN_Y); - DUMP_REG(DC_WIN_BLEND_3WIN_XY); - DUMP_REG(DC_WIN_HP_FETCH_CONTROL); - DUMP_REG(DC_WINBUF_START_ADDR); - DUMP_REG(DC_WINBUF_START_ADDR_NS); - DUMP_REG(DC_WINBUF_START_ADDR_U); - DUMP_REG(DC_WINBUF_START_ADDR_U_NS); - DUMP_REG(DC_WINBUF_START_ADDR_V); - DUMP_REG(DC_WINBUF_START_ADDR_V_NS); - DUMP_REG(DC_WINBUF_ADDR_H_OFFSET); - DUMP_REG(DC_WINBUF_ADDR_H_OFFSET_NS); - DUMP_REG(DC_WINBUF_ADDR_V_OFFSET); - DUMP_REG(DC_WINBUF_ADDR_V_OFFSET_NS); - DUMP_REG(DC_WINBUF_UFLOW_STATUS); - DUMP_REG(DC_WINBUF_AD_UFLOW_STATUS); - DUMP_REG(DC_WINBUF_BD_UFLOW_STATUS); - DUMP_REG(DC_WINBUF_CD_UFLOW_STATUS); - -#undef DUMP_REG + for (i = 0; i < ARRAY_SIZE(tegra_dc_regs); i++) { + unsigned int offset = tegra_dc_regs[i].offset; + + seq_printf(s, "%-40s %#05x %08x\n", tegra_dc_regs[i].name, + offset, tegra_dc_readl(dc, offset)); + } unlock: drm_modeset_unlock(&dc->base.mutex); -- cgit v1.2.3 From a40051c0fb3c5a655eb014b3b9d66c0c318bcbf9 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 10 Nov 2017 12:18:22 +0100 Subject: drm/tegra: dsi: Move register definitions into a table After commit 75af8fa7fd47 ("drm/tegra: dsi: Trace register accesses"), the debugfs register dump implementation causes excessive stack usage and can result in build warnings. To fix this, move the register definitions into a table and iterate over the table while dumping the registers to debugfs. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dsi.c | 170 +++++++++++++++++++++----------------------- 1 file changed, 83 insertions(+), 87 deletions(-) diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index 046649ec9441..4a78af08df82 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -122,12 +122,89 @@ static inline void tegra_dsi_writel(struct tegra_dsi *dsi, u32 value, writel(value, dsi->regs + (offset << 2)); } +#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name } + +static const struct debugfs_reg32 tegra_dsi_regs[] = { + DEBUGFS_REG32(DSI_INCR_SYNCPT), + DEBUGFS_REG32(DSI_INCR_SYNCPT_CONTROL), + DEBUGFS_REG32(DSI_INCR_SYNCPT_ERROR), + DEBUGFS_REG32(DSI_CTXSW), + DEBUGFS_REG32(DSI_RD_DATA), + DEBUGFS_REG32(DSI_WR_DATA), + DEBUGFS_REG32(DSI_POWER_CONTROL), + DEBUGFS_REG32(DSI_INT_ENABLE), + DEBUGFS_REG32(DSI_INT_STATUS), + DEBUGFS_REG32(DSI_INT_MASK), + DEBUGFS_REG32(DSI_HOST_CONTROL), + DEBUGFS_REG32(DSI_CONTROL), + DEBUGFS_REG32(DSI_SOL_DELAY), + DEBUGFS_REG32(DSI_MAX_THRESHOLD), + DEBUGFS_REG32(DSI_TRIGGER), + DEBUGFS_REG32(DSI_TX_CRC), + DEBUGFS_REG32(DSI_STATUS), + DEBUGFS_REG32(DSI_INIT_SEQ_CONTROL), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_0), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_1), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_2), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_3), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_4), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_5), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_6), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_7), + DEBUGFS_REG32(DSI_PKT_SEQ_0_LO), + DEBUGFS_REG32(DSI_PKT_SEQ_0_HI), + DEBUGFS_REG32(DSI_PKT_SEQ_1_LO), + DEBUGFS_REG32(DSI_PKT_SEQ_1_HI), + DEBUGFS_REG32(DSI_PKT_SEQ_2_LO), + DEBUGFS_REG32(DSI_PKT_SEQ_2_HI), + DEBUGFS_REG32(DSI_PKT_SEQ_3_LO), + DEBUGFS_REG32(DSI_PKT_SEQ_3_HI), + DEBUGFS_REG32(DSI_PKT_SEQ_4_LO), + DEBUGFS_REG32(DSI_PKT_SEQ_4_HI), + DEBUGFS_REG32(DSI_PKT_SEQ_5_LO), + DEBUGFS_REG32(DSI_PKT_SEQ_5_HI), + DEBUGFS_REG32(DSI_DCS_CMDS), + DEBUGFS_REG32(DSI_PKT_LEN_0_1), + DEBUGFS_REG32(DSI_PKT_LEN_2_3), + DEBUGFS_REG32(DSI_PKT_LEN_4_5), + DEBUGFS_REG32(DSI_PKT_LEN_6_7), + DEBUGFS_REG32(DSI_PHY_TIMING_0), + DEBUGFS_REG32(DSI_PHY_TIMING_1), + DEBUGFS_REG32(DSI_PHY_TIMING_2), + DEBUGFS_REG32(DSI_BTA_TIMING), + DEBUGFS_REG32(DSI_TIMEOUT_0), + DEBUGFS_REG32(DSI_TIMEOUT_1), + DEBUGFS_REG32(DSI_TO_TALLY), + DEBUGFS_REG32(DSI_PAD_CONTROL_0), + DEBUGFS_REG32(DSI_PAD_CONTROL_CD), + DEBUGFS_REG32(DSI_PAD_CD_STATUS), + DEBUGFS_REG32(DSI_VIDEO_MODE_CONTROL), + DEBUGFS_REG32(DSI_PAD_CONTROL_1), + DEBUGFS_REG32(DSI_PAD_CONTROL_2), + DEBUGFS_REG32(DSI_PAD_CONTROL_3), + DEBUGFS_REG32(DSI_PAD_CONTROL_4), + DEBUGFS_REG32(DSI_GANGED_MODE_CONTROL), + DEBUGFS_REG32(DSI_GANGED_MODE_START), + DEBUGFS_REG32(DSI_GANGED_MODE_SIZE), + DEBUGFS_REG32(DSI_RAW_DATA_BYTE_COUNT), + DEBUGFS_REG32(DSI_ULTRA_LOW_POWER_CONTROL), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_8), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_9), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_10), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_11), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_12), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_13), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_14), + DEBUGFS_REG32(DSI_INIT_SEQ_DATA_15), +}; + static int tegra_dsi_show_regs(struct seq_file *s, void *data) { struct drm_info_node *node = s->private; struct tegra_dsi *dsi = node->info_ent->data; struct drm_crtc *crtc = dsi->output.encoder.crtc; struct drm_device *drm = node->minor->dev; + unsigned int i; int err = 0; drm_modeset_lock_all(drm); @@ -137,93 +214,12 @@ static int tegra_dsi_show_regs(struct seq_file *s, void *data) goto unlock; } -#define DUMP_REG(name) \ - seq_printf(s, "%-32s %#05x %08x\n", #name, name, \ - tegra_dsi_readl(dsi, name)) - - DUMP_REG(DSI_INCR_SYNCPT); - DUMP_REG(DSI_INCR_SYNCPT_CONTROL); - DUMP_REG(DSI_INCR_SYNCPT_ERROR); - DUMP_REG(DSI_CTXSW); - DUMP_REG(DSI_RD_DATA); - DUMP_REG(DSI_WR_DATA); - DUMP_REG(DSI_POWER_CONTROL); - DUMP_REG(DSI_INT_ENABLE); - DUMP_REG(DSI_INT_STATUS); - DUMP_REG(DSI_INT_MASK); - DUMP_REG(DSI_HOST_CONTROL); - DUMP_REG(DSI_CONTROL); - DUMP_REG(DSI_SOL_DELAY); - DUMP_REG(DSI_MAX_THRESHOLD); - DUMP_REG(DSI_TRIGGER); - DUMP_REG(DSI_TX_CRC); - DUMP_REG(DSI_STATUS); - - DUMP_REG(DSI_INIT_SEQ_CONTROL); - DUMP_REG(DSI_INIT_SEQ_DATA_0); - DUMP_REG(DSI_INIT_SEQ_DATA_1); - DUMP_REG(DSI_INIT_SEQ_DATA_2); - DUMP_REG(DSI_INIT_SEQ_DATA_3); - DUMP_REG(DSI_INIT_SEQ_DATA_4); - DUMP_REG(DSI_INIT_SEQ_DATA_5); - DUMP_REG(DSI_INIT_SEQ_DATA_6); - DUMP_REG(DSI_INIT_SEQ_DATA_7); - - DUMP_REG(DSI_PKT_SEQ_0_LO); - DUMP_REG(DSI_PKT_SEQ_0_HI); - DUMP_REG(DSI_PKT_SEQ_1_LO); - DUMP_REG(DSI_PKT_SEQ_1_HI); - DUMP_REG(DSI_PKT_SEQ_2_LO); - DUMP_REG(DSI_PKT_SEQ_2_HI); - DUMP_REG(DSI_PKT_SEQ_3_LO); - DUMP_REG(DSI_PKT_SEQ_3_HI); - DUMP_REG(DSI_PKT_SEQ_4_LO); - DUMP_REG(DSI_PKT_SEQ_4_HI); - DUMP_REG(DSI_PKT_SEQ_5_LO); - DUMP_REG(DSI_PKT_SEQ_5_HI); - - DUMP_REG(DSI_DCS_CMDS); - - DUMP_REG(DSI_PKT_LEN_0_1); - DUMP_REG(DSI_PKT_LEN_2_3); - DUMP_REG(DSI_PKT_LEN_4_5); - DUMP_REG(DSI_PKT_LEN_6_7); - - DUMP_REG(DSI_PHY_TIMING_0); - DUMP_REG(DSI_PHY_TIMING_1); - DUMP_REG(DSI_PHY_TIMING_2); - DUMP_REG(DSI_BTA_TIMING); - - DUMP_REG(DSI_TIMEOUT_0); - DUMP_REG(DSI_TIMEOUT_1); - DUMP_REG(DSI_TO_TALLY); - - DUMP_REG(DSI_PAD_CONTROL_0); - DUMP_REG(DSI_PAD_CONTROL_CD); - DUMP_REG(DSI_PAD_CD_STATUS); - DUMP_REG(DSI_VIDEO_MODE_CONTROL); - DUMP_REG(DSI_PAD_CONTROL_1); - DUMP_REG(DSI_PAD_CONTROL_2); - DUMP_REG(DSI_PAD_CONTROL_3); - DUMP_REG(DSI_PAD_CONTROL_4); - - DUMP_REG(DSI_GANGED_MODE_CONTROL); - DUMP_REG(DSI_GANGED_MODE_START); - DUMP_REG(DSI_GANGED_MODE_SIZE); - - DUMP_REG(DSI_RAW_DATA_BYTE_COUNT); - DUMP_REG(DSI_ULTRA_LOW_POWER_CONTROL); - - DUMP_REG(DSI_INIT_SEQ_DATA_8); - DUMP_REG(DSI_INIT_SEQ_DATA_9); - DUMP_REG(DSI_INIT_SEQ_DATA_10); - DUMP_REG(DSI_INIT_SEQ_DATA_11); - DUMP_REG(DSI_INIT_SEQ_DATA_12); - DUMP_REG(DSI_INIT_SEQ_DATA_13); - DUMP_REG(DSI_INIT_SEQ_DATA_14); - DUMP_REG(DSI_INIT_SEQ_DATA_15); - -#undef DUMP_REG + for (i = 0; i < ARRAY_SIZE(tegra_dsi_regs); i++) { + unsigned int offset = tegra_dsi_regs[i].offset; + + seq_printf(s, "%-32s %#05x %08x\n", tegra_dsi_regs[i].name, + offset, tegra_dsi_readl(dsi, offset)); + } unlock: drm_modeset_unlock_all(drm); -- cgit v1.2.3 From fbfe16df30817a9d62dfb2633121068361fb7721 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 10 Nov 2017 12:20:46 +0100 Subject: drm/tegra: hdmi: Move register definitions into a table After commit 07a8aab89927 ("drm/tegra: hdmi: Trace register accesses"), the debugfs register dump implementation causes excessive stack usage and can result in build warnings. To fix this, move the register definitions into a table and iterate over the table while dumping the registers to debugfs. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/hdmi.c | 346 ++++++++++++++++++++++--------------------- 1 file changed, 176 insertions(+), 170 deletions(-) diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 6434b3d3d1ba..a68d80476e46 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -1225,12 +1225,182 @@ static const struct drm_encoder_helper_funcs tegra_hdmi_encoder_helper_funcs = { .atomic_check = tegra_hdmi_encoder_atomic_check, }; +#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name } + +static const struct debugfs_reg32 tegra_hdmi_regs[] = { + DEBUGFS_REG32(HDMI_CTXSW), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_STATE0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_STATE1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_STATE2), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_AN_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_AN_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CN_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CN_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_AKSV_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_AKSV_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_BKSV_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_BKSV_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CKSV_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CKSV_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_DKSV_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_DKSV_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CMODE), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_MPRIME_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_MPRIME_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_SPRIME_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB2), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB1), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_RI), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CS_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CS_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_EMU0), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_EMU_RDATA0), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_EMU1), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_EMU2), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_STATUS), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_STATUS), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_STATUS), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_HEADER), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_VSYNC_KEEPOUT), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_VSYNC_WINDOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GCP_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GCP_STATUS), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GCP_SUBPACK), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_CHANNEL_STATUS1), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_CHANNEL_STATUS2), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_EMU0), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_EMU1), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_EMU1_RDATA), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_SPARE), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_SPDIF_CHN_STATUS1), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_SPDIF_CHN_STATUS2), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_HDCPRIF_ROM_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CAP), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_PWR), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_TEST), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_PLL0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_PLL1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_PLL2), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CSTM), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_LVDS), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CRCA), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CRCB), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_BLANK), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_CTL), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(0)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(1)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(2)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(3)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(4)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(5)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(6)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(7)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(8)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(9)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(10)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(11)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(12)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(13)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(14)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(15)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_VCRCA0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_VCRCA1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CCRCA0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CCRCA1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_EDATAA0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_EDATAA1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_COUNTA0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_COUNTA1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_DEBUGA0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_DEBUGA1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_TRIG), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_MSCHECK), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_DEBUG0), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_DEBUG1), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_DEBUG2), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(0)), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(1)), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(2)), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(3)), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(4)), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(5)), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(6)), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_PULSE_WIDTH), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_THRESHOLD), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_CNTRL0), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_N), + DEBUGFS_REG32(HDMI_NV_PDISP_HDCPRIF_ROM_TIMING), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_REFCLK), + DEBUGFS_REG32(HDMI_NV_PDISP_CRC_CONTROL), + DEBUGFS_REG32(HDMI_NV_PDISP_INPUT_CONTROL), + DEBUGFS_REG32(HDMI_NV_PDISP_SCRATCH), + DEBUGFS_REG32(HDMI_NV_PDISP_PE_CURRENT), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_DEBUG0), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_DEBUG1), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_DEBUG2), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_0), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_1), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_2), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_3), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_TRIG), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_SKEY_INDEX), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_CNTRL0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_SPARE0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE), + DEBUGFS_REG32(HDMI_NV_PDISP_INT_STATUS), + DEBUGFS_REG32(HDMI_NV_PDISP_INT_MASK), + DEBUGFS_REG32(HDMI_NV_PDISP_INT_ENABLE), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT), +}; + static int tegra_hdmi_show_regs(struct seq_file *s, void *data) { struct drm_info_node *node = s->private; struct tegra_hdmi *hdmi = node->info_ent->data; struct drm_crtc *crtc = hdmi->output.encoder.crtc; struct drm_device *drm = node->minor->dev; + unsigned int i; int err = 0; drm_modeset_lock_all(drm); @@ -1240,176 +1410,12 @@ static int tegra_hdmi_show_regs(struct seq_file *s, void *data) goto unlock; } -#define DUMP_REG(name) \ - seq_printf(s, "%-56s %#05x %08x\n", #name, name, \ - tegra_hdmi_readl(hdmi, name)) - - DUMP_REG(HDMI_CTXSW); - DUMP_REG(HDMI_NV_PDISP_SOR_STATE0); - DUMP_REG(HDMI_NV_PDISP_SOR_STATE1); - DUMP_REG(HDMI_NV_PDISP_SOR_STATE2); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_AN_MSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_AN_LSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CN_MSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CN_LSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_AKSV_MSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_AKSV_LSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_BKSV_MSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_BKSV_LSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CKSV_MSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CKSV_LSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_DKSV_MSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_DKSV_LSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CTRL); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CMODE); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_MPRIME_MSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_MPRIME_LSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_SPRIME_MSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB2); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB1); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_RI); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CS_MSB); - DUMP_REG(HDMI_NV_PDISP_RG_HDCP_CS_LSB); - DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_EMU0); - DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_EMU_RDATA0); - DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_EMU1); - DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_EMU2); - DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); - DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_STATUS); - DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER); - DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL); - DUMP_REG(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_STATUS); - DUMP_REG(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER); - DUMP_REG(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_CTRL); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_STATUS); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_HEADER); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_CTRL); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_HIGH); - DUMP_REG(HDMI_NV_PDISP_HDMI_CTRL); - DUMP_REG(HDMI_NV_PDISP_HDMI_VSYNC_KEEPOUT); - DUMP_REG(HDMI_NV_PDISP_HDMI_VSYNC_WINDOW); - DUMP_REG(HDMI_NV_PDISP_HDMI_GCP_CTRL); - DUMP_REG(HDMI_NV_PDISP_HDMI_GCP_STATUS); - DUMP_REG(HDMI_NV_PDISP_HDMI_GCP_SUBPACK); - DUMP_REG(HDMI_NV_PDISP_HDMI_CHANNEL_STATUS1); - DUMP_REG(HDMI_NV_PDISP_HDMI_CHANNEL_STATUS2); - DUMP_REG(HDMI_NV_PDISP_HDMI_EMU0); - DUMP_REG(HDMI_NV_PDISP_HDMI_EMU1); - DUMP_REG(HDMI_NV_PDISP_HDMI_EMU1_RDATA); - DUMP_REG(HDMI_NV_PDISP_HDMI_SPARE); - DUMP_REG(HDMI_NV_PDISP_HDMI_SPDIF_CHN_STATUS1); - DUMP_REG(HDMI_NV_PDISP_HDMI_SPDIF_CHN_STATUS2); - DUMP_REG(HDMI_NV_PDISP_HDMI_HDCPRIF_ROM_CTRL); - DUMP_REG(HDMI_NV_PDISP_SOR_CAP); - DUMP_REG(HDMI_NV_PDISP_SOR_PWR); - DUMP_REG(HDMI_NV_PDISP_SOR_TEST); - DUMP_REG(HDMI_NV_PDISP_SOR_PLL0); - DUMP_REG(HDMI_NV_PDISP_SOR_PLL1); - DUMP_REG(HDMI_NV_PDISP_SOR_PLL2); - DUMP_REG(HDMI_NV_PDISP_SOR_CSTM); - DUMP_REG(HDMI_NV_PDISP_SOR_LVDS); - DUMP_REG(HDMI_NV_PDISP_SOR_CRCA); - DUMP_REG(HDMI_NV_PDISP_SOR_CRCB); - DUMP_REG(HDMI_NV_PDISP_SOR_BLANK); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_CTL); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(0)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(1)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(2)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(3)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(4)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(5)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(6)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(7)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(8)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(9)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(10)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(11)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(12)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(13)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(14)); - DUMP_REG(HDMI_NV_PDISP_SOR_SEQ_INST(15)); - DUMP_REG(HDMI_NV_PDISP_SOR_VCRCA0); - DUMP_REG(HDMI_NV_PDISP_SOR_VCRCA1); - DUMP_REG(HDMI_NV_PDISP_SOR_CCRCA0); - DUMP_REG(HDMI_NV_PDISP_SOR_CCRCA1); - DUMP_REG(HDMI_NV_PDISP_SOR_EDATAA0); - DUMP_REG(HDMI_NV_PDISP_SOR_EDATAA1); - DUMP_REG(HDMI_NV_PDISP_SOR_COUNTA0); - DUMP_REG(HDMI_NV_PDISP_SOR_COUNTA1); - DUMP_REG(HDMI_NV_PDISP_SOR_DEBUGA0); - DUMP_REG(HDMI_NV_PDISP_SOR_DEBUGA1); - DUMP_REG(HDMI_NV_PDISP_SOR_TRIG); - DUMP_REG(HDMI_NV_PDISP_SOR_MSCHECK); - DUMP_REG(HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT); - DUMP_REG(HDMI_NV_PDISP_AUDIO_DEBUG0); - DUMP_REG(HDMI_NV_PDISP_AUDIO_DEBUG1); - DUMP_REG(HDMI_NV_PDISP_AUDIO_DEBUG2); - DUMP_REG(HDMI_NV_PDISP_AUDIO_FS(0)); - DUMP_REG(HDMI_NV_PDISP_AUDIO_FS(1)); - DUMP_REG(HDMI_NV_PDISP_AUDIO_FS(2)); - DUMP_REG(HDMI_NV_PDISP_AUDIO_FS(3)); - DUMP_REG(HDMI_NV_PDISP_AUDIO_FS(4)); - DUMP_REG(HDMI_NV_PDISP_AUDIO_FS(5)); - DUMP_REG(HDMI_NV_PDISP_AUDIO_FS(6)); - DUMP_REG(HDMI_NV_PDISP_AUDIO_PULSE_WIDTH); - DUMP_REG(HDMI_NV_PDISP_AUDIO_THRESHOLD); - DUMP_REG(HDMI_NV_PDISP_AUDIO_CNTRL0); - DUMP_REG(HDMI_NV_PDISP_AUDIO_N); - DUMP_REG(HDMI_NV_PDISP_HDCPRIF_ROM_TIMING); - DUMP_REG(HDMI_NV_PDISP_SOR_REFCLK); - DUMP_REG(HDMI_NV_PDISP_CRC_CONTROL); - DUMP_REG(HDMI_NV_PDISP_INPUT_CONTROL); - DUMP_REG(HDMI_NV_PDISP_SCRATCH); - DUMP_REG(HDMI_NV_PDISP_PE_CURRENT); - DUMP_REG(HDMI_NV_PDISP_KEY_CTRL); - DUMP_REG(HDMI_NV_PDISP_KEY_DEBUG0); - DUMP_REG(HDMI_NV_PDISP_KEY_DEBUG1); - DUMP_REG(HDMI_NV_PDISP_KEY_DEBUG2); - DUMP_REG(HDMI_NV_PDISP_KEY_HDCP_KEY_0); - DUMP_REG(HDMI_NV_PDISP_KEY_HDCP_KEY_1); - DUMP_REG(HDMI_NV_PDISP_KEY_HDCP_KEY_2); - DUMP_REG(HDMI_NV_PDISP_KEY_HDCP_KEY_3); - DUMP_REG(HDMI_NV_PDISP_KEY_HDCP_KEY_TRIG); - DUMP_REG(HDMI_NV_PDISP_KEY_SKEY_INDEX); - DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_CNTRL0); - DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_SPARE0); - DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0); - DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH1); - DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR); - DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE); - DUMP_REG(HDMI_NV_PDISP_INT_STATUS); - DUMP_REG(HDMI_NV_PDISP_INT_MASK); - DUMP_REG(HDMI_NV_PDISP_INT_ENABLE); - DUMP_REG(HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT); - -#undef DUMP_REG + for (i = 0; i < ARRAY_SIZE(tegra_hdmi_regs); i++) { + unsigned int offset = tegra_hdmi_regs[i].offset; + + seq_printf(s, "%-56s %#05x %08x\n", tegra_hdmi_regs[i].name, + offset, tegra_hdmi_readl(hdmi, offset)); + } unlock: drm_modeset_unlock_all(drm); -- cgit v1.2.3 From 062f5b2c423ce6069b385745153938cf74013d4a Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 10 Nov 2017 12:21:51 +0100 Subject: drm/tegra: sor: Move register definitions into a table After commit 932f6529139e ("drm/tegra: sor: Trace register accesses"), the debugfs register dump implementation causes excessive stack usage and can result in build warnings. To fix this, move the register definitions into a table and iterate over the table while dumping the registers to debugfs. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/sor.c | 246 +++++++++++++++++++++++--------------------- 1 file changed, 126 insertions(+), 120 deletions(-) diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index b0a1dedac802..75b21dbaa8f0 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -1105,12 +1105,132 @@ unlock: return err; } +#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name } + +static const struct debugfs_reg32 tegra_sor_regs[] = { + DEBUGFS_REG32(SOR_CTXSW), + DEBUGFS_REG32(SOR_SUPER_STATE0), + DEBUGFS_REG32(SOR_SUPER_STATE1), + DEBUGFS_REG32(SOR_STATE0), + DEBUGFS_REG32(SOR_STATE1), + DEBUGFS_REG32(SOR_HEAD_STATE0(0)), + DEBUGFS_REG32(SOR_HEAD_STATE0(1)), + DEBUGFS_REG32(SOR_HEAD_STATE1(0)), + DEBUGFS_REG32(SOR_HEAD_STATE1(1)), + DEBUGFS_REG32(SOR_HEAD_STATE2(0)), + DEBUGFS_REG32(SOR_HEAD_STATE2(1)), + DEBUGFS_REG32(SOR_HEAD_STATE3(0)), + DEBUGFS_REG32(SOR_HEAD_STATE3(1)), + DEBUGFS_REG32(SOR_HEAD_STATE4(0)), + DEBUGFS_REG32(SOR_HEAD_STATE4(1)), + DEBUGFS_REG32(SOR_HEAD_STATE5(0)), + DEBUGFS_REG32(SOR_HEAD_STATE5(1)), + DEBUGFS_REG32(SOR_CRC_CNTRL), + DEBUGFS_REG32(SOR_DP_DEBUG_MVID), + DEBUGFS_REG32(SOR_CLK_CNTRL), + DEBUGFS_REG32(SOR_CAP), + DEBUGFS_REG32(SOR_PWR), + DEBUGFS_REG32(SOR_TEST), + DEBUGFS_REG32(SOR_PLL0), + DEBUGFS_REG32(SOR_PLL1), + DEBUGFS_REG32(SOR_PLL2), + DEBUGFS_REG32(SOR_PLL3), + DEBUGFS_REG32(SOR_CSTM), + DEBUGFS_REG32(SOR_LVDS), + DEBUGFS_REG32(SOR_CRCA), + DEBUGFS_REG32(SOR_CRCB), + DEBUGFS_REG32(SOR_BLANK), + DEBUGFS_REG32(SOR_SEQ_CTL), + DEBUGFS_REG32(SOR_LANE_SEQ_CTL), + DEBUGFS_REG32(SOR_SEQ_INST(0)), + DEBUGFS_REG32(SOR_SEQ_INST(1)), + DEBUGFS_REG32(SOR_SEQ_INST(2)), + DEBUGFS_REG32(SOR_SEQ_INST(3)), + DEBUGFS_REG32(SOR_SEQ_INST(4)), + DEBUGFS_REG32(SOR_SEQ_INST(5)), + DEBUGFS_REG32(SOR_SEQ_INST(6)), + DEBUGFS_REG32(SOR_SEQ_INST(7)), + DEBUGFS_REG32(SOR_SEQ_INST(8)), + DEBUGFS_REG32(SOR_SEQ_INST(9)), + DEBUGFS_REG32(SOR_SEQ_INST(10)), + DEBUGFS_REG32(SOR_SEQ_INST(11)), + DEBUGFS_REG32(SOR_SEQ_INST(12)), + DEBUGFS_REG32(SOR_SEQ_INST(13)), + DEBUGFS_REG32(SOR_SEQ_INST(14)), + DEBUGFS_REG32(SOR_SEQ_INST(15)), + DEBUGFS_REG32(SOR_PWM_DIV), + DEBUGFS_REG32(SOR_PWM_CTL), + DEBUGFS_REG32(SOR_VCRC_A0), + DEBUGFS_REG32(SOR_VCRC_A1), + DEBUGFS_REG32(SOR_VCRC_B0), + DEBUGFS_REG32(SOR_VCRC_B1), + DEBUGFS_REG32(SOR_CCRC_A0), + DEBUGFS_REG32(SOR_CCRC_A1), + DEBUGFS_REG32(SOR_CCRC_B0), + DEBUGFS_REG32(SOR_CCRC_B1), + DEBUGFS_REG32(SOR_EDATA_A0), + DEBUGFS_REG32(SOR_EDATA_A1), + DEBUGFS_REG32(SOR_EDATA_B0), + DEBUGFS_REG32(SOR_EDATA_B1), + DEBUGFS_REG32(SOR_COUNT_A0), + DEBUGFS_REG32(SOR_COUNT_A1), + DEBUGFS_REG32(SOR_COUNT_B0), + DEBUGFS_REG32(SOR_COUNT_B1), + DEBUGFS_REG32(SOR_DEBUG_A0), + DEBUGFS_REG32(SOR_DEBUG_A1), + DEBUGFS_REG32(SOR_DEBUG_B0), + DEBUGFS_REG32(SOR_DEBUG_B1), + DEBUGFS_REG32(SOR_TRIG), + DEBUGFS_REG32(SOR_MSCHECK), + DEBUGFS_REG32(SOR_XBAR_CTRL), + DEBUGFS_REG32(SOR_XBAR_POL), + DEBUGFS_REG32(SOR_DP_LINKCTL0), + DEBUGFS_REG32(SOR_DP_LINKCTL1), + DEBUGFS_REG32(SOR_LANE_DRIVE_CURRENT0), + DEBUGFS_REG32(SOR_LANE_DRIVE_CURRENT1), + DEBUGFS_REG32(SOR_LANE4_DRIVE_CURRENT0), + DEBUGFS_REG32(SOR_LANE4_DRIVE_CURRENT1), + DEBUGFS_REG32(SOR_LANE_PREEMPHASIS0), + DEBUGFS_REG32(SOR_LANE_PREEMPHASIS1), + DEBUGFS_REG32(SOR_LANE4_PREEMPHASIS0), + DEBUGFS_REG32(SOR_LANE4_PREEMPHASIS1), + DEBUGFS_REG32(SOR_LANE_POSTCURSOR0), + DEBUGFS_REG32(SOR_LANE_POSTCURSOR1), + DEBUGFS_REG32(SOR_DP_CONFIG0), + DEBUGFS_REG32(SOR_DP_CONFIG1), + DEBUGFS_REG32(SOR_DP_MN0), + DEBUGFS_REG32(SOR_DP_MN1), + DEBUGFS_REG32(SOR_DP_PADCTL0), + DEBUGFS_REG32(SOR_DP_PADCTL1), + DEBUGFS_REG32(SOR_DP_DEBUG0), + DEBUGFS_REG32(SOR_DP_DEBUG1), + DEBUGFS_REG32(SOR_DP_SPARE0), + DEBUGFS_REG32(SOR_DP_SPARE1), + DEBUGFS_REG32(SOR_DP_AUDIO_CTRL), + DEBUGFS_REG32(SOR_DP_AUDIO_HBLANK_SYMBOLS), + DEBUGFS_REG32(SOR_DP_AUDIO_VBLANK_SYMBOLS), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_HEADER), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK0), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK1), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK2), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK3), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK4), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK5), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK6), + DEBUGFS_REG32(SOR_DP_TPG), + DEBUGFS_REG32(SOR_DP_TPG_CONFIG), + DEBUGFS_REG32(SOR_DP_LQ_CSTM0), + DEBUGFS_REG32(SOR_DP_LQ_CSTM1), + DEBUGFS_REG32(SOR_DP_LQ_CSTM2), +}; + static int tegra_sor_show_regs(struct seq_file *s, void *data) { struct drm_info_node *node = s->private; struct tegra_sor *sor = node->info_ent->data; struct drm_crtc *crtc = sor->output.encoder.crtc; struct drm_device *drm = node->minor->dev; + unsigned int i; int err = 0; drm_modeset_lock_all(drm); @@ -1120,126 +1240,12 @@ static int tegra_sor_show_regs(struct seq_file *s, void *data) goto unlock; } -#define DUMP_REG(name) \ - seq_printf(s, "%-38s %#05x %08x\n", #name, name, \ - tegra_sor_readl(sor, name)) - - DUMP_REG(SOR_CTXSW); - DUMP_REG(SOR_SUPER_STATE0); - DUMP_REG(SOR_SUPER_STATE1); - DUMP_REG(SOR_STATE0); - DUMP_REG(SOR_STATE1); - DUMP_REG(SOR_HEAD_STATE0(0)); - DUMP_REG(SOR_HEAD_STATE0(1)); - DUMP_REG(SOR_HEAD_STATE1(0)); - DUMP_REG(SOR_HEAD_STATE1(1)); - DUMP_REG(SOR_HEAD_STATE2(0)); - DUMP_REG(SOR_HEAD_STATE2(1)); - DUMP_REG(SOR_HEAD_STATE3(0)); - DUMP_REG(SOR_HEAD_STATE3(1)); - DUMP_REG(SOR_HEAD_STATE4(0)); - DUMP_REG(SOR_HEAD_STATE4(1)); - DUMP_REG(SOR_HEAD_STATE5(0)); - DUMP_REG(SOR_HEAD_STATE5(1)); - DUMP_REG(SOR_CRC_CNTRL); - DUMP_REG(SOR_DP_DEBUG_MVID); - DUMP_REG(SOR_CLK_CNTRL); - DUMP_REG(SOR_CAP); - DUMP_REG(SOR_PWR); - DUMP_REG(SOR_TEST); - DUMP_REG(SOR_PLL0); - DUMP_REG(SOR_PLL1); - DUMP_REG(SOR_PLL2); - DUMP_REG(SOR_PLL3); - DUMP_REG(SOR_CSTM); - DUMP_REG(SOR_LVDS); - DUMP_REG(SOR_CRCA); - DUMP_REG(SOR_CRCB); - DUMP_REG(SOR_BLANK); - DUMP_REG(SOR_SEQ_CTL); - DUMP_REG(SOR_LANE_SEQ_CTL); - DUMP_REG(SOR_SEQ_INST(0)); - DUMP_REG(SOR_SEQ_INST(1)); - DUMP_REG(SOR_SEQ_INST(2)); - DUMP_REG(SOR_SEQ_INST(3)); - DUMP_REG(SOR_SEQ_INST(4)); - DUMP_REG(SOR_SEQ_INST(5)); - DUMP_REG(SOR_SEQ_INST(6)); - DUMP_REG(SOR_SEQ_INST(7)); - DUMP_REG(SOR_SEQ_INST(8)); - DUMP_REG(SOR_SEQ_INST(9)); - DUMP_REG(SOR_SEQ_INST(10)); - DUMP_REG(SOR_SEQ_INST(11)); - DUMP_REG(SOR_SEQ_INST(12)); - DUMP_REG(SOR_SEQ_INST(13)); - DUMP_REG(SOR_SEQ_INST(14)); - DUMP_REG(SOR_SEQ_INST(15)); - DUMP_REG(SOR_PWM_DIV); - DUMP_REG(SOR_PWM_CTL); - DUMP_REG(SOR_VCRC_A0); - DUMP_REG(SOR_VCRC_A1); - DUMP_REG(SOR_VCRC_B0); - DUMP_REG(SOR_VCRC_B1); - DUMP_REG(SOR_CCRC_A0); - DUMP_REG(SOR_CCRC_A1); - DUMP_REG(SOR_CCRC_B0); - DUMP_REG(SOR_CCRC_B1); - DUMP_REG(SOR_EDATA_A0); - DUMP_REG(SOR_EDATA_A1); - DUMP_REG(SOR_EDATA_B0); - DUMP_REG(SOR_EDATA_B1); - DUMP_REG(SOR_COUNT_A0); - DUMP_REG(SOR_COUNT_A1); - DUMP_REG(SOR_COUNT_B0); - DUMP_REG(SOR_COUNT_B1); - DUMP_REG(SOR_DEBUG_A0); - DUMP_REG(SOR_DEBUG_A1); - DUMP_REG(SOR_DEBUG_B0); - DUMP_REG(SOR_DEBUG_B1); - DUMP_REG(SOR_TRIG); - DUMP_REG(SOR_MSCHECK); - DUMP_REG(SOR_XBAR_CTRL); - DUMP_REG(SOR_XBAR_POL); - DUMP_REG(SOR_DP_LINKCTL0); - DUMP_REG(SOR_DP_LINKCTL1); - DUMP_REG(SOR_LANE_DRIVE_CURRENT0); - DUMP_REG(SOR_LANE_DRIVE_CURRENT1); - DUMP_REG(SOR_LANE4_DRIVE_CURRENT0); - DUMP_REG(SOR_LANE4_DRIVE_CURRENT1); - DUMP_REG(SOR_LANE_PREEMPHASIS0); - DUMP_REG(SOR_LANE_PREEMPHASIS1); - DUMP_REG(SOR_LANE4_PREEMPHASIS0); - DUMP_REG(SOR_LANE4_PREEMPHASIS1); - DUMP_REG(SOR_LANE_POSTCURSOR0); - DUMP_REG(SOR_LANE_POSTCURSOR1); - DUMP_REG(SOR_DP_CONFIG0); - DUMP_REG(SOR_DP_CONFIG1); - DUMP_REG(SOR_DP_MN0); - DUMP_REG(SOR_DP_MN1); - DUMP_REG(SOR_DP_PADCTL0); - DUMP_REG(SOR_DP_PADCTL1); - DUMP_REG(SOR_DP_DEBUG0); - DUMP_REG(SOR_DP_DEBUG1); - DUMP_REG(SOR_DP_SPARE0); - DUMP_REG(SOR_DP_SPARE1); - DUMP_REG(SOR_DP_AUDIO_CTRL); - DUMP_REG(SOR_DP_AUDIO_HBLANK_SYMBOLS); - DUMP_REG(SOR_DP_AUDIO_VBLANK_SYMBOLS); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_HEADER); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK0); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK1); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK2); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK3); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK4); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK5); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK6); - DUMP_REG(SOR_DP_TPG); - DUMP_REG(SOR_DP_TPG_CONFIG); - DUMP_REG(SOR_DP_LQ_CSTM0); - DUMP_REG(SOR_DP_LQ_CSTM1); - DUMP_REG(SOR_DP_LQ_CSTM2); - -#undef DUMP_REG + for (i = 0; i < ARRAY_SIZE(tegra_sor_regs); i++) { + unsigned int offset = tegra_sor_regs[i].offset; + + seq_printf(s, "%-38s %#05x %08x\n", tegra_sor_regs[i].name, + offset, tegra_sor_readl(sor, offset)); + } unlock: drm_modeset_unlock_all(drm); -- cgit v1.2.3 From c49c81e21ce1a646c1402597d12137c679ac7f71 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 8 Nov 2017 13:32:05 +0100 Subject: drm/tegra: dc: Reshuffle some code Reshuffle some code so that functions are defined closer to where they are used. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 152 ++++++++++++++++++++++----------------------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 0c77473d6510..271357482401 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -909,82 +909,6 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc) return 0; } -static u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc) -{ - struct tegra_dc *dc = to_tegra_dc(crtc); - - if (dc->syncpt) - return host1x_syncpt_read(dc->syncpt); - - /* fallback to software emulated VBLANK counter */ - return drm_crtc_vblank_count(&dc->base); -} - -static int tegra_dc_enable_vblank(struct drm_crtc *crtc) -{ - struct tegra_dc *dc = to_tegra_dc(crtc); - unsigned long value, flags; - - spin_lock_irqsave(&dc->lock, flags); - - value = tegra_dc_readl(dc, DC_CMD_INT_MASK); - value |= VBLANK_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_MASK); - - spin_unlock_irqrestore(&dc->lock, flags); - - return 0; -} - -static void tegra_dc_disable_vblank(struct drm_crtc *crtc) -{ - struct tegra_dc *dc = to_tegra_dc(crtc); - unsigned long value, flags; - - spin_lock_irqsave(&dc->lock, flags); - - value = tegra_dc_readl(dc, DC_CMD_INT_MASK); - value &= ~VBLANK_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_MASK); - - spin_unlock_irqrestore(&dc->lock, flags); -} - -static void tegra_dc_finish_page_flip(struct tegra_dc *dc) -{ - struct drm_device *drm = dc->base.dev; - struct drm_crtc *crtc = &dc->base; - unsigned long flags, base; - struct tegra_bo *bo; - - spin_lock_irqsave(&drm->event_lock, flags); - - if (!dc->event) { - spin_unlock_irqrestore(&drm->event_lock, flags); - return; - } - - bo = tegra_fb_get_plane(crtc->primary->fb, 0); - - spin_lock(&dc->lock); - - /* check if new start address has been latched */ - tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); - tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); - base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR); - tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); - - spin_unlock(&dc->lock); - - if (base == bo->paddr + crtc->primary->fb->offsets[0]) { - drm_crtc_send_vblank_event(crtc, dc->event); - drm_crtc_vblank_put(crtc); - dc->event = NULL; - } - - spin_unlock_irqrestore(&drm->event_lock, flags); -} - static void tegra_dc_destroy(struct drm_crtc *crtc) { drm_crtc_cleanup(crtc); @@ -1035,6 +959,47 @@ static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc, kfree(state); } +static u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc) +{ + struct tegra_dc *dc = to_tegra_dc(crtc); + + if (dc->syncpt) + return host1x_syncpt_read(dc->syncpt); + + /* fallback to software emulated VBLANK counter */ + return drm_crtc_vblank_count(&dc->base); +} + +static int tegra_dc_enable_vblank(struct drm_crtc *crtc) +{ + struct tegra_dc *dc = to_tegra_dc(crtc); + unsigned long value, flags; + + spin_lock_irqsave(&dc->lock, flags); + + value = tegra_dc_readl(dc, DC_CMD_INT_MASK); + value |= VBLANK_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_MASK); + + spin_unlock_irqrestore(&dc->lock, flags); + + return 0; +} + +static void tegra_dc_disable_vblank(struct drm_crtc *crtc) +{ + struct tegra_dc *dc = to_tegra_dc(crtc); + unsigned long value, flags; + + spin_lock_irqsave(&dc->lock, flags); + + value = tegra_dc_readl(dc, DC_CMD_INT_MASK); + value &= ~VBLANK_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_MASK); + + spin_unlock_irqrestore(&dc->lock, flags); +} + static const struct drm_crtc_funcs tegra_crtc_funcs = { .page_flip = drm_atomic_helper_page_flip, .set_config = drm_atomic_helper_set_config, @@ -1342,6 +1307,41 @@ static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { .atomic_disable = tegra_crtc_atomic_disable, }; +static void tegra_dc_finish_page_flip(struct tegra_dc *dc) +{ + struct drm_device *drm = dc->base.dev; + struct drm_crtc *crtc = &dc->base; + unsigned long flags, base; + struct tegra_bo *bo; + + spin_lock_irqsave(&drm->event_lock, flags); + + if (!dc->event) { + spin_unlock_irqrestore(&drm->event_lock, flags); + return; + } + + bo = tegra_fb_get_plane(crtc->primary->fb, 0); + + spin_lock(&dc->lock); + + /* check if new start address has been latched */ + tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); + tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); + base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR); + tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); + + spin_unlock(&dc->lock); + + if (base == bo->paddr + crtc->primary->fb->offsets[0]) { + drm_crtc_send_vblank_event(crtc, dc->event); + drm_crtc_vblank_put(crtc); + dc->event = NULL; + } + + spin_unlock_irqrestore(&drm->event_lock, flags); +} + static irqreturn_t tegra_dc_irq(int irq, void *data) { struct tegra_dc *dc = data; -- cgit v1.2.3 From b95800eeef09f4853904ec4e926d098f1ca4698a Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 8 Nov 2017 13:40:54 +0100 Subject: drm/tegra: dc: Register debugfs in ->late_register() The ->late_register() and ->early_unregister() callbacks are called at the right time to make sure userspace only accesses interfaces when it should. Move debugfs registration and unregistration to these callback functions to avoid potential races with userspace. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 1120 +++++++++++++++++++++----------------------- drivers/gpu/drm/tegra/dc.h | 2 - 2 files changed, 546 insertions(+), 576 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 271357482401..29777604df39 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -959,37 +959,370 @@ static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc, kfree(state); } -static u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc) -{ - struct tegra_dc *dc = to_tegra_dc(crtc); - - if (dc->syncpt) - return host1x_syncpt_read(dc->syncpt); +#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name } - /* fallback to software emulated VBLANK counter */ - return drm_crtc_vblank_count(&dc->base); -} +static const struct debugfs_reg32 tegra_dc_regs[] = { + DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT), + DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL), + DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_ERROR), + DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT), + DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL), + DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_ERROR), + DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT), + DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL), + DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_ERROR), + DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT), + DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL), + DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_ERROR), + DEBUGFS_REG32(DC_CMD_CONT_SYNCPT_VSYNC), + DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND_OPTION0), + DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND), + DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE), + DEBUGFS_REG32(DC_CMD_DISPLAY_POWER_CONTROL), + DEBUGFS_REG32(DC_CMD_INT_STATUS), + DEBUGFS_REG32(DC_CMD_INT_MASK), + DEBUGFS_REG32(DC_CMD_INT_ENABLE), + DEBUGFS_REG32(DC_CMD_INT_TYPE), + DEBUGFS_REG32(DC_CMD_INT_POLARITY), + DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE1), + DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE2), + DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE3), + DEBUGFS_REG32(DC_CMD_STATE_ACCESS), + DEBUGFS_REG32(DC_CMD_STATE_CONTROL), + DEBUGFS_REG32(DC_CMD_DISPLAY_WINDOW_HEADER), + DEBUGFS_REG32(DC_CMD_REG_ACT_CONTROL), + DEBUGFS_REG32(DC_COM_CRC_CONTROL), + DEBUGFS_REG32(DC_COM_CRC_CHECKSUM), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(0)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(1)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(2)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(3)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(0)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(1)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(2)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(3)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(0)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(1)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(2)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(3)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(0)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(1)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(2)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(3)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(0)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(1)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(0)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(1)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(2)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(3)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(4)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(5)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(6)), + DEBUGFS_REG32(DC_COM_PIN_MISC_CONTROL), + DEBUGFS_REG32(DC_COM_PIN_PM0_CONTROL), + DEBUGFS_REG32(DC_COM_PIN_PM0_DUTY_CYCLE), + DEBUGFS_REG32(DC_COM_PIN_PM1_CONTROL), + DEBUGFS_REG32(DC_COM_PIN_PM1_DUTY_CYCLE), + DEBUGFS_REG32(DC_COM_SPI_CONTROL), + DEBUGFS_REG32(DC_COM_SPI_START_BYTE), + DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_AB), + DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_CD), + DEBUGFS_REG32(DC_COM_HSPI_CS_DC), + DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_A), + DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_B), + DEBUGFS_REG32(DC_COM_GPIO_CTRL), + DEBUGFS_REG32(DC_COM_GPIO_DEBOUNCE_COUNTER), + DEBUGFS_REG32(DC_COM_CRC_CHECKSUM_LATCHED), + DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS0), + DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS1), + DEBUGFS_REG32(DC_DISP_DISP_WIN_OPTIONS), + DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY), + DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER), + DEBUGFS_REG32(DC_DISP_DISP_TIMING_OPTIONS), + DEBUGFS_REG32(DC_DISP_REF_TO_SYNC), + DEBUGFS_REG32(DC_DISP_SYNC_WIDTH), + DEBUGFS_REG32(DC_DISP_BACK_PORCH), + DEBUGFS_REG32(DC_DISP_ACTIVE), + DEBUGFS_REG32(DC_DISP_FRONT_PORCH), + DEBUGFS_REG32(DC_DISP_H_PULSE0_CONTROL), + DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_A), + DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_B), + DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_C), + DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_D), + DEBUGFS_REG32(DC_DISP_H_PULSE1_CONTROL), + DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_A), + DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_B), + DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_C), + DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_D), + DEBUGFS_REG32(DC_DISP_H_PULSE2_CONTROL), + DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_A), + DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_B), + DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_C), + DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_D), + DEBUGFS_REG32(DC_DISP_V_PULSE0_CONTROL), + DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_A), + DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_B), + DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_C), + DEBUGFS_REG32(DC_DISP_V_PULSE1_CONTROL), + DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_A), + DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_B), + DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_C), + DEBUGFS_REG32(DC_DISP_V_PULSE2_CONTROL), + DEBUGFS_REG32(DC_DISP_V_PULSE2_POSITION_A), + DEBUGFS_REG32(DC_DISP_V_PULSE3_CONTROL), + DEBUGFS_REG32(DC_DISP_V_PULSE3_POSITION_A), + DEBUGFS_REG32(DC_DISP_M0_CONTROL), + DEBUGFS_REG32(DC_DISP_M1_CONTROL), + DEBUGFS_REG32(DC_DISP_DI_CONTROL), + DEBUGFS_REG32(DC_DISP_PP_CONTROL), + DEBUGFS_REG32(DC_DISP_PP_SELECT_A), + DEBUGFS_REG32(DC_DISP_PP_SELECT_B), + DEBUGFS_REG32(DC_DISP_PP_SELECT_C), + DEBUGFS_REG32(DC_DISP_PP_SELECT_D), + DEBUGFS_REG32(DC_DISP_DISP_CLOCK_CONTROL), + DEBUGFS_REG32(DC_DISP_DISP_INTERFACE_CONTROL), + DEBUGFS_REG32(DC_DISP_DISP_COLOR_CONTROL), + DEBUGFS_REG32(DC_DISP_SHIFT_CLOCK_OPTIONS), + DEBUGFS_REG32(DC_DISP_DATA_ENABLE_OPTIONS), + DEBUGFS_REG32(DC_DISP_SERIAL_INTERFACE_OPTIONS), + DEBUGFS_REG32(DC_DISP_LCD_SPI_OPTIONS), + DEBUGFS_REG32(DC_DISP_BORDER_COLOR), + DEBUGFS_REG32(DC_DISP_COLOR_KEY0_LOWER), + DEBUGFS_REG32(DC_DISP_COLOR_KEY0_UPPER), + DEBUGFS_REG32(DC_DISP_COLOR_KEY1_LOWER), + DEBUGFS_REG32(DC_DISP_COLOR_KEY1_UPPER), + DEBUGFS_REG32(DC_DISP_CURSOR_FOREGROUND), + DEBUGFS_REG32(DC_DISP_CURSOR_BACKGROUND), + DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR), + DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_NS), + DEBUGFS_REG32(DC_DISP_CURSOR_POSITION), + DEBUGFS_REG32(DC_DISP_CURSOR_POSITION_NS), + DEBUGFS_REG32(DC_DISP_INIT_SEQ_CONTROL), + DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_A), + DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_B), + DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_C), + DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_D), + DEBUGFS_REG32(DC_DISP_DC_MCCIF_FIFOCTRL), + DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0A_HYST), + DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0B_HYST), + DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1A_HYST), + DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1B_HYST), + DEBUGFS_REG32(DC_DISP_DAC_CRT_CTRL), + DEBUGFS_REG32(DC_DISP_DISP_MISC_CONTROL), + DEBUGFS_REG32(DC_DISP_SD_CONTROL), + DEBUGFS_REG32(DC_DISP_SD_CSC_COEFF), + DEBUGFS_REG32(DC_DISP_SD_LUT(0)), + DEBUGFS_REG32(DC_DISP_SD_LUT(1)), + DEBUGFS_REG32(DC_DISP_SD_LUT(2)), + DEBUGFS_REG32(DC_DISP_SD_LUT(3)), + DEBUGFS_REG32(DC_DISP_SD_LUT(4)), + DEBUGFS_REG32(DC_DISP_SD_LUT(5)), + DEBUGFS_REG32(DC_DISP_SD_LUT(6)), + DEBUGFS_REG32(DC_DISP_SD_LUT(7)), + DEBUGFS_REG32(DC_DISP_SD_LUT(8)), + DEBUGFS_REG32(DC_DISP_SD_FLICKER_CONTROL), + DEBUGFS_REG32(DC_DISP_DC_PIXEL_COUNT), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(0)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(1)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(2)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(3)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(4)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(5)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(6)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(7)), + DEBUGFS_REG32(DC_DISP_SD_BL_TF(0)), + DEBUGFS_REG32(DC_DISP_SD_BL_TF(1)), + DEBUGFS_REG32(DC_DISP_SD_BL_TF(2)), + DEBUGFS_REG32(DC_DISP_SD_BL_TF(3)), + DEBUGFS_REG32(DC_DISP_SD_BL_CONTROL), + DEBUGFS_REG32(DC_DISP_SD_HW_K_VALUES), + DEBUGFS_REG32(DC_DISP_SD_MAN_K_VALUES), + DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_HI), + DEBUGFS_REG32(DC_DISP_BLEND_CURSOR_CONTROL), + DEBUGFS_REG32(DC_WIN_WIN_OPTIONS), + DEBUGFS_REG32(DC_WIN_BYTE_SWAP), + DEBUGFS_REG32(DC_WIN_BUFFER_CONTROL), + DEBUGFS_REG32(DC_WIN_COLOR_DEPTH), + DEBUGFS_REG32(DC_WIN_POSITION), + DEBUGFS_REG32(DC_WIN_SIZE), + DEBUGFS_REG32(DC_WIN_PRESCALED_SIZE), + DEBUGFS_REG32(DC_WIN_H_INITIAL_DDA), + DEBUGFS_REG32(DC_WIN_V_INITIAL_DDA), + DEBUGFS_REG32(DC_WIN_DDA_INC), + DEBUGFS_REG32(DC_WIN_LINE_STRIDE), + DEBUGFS_REG32(DC_WIN_BUF_STRIDE), + DEBUGFS_REG32(DC_WIN_UV_BUF_STRIDE), + DEBUGFS_REG32(DC_WIN_BUFFER_ADDR_MODE), + DEBUGFS_REG32(DC_WIN_DV_CONTROL), + DEBUGFS_REG32(DC_WIN_BLEND_NOKEY), + DEBUGFS_REG32(DC_WIN_BLEND_1WIN), + DEBUGFS_REG32(DC_WIN_BLEND_2WIN_X), + DEBUGFS_REG32(DC_WIN_BLEND_2WIN_Y), + DEBUGFS_REG32(DC_WIN_BLEND_3WIN_XY), + DEBUGFS_REG32(DC_WIN_HP_FETCH_CONTROL), + DEBUGFS_REG32(DC_WINBUF_START_ADDR), + DEBUGFS_REG32(DC_WINBUF_START_ADDR_NS), + DEBUGFS_REG32(DC_WINBUF_START_ADDR_U), + DEBUGFS_REG32(DC_WINBUF_START_ADDR_U_NS), + DEBUGFS_REG32(DC_WINBUF_START_ADDR_V), + DEBUGFS_REG32(DC_WINBUF_START_ADDR_V_NS), + DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET), + DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET_NS), + DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET), + DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET_NS), + DEBUGFS_REG32(DC_WINBUF_UFLOW_STATUS), + DEBUGFS_REG32(DC_WINBUF_AD_UFLOW_STATUS), + DEBUGFS_REG32(DC_WINBUF_BD_UFLOW_STATUS), + DEBUGFS_REG32(DC_WINBUF_CD_UFLOW_STATUS), +}; -static int tegra_dc_enable_vblank(struct drm_crtc *crtc) +static int tegra_dc_show_regs(struct seq_file *s, void *data) { - struct tegra_dc *dc = to_tegra_dc(crtc); - unsigned long value, flags; + struct drm_info_node *node = s->private; + struct tegra_dc *dc = node->info_ent->data; + unsigned int i; + int err = 0; - spin_lock_irqsave(&dc->lock, flags); + drm_modeset_lock(&dc->base.mutex, NULL); - value = tegra_dc_readl(dc, DC_CMD_INT_MASK); - value |= VBLANK_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_MASK); + if (!dc->base.state->active) { + err = -EBUSY; + goto unlock; + } - spin_unlock_irqrestore(&dc->lock, flags); + for (i = 0; i < ARRAY_SIZE(tegra_dc_regs); i++) { + unsigned int offset = tegra_dc_regs[i].offset; - return 0; + seq_printf(s, "%-40s %#05x %08x\n", tegra_dc_regs[i].name, + offset, tegra_dc_readl(dc, offset)); + } + +unlock: + drm_modeset_unlock(&dc->base.mutex); + return err; } -static void tegra_dc_disable_vblank(struct drm_crtc *crtc) +static int tegra_dc_show_crc(struct seq_file *s, void *data) { - struct tegra_dc *dc = to_tegra_dc(crtc); - unsigned long value, flags; + struct drm_info_node *node = s->private; + struct tegra_dc *dc = node->info_ent->data; + int err = 0; + u32 value; + + drm_modeset_lock(&dc->base.mutex, NULL); + + if (!dc->base.state->active) { + err = -EBUSY; + goto unlock; + } + + value = DC_COM_CRC_CONTROL_ACTIVE_DATA | DC_COM_CRC_CONTROL_ENABLE; + tegra_dc_writel(dc, value, DC_COM_CRC_CONTROL); + tegra_dc_commit(dc); + + drm_crtc_wait_one_vblank(&dc->base); + drm_crtc_wait_one_vblank(&dc->base); + + value = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM); + seq_printf(s, "%08x\n", value); + + tegra_dc_writel(dc, 0, DC_COM_CRC_CONTROL); + +unlock: + drm_modeset_unlock(&dc->base.mutex); + return err; +} + +static int tegra_dc_show_stats(struct seq_file *s, void *data) +{ + struct drm_info_node *node = s->private; + struct tegra_dc *dc = node->info_ent->data; + + seq_printf(s, "frames: %lu\n", dc->stats.frames); + seq_printf(s, "vblank: %lu\n", dc->stats.vblank); + seq_printf(s, "underflow: %lu\n", dc->stats.underflow); + seq_printf(s, "overflow: %lu\n", dc->stats.overflow); + + return 0; +} + +static struct drm_info_list debugfs_files[] = { + { "regs", tegra_dc_show_regs, 0, NULL }, + { "crc", tegra_dc_show_crc, 0, NULL }, + { "stats", tegra_dc_show_stats, 0, NULL }, +}; + +static int tegra_dc_late_register(struct drm_crtc *crtc) +{ + unsigned int i, count = ARRAY_SIZE(debugfs_files); + struct drm_minor *minor = crtc->dev->primary; + struct dentry *root = crtc->debugfs_entry; + struct tegra_dc *dc = to_tegra_dc(crtc); + int err; + + dc->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), + GFP_KERNEL); + if (!dc->debugfs_files) + return -ENOMEM; + + for (i = 0; i < count; i++) + dc->debugfs_files[i].data = dc; + + err = drm_debugfs_create_files(dc->debugfs_files, count, root, minor); + if (err < 0) + goto free; + + return 0; + +free: + kfree(dc->debugfs_files); + dc->debugfs_files = NULL; + + return err; +} + +static void tegra_dc_early_unregister(struct drm_crtc *crtc) +{ + unsigned int count = ARRAY_SIZE(debugfs_files); + struct drm_minor *minor = crtc->dev->primary; + struct tegra_dc *dc = to_tegra_dc(crtc); + + drm_debugfs_remove_files(dc->debugfs_files, count, minor); + kfree(dc->debugfs_files); + dc->debugfs_files = NULL; +} + +static u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc) +{ + struct tegra_dc *dc = to_tegra_dc(crtc); + + if (dc->syncpt) + return host1x_syncpt_read(dc->syncpt); + + /* fallback to software emulated VBLANK counter */ + return drm_crtc_vblank_count(&dc->base); +} + +static int tegra_dc_enable_vblank(struct drm_crtc *crtc) +{ + struct tegra_dc *dc = to_tegra_dc(crtc); + unsigned long value, flags; + + spin_lock_irqsave(&dc->lock, flags); + + value = tegra_dc_readl(dc, DC_CMD_INT_MASK); + value |= VBLANK_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_MASK); + + spin_unlock_irqrestore(&dc->lock, flags); + + return 0; +} + +static void tegra_dc_disable_vblank(struct drm_crtc *crtc) +{ + struct tegra_dc *dc = to_tegra_dc(crtc); + unsigned long value, flags; spin_lock_irqsave(&dc->lock, flags); @@ -1007,6 +1340,8 @@ static const struct drm_crtc_funcs tegra_crtc_funcs = { .reset = tegra_crtc_reset, .atomic_duplicate_state = tegra_crtc_atomic_duplicate_state, .atomic_destroy_state = tegra_crtc_atomic_destroy_state, + .late_register = tegra_dc_late_register, + .early_unregister = tegra_dc_early_unregister, .get_vblank_counter = tegra_dc_get_vblank_counter, .enable_vblank = tegra_dc_enable_vblank, .disable_vblank = tegra_dc_disable_vblank, @@ -1148,590 +1483,239 @@ static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout) static void tegra_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) -{ - struct tegra_dc *dc = to_tegra_dc(crtc); - u32 value; - - if (!tegra_dc_idle(dc)) { - tegra_dc_stop(dc); - - /* - * Ignore the return value, there isn't anything useful to do - * in case this fails. - */ - tegra_dc_wait_idle(dc, 100); - } - - /* - * This should really be part of the RGB encoder driver, but clearing - * these bits has the side-effect of stopping the display controller. - * When that happens no VBLANK interrupts will be raised. At the same - * time the encoder is disabled before the display controller, so the - * above code is always going to timeout waiting for the controller - * to go idle. - * - * Given the close coupling between the RGB encoder and the display - * controller doing it here is still kind of okay. None of the other - * encoder drivers require these bits to be cleared. - * - * XXX: Perhaps given that the display controller is switched off at - * this point anyway maybe clearing these bits isn't even useful for - * the RGB encoder? - */ - if (dc->rgb) { - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); - value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | - PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); - } - - tegra_dc_stats_reset(&dc->stats); - drm_crtc_vblank_off(crtc); - - pm_runtime_put_sync(dc->dev); -} - -static void tegra_crtc_atomic_enable(struct drm_crtc *crtc, - struct drm_crtc_state *old_state) -{ - struct drm_display_mode *mode = &crtc->state->adjusted_mode; - struct tegra_dc_state *state = to_dc_state(crtc->state); - struct tegra_dc *dc = to_tegra_dc(crtc); - u32 value; - - pm_runtime_get_sync(dc->dev); - - /* initialize display controller */ - if (dc->syncpt) { - u32 syncpt = host1x_syncpt_id(dc->syncpt); - - value = SYNCPT_CNTRL_NO_STALL; - tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); - - value = SYNCPT_VSYNC_ENABLE | syncpt; - tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC); - } - - value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); - - value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); - - /* initialize timer */ - value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) | - WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20); - tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY); - - value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) | - WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1); - tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); - - value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); - - value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_MASK); - - if (dc->soc->supports_border_color) - tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR); - - /* apply PLL and pixel clock changes */ - tegra_dc_commit_state(dc, state); - - /* program display mode */ - tegra_dc_set_timings(dc, mode); - - /* interlacing isn't supported yet, so disable it */ - if (dc->soc->supports_interlacing) { - value = tegra_dc_readl(dc, DC_DISP_INTERLACE_CONTROL); - value &= ~INTERLACE_ENABLE; - tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL); - } - - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); - value &= ~DISP_CTRL_MODE_MASK; - value |= DISP_CTRL_MODE_C_DISPLAY; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); - - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); - value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | - PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); - - tegra_dc_commit(dc); - - drm_crtc_vblank_on(crtc); -} - -static int tegra_crtc_atomic_check(struct drm_crtc *crtc, - struct drm_crtc_state *state) -{ - return 0; -} - -static void tegra_crtc_atomic_begin(struct drm_crtc *crtc, - struct drm_crtc_state *old_crtc_state) -{ - struct tegra_dc *dc = to_tegra_dc(crtc); - - if (crtc->state->event) { - crtc->state->event->pipe = drm_crtc_index(crtc); - - WARN_ON(drm_crtc_vblank_get(crtc) != 0); - - dc->event = crtc->state->event; - crtc->state->event = NULL; - } -} - -static void tegra_crtc_atomic_flush(struct drm_crtc *crtc, - struct drm_crtc_state *old_crtc_state) -{ - struct tegra_dc_state *state = to_dc_state(crtc->state); - struct tegra_dc *dc = to_tegra_dc(crtc); - - tegra_dc_writel(dc, state->planes << 8, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, state->planes, DC_CMD_STATE_CONTROL); -} - -static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { - .atomic_check = tegra_crtc_atomic_check, - .atomic_begin = tegra_crtc_atomic_begin, - .atomic_flush = tegra_crtc_atomic_flush, - .atomic_enable = tegra_crtc_atomic_enable, - .atomic_disable = tegra_crtc_atomic_disable, -}; - -static void tegra_dc_finish_page_flip(struct tegra_dc *dc) -{ - struct drm_device *drm = dc->base.dev; - struct drm_crtc *crtc = &dc->base; - unsigned long flags, base; - struct tegra_bo *bo; - - spin_lock_irqsave(&drm->event_lock, flags); - - if (!dc->event) { - spin_unlock_irqrestore(&drm->event_lock, flags); - return; - } - - bo = tegra_fb_get_plane(crtc->primary->fb, 0); - - spin_lock(&dc->lock); - - /* check if new start address has been latched */ - tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); - tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); - base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR); - tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); - - spin_unlock(&dc->lock); - - if (base == bo->paddr + crtc->primary->fb->offsets[0]) { - drm_crtc_send_vblank_event(crtc, dc->event); - drm_crtc_vblank_put(crtc); - dc->event = NULL; - } - - spin_unlock_irqrestore(&drm->event_lock, flags); -} - -static irqreturn_t tegra_dc_irq(int irq, void *data) -{ - struct tegra_dc *dc = data; - unsigned long status; - - status = tegra_dc_readl(dc, DC_CMD_INT_STATUS); - tegra_dc_writel(dc, status, DC_CMD_INT_STATUS); - - if (status & FRAME_END_INT) { - /* - dev_dbg(dc->dev, "%s(): frame end\n", __func__); - */ - dc->stats.frames++; - } - - if (status & VBLANK_INT) { - /* - dev_dbg(dc->dev, "%s(): vertical blank\n", __func__); - */ - drm_crtc_handle_vblank(&dc->base); - tegra_dc_finish_page_flip(dc); - dc->stats.vblank++; - } - - if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) { - /* - dev_dbg(dc->dev, "%s(): underflow\n", __func__); - */ - dc->stats.underflow++; - } - - if (status & (WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT)) { - /* - dev_dbg(dc->dev, "%s(): overflow\n", __func__); - */ - dc->stats.overflow++; - } - - return IRQ_HANDLED; -} - -#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name } - -static const struct debugfs_reg32 tegra_dc_regs[] = { - DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT), - DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL), - DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_ERROR), - DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT), - DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL), - DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_ERROR), - DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT), - DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL), - DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_ERROR), - DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT), - DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL), - DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_ERROR), - DEBUGFS_REG32(DC_CMD_CONT_SYNCPT_VSYNC), - DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND_OPTION0), - DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND), - DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE), - DEBUGFS_REG32(DC_CMD_DISPLAY_POWER_CONTROL), - DEBUGFS_REG32(DC_CMD_INT_STATUS), - DEBUGFS_REG32(DC_CMD_INT_MASK), - DEBUGFS_REG32(DC_CMD_INT_ENABLE), - DEBUGFS_REG32(DC_CMD_INT_TYPE), - DEBUGFS_REG32(DC_CMD_INT_POLARITY), - DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE1), - DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE2), - DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE3), - DEBUGFS_REG32(DC_CMD_STATE_ACCESS), - DEBUGFS_REG32(DC_CMD_STATE_CONTROL), - DEBUGFS_REG32(DC_CMD_DISPLAY_WINDOW_HEADER), - DEBUGFS_REG32(DC_CMD_REG_ACT_CONTROL), - DEBUGFS_REG32(DC_COM_CRC_CONTROL), - DEBUGFS_REG32(DC_COM_CRC_CHECKSUM), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(0)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(1)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(2)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(3)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(0)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(1)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(2)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(3)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(0)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(1)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(2)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(3)), - DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(0)), - DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(1)), - DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(2)), - DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(3)), - DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(0)), - DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(1)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(0)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(1)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(2)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(3)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(4)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(5)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(6)), - DEBUGFS_REG32(DC_COM_PIN_MISC_CONTROL), - DEBUGFS_REG32(DC_COM_PIN_PM0_CONTROL), - DEBUGFS_REG32(DC_COM_PIN_PM0_DUTY_CYCLE), - DEBUGFS_REG32(DC_COM_PIN_PM1_CONTROL), - DEBUGFS_REG32(DC_COM_PIN_PM1_DUTY_CYCLE), - DEBUGFS_REG32(DC_COM_SPI_CONTROL), - DEBUGFS_REG32(DC_COM_SPI_START_BYTE), - DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_AB), - DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_CD), - DEBUGFS_REG32(DC_COM_HSPI_CS_DC), - DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_A), - DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_B), - DEBUGFS_REG32(DC_COM_GPIO_CTRL), - DEBUGFS_REG32(DC_COM_GPIO_DEBOUNCE_COUNTER), - DEBUGFS_REG32(DC_COM_CRC_CHECKSUM_LATCHED), - DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS0), - DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS1), - DEBUGFS_REG32(DC_DISP_DISP_WIN_OPTIONS), - DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY), - DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER), - DEBUGFS_REG32(DC_DISP_DISP_TIMING_OPTIONS), - DEBUGFS_REG32(DC_DISP_REF_TO_SYNC), - DEBUGFS_REG32(DC_DISP_SYNC_WIDTH), - DEBUGFS_REG32(DC_DISP_BACK_PORCH), - DEBUGFS_REG32(DC_DISP_ACTIVE), - DEBUGFS_REG32(DC_DISP_FRONT_PORCH), - DEBUGFS_REG32(DC_DISP_H_PULSE0_CONTROL), - DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_A), - DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_B), - DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_C), - DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_D), - DEBUGFS_REG32(DC_DISP_H_PULSE1_CONTROL), - DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_A), - DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_B), - DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_C), - DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_D), - DEBUGFS_REG32(DC_DISP_H_PULSE2_CONTROL), - DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_A), - DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_B), - DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_C), - DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_D), - DEBUGFS_REG32(DC_DISP_V_PULSE0_CONTROL), - DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_A), - DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_B), - DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_C), - DEBUGFS_REG32(DC_DISP_V_PULSE1_CONTROL), - DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_A), - DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_B), - DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_C), - DEBUGFS_REG32(DC_DISP_V_PULSE2_CONTROL), - DEBUGFS_REG32(DC_DISP_V_PULSE2_POSITION_A), - DEBUGFS_REG32(DC_DISP_V_PULSE3_CONTROL), - DEBUGFS_REG32(DC_DISP_V_PULSE3_POSITION_A), - DEBUGFS_REG32(DC_DISP_M0_CONTROL), - DEBUGFS_REG32(DC_DISP_M1_CONTROL), - DEBUGFS_REG32(DC_DISP_DI_CONTROL), - DEBUGFS_REG32(DC_DISP_PP_CONTROL), - DEBUGFS_REG32(DC_DISP_PP_SELECT_A), - DEBUGFS_REG32(DC_DISP_PP_SELECT_B), - DEBUGFS_REG32(DC_DISP_PP_SELECT_C), - DEBUGFS_REG32(DC_DISP_PP_SELECT_D), - DEBUGFS_REG32(DC_DISP_DISP_CLOCK_CONTROL), - DEBUGFS_REG32(DC_DISP_DISP_INTERFACE_CONTROL), - DEBUGFS_REG32(DC_DISP_DISP_COLOR_CONTROL), - DEBUGFS_REG32(DC_DISP_SHIFT_CLOCK_OPTIONS), - DEBUGFS_REG32(DC_DISP_DATA_ENABLE_OPTIONS), - DEBUGFS_REG32(DC_DISP_SERIAL_INTERFACE_OPTIONS), - DEBUGFS_REG32(DC_DISP_LCD_SPI_OPTIONS), - DEBUGFS_REG32(DC_DISP_BORDER_COLOR), - DEBUGFS_REG32(DC_DISP_COLOR_KEY0_LOWER), - DEBUGFS_REG32(DC_DISP_COLOR_KEY0_UPPER), - DEBUGFS_REG32(DC_DISP_COLOR_KEY1_LOWER), - DEBUGFS_REG32(DC_DISP_COLOR_KEY1_UPPER), - DEBUGFS_REG32(DC_DISP_CURSOR_FOREGROUND), - DEBUGFS_REG32(DC_DISP_CURSOR_BACKGROUND), - DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR), - DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_NS), - DEBUGFS_REG32(DC_DISP_CURSOR_POSITION), - DEBUGFS_REG32(DC_DISP_CURSOR_POSITION_NS), - DEBUGFS_REG32(DC_DISP_INIT_SEQ_CONTROL), - DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_A), - DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_B), - DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_C), - DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_D), - DEBUGFS_REG32(DC_DISP_DC_MCCIF_FIFOCTRL), - DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0A_HYST), - DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0B_HYST), - DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1A_HYST), - DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1B_HYST), - DEBUGFS_REG32(DC_DISP_DAC_CRT_CTRL), - DEBUGFS_REG32(DC_DISP_DISP_MISC_CONTROL), - DEBUGFS_REG32(DC_DISP_SD_CONTROL), - DEBUGFS_REG32(DC_DISP_SD_CSC_COEFF), - DEBUGFS_REG32(DC_DISP_SD_LUT(0)), - DEBUGFS_REG32(DC_DISP_SD_LUT(1)), - DEBUGFS_REG32(DC_DISP_SD_LUT(2)), - DEBUGFS_REG32(DC_DISP_SD_LUT(3)), - DEBUGFS_REG32(DC_DISP_SD_LUT(4)), - DEBUGFS_REG32(DC_DISP_SD_LUT(5)), - DEBUGFS_REG32(DC_DISP_SD_LUT(6)), - DEBUGFS_REG32(DC_DISP_SD_LUT(7)), - DEBUGFS_REG32(DC_DISP_SD_LUT(8)), - DEBUGFS_REG32(DC_DISP_SD_FLICKER_CONTROL), - DEBUGFS_REG32(DC_DISP_DC_PIXEL_COUNT), - DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(0)), - DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(1)), - DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(2)), - DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(3)), - DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(4)), - DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(5)), - DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(6)), - DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(7)), - DEBUGFS_REG32(DC_DISP_SD_BL_TF(0)), - DEBUGFS_REG32(DC_DISP_SD_BL_TF(1)), - DEBUGFS_REG32(DC_DISP_SD_BL_TF(2)), - DEBUGFS_REG32(DC_DISP_SD_BL_TF(3)), - DEBUGFS_REG32(DC_DISP_SD_BL_CONTROL), - DEBUGFS_REG32(DC_DISP_SD_HW_K_VALUES), - DEBUGFS_REG32(DC_DISP_SD_MAN_K_VALUES), - DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_HI), - DEBUGFS_REG32(DC_DISP_BLEND_CURSOR_CONTROL), - DEBUGFS_REG32(DC_WIN_WIN_OPTIONS), - DEBUGFS_REG32(DC_WIN_BYTE_SWAP), - DEBUGFS_REG32(DC_WIN_BUFFER_CONTROL), - DEBUGFS_REG32(DC_WIN_COLOR_DEPTH), - DEBUGFS_REG32(DC_WIN_POSITION), - DEBUGFS_REG32(DC_WIN_SIZE), - DEBUGFS_REG32(DC_WIN_PRESCALED_SIZE), - DEBUGFS_REG32(DC_WIN_H_INITIAL_DDA), - DEBUGFS_REG32(DC_WIN_V_INITIAL_DDA), - DEBUGFS_REG32(DC_WIN_DDA_INC), - DEBUGFS_REG32(DC_WIN_LINE_STRIDE), - DEBUGFS_REG32(DC_WIN_BUF_STRIDE), - DEBUGFS_REG32(DC_WIN_UV_BUF_STRIDE), - DEBUGFS_REG32(DC_WIN_BUFFER_ADDR_MODE), - DEBUGFS_REG32(DC_WIN_DV_CONTROL), - DEBUGFS_REG32(DC_WIN_BLEND_NOKEY), - DEBUGFS_REG32(DC_WIN_BLEND_1WIN), - DEBUGFS_REG32(DC_WIN_BLEND_2WIN_X), - DEBUGFS_REG32(DC_WIN_BLEND_2WIN_Y), - DEBUGFS_REG32(DC_WIN_BLEND_3WIN_XY), - DEBUGFS_REG32(DC_WIN_HP_FETCH_CONTROL), - DEBUGFS_REG32(DC_WINBUF_START_ADDR), - DEBUGFS_REG32(DC_WINBUF_START_ADDR_NS), - DEBUGFS_REG32(DC_WINBUF_START_ADDR_U), - DEBUGFS_REG32(DC_WINBUF_START_ADDR_U_NS), - DEBUGFS_REG32(DC_WINBUF_START_ADDR_V), - DEBUGFS_REG32(DC_WINBUF_START_ADDR_V_NS), - DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET), - DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET_NS), - DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET), - DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET_NS), - DEBUGFS_REG32(DC_WINBUF_UFLOW_STATUS), - DEBUGFS_REG32(DC_WINBUF_AD_UFLOW_STATUS), - DEBUGFS_REG32(DC_WINBUF_BD_UFLOW_STATUS), - DEBUGFS_REG32(DC_WINBUF_CD_UFLOW_STATUS), -}; - -static int tegra_dc_show_regs(struct seq_file *s, void *data) -{ - struct drm_info_node *node = s->private; - struct tegra_dc *dc = node->info_ent->data; - unsigned int i; - int err = 0; +{ + struct tegra_dc *dc = to_tegra_dc(crtc); + u32 value; - drm_modeset_lock(&dc->base.mutex, NULL); + if (!tegra_dc_idle(dc)) { + tegra_dc_stop(dc); - if (!dc->base.state->active) { - err = -EBUSY; - goto unlock; + /* + * Ignore the return value, there isn't anything useful to do + * in case this fails. + */ + tegra_dc_wait_idle(dc, 100); } - for (i = 0; i < ARRAY_SIZE(tegra_dc_regs); i++) { - unsigned int offset = tegra_dc_regs[i].offset; - - seq_printf(s, "%-40s %#05x %08x\n", tegra_dc_regs[i].name, - offset, tegra_dc_readl(dc, offset)); + /* + * This should really be part of the RGB encoder driver, but clearing + * these bits has the side-effect of stopping the display controller. + * When that happens no VBLANK interrupts will be raised. At the same + * time the encoder is disabled before the display controller, so the + * above code is always going to timeout waiting for the controller + * to go idle. + * + * Given the close coupling between the RGB encoder and the display + * controller doing it here is still kind of okay. None of the other + * encoder drivers require these bits to be cleared. + * + * XXX: Perhaps given that the display controller is switched off at + * this point anyway maybe clearing these bits isn't even useful for + * the RGB encoder? + */ + if (dc->rgb) { + value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); + value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | + PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); + tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); } -unlock: - drm_modeset_unlock(&dc->base.mutex); - return err; + tegra_dc_stats_reset(&dc->stats); + drm_crtc_vblank_off(crtc); + + pm_runtime_put_sync(dc->dev); } -static int tegra_dc_show_crc(struct seq_file *s, void *data) +static void tegra_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) { - struct drm_info_node *node = s->private; - struct tegra_dc *dc = node->info_ent->data; - int err = 0; + struct drm_display_mode *mode = &crtc->state->adjusted_mode; + struct tegra_dc_state *state = to_dc_state(crtc->state); + struct tegra_dc *dc = to_tegra_dc(crtc); u32 value; - drm_modeset_lock(&dc->base.mutex, NULL); + pm_runtime_get_sync(dc->dev); - if (!dc->base.state->active) { - err = -EBUSY; - goto unlock; + /* initialize display controller */ + if (dc->syncpt) { + u32 syncpt = host1x_syncpt_id(dc->syncpt); + + value = SYNCPT_CNTRL_NO_STALL; + tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); + + value = SYNCPT_VSYNC_ENABLE | syncpt; + tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC); } - value = DC_COM_CRC_CONTROL_ACTIVE_DATA | DC_COM_CRC_CONTROL_ENABLE; - tegra_dc_writel(dc, value, DC_COM_CRC_CONTROL); - tegra_dc_commit(dc); + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); - drm_crtc_wait_one_vblank(&dc->base); - drm_crtc_wait_one_vblank(&dc->base); + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); - value = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM); - seq_printf(s, "%08x\n", value); + /* initialize timer */ + value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) | + WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20); + tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY); - tegra_dc_writel(dc, 0, DC_COM_CRC_CONTROL); + value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) | + WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1); + tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); -unlock: - drm_modeset_unlock(&dc->base.mutex); - return err; + value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); + + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_MASK); + + if (dc->soc->supports_border_color) + tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR); + + /* apply PLL and pixel clock changes */ + tegra_dc_commit_state(dc, state); + + /* program display mode */ + tegra_dc_set_timings(dc, mode); + + /* interlacing isn't supported yet, so disable it */ + if (dc->soc->supports_interlacing) { + value = tegra_dc_readl(dc, DC_DISP_INTERLACE_CONTROL); + value &= ~INTERLACE_ENABLE; + tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL); + } + + value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); + value &= ~DISP_CTRL_MODE_MASK; + value |= DISP_CTRL_MODE_C_DISPLAY; + tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); + + value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); + value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | + PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; + tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); + + tegra_dc_commit(dc); + + drm_crtc_vblank_on(crtc); } -static int tegra_dc_show_stats(struct seq_file *s, void *data) +static int tegra_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) { - struct drm_info_node *node = s->private; - struct tegra_dc *dc = node->info_ent->data; + return 0; +} - seq_printf(s, "frames: %lu\n", dc->stats.frames); - seq_printf(s, "vblank: %lu\n", dc->stats.vblank); - seq_printf(s, "underflow: %lu\n", dc->stats.underflow); - seq_printf(s, "overflow: %lu\n", dc->stats.overflow); +static void tegra_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct tegra_dc *dc = to_tegra_dc(crtc); - return 0; + if (crtc->state->event) { + crtc->state->event->pipe = drm_crtc_index(crtc); + + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + + dc->event = crtc->state->event; + crtc->state->event = NULL; + } } -static struct drm_info_list debugfs_files[] = { - { "regs", tegra_dc_show_regs, 0, NULL }, - { "crc", tegra_dc_show_crc, 0, NULL }, - { "stats", tegra_dc_show_stats, 0, NULL }, +static void tegra_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct tegra_dc_state *state = to_dc_state(crtc->state); + struct tegra_dc *dc = to_tegra_dc(crtc); + + tegra_dc_writel(dc, state->planes << 8, DC_CMD_STATE_CONTROL); + tegra_dc_writel(dc, state->planes, DC_CMD_STATE_CONTROL); +} + +static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { + .atomic_check = tegra_crtc_atomic_check, + .atomic_begin = tegra_crtc_atomic_begin, + .atomic_flush = tegra_crtc_atomic_flush, + .atomic_enable = tegra_crtc_atomic_enable, + .atomic_disable = tegra_crtc_atomic_disable, }; -static int tegra_dc_debugfs_init(struct tegra_dc *dc, struct drm_minor *minor) +static void tegra_dc_finish_page_flip(struct tegra_dc *dc) { - unsigned int i; - char *name; - int err; - - name = kasprintf(GFP_KERNEL, "dc.%d", dc->pipe); - dc->debugfs = debugfs_create_dir(name, minor->debugfs_root); - kfree(name); + struct drm_device *drm = dc->base.dev; + struct drm_crtc *crtc = &dc->base; + unsigned long flags, base; + struct tegra_bo *bo; - if (!dc->debugfs) - return -ENOMEM; + spin_lock_irqsave(&drm->event_lock, flags); - dc->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), - GFP_KERNEL); - if (!dc->debugfs_files) { - err = -ENOMEM; - goto remove; + if (!dc->event) { + spin_unlock_irqrestore(&drm->event_lock, flags); + return; } - for (i = 0; i < ARRAY_SIZE(debugfs_files); i++) - dc->debugfs_files[i].data = dc; + bo = tegra_fb_get_plane(crtc->primary->fb, 0); - err = drm_debugfs_create_files(dc->debugfs_files, - ARRAY_SIZE(debugfs_files), - dc->debugfs, minor); - if (err < 0) - goto free; + spin_lock(&dc->lock); - dc->minor = minor; + /* check if new start address has been latched */ + tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); + tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); + base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR); + tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); - return 0; + spin_unlock(&dc->lock); -free: - kfree(dc->debugfs_files); - dc->debugfs_files = NULL; -remove: - debugfs_remove(dc->debugfs); - dc->debugfs = NULL; + if (base == bo->paddr + crtc->primary->fb->offsets[0]) { + drm_crtc_send_vblank_event(crtc, dc->event); + drm_crtc_vblank_put(crtc); + dc->event = NULL; + } - return err; + spin_unlock_irqrestore(&drm->event_lock, flags); } -static int tegra_dc_debugfs_exit(struct tegra_dc *dc) +static irqreturn_t tegra_dc_irq(int irq, void *data) { - drm_debugfs_remove_files(dc->debugfs_files, ARRAY_SIZE(debugfs_files), - dc->minor); - dc->minor = NULL; + struct tegra_dc *dc = data; + unsigned long status; - kfree(dc->debugfs_files); - dc->debugfs_files = NULL; + status = tegra_dc_readl(dc, DC_CMD_INT_STATUS); + tegra_dc_writel(dc, status, DC_CMD_INT_STATUS); + + if (status & FRAME_END_INT) { + /* + dev_dbg(dc->dev, "%s(): frame end\n", __func__); + */ + dc->stats.frames++; + } - debugfs_remove(dc->debugfs); - dc->debugfs = NULL; + if (status & VBLANK_INT) { + /* + dev_dbg(dc->dev, "%s(): vertical blank\n", __func__); + */ + drm_crtc_handle_vblank(&dc->base); + tegra_dc_finish_page_flip(dc); + dc->stats.vblank++; + } - return 0; + if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) { + /* + dev_dbg(dc->dev, "%s(): underflow\n", __func__); + */ + dc->stats.underflow++; + } + + if (status & (WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT)) { + /* + dev_dbg(dc->dev, "%s(): overflow\n", __func__); + */ + dc->stats.overflow++; + } + + return IRQ_HANDLED; } static int tegra_dc_init(struct host1x_client *client) @@ -1797,12 +1781,6 @@ static int tegra_dc_init(struct host1x_client *client) if (err < 0) goto cleanup; - if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_dc_debugfs_init(dc, drm->primary); - if (err < 0) - dev_err(dc->dev, "debugfs setup failed: %d\n", err); - } - err = devm_request_irq(dc->dev, dc->irq, tegra_dc_irq, 0, dev_name(dc->dev), dc); if (err < 0) { @@ -1835,12 +1813,6 @@ static int tegra_dc_exit(struct host1x_client *client) devm_free_irq(dc->dev, dc->irq, dc); - if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_dc_debugfs_exit(dc); - if (err < 0) - dev_err(dc->dev, "debugfs cleanup failed: %d\n", err); - } - err = tegra_dc_rgb_exit(dc); if (err) { dev_err(dc->dev, "failed to shutdown RGB output: %d\n", err); diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index cb100b6e3282..032c734dd470 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -56,8 +56,6 @@ struct tegra_dc { struct list_head list; struct drm_info_list *debugfs_files; - struct drm_minor *minor; - struct dentry *debugfs; /* page-flip handling */ struct drm_pending_vblank_event *event; -- cgit v1.2.3 From a813d70425061ad59bc8f980ae822f4fc35c9250 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 8 Nov 2017 13:12:44 +0100 Subject: drm/tegra: dsi: Register debugfs in ->late_register() The ->late_register() and ->early_unregister() callbacks are called at the right time to make sure userspace only accesses interfaces when it should. Move debugfs registration and unregistration to these callback functions to avoid potential races with userspace. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dsi.c | 58 ++++++++++++++------------------------------- 1 file changed, 18 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index 4a78af08df82..4d2ed966f9e3 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -65,8 +65,6 @@ struct tegra_dsi { struct clk *clk; struct drm_info_list *debugfs_files; - struct drm_minor *minor; - struct dentry *debugfs; unsigned long flags; enum mipi_dsi_pixel_format format; @@ -230,58 +228,46 @@ static struct drm_info_list debugfs_files[] = { { "regs", tegra_dsi_show_regs, 0, NULL }, }; -static int tegra_dsi_debugfs_init(struct tegra_dsi *dsi, - struct drm_minor *minor) +static int tegra_dsi_late_register(struct drm_connector *connector) { - const char *name = dev_name(dsi->dev); - unsigned int i; + struct tegra_output *output = connector_to_output(connector); + unsigned int i, count = ARRAY_SIZE(debugfs_files); + struct drm_minor *minor = connector->dev->primary; + struct dentry *root = connector->debugfs_entry; + struct tegra_dsi *dsi = to_dsi(output); int err; - dsi->debugfs = debugfs_create_dir(name, minor->debugfs_root); - if (!dsi->debugfs) - return -ENOMEM; - dsi->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), GFP_KERNEL); - if (!dsi->debugfs_files) { - err = -ENOMEM; - goto remove; - } + if (!dsi->debugfs_files) + return -ENOMEM; - for (i = 0; i < ARRAY_SIZE(debugfs_files); i++) + for (i = 0; i < count; i++) dsi->debugfs_files[i].data = dsi; - err = drm_debugfs_create_files(dsi->debugfs_files, - ARRAY_SIZE(debugfs_files), - dsi->debugfs, minor); + err = drm_debugfs_create_files(dsi->debugfs_files, count, root, minor); if (err < 0) goto free; - dsi->minor = minor; - return 0; free: kfree(dsi->debugfs_files); dsi->debugfs_files = NULL; -remove: - debugfs_remove(dsi->debugfs); - dsi->debugfs = NULL; return err; } -static void tegra_dsi_debugfs_exit(struct tegra_dsi *dsi) +static void tegra_dsi_early_unregister(struct drm_connector *connector) { - drm_debugfs_remove_files(dsi->debugfs_files, ARRAY_SIZE(debugfs_files), - dsi->minor); - dsi->minor = NULL; + struct tegra_output *output = connector_to_output(connector); + unsigned int count = ARRAY_SIZE(debugfs_files); + struct tegra_dsi *dsi = to_dsi(output); + drm_debugfs_remove_files(dsi->debugfs_files, count, + connector->dev->primary); kfree(dsi->debugfs_files); dsi->debugfs_files = NULL; - - debugfs_remove(dsi->debugfs); - dsi->debugfs = NULL; } #define PKT_ID0(id) ((((id) & 0x3f) << 3) | (1 << 9)) @@ -823,6 +809,8 @@ static const struct drm_connector_funcs tegra_dsi_connector_funcs = { .destroy = tegra_output_connector_destroy, .atomic_duplicate_state = tegra_dsi_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .late_register = tegra_dsi_late_register, + .early_unregister = tegra_dsi_early_unregister, }; static enum drm_mode_status @@ -1076,12 +1064,6 @@ static int tegra_dsi_init(struct host1x_client *client) dsi->output.encoder.possible_crtcs = 0x3; } - if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_dsi_debugfs_init(dsi, drm->primary); - if (err < 0) - dev_err(dsi->dev, "debugfs setup failed: %d\n", err); - } - return 0; } @@ -1090,10 +1072,6 @@ static int tegra_dsi_exit(struct host1x_client *client) struct tegra_dsi *dsi = host1x_client_to_dsi(client); tegra_output_exit(&dsi->output); - - if (IS_ENABLED(CONFIG_DEBUG_FS)) - tegra_dsi_debugfs_exit(dsi); - regulator_disable(dsi->vdd); return 0; -- cgit v1.2.3 From 1d60047dd6fcd8bb840f3652ce25ab478a69c7ef Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 8 Nov 2017 13:18:31 +0100 Subject: drm/tegra: hdmi: Register debugfs in ->late_register() The ->late_register() and ->early_unregister() callbacks are called at the right time to make sure userspace only accesses interfaces when it should. Move debugfs registration and unregistration to these callback functions to avoid potential races with userspace. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/hdmi.c | 510 +++++++++++++++++++++---------------------- 1 file changed, 245 insertions(+), 265 deletions(-) diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index a68d80476e46..784739a9f497 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -79,8 +79,6 @@ struct tegra_hdmi { bool dvi; struct drm_info_list *debugfs_files; - struct drm_minor *minor; - struct dentry *debugfs; }; static inline struct tegra_hdmi * @@ -910,6 +908,249 @@ tegra_hdmi_connector_detect(struct drm_connector *connector, bool force) return status; } +#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name } + +static const struct debugfs_reg32 tegra_hdmi_regs[] = { + DEBUGFS_REG32(HDMI_CTXSW), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_STATE0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_STATE1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_STATE2), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_AN_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_AN_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CN_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CN_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_AKSV_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_AKSV_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_BKSV_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_BKSV_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CKSV_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CKSV_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_DKSV_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_DKSV_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CMODE), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_MPRIME_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_MPRIME_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_SPRIME_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB2), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB1), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_RI), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CS_MSB), + DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CS_LSB), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_EMU0), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_EMU_RDATA0), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_EMU1), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_EMU2), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_STATUS), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_STATUS), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_STATUS), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_HEADER), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_HIGH), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_VSYNC_KEEPOUT), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_VSYNC_WINDOW), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GCP_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GCP_STATUS), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GCP_SUBPACK), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_CHANNEL_STATUS1), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_CHANNEL_STATUS2), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_EMU0), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_EMU1), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_EMU1_RDATA), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_SPARE), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_SPDIF_CHN_STATUS1), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_SPDIF_CHN_STATUS2), + DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_HDCPRIF_ROM_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CAP), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_PWR), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_TEST), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_PLL0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_PLL1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_PLL2), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CSTM), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_LVDS), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CRCA), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CRCB), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_BLANK), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_CTL), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(0)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(1)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(2)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(3)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(4)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(5)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(6)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(7)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(8)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(9)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(10)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(11)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(12)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(13)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(14)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(15)), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_VCRCA0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_VCRCA1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CCRCA0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CCRCA1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_EDATAA0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_EDATAA1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_COUNTA0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_COUNTA1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_DEBUGA0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_DEBUGA1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_TRIG), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_MSCHECK), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_DEBUG0), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_DEBUG1), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_DEBUG2), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(0)), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(1)), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(2)), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(3)), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(4)), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(5)), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(6)), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_PULSE_WIDTH), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_THRESHOLD), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_CNTRL0), + DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_N), + DEBUGFS_REG32(HDMI_NV_PDISP_HDCPRIF_ROM_TIMING), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_REFCLK), + DEBUGFS_REG32(HDMI_NV_PDISP_CRC_CONTROL), + DEBUGFS_REG32(HDMI_NV_PDISP_INPUT_CONTROL), + DEBUGFS_REG32(HDMI_NV_PDISP_SCRATCH), + DEBUGFS_REG32(HDMI_NV_PDISP_PE_CURRENT), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_CTRL), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_DEBUG0), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_DEBUG1), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_DEBUG2), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_0), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_1), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_2), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_3), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_TRIG), + DEBUGFS_REG32(HDMI_NV_PDISP_KEY_SKEY_INDEX), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_CNTRL0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_SPARE0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH1), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE), + DEBUGFS_REG32(HDMI_NV_PDISP_INT_STATUS), + DEBUGFS_REG32(HDMI_NV_PDISP_INT_MASK), + DEBUGFS_REG32(HDMI_NV_PDISP_INT_ENABLE), + DEBUGFS_REG32(HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT), +}; + +static int tegra_hdmi_show_regs(struct seq_file *s, void *data) +{ + struct drm_info_node *node = s->private; + struct tegra_hdmi *hdmi = node->info_ent->data; + struct drm_crtc *crtc = hdmi->output.encoder.crtc; + struct drm_device *drm = node->minor->dev; + unsigned int i; + int err = 0; + + drm_modeset_lock_all(drm); + + if (!crtc || !crtc->state->active) { + err = -EBUSY; + goto unlock; + } + + for (i = 0; i < ARRAY_SIZE(tegra_hdmi_regs); i++) { + unsigned int offset = tegra_hdmi_regs[i].offset; + + seq_printf(s, "%-56s %#05x %08x\n", tegra_hdmi_regs[i].name, + offset, tegra_hdmi_readl(hdmi, offset)); + } + +unlock: + drm_modeset_unlock_all(drm); + return err; +} + +static struct drm_info_list debugfs_files[] = { + { "regs", tegra_hdmi_show_regs, 0, NULL }, +}; + +static int tegra_hdmi_late_register(struct drm_connector *connector) +{ + struct tegra_output *output = connector_to_output(connector); + unsigned int i, count = ARRAY_SIZE(debugfs_files); + struct drm_minor *minor = connector->dev->primary; + struct dentry *root = connector->debugfs_entry; + struct tegra_hdmi *hdmi = to_hdmi(output); + int err; + + hdmi->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), + GFP_KERNEL); + if (!hdmi->debugfs_files) + return -ENOMEM; + + for (i = 0; i < count; i++) + hdmi->debugfs_files[i].data = hdmi; + + err = drm_debugfs_create_files(hdmi->debugfs_files, count, root, minor); + if (err < 0) + goto free; + + return 0; + +free: + kfree(hdmi->debugfs_files); + hdmi->debugfs_files = NULL; + + return err; +} + +static void tegra_hdmi_early_unregister(struct drm_connector *connector) +{ + struct tegra_output *output = connector_to_output(connector); + struct drm_minor *minor = connector->dev->primary; + unsigned int count = ARRAY_SIZE(debugfs_files); + struct tegra_hdmi *hdmi = to_hdmi(output); + + drm_debugfs_remove_files(hdmi->debugfs_files, count, minor); + kfree(hdmi->debugfs_files); + hdmi->debugfs_files = NULL; +} + static const struct drm_connector_funcs tegra_hdmi_connector_funcs = { .reset = drm_atomic_helper_connector_reset, .detect = tegra_hdmi_connector_detect, @@ -917,6 +1158,8 @@ static const struct drm_connector_funcs tegra_hdmi_connector_funcs = { .destroy = tegra_output_connector_destroy, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .late_register = tegra_hdmi_late_register, + .early_unregister = tegra_hdmi_early_unregister, }; static enum drm_mode_status @@ -1225,260 +1468,6 @@ static const struct drm_encoder_helper_funcs tegra_hdmi_encoder_helper_funcs = { .atomic_check = tegra_hdmi_encoder_atomic_check, }; -#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name } - -static const struct debugfs_reg32 tegra_hdmi_regs[] = { - DEBUGFS_REG32(HDMI_CTXSW), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_STATE0), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_STATE1), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_STATE2), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_AN_MSB), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_AN_LSB), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CN_MSB), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CN_LSB), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_AKSV_MSB), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_AKSV_LSB), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_BKSV_MSB), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_BKSV_LSB), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CKSV_MSB), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CKSV_LSB), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_DKSV_MSB), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_DKSV_LSB), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CTRL), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CMODE), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_MPRIME_MSB), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_MPRIME_LSB), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_SPRIME_MSB), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB2), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB1), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_RI), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CS_MSB), - DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CS_LSB), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_EMU0), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_EMU_RDATA0), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_EMU1), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_EMU2), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_STATUS), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_STATUS), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_CTRL), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_STATUS), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_HEADER), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_LOW), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_HIGH), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_LOW), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_HIGH), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_LOW), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_HIGH), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_LOW), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_HIGH), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_CTRL), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_HIGH), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_HIGH), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_HIGH), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_HIGH), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_HIGH), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_HIGH), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_CTRL), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_VSYNC_KEEPOUT), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_VSYNC_WINDOW), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GCP_CTRL), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GCP_STATUS), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GCP_SUBPACK), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_CHANNEL_STATUS1), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_CHANNEL_STATUS2), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_EMU0), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_EMU1), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_EMU1_RDATA), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_SPARE), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_SPDIF_CHN_STATUS1), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_SPDIF_CHN_STATUS2), - DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_HDCPRIF_ROM_CTRL), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CAP), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_PWR), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_TEST), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_PLL0), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_PLL1), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_PLL2), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CSTM), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_LVDS), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CRCA), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CRCB), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_BLANK), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_CTL), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(0)), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(1)), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(2)), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(3)), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(4)), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(5)), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(6)), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(7)), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(8)), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(9)), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(10)), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(11)), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(12)), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(13)), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(14)), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(15)), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_VCRCA0), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_VCRCA1), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CCRCA0), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CCRCA1), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_EDATAA0), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_EDATAA1), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_COUNTA0), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_COUNTA1), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_DEBUGA0), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_DEBUGA1), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_TRIG), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_MSCHECK), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT), - DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_DEBUG0), - DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_DEBUG1), - DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_DEBUG2), - DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(0)), - DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(1)), - DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(2)), - DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(3)), - DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(4)), - DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(5)), - DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(6)), - DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_PULSE_WIDTH), - DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_THRESHOLD), - DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_CNTRL0), - DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_N), - DEBUGFS_REG32(HDMI_NV_PDISP_HDCPRIF_ROM_TIMING), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_REFCLK), - DEBUGFS_REG32(HDMI_NV_PDISP_CRC_CONTROL), - DEBUGFS_REG32(HDMI_NV_PDISP_INPUT_CONTROL), - DEBUGFS_REG32(HDMI_NV_PDISP_SCRATCH), - DEBUGFS_REG32(HDMI_NV_PDISP_PE_CURRENT), - DEBUGFS_REG32(HDMI_NV_PDISP_KEY_CTRL), - DEBUGFS_REG32(HDMI_NV_PDISP_KEY_DEBUG0), - DEBUGFS_REG32(HDMI_NV_PDISP_KEY_DEBUG1), - DEBUGFS_REG32(HDMI_NV_PDISP_KEY_DEBUG2), - DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_0), - DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_1), - DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_2), - DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_3), - DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_TRIG), - DEBUGFS_REG32(HDMI_NV_PDISP_KEY_SKEY_INDEX), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_CNTRL0), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_SPARE0), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH1), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE), - DEBUGFS_REG32(HDMI_NV_PDISP_INT_STATUS), - DEBUGFS_REG32(HDMI_NV_PDISP_INT_MASK), - DEBUGFS_REG32(HDMI_NV_PDISP_INT_ENABLE), - DEBUGFS_REG32(HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT), -}; - -static int tegra_hdmi_show_regs(struct seq_file *s, void *data) -{ - struct drm_info_node *node = s->private; - struct tegra_hdmi *hdmi = node->info_ent->data; - struct drm_crtc *crtc = hdmi->output.encoder.crtc; - struct drm_device *drm = node->minor->dev; - unsigned int i; - int err = 0; - - drm_modeset_lock_all(drm); - - if (!crtc || !crtc->state->active) { - err = -EBUSY; - goto unlock; - } - - for (i = 0; i < ARRAY_SIZE(tegra_hdmi_regs); i++) { - unsigned int offset = tegra_hdmi_regs[i].offset; - - seq_printf(s, "%-56s %#05x %08x\n", tegra_hdmi_regs[i].name, - offset, tegra_hdmi_readl(hdmi, offset)); - } - -unlock: - drm_modeset_unlock_all(drm); - return err; -} - -static struct drm_info_list debugfs_files[] = { - { "regs", tegra_hdmi_show_regs, 0, NULL }, -}; - -static int tegra_hdmi_debugfs_init(struct tegra_hdmi *hdmi, - struct drm_minor *minor) -{ - unsigned int i; - int err; - - hdmi->debugfs = debugfs_create_dir("hdmi", minor->debugfs_root); - if (!hdmi->debugfs) - return -ENOMEM; - - hdmi->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), - GFP_KERNEL); - if (!hdmi->debugfs_files) { - err = -ENOMEM; - goto remove; - } - - for (i = 0; i < ARRAY_SIZE(debugfs_files); i++) - hdmi->debugfs_files[i].data = hdmi; - - err = drm_debugfs_create_files(hdmi->debugfs_files, - ARRAY_SIZE(debugfs_files), - hdmi->debugfs, minor); - if (err < 0) - goto free; - - hdmi->minor = minor; - - return 0; - -free: - kfree(hdmi->debugfs_files); - hdmi->debugfs_files = NULL; -remove: - debugfs_remove(hdmi->debugfs); - hdmi->debugfs = NULL; - - return err; -} - -static void tegra_hdmi_debugfs_exit(struct tegra_hdmi *hdmi) -{ - drm_debugfs_remove_files(hdmi->debugfs_files, ARRAY_SIZE(debugfs_files), - hdmi->minor); - hdmi->minor = NULL; - - kfree(hdmi->debugfs_files); - hdmi->debugfs_files = NULL; - - debugfs_remove(hdmi->debugfs); - hdmi->debugfs = NULL; -} - static int tegra_hdmi_init(struct host1x_client *client) { struct drm_device *drm = dev_get_drvdata(client->parent); @@ -1511,12 +1500,6 @@ static int tegra_hdmi_init(struct host1x_client *client) hdmi->output.encoder.possible_crtcs = 0x3; - if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_hdmi_debugfs_init(hdmi, drm->primary); - if (err < 0) - dev_err(client->dev, "debugfs setup failed: %d\n", err); - } - err = regulator_enable(hdmi->hdmi); if (err < 0) { dev_err(client->dev, "failed to enable HDMI regulator: %d\n", @@ -1549,9 +1532,6 @@ static int tegra_hdmi_exit(struct host1x_client *client) regulator_disable(hdmi->pll); regulator_disable(hdmi->hdmi); - if (IS_ENABLED(CONFIG_DEBUG_FS)) - tegra_hdmi_debugfs_exit(hdmi); - return 0; } -- cgit v1.2.3 From d92e600998d83569cc0955a63c5f036867336343 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 12 Oct 2017 19:07:54 +0200 Subject: drm/tegra: sor: Root debugfs files at the connector Rather create new files within the top-level DRM device's debugfs node, add the SOR specific files to the connector's debugfs node. This avoids the need to come up with subdirectory names and is also more intuitive. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/sor.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 75b21dbaa8f0..f40fc987900c 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -184,7 +184,6 @@ struct tegra_sor { struct drm_info_list *debugfs_files; struct drm_minor *minor; - struct dentry *debugfs; const struct tegra_sor_ops *ops; @@ -1260,14 +1259,10 @@ static const struct drm_info_list debugfs_files[] = { static int tegra_sor_debugfs_init(struct tegra_sor *sor, struct drm_minor *minor) { - const char *name = sor->soc->supports_dp ? "sor1" : "sor"; + struct dentry *root = sor->output.connector.debugfs_entry; unsigned int i; int err; - sor->debugfs = debugfs_create_dir(name, minor->debugfs_root); - if (!sor->debugfs) - return -ENOMEM; - sor->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), GFP_KERNEL); if (!sor->debugfs_files) { @@ -1280,7 +1275,7 @@ static int tegra_sor_debugfs_init(struct tegra_sor *sor, err = drm_debugfs_create_files(sor->debugfs_files, ARRAY_SIZE(debugfs_files), - sor->debugfs, minor); + root, minor); if (err < 0) goto free; @@ -1292,13 +1287,14 @@ free: kfree(sor->debugfs_files); sor->debugfs_files = NULL; remove: - debugfs_remove_recursive(sor->debugfs); - sor->debugfs = NULL; + debugfs_remove_recursive(root); return err; } static void tegra_sor_debugfs_exit(struct tegra_sor *sor) { + struct dentry *root = sor->output.connector.debugfs_entry; + drm_debugfs_remove_files(sor->debugfs_files, ARRAY_SIZE(debugfs_files), sor->minor); sor->minor = NULL; @@ -1306,8 +1302,7 @@ static void tegra_sor_debugfs_exit(struct tegra_sor *sor) kfree(sor->debugfs_files); sor->debugfs_files = NULL; - debugfs_remove_recursive(sor->debugfs); - sor->debugfs = NULL; + debugfs_remove_recursive(root); } static void tegra_sor_connector_reset(struct drm_connector *connector) -- cgit v1.2.3 From 5b8e043b6df4caf4aa41921ed584659a0c5ad269 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 8 Nov 2017 13:20:01 +0100 Subject: drm/tegra: sor: Register debugfs in ->late_register() The ->late_register() and ->early_unregister() callbacks are called at the right time to make sure userspace only accesses interfaces when it should. Move debugfs registration and unregistration to these callback functions to avoid potential races with userspace. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/sor.c | 53 ++++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index f40fc987900c..2fba6c2bd486 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -183,7 +183,6 @@ struct tegra_sor { struct drm_dp_aux *aux; struct drm_info_list *debugfs_files; - struct drm_minor *minor; const struct tegra_sor_ops *ops; @@ -1256,53 +1255,46 @@ static const struct drm_info_list debugfs_files[] = { { "regs", tegra_sor_show_regs, 0, NULL }, }; -static int tegra_sor_debugfs_init(struct tegra_sor *sor, - struct drm_minor *minor) +static int tegra_sor_late_register(struct drm_connector *connector) { - struct dentry *root = sor->output.connector.debugfs_entry; - unsigned int i; + struct tegra_output *output = connector_to_output(connector); + unsigned int i, count = ARRAY_SIZE(debugfs_files); + struct drm_minor *minor = connector->dev->primary; + struct dentry *root = connector->debugfs_entry; + struct tegra_sor *sor = to_sor(output); int err; sor->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), GFP_KERNEL); - if (!sor->debugfs_files) { - err = -ENOMEM; - goto remove; - } + if (!sor->debugfs_files) + return -ENOMEM; - for (i = 0; i < ARRAY_SIZE(debugfs_files); i++) + for (i = 0; i < count; i++) sor->debugfs_files[i].data = sor; - err = drm_debugfs_create_files(sor->debugfs_files, - ARRAY_SIZE(debugfs_files), - root, minor); + err = drm_debugfs_create_files(sor->debugfs_files, count, root, minor); if (err < 0) goto free; - sor->minor = minor; - return 0; free: kfree(sor->debugfs_files); sor->debugfs_files = NULL; -remove: - debugfs_remove_recursive(root); + return err; } -static void tegra_sor_debugfs_exit(struct tegra_sor *sor) +static void tegra_sor_early_unregister(struct drm_connector *connector) { - struct dentry *root = sor->output.connector.debugfs_entry; - - drm_debugfs_remove_files(sor->debugfs_files, ARRAY_SIZE(debugfs_files), - sor->minor); - sor->minor = NULL; + struct tegra_output *output = connector_to_output(connector); + unsigned int count = ARRAY_SIZE(debugfs_files); + struct tegra_sor *sor = to_sor(output); + drm_debugfs_remove_files(sor->debugfs_files, count, + connector->dev->primary); kfree(sor->debugfs_files); sor->debugfs_files = NULL; - - debugfs_remove_recursive(root); } static void tegra_sor_connector_reset(struct drm_connector *connector) @@ -1355,6 +1347,8 @@ static const struct drm_connector_funcs tegra_sor_connector_funcs = { .destroy = tegra_output_connector_destroy, .atomic_duplicate_state = tegra_sor_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .late_register = tegra_sor_late_register, + .early_unregister = tegra_sor_early_unregister, }; static int tegra_sor_connector_get_modes(struct drm_connector *connector) @@ -2347,12 +2341,6 @@ static int tegra_sor_init(struct host1x_client *client) sor->output.encoder.possible_crtcs = 0x3; - if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_sor_debugfs_init(sor, drm->primary); - if (err < 0) - dev_err(sor->dev, "debugfs setup failed: %d\n", err); - } - if (sor->aux) { err = drm_dp_aux_attach(sor->aux, &sor->output); if (err < 0) { @@ -2421,9 +2409,6 @@ static int tegra_sor_exit(struct host1x_client *client) clk_disable_unprepare(sor->clk_dp); clk_disable_unprepare(sor->clk); - if (IS_ENABLED(CONFIG_DEBUG_FS)) - tegra_sor_debugfs_exit(sor); - return 0; } -- cgit v1.2.3 From 67485fb8b89389f4475af0596d33bd5c11896522 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 9 Nov 2017 13:17:11 +0100 Subject: drm/tegra: Do not wrap lines unnecessarily The tegra_drm_alloc() function signature fits on a single line, no need to wrap it. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/drm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 52552b9b89ef..de1d122ba820 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -1148,8 +1148,7 @@ int tegra_drm_unregister_client(struct tegra_drm *tegra, return 0; } -void *tegra_drm_alloc(struct tegra_drm *tegra, size_t size, - dma_addr_t *dma) +void *tegra_drm_alloc(struct tegra_drm *tegra, size_t size, dma_addr_t *dma) { struct iova *alloc; void *virt; -- cgit v1.2.3 From 9b49f674eb205f9a01638ffc4b6add49741955ad Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 8 Nov 2017 13:22:17 +0100 Subject: drm/tegra: vic: Properly align arguments Properly align function arguments on subsequent lines with the first argument on the first line. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/vic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tegra/vic.c b/drivers/gpu/drm/tegra/vic.c index 18024183aa2b..d9664a34fb43 100644 --- a/drivers/gpu/drm/tegra/vic.c +++ b/drivers/gpu/drm/tegra/vic.c @@ -115,7 +115,7 @@ static int vic_boot(struct vic *vic) } static void *vic_falcon_alloc(struct falcon *falcon, size_t size, - dma_addr_t *iova) + dma_addr_t *iova) { struct tegra_drm *tegra = falcon->data; -- cgit v1.2.3 From 7116e9a8022cd468a6a952e1d0d662da73f75ce5 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 13 Nov 2017 11:20:48 +0100 Subject: drm/tegra: dc: Support background color Starting with Tegra124, the interface to set the background color (the value generated for pixels that are not sourced from any window) is via a different register. Earlier generations called this the border color. Reverse the feature flag and assume that IP revisions that don't have support for background color will support border color instead. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 14 ++++++++------ drivers/gpu/drm/tegra/dc.h | 8 +++++++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 29777604df39..6cee248601a9 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1572,7 +1572,9 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc, WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; tegra_dc_writel(dc, value, DC_CMD_INT_MASK); - if (dc->soc->supports_border_color) + if (dc->soc->supports_background_color) + tegra_dc_writel(dc, 0, DC_DISP_BLEND_BACKGROUND_COLOR); + else tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR); /* apply PLL and pixel clock changes */ @@ -1835,7 +1837,7 @@ static const struct host1x_client_ops dc_client_ops = { }; static const struct tegra_dc_soc_info tegra20_dc_soc_info = { - .supports_border_color = true, + .supports_background_color = false, .supports_interlacing = false, .supports_cursor = false, .supports_block_linear = false, @@ -1845,7 +1847,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = { }; static const struct tegra_dc_soc_info tegra30_dc_soc_info = { - .supports_border_color = true, + .supports_background_color = false, .supports_interlacing = false, .supports_cursor = false, .supports_block_linear = false, @@ -1855,7 +1857,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = { }; static const struct tegra_dc_soc_info tegra114_dc_soc_info = { - .supports_border_color = true, + .supports_background_color = false, .supports_interlacing = false, .supports_cursor = false, .supports_block_linear = false, @@ -1865,7 +1867,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = { }; static const struct tegra_dc_soc_info tegra124_dc_soc_info = { - .supports_border_color = false, + .supports_background_color = true, .supports_interlacing = true, .supports_cursor = true, .supports_block_linear = true, @@ -1875,7 +1877,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = { }; static const struct tegra_dc_soc_info tegra210_dc_soc_info = { - .supports_border_color = false, + .supports_background_color = true, .supports_interlacing = true, .supports_cursor = true, .supports_block_linear = true, diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 032c734dd470..571a6c78619f 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -26,7 +26,7 @@ struct tegra_dc_stats { }; struct tegra_dc_soc_info { - bool supports_border_color; + bool supports_background_color; bool supports_interlacing; bool supports_cursor; bool supports_block_linear; @@ -447,6 +447,12 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define DC_DISP_SD_HW_K_VALUES 0x4dd #define DC_DISP_SD_MAN_K_VALUES 0x4de +#define DC_DISP_BLEND_BACKGROUND_COLOR 0x4e4 +#define BACKGROUND_COLOR_ALPHA(x) (((x) & 0xff) << 24) +#define BACKGROUND_COLOR_BLUE(x) (((x) & 0xff) << 16) +#define BACKGROUND_COLOR_GREEN(x) (((x) & 0xff) << 8) +#define BACKGROUND_COLOR_RED(x) (((x) & 0xff) << 0) + #define DC_DISP_INTERLACE_CONTROL 0x4e5 #define INTERLACE_STATUS (1 << 2) #define INTERLACE_START (1 << 1) -- cgit v1.2.3 From 31b02caea3b30b044fe39301aad34886f7fd2556 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 12 Oct 2017 17:40:46 +0200 Subject: drm/tegra: Use atomic commit helpers There's no reason not to use them, and they already get all the semantics right, so rip out all of the custom code and replace it by the helpers. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 27 ++++++++----- drivers/gpu/drm/tegra/dc.h | 2 + drivers/gpu/drm/tegra/drm.c | 99 +++++---------------------------------------- drivers/gpu/drm/tegra/drm.h | 6 --- 4 files changed, 27 insertions(+), 107 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 6cee248601a9..b6aa669a7cd8 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1648,8 +1648,8 @@ static void tegra_dc_finish_page_flip(struct tegra_dc *dc) { struct drm_device *drm = dc->base.dev; struct drm_crtc *crtc = &dc->base; - unsigned long flags, base; - struct tegra_bo *bo; + u64 base = 0, phys = 0; + unsigned long flags; spin_lock_irqsave(&drm->event_lock, flags); @@ -1658,19 +1658,24 @@ static void tegra_dc_finish_page_flip(struct tegra_dc *dc) return; } - bo = tegra_fb_get_plane(crtc->primary->fb, 0); + if (crtc->primary->fb) { + struct tegra_bo *bo = tegra_fb_get_plane(crtc->primary->fb, 0); - spin_lock(&dc->lock); + phys = bo->paddr + crtc->primary->fb->offsets[0]; - /* check if new start address has been latched */ - tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); - tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); - base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR); - tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); + spin_lock(&dc->lock); - spin_unlock(&dc->lock); + /* check if new start address has been latched */ + tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); + tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); + base = (u64)tegra_dc_readl(dc, DC_WINBUF_START_ADDR_HI) << 32; + base |= tegra_dc_readl(dc, DC_WINBUF_START_ADDR); + tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); + + spin_unlock(&dc->lock); + } - if (base == bo->paddr + crtc->primary->fb->offsets[0]) { + if (base == phys) { drm_crtc_send_vblank_event(crtc, dc->event); drm_crtc_vblank_put(crtc); dc->event = NULL; diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 571a6c78619f..581818064b4f 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -579,6 +579,8 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define DC_WINBUF_SURFACE_KIND_BLOCK (2 << 0) #define DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(x) (((x) & 0x7) << 4) +#define DC_WINBUF_START_ADDR_HI 0x80d + #define DC_WINBUF_AD_UFLOW_STATUS 0xbca #define DC_WINBUF_BD_UFLOW_STATUS 0xdca #define DC_WINBUF_CD_UFLOW_STATUS 0xfca diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index de1d122ba820..90d876fc6ea7 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -33,97 +33,18 @@ struct tegra_drm_file { struct mutex lock; }; -static void tegra_atomic_schedule(struct tegra_drm *tegra, - struct drm_atomic_state *state) -{ - tegra->commit.state = state; - schedule_work(&tegra->commit.work); -} - -static void tegra_atomic_complete(struct tegra_drm *tegra, - struct drm_atomic_state *state) -{ - struct drm_device *drm = tegra->drm; - - /* - * Everything below can be run asynchronously without the need to grab - * any modeset locks at all under one condition: It must be guaranteed - * that the asynchronous work has either been cancelled (if the driver - * supports it, which at least requires that the framebuffers get - * cleaned up with drm_atomic_helper_cleanup_planes()) or completed - * before the new state gets committed on the software side with - * drm_atomic_helper_swap_state(). - * - * This scheme allows new atomic state updates to be prepared and - * checked in parallel to the asynchronous completion of the previous - * update. Which is important since compositors need to figure out the - * composition of the next frame right after having submitted the - * current layout. - */ - - drm_atomic_helper_commit_modeset_disables(drm, state); - drm_atomic_helper_commit_modeset_enables(drm, state); - drm_atomic_helper_commit_planes(drm, state, - DRM_PLANE_COMMIT_ACTIVE_ONLY); - - drm_atomic_helper_wait_for_vblanks(drm, state); - - drm_atomic_helper_cleanup_planes(drm, state); - drm_atomic_state_put(state); -} - -static void tegra_atomic_work(struct work_struct *work) -{ - struct tegra_drm *tegra = container_of(work, struct tegra_drm, - commit.work); - - tegra_atomic_complete(tegra, tegra->commit.state); -} - -static int tegra_atomic_commit(struct drm_device *drm, - struct drm_atomic_state *state, bool nonblock) -{ - struct tegra_drm *tegra = drm->dev_private; - int err; - - err = drm_atomic_helper_prepare_planes(drm, state); - if (err) - return err; - - /* serialize outstanding nonblocking commits */ - mutex_lock(&tegra->commit.lock); - flush_work(&tegra->commit.work); - - /* - * This is the point of no return - everything below never fails except - * when the hw goes bonghits. Which means we can commit the new state on - * the software side now. - */ - - err = drm_atomic_helper_swap_state(state, true); - if (err) { - mutex_unlock(&tegra->commit.lock); - drm_atomic_helper_cleanup_planes(drm, state); - return err; - } - - drm_atomic_state_get(state); - if (nonblock) - tegra_atomic_schedule(tegra, state); - else - tegra_atomic_complete(tegra, state); - - mutex_unlock(&tegra->commit.lock); - return 0; -} - -static const struct drm_mode_config_funcs tegra_drm_mode_funcs = { +static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = { .fb_create = tegra_fb_create, #ifdef CONFIG_DRM_FBDEV_EMULATION .output_poll_changed = tegra_fb_output_poll_changed, #endif .atomic_check = drm_atomic_helper_check, - .atomic_commit = tegra_atomic_commit, + .atomic_commit = drm_atomic_helper_commit, +}; + +static const struct drm_mode_config_helper_funcs +tegra_drm_mode_config_helpers = { + .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, }; static int tegra_drm_load(struct drm_device *drm, unsigned long flags) @@ -172,9 +93,6 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) mutex_init(&tegra->clients_lock); INIT_LIST_HEAD(&tegra->clients); - mutex_init(&tegra->commit.lock); - INIT_WORK(&tegra->commit.work, tegra_atomic_work); - drm->dev_private = tegra; tegra->drm = drm; @@ -188,7 +106,8 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) drm->mode_config.allow_fb_modifiers = true; - drm->mode_config.funcs = &tegra_drm_mode_funcs; + drm->mode_config.funcs = &tegra_drm_mode_config_funcs; + drm->mode_config.helper_private = &tegra_drm_mode_config_helpers; err = tegra_drm_fb_prepare(drm); if (err < 0) diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index ddae331ad8b6..a3d84c4af357 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -62,12 +62,6 @@ struct tegra_drm { unsigned int pitch_align; - struct { - struct drm_atomic_state *state; - struct work_struct work; - struct mutex lock; - } commit; - struct drm_atomic_state *state; }; -- cgit v1.2.3 From 9d99ab6e5fe27980a2a7cdd1957993099727530f Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 12 Oct 2017 17:40:46 +0200 Subject: drm/tegra: Remove custom page-flip handler Tegra display hardware has GO bits and meets all the requirements to use drm_crtc_arm_vblank_event(). Use it instead and get rid of the hand- rolled implementation. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 62 +++++++++++++--------------------------------- drivers/gpu/drm/tegra/dc.h | 3 --- 2 files changed, 17 insertions(+), 48 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index b6aa669a7cd8..05442d78e13d 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1523,6 +1523,15 @@ static void tegra_crtc_atomic_disable(struct drm_crtc *crtc, tegra_dc_stats_reset(&dc->stats); drm_crtc_vblank_off(crtc); + spin_lock_irq(&crtc->dev->event_lock); + + if (crtc->state->event) { + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + } + + spin_unlock_irq(&crtc->dev->event_lock); + pm_runtime_put_sync(dc->dev); } @@ -1614,14 +1623,18 @@ static int tegra_crtc_atomic_check(struct drm_crtc *crtc, static void tegra_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { - struct tegra_dc *dc = to_tegra_dc(crtc); + unsigned long flags; if (crtc->state->event) { - crtc->state->event->pipe = drm_crtc_index(crtc); + spin_lock_irqsave(&crtc->dev->event_lock, flags); + + if (drm_crtc_vblank_get(crtc) != 0) + drm_crtc_send_vblank_event(crtc, crtc->state->event); + else + drm_crtc_arm_vblank_event(crtc, crtc->state->event); - WARN_ON(drm_crtc_vblank_get(crtc) != 0); + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); - dc->event = crtc->state->event; crtc->state->event = NULL; } } @@ -1644,46 +1657,6 @@ static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { .atomic_disable = tegra_crtc_atomic_disable, }; -static void tegra_dc_finish_page_flip(struct tegra_dc *dc) -{ - struct drm_device *drm = dc->base.dev; - struct drm_crtc *crtc = &dc->base; - u64 base = 0, phys = 0; - unsigned long flags; - - spin_lock_irqsave(&drm->event_lock, flags); - - if (!dc->event) { - spin_unlock_irqrestore(&drm->event_lock, flags); - return; - } - - if (crtc->primary->fb) { - struct tegra_bo *bo = tegra_fb_get_plane(crtc->primary->fb, 0); - - phys = bo->paddr + crtc->primary->fb->offsets[0]; - - spin_lock(&dc->lock); - - /* check if new start address has been latched */ - tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); - tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); - base = (u64)tegra_dc_readl(dc, DC_WINBUF_START_ADDR_HI) << 32; - base |= tegra_dc_readl(dc, DC_WINBUF_START_ADDR); - tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); - - spin_unlock(&dc->lock); - } - - if (base == phys) { - drm_crtc_send_vblank_event(crtc, dc->event); - drm_crtc_vblank_put(crtc); - dc->event = NULL; - } - - spin_unlock_irqrestore(&drm->event_lock, flags); -} - static irqreturn_t tegra_dc_irq(int irq, void *data) { struct tegra_dc *dc = data; @@ -1704,7 +1677,6 @@ static irqreturn_t tegra_dc_irq(int irq, void *data) dev_dbg(dc->dev, "%s(): vertical blank\n", __func__); */ drm_crtc_handle_vblank(&dc->base); - tegra_dc_finish_page_flip(dc); dc->stats.vblank++; } diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 581818064b4f..6e46e4aed10b 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -57,9 +57,6 @@ struct tegra_dc { struct drm_info_list *debugfs_files; - /* page-flip handling */ - struct drm_pending_vblank_event *event; - const struct tegra_dc_soc_info *soc; struct iommu_domain *domain; -- cgit v1.2.3 From 6f70ec54e1c11b916c16ec3caa71e4ed2c248aa3 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 30 Aug 2017 18:02:51 +0200 Subject: drm/tegra: dc: Remove tegra_primary_plane_destroy() This function is a simple wrapper around tegra_plane_destroy(), so it can be dropped. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 05442d78e13d..bcdcba78491b 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -417,11 +417,6 @@ static const u32 tegra_primary_plane_formats[] = { DRM_FORMAT_RGB565, }; -static void tegra_primary_plane_destroy(struct drm_plane *plane) -{ - tegra_plane_destroy(plane); -} - static void tegra_plane_reset(struct drm_plane *plane) { struct tegra_plane_state *state; @@ -466,7 +461,7 @@ static void tegra_plane_atomic_destroy_state(struct drm_plane *plane, static const struct drm_plane_funcs tegra_primary_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .destroy = tegra_primary_plane_destroy, + .destroy = tegra_plane_destroy, .reset = tegra_plane_reset, .atomic_duplicate_state = tegra_plane_atomic_duplicate_state, .atomic_destroy_state = tegra_plane_atomic_destroy_state, -- cgit v1.2.3 From c1cb4b6171aeaee06a8d70e93ef57f7923f50010 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 30 Aug 2017 18:04:12 +0200 Subject: drm/tegra: dc: Remove duplicate plane funcs Both tegra_primary_plane_funcs and tegra_cursor_plane_funcs are identical. Get rid of the duplicate and use one set of function pointers for all planes. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index bcdcba78491b..a3f2924824dc 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -458,7 +458,7 @@ static void tegra_plane_atomic_destroy_state(struct drm_plane *plane, kfree(state); } -static const struct drm_plane_funcs tegra_primary_plane_funcs = { +static const struct drm_plane_funcs tegra_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = tegra_plane_destroy, @@ -655,7 +655,7 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm, formats = tegra_primary_plane_formats; err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, - &tegra_primary_plane_funcs, formats, + &tegra_plane_funcs, formats, num_formats, NULL, DRM_PLANE_TYPE_PRIMARY, NULL); if (err < 0) { @@ -781,15 +781,6 @@ static void tegra_cursor_atomic_disable(struct drm_plane *plane, tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); } -static const struct drm_plane_funcs tegra_cursor_plane_funcs = { - .update_plane = drm_atomic_helper_update_plane, - .disable_plane = drm_atomic_helper_disable_plane, - .destroy = tegra_plane_destroy, - .reset = tegra_plane_reset, - .atomic_duplicate_state = tegra_plane_atomic_duplicate_state, - .atomic_destroy_state = tegra_plane_atomic_destroy_state, -}; - static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = { .atomic_check = tegra_cursor_atomic_check, .atomic_update = tegra_cursor_atomic_update, @@ -821,7 +812,7 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, formats = tegra_cursor_plane_formats; err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe, - &tegra_cursor_plane_funcs, formats, + &tegra_plane_funcs, formats, num_formats, NULL, DRM_PLANE_TYPE_CURSOR, NULL); if (err < 0) { -- cgit v1.2.3 From b652ab1db477f8556c098adb2e5951f36ce1f0c4 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 30 Aug 2017 18:02:51 +0200 Subject: drm/tegra: dc: Remove tegra_overlay_plane_destroy() This function is a simple wrapper around tegra_plane_destroy(), so it can be dropped. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index a3f2924824dc..e60f44a186bb 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -825,15 +825,10 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, return &plane->base; } -static void tegra_overlay_plane_destroy(struct drm_plane *plane) -{ - tegra_plane_destroy(plane); -} - static const struct drm_plane_funcs tegra_overlay_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .destroy = tegra_overlay_plane_destroy, + .destroy = tegra_plane_destroy, .reset = tegra_plane_reset, .atomic_duplicate_state = tegra_plane_atomic_duplicate_state, .atomic_destroy_state = tegra_plane_atomic_destroy_state, -- cgit v1.2.3 From 301e0ddb3458255ed0fb3bb1f46b9caf186eafb5 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 30 Aug 2017 18:04:12 +0200 Subject: drm/tegra: dc: Remove duplicate plane funcs Both tegra_overlay_plane_funcs is identical to tegra_plane_funcs. Get rid of the duplicate and use one set of function pointers for all planes. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index e60f44a186bb..775e51c7a5c0 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -825,15 +825,6 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, return &plane->base; } -static const struct drm_plane_funcs tegra_overlay_plane_funcs = { - .update_plane = drm_atomic_helper_update_plane, - .disable_plane = drm_atomic_helper_disable_plane, - .destroy = tegra_plane_destroy, - .reset = tegra_plane_reset, - .atomic_duplicate_state = tegra_plane_atomic_duplicate_state, - .atomic_destroy_state = tegra_plane_atomic_destroy_state, -}; - static const uint32_t tegra_overlay_plane_formats[] = { DRM_FORMAT_XBGR8888, DRM_FORMAT_XRGB8888, @@ -863,7 +854,7 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, formats = tegra_overlay_plane_formats; err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe, - &tegra_overlay_plane_funcs, formats, + &tegra_plane_funcs, formats, num_formats, NULL, DRM_PLANE_TYPE_OVERLAY, NULL); if (err < 0) { -- cgit v1.2.3 From b1415ff21d43e264b32c18faf1c7ae7e53bc235e Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 10 Nov 2017 16:00:05 +0100 Subject: drm/tegra: dc: Move state definition to header Move the display controller state definition to the header file so that it can be referenced by other files. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 18 ------------------ drivers/gpu/drm/tegra/dc.h | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 775e51c7a5c0..7b017c03feb3 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -34,24 +34,6 @@ static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane) return container_of(plane, struct tegra_plane, base); } -struct tegra_dc_state { - struct drm_crtc_state base; - - struct clk *clk; - unsigned long pclk; - unsigned int div; - - u32 planes; -}; - -static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state) -{ - if (state) - return container_of(state, struct tegra_dc_state, base); - - return NULL; -} - struct tegra_plane_state { struct drm_plane_state base; diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 6e46e4aed10b..b65dfbb0af89 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -18,6 +18,24 @@ struct tegra_output; +struct tegra_dc_state { + struct drm_crtc_state base; + + struct clk *clk; + unsigned long pclk; + unsigned int div; + + u32 planes; +}; + +static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state) +{ + if (state) + return container_of(state, struct tegra_dc_state, base); + + return NULL; +} + struct tegra_dc_stats { unsigned long frames; unsigned long vblank; -- cgit v1.2.3 From 5acd351427361131c583dfb11c7bf4c364c98a9b Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 10 Nov 2017 15:27:25 +0100 Subject: drm/tegra: Move common plane code to separate file Subsequent patches will add support for Tegra186 which has a different architecture and needs different plane code but which can share a lot of code with earlier Tegra support. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/Makefile | 1 + drivers/gpu/drm/tegra/dc.c | 199 ++--------------------------------------- drivers/gpu/drm/tegra/plane.c | 180 +++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/tegra/plane.h | 59 ++++++++++++ 4 files changed, 245 insertions(+), 194 deletions(-) create mode 100644 drivers/gpu/drm/tegra/plane.c create mode 100644 drivers/gpu/drm/tegra/plane.h diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile index 46d65d39214d..a47784765217 100644 --- a/drivers/gpu/drm/tegra/Makefile +++ b/drivers/gpu/drm/tegra/Makefile @@ -5,6 +5,7 @@ tegra-drm-y := \ drm.o \ gem.o \ fb.o \ + plane.o \ dc.o \ output.o \ rgb.o \ diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 7b017c03feb3..3aa0da6244db 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -19,38 +19,12 @@ #include "dc.h" #include "drm.h" #include "gem.h" +#include "plane.h" #include #include #include -struct tegra_plane { - struct drm_plane base; - unsigned int index; -}; - -static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane) -{ - return container_of(plane, struct tegra_plane, base); -} - -struct tegra_plane_state { - struct drm_plane_state base; - - struct tegra_bo_tiling tiling; - u32 format; - u32 swap; -}; - -static inline struct tegra_plane_state * -to_tegra_plane_state(struct drm_plane_state *state) -{ - if (state) - return container_of(state, struct tegra_plane_state, base); - - return NULL; -} - static void tegra_dc_stats_reset(struct tegra_dc_stats *stats) { stats->frames = 0; @@ -97,81 +71,6 @@ void tegra_dc_commit(struct tegra_dc *dc) tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); } -static int tegra_dc_format(u32 fourcc, u32 *format, u32 *swap) -{ - /* assume no swapping of fetched data */ - if (swap) - *swap = BYTE_SWAP_NOSWAP; - - switch (fourcc) { - case DRM_FORMAT_XBGR8888: - *format = WIN_COLOR_DEPTH_R8G8B8A8; - break; - - case DRM_FORMAT_XRGB8888: - *format = WIN_COLOR_DEPTH_B8G8R8A8; - break; - - case DRM_FORMAT_RGB565: - *format = WIN_COLOR_DEPTH_B5G6R5; - break; - - case DRM_FORMAT_UYVY: - *format = WIN_COLOR_DEPTH_YCbCr422; - break; - - case DRM_FORMAT_YUYV: - if (swap) - *swap = BYTE_SWAP_SWAP2; - - *format = WIN_COLOR_DEPTH_YCbCr422; - break; - - case DRM_FORMAT_YUV420: - *format = WIN_COLOR_DEPTH_YCbCr420P; - break; - - case DRM_FORMAT_YUV422: - *format = WIN_COLOR_DEPTH_YCbCr422P; - break; - - default: - return -EINVAL; - } - - return 0; -} - -static bool tegra_dc_format_is_yuv(unsigned int format, bool *planar) -{ - switch (format) { - case WIN_COLOR_DEPTH_YCbCr422: - case WIN_COLOR_DEPTH_YUV422: - if (planar) - *planar = false; - - return true; - - case WIN_COLOR_DEPTH_YCbCr420P: - case WIN_COLOR_DEPTH_YUV420P: - case WIN_COLOR_DEPTH_YCbCr422P: - case WIN_COLOR_DEPTH_YUV422P: - case WIN_COLOR_DEPTH_YCbCr422R: - case WIN_COLOR_DEPTH_YUV422R: - case WIN_COLOR_DEPTH_YCbCr422RA: - case WIN_COLOR_DEPTH_YUV422RA: - if (planar) - *planar = true; - - return true; - } - - if (planar) - *planar = false; - - return false; -} - static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v, unsigned int bpp) { @@ -223,7 +122,7 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, * For YUV planar modes, the number of bytes per pixel takes into * account only the luma component and therefore is 1. */ - yuv = tegra_dc_format_is_yuv(window->format, &planar); + yuv = tegra_plane_format_is_yuv(window->format, &planar); if (!yuv) bpp = window->bits_per_pixel / 8; else @@ -385,101 +284,12 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, spin_unlock_irqrestore(&dc->lock, flags); } -static void tegra_plane_destroy(struct drm_plane *plane) -{ - struct tegra_plane *p = to_tegra_plane(plane); - - drm_plane_cleanup(plane); - kfree(p); -} - static const u32 tegra_primary_plane_formats[] = { DRM_FORMAT_XBGR8888, DRM_FORMAT_XRGB8888, DRM_FORMAT_RGB565, }; -static void tegra_plane_reset(struct drm_plane *plane) -{ - struct tegra_plane_state *state; - - if (plane->state) - __drm_atomic_helper_plane_destroy_state(plane->state); - - kfree(plane->state); - plane->state = NULL; - - state = kzalloc(sizeof(*state), GFP_KERNEL); - if (state) { - plane->state = &state->base; - plane->state->plane = plane; - } -} - -static struct drm_plane_state *tegra_plane_atomic_duplicate_state(struct drm_plane *plane) -{ - struct tegra_plane_state *state = to_tegra_plane_state(plane->state); - struct tegra_plane_state *copy; - - copy = kmalloc(sizeof(*copy), GFP_KERNEL); - if (!copy) - return NULL; - - __drm_atomic_helper_plane_duplicate_state(plane, ©->base); - copy->tiling = state->tiling; - copy->format = state->format; - copy->swap = state->swap; - - return ©->base; -} - -static void tegra_plane_atomic_destroy_state(struct drm_plane *plane, - struct drm_plane_state *state) -{ - __drm_atomic_helper_plane_destroy_state(state); - kfree(state); -} - -static const struct drm_plane_funcs tegra_plane_funcs = { - .update_plane = drm_atomic_helper_update_plane, - .disable_plane = drm_atomic_helper_disable_plane, - .destroy = tegra_plane_destroy, - .reset = tegra_plane_reset, - .atomic_duplicate_state = tegra_plane_atomic_duplicate_state, - .atomic_destroy_state = tegra_plane_atomic_destroy_state, -}; - -static int tegra_plane_state_add(struct tegra_plane *plane, - struct drm_plane_state *state) -{ - struct drm_crtc_state *crtc_state; - struct tegra_dc_state *tegra; - struct drm_rect clip; - int err; - - /* Propagate errors from allocation or locking failures. */ - crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); - if (IS_ERR(crtc_state)) - return PTR_ERR(crtc_state); - - clip.x1 = 0; - clip.y1 = 0; - clip.x2 = crtc_state->mode.hdisplay; - clip.y2 = crtc_state->mode.vdisplay; - - /* Check plane state for visibility and calculate clipping bounds */ - err = drm_atomic_helper_check_plane_state(state, crtc_state, &clip, - 0, INT_MAX, true, true); - if (err < 0) - return err; - - tegra = to_dc_state(crtc_state); - - tegra->planes |= WIN_A_ACT_REQ << plane->index; - - return 0; -} - static int tegra_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *state) { @@ -493,8 +303,9 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, if (!state->crtc) return 0; - err = tegra_dc_format(state->fb->format->format, &plane_state->format, - &plane_state->swap); + err = tegra_plane_format(state->fb->format->format, + &plane_state->format, + &plane_state->swap); if (err < 0) return err; diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c new file mode 100644 index 000000000000..78b24aa1ac83 --- /dev/null +++ b/drivers/gpu/drm/tegra/plane.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "dc.h" +#include "plane.h" + +static void tegra_plane_destroy(struct drm_plane *plane) +{ + struct tegra_plane *p = to_tegra_plane(plane); + + drm_plane_cleanup(plane); + kfree(p); +} + +static void tegra_plane_reset(struct drm_plane *plane) +{ + struct tegra_plane_state *state; + + if (plane->state) + __drm_atomic_helper_plane_destroy_state(plane->state); + + kfree(plane->state); + plane->state = NULL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (state) { + plane->state = &state->base; + plane->state->plane = plane; + } +} + +static struct drm_plane_state * +tegra_plane_atomic_duplicate_state(struct drm_plane *plane) +{ + struct tegra_plane_state *state = to_tegra_plane_state(plane->state); + struct tegra_plane_state *copy; + + copy = kmalloc(sizeof(*copy), GFP_KERNEL); + if (!copy) + return NULL; + + __drm_atomic_helper_plane_duplicate_state(plane, ©->base); + copy->tiling = state->tiling; + copy->format = state->format; + copy->swap = state->swap; + + return ©->base; +} + +static void tegra_plane_atomic_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + __drm_atomic_helper_plane_destroy_state(state); + kfree(state); +} + +const struct drm_plane_funcs tegra_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = tegra_plane_destroy, + .reset = tegra_plane_reset, + .atomic_duplicate_state = tegra_plane_atomic_duplicate_state, + .atomic_destroy_state = tegra_plane_atomic_destroy_state, +}; + +int tegra_plane_state_add(struct tegra_plane *plane, + struct drm_plane_state *state) +{ + struct drm_crtc_state *crtc_state; + struct tegra_dc_state *tegra; + struct drm_rect clip; + int err; + + /* Propagate errors from allocation or locking failures. */ + crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + clip.x1 = 0; + clip.y1 = 0; + clip.x2 = crtc_state->mode.hdisplay; + clip.y2 = crtc_state->mode.vdisplay; + + /* Check plane state for visibility and calculate clipping bounds */ + err = drm_atomic_helper_check_plane_state(state, crtc_state, &clip, + 0, INT_MAX, true, true); + if (err < 0) + return err; + + tegra = to_dc_state(crtc_state); + + tegra->planes |= WIN_A_ACT_REQ << plane->index; + + return 0; +} + +int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap) +{ + /* assume no swapping of fetched data */ + if (swap) + *swap = BYTE_SWAP_NOSWAP; + + switch (fourcc) { + case DRM_FORMAT_XBGR8888: + *format = WIN_COLOR_DEPTH_R8G8B8A8; + break; + + case DRM_FORMAT_XRGB8888: + *format = WIN_COLOR_DEPTH_B8G8R8A8; + break; + + case DRM_FORMAT_RGB565: + *format = WIN_COLOR_DEPTH_B5G6R5; + break; + + case DRM_FORMAT_UYVY: + *format = WIN_COLOR_DEPTH_YCbCr422; + break; + + case DRM_FORMAT_YUYV: + if (!swap) + return -EINVAL; + + *format = WIN_COLOR_DEPTH_YCbCr422; + *swap = BYTE_SWAP_SWAP2; + break; + + case DRM_FORMAT_YUV420: + *format = WIN_COLOR_DEPTH_YCbCr420P; + break; + + case DRM_FORMAT_YUV422: + *format = WIN_COLOR_DEPTH_YCbCr422P; + break; + + default: + return -EINVAL; + } + + return 0; +} + +bool tegra_plane_format_is_yuv(unsigned int format, bool *planar) +{ + switch (format) { + case WIN_COLOR_DEPTH_YCbCr422: + case WIN_COLOR_DEPTH_YUV422: + if (planar) + *planar = false; + + return true; + + case WIN_COLOR_DEPTH_YCbCr420P: + case WIN_COLOR_DEPTH_YUV420P: + case WIN_COLOR_DEPTH_YCbCr422P: + case WIN_COLOR_DEPTH_YUV422P: + case WIN_COLOR_DEPTH_YCbCr422R: + case WIN_COLOR_DEPTH_YUV422R: + case WIN_COLOR_DEPTH_YCbCr422RA: + case WIN_COLOR_DEPTH_YUV422RA: + if (planar) + *planar = true; + + return true; + } + + if (planar) + *planar = false; + + return false; +} diff --git a/drivers/gpu/drm/tegra/plane.h b/drivers/gpu/drm/tegra/plane.h new file mode 100644 index 000000000000..8237b885acd7 --- /dev/null +++ b/drivers/gpu/drm/tegra/plane.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef TEGRA_PLANE_H +#define TEGRA_PLANE_H 1 + +#include + +struct tegra_bo; + +struct tegra_plane { + struct drm_plane base; + unsigned int index; +}; + +struct tegra_cursor { + struct tegra_plane base; + + struct tegra_bo *bo; + unsigned int width; + unsigned int height; +}; + +static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane) +{ + return container_of(plane, struct tegra_plane, base); +} + +struct tegra_plane_state { + struct drm_plane_state base; + + struct tegra_bo_tiling tiling; + u32 format; + u32 swap; +}; + +static inline struct tegra_plane_state * +to_tegra_plane_state(struct drm_plane_state *state) +{ + if (state) + return container_of(state, struct tegra_plane_state, base); + + return NULL; +} + +extern const struct drm_plane_funcs tegra_plane_funcs; + +int tegra_plane_state_add(struct tegra_plane *plane, + struct drm_plane_state *state); + +int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap); +bool tegra_plane_format_is_yuv(unsigned int format, bool *planar); + +#endif /* TEGRA_PLANE_H */ -- cgit v1.2.3 From c4755fb9064f64083fe559e92a46df817fc5e07b Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 13 Nov 2017 11:08:13 +0100 Subject: drm/tegra: Add Tegra186 display hub support The display architecture has changed in several significant ways with the new Tegra186 SoC. Shared between all display controllers is a set of common resources referred to as the display hub. The hub generates accesses to memory and feeds them into various composition pipelines, each of which being a window that can be assigned to arbitrary heads. Atomic state is subclassed in order to track the global bandwidth requirements and select and adjust the hub clocks appropriately. The plane code is shared to a large degree with earlier SoC generations, except where the programming differs. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/Makefile | 1 + drivers/gpu/drm/tegra/dc.c | 31 ++ drivers/gpu/drm/tegra/dc.h | 114 ++++++ drivers/gpu/drm/tegra/drm.c | 67 +++- drivers/gpu/drm/tegra/drm.h | 19 + drivers/gpu/drm/tegra/hub.c | 790 +++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/tegra/hub.h | 82 +++++ drivers/gpu/drm/tegra/plane.h | 2 + 8 files changed, 1103 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/drm/tegra/hub.c create mode 100644 drivers/gpu/drm/tegra/hub.h diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile index a47784765217..2e0d6213f6bc 100644 --- a/drivers/gpu/drm/tegra/Makefile +++ b/drivers/gpu/drm/tegra/Makefile @@ -5,6 +5,7 @@ tegra-drm-y := \ drm.o \ gem.o \ fb.o \ + hub.o \ plane.o \ dc.o \ output.o \ diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 3aa0da6244db..25e998f8f339 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -447,6 +447,15 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm, num_formats = ARRAY_SIZE(tegra_primary_plane_formats); formats = tegra_primary_plane_formats; + /* + * XXX compute offset so that we can directly access windows. + * + * Always use window A as primary window. + */ + plane->offset = 0; + plane->index = 0; + plane->depth = 255; + err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, &tegra_plane_funcs, formats, num_formats, NULL, @@ -641,7 +650,10 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, if (!plane) return ERR_PTR(-ENOMEM); + /* XXX compute offset so that we can directly access windows */ + plane->offset = 0; plane->index = index; + plane->depth = 0; num_formats = ARRAY_SIZE(tegra_overlay_plane_formats); formats = tegra_overlay_plane_formats; @@ -1382,6 +1394,25 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc, static int tegra_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) { + struct tegra_atomic_state *s = to_tegra_atomic_state(state->state); + struct tegra_dc_state *tegra = to_dc_state(state); + + /* + * The display hub display clock needs to be fed by the display clock + * with the highest frequency to ensure proper functioning of all the + * displays. + * + * Note that this isn't used before Tegra186, but it doesn't hurt and + * conditionalizing it would make the code less clean. + */ + if (state->active) { + if (!s->clk_disp || tegra->pclk > s->rate) { + s->dc = to_tegra_dc(crtc); + s->clk_disp = s->dc->clk; + s->rate = tegra->pclk; + } + } + return 0; } diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index b65dfbb0af89..22c5091006bc 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -209,6 +209,8 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define WIN_B_UPDATE (1 << 10) #define WIN_C_UPDATE (1 << 11) #define CURSOR_UPDATE (1 << 15) +#define COMMON_ACTREQ (1 << 16) +#define COMMON_UPDATE (1 << 17) #define NC_HOST_TRIG (1 << 24) #define DC_CMD_DISPLAY_WINDOW_HEADER 0x042 @@ -486,6 +488,35 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define CURSOR_SRC_BLEND_MASK (3 << 8) #define CURSOR_ALPHA 0xff +#define DC_WIN_CORE_ACT_CONTROL 0x50e +#define VCOUNTER (0 << 0) +#define HCOUNTER (1 << 0) + +#define DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA 0x543 +#define LATENCY_CTL_MODE_ENABLE (1 << 2) + +#define DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB 0x544 +#define WATERMARK_MASK 0x1fffffff + +#define DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER 0x560 +#define PIPE_METER_INT(x) (((x) & 0xff) << 8) +#define PIPE_METER_FRAC(x) (((x) & 0xff) << 0) + +#define DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG 0x561 +#define MEMPOOL_ENTRIES(x) (((x) & 0xffff) << 0) + +#define DC_WIN_CORE_IHUB_WGRP_FETCH_METER 0x562 +#define SLOTS(x) (((x) & 0xff) << 0) + +#define DC_WIN_CORE_IHUB_LINEBUF_CONFIG 0x563 +#define MODE_TWO_LINES (0 << 14) +#define MODE_FOUR_LINES (1 << 14) + +#define DC_WIN_CORE_IHUB_THREAD_GROUP 0x568 +#define THREAD_NUM_MASK (0x1f << 1) +#define THREAD_NUM(x) (((x) & 0x1f) << 1) +#define THREAD_GROUP_ENABLE (1 << 0) + #define DC_WIN_CSC_YOF 0x611 #define DC_WIN_CSC_KYRGB 0x612 #define DC_WIN_CSC_KUR 0x613 @@ -596,8 +627,91 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define DC_WINBUF_START_ADDR_HI 0x80d +#define DC_WINBUF_CDE_CONTROL 0x82f +#define ENABLE_SURFACE (1 << 0) + #define DC_WINBUF_AD_UFLOW_STATUS 0xbca #define DC_WINBUF_BD_UFLOW_STATUS 0xdca #define DC_WINBUF_CD_UFLOW_STATUS 0xfca +/* Tegra186 and later */ +#define DC_WIN_CORE_WINDOWGROUP_SET_CONTROL 0x702 +#define OWNER_MASK (0xf << 0) +#define OWNER(x) (((x) & 0xf) << 0) + +#define DC_WIN_CROPPED_SIZE 0x706 + +#define DC_WIN_PLANAR_STORAGE 0x709 +#define PITCH(x) (((x) >> 6) & 0x1fff) + +#define DC_WIN_SET_PARAMS 0x70d +#define CLAMP_BEFORE_BLEND (1 << 15) +#define DEGAMMA_NONE (0 << 13) +#define DEGAMMA_SRGB (1 << 13) +#define DEGAMMA_YUV8_10 (2 << 13) +#define DEGAMMA_YUV12 (3 << 13) +#define INPUT_RANGE_BYPASS (0 << 10) +#define INPUT_RANGE_LIMITED (1 << 10) +#define INPUT_RANGE_FULL (2 << 10) +#define COLOR_SPACE_RGB (0 << 8) +#define COLOR_SPACE_YUV_601 (1 << 8) +#define COLOR_SPACE_YUV_709 (2 << 8) +#define COLOR_SPACE_YUV_2020 (3 << 8) + +#define DC_WIN_WINDOWGROUP_SET_CONTROL_INPUT_SCALER 0x70e +#define HORIZONTAL_TAPS_2 (1 << 3) +#define HORIZONTAL_TAPS_5 (4 << 3) +#define VERTICAL_TAPS_2 (1 << 0) +#define VERTICAL_TAPS_5 (4 << 0) + +#define DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_USAGE 0x711 +#define INPUT_SCALER_USE422 (1 << 2) +#define INPUT_SCALER_VBYPASS (1 << 1) +#define INPUT_SCALER_HBYPASS (1 << 0) + +#define DC_WIN_BLEND_LAYER_CONTROL 0x716 +#define COLOR_KEY_NONE (0 << 25) +#define COLOR_KEY_SRC (1 << 25) +#define COLOR_KEY_DST (2 << 25) +#define BLEND_BYPASS (1 << 24) +#define K2(x) (((x) & 0xff) << 16) +#define K1(x) (((x) & 0xff) << 8) +#define WINDOW_LAYER_DEPTH(x) (((x) & 0xff) << 0) + +#define DC_WIN_BLEND_MATCH_SELECT 0x717 +#define BLEND_FACTOR_DST_ALPHA_ZERO (0 << 12) +#define BLEND_FACTOR_DST_ALPHA_ONE (1 << 12) +#define BLEND_FACTOR_DST_ALPHA_NEG_K1_TIMES_SRC (2 << 12) +#define BLEND_FACTOR_DST_ALPHA_K2 (3 << 12) +#define BLEND_FACTOR_SRC_ALPHA_ZERO (0 << 8) +#define BLEND_FACTOR_SRC_ALPHA_K1 (1 << 8) +#define BLEND_FACTOR_SRC_ALPHA_K2 (2 << 8) +#define BLEND_FACTOR_SRC_ALPHA_NEG_K1_TIMES_DST (3 << 8) +#define BLEND_FACTOR_DST_COLOR_ZERO (0 << 4) +#define BLEND_FACTOR_DST_COLOR_ONE (1 << 4) +#define BLEND_FACTOR_DST_COLOR_K1 (2 << 4) +#define BLEND_FACTOR_DST_COLOR_K2 (3 << 4) +#define BLEND_FACTOR_DST_COLOR_K1_TIMES_DST (4 << 4) +#define BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_DST (5 << 4) +#define BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC (6 << 4) +#define BLEND_FACTOR_DST_COLOR_NEG_K1 (7 << 4) +#define BLEND_FACTOR_SRC_COLOR_ZERO (0 << 0) +#define BLEND_FACTOR_SRC_COLOR_ONE (1 << 0) +#define BLEND_FACTOR_SRC_COLOR_K1 (2 << 0) +#define BLEND_FACTOR_SRC_COLOR_K1_TIMES_DST (3 << 0) +#define BLEND_FACTOR_SRC_COLOR_NEG_K1_TIMES_DST (4 << 0) +#define BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC (5 << 0) + +#define DC_WIN_BLEND_NOMATCH_SELECT 0x718 + +#define DC_WIN_PRECOMP_WGRP_PARAMS 0x724 +#define SWAP_UV (1 << 0) + +#define DC_WIN_WINDOW_SET_CONTROL 0x730 +#define CONTROL_CSC_ENABLE (1 << 5) + +#define DC_WINBUF_CROPPED_POINT 0x806 +#define OFFSET_Y(x) (((x) & 0xffff) << 16) +#define OFFSET_X(x) (((x) & 0xffff) << 0) + #endif /* TEGRA_DC_H */ diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 90d876fc6ea7..98a2494b4ed0 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -33,6 +33,35 @@ struct tegra_drm_file { struct mutex lock; }; +static struct drm_atomic_state * +tegra_atomic_state_alloc(struct drm_device *drm) +{ + struct tegra_atomic_state *state = kzalloc(sizeof(*state), GFP_KERNEL); + + if (!state || drm_atomic_state_init(drm, &state->base) < 0) { + kfree(state); + return NULL; + } + + return &state->base; +} + +static void tegra_atomic_state_clear(struct drm_atomic_state *state) +{ + struct tegra_atomic_state *tegra = to_tegra_atomic_state(state); + + drm_atomic_state_default_clear(state); + tegra->clk_disp = NULL; + tegra->dc = NULL; + tegra->rate = 0; +} + +static void tegra_atomic_state_free(struct drm_atomic_state *state) +{ + drm_atomic_state_default_release(state); + kfree(state); +} + static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = { .fb_create = tegra_fb_create, #ifdef CONFIG_DRM_FBDEV_EMULATION @@ -40,11 +69,32 @@ static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = { #endif .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, + .atomic_state_alloc = tegra_atomic_state_alloc, + .atomic_state_clear = tegra_atomic_state_clear, + .atomic_state_free = tegra_atomic_state_free, }; +static void tegra_atomic_commit_tail(struct drm_atomic_state *old_state) +{ + struct drm_device *drm = old_state->dev; + struct tegra_drm *tegra = drm->dev_private; + + if (tegra->hub) { + drm_atomic_helper_commit_modeset_disables(drm, old_state); + tegra_display_hub_atomic_commit(drm, old_state); + drm_atomic_helper_commit_planes(drm, old_state, 0); + drm_atomic_helper_commit_modeset_enables(drm, old_state); + drm_atomic_helper_commit_hw_done(old_state); + drm_atomic_helper_wait_for_vblanks(drm, old_state); + drm_atomic_helper_cleanup_planes(drm, old_state); + } else { + drm_atomic_helper_commit_tail_rpm(old_state); + } +} + static const struct drm_mode_config_helper_funcs tegra_drm_mode_config_helpers = { - .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, + .atomic_commit_tail = tegra_atomic_commit_tail, }; static int tegra_drm_load(struct drm_device *drm, unsigned long flags) @@ -119,6 +169,12 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) if (err < 0) goto fbdev; + if (tegra->hub) { + err = tegra_display_hub_prepare(tegra->hub); + if (err < 0) + goto device; + } + /* * We don't use the drm_irq_install() helpers provided by the DRM * core, so we need to set this manually in order to allow the @@ -131,16 +187,19 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) err = drm_vblank_init(drm, drm->mode_config.num_crtc); if (err < 0) - goto device; + goto hub; drm_mode_config_reset(drm); err = tegra_drm_fb_init(drm); if (err < 0) - goto device; + goto hub; return 0; +hub: + if (tegra->hub) + tegra_display_hub_cleanup(tegra->hub); device: host1x_device_exit(device); fbdev: @@ -1235,6 +1294,7 @@ static const struct of_device_id host1x_drm_subdevs[] = { { .compatible = "nvidia,tegra210-sor", }, { .compatible = "nvidia,tegra210-sor1", }, { .compatible = "nvidia,tegra210-vic", }, + { .compatible = "nvidia,tegra186-display", }, { .compatible = "nvidia,tegra186-vic", }, { /* sentinel */ } }; @@ -1250,6 +1310,7 @@ static struct host1x_driver host1x_drm_driver = { }; static struct platform_driver * const drivers[] = { + &tegra_display_hub_driver, &tegra_dc_driver, &tegra_hdmi_driver, &tegra_dsi_driver, diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index a3d84c4af357..d4dfd239e2a5 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -23,6 +24,7 @@ #include #include "gem.h" +#include "hub.h" #include "trace.h" struct reset_control; @@ -40,6 +42,20 @@ struct tegra_fbdev { }; #endif +struct tegra_atomic_state { + struct drm_atomic_state base; + + struct clk *clk_disp; + struct tegra_dc *dc; + unsigned long rate; +}; + +static inline struct tegra_atomic_state * +to_tegra_atomic_state(struct drm_atomic_state *state) +{ + return container_of(state, struct tegra_atomic_state, base); +} + struct tegra_drm { struct drm_device *drm; @@ -62,6 +78,8 @@ struct tegra_drm { unsigned int pitch_align; + struct tegra_display_hub *hub; + struct drm_atomic_state *state; }; @@ -187,6 +205,7 @@ void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev); void tegra_fb_output_poll_changed(struct drm_device *drm); #endif +extern struct platform_driver tegra_display_hub_driver; extern struct platform_driver tegra_dc_driver; extern struct platform_driver tegra_hdmi_driver; extern struct platform_driver tegra_dsi_driver; diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c new file mode 100644 index 000000000000..33d008fb8745 --- /dev/null +++ b/drivers/gpu/drm/tegra/hub.c @@ -0,0 +1,790 @@ +/* + * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "drm.h" +#include "dc.h" +#include "plane.h" + +static const u32 tegra_shared_plane_formats[] = { + DRM_FORMAT_XBGR8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB565, +}; + +static inline unsigned int tegra_plane_offset(struct tegra_shared_plane *plane, + unsigned int offset) +{ + struct tegra_plane *p = &plane->base; + + if (offset >= 0x500 && offset <= 0x581) { + offset = 0x000 + (offset - 0x500); + return p->offset + offset; + } + + if (offset >= 0x700 && offset <= 0x73c) { + offset = 0x180 + (offset - 0x700); + return p->offset + offset; + } + + if (offset >= 0x800 && offset <= 0x83e) { + offset = 0x1c0 + (offset - 0x800); + return p->offset + offset; + } + + dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset); + + return p->offset + offset; +} + +static inline u32 tegra_plane_readl(struct tegra_shared_plane *plane, + unsigned int offset) +{ + return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset)); +} + +static inline void tegra_plane_writel(struct tegra_shared_plane *plane, + u32 value, unsigned int offset) +{ + tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset)); +} + +static int tegra_windowgroup_enable(struct tegra_windowgroup *wgrp) +{ + mutex_lock(&wgrp->lock); + + if (wgrp->usecount == 0) { + pm_runtime_get_sync(wgrp->parent); + reset_control_deassert(wgrp->rst); + } + + wgrp->usecount++; + mutex_unlock(&wgrp->lock); + + return 0; +} + +static void tegra_windowgroup_disable(struct tegra_windowgroup *wgrp) +{ + int err; + + mutex_lock(&wgrp->lock); + + if (wgrp->usecount == 1) { + err = reset_control_assert(wgrp->rst); + if (err < 0) { + pr_err("failed to assert reset for window group %u\n", + wgrp->index); + } + + pm_runtime_put(wgrp->parent); + } + + wgrp->usecount--; + mutex_unlock(&wgrp->lock); +} + +int tegra_display_hub_prepare(struct tegra_display_hub *hub) +{ + unsigned int i; + + /* + * XXX Enabling/disabling windowgroups needs to happen when the owner + * display controller is disabled. There's currently no good point at + * which this could be executed, so unconditionally enable all window + * groups for now. + */ + for (i = 0; i < hub->soc->num_wgrps; i++) { + struct tegra_windowgroup *wgrp = &hub->wgrps[i]; + + tegra_windowgroup_enable(wgrp); + } + + return 0; +} + +void tegra_display_hub_cleanup(struct tegra_display_hub *hub) +{ + unsigned int i; + + /* + * XXX Remove this once window groups can be more fine-grainedly + * enabled and disabled. + */ + for (i = 0; i < hub->soc->num_wgrps; i++) { + struct tegra_windowgroup *wgrp = &hub->wgrps[i]; + + tegra_windowgroup_disable(wgrp); + } +} + +static void tegra_shared_plane_update(struct tegra_shared_plane *plane) +{ + struct tegra_dc *dc = plane->dc; + unsigned long timeout; + u32 mask, value; + + mask = COMMON_UPDATE | WIN_A_UPDATE << plane->base.index; + tegra_dc_writel(dc, mask, DC_CMD_STATE_CONTROL); + + timeout = jiffies + msecs_to_jiffies(1000); + + while (time_before(jiffies, timeout)) { + value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); + if ((value & mask) == 0) + break; + + usleep_range(100, 400); + } +} + +static void tegra_shared_plane_activate(struct tegra_shared_plane *plane) +{ + struct tegra_dc *dc = plane->dc; + unsigned long timeout; + u32 mask, value; + + mask = COMMON_ACTREQ | WIN_A_ACT_REQ << plane->base.index; + tegra_dc_writel(dc, mask, DC_CMD_STATE_CONTROL); + + timeout = jiffies + msecs_to_jiffies(1000); + + while (time_before(jiffies, timeout)) { + value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); + if ((value & mask) == 0) + break; + + usleep_range(100, 400); + } +} + +static unsigned int +tegra_shared_plane_get_owner(struct tegra_shared_plane *plane, + struct tegra_dc *dc) +{ + unsigned int offset = + tegra_plane_offset(plane, DC_WIN_CORE_WINDOWGROUP_SET_CONTROL); + + return tegra_dc_readl(dc, offset) & OWNER_MASK; +} + +static bool tegra_dc_owns_shared_plane(struct tegra_dc *dc, + struct tegra_shared_plane *plane) +{ + struct device *dev = dc->dev; + + if (tegra_shared_plane_get_owner(plane, dc) == dc->pipe) { + if (plane->dc == dc) + return true; + + dev_WARN(dev, "head %u owns window %u but is not attached\n", + dc->pipe, plane->base.index); + } + + return false; +} + +static int tegra_shared_plane_set_owner(struct tegra_shared_plane *plane, + struct tegra_dc *new) +{ + unsigned int offset = + tegra_plane_offset(plane, DC_WIN_CORE_WINDOWGROUP_SET_CONTROL); + struct tegra_dc *old = plane->dc, *dc = new ? new : old; + struct device *dev = new ? new->dev : old->dev; + unsigned int owner, index = plane->base.index; + u32 value; + + value = tegra_dc_readl(dc, offset); + owner = value & OWNER_MASK; + + if (new && (owner != OWNER_MASK && owner != new->pipe)) { + dev_WARN(dev, "window %u owned by head %u\n", index, owner); + return -EBUSY; + } + + /* + * This seems to happen whenever the head has been disabled with one + * or more windows being active. This is harmless because we'll just + * reassign the window to the new head anyway. + */ + if (old && owner == OWNER_MASK) + dev_dbg(dev, "window %u not owned by head %u but %u\n", index, + old->pipe, owner); + + value &= ~OWNER_MASK; + + if (new) + value |= OWNER(new->pipe); + else + value |= OWNER_MASK; + + tegra_dc_writel(dc, value, offset); + + plane->dc = new; + + return 0; +} + +static void tegra_dc_assign_shared_plane(struct tegra_dc *dc, + struct tegra_shared_plane *plane) +{ + u32 value; + int err; + + if (!tegra_dc_owns_shared_plane(dc, plane)) { + err = tegra_shared_plane_set_owner(plane, dc); + if (err < 0) + return; + } + + value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_LINEBUF_CONFIG); + value |= MODE_FOUR_LINES; + tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_LINEBUF_CONFIG); + + value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_FETCH_METER); + value = SLOTS(1); + tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_FETCH_METER); + + /* disable watermark */ + value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA); + value &= ~LATENCY_CTL_MODE_ENABLE; + tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA); + + value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB); + value |= WATERMARK_MASK; + tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB); + + /* pipe meter */ + value = tegra_plane_readl(plane, DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER); + value = PIPE_METER_INT(0) | PIPE_METER_FRAC(0); + tegra_plane_writel(plane, value, DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER); + + /* mempool entries */ + value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG); + value = MEMPOOL_ENTRIES(0x331); + tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG); + + value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_THREAD_GROUP); + value &= ~THREAD_NUM_MASK; + value |= THREAD_NUM(plane->base.index); + value |= THREAD_GROUP_ENABLE; + tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_THREAD_GROUP); + + tegra_shared_plane_update(plane); + tegra_shared_plane_activate(plane); +} + +static void tegra_dc_remove_shared_plane(struct tegra_dc *dc, + struct tegra_shared_plane *plane) +{ + tegra_shared_plane_set_owner(plane, NULL); +} + +static int tegra_shared_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct tegra_plane_state *plane_state = to_tegra_plane_state(state); + struct tegra_shared_plane *tegra = to_tegra_shared_plane(plane); + struct tegra_bo_tiling *tiling = &plane_state->tiling; + struct tegra_dc *dc = to_tegra_dc(state->crtc); + int err; + + /* no need for further checks if the plane is being disabled */ + if (!state->crtc || !state->fb) + return 0; + + err = tegra_plane_format(state->fb->format->format, + &plane_state->format, + &plane_state->swap); + if (err < 0) + return err; + + err = tegra_fb_get_tiling(state->fb, tiling); + if (err < 0) + return err; + + if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK && + !dc->soc->supports_block_linear) { + DRM_ERROR("hardware doesn't support block linear mode\n"); + return -EINVAL; + } + + /* + * Tegra doesn't support different strides for U and V planes so we + * error out if the user tries to display a framebuffer with such a + * configuration. + */ + if (state->fb->format->num_planes > 2) { + if (state->fb->pitches[2] != state->fb->pitches[1]) { + DRM_ERROR("unsupported UV-plane configuration\n"); + return -EINVAL; + } + } + + /* XXX scaling is not yet supported, add a check here */ + + err = tegra_plane_state_add(&tegra->base, state); + if (err < 0) + return err; + + return 0; +} + +static void tegra_shared_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct tegra_shared_plane *p = to_tegra_shared_plane(plane); + struct tegra_dc *dc = to_tegra_dc(old_state->crtc); + u32 value; + + /* rien ne va plus */ + if (!old_state || !old_state->crtc) + return; + + /* + * XXX Legacy helpers seem to sometimes call ->atomic_disable() even + * on planes that are already disabled. Make sure we fallback to the + * head for this particular state instead of crashing. + */ + if (WARN_ON(p->dc == NULL)) + p->dc = dc; + + pm_runtime_get_sync(dc->dev); + + value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS); + value &= ~WIN_ENABLE; + tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS); + + tegra_dc_remove_shared_plane(dc, p); + + pm_runtime_put(dc->dev); +} + +static void tegra_shared_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct tegra_plane_state *state = to_tegra_plane_state(plane->state); + struct tegra_shared_plane *p = to_tegra_shared_plane(plane); + struct tegra_dc *dc = to_tegra_dc(plane->state->crtc); + struct drm_framebuffer *fb = plane->state->fb; + struct tegra_bo *bo; + dma_addr_t base; + u32 value; + + /* rien ne va plus */ + if (!plane->state->crtc || !plane->state->fb) + return; + + if (!plane->state->visible) { + tegra_shared_plane_atomic_disable(plane, old_state); + return; + } + + pm_runtime_get_sync(dc->dev); + + tegra_dc_assign_shared_plane(dc, p); + + tegra_plane_writel(p, VCOUNTER, DC_WIN_CORE_ACT_CONTROL); + + /* blending */ + value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 | + BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC | + BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC; + tegra_plane_writel(p, value, DC_WIN_BLEND_MATCH_SELECT); + + value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 | + BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC | + BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC; + tegra_plane_writel(p, value, DC_WIN_BLEND_NOMATCH_SELECT); + + value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(p->base.depth); + tegra_plane_writel(p, value, DC_WIN_BLEND_LAYER_CONTROL); + + /* bypass scaling */ + value = HORIZONTAL_TAPS_5 | VERTICAL_TAPS_5; + tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_CONTROL_INPUT_SCALER); + + value = INPUT_SCALER_VBYPASS | INPUT_SCALER_HBYPASS; + tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_USAGE); + + /* disable compression */ + tegra_plane_writel(p, 0, DC_WINBUF_CDE_CONTROL); + + bo = tegra_fb_get_plane(fb, 0); + base = bo->paddr; + + tegra_plane_writel(p, state->format, DC_WIN_COLOR_DEPTH); + tegra_plane_writel(p, 0, DC_WIN_PRECOMP_WGRP_PARAMS); + + value = V_POSITION(plane->state->crtc_y) | + H_POSITION(plane->state->crtc_x); + tegra_plane_writel(p, value, DC_WIN_POSITION); + + value = V_SIZE(plane->state->crtc_h) | H_SIZE(plane->state->crtc_w); + tegra_plane_writel(p, value, DC_WIN_SIZE); + + value = WIN_ENABLE | COLOR_EXPAND; + tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS); + + value = V_SIZE(plane->state->crtc_h) | H_SIZE(plane->state->crtc_w); + tegra_plane_writel(p, value, DC_WIN_CROPPED_SIZE); + + tegra_plane_writel(p, upper_32_bits(base), DC_WINBUF_START_ADDR_HI); + tegra_plane_writel(p, lower_32_bits(base), DC_WINBUF_START_ADDR); + + value = PITCH(fb->pitches[0]); + tegra_plane_writel(p, value, DC_WIN_PLANAR_STORAGE); + + value = CLAMP_BEFORE_BLEND | DEGAMMA_SRGB | INPUT_RANGE_FULL; + tegra_plane_writel(p, value, DC_WIN_SET_PARAMS); + + value = OFFSET_X(plane->state->src_y >> 16) | + OFFSET_Y(plane->state->src_x >> 16); + tegra_plane_writel(p, value, DC_WINBUF_CROPPED_POINT); + + if (dc->soc->supports_block_linear) { + unsigned long height = state->tiling.value; + + /* XXX */ + switch (state->tiling.mode) { + case TEGRA_BO_TILING_MODE_PITCH: + value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(0) | + DC_WINBUF_SURFACE_KIND_PITCH; + break; + + /* XXX not supported on Tegra186 and later */ + case TEGRA_BO_TILING_MODE_TILED: + value = DC_WINBUF_SURFACE_KIND_TILED; + break; + + case TEGRA_BO_TILING_MODE_BLOCK: + value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) | + DC_WINBUF_SURFACE_KIND_BLOCK; + break; + } + + tegra_plane_writel(p, value, DC_WINBUF_SURFACE_KIND); + } + + /* disable gamut CSC */ + value = tegra_plane_readl(p, DC_WIN_WINDOW_SET_CONTROL); + value &= ~CONTROL_CSC_ENABLE; + tegra_plane_writel(p, value, DC_WIN_WINDOW_SET_CONTROL); + + pm_runtime_put(dc->dev); +} + +static const struct drm_plane_helper_funcs tegra_shared_plane_helper_funcs = { + .atomic_check = tegra_shared_plane_atomic_check, + .atomic_update = tegra_shared_plane_atomic_update, + .atomic_disable = tegra_shared_plane_atomic_disable, +}; + +struct drm_plane *tegra_shared_plane_create(struct drm_device *drm, + struct tegra_dc *dc, + unsigned int wgrp, + unsigned int index) +{ + enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY; + struct tegra_drm *tegra = drm->dev_private; + struct tegra_display_hub *hub = tegra->hub; + /* planes can be assigned to arbitrary CRTCs */ + unsigned int possible_crtcs = 0x7; + struct tegra_shared_plane *plane; + unsigned int num_formats; + struct drm_plane *p; + const u32 *formats; + int err; + + plane = kzalloc(sizeof(*plane), GFP_KERNEL); + if (!plane) + return ERR_PTR(-ENOMEM); + + plane->base.offset = 0x0a00 + 0x0300 * index; + plane->base.index = index; + plane->base.depth = 0; + + plane->wgrp = &hub->wgrps[wgrp]; + plane->wgrp->parent = dc->dev; + + p = &plane->base.base; + + num_formats = ARRAY_SIZE(tegra_shared_plane_formats); + formats = tegra_shared_plane_formats; + + err = drm_universal_plane_init(drm, p, possible_crtcs, + &tegra_plane_funcs, formats, + num_formats, NULL, type, NULL); + if (err < 0) { + kfree(plane); + return ERR_PTR(err); + } + + drm_plane_helper_add(p, &tegra_shared_plane_helper_funcs); + + return p; +} + +static void tegra_display_hub_update(struct tegra_dc *dc) +{ + u32 value; + + pm_runtime_get_sync(dc->dev); + + value = tegra_dc_readl(dc, DC_CMD_IHUB_COMMON_MISC_CTL); + value &= ~LATENCY_EVENT; + tegra_dc_writel(dc, value, DC_CMD_IHUB_COMMON_MISC_CTL); + + value = tegra_dc_readl(dc, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER); + value = CURS_SLOTS(1) | WGRP_SLOTS(1); + tegra_dc_writel(dc, value, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER); + + tegra_dc_writel(dc, COMMON_UPDATE, DC_CMD_STATE_CONTROL); + tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); + tegra_dc_writel(dc, COMMON_ACTREQ, DC_CMD_STATE_CONTROL); + tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); + + pm_runtime_put(dc->dev); +} + +void tegra_display_hub_atomic_commit(struct drm_device *drm, + struct drm_atomic_state *state) +{ + struct tegra_atomic_state *s = to_tegra_atomic_state(state); + struct tegra_drm *tegra = drm->dev_private; + struct tegra_display_hub *hub = tegra->hub; + struct device *dev = hub->client.dev; + int err; + + if (s->clk_disp) { + err = clk_set_rate(s->clk_disp, s->rate); + if (err < 0) + dev_err(dev, "failed to set rate of %pC to %lu Hz\n", + s->clk_disp, s->rate); + + err = clk_set_parent(hub->clk_disp, s->clk_disp); + if (err < 0) + dev_err(dev, "failed to set parent of %pC to %pC: %d\n", + hub->clk_disp, s->clk_disp, err); + } + + if (s->dc) + tegra_display_hub_update(s->dc); +} + +static int tegra_display_hub_init(struct host1x_client *client) +{ + struct tegra_display_hub *hub = to_tegra_display_hub(client); + struct drm_device *drm = dev_get_drvdata(client->parent); + struct tegra_drm *tegra = drm->dev_private; + + tegra->hub = hub; + + return 0; +} + +static int tegra_display_hub_exit(struct host1x_client *client) +{ + struct drm_device *drm = dev_get_drvdata(client->parent); + struct tegra_drm *tegra = drm->dev_private; + + tegra->hub = NULL; + + return 0; +} + +static const struct host1x_client_ops tegra_display_hub_ops = { + .init = tegra_display_hub_init, + .exit = tegra_display_hub_exit, +}; + +static int tegra_display_hub_probe(struct platform_device *pdev) +{ + struct tegra_display_hub *hub; + unsigned int i; + int err; + + hub = devm_kzalloc(&pdev->dev, sizeof(*hub), GFP_KERNEL); + if (!hub) + return -ENOMEM; + + hub->soc = of_device_get_match_data(&pdev->dev); + + hub->clk_disp = devm_clk_get(&pdev->dev, "disp"); + if (IS_ERR(hub->clk_disp)) { + err = PTR_ERR(hub->clk_disp); + return err; + } + + hub->clk_dsc = devm_clk_get(&pdev->dev, "dsc"); + if (IS_ERR(hub->clk_dsc)) { + err = PTR_ERR(hub->clk_dsc); + return err; + } + + hub->clk_hub = devm_clk_get(&pdev->dev, "hub"); + if (IS_ERR(hub->clk_hub)) { + err = PTR_ERR(hub->clk_hub); + return err; + } + + hub->rst = devm_reset_control_get(&pdev->dev, "misc"); + if (IS_ERR(hub->rst)) { + err = PTR_ERR(hub->rst); + return err; + } + + hub->wgrps = devm_kcalloc(&pdev->dev, hub->soc->num_wgrps, + sizeof(*hub->wgrps), GFP_KERNEL); + if (!hub->wgrps) + return -ENOMEM; + + for (i = 0; i < hub->soc->num_wgrps; i++) { + struct tegra_windowgroup *wgrp = &hub->wgrps[i]; + char id[8]; + + snprintf(id, sizeof(id), "wgrp%u", i); + mutex_init(&wgrp->lock); + wgrp->usecount = 0; + wgrp->index = i; + + wgrp->rst = devm_reset_control_get(&pdev->dev, id); + if (IS_ERR(wgrp->rst)) + return PTR_ERR(wgrp->rst); + + err = reset_control_assert(wgrp->rst); + if (err < 0) + return err; + } + + /* XXX: enable clock across reset? */ + err = reset_control_assert(hub->rst); + if (err < 0) + return err; + + platform_set_drvdata(pdev, hub); + pm_runtime_enable(&pdev->dev); + + INIT_LIST_HEAD(&hub->client.list); + hub->client.ops = &tegra_display_hub_ops; + hub->client.dev = &pdev->dev; + + err = host1x_client_register(&hub->client); + if (err < 0) + dev_err(&pdev->dev, "failed to register host1x client: %d\n", + err); + + return err; +} + +static int tegra_display_hub_remove(struct platform_device *pdev) +{ + struct tegra_display_hub *hub = platform_get_drvdata(pdev); + int err; + + err = host1x_client_unregister(&hub->client); + if (err < 0) { + dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", + err); + } + + pm_runtime_disable(&pdev->dev); + + return err; +} + +static int tegra_display_hub_suspend(struct device *dev) +{ + struct tegra_display_hub *hub = dev_get_drvdata(dev); + int err; + + err = reset_control_assert(hub->rst); + if (err < 0) + return err; + + clk_disable_unprepare(hub->clk_hub); + clk_disable_unprepare(hub->clk_dsc); + clk_disable_unprepare(hub->clk_disp); + + return 0; +} + +static int tegra_display_hub_resume(struct device *dev) +{ + struct tegra_display_hub *hub = dev_get_drvdata(dev); + int err; + + err = clk_prepare_enable(hub->clk_disp); + if (err < 0) + return err; + + err = clk_prepare_enable(hub->clk_dsc); + if (err < 0) + goto disable_disp; + + err = clk_prepare_enable(hub->clk_hub); + if (err < 0) + goto disable_dsc; + + err = reset_control_deassert(hub->rst); + if (err < 0) + goto disable_hub; + + return 0; + +disable_hub: + clk_disable_unprepare(hub->clk_hub); +disable_dsc: + clk_disable_unprepare(hub->clk_dsc); +disable_disp: + clk_disable_unprepare(hub->clk_disp); + return err; +} + +static const struct dev_pm_ops tegra_display_hub_pm_ops = { + SET_RUNTIME_PM_OPS(tegra_display_hub_suspend, + tegra_display_hub_resume, NULL) +}; + +static const struct tegra_display_hub_soc tegra186_display_hub = { + .num_wgrps = 6, +}; + +static const struct of_device_id tegra_display_hub_of_match[] = { + { + .compatible = "nvidia,tegra186-display", + .data = &tegra186_display_hub + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, tegra_display_hub_of_match); + +struct platform_driver tegra_display_hub_driver = { + .driver = { + .name = "tegra-display-hub", + .of_match_table = tegra_display_hub_of_match, + .pm = &tegra_display_hub_pm_ops, + }, + .probe = tegra_display_hub_probe, + .remove = tegra_display_hub_remove, +}; diff --git a/drivers/gpu/drm/tegra/hub.h b/drivers/gpu/drm/tegra/hub.h new file mode 100644 index 000000000000..965c333736b0 --- /dev/null +++ b/drivers/gpu/drm/tegra/hub.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef TEGRA_HUB_H +#define TEGRA_HUB_H 1 + +#include +#include + +#include "plane.h" + +struct tegra_dc; + +struct tegra_windowgroup { + unsigned int usecount; + struct mutex lock; + + unsigned int index; + struct device *parent; + struct reset_control *rst; +}; + +struct tegra_shared_plane { + struct tegra_plane base; + struct tegra_windowgroup *wgrp; + struct tegra_dc *dc; +}; + +static inline struct tegra_shared_plane * +to_tegra_shared_plane(struct drm_plane *plane) +{ + return container_of(plane, struct tegra_shared_plane, base.base); +} + +struct tegra_display_hub_soc { + unsigned int num_wgrps; +}; + +struct tegra_display_hub { + struct host1x_client client; + struct clk *clk_disp; + struct clk *clk_dsc; + struct clk *clk_hub; + struct reset_control *rst; + + const struct tegra_display_hub_soc *soc; + struct tegra_windowgroup *wgrps; +}; + +static inline struct tegra_display_hub * +to_tegra_display_hub(struct host1x_client *client) +{ + return container_of(client, struct tegra_display_hub, client); +} + +struct tegra_dc; +struct tegra_plane; + +int tegra_display_hub_prepare(struct tegra_display_hub *hub); +void tegra_display_hub_cleanup(struct tegra_display_hub *hub); + +struct drm_plane *tegra_shared_plane_create(struct drm_device *drm, + struct tegra_dc *dc, + unsigned int wgrp, + unsigned int index); + +void tegra_display_hub_atomic_commit(struct drm_device *drm, + struct drm_atomic_state *state); + +#define DC_CMD_IHUB_COMMON_MISC_CTL 0x068 +#define LATENCY_EVENT (1 << 3) + +#define DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER 0x451 +#define CURS_SLOTS(x) (((x) & 0xff) << 8) +#define WGRP_SLOTS(x) (((x) & 0xff) << 0) + +#endif /* TEGRA_HUB_H */ diff --git a/drivers/gpu/drm/tegra/plane.h b/drivers/gpu/drm/tegra/plane.h index 8237b885acd7..fc7566f630fa 100644 --- a/drivers/gpu/drm/tegra/plane.h +++ b/drivers/gpu/drm/tegra/plane.h @@ -15,7 +15,9 @@ struct tegra_bo; struct tegra_plane { struct drm_plane base; + unsigned int offset; unsigned int index; + unsigned int depth; }; struct tegra_cursor { -- cgit v1.2.3 From 473079549f27eab5ad449f2c4f079014f0fe74a5 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 30 Aug 2017 17:42:54 +0200 Subject: drm/tegra: dc: Add Tegra186 support The display architecture has changed in several signifcant ways with the new Tegra186 SoC. Display controllers are a completely different design, but have been given a frontend that simulates the register interface for earlier chips. Unfortunately the frontend isn't completely backwards compatible, so the driver needs parameterization to take the changes into account. One big change is that the total number of display controllers has been increased to three. At the same time the number of planes available has remained constant. However, planes can now be freely assigned between the display controllers, giving applications more flexibility in making the best use of the available resources. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 250 +++++++++++++++++++++++++++++++++++--------- drivers/gpu/drm/tegra/dc.h | 98 +++++++++++------ drivers/gpu/drm/tegra/drm.c | 1 + 3 files changed, 267 insertions(+), 82 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 25e998f8f339..5878d42d153c 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -19,6 +19,7 @@ #include "dc.h" #include "drm.h" #include "gem.h" +#include "hub.h" #include "plane.h" #include @@ -419,8 +420,8 @@ static const struct drm_plane_helper_funcs tegra_plane_helper_funcs = { .atomic_update = tegra_plane_atomic_update, }; -static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm, - struct tegra_dc *dc) +static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, + struct tegra_dc *dc) { /* * Ideally this would use drm_crtc_mask(), but that would require the @@ -435,6 +436,7 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm, * the same as drm_crtc_index() after registration. */ unsigned long possible_crtcs = 1 << drm->mode_config.num_crtc; + enum drm_plane_type type = DRM_PLANE_TYPE_PRIMARY; struct tegra_plane *plane; unsigned int num_formats; const u32 *formats; @@ -458,8 +460,7 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm, err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, &tegra_plane_funcs, formats, - num_formats, NULL, - DRM_PLANE_TYPE_PRIMARY, NULL); + num_formats, NULL, type, NULL); if (err < 0) { kfree(plane); return ERR_PTR(err); @@ -672,18 +673,61 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, return &plane->base; } -static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc) +static struct drm_plane *tegra_dc_add_shared_planes(struct drm_device *drm, + struct tegra_dc *dc) +{ + struct drm_plane *plane, *primary = NULL; + unsigned int i, j; + + for (i = 0; i < dc->soc->num_wgrps; i++) { + const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i]; + + if (wgrp->dc == dc->pipe) { + for (j = 0; j < wgrp->num_windows; j++) { + unsigned int index = wgrp->windows[j]; + + plane = tegra_shared_plane_create(drm, dc, + wgrp->index, + index); + if (IS_ERR(plane)) + return plane; + + /* + * Choose the first shared plane owned by this + * head as the primary plane. + */ + if (!primary) { + plane->type = DRM_PLANE_TYPE_PRIMARY; + primary = plane; + } + } + } + } + + return primary; +} + +static struct drm_plane *tegra_dc_add_planes(struct drm_device *drm, + struct tegra_dc *dc) { - struct drm_plane *plane; + struct drm_plane *plane, *primary; unsigned int i; + primary = tegra_primary_plane_create(drm, dc); + if (IS_ERR(primary)) + return primary; + for (i = 0; i < 2; i++) { plane = tegra_dc_overlay_plane_create(drm, dc, 1 + i); - if (IS_ERR(plane)) - return PTR_ERR(plane); + if (IS_ERR(plane)) { + /* XXX tegra_plane_destroy() */ + drm_plane_cleanup(primary); + kfree(primary); + return plane; + } } - return 0; + return primary; } static void tegra_dc_destroy(struct drm_crtc *crtc) @@ -1073,7 +1117,8 @@ static u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc) { struct tegra_dc *dc = to_tegra_dc(crtc); - if (dc->syncpt) + /* XXX vblank syncpoints don't work with nvdisplay yet */ + if (dc->syncpt && !dc->soc->has_nvdisplay) return host1x_syncpt_read(dc->syncpt); /* fallback to software emulated VBLANK counter */ @@ -1131,10 +1176,12 @@ static int tegra_dc_set_timings(struct tegra_dc *dc, unsigned int v_ref_to_sync = 1; unsigned long value; - tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS); + if (!dc->soc->has_nvdisplay) { + tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS); - value = (v_ref_to_sync << 16) | h_ref_to_sync; - tegra_dc_writel(dc, value, DC_DISP_REF_TO_SYNC); + value = (v_ref_to_sync << 16) | h_ref_to_sync; + tegra_dc_writel(dc, value, DC_DISP_REF_TO_SYNC); + } value = ((mode->vsync_end - mode->vsync_start) << 16) | ((mode->hsync_end - mode->hsync_start) << 0); @@ -1213,8 +1260,10 @@ static void tegra_dc_commit_state(struct tegra_dc *dc, state->div); DRM_DEBUG_KMS("pclk: %lu\n", state->pclk); - value = SHIFT_CLK_DIVIDER(state->div) | PIXEL_CLK_DIVIDER_PCD1; - tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL); + if (!dc->soc->has_nvdisplay) { + value = SHIFT_CLK_DIVIDER(state->div) | PIXEL_CLK_DIVIDER_PCD1; + tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL); + } err = clk_set_rate(dc->clk, state->pclk); if (err < 0) @@ -1324,39 +1373,66 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc, /* initialize display controller */ if (dc->syncpt) { - u32 syncpt = host1x_syncpt_id(dc->syncpt); + u32 syncpt = host1x_syncpt_id(dc->syncpt), enable; + + if (dc->soc->has_nvdisplay) + enable = 1 << 31; + else + enable = 1 << 8; value = SYNCPT_CNTRL_NO_STALL; tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); - value = SYNCPT_VSYNC_ENABLE | syncpt; + value = enable | syncpt; tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC); } - value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); - - value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); + if (dc->soc->has_nvdisplay) { + value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT | + DSC_OBUF_UF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); - /* initialize timer */ - value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) | - WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20); - tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY); + value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT | + DSC_OBUF_UF_INT | SD3_BUCKET_WALK_DONE_INT | + HEAD_UF_INT | MSF_INT | REG_TMOUT_INT | + REGION_CRC_INT | V_PULSE2_INT | V_PULSE3_INT | + VBLANK_INT | FRAME_END_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); - value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) | - WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1); - tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); + value = SD3_BUCKET_WALK_DONE_INT | HEAD_UF_INT | VBLANK_INT | + FRAME_END_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); - value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); + value = HEAD_UF_INT | REG_TMOUT_INT | FRAME_END_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_MASK); - value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_MASK); + tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); + } else { + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); + + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); + + /* initialize timer */ + value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) | + WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20); + tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY); + + value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) | + WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1); + tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); + + value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); + + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_MASK); + } if (dc->soc->supports_background_color) tegra_dc_writel(dc, 0, DC_DISP_BLEND_BACKGROUND_COLOR); @@ -1381,10 +1457,18 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc, value |= DISP_CTRL_MODE_C_DISPLAY; tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); - value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | - PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); + if (!dc->soc->has_nvdisplay) { + value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); + value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | + PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; + tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); + } + + /* enable underflow reporting and display red for missing pixels */ + if (dc->soc->has_nvdisplay) { + value = UNDERFLOW_MODE_RED | UNDERFLOW_REPORT_ENABLE; + tegra_dc_writel(dc, value, DC_COM_RG_UNDERFLOW); + } tegra_dc_commit(dc); @@ -1440,9 +1524,15 @@ static void tegra_crtc_atomic_flush(struct drm_crtc *crtc, { struct tegra_dc_state *state = to_dc_state(crtc->state); struct tegra_dc *dc = to_tegra_dc(crtc); + u32 value; + + value = state->planes << 8 | GENERAL_UPDATE; + tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); + value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, state->planes << 8, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, state->planes, DC_CMD_STATE_CONTROL); + value = state->planes | GENERAL_ACT_REQ; + tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); + value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); } static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { @@ -1490,6 +1580,11 @@ static irqreturn_t tegra_dc_irq(int irq, void *data) dc->stats.overflow++; } + if (status & HEAD_UF_INT) { + dev_dbg_ratelimited(dc->dev, "%s(): head underflow\n", __func__); + dc->stats.underflow++; + } + return IRQ_HANDLED; } @@ -1518,7 +1613,11 @@ static int tegra_dc_init(struct host1x_client *client) dc->domain = tegra->domain; } - primary = tegra_dc_primary_plane_create(drm, dc); + if (dc->soc->wgrps) + primary = tegra_dc_add_shared_planes(drm, dc); + else + primary = tegra_dc_add_planes(drm, dc); + if (IS_ERR(primary)) { err = PTR_ERR(primary); goto cleanup; @@ -1552,10 +1651,6 @@ static int tegra_dc_init(struct host1x_client *client) goto cleanup; } - err = tegra_dc_add_planes(drm, dc); - if (err < 0) - goto cleanup; - err = devm_request_irq(dc->dev, dc->irq, tegra_dc_irq, 0, dev_name(dc->dev), dc); if (err < 0) { @@ -1567,10 +1662,10 @@ static int tegra_dc_init(struct host1x_client *client) return 0; cleanup: - if (cursor) + if (!IS_ERR_OR_NULL(cursor)) drm_plane_cleanup(cursor); - if (primary) + if (!IS_ERR(primary)) drm_plane_cleanup(primary); if (tegra->domain) { @@ -1617,6 +1712,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = { .pitch_align = 8, .has_powergate = false, .broken_reset = true, + .has_nvdisplay = false, }; static const struct tegra_dc_soc_info tegra30_dc_soc_info = { @@ -1627,6 +1723,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = { .pitch_align = 8, .has_powergate = false, .broken_reset = false, + .has_nvdisplay = false, }; static const struct tegra_dc_soc_info tegra114_dc_soc_info = { @@ -1637,6 +1734,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = { .pitch_align = 64, .has_powergate = true, .broken_reset = false, + .has_nvdisplay = false, }; static const struct tegra_dc_soc_info tegra124_dc_soc_info = { @@ -1647,6 +1745,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = { .pitch_align = 64, .has_powergate = true, .broken_reset = false, + .has_nvdisplay = false, }; static const struct tegra_dc_soc_info tegra210_dc_soc_info = { @@ -1657,10 +1756,61 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = { .pitch_align = 64, .has_powergate = true, .broken_reset = false, + .has_nvdisplay = false, +}; + +static const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = { + { + .index = 0, + .dc = 0, + .windows = (const unsigned int[]) { 0 }, + .num_windows = 1, + }, { + .index = 1, + .dc = 1, + .windows = (const unsigned int[]) { 1 }, + .num_windows = 1, + }, { + .index = 2, + .dc = 1, + .windows = (const unsigned int[]) { 2 }, + .num_windows = 1, + }, { + .index = 3, + .dc = 2, + .windows = (const unsigned int[]) { 3 }, + .num_windows = 1, + }, { + .index = 4, + .dc = 2, + .windows = (const unsigned int[]) { 4 }, + .num_windows = 1, + }, { + .index = 5, + .dc = 2, + .windows = (const unsigned int[]) { 5 }, + .num_windows = 1, + }, +}; + +static const struct tegra_dc_soc_info tegra186_dc_soc_info = { + .supports_background_color = true, + .supports_interlacing = true, + .supports_cursor = true, + .supports_block_linear = true, + .pitch_align = 64, + .has_powergate = false, + .broken_reset = false, + .has_nvdisplay = true, + .wgrps = tegra186_dc_wgrps, + .num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps), }; static const struct of_device_id tegra_dc_of_match[] = { { + .compatible = "nvidia,tegra186-dc", + .data = &tegra186_dc_soc_info, + }, { .compatible = "nvidia,tegra210-dc", .data = &tegra210_dc_soc_info, }, { diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 22c5091006bc..47f43663adcb 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -43,6 +43,13 @@ struct tegra_dc_stats { unsigned long overflow; }; +struct tegra_windowgroup_soc { + unsigned int index; + unsigned int dc; + const unsigned int *windows; + unsigned int num_windows; +}; + struct tegra_dc_soc_info { bool supports_background_color; bool supports_interlacing; @@ -51,6 +58,9 @@ struct tegra_dc_soc_info { unsigned int pitch_align; bool has_powergate; bool broken_reset; + bool has_nvdisplay; + const struct tegra_windowgroup_soc *wgrps; + unsigned int num_wgrps; }; struct tegra_dc { @@ -180,15 +190,26 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define DC_CMD_INT_ENABLE 0x039 #define DC_CMD_INT_TYPE 0x03a #define DC_CMD_INT_POLARITY 0x03b -#define CTXSW_INT (1 << 0) -#define FRAME_END_INT (1 << 1) -#define VBLANK_INT (1 << 2) -#define WIN_A_UF_INT (1 << 8) -#define WIN_B_UF_INT (1 << 9) -#define WIN_C_UF_INT (1 << 10) -#define WIN_A_OF_INT (1 << 14) -#define WIN_B_OF_INT (1 << 15) -#define WIN_C_OF_INT (1 << 16) +#define CTXSW_INT (1 << 0) +#define FRAME_END_INT (1 << 1) +#define VBLANK_INT (1 << 2) +#define V_PULSE3_INT (1 << 4) +#define V_PULSE2_INT (1 << 5) +#define REGION_CRC_INT (1 << 6) +#define REG_TMOUT_INT (1 << 7) +#define WIN_A_UF_INT (1 << 8) +#define WIN_B_UF_INT (1 << 9) +#define WIN_C_UF_INT (1 << 10) +#define MSF_INT (1 << 12) +#define WIN_A_OF_INT (1 << 14) +#define WIN_B_OF_INT (1 << 15) +#define WIN_C_OF_INT (1 << 16) +#define HEAD_UF_INT (1 << 23) +#define SD3_BUCKET_WALK_DONE_INT (1 << 24) +#define DSC_OBUF_UF_INT (1 << 26) +#define DSC_RBUF_UF_INT (1 << 27) +#define DSC_BBUF_UF_INT (1 << 28) +#define DSC_TO_UF_INT (1 << 29) #define DC_CMD_SIGNAL_RAISE1 0x03c #define DC_CMD_SIGNAL_RAISE2 0x03d @@ -253,6 +274,10 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define DC_COM_GPIO_DEBOUNCE_COUNTER 0x328 #define DC_COM_CRC_CHECKSUM_LATCHED 0x329 +#define DC_COM_RG_UNDERFLOW 0x365 +#define UNDERFLOW_MODE_RED (1 << 8) +#define UNDERFLOW_REPORT_ENABLE (1 << 0) + #define DC_DISP_DISP_SIGNAL_OPTIONS0 0x400 #define H_PULSE0_ENABLE (1 << 8) #define H_PULSE1_ENABLE (1 << 10) @@ -375,29 +400,33 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define DISP_ORDER_BLUE_RED (1 << 9) #define DC_DISP_DISP_COLOR_CONTROL 0x430 -#define BASE_COLOR_SIZE666 (0 << 0) -#define BASE_COLOR_SIZE111 (1 << 0) -#define BASE_COLOR_SIZE222 (2 << 0) -#define BASE_COLOR_SIZE333 (3 << 0) -#define BASE_COLOR_SIZE444 (4 << 0) -#define BASE_COLOR_SIZE555 (5 << 0) -#define BASE_COLOR_SIZE565 (6 << 0) -#define BASE_COLOR_SIZE332 (7 << 0) -#define BASE_COLOR_SIZE888 (8 << 0) +#define BASE_COLOR_SIZE666 ( 0 << 0) +#define BASE_COLOR_SIZE111 ( 1 << 0) +#define BASE_COLOR_SIZE222 ( 2 << 0) +#define BASE_COLOR_SIZE333 ( 3 << 0) +#define BASE_COLOR_SIZE444 ( 4 << 0) +#define BASE_COLOR_SIZE555 ( 5 << 0) +#define BASE_COLOR_SIZE565 ( 6 << 0) +#define BASE_COLOR_SIZE332 ( 7 << 0) +#define BASE_COLOR_SIZE888 ( 8 << 0) +#define BASE_COLOR_SIZE101010 (10 << 0) +#define BASE_COLOR_SIZE121212 (12 << 0) #define DITHER_CONTROL_MASK (3 << 8) #define DITHER_CONTROL_DISABLE (0 << 8) #define DITHER_CONTROL_ORDERED (2 << 8) #define DITHER_CONTROL_ERRDIFF (3 << 8) #define BASE_COLOR_SIZE_MASK (0xf << 0) -#define BASE_COLOR_SIZE_666 (0 << 0) -#define BASE_COLOR_SIZE_111 (1 << 0) -#define BASE_COLOR_SIZE_222 (2 << 0) -#define BASE_COLOR_SIZE_333 (3 << 0) -#define BASE_COLOR_SIZE_444 (4 << 0) -#define BASE_COLOR_SIZE_555 (5 << 0) -#define BASE_COLOR_SIZE_565 (6 << 0) -#define BASE_COLOR_SIZE_332 (7 << 0) -#define BASE_COLOR_SIZE_888 (8 << 0) +#define BASE_COLOR_SIZE_666 ( 0 << 0) +#define BASE_COLOR_SIZE_111 ( 1 << 0) +#define BASE_COLOR_SIZE_222 ( 2 << 0) +#define BASE_COLOR_SIZE_333 ( 3 << 0) +#define BASE_COLOR_SIZE_444 ( 4 << 0) +#define BASE_COLOR_SIZE_555 ( 5 << 0) +#define BASE_COLOR_SIZE_565 ( 6 << 0) +#define BASE_COLOR_SIZE_332 ( 7 << 0) +#define BASE_COLOR_SIZE_888 ( 8 << 0) +#define BASE_COLOR_SIZE_101010 ( 10 << 0) +#define BASE_COLOR_SIZE_121212 ( 12 << 0) #define DC_DISP_SHIFT_CLOCK_OPTIONS 0x431 #define SC1_H_QUALIFIER_NONE (1 << 16) @@ -571,16 +600,16 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define WIN_COLOR_DEPTH_YUV422RA 25 #define DC_WIN_POSITION 0x704 -#define H_POSITION(x) (((x) & 0x1fff) << 0) -#define V_POSITION(x) (((x) & 0x1fff) << 16) +#define H_POSITION(x) (((x) & 0x1fff) << 0) /* XXX 0x7fff on Tegra186 */ +#define V_POSITION(x) (((x) & 0x1fff) << 16) /* XXX 0x7fff on Tegra186 */ #define DC_WIN_SIZE 0x705 -#define H_SIZE(x) (((x) & 0x1fff) << 0) -#define V_SIZE(x) (((x) & 0x1fff) << 16) +#define H_SIZE(x) (((x) & 0x1fff) << 0) /* XXX 0x7fff on Tegra186 */ +#define V_SIZE(x) (((x) & 0x1fff) << 16) /* XXX 0x7fff on Tegra186 */ #define DC_WIN_PRESCALED_SIZE 0x706 #define H_PRESCALED_SIZE(x) (((x) & 0x7fff) << 0) -#define V_PRESCALED_SIZE(x) (((x) & 0x1fff) << 16) +#define V_PRESCALED_SIZE(x) (((x) & 0x1fff) << 16) /* XXX 0x7fff on Tegra186 */ #define DC_WIN_H_INITIAL_DDA 0x707 #define DC_WIN_V_INITIAL_DDA 0x708 @@ -596,6 +625,7 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define DC_WIN_BUFFER_ADDR_MODE_TILE (1 << 0) #define DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV (0 << 16) #define DC_WIN_BUFFER_ADDR_MODE_TILE_UV (1 << 16) + #define DC_WIN_DV_CONTROL 0x70e #define DC_WIN_BLEND_NOKEY 0x70f @@ -635,6 +665,10 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define DC_WINBUF_CD_UFLOW_STATUS 0xfca /* Tegra186 and later */ +#define DC_DISP_CORE_SOR_SET_CONTROL(x) (0x403 + (x)) +#define PROTOCOL_MASK (0xf << 8) +#define PROTOCOL_SINGLE_TMDS_A (0x1 << 8) + #define DC_WIN_CORE_WINDOWGROUP_SET_CONTROL 0x702 #define OWNER_MASK (0xf << 0) #define OWNER(x) (((x) & 0xf) << 0) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 98a2494b4ed0..8ade997c2b6c 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -1295,6 +1295,7 @@ static const struct of_device_id host1x_drm_subdevs[] = { { .compatible = "nvidia,tegra210-sor1", }, { .compatible = "nvidia,tegra210-vic", }, { .compatible = "nvidia,tegra186-display", }, + { .compatible = "nvidia,tegra186-dc", }, { .compatible = "nvidia,tegra186-vic", }, { /* sentinel */ } }; -- cgit v1.2.3 From 7772fdaef939891d790c3461a4d1681417e0f51f Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 12 Oct 2017 17:30:55 +0200 Subject: drm/tegra: Support ARGB and ABGR formats These formats can easily be supported on all generations of Tegra. Note that the XRGB and XBGR formats that we supported were in fact using the ARGB and ABGR Tegra formats. This happened to work in cases where no alpha was being considered. This change is also a fix for those formats. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 4 ++++ drivers/gpu/drm/tegra/dc.h | 2 ++ drivers/gpu/drm/tegra/plane.c | 8 ++++++++ 3 files changed, 14 insertions(+) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 5878d42d153c..6790e2d869c4 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -287,7 +287,9 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, static const u32 tegra_primary_plane_formats[] = { DRM_FORMAT_XBGR8888, + DRM_FORMAT_ABGR8888, DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, DRM_FORMAT_RGB565, }; @@ -630,7 +632,9 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, static const uint32_t tegra_overlay_plane_formats[] = { DRM_FORMAT_XBGR8888, + DRM_FORMAT_ABGR8888, DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, DRM_FORMAT_RGB565, DRM_FORMAT_UYVY, DRM_FORMAT_YUYV, diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 47f43663adcb..018fea74fb50 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -598,6 +598,8 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define WIN_COLOR_DEPTH_YUV422R 23 #define WIN_COLOR_DEPTH_YCbCr422RA 24 #define WIN_COLOR_DEPTH_YUV422RA 25 +#define WIN_COLOR_DEPTH_B8G8R8X8 37 +#define WIN_COLOR_DEPTH_R8G8B8X8 38 #define DC_WIN_POSITION 0x704 #define H_POSITION(x) (((x) & 0x1fff) << 0) /* XXX 0x7fff on Tegra186 */ diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c index 78b24aa1ac83..88b5aea4a48e 100644 --- a/drivers/gpu/drm/tegra/plane.c +++ b/drivers/gpu/drm/tegra/plane.c @@ -111,10 +111,18 @@ int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap) switch (fourcc) { case DRM_FORMAT_XBGR8888: + *format = WIN_COLOR_DEPTH_R8G8B8X8; + break; + + case DRM_FORMAT_ABGR8888: *format = WIN_COLOR_DEPTH_R8G8B8A8; break; case DRM_FORMAT_XRGB8888: + *format = WIN_COLOR_DEPTH_B8G8R8X8; + break; + + case DRM_FORMAT_ARGB8888: *format = WIN_COLOR_DEPTH_B8G8R8A8; break; -- cgit v1.2.3 From 880cee0b7ff379ebcf1f3d839fa59d1bcd726797 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 12 Oct 2017 19:04:17 +0200 Subject: drm/tegra: sor: Parameterize register offsets Future Tegra generations have an increased number of display controllers that can drive individual SORs. In order to support that, the offset and layout of some registers has changed in backwards-incompatible ways. Use parameterized register offsets to support this. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/sor.c | 203 +++++++++++++++++++++++++++----------------- 1 file changed, 127 insertions(+), 76 deletions(-) diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 2fba6c2bd486..d51399587aca 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -144,12 +144,29 @@ static const struct tegra_sor_hdmi_settings tegra210_sor_hdmi_defaults[] = { }; #endif +struct tegra_sor_regs { + unsigned int head_state0; + unsigned int head_state1; + unsigned int head_state2; + unsigned int head_state3; + unsigned int head_state4; + unsigned int head_state5; + unsigned int pll0; + unsigned int pll1; + unsigned int pll2; + unsigned int pll3; + unsigned int dp_padctl0; + unsigned int dp_padctl2; +}; + struct tegra_sor_soc { bool supports_edp; bool supports_lvds; bool supports_hdmi; bool supports_dp; + const struct tegra_sor_regs *regs; + const struct tegra_sor_hdmi_settings *settings; unsigned int num_settings; @@ -387,23 +404,23 @@ static int tegra_sor_dp_train_fast(struct tegra_sor *sor, /* disable LVDS mode */ tegra_sor_writel(sor, 0, SOR_LVDS); - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value |= SOR_DP_PADCTL_TX_PU_ENABLE; value &= ~SOR_DP_PADCTL_TX_PU_MASK; value |= SOR_DP_PADCTL_TX_PU(2); /* XXX: don't hardcode? */ - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value |= SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 | SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0; - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); usleep_range(10, 100); - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value &= ~(SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 | SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0); - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); err = drm_dp_aux_prepare(sor->aux, DP_SET_ANSI_8B10B); if (err < 0) @@ -895,31 +912,31 @@ static void tegra_sor_mode_set(struct tegra_sor *sor, */ value = ((mode->vtotal & 0x7fff) << 16) | (mode->htotal & 0x7fff); - tegra_sor_writel(sor, value, SOR_HEAD_STATE1(dc->pipe)); + tegra_sor_writel(sor, value, sor->soc->regs->head_state1 + dc->pipe); /* sync end = sync width - 1 */ vse = mode->vsync_end - mode->vsync_start - 1; hse = mode->hsync_end - mode->hsync_start - 1; value = ((vse & 0x7fff) << 16) | (hse & 0x7fff); - tegra_sor_writel(sor, value, SOR_HEAD_STATE2(dc->pipe)); + tegra_sor_writel(sor, value, sor->soc->regs->head_state2 + dc->pipe); /* blank end = sync end + back porch */ vbe = vse + (mode->vtotal - mode->vsync_end); hbe = hse + (mode->htotal - mode->hsync_end); value = ((vbe & 0x7fff) << 16) | (hbe & 0x7fff); - tegra_sor_writel(sor, value, SOR_HEAD_STATE3(dc->pipe)); + tegra_sor_writel(sor, value, sor->soc->regs->head_state3 + dc->pipe); /* blank start = blank end + active */ vbs = vbe + mode->vdisplay; hbs = hbe + mode->hdisplay; value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff); - tegra_sor_writel(sor, value, SOR_HEAD_STATE4(dc->pipe)); + tegra_sor_writel(sor, value, sor->soc->regs->head_state4 + dc->pipe); /* XXX interlacing support */ - tegra_sor_writel(sor, 0x001, SOR_HEAD_STATE5(dc->pipe)); + tegra_sor_writel(sor, 0x001, sor->soc->regs->head_state5 + dc->pipe); } static int tegra_sor_detach(struct tegra_sor *sor) @@ -1001,10 +1018,10 @@ static int tegra_sor_power_down(struct tegra_sor *sor) return err; } - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 | SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2); - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); /* stop lane sequencer */ value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP | @@ -1024,20 +1041,20 @@ static int tegra_sor_power_down(struct tegra_sor *sor) if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0) return -ETIMEDOUT; - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value |= SOR_PLL2_PORT_POWERDOWN; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); usleep_range(20, 100); - value = tegra_sor_readl(sor, SOR_PLL0); + value = tegra_sor_readl(sor, sor->soc->regs->pll0); value |= SOR_PLL0_VCOPD | SOR_PLL0_PWR; - tegra_sor_writel(sor, value, SOR_PLL0); + tegra_sor_writel(sor, value, sor->soc->regs->pll0); - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value |= SOR_PLL2_SEQ_PLLCAPPD; value |= SOR_PLL2_SEQ_PLLCAPPD_ENFORCE; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); usleep_range(20, 100); @@ -1528,40 +1545,40 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK; tegra_sor_writel(sor, value, SOR_CLK_CNTRL); - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value &= ~SOR_PLL2_BANDGAP_POWERDOWN; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); usleep_range(20, 100); - value = tegra_sor_readl(sor, SOR_PLL3); + value = tegra_sor_readl(sor, sor->soc->regs->pll3); value |= SOR_PLL3_PLL_VDD_MODE_3V3; - tegra_sor_writel(sor, value, SOR_PLL3); + tegra_sor_writel(sor, value, sor->soc->regs->pll3); value = SOR_PLL0_ICHPMP(0xf) | SOR_PLL0_VCOCAP_RST | SOR_PLL0_PLLREG_LEVEL_V45 | SOR_PLL0_RESISTOR_EXT; - tegra_sor_writel(sor, value, SOR_PLL0); + tegra_sor_writel(sor, value, sor->soc->regs->pll0); - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value |= SOR_PLL2_SEQ_PLLCAPPD; value &= ~SOR_PLL2_SEQ_PLLCAPPD_ENFORCE; value |= SOR_PLL2_LVDS_ENABLE; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); value = SOR_PLL1_TERM_COMPOUT | SOR_PLL1_TMDS_TERM; - tegra_sor_writel(sor, value, SOR_PLL1); + tegra_sor_writel(sor, value, sor->soc->regs->pll1); while (true) { - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); if ((value & SOR_PLL2_SEQ_PLLCAPPD_ENFORCE) == 0) break; usleep_range(250, 1000); } - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value &= ~SOR_PLL2_POWERDOWN_OVERRIDE; value &= ~SOR_PLL2_PORT_POWERDOWN; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); /* * power up @@ -1574,18 +1591,18 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) tegra_sor_writel(sor, value, SOR_CLK_CNTRL); /* step 1 */ - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value |= SOR_PLL2_SEQ_PLLCAPPD_ENFORCE | SOR_PLL2_PORT_POWERDOWN | SOR_PLL2_BANDGAP_POWERDOWN; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); - value = tegra_sor_readl(sor, SOR_PLL0); + value = tegra_sor_readl(sor, sor->soc->regs->pll0); value |= SOR_PLL0_VCOPD | SOR_PLL0_PWR; - tegra_sor_writel(sor, value, SOR_PLL0); + tegra_sor_writel(sor, value, sor->soc->regs->pll0); - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value &= ~SOR_DP_PADCTL_PAD_CAL_PD; - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); /* step 2 */ err = tegra_io_rail_power_on(TEGRA_IO_RAIL_LVDS); @@ -1595,28 +1612,28 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) usleep_range(5, 100); /* step 3 */ - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value &= ~SOR_PLL2_BANDGAP_POWERDOWN; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); usleep_range(20, 100); /* step 4 */ - value = tegra_sor_readl(sor, SOR_PLL0); + value = tegra_sor_readl(sor, sor->soc->regs->pll0); value &= ~SOR_PLL0_VCOPD; value &= ~SOR_PLL0_PWR; - tegra_sor_writel(sor, value, SOR_PLL0); + tegra_sor_writel(sor, value, sor->soc->regs->pll0); - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value &= ~SOR_PLL2_SEQ_PLLCAPPD_ENFORCE; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); usleep_range(200, 1000); /* step 5 */ - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value &= ~SOR_PLL2_PORT_POWERDOWN; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); /* XXX not in TRM */ for (value = 0, i = 0; i < 5; i++) @@ -1632,7 +1649,7 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) dev_err(sor->dev, "failed to set parent clock: %d\n", err); /* power DP lanes */ - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); if (link.num_lanes <= 2) value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_2); @@ -1649,7 +1666,7 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) else value |= SOR_DP_PADCTL_PD_TXD_0; - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); value = tegra_sor_readl(sor, SOR_DP_LINKCTL0); value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK; @@ -1693,9 +1710,9 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) tegra_sor_writel(sor, value, SOR_DP_TPG); /* enable pad calibration logic */ - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value |= SOR_DP_PADCTL_PAD_CAL_PD; - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); err = drm_dp_link_probe(sor->aux, &link); if (err < 0) @@ -2017,38 +2034,38 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) usleep_range(20, 100); - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value &= ~SOR_PLL2_BANDGAP_POWERDOWN; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); usleep_range(20, 100); - value = tegra_sor_readl(sor, SOR_PLL3); + value = tegra_sor_readl(sor, sor->soc->regs->pll3); value &= ~SOR_PLL3_PLL_VDD_MODE_3V3; - tegra_sor_writel(sor, value, SOR_PLL3); + tegra_sor_writel(sor, value, sor->soc->regs->pll3); - value = tegra_sor_readl(sor, SOR_PLL0); + value = tegra_sor_readl(sor, sor->soc->regs->pll0); value &= ~SOR_PLL0_VCOPD; value &= ~SOR_PLL0_PWR; - tegra_sor_writel(sor, value, SOR_PLL0); + tegra_sor_writel(sor, value, sor->soc->regs->pll0); - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value &= ~SOR_PLL2_SEQ_PLLCAPPD_ENFORCE; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); usleep_range(200, 400); - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value &= ~SOR_PLL2_POWERDOWN_OVERRIDE; value &= ~SOR_PLL2_PORT_POWERDOWN; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); usleep_range(20, 100); - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value |= SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 | SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2; - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); while (true) { value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL); @@ -2166,9 +2183,9 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) tegra_sor_writel(sor, value, SOR_STATE1); /* power up pad calibration */ - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value &= ~SOR_DP_PADCTL_PAD_CAL_PD; - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); /* production settings */ settings = tegra_sor_hdmi_find_settings(sor, mode->clock * 1000); @@ -2178,24 +2195,24 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) return; } - value = tegra_sor_readl(sor, SOR_PLL0); + value = tegra_sor_readl(sor, sor->soc->regs->pll0); value &= ~SOR_PLL0_ICHPMP_MASK; value &= ~SOR_PLL0_VCOCAP_MASK; value |= SOR_PLL0_ICHPMP(settings->ichpmp); value |= SOR_PLL0_VCOCAP(settings->vcocap); - tegra_sor_writel(sor, value, SOR_PLL0); + tegra_sor_writel(sor, value, sor->soc->regs->pll0); tegra_sor_dp_term_calibrate(sor); - value = tegra_sor_readl(sor, SOR_PLL1); + value = tegra_sor_readl(sor, sor->soc->regs->pll1); value &= ~SOR_PLL1_LOADADJ_MASK; value |= SOR_PLL1_LOADADJ(settings->loadadj); - tegra_sor_writel(sor, value, SOR_PLL1); + tegra_sor_writel(sor, value, sor->soc->regs->pll1); - value = tegra_sor_readl(sor, SOR_PLL3); + value = tegra_sor_readl(sor, sor->soc->regs->pll3); value &= ~SOR_PLL3_BG_VREF_LEVEL_MASK; value |= SOR_PLL3_BG_VREF_LEVEL(settings->bg_vref); - tegra_sor_writel(sor, value, SOR_PLL3); + tegra_sor_writel(sor, value, sor->soc->regs->pll3); value = settings->drive_current[0] << 24 | settings->drive_current[1] << 16 | @@ -2209,16 +2226,16 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) settings->preemphasis[3] << 0; tegra_sor_writel(sor, value, SOR_LANE_PREEMPHASIS0); - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value &= ~SOR_DP_PADCTL_TX_PU_MASK; value |= SOR_DP_PADCTL_TX_PU_ENABLE; value |= SOR_DP_PADCTL_TX_PU(settings->tx_pu); - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); /* power down pad calibration */ - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value |= SOR_DP_PADCTL_PAD_CAL_PD; - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); /* miscellaneous display controller settings */ value = VSYNC_H_POSITION(1); @@ -2250,16 +2267,16 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) dev_err(sor->dev, "failed to power up SOR: %d\n", err); /* configure dynamic range of output */ - value = tegra_sor_readl(sor, SOR_HEAD_STATE0(dc->pipe)); + value = tegra_sor_readl(sor, sor->soc->regs->head_state0 + dc->pipe); value &= ~SOR_HEAD_STATE_RANGECOMPRESS_MASK; value &= ~SOR_HEAD_STATE_DYNRANGE_MASK; - tegra_sor_writel(sor, value, SOR_HEAD_STATE0(dc->pipe)); + tegra_sor_writel(sor, value, sor->soc->regs->head_state0 + dc->pipe); /* configure colorspace */ - value = tegra_sor_readl(sor, SOR_HEAD_STATE0(dc->pipe)); + value = tegra_sor_readl(sor, sor->soc->regs->head_state0 + dc->pipe); value &= ~SOR_HEAD_STATE_COLORSPACE_MASK; value |= SOR_HEAD_STATE_COLORSPACE_RGB; - tegra_sor_writel(sor, value, SOR_HEAD_STATE0(dc->pipe)); + tegra_sor_writel(sor, value, sor->soc->regs->head_state0 + dc->pipe); tegra_sor_mode_set(sor, mode, state); @@ -2488,19 +2505,51 @@ static const u8 tegra124_sor_xbar_cfg[5] = { 0, 1, 2, 3, 4 }; +static const struct tegra_sor_regs tegra124_sor_regs = { + .head_state0 = 0x05, + .head_state1 = 0x07, + .head_state2 = 0x09, + .head_state3 = 0x0b, + .head_state4 = 0x0d, + .head_state5 = 0x0f, + .pll0 = 0x17, + .pll1 = 0x18, + .pll2 = 0x19, + .pll3 = 0x1a, + .dp_padctl0 = 0x5c, + .dp_padctl2 = 0x73, +}; + static const struct tegra_sor_soc tegra124_sor = { .supports_edp = true, .supports_lvds = true, .supports_hdmi = false, .supports_dp = false, + .regs = &tegra124_sor_regs, .xbar_cfg = tegra124_sor_xbar_cfg, }; +static const struct tegra_sor_regs tegra210_sor_regs = { + .head_state0 = 0x05, + .head_state1 = 0x07, + .head_state2 = 0x09, + .head_state3 = 0x0b, + .head_state4 = 0x0d, + .head_state5 = 0x0f, + .pll0 = 0x17, + .pll1 = 0x18, + .pll2 = 0x19, + .pll3 = 0x1a, + .dp_padctl0 = 0x5c, + .dp_padctl2 = 0x73, +}; + static const struct tegra_sor_soc tegra210_sor = { .supports_edp = true, .supports_lvds = false, .supports_hdmi = false, .supports_dp = false, + .regs = &tegra210_sor_regs, .xbar_cfg = tegra124_sor_xbar_cfg, }; @@ -2514,6 +2563,8 @@ static const struct tegra_sor_soc tegra210_sor1 = { .supports_hdmi = true, .supports_dp = true, + .regs = &tegra210_sor_regs, + .num_settings = ARRAY_SIZE(tegra210_sor_hdmi_defaults), .settings = tegra210_sor_hdmi_defaults, -- cgit v1.2.3 From c57997bce423fb71334a1fefa524569e48a1718f Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 12 Oct 2017 19:12:57 +0200 Subject: drm/tegra: sor: Add Tegra186 support The SOR found on Tegra186 is very similar to the one found on Tegra210 and earlier. However, due to some changes in the display architecture, some programming sequences have changed and some register have moved around. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 13 ++ drivers/gpu/drm/tegra/dc.h | 5 +- drivers/gpu/drm/tegra/drm.c | 2 + drivers/gpu/drm/tegra/drm.h | 2 + drivers/gpu/drm/tegra/output.c | 24 ++ drivers/gpu/drm/tegra/sor.c | 487 +++++++++++++++++++++++++++++++---------- drivers/gpu/drm/tegra/sor.h | 12 + 7 files changed, 422 insertions(+), 123 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 6790e2d869c4..906752d86622 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -54,6 +54,19 @@ static u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset) return value; } +bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev) +{ + struct device_node *np = dc->dev->of_node; + struct of_phandle_iterator it; + int err; + + of_for_each_phandle(&it, err, np, "nvidia,outputs", NULL, 0) + if (it.node == dev->of_node) + return true; + + return false; +} + /* * Double-buffered registers have two copies: ASSEMBLY and ACTIVE. When the * *_ACT_REQ bits are set the ASSEMBLY copy is latched into the ACTIVE copy. diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 018fea74fb50..336d2c22f521 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -141,6 +141,7 @@ struct tegra_dc_window { }; /* from dc.c */ +bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev); void tegra_dc_commit(struct tegra_dc *dc); int tegra_dc_state_setup_clock(struct tegra_dc *dc, struct drm_crtc_state *crtc_state, @@ -289,10 +290,10 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define HDMI_ENABLE (1 << 30) #define DSI_ENABLE (1 << 29) #define SOR1_TIMING_CYA (1 << 27) -#define SOR1_ENABLE (1 << 26) -#define SOR_ENABLE (1 << 25) #define CURSOR_ENABLE (1 << 16) +#define SOR_ENABLE(x) (1 << (25 + (x))) + #define DC_DISP_DISP_MEM_HIGH_PRIORITY 0x403 #define CURSOR_THRESHOLD(x) (((x) & 0x03) << 24) #define WINDOW_A_THRESHOLD(x) (((x) & 0x7f) << 16) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 8ade997c2b6c..2fa1b48e14d2 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -1296,6 +1296,8 @@ static const struct of_device_id host1x_drm_subdevs[] = { { .compatible = "nvidia,tegra210-vic", }, { .compatible = "nvidia,tegra186-display", }, { .compatible = "nvidia,tegra186-dc", }, + { .compatible = "nvidia,tegra186-sor", }, + { .compatible = "nvidia,tegra186-sor1", }, { .compatible = "nvidia,tegra186-vic", }, { /* sentinel */ } }; diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index d4dfd239e2a5..c52bc5978d1c 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -164,6 +164,8 @@ int tegra_output_probe(struct tegra_output *output); void tegra_output_remove(struct tegra_output *output); int tegra_output_init(struct drm_device *drm, struct tegra_output *output); void tegra_output_exit(struct tegra_output *output); +void tegra_output_find_possible_crtcs(struct tegra_output *output, + struct drm_device *drm); int tegra_output_connector_get_modes(struct drm_connector *connector); enum drm_connector_status diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 24f8a3b712b4..ffe34bd0bb9d 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -9,7 +9,9 @@ #include #include + #include "drm.h" +#include "dc.h" #include @@ -218,3 +220,25 @@ void tegra_output_exit(struct tegra_output *output) if (output->panel) drm_panel_detach(output->panel); } + +void tegra_output_find_possible_crtcs(struct tegra_output *output, + struct drm_device *drm) +{ + struct device *dev = output->dev; + struct drm_crtc *crtc; + unsigned int mask = 0; + + drm_for_each_crtc(crtc, drm) { + struct tegra_dc *dc = to_tegra_dc(crtc); + + if (tegra_dc_has_output(dc, dev)) + mask |= drm_crtc_mask(crtc); + } + + if (mask == 0) { + dev_warn(dev, "missing output definition for heads in DT\n"); + mask = 0x3; + } + + output->encoder.possible_crtcs = mask; +} diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index d51399587aca..1d7f24df0b10 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -28,17 +28,30 @@ #include "sor.h" #include "trace.h" +/* + * XXX Remove this after the commit adding it to soc/tegra/pmc.h has been + * merged. Having this around after the commit is merged should be safe since + * the preprocessor will effectively replace all occurrences and therefore no + * duplicate will be defined. + */ +#define TEGRA_IO_PAD_HDMI_DP0 26 + #define SOR_REKEY 0x38 struct tegra_sor_hdmi_settings { unsigned long frequency; u8 vcocap; + u8 filter; u8 ichpmp; u8 loadadj; - u8 termadj; - u8 tx_pu; - u8 bg_vref; + u8 tmds_termadj; + u8 tx_pu_value; + u8 bg_temp_coef; + u8 bg_vref_level; + u8 avdd10_level; + u8 avdd14_level; + u8 sparepll; u8 drive_current[4]; u8 preemphasis[4]; @@ -49,51 +62,76 @@ static const struct tegra_sor_hdmi_settings tegra210_sor_hdmi_defaults[] = { { .frequency = 54000000, .vcocap = 0x0, + .filter = 0x0, .ichpmp = 0x1, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x10, - .bg_vref = 0x8, + .tmds_termadj = 0x9, + .tx_pu_value = 0x10, + .bg_temp_coef = 0x3, + .bg_vref_level = 0x8, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x33, 0x3a, 0x3a, 0x3a }, .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, }, { .frequency = 75000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0x1, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x40, - .bg_vref = 0x8, + .tmds_termadj = 0x9, + .tx_pu_value = 0x40, + .bg_temp_coef = 0x3, + .bg_vref_level = 0x8, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x33, 0x3a, 0x3a, 0x3a }, .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, }, { .frequency = 150000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0x1, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x66, - .bg_vref = 0x8, + .tmds_termadj = 0x9, + .tx_pu_value = 0x66, + .bg_temp_coef = 0x3, + .bg_vref_level = 0x8, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x33, 0x3a, 0x3a, 0x3a }, .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, }, { .frequency = 300000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0x1, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x66, - .bg_vref = 0xa, + .tmds_termadj = 0x9, + .tx_pu_value = 0x66, + .bg_temp_coef = 0x3, + .bg_vref_level = 0xa, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x33, 0x3f, 0x3f, 0x3f }, .preemphasis = { 0x00, 0x17, 0x17, 0x17 }, }, { .frequency = 600000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0x1, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x66, - .bg_vref = 0x8, + .tmds_termadj = 0x9, + .tx_pu_value = 0x66, + .bg_temp_coef = 0x3, + .bg_vref_level = 0x8, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x33, 0x3f, 0x3f, 0x3f }, .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, }, @@ -103,47 +141,146 @@ static const struct tegra_sor_hdmi_settings tegra210_sor_hdmi_defaults[] = { { .frequency = 75000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0x1, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x40, - .bg_vref = 0x8, + .tmds_termadj = 0x9, + .tx_pu_value = 0x40, + .bg_temp_coef = 0x3, + .bg_vref_level = 0x8, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x29, 0x29, 0x29, 0x29 }, .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, }, { .frequency = 150000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0x1, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x66, - .bg_vref = 0x8, + .tmds_termadj = 0x9, + .tx_pu_value = 0x66, + .bg_temp_coef = 0x3, + .bg_vref_level = 0x8, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x30, 0x37, 0x37, 0x37 }, .preemphasis = { 0x01, 0x02, 0x02, 0x02 }, }, { .frequency = 300000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0x6, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x66, - .bg_vref = 0xf, + .tmds_termadj = 0x9, + .tx_pu_value = 0x66, + .bg_temp_coef = 0x3, + .bg_vref_level = 0xf, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x30, 0x37, 0x37, 0x37 }, .preemphasis = { 0x10, 0x3e, 0x3e, 0x3e }, }, { .frequency = 600000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0xa, .loadadj = 0x3, - .termadj = 0xb, - .tx_pu = 0x66, - .bg_vref = 0xe, + .tmds_termadj = 0xb, + .tx_pu_value = 0x66, + .bg_temp_coef = 0x3, + .bg_vref_level = 0xe, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x35, 0x3e, 0x3e, 0x3e }, .preemphasis = { 0x02, 0x3f, 0x3f, 0x3f }, }, }; #endif +static const struct tegra_sor_hdmi_settings tegra186_sor_hdmi_defaults[] = { + { + .frequency = 54000000, + .vcocap = 0, + .filter = 5, + .ichpmp = 5, + .loadadj = 3, + .tmds_termadj = 0xf, + .tx_pu_value = 0, + .bg_temp_coef = 3, + .bg_vref_level = 8, + .avdd10_level = 4, + .avdd14_level = 4, + .sparepll = 0x54, + .drive_current = { 0x3a, 0x3a, 0x3a, 0x33 }, + .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, + }, { + .frequency = 75000000, + .vcocap = 1, + .filter = 5, + .ichpmp = 5, + .loadadj = 3, + .tmds_termadj = 0xf, + .tx_pu_value = 0, + .bg_temp_coef = 3, + .bg_vref_level = 8, + .avdd10_level = 4, + .avdd14_level = 4, + .sparepll = 0x44, + .drive_current = { 0x3a, 0x3a, 0x3a, 0x33 }, + .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, + }, { + .frequency = 150000000, + .vcocap = 3, + .filter = 5, + .ichpmp = 5, + .loadadj = 3, + .tmds_termadj = 15, + .tx_pu_value = 0x66 /* 0 */, + .bg_temp_coef = 3, + .bg_vref_level = 8, + .avdd10_level = 4, + .avdd14_level = 4, + .sparepll = 0x00, /* 0x34 */ + .drive_current = { 0x3a, 0x3a, 0x3a, 0x37 }, + .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, + }, { + .frequency = 300000000, + .vcocap = 3, + .filter = 5, + .ichpmp = 5, + .loadadj = 3, + .tmds_termadj = 15, + .tx_pu_value = 64, + .bg_temp_coef = 3, + .bg_vref_level = 8, + .avdd10_level = 4, + .avdd14_level = 4, + .sparepll = 0x34, + .drive_current = { 0x3d, 0x3d, 0x3d, 0x33 }, + .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, + }, { + .frequency = 600000000, + .vcocap = 3, + .filter = 5, + .ichpmp = 5, + .loadadj = 3, + .tmds_termadj = 12, + .tx_pu_value = 96, + .bg_temp_coef = 3, + .bg_vref_level = 8, + .avdd10_level = 4, + .avdd14_level = 4, + .sparepll = 0x34, + .drive_current = { 0x3d, 0x3d, 0x3d, 0x33 }, + .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, + } +}; + struct tegra_sor_regs { unsigned int head_state0; unsigned int head_state1; @@ -166,6 +303,7 @@ struct tegra_sor_soc { bool supports_dp; const struct tegra_sor_regs *regs; + bool has_nvdisplay; const struct tegra_sor_hdmi_settings *settings; unsigned int num_settings; @@ -188,6 +326,7 @@ struct tegra_sor { const struct tegra_sor_soc *soc; void __iomem *regs; + unsigned int index; struct reset_control *rst; struct clk *clk_parent; @@ -202,6 +341,7 @@ struct tegra_sor { struct drm_info_list *debugfs_files; const struct tegra_sor_ops *ops; + enum tegra_io_pad pad; /* for HDMI 2.0 */ struct tegra_sor_hdmi_settings *settings; @@ -480,47 +620,6 @@ static int tegra_sor_dp_train_fast(struct tegra_sor *sor, return 0; } -static void tegra_sor_dp_term_calibrate(struct tegra_sor *sor) -{ - u32 mask = 0x08, adj = 0, value; - - /* enable pad calibration logic */ - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); - value &= ~SOR_DP_PADCTL_PAD_CAL_PD; - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); - - value = tegra_sor_readl(sor, SOR_PLL1); - value |= SOR_PLL1_TMDS_TERM; - tegra_sor_writel(sor, value, SOR_PLL1); - - while (mask) { - adj |= mask; - - value = tegra_sor_readl(sor, SOR_PLL1); - value &= ~SOR_PLL1_TMDS_TERMADJ_MASK; - value |= SOR_PLL1_TMDS_TERMADJ(adj); - tegra_sor_writel(sor, value, SOR_PLL1); - - usleep_range(100, 200); - - value = tegra_sor_readl(sor, SOR_PLL1); - if (value & SOR_PLL1_TERM_COMPOUT) - adj &= ~mask; - - mask >>= 1; - } - - value = tegra_sor_readl(sor, SOR_PLL1); - value &= ~SOR_PLL1_TMDS_TERMADJ_MASK; - value |= SOR_PLL1_TMDS_TERMADJ(adj); - tegra_sor_writel(sor, value, SOR_PLL1); - - /* disable pad calibration logic */ - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); - value |= SOR_DP_PADCTL_PAD_CAL_PD; - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); -} - static void tegra_sor_super_update(struct tegra_sor *sor) { tegra_sor_writel(sor, 0, SOR_SUPER_STATE0); @@ -1217,6 +1316,7 @@ static const struct debugfs_reg32 tegra_sor_regs[] = { DEBUGFS_REG32(SOR_DP_MN1), DEBUGFS_REG32(SOR_DP_PADCTL0), DEBUGFS_REG32(SOR_DP_PADCTL1), + DEBUGFS_REG32(SOR_DP_PADCTL2), DEBUGFS_REG32(SOR_DP_DEBUG0), DEBUGFS_REG32(SOR_DP_DEBUG1), DEBUGFS_REG32(SOR_DP_SPARE0), @@ -1429,7 +1529,7 @@ static void tegra_sor_edp_disable(struct drm_encoder *encoder) */ if (dc) { value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); - value &= ~SOR_ENABLE; + value &= ~SOR_ENABLE(0); tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); tegra_dc_commit(dc); @@ -1445,9 +1545,9 @@ static void tegra_sor_edp_disable(struct drm_encoder *encoder) dev_err(sor->dev, "failed to disable DP: %d\n", err); } - err = tegra_io_rail_power_off(TEGRA_IO_RAIL_LVDS); + err = tegra_io_pad_power_disable(sor->pad); if (err < 0) - dev_err(sor->dev, "failed to power off I/O rail: %d\n", err); + dev_err(sor->dev, "failed to power off I/O pad: %d\n", err); if (output->panel) drm_panel_unprepare(output->panel); @@ -1605,9 +1705,9 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); /* step 2 */ - err = tegra_io_rail_power_on(TEGRA_IO_RAIL_LVDS); + err = tegra_io_pad_power_enable(sor->pad); if (err < 0) - dev_err(sor->dev, "failed to power on I/O rail: %d\n", err); + dev_err(sor->dev, "failed to power on I/O pad: %d\n", err); usleep_range(5, 100); @@ -1785,7 +1885,7 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) tegra_sor_update(sor); value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); - value |= SOR_ENABLE; + value |= SOR_ENABLE(0); tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); tegra_dc_commit(dc); @@ -1984,8 +2084,12 @@ static void tegra_sor_hdmi_disable(struct drm_encoder *encoder) /* disable display to SOR clock */ value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); - value &= ~SOR1_TIMING_CYA; - value &= ~SOR1_ENABLE; + + if (!sor->soc->has_nvdisplay) + value &= ~(SOR1_TIMING_CYA | SOR_ENABLE(1)); + else + value &= ~SOR_ENABLE(sor->index); + tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); tegra_dc_commit(dc); @@ -1994,9 +2098,9 @@ static void tegra_sor_hdmi_disable(struct drm_encoder *encoder) if (err < 0) dev_err(sor->dev, "failed to power down SOR: %d\n", err); - err = tegra_io_rail_power_off(TEGRA_IO_RAIL_HDMI); + err = tegra_io_pad_power_disable(sor->pad); if (err < 0) - dev_err(sor->dev, "failed to power off HDMI rail: %d\n", err); + dev_err(sor->dev, "failed to power off I/O pad: %d\n", err); pm_runtime_put(sor->dev); } @@ -2028,9 +2132,9 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) div = clk_get_rate(sor->clk) / 1000000 * 4; - err = tegra_io_rail_power_on(TEGRA_IO_RAIL_HDMI); + err = tegra_io_pad_power_enable(sor->pad); if (err < 0) - dev_err(sor->dev, "failed to power on HDMI rail: %d\n", err); + dev_err(sor->dev, "failed to power on I/O pad: %d\n", err); usleep_range(20, 100); @@ -2099,10 +2203,19 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_PCLK; tegra_sor_writel(sor, value, SOR_CLK_CNTRL); + /* SOR pad PLL stabilization time */ + usleep_range(250, 1000); + + value = tegra_sor_readl(sor, SOR_DP_LINKCTL0); + value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK; + value |= SOR_DP_LINKCTL_LANE_COUNT(4); + tegra_sor_writel(sor, value, SOR_DP_LINKCTL0); + value = tegra_sor_readl(sor, SOR_DP_SPARE0); - value |= SOR_DP_SPARE_DISP_VIDEO_PREAMBLE; + value &= ~SOR_DP_SPARE_DISP_VIDEO_PREAMBLE; value &= ~SOR_DP_SPARE_PANEL_INTERNAL; - value |= SOR_DP_SPARE_SEQ_ENABLE; + value &= ~SOR_DP_SPARE_SEQ_ENABLE; + value &= ~SOR_DP_SPARE_MACRO_SOR_CLK; tegra_sor_writel(sor, value, SOR_DP_SPARE0); value = SOR_SEQ_CTL_PU_PC(0) | SOR_SEQ_CTL_PU_PC_ALT(0) | @@ -2114,9 +2227,11 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) tegra_sor_writel(sor, value, SOR_SEQ_INST(0)); tegra_sor_writel(sor, value, SOR_SEQ_INST(8)); - /* program the reference clock */ - value = SOR_REFCLK_DIV_INT(div) | SOR_REFCLK_DIV_FRAC(div); - tegra_sor_writel(sor, value, SOR_REFCLK); + if (!sor->soc->has_nvdisplay) { + /* program the reference clock */ + value = SOR_REFCLK_DIV_INT(div) | SOR_REFCLK_DIV_FRAC(div); + tegra_sor_writel(sor, value, SOR_REFCLK); + } /* XXX not in TRM */ for (value = 0, i = 0; i < 5; i++) @@ -2139,13 +2254,16 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) return; } - value = SOR_INPUT_CONTROL_HDMI_SRC_SELECT(dc->pipe); - /* XXX is this the proper check? */ - if (mode->clock < 75000) - value |= SOR_INPUT_CONTROL_ARM_VIDEO_RANGE_LIMITED; + if (!sor->soc->has_nvdisplay) { + value = SOR_INPUT_CONTROL_HDMI_SRC_SELECT(dc->pipe); + + /* XXX is this the proper check? */ + if (mode->clock < 75000) + value |= SOR_INPUT_CONTROL_ARM_VIDEO_RANGE_LIMITED; - tegra_sor_writel(sor, value, SOR_INPUT_CONTROL); + tegra_sor_writel(sor, value, SOR_INPUT_CONTROL); + } max_ac = ((mode->htotal - mode->hdisplay) - SOR_REKEY - 18) / 32; @@ -2153,20 +2271,23 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) SOR_HDMI_CTRL_AUDIO_LAYOUT | SOR_HDMI_CTRL_REKEY(SOR_REKEY); tegra_sor_writel(sor, value, SOR_HDMI_CTRL); - /* H_PULSE2 setup */ - pulse_start = h_ref_to_sync + (mode->hsync_end - mode->hsync_start) + - (mode->htotal - mode->hsync_end) - 10; + if (!dc->soc->has_nvdisplay) { + /* H_PULSE2 setup */ + pulse_start = h_ref_to_sync + + (mode->hsync_end - mode->hsync_start) + + (mode->htotal - mode->hsync_end) - 10; - value = PULSE_LAST_END_A | PULSE_QUAL_VACTIVE | - PULSE_POLARITY_HIGH | PULSE_MODE_NORMAL; - tegra_dc_writel(dc, value, DC_DISP_H_PULSE2_CONTROL); + value = PULSE_LAST_END_A | PULSE_QUAL_VACTIVE | + PULSE_POLARITY_HIGH | PULSE_MODE_NORMAL; + tegra_dc_writel(dc, value, DC_DISP_H_PULSE2_CONTROL); - value = PULSE_END(pulse_start + 8) | PULSE_START(pulse_start); - tegra_dc_writel(dc, value, DC_DISP_H_PULSE2_POSITION_A); + value = PULSE_END(pulse_start + 8) | PULSE_START(pulse_start); + tegra_dc_writel(dc, value, DC_DISP_H_PULSE2_POSITION_A); - value = tegra_dc_readl(dc, DC_DISP_DISP_SIGNAL_OPTIONS0); - value |= H_PULSE2_ENABLE; - tegra_dc_writel(dc, value, DC_DISP_DISP_SIGNAL_OPTIONS0); + value = tegra_dc_readl(dc, DC_DISP_DISP_SIGNAL_OPTIONS0); + value |= H_PULSE2_ENABLE; + tegra_dc_writel(dc, value, DC_DISP_DISP_SIGNAL_OPTIONS0); + } /* infoframe setup */ err = tegra_sor_hdmi_setup_avi_infoframe(sor, mode); @@ -2197,49 +2318,66 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) value = tegra_sor_readl(sor, sor->soc->regs->pll0); value &= ~SOR_PLL0_ICHPMP_MASK; + value &= ~SOR_PLL0_FILTER_MASK; value &= ~SOR_PLL0_VCOCAP_MASK; value |= SOR_PLL0_ICHPMP(settings->ichpmp); + value |= SOR_PLL0_FILTER(settings->filter); value |= SOR_PLL0_VCOCAP(settings->vcocap); tegra_sor_writel(sor, value, sor->soc->regs->pll0); - tegra_sor_dp_term_calibrate(sor); - + /* XXX not in TRM */ value = tegra_sor_readl(sor, sor->soc->regs->pll1); value &= ~SOR_PLL1_LOADADJ_MASK; + value &= ~SOR_PLL1_TMDS_TERMADJ_MASK; value |= SOR_PLL1_LOADADJ(settings->loadadj); + value |= SOR_PLL1_TMDS_TERMADJ(settings->tmds_termadj); + value |= SOR_PLL1_TMDS_TERM; tegra_sor_writel(sor, value, sor->soc->regs->pll1); value = tegra_sor_readl(sor, sor->soc->regs->pll3); + value &= ~SOR_PLL3_BG_TEMP_COEF_MASK; value &= ~SOR_PLL3_BG_VREF_LEVEL_MASK; - value |= SOR_PLL3_BG_VREF_LEVEL(settings->bg_vref); + value &= ~SOR_PLL3_AVDD10_LEVEL_MASK; + value &= ~SOR_PLL3_AVDD14_LEVEL_MASK; + value |= SOR_PLL3_BG_TEMP_COEF(settings->bg_temp_coef); + value |= SOR_PLL3_BG_VREF_LEVEL(settings->bg_vref_level); + value |= SOR_PLL3_AVDD10_LEVEL(settings->avdd10_level); + value |= SOR_PLL3_AVDD14_LEVEL(settings->avdd14_level); tegra_sor_writel(sor, value, sor->soc->regs->pll3); - value = settings->drive_current[0] << 24 | - settings->drive_current[1] << 16 | - settings->drive_current[2] << 8 | - settings->drive_current[3] << 0; + value = settings->drive_current[3] << 24 | + settings->drive_current[2] << 16 | + settings->drive_current[1] << 8 | + settings->drive_current[0] << 0; tegra_sor_writel(sor, value, SOR_LANE_DRIVE_CURRENT0); - value = settings->preemphasis[0] << 24 | - settings->preemphasis[1] << 16 | - settings->preemphasis[2] << 8 | - settings->preemphasis[3] << 0; + value = settings->preemphasis[3] << 24 | + settings->preemphasis[2] << 16 | + settings->preemphasis[1] << 8 | + settings->preemphasis[0] << 0; tegra_sor_writel(sor, value, SOR_LANE_PREEMPHASIS0); value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value &= ~SOR_DP_PADCTL_TX_PU_MASK; value |= SOR_DP_PADCTL_TX_PU_ENABLE; - value |= SOR_DP_PADCTL_TX_PU(settings->tx_pu); + value |= SOR_DP_PADCTL_TX_PU(settings->tx_pu_value); tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl2); + value &= ~SOR_DP_PADCTL_SPAREPLL_MASK; + value |= SOR_DP_PADCTL_SPAREPLL(settings->sparepll); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl2); + /* power down pad calibration */ value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value |= SOR_DP_PADCTL_PAD_CAL_PD; tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); - /* miscellaneous display controller settings */ - value = VSYNC_H_POSITION(1); - tegra_dc_writel(dc, value, DC_DISP_DISP_TIMING_OPTIONS); + if (!dc->soc->has_nvdisplay) { + /* miscellaneous display controller settings */ + value = VSYNC_H_POSITION(1); + tegra_dc_writel(dc, value, DC_DISP_DISP_TIMING_OPTIONS); + } value = tegra_dc_readl(dc, DC_DISP_DISP_COLOR_CONTROL); value &= ~DITHER_CONTROL_MASK; @@ -2254,6 +2392,14 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) value |= BASE_COLOR_SIZE_888; break; + case 10: + value |= BASE_COLOR_SIZE_101010; + break; + + case 12: + value |= BASE_COLOR_SIZE_121212; + break; + default: WARN(1, "%u bits-per-color not supported\n", state->bpc); value |= BASE_COLOR_SIZE_888; @@ -2262,6 +2408,12 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) tegra_dc_writel(dc, value, DC_DISP_DISP_COLOR_CONTROL); + /* XXX set display head owner */ + value = tegra_sor_readl(sor, SOR_STATE1); + value &= ~SOR_STATE_ASY_OWNER_MASK; + value |= SOR_STATE_ASY_OWNER(1 + dc->pipe); + tegra_sor_writel(sor, value, SOR_STATE1); + err = tegra_sor_power_up(sor, 250); if (err < 0) dev_err(sor->dev, "failed to power up SOR: %d\n", err); @@ -2282,15 +2434,32 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) tegra_sor_update(sor); + /* program preamble timing in SOR (XXX) */ + value = tegra_sor_readl(sor, SOR_DP_SPARE0); + value &= ~SOR_DP_SPARE_DISP_VIDEO_PREAMBLE; + tegra_sor_writel(sor, value, SOR_DP_SPARE0); + err = tegra_sor_attach(sor); if (err < 0) dev_err(sor->dev, "failed to attach SOR: %d\n", err); /* enable display to SOR clock and generate HDMI preamble */ value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); - value |= SOR1_ENABLE | SOR1_TIMING_CYA; + + if (!sor->soc->has_nvdisplay) + value |= SOR_ENABLE(1) | SOR1_TIMING_CYA; + else + value |= SOR_ENABLE(sor->index); + tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); + if (dc->soc->has_nvdisplay) { + value = tegra_dc_readl(dc, DC_DISP_CORE_SOR_SET_CONTROL(sor->index)); + value &= ~PROTOCOL_MASK; + value |= PROTOCOL_SINGLE_TMDS_A; + tegra_dc_writel(dc, value, DC_DISP_CORE_SOR_SET_CONTROL(sor->index)); + } + tegra_dc_commit(dc); err = tegra_sor_wakeup(sor); @@ -2356,7 +2525,7 @@ static int tegra_sor_init(struct host1x_client *client) return err; } - sor->output.encoder.possible_crtcs = 0x3; + tegra_output_find_possible_crtcs(&sor->output, drm); if (sor->aux) { err = drm_dp_aux_attach(sor->aux, &sor->output); @@ -2526,6 +2695,7 @@ static const struct tegra_sor_soc tegra124_sor = { .supports_hdmi = false, .supports_dp = false, .regs = &tegra124_sor_regs, + .has_nvdisplay = false, .xbar_cfg = tegra124_sor_xbar_cfg, }; @@ -2550,6 +2720,7 @@ static const struct tegra_sor_soc tegra210_sor = { .supports_hdmi = false, .supports_dp = false, .regs = &tegra210_sor_regs, + .has_nvdisplay = false, .xbar_cfg = tegra124_sor_xbar_cfg, }; @@ -2564,6 +2735,7 @@ static const struct tegra_sor_soc tegra210_sor1 = { .supports_dp = true, .regs = &tegra210_sor_regs, + .has_nvdisplay = false, .num_settings = ARRAY_SIZE(tegra210_sor_hdmi_defaults), .settings = tegra210_sor_hdmi_defaults, @@ -2571,7 +2743,51 @@ static const struct tegra_sor_soc tegra210_sor1 = { .xbar_cfg = tegra210_sor_xbar_cfg, }; +static const struct tegra_sor_regs tegra186_sor_regs = { + .head_state0 = 0x151, + .head_state1 = 0x154, + .head_state2 = 0x157, + .head_state3 = 0x15a, + .head_state4 = 0x15d, + .head_state5 = 0x160, + .pll0 = 0x163, + .pll1 = 0x164, + .pll2 = 0x165, + .pll3 = 0x166, + .dp_padctl0 = 0x168, + .dp_padctl2 = 0x16a, +}; + +static const struct tegra_sor_soc tegra186_sor = { + .supports_edp = false, + .supports_lvds = false, + .supports_hdmi = false, + .supports_dp = true, + + .regs = &tegra186_sor_regs, + .has_nvdisplay = true, + + .xbar_cfg = tegra124_sor_xbar_cfg, +}; + +static const struct tegra_sor_soc tegra186_sor1 = { + .supports_edp = false, + .supports_lvds = false, + .supports_hdmi = true, + .supports_dp = true, + + .regs = &tegra186_sor_regs, + .has_nvdisplay = true, + + .num_settings = ARRAY_SIZE(tegra186_sor_hdmi_defaults), + .settings = tegra186_sor_hdmi_defaults, + + .xbar_cfg = tegra124_sor_xbar_cfg, +}; + static const struct of_device_id tegra_sor_of_match[] = { + { .compatible = "nvidia,tegra186-sor1", .data = &tegra186_sor1 }, + { .compatible = "nvidia,tegra186-sor", .data = &tegra186_sor }, { .compatible = "nvidia,tegra210-sor1", .data = &tegra210_sor1 }, { .compatible = "nvidia,tegra210-sor", .data = &tegra210_sor }, { .compatible = "nvidia,tegra124-sor", .data = &tegra124_sor }, @@ -2579,6 +2795,29 @@ static const struct of_device_id tegra_sor_of_match[] = { }; MODULE_DEVICE_TABLE(of, tegra_sor_of_match); +static int tegra_sor_parse_dt(struct tegra_sor *sor) +{ + struct device_node *np = sor->dev->of_node; + u32 value; + int err; + + if (sor->soc->has_nvdisplay) { + err = of_property_read_u32(np, "nvidia,interface", &value); + if (err < 0) + return err; + + sor->index = value; + + /* + * override the default that we already set for Tegra210 and + * earlier + */ + sor->pad = TEGRA_IO_PAD_HDMI_DP0 + sor->index; + } + + return 0; +} + static int tegra_sor_probe(struct platform_device *pdev) { struct device_node *np; @@ -2614,6 +2853,7 @@ static int tegra_sor_probe(struct platform_device *pdev) if (!sor->aux) { if (sor->soc->supports_hdmi) { sor->ops = &tegra_sor_hdmi_ops; + sor->pad = TEGRA_IO_PAD_HDMI; } else if (sor->soc->supports_lvds) { dev_err(&pdev->dev, "LVDS not supported yet\n"); return -ENODEV; @@ -2624,6 +2864,7 @@ static int tegra_sor_probe(struct platform_device *pdev) } else { if (sor->soc->supports_edp) { sor->ops = &tegra_sor_edp_ops; + sor->pad = TEGRA_IO_PAD_LVDS; } else if (sor->soc->supports_dp) { dev_err(&pdev->dev, "DisplayPort not supported yet\n"); return -ENODEV; @@ -2633,6 +2874,10 @@ static int tegra_sor_probe(struct platform_device *pdev) } } + err = tegra_sor_parse_dt(sor); + if (err < 0) + return err; + err = tegra_output_probe(&sor->output); if (err < 0) { dev_err(&pdev->dev, "failed to probe output: %d\n", err); diff --git a/drivers/gpu/drm/tegra/sor.h b/drivers/gpu/drm/tegra/sor.h index 865c73b48968..e85ffc8d98e4 100644 --- a/drivers/gpu/drm/tegra/sor.h +++ b/drivers/gpu/drm/tegra/sor.h @@ -89,6 +89,8 @@ #define SOR_PLL0 0x17 #define SOR_PLL0_ICHPMP_MASK (0xf << 24) #define SOR_PLL0_ICHPMP(x) (((x) & 0xf) << 24) +#define SOR_PLL0_FILTER_MASK (0xf << 16) +#define SOR_PLL0_FILTER(x) (((x) & 0xf) << 16) #define SOR_PLL0_VCOCAP_MASK (0xf << 8) #define SOR_PLL0_VCOCAP(x) (((x) & 0xf) << 8) #define SOR_PLL0_VCOCAP_RST SOR_PLL0_VCOCAP(3) @@ -122,10 +124,16 @@ #define SOR_PLL2_SEQ_PLL_PULLDOWN (1 << 16) #define SOR_PLL3 0x1a +#define SOR_PLL3_BG_TEMP_COEF_MASK (0xf << 28) +#define SOR_PLL3_BG_TEMP_COEF(x) (((x) & 0xf) << 28) #define SOR_PLL3_BG_VREF_LEVEL_MASK (0xf << 24) #define SOR_PLL3_BG_VREF_LEVEL(x) (((x) & 0xf) << 24) #define SOR_PLL3_PLL_VDD_MODE_1V8 (0 << 13) #define SOR_PLL3_PLL_VDD_MODE_3V3 (1 << 13) +#define SOR_PLL3_AVDD10_LEVEL_MASK (0xf << 8) +#define SOR_PLL3_AVDD10_LEVEL(x) (((x) & 0xf) << 8) +#define SOR_PLL3_AVDD14_LEVEL_MASK (0xf << 4) +#define SOR_PLL3_AVDD14_LEVEL(x) (((x) & 0xf) << 4) #define SOR_CSTM 0x1b #define SOR_CSTM_ROTCLK_MASK (0xf << 24) @@ -334,6 +342,10 @@ #define SOR_DP_LQ_CSTM1 0x70 #define SOR_DP_LQ_CSTM2 0x71 +#define SOR_DP_PADCTL2 0x73 +#define SOR_DP_PADCTL_SPAREPLL_MASK (0xff << 24) +#define SOR_DP_PADCTL_SPAREPLL(x) (((x) & 0xff) << 24) + #define SOR_HDMI_AUDIO_INFOFRAME_CTRL 0x9a #define SOR_HDMI_AUDIO_INFOFRAME_STATUS 0x9b #define SOR_HDMI_AUDIO_INFOFRAME_HEADER 0x9c -- cgit v1.2.3 From 36e90221acf37dd0eb5dee70cd189cc60f2e501a Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 12 Oct 2017 19:14:21 +0200 Subject: drm/tegra: sor: Support HDMI 2.0 modes In addition to using the SCDC helpers to enable support for scrambling for HDMI 2.0 modes, take into account the high pixel clocks when programming some of the registers. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/sor.c | 121 +++++++++++++++++++++++++++++++++++++++++--- drivers/gpu/drm/tegra/sor.h | 4 ++ 2 files changed, 119 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 1d7f24df0b10..f6313c4d612e 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "dc.h" #include "drm.h" @@ -350,11 +351,16 @@ struct tegra_sor { struct regulator *avdd_io_supply; struct regulator *vdd_pll_supply; struct regulator *hdmi_supply; + + struct delayed_work scdc; + bool scdc_enabled; }; struct tegra_sor_state { struct drm_connector_state base; + unsigned int link_speed; + unsigned long pclk; unsigned int bpc; }; @@ -1489,10 +1495,6 @@ static enum drm_mode_status tegra_sor_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { - /* HDMI 2.0 modes are not yet supported */ - if (mode->clock > 340000) - return MODE_NOCLOCK; - return MODE_OK; } @@ -1917,6 +1919,18 @@ tegra_sor_encoder_atomic_check(struct drm_encoder *encoder, info = &output->connector.display_info; + /* + * For HBR2 modes, the SOR brick needs to use the x20 multiplier, so + * the pixel clock must be corrected accordingly. + */ + if (pclk >= 340000000) { + state->link_speed = 20; + state->pclk = pclk / 2; + } else { + state->link_speed = 10; + state->pclk = pclk; + } + err = tegra_dc_state_setup_clock(dc, crtc_state, sor->clk_parent, pclk, 0); if (err < 0) { @@ -2067,6 +2081,81 @@ tegra_sor_hdmi_find_settings(struct tegra_sor *sor, unsigned long frequency) return NULL; } +static void tegra_sor_hdmi_disable_scrambling(struct tegra_sor *sor) +{ + u32 value; + + value = tegra_sor_readl(sor, SOR_HDMI2_CTRL); + value &= ~SOR_HDMI2_CTRL_CLOCK_MODE_DIV_BY_4; + value &= ~SOR_HDMI2_CTRL_SCRAMBLE; + tegra_sor_writel(sor, value, SOR_HDMI2_CTRL); +} + +static void tegra_sor_hdmi_scdc_disable(struct tegra_sor *sor) +{ + struct i2c_adapter *ddc = sor->output.ddc; + + drm_scdc_set_high_tmds_clock_ratio(ddc, false); + drm_scdc_set_scrambling(ddc, false); + + tegra_sor_hdmi_disable_scrambling(sor); +} + +static void tegra_sor_hdmi_scdc_stop(struct tegra_sor *sor) +{ + if (sor->scdc_enabled) { + cancel_delayed_work_sync(&sor->scdc); + tegra_sor_hdmi_scdc_disable(sor); + } +} + +static void tegra_sor_hdmi_enable_scrambling(struct tegra_sor *sor) +{ + u32 value; + + value = tegra_sor_readl(sor, SOR_HDMI2_CTRL); + value |= SOR_HDMI2_CTRL_CLOCK_MODE_DIV_BY_4; + value |= SOR_HDMI2_CTRL_SCRAMBLE; + tegra_sor_writel(sor, value, SOR_HDMI2_CTRL); +} + +static void tegra_sor_hdmi_scdc_enable(struct tegra_sor *sor) +{ + struct i2c_adapter *ddc = sor->output.ddc; + + drm_scdc_set_high_tmds_clock_ratio(ddc, true); + drm_scdc_set_scrambling(ddc, true); + + tegra_sor_hdmi_enable_scrambling(sor); +} + +static void tegra_sor_hdmi_scdc_work(struct work_struct *work) +{ + struct tegra_sor *sor = container_of(work, struct tegra_sor, scdc.work); + struct i2c_adapter *ddc = sor->output.ddc; + + if (!drm_scdc_get_scrambling_status(ddc)) { + DRM_DEBUG_KMS("SCDC not scrambled\n"); + tegra_sor_hdmi_scdc_enable(sor); + } + + schedule_delayed_work(&sor->scdc, msecs_to_jiffies(5000)); +} + +static void tegra_sor_hdmi_scdc_start(struct tegra_sor *sor) +{ + struct drm_scdc *scdc = &sor->output.connector.display_info.hdmi.scdc; + struct drm_display_mode *mode; + + mode = &sor->output.encoder.crtc->state->adjusted_mode; + + if (mode->clock >= 340000 && scdc->supported) { + schedule_delayed_work(&sor->scdc, msecs_to_jiffies(5000)); + tegra_sor_hdmi_scdc_enable(sor); + sor->scdc_enabled = true; + } +} + static void tegra_sor_hdmi_disable(struct drm_encoder *encoder) { struct tegra_output *output = encoder_to_output(encoder); @@ -2075,6 +2164,8 @@ static void tegra_sor_hdmi_disable(struct drm_encoder *encoder) u32 value; int err; + tegra_sor_hdmi_scdc_stop(sor); + err = tegra_sor_detach(sor); if (err < 0) dev_err(sor->dev, "failed to detach SOR: %d\n", err); @@ -2114,12 +2205,14 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) struct tegra_sor *sor = to_sor(output); struct tegra_sor_state *state; struct drm_display_mode *mode; + unsigned long rate, pclk; unsigned int div, i; u32 value; int err; state = to_sor_state(output->connector.state); mode = &encoder->crtc->state->adjusted_mode; + pclk = mode->clock * 1000; pm_runtime_get_sync(sor->dev); @@ -2195,10 +2288,13 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK; value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK; - if (mode->clock < 340000) + if (mode->clock < 340000) { + DRM_DEBUG_KMS("setting 2.7 GHz link speed\n"); value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G2_70; - else + } else { + DRM_DEBUG_KMS("setting 5.4 GHz link speed\n"); value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G5_40; + } value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_PCLK; tegra_sor_writel(sor, value, SOR_CLK_CNTRL); @@ -2254,6 +2350,15 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) return; } + /* adjust clock rate for HDMI 2.0 modes */ + rate = clk_get_rate(sor->clk_parent); + + if (mode->clock >= 340000) + rate /= 2; + + DRM_DEBUG_KMS("setting clock to %lu Hz, mode: %lu Hz\n", rate, pclk); + + clk_set_rate(sor->clk, rate); if (!sor->soc->has_nvdisplay) { value = SOR_INPUT_CONTROL_HDMI_SRC_SELECT(dc->pipe); @@ -2465,6 +2570,8 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) err = tegra_sor_wakeup(sor); if (err < 0) dev_err(sor->dev, "failed to wakeup SOR: %d\n", err); + + tegra_sor_hdmi_scdc_start(sor); } static const struct drm_encoder_helper_funcs tegra_sor_hdmi_helpers = { @@ -2652,6 +2759,8 @@ static int tegra_sor_hdmi_probe(struct tegra_sor *sor) return err; } + INIT_DELAYED_WORK(&sor->scdc, tegra_sor_hdmi_scdc_work); + return 0; } diff --git a/drivers/gpu/drm/tegra/sor.h b/drivers/gpu/drm/tegra/sor.h index e85ffc8d98e4..fb0854d92a27 100644 --- a/drivers/gpu/drm/tegra/sor.h +++ b/drivers/gpu/drm/tegra/sor.h @@ -382,4 +382,8 @@ #define SOR_HDMI_VSI_INFOFRAME_STATUS 0x124 #define SOR_HDMI_VSI_INFOFRAME_HEADER 0x125 +#define SOR_HDMI2_CTRL 0x13e +#define SOR_HDMI2_CTRL_CLOCK_MODE_DIV_BY_4 (1 << 1) +#define SOR_HDMI2_CTRL_SCRAMBLE (1 << 0) + #endif -- cgit v1.2.3 From 82b81b3ec1a796d10206fd8333437db20ef215fc Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 12 Oct 2017 17:32:52 +0200 Subject: drm/tegra: dpaux: Implement runtime PM Move clock and reset management into runtime PM callbacks and hook them up. This cleans up the code structure so that power management code does not clutter up the rest. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dpaux.c | 116 +++++++++++++++++++++++++++--------------- 1 file changed, 76 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/tegra/dpaux.c b/drivers/gpu/drm/tegra/dpaux.c index e4da041ba89b..2cd671b1a9cc 100644 --- a/drivers/gpu/drm/tegra/dpaux.c +++ b/drivers/gpu/drm/tegra/dpaux.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -467,52 +468,37 @@ static int tegra_dpaux_probe(struct platform_device *pdev) return PTR_ERR(dpaux->clk); } - err = clk_prepare_enable(dpaux->clk); - if (err < 0) { - dev_err(&pdev->dev, "failed to enable module clock: %d\n", - err); - return err; - } - - if (dpaux->rst) - reset_control_deassert(dpaux->rst); - dpaux->clk_parent = devm_clk_get(&pdev->dev, "parent"); if (IS_ERR(dpaux->clk_parent)) { dev_err(&pdev->dev, "failed to get parent clock: %ld\n", PTR_ERR(dpaux->clk_parent)); - err = PTR_ERR(dpaux->clk_parent); - goto assert_reset; - } - - err = clk_prepare_enable(dpaux->clk_parent); - if (err < 0) { - dev_err(&pdev->dev, "failed to enable parent clock: %d\n", - err); - goto assert_reset; + return PTR_ERR(dpaux->clk_parent); } err = clk_set_rate(dpaux->clk_parent, 270000000); if (err < 0) { dev_err(&pdev->dev, "failed to set clock to 270 MHz: %d\n", err); - goto disable_parent_clk; + return err; } dpaux->vdd = devm_regulator_get(&pdev->dev, "vdd"); if (IS_ERR(dpaux->vdd)) { dev_err(&pdev->dev, "failed to get VDD supply: %ld\n", PTR_ERR(dpaux->vdd)); - err = PTR_ERR(dpaux->vdd); - goto disable_parent_clk; + return PTR_ERR(dpaux->vdd); } + platform_set_drvdata(pdev, dpaux); + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + err = devm_request_irq(dpaux->dev, dpaux->irq, tegra_dpaux_irq, 0, dev_name(dpaux->dev), dpaux); if (err < 0) { dev_err(dpaux->dev, "failed to request IRQ#%u: %d\n", dpaux->irq, err); - goto disable_parent_clk; + return err; } disable_irq(dpaux->irq); @@ -522,7 +508,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev) err = drm_dp_aux_register(&dpaux->aux); if (err < 0) - goto disable_parent_clk; + return err; /* * Assume that by default the DPAUX/I2C pads will be used for HDMI, @@ -560,45 +546,94 @@ static int tegra_dpaux_probe(struct platform_device *pdev) list_add_tail(&dpaux->list, &dpaux_list); mutex_unlock(&dpaux_lock); - platform_set_drvdata(pdev, dpaux); - return 0; - -disable_parent_clk: - clk_disable_unprepare(dpaux->clk_parent); -assert_reset: - if (dpaux->rst) - reset_control_assert(dpaux->rst); - - clk_disable_unprepare(dpaux->clk); - - return err; } static int tegra_dpaux_remove(struct platform_device *pdev) { struct tegra_dpaux *dpaux = platform_get_drvdata(pdev); + cancel_work_sync(&dpaux->work); + /* make sure pads are powered down when not in use */ tegra_dpaux_pad_power_down(dpaux); + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + drm_dp_aux_unregister(&dpaux->aux); mutex_lock(&dpaux_lock); list_del(&dpaux->list); mutex_unlock(&dpaux_lock); - cancel_work_sync(&dpaux->work); + return 0; +} - clk_disable_unprepare(dpaux->clk_parent); +#ifdef CONFIG_PM +static int tegra_dpaux_suspend(struct device *dev) +{ + struct tegra_dpaux *dpaux = dev_get_drvdata(dev); + int err = 0; + + if (dpaux->rst) { + err = reset_control_assert(dpaux->rst); + if (err < 0) { + dev_err(dev, "failed to assert reset: %d\n", err); + return err; + } + } - if (dpaux->rst) - reset_control_assert(dpaux->rst); + usleep_range(1000, 2000); + clk_disable_unprepare(dpaux->clk_parent); clk_disable_unprepare(dpaux->clk); + return err; +} + +static int tegra_dpaux_resume(struct device *dev) +{ + struct tegra_dpaux *dpaux = dev_get_drvdata(dev); + int err; + + err = clk_prepare_enable(dpaux->clk); + if (err < 0) { + dev_err(dev, "failed to enable clock: %d\n", err); + return err; + } + + err = clk_prepare_enable(dpaux->clk_parent); + if (err < 0) { + dev_err(dev, "failed to enable parent clock: %d\n", err); + goto disable_clk; + } + + usleep_range(1000, 2000); + + if (dpaux->rst) { + err = reset_control_deassert(dpaux->rst); + if (err < 0) { + dev_err(dev, "failed to deassert reset: %d\n", err); + goto disable_parent; + } + + usleep_range(1000, 2000); + } + return 0; + +disable_parent: + clk_disable_unprepare(dpaux->clk_parent); +disable_clk: + clk_disable_unprepare(dpaux->clk); + return err; } +#endif + +static const struct dev_pm_ops tegra_dpaux_pm_ops = { + SET_RUNTIME_PM_OPS(tegra_dpaux_suspend, tegra_dpaux_resume, NULL) +}; static const struct of_device_id tegra_dpaux_of_match[] = { { .compatible = "nvidia,tegra210-dpaux", }, @@ -611,6 +646,7 @@ struct platform_driver tegra_dpaux_driver = { .driver = { .name = "tegra-dpaux", .of_match_table = tegra_dpaux_of_match, + .pm = &tegra_dpaux_pm_ops, }, .probe = tegra_dpaux_probe, .remove = tegra_dpaux_remove, -- cgit v1.2.3 From 7b2c2845e5d59db1d9dfa822436a71b2de18a7ee Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 12 Oct 2017 17:34:11 +0200 Subject: drm/tegra: dpaux: Add Tegra186 support DPAUX is the same as on previous generations. Supporting it is as simple as adding the compatible string so that the driver will bind to any of the devices. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dpaux.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/tegra/dpaux.c b/drivers/gpu/drm/tegra/dpaux.c index 2cd671b1a9cc..312b93c0323c 100644 --- a/drivers/gpu/drm/tegra/dpaux.c +++ b/drivers/gpu/drm/tegra/dpaux.c @@ -636,6 +636,7 @@ static const struct dev_pm_ops tegra_dpaux_pm_ops = { }; static const struct of_device_id tegra_dpaux_of_match[] = { + { .compatible = "nvidia,tegra186-dpaux", }, { .compatible = "nvidia,tegra210-dpaux", }, { .compatible = "nvidia,tegra124-dpaux", }, { }, -- cgit v1.2.3 From 71835caa00e8a64ada3c2e30c56468c39c81f60c Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 14 Nov 2017 16:09:30 +0100 Subject: drm/tegra: fb: Force alpha formats Tegra20 and Tegra30 don't actually support the 24-bit RGB formats that don't have an alpha component. In order to allow the fbdev emulation to run on those chips, force the 32-bit RGBA formats. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/fb.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 406e895d82cc..1af4ef9241f1 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -253,6 +253,19 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper, cmd.height = sizes->surface_height; cmd.pitches[0] = round_up(sizes->surface_width * bytes_per_pixel, tegra->pitch_align); + + /* + * Early generations of Tegra (Tegra20 and Tegra30) do not support any + * of the X* or *X formats, only their A* or *A equivalents. Force the + * legacy framebuffer format to include an alpha component so that the + * framebuffer emulation can be supported on all generations. + */ + if (sizes->surface_bpp == 32 && sizes->surface_depth == 24) + sizes->surface_depth = 32; + + if (sizes->surface_bpp == 16 && sizes->surface_depth == 15) + sizes->surface_depth = 16; + cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); -- cgit v1.2.3 From 511c7023cf23421d1d0455b22c139fe1be1b9e87 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 14 Nov 2017 16:07:40 +0100 Subject: drm/tegra: dc: Support more formats Also, split up formats into per-SoC lists because not all generations support all of them. Note that the list is now exhaustive for all RGB formats, but not for YUV and indexed formats. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 147 +++++++++++++++++++++++++++++++++++++++--- drivers/gpu/drm/tegra/dc.h | 20 +++++- drivers/gpu/drm/tegra/hub.c | 22 ++++++- drivers/gpu/drm/tegra/plane.c | 68 ++++++++++++++++--- 4 files changed, 235 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 906752d86622..eb005dce392a 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -298,12 +298,60 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, spin_unlock_irqrestore(&dc->lock, flags); } -static const u32 tegra_primary_plane_formats[] = { - DRM_FORMAT_XBGR8888, +static const u32 tegra20_primary_formats[] = { + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGBA5551, DRM_FORMAT_ABGR8888, - DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, +}; + +static const u32 tegra114_primary_formats[] = { + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_ARGB8888, + /* new on Tegra114 */ + DRM_FORMAT_ABGR4444, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_BGRX5551, + DRM_FORMAT_BGR565, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, +}; + +static const u32 tegra124_primary_formats[] = { + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ARGB1555, DRM_FORMAT_RGB565, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_ARGB8888, + /* new on Tegra114 */ + DRM_FORMAT_ABGR4444, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_BGRX5551, + DRM_FORMAT_BGR565, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + /* new on Tegra124 */ + DRM_FORMAT_RGBX8888, + DRM_FORMAT_BGRX8888, }; static int tegra_plane_atomic_check(struct drm_plane *plane, @@ -461,8 +509,8 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, if (!plane) return ERR_PTR(-ENOMEM); - num_formats = ARRAY_SIZE(tegra_primary_plane_formats); - formats = tegra_primary_plane_formats; + num_formats = dc->soc->num_primary_formats; + formats = dc->soc->primary_formats; /* * XXX compute offset so that we can directly access windows. @@ -643,12 +691,71 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, return &plane->base; } -static const uint32_t tegra_overlay_plane_formats[] = { - DRM_FORMAT_XBGR8888, +static const u32 tegra20_overlay_formats[] = { + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_ARGB8888, + /* planar formats */ + DRM_FORMAT_UYVY, + DRM_FORMAT_YUYV, + DRM_FORMAT_YUV420, + DRM_FORMAT_YUV422, +}; + +static const u32 tegra114_overlay_formats[] = { + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGBA5551, DRM_FORMAT_ABGR8888, - DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, + /* new on Tegra114 */ + DRM_FORMAT_ABGR4444, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_BGRX5551, + DRM_FORMAT_BGR565, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + /* planar formats */ + DRM_FORMAT_UYVY, + DRM_FORMAT_YUYV, + DRM_FORMAT_YUV420, + DRM_FORMAT_YUV422, +}; + +static const u32 tegra124_overlay_formats[] = { + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ARGB1555, DRM_FORMAT_RGB565, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_ARGB8888, + /* new on Tegra114 */ + DRM_FORMAT_ABGR4444, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_BGRX5551, + DRM_FORMAT_BGR565, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + /* new on Tegra124 */ + DRM_FORMAT_RGBX8888, + DRM_FORMAT_BGRX8888, + /* planar formats */ DRM_FORMAT_UYVY, DRM_FORMAT_YUYV, DRM_FORMAT_YUV420, @@ -673,8 +780,8 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, plane->index = index; plane->depth = 0; - num_formats = ARRAY_SIZE(tegra_overlay_plane_formats); - formats = tegra_overlay_plane_formats; + num_formats = dc->soc->num_overlay_formats; + formats = dc->soc->overlay_formats; err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe, &tegra_plane_funcs, formats, @@ -1730,6 +1837,10 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = { .has_powergate = false, .broken_reset = true, .has_nvdisplay = false, + .num_primary_formats = ARRAY_SIZE(tegra20_primary_formats), + .primary_formats = tegra20_primary_formats, + .num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats), + .overlay_formats = tegra20_overlay_formats, }; static const struct tegra_dc_soc_info tegra30_dc_soc_info = { @@ -1741,6 +1852,10 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = { .has_powergate = false, .broken_reset = false, .has_nvdisplay = false, + .num_primary_formats = ARRAY_SIZE(tegra20_primary_formats), + .primary_formats = tegra20_primary_formats, + .num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats), + .overlay_formats = tegra20_overlay_formats, }; static const struct tegra_dc_soc_info tegra114_dc_soc_info = { @@ -1752,6 +1867,10 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = { .has_powergate = true, .broken_reset = false, .has_nvdisplay = false, + .num_primary_formats = ARRAY_SIZE(tegra114_primary_formats), + .primary_formats = tegra114_primary_formats, + .num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats), + .overlay_formats = tegra114_overlay_formats, }; static const struct tegra_dc_soc_info tegra124_dc_soc_info = { @@ -1763,6 +1882,10 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = { .has_powergate = true, .broken_reset = false, .has_nvdisplay = false, + .num_primary_formats = ARRAY_SIZE(tegra124_primary_formats), + .primary_formats = tegra114_primary_formats, + .num_overlay_formats = ARRAY_SIZE(tegra124_overlay_formats), + .overlay_formats = tegra114_overlay_formats, }; static const struct tegra_dc_soc_info tegra210_dc_soc_info = { @@ -1774,6 +1897,10 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = { .has_powergate = true, .broken_reset = false, .has_nvdisplay = false, + .num_primary_formats = ARRAY_SIZE(tegra114_primary_formats), + .primary_formats = tegra114_primary_formats, + .num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats), + .overlay_formats = tegra114_overlay_formats, }; static const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = { diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 336d2c22f521..d680027fa272 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -61,6 +61,10 @@ struct tegra_dc_soc_info { bool has_nvdisplay; const struct tegra_windowgroup_soc *wgrps; unsigned int num_wgrps; + const u32 *primary_formats; + unsigned int num_primary_formats; + const u32 *overlay_formats; + unsigned int num_overlay_formats; }; struct tegra_dc { @@ -582,9 +586,9 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define WIN_COLOR_DEPTH_P4 2 #define WIN_COLOR_DEPTH_P8 3 #define WIN_COLOR_DEPTH_B4G4R4A4 4 -#define WIN_COLOR_DEPTH_B5G5R5A 5 +#define WIN_COLOR_DEPTH_B5G5R5A1 5 #define WIN_COLOR_DEPTH_B5G6R5 6 -#define WIN_COLOR_DEPTH_AB5G5R5 7 +#define WIN_COLOR_DEPTH_A1B5G5R5 7 #define WIN_COLOR_DEPTH_B8G8R8A8 12 #define WIN_COLOR_DEPTH_R8G8B8A8 13 #define WIN_COLOR_DEPTH_B6x2G6x2R6x2A8 14 @@ -599,8 +603,20 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define WIN_COLOR_DEPTH_YUV422R 23 #define WIN_COLOR_DEPTH_YCbCr422RA 24 #define WIN_COLOR_DEPTH_YUV422RA 25 +#define WIN_COLOR_DEPTH_R4G4B4A4 27 +#define WIN_COLOR_DEPTH_R5G5B5A 28 +#define WIN_COLOR_DEPTH_AR5G5B5 29 +#define WIN_COLOR_DEPTH_B5G5R5X1 30 +#define WIN_COLOR_DEPTH_X1B5G5R5 31 +#define WIN_COLOR_DEPTH_R5G5B5X1 32 +#define WIN_COLOR_DEPTH_X1R5G5B5 33 +#define WIN_COLOR_DEPTH_R5G6B5 34 +#define WIN_COLOR_DEPTH_A8R8G8B8 35 +#define WIN_COLOR_DEPTH_A8B8G8R8 36 #define WIN_COLOR_DEPTH_B8G8R8X8 37 #define WIN_COLOR_DEPTH_R8G8B8X8 38 +#define WIN_COLOR_DEPTH_X8B8G8R8 65 +#define WIN_COLOR_DEPTH_X8R8G8B8 66 #define DC_WIN_POSITION 0x704 #define H_POSITION(x) (((x) & 0x1fff) << 0) /* XXX 0x7fff on Tegra186 */ diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c index 33d008fb8745..cccd44711d68 100644 --- a/drivers/gpu/drm/tegra/hub.c +++ b/drivers/gpu/drm/tegra/hub.c @@ -26,9 +26,27 @@ #include "plane.h" static const u32 tegra_shared_plane_formats[] = { - DRM_FORMAT_XBGR8888, - DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB1555, DRM_FORMAT_RGB565, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + /* new on Tegra114 */ + DRM_FORMAT_ABGR4444, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_BGRX5551, + DRM_FORMAT_BGR565, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + /* planar formats */ + DRM_FORMAT_UYVY, + DRM_FORMAT_YUYV, + DRM_FORMAT_YUV420, + DRM_FORMAT_YUV422, }; static inline unsigned int tegra_plane_offset(struct tegra_shared_plane *plane, diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c index 88b5aea4a48e..9146aead973b 100644 --- a/drivers/gpu/drm/tegra/plane.c +++ b/drivers/gpu/drm/tegra/plane.c @@ -110,24 +110,76 @@ int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap) *swap = BYTE_SWAP_NOSWAP; switch (fourcc) { - case DRM_FORMAT_XBGR8888: - *format = WIN_COLOR_DEPTH_R8G8B8X8; + case DRM_FORMAT_ARGB4444: + *format = WIN_COLOR_DEPTH_B4G4R4A4; break; - case DRM_FORMAT_ABGR8888: - *format = WIN_COLOR_DEPTH_R8G8B8A8; + case DRM_FORMAT_ARGB1555: + *format = WIN_COLOR_DEPTH_B5G5R5A1; break; - case DRM_FORMAT_XRGB8888: - *format = WIN_COLOR_DEPTH_B8G8R8X8; + case DRM_FORMAT_RGB565: + *format = WIN_COLOR_DEPTH_B5G6R5; + break; + + case DRM_FORMAT_RGBA5551: + *format = WIN_COLOR_DEPTH_A1B5G5R5; break; case DRM_FORMAT_ARGB8888: *format = WIN_COLOR_DEPTH_B8G8R8A8; break; - case DRM_FORMAT_RGB565: - *format = WIN_COLOR_DEPTH_B5G6R5; + case DRM_FORMAT_ABGR8888: + *format = WIN_COLOR_DEPTH_R8G8B8A8; + break; + + case DRM_FORMAT_ABGR4444: + *format = WIN_COLOR_DEPTH_R4G4B4A4; + break; + + case DRM_FORMAT_ABGR1555: + *format = WIN_COLOR_DEPTH_R5G5B5A; + break; + + case DRM_FORMAT_BGRA5551: + *format = WIN_COLOR_DEPTH_AR5G5B5; + break; + + case DRM_FORMAT_XRGB1555: + *format = WIN_COLOR_DEPTH_B5G5R5X1; + break; + + case DRM_FORMAT_RGBX5551: + *format = WIN_COLOR_DEPTH_X1B5G5R5; + break; + + case DRM_FORMAT_XBGR1555: + *format = WIN_COLOR_DEPTH_R5G5B5X1; + break; + + case DRM_FORMAT_BGRX5551: + *format = WIN_COLOR_DEPTH_X1R5G5B5; + break; + + case DRM_FORMAT_BGR565: + *format = WIN_COLOR_DEPTH_R5G6B5; + break; + + case DRM_FORMAT_BGRA8888: + *format = WIN_COLOR_DEPTH_A8R8G8B8; + break; + + case DRM_FORMAT_RGBA8888: + *format = WIN_COLOR_DEPTH_A8B8G8R8; + break; + + case DRM_FORMAT_XRGB8888: + *format = WIN_COLOR_DEPTH_B8G8R8X8; + break; + + case DRM_FORMAT_XBGR8888: + *format = WIN_COLOR_DEPTH_R8G8B8X8; break; case DRM_FORMAT_UYVY: -- cgit v1.2.3 From 1087fac18b8e3ec8fadf1595bbc46dce7ff08a81 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 14 Dec 2017 13:37:53 +0100 Subject: drm/tegra: dc: Use direct offset to plane registers Traditionally, windows were accessed indirectly, through a register selection window that required a global register to be programmed with the index of the window to access. Since the global register could be written from modesetting functions as well as the interrupt handler concurrently, accesses had to be serialized using a lock. Using direct accesses to the window registers the lock can be avoided. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 173 ++++++++++++++++++++++-------------------- drivers/gpu/drm/tegra/hub.c | 45 +++++------ drivers/gpu/drm/tegra/hub.h | 1 - drivers/gpu/drm/tegra/plane.h | 2 + drivers/gpu/drm/tegra/sor.c | 2 + 5 files changed, 116 insertions(+), 107 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index eb005dce392a..edae21197fc6 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -34,26 +34,53 @@ static void tegra_dc_stats_reset(struct tegra_dc_stats *stats) stats->overflow = 0; } -/* - * Reads the active copy of a register. This takes the dc->lock spinlock to - * prevent races with the VBLANK processing which also needs access to the - * active copy of some registers. - */ +/* Reads the active copy of a register. */ static u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset) { - unsigned long flags; u32 value; - spin_lock_irqsave(&dc->lock, flags); - tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); value = tegra_dc_readl(dc, offset); tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); - spin_unlock_irqrestore(&dc->lock, flags); return value; } +static inline unsigned int tegra_plane_offset(struct tegra_plane *plane, + unsigned int offset) +{ + if (offset >= 0x500 && offset <= 0x638) { + offset = 0x000 + (offset - 0x500); + return plane->offset + offset; + } + + if (offset >= 0x700 && offset <= 0x719) { + offset = 0x180 + (offset - 0x700); + return plane->offset + offset; + } + + if (offset >= 0x800 && offset <= 0x839) { + offset = 0x1c0 + (offset - 0x800); + return plane->offset + offset; + } + + dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset); + + return plane->offset + offset; +} + +static inline u32 tegra_plane_readl(struct tegra_plane *plane, + unsigned int offset) +{ + return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset)); +} + +static inline void tegra_plane_writel(struct tegra_plane *plane, u32 value, + unsigned int offset) +{ + tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset)); +} + bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev) { struct device_node *np = dc->dev->of_node; @@ -125,12 +152,13 @@ static inline u32 compute_initial_dda(unsigned int in) return dfixed_frac(inf); } -static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, +static void tegra_dc_setup_window(struct tegra_plane *plane, const struct tegra_dc_window *window) { unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp; - unsigned long value, flags; + struct tegra_dc *dc = plane->dc; bool yuv, planar; + u32 value; /* * For YUV planar modes, the number of bytes per pixel takes into @@ -142,19 +170,14 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, else bpp = planar ? 1 : 2; - spin_lock_irqsave(&dc->lock, flags); - - value = WINDOW_A_SELECT << index; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); - - tegra_dc_writel(dc, window->format, DC_WIN_COLOR_DEPTH); - tegra_dc_writel(dc, window->swap, DC_WIN_BYTE_SWAP); + tegra_plane_writel(plane, window->format, DC_WIN_COLOR_DEPTH); + tegra_plane_writel(plane, window->swap, DC_WIN_BYTE_SWAP); value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x); - tegra_dc_writel(dc, value, DC_WIN_POSITION); + tegra_plane_writel(plane, value, DC_WIN_POSITION); value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w); - tegra_dc_writel(dc, value, DC_WIN_SIZE); + tegra_plane_writel(plane, value, DC_WIN_SIZE); h_offset = window->src.x * bpp; v_offset = window->src.y; @@ -162,7 +185,7 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, v_size = window->src.h; value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size); - tegra_dc_writel(dc, value, DC_WIN_PRESCALED_SIZE); + tegra_plane_writel(plane, value, DC_WIN_PRESCALED_SIZE); /* * For DDA computations the number of bytes per pixel for YUV planar @@ -175,33 +198,33 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp); value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda); - tegra_dc_writel(dc, value, DC_WIN_DDA_INC); + tegra_plane_writel(plane, value, DC_WIN_DDA_INC); h_dda = compute_initial_dda(window->src.x); v_dda = compute_initial_dda(window->src.y); - tegra_dc_writel(dc, h_dda, DC_WIN_H_INITIAL_DDA); - tegra_dc_writel(dc, v_dda, DC_WIN_V_INITIAL_DDA); + tegra_plane_writel(plane, h_dda, DC_WIN_H_INITIAL_DDA); + tegra_plane_writel(plane, v_dda, DC_WIN_V_INITIAL_DDA); - tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE); - tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE); + tegra_plane_writel(plane, 0, DC_WIN_UV_BUF_STRIDE); + tegra_plane_writel(plane, 0, DC_WIN_BUF_STRIDE); - tegra_dc_writel(dc, window->base[0], DC_WINBUF_START_ADDR); + tegra_plane_writel(plane, window->base[0], DC_WINBUF_START_ADDR); if (yuv && planar) { - tegra_dc_writel(dc, window->base[1], DC_WINBUF_START_ADDR_U); - tegra_dc_writel(dc, window->base[2], DC_WINBUF_START_ADDR_V); + tegra_plane_writel(plane, window->base[1], DC_WINBUF_START_ADDR_U); + tegra_plane_writel(plane, window->base[2], DC_WINBUF_START_ADDR_V); value = window->stride[1] << 16 | window->stride[0]; - tegra_dc_writel(dc, value, DC_WIN_LINE_STRIDE); + tegra_plane_writel(plane, value, DC_WIN_LINE_STRIDE); } else { - tegra_dc_writel(dc, window->stride[0], DC_WIN_LINE_STRIDE); + tegra_plane_writel(plane, window->stride[0], DC_WIN_LINE_STRIDE); } if (window->bottom_up) v_offset += window->src.h - 1; - tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET); - tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET); + tegra_plane_writel(plane, h_offset, DC_WINBUF_ADDR_H_OFFSET); + tegra_plane_writel(plane, v_offset, DC_WINBUF_ADDR_V_OFFSET); if (dc->soc->supports_block_linear) { unsigned long height = window->tiling.value; @@ -221,7 +244,7 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, break; } - tegra_dc_writel(dc, value, DC_WINBUF_SURFACE_KIND); + tegra_plane_writel(plane, value, DC_WINBUF_SURFACE_KIND); } else { switch (window->tiling.mode) { case TEGRA_BO_TILING_MODE_PITCH: @@ -242,21 +265,21 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, break; } - tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE); + tegra_plane_writel(plane, value, DC_WIN_BUFFER_ADDR_MODE); } value = WIN_ENABLE; if (yuv) { /* setup default colorspace conversion coefficients */ - tegra_dc_writel(dc, 0x00f0, DC_WIN_CSC_YOF); - tegra_dc_writel(dc, 0x012a, DC_WIN_CSC_KYRGB); - tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KUR); - tegra_dc_writel(dc, 0x0198, DC_WIN_CSC_KVR); - tegra_dc_writel(dc, 0x039b, DC_WIN_CSC_KUG); - tegra_dc_writel(dc, 0x032f, DC_WIN_CSC_KVG); - tegra_dc_writel(dc, 0x0204, DC_WIN_CSC_KUB); - tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KVB); + tegra_plane_writel(plane, 0x00f0, DC_WIN_CSC_YOF); + tegra_plane_writel(plane, 0x012a, DC_WIN_CSC_KYRGB); + tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KUR); + tegra_plane_writel(plane, 0x0198, DC_WIN_CSC_KVR); + tegra_plane_writel(plane, 0x039b, DC_WIN_CSC_KUG); + tegra_plane_writel(plane, 0x032f, DC_WIN_CSC_KVG); + tegra_plane_writel(plane, 0x0204, DC_WIN_CSC_KUB); + tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KVB); value |= CSC_ENABLE; } else if (window->bits_per_pixel < 24) { @@ -266,36 +289,34 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, if (window->bottom_up) value |= V_DIRECTION; - tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); + tegra_plane_writel(plane, value, DC_WIN_WIN_OPTIONS); /* * Disable blending and assume Window A is the bottom-most window, * Window C is the top-most window and Window B is in the middle. */ - tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_NOKEY); - tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_1WIN); + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_NOKEY); + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_1WIN); - switch (index) { + switch (plane->index) { case 0: - tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_X); - tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y); - tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY); + tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); + tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY); break; case 1: - tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X); - tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y); - tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY); + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); + tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY); break; case 2: - tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X); - tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_Y); - tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_3WIN_XY); + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_Y); + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_3WIN_XY); break; } - - spin_unlock_irqrestore(&dc->lock, flags); } static const u32 tegra20_primary_formats[] = { @@ -405,32 +426,22 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, static void tegra_plane_atomic_disable(struct drm_plane *plane, struct drm_plane_state *old_state) { - struct tegra_dc *dc = to_tegra_dc(old_state->crtc); struct tegra_plane *p = to_tegra_plane(plane); - unsigned long flags; u32 value; /* rien ne va plus */ if (!old_state || !old_state->crtc) return; - spin_lock_irqsave(&dc->lock, flags); - - value = WINDOW_A_SELECT << p->index; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); - - value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS); + value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS); value &= ~WIN_ENABLE; - tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); - - spin_unlock_irqrestore(&dc->lock, flags); + tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS); } static void tegra_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) { struct tegra_plane_state *state = to_tegra_plane_state(plane->state); - struct tegra_dc *dc = to_tegra_dc(plane->state->crtc); struct drm_framebuffer *fb = plane->state->fb; struct tegra_plane *p = to_tegra_plane(plane); struct tegra_dc_window window; @@ -474,7 +485,7 @@ static void tegra_plane_atomic_update(struct drm_plane *plane, window.stride[i] = fb->pitches[i]; } - tegra_dc_setup_window(dc, p->index, &window); + tegra_dc_setup_window(p, &window); } static const struct drm_plane_helper_funcs tegra_plane_helper_funcs = { @@ -509,17 +520,14 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, if (!plane) return ERR_PTR(-ENOMEM); - num_formats = dc->soc->num_primary_formats; - formats = dc->soc->primary_formats; - - /* - * XXX compute offset so that we can directly access windows. - * - * Always use window A as primary window. - */ - plane->offset = 0; + /* Always use window A as primary window */ + plane->offset = 0xa00; plane->index = 0; plane->depth = 255; + plane->dc = dc; + + num_formats = dc->soc->num_primary_formats; + formats = dc->soc->primary_formats; err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, &tegra_plane_funcs, formats, @@ -673,6 +681,7 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, * need to special-casing the cursor plane. */ plane->index = 6; + plane->dc = dc; num_formats = ARRAY_SIZE(tegra_cursor_plane_formats); formats = tegra_cursor_plane_formats; @@ -775,10 +784,10 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, if (!plane) return ERR_PTR(-ENOMEM); - /* XXX compute offset so that we can directly access windows */ - plane->offset = 0; + plane->offset = 0xa00 + 0x200 * index; plane->index = index; plane->depth = 0; + plane->dc = dc; num_formats = dc->soc->num_overlay_formats; formats = dc->soc->overlay_formats; diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c index cccd44711d68..63541dbd864d 100644 --- a/drivers/gpu/drm/tegra/hub.c +++ b/drivers/gpu/drm/tegra/hub.c @@ -49,39 +49,37 @@ static const u32 tegra_shared_plane_formats[] = { DRM_FORMAT_YUV422, }; -static inline unsigned int tegra_plane_offset(struct tegra_shared_plane *plane, +static inline unsigned int tegra_plane_offset(struct tegra_plane *plane, unsigned int offset) { - struct tegra_plane *p = &plane->base; - if (offset >= 0x500 && offset <= 0x581) { offset = 0x000 + (offset - 0x500); - return p->offset + offset; + return plane->offset + offset; } if (offset >= 0x700 && offset <= 0x73c) { offset = 0x180 + (offset - 0x700); - return p->offset + offset; + return plane->offset + offset; } if (offset >= 0x800 && offset <= 0x83e) { offset = 0x1c0 + (offset - 0x800); - return p->offset + offset; + return plane->offset + offset; } dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset); - return p->offset + offset; + return plane->offset + offset; } -static inline u32 tegra_plane_readl(struct tegra_shared_plane *plane, +static inline u32 tegra_plane_readl(struct tegra_plane *plane, unsigned int offset) { return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset)); } -static inline void tegra_plane_writel(struct tegra_shared_plane *plane, - u32 value, unsigned int offset) +static inline void tegra_plane_writel(struct tegra_plane *plane, u32 value, + unsigned int offset) { tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset)); } @@ -155,7 +153,7 @@ void tegra_display_hub_cleanup(struct tegra_display_hub *hub) } } -static void tegra_shared_plane_update(struct tegra_shared_plane *plane) +static void tegra_shared_plane_update(struct tegra_plane *plane) { struct tegra_dc *dc = plane->dc; unsigned long timeout; @@ -175,7 +173,7 @@ static void tegra_shared_plane_update(struct tegra_shared_plane *plane) } } -static void tegra_shared_plane_activate(struct tegra_shared_plane *plane) +static void tegra_shared_plane_activate(struct tegra_plane *plane) { struct tegra_dc *dc = plane->dc; unsigned long timeout; @@ -196,8 +194,7 @@ static void tegra_shared_plane_activate(struct tegra_shared_plane *plane) } static unsigned int -tegra_shared_plane_get_owner(struct tegra_shared_plane *plane, - struct tegra_dc *dc) +tegra_shared_plane_get_owner(struct tegra_plane *plane, struct tegra_dc *dc) { unsigned int offset = tegra_plane_offset(plane, DC_WIN_CORE_WINDOWGROUP_SET_CONTROL); @@ -206,7 +203,7 @@ tegra_shared_plane_get_owner(struct tegra_shared_plane *plane, } static bool tegra_dc_owns_shared_plane(struct tegra_dc *dc, - struct tegra_shared_plane *plane) + struct tegra_plane *plane) { struct device *dev = dc->dev; @@ -215,20 +212,20 @@ static bool tegra_dc_owns_shared_plane(struct tegra_dc *dc, return true; dev_WARN(dev, "head %u owns window %u but is not attached\n", - dc->pipe, plane->base.index); + dc->pipe, plane->index); } return false; } -static int tegra_shared_plane_set_owner(struct tegra_shared_plane *plane, +static int tegra_shared_plane_set_owner(struct tegra_plane *plane, struct tegra_dc *new) { unsigned int offset = tegra_plane_offset(plane, DC_WIN_CORE_WINDOWGROUP_SET_CONTROL); struct tegra_dc *old = plane->dc, *dc = new ? new : old; struct device *dev = new ? new->dev : old->dev; - unsigned int owner, index = plane->base.index; + unsigned int owner, index = plane->index; u32 value; value = tegra_dc_readl(dc, offset); @@ -246,7 +243,7 @@ static int tegra_shared_plane_set_owner(struct tegra_shared_plane *plane, */ if (old && owner == OWNER_MASK) dev_dbg(dev, "window %u not owned by head %u but %u\n", index, - old->pipe, owner); + old->pipe, owner); value &= ~OWNER_MASK; @@ -263,7 +260,7 @@ static int tegra_shared_plane_set_owner(struct tegra_shared_plane *plane, } static void tegra_dc_assign_shared_plane(struct tegra_dc *dc, - struct tegra_shared_plane *plane) + struct tegra_plane *plane) { u32 value; int err; @@ -312,7 +309,7 @@ static void tegra_dc_assign_shared_plane(struct tegra_dc *dc, } static void tegra_dc_remove_shared_plane(struct tegra_dc *dc, - struct tegra_shared_plane *plane) + struct tegra_plane *plane) { tegra_shared_plane_set_owner(plane, NULL); } @@ -370,8 +367,8 @@ static int tegra_shared_plane_atomic_check(struct drm_plane *plane, static void tegra_shared_plane_atomic_disable(struct drm_plane *plane, struct drm_plane_state *old_state) { - struct tegra_shared_plane *p = to_tegra_shared_plane(plane); struct tegra_dc *dc = to_tegra_dc(old_state->crtc); + struct tegra_plane *p = to_tegra_plane(plane); u32 value; /* rien ne va plus */ @@ -401,9 +398,9 @@ static void tegra_shared_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) { struct tegra_plane_state *state = to_tegra_plane_state(plane->state); - struct tegra_shared_plane *p = to_tegra_shared_plane(plane); struct tegra_dc *dc = to_tegra_dc(plane->state->crtc); struct drm_framebuffer *fb = plane->state->fb; + struct tegra_plane *p = to_tegra_plane(plane); struct tegra_bo *bo; dma_addr_t base; u32 value; @@ -434,7 +431,7 @@ static void tegra_shared_plane_atomic_update(struct drm_plane *plane, BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC; tegra_plane_writel(p, value, DC_WIN_BLEND_NOMATCH_SELECT); - value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(p->base.depth); + value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(p->depth); tegra_plane_writel(p, value, DC_WIN_BLEND_LAYER_CONTROL); /* bypass scaling */ diff --git a/drivers/gpu/drm/tegra/hub.h b/drivers/gpu/drm/tegra/hub.h index 965c333736b0..890a47cd05c3 100644 --- a/drivers/gpu/drm/tegra/hub.h +++ b/drivers/gpu/drm/tegra/hub.h @@ -28,7 +28,6 @@ struct tegra_windowgroup { struct tegra_shared_plane { struct tegra_plane base; struct tegra_windowgroup *wgrp; - struct tegra_dc *dc; }; static inline struct tegra_shared_plane * diff --git a/drivers/gpu/drm/tegra/plane.h b/drivers/gpu/drm/tegra/plane.h index fc7566f630fa..13b900d777e8 100644 --- a/drivers/gpu/drm/tegra/plane.h +++ b/drivers/gpu/drm/tegra/plane.h @@ -12,9 +12,11 @@ #include struct tegra_bo; +struct tegra_dc; struct tegra_plane { struct drm_plane base; + struct tegra_dc *dc; unsigned int offset; unsigned int index; unsigned int depth; diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index f6313c4d612e..4be9edf9c6fe 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -3047,6 +3047,8 @@ static int tegra_sor_probe(struct platform_device *pdev) name, err); goto remove; } + } else { + sor->clk_out = sor->clk; } sor->clk_parent = devm_clk_get(&pdev->dev, "parent"); -- cgit v1.2.3 From 363541e8eeed16fdf696252008ddee823730f82c Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 14 Dec 2017 13:50:19 +0100 Subject: drm/tegra: dc: Remove redundant spinlock The spinlock is only used to serialize accesses to the DC_CMD_INT_MASK register. However, this register is accesses either with interrupts masked (in tegra_crtc_atomic_enable()) or protected by the vbl_lock and vblank_time_lock spinlocks of the DRM device. Therefore, these accesses don't need any extra serialization and the lock can be removed. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 13 ++----------- drivers/gpu/drm/tegra/dc.h | 1 - 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index edae21197fc6..cd84d8514d48 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1261,31 +1261,23 @@ static u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc) static int tegra_dc_enable_vblank(struct drm_crtc *crtc) { struct tegra_dc *dc = to_tegra_dc(crtc); - unsigned long value, flags; - - spin_lock_irqsave(&dc->lock, flags); + u32 value; value = tegra_dc_readl(dc, DC_CMD_INT_MASK); value |= VBLANK_INT; tegra_dc_writel(dc, value, DC_CMD_INT_MASK); - spin_unlock_irqrestore(&dc->lock, flags); - return 0; } static void tegra_dc_disable_vblank(struct drm_crtc *crtc) { struct tegra_dc *dc = to_tegra_dc(crtc); - unsigned long value, flags; - - spin_lock_irqsave(&dc->lock, flags); + u32 value; value = tegra_dc_readl(dc, DC_CMD_INT_MASK); value &= ~VBLANK_INT; tegra_dc_writel(dc, value, DC_CMD_INT_MASK); - - spin_unlock_irqrestore(&dc->lock, flags); } static const struct drm_crtc_funcs tegra_crtc_funcs = { @@ -2033,7 +2025,6 @@ static int tegra_dc_probe(struct platform_device *pdev) dc->soc = of_device_get_match_data(&pdev->dev); - spin_lock_init(&dc->lock); INIT_LIST_HEAD(&dc->list); dc->dev = &pdev->dev; diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index d680027fa272..7dd02f07244f 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -71,7 +71,6 @@ struct tegra_dc { struct host1x_client client; struct host1x_syncpt *syncpt; struct device *dev; - spinlock_t lock; struct drm_crtc base; unsigned int powergate; -- cgit v1.2.3 From ab7d3f5826c55ad23101327eab435660caa83436 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 14 Dec 2017 13:46:20 +0100 Subject: drm/tegra: Implement zpos property Implement the standard zpos property for planes on Tegra124 and later. Earlier generations have a different blending unit that needs different programming. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 94 ++++++++++++++++++++++++++++++------------- drivers/gpu/drm/tegra/dc.h | 2 + drivers/gpu/drm/tegra/drm.c | 25 +++++++++++- drivers/gpu/drm/tegra/hub.c | 5 ++- drivers/gpu/drm/tegra/plane.h | 1 - 5 files changed, 95 insertions(+), 32 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index cd84d8514d48..d118094a96c1 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -152,6 +152,55 @@ static inline u32 compute_initial_dda(unsigned int in) return dfixed_frac(inf); } +static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane) +{ + /* + * Disable blending and assume Window A is the bottom-most window, + * Window C is the top-most window and Window B is in the middle. + */ + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_NOKEY); + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_1WIN); + + switch (plane->index) { + case 0: + tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); + tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY); + break; + + case 1: + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); + tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY); + break; + + case 2: + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_Y); + tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_3WIN_XY); + break; + } +} + +static void tegra_plane_setup_blending(struct tegra_plane *plane, + const struct tegra_dc_window *window) +{ + u32 value; + + value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 | + BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC | + BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC; + tegra_plane_writel(plane, value, DC_WIN_BLEND_MATCH_SELECT); + + value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 | + BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC | + BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC; + tegra_plane_writel(plane, value, DC_WIN_BLEND_NOMATCH_SELECT); + + value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - window->zpos); + tegra_plane_writel(plane, value, DC_WIN_BLEND_LAYER_CONTROL); +} + static void tegra_dc_setup_window(struct tegra_plane *plane, const struct tegra_dc_window *window) { @@ -291,32 +340,10 @@ static void tegra_dc_setup_window(struct tegra_plane *plane, tegra_plane_writel(plane, value, DC_WIN_WIN_OPTIONS); - /* - * Disable blending and assume Window A is the bottom-most window, - * Window C is the top-most window and Window B is in the middle. - */ - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_NOKEY); - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_1WIN); - - switch (plane->index) { - case 0: - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_X); - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY); - break; - - case 1: - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X); - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY); - break; - - case 2: - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X); - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_Y); - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_3WIN_XY); - break; - } + if (dc->soc->supports_blending) + tegra_plane_setup_blending(plane, window); + else + tegra_plane_setup_blending_legacy(plane); } static const u32 tegra20_primary_formats[] = { @@ -467,6 +494,7 @@ static void tegra_plane_atomic_update(struct drm_plane *plane, window.bottom_up = tegra_fb_is_bottom_up(fb); /* copy from state */ + window.zpos = plane->state->normalized_zpos; window.tiling = state->tiling; window.format = state->format; window.swap = state->swap; @@ -523,7 +551,6 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, /* Always use window A as primary window */ plane->offset = 0xa00; plane->index = 0; - plane->depth = 255; plane->dc = dc; num_formats = dc->soc->num_primary_formats; @@ -539,6 +566,9 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs); + if (dc->soc->supports_blending) + drm_plane_create_zpos_property(&plane->base, 0, 0, 255); + return &plane->base; } @@ -786,7 +816,6 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, plane->offset = 0xa00 + 0x200 * index; plane->index = index; - plane->depth = 0; plane->dc = dc; num_formats = dc->soc->num_overlay_formats; @@ -803,6 +832,9 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs); + if (dc->soc->supports_blending) + drm_plane_create_zpos_property(&plane->base, 0, 0, 255); + return &plane->base; } @@ -1834,6 +1866,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = { .supports_interlacing = false, .supports_cursor = false, .supports_block_linear = false, + .supports_blending = false, .pitch_align = 8, .has_powergate = false, .broken_reset = true, @@ -1849,6 +1882,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = { .supports_interlacing = false, .supports_cursor = false, .supports_block_linear = false, + .supports_blending = false, .pitch_align = 8, .has_powergate = false, .broken_reset = false, @@ -1864,6 +1898,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = { .supports_interlacing = false, .supports_cursor = false, .supports_block_linear = false, + .supports_blending = false, .pitch_align = 64, .has_powergate = true, .broken_reset = false, @@ -1879,6 +1914,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = { .supports_interlacing = true, .supports_cursor = true, .supports_block_linear = true, + .supports_blending = true, .pitch_align = 64, .has_powergate = true, .broken_reset = false, @@ -1894,6 +1930,7 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = { .supports_interlacing = true, .supports_cursor = true, .supports_block_linear = true, + .supports_blending = true, .pitch_align = 64, .has_powergate = true, .broken_reset = false, @@ -1943,6 +1980,7 @@ static const struct tegra_dc_soc_info tegra186_dc_soc_info = { .supports_interlacing = true, .supports_cursor = true, .supports_block_linear = true, + .supports_blending = true, .pitch_align = 64, .has_powergate = false, .broken_reset = false, diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 7dd02f07244f..f4568e054a63 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -55,6 +55,7 @@ struct tegra_dc_soc_info { bool supports_interlacing; bool supports_cursor; bool supports_block_linear; + bool supports_blending; unsigned int pitch_align; bool has_powergate; bool broken_reset; @@ -136,6 +137,7 @@ struct tegra_dc_window { unsigned int bits_per_pixel; unsigned int stride[2]; unsigned long base[3]; + unsigned int zpos; bool bottom_up; struct tegra_bo_tiling tiling; diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 2fa1b48e14d2..52468f669277 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -33,6 +33,29 @@ struct tegra_drm_file { struct mutex lock; }; +static int tegra_atomic_check(struct drm_device *drm, + struct drm_atomic_state *state) +{ + int err; + + err = drm_atomic_helper_check_modeset(drm, state); + if (err < 0) + return err; + + err = drm_atomic_normalize_zpos(drm, state); + if (err < 0) + return err; + + err = drm_atomic_helper_check_planes(drm, state); + if (err < 0) + return err; + + if (state->legacy_cursor_update) + state->async_update = !drm_atomic_helper_async_check(drm, state); + + return 0; +} + static struct drm_atomic_state * tegra_atomic_state_alloc(struct drm_device *drm) { @@ -67,7 +90,7 @@ static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = { #ifdef CONFIG_DRM_FBDEV_EMULATION .output_poll_changed = tegra_fb_output_poll_changed, #endif - .atomic_check = drm_atomic_helper_check, + .atomic_check = tegra_atomic_check, .atomic_commit = drm_atomic_helper_commit, .atomic_state_alloc = tegra_atomic_state_alloc, .atomic_state_clear = tegra_atomic_state_clear, diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c index 63541dbd864d..90bcf24b1523 100644 --- a/drivers/gpu/drm/tegra/hub.c +++ b/drivers/gpu/drm/tegra/hub.c @@ -399,6 +399,7 @@ static void tegra_shared_plane_atomic_update(struct drm_plane *plane, { struct tegra_plane_state *state = to_tegra_plane_state(plane->state); struct tegra_dc *dc = to_tegra_dc(plane->state->crtc); + unsigned int zpos = plane->state->normalized_zpos; struct drm_framebuffer *fb = plane->state->fb; struct tegra_plane *p = to_tegra_plane(plane); struct tegra_bo *bo; @@ -431,7 +432,7 @@ static void tegra_shared_plane_atomic_update(struct drm_plane *plane, BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC; tegra_plane_writel(p, value, DC_WIN_BLEND_NOMATCH_SELECT); - value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(p->depth); + value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - zpos); tegra_plane_writel(p, value, DC_WIN_BLEND_LAYER_CONTROL); /* bypass scaling */ @@ -536,7 +537,6 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm, plane->base.offset = 0x0a00 + 0x0300 * index; plane->base.index = index; - plane->base.depth = 0; plane->wgrp = &hub->wgrps[wgrp]; plane->wgrp->parent = dc->dev; @@ -555,6 +555,7 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm, } drm_plane_helper_add(p, &tegra_shared_plane_helper_funcs); + drm_plane_create_zpos_property(p, 0, 0, 255); return p; } diff --git a/drivers/gpu/drm/tegra/plane.h b/drivers/gpu/drm/tegra/plane.h index 13b900d777e8..dca66cb95d25 100644 --- a/drivers/gpu/drm/tegra/plane.h +++ b/drivers/gpu/drm/tegra/plane.h @@ -19,7 +19,6 @@ struct tegra_plane { struct tegra_dc *dc; unsigned int offset; unsigned int index; - unsigned int depth; }; struct tegra_cursor { -- cgit v1.2.3 From 41c3068cc2fd0ce179b910c4500934786e8156ba Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 14 Nov 2017 16:11:28 +0100 Subject: gpu: host1x: Use IOMMU groups Use IOMMU groups to attach the host1x device to its IOMMU domain. This is not strictly necessary because the domain isn't shared with any other device, but it makes the code consistent with how IOMMU is handled in other drivers and provides an easy way to detect when no IOMMU has been attached via device tree. Signed-off-by: Thierry Reding --- drivers/gpu/host1x/dev.c | 22 +++++++++++++++------- drivers/gpu/host1x/dev.h | 1 + 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c index 1f916b579e95..03db71173f5d 100644 --- a/drivers/gpu/host1x/dev.c +++ b/drivers/gpu/host1x/dev.c @@ -218,19 +218,24 @@ static int host1x_probe(struct platform_device *pdev) return err; } - if (iommu_present(&platform_bus_type)) { + host->group = iommu_group_get(&pdev->dev); + if (host->group) { struct iommu_domain_geometry *geometry; unsigned long order; host->domain = iommu_domain_alloc(&platform_bus_type); - if (!host->domain) - return -ENOMEM; + if (!host->domain) { + err = -ENOMEM; + goto put_group; + } - err = iommu_attach_device(host->domain, &pdev->dev); + err = iommu_attach_group(host->domain, host->group); if (err) { if (err == -ENODEV) { iommu_domain_free(host->domain); host->domain = NULL; + iommu_group_put(host->group); + host->group = NULL; goto skip_iommu; } @@ -296,13 +301,15 @@ fail_unprepare_disable: fail_free_channels: host1x_channel_list_free(&host->channel_list); fail_detach_device: - if (host->domain) { + if (host->group && host->domain) { put_iova_domain(&host->iova); - iommu_detach_device(host->domain, &pdev->dev); + iommu_detach_group(host->domain, host->group); } fail_free_domain: if (host->domain) iommu_domain_free(host->domain); +put_group: + iommu_group_put(host->group); return err; } @@ -319,8 +326,9 @@ static int host1x_remove(struct platform_device *pdev) if (host->domain) { put_iova_domain(&host->iova); - iommu_detach_device(host->domain, &pdev->dev); + iommu_detach_group(host->domain, host->group); iommu_domain_free(host->domain); + iommu_group_put(host->group); } return 0; diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h index 502769726480..43e9fabb43a1 100644 --- a/drivers/gpu/host1x/dev.h +++ b/drivers/gpu/host1x/dev.h @@ -117,6 +117,7 @@ struct host1x { struct clk *clk; struct reset_control *rst; + struct iommu_group *group; struct iommu_domain *domain; struct iova_domain iova; dma_addr_t iova_end; -- cgit v1.2.3 From bc8828bd08bd2a645caeb64d299d67faca7a3b4f Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 12 Oct 2017 17:43:33 +0200 Subject: drm/tegra: Use IOMMU groups In order to support IOMMUs more generically and transparently handle the ARM SMMU on Tegra186, move to using groups instead of devices for domain attachment. An IOMMU group is a set of devices that share the same IOMMU domain and is therefore a good match to represent what Tegra DRM needs. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 27 +++++++++++++++++---------- drivers/gpu/drm/tegra/drm.h | 1 + drivers/gpu/drm/tegra/vic.c | 18 ++++++++++-------- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index d118094a96c1..dc91911094fc 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1748,6 +1748,7 @@ static irqreturn_t tegra_dc_irq(int irq, void *data) static int tegra_dc_init(struct host1x_client *client) { struct drm_device *drm = dev_get_drvdata(client->parent); + struct iommu_group *group = iommu_group_get(client->dev); unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED; struct tegra_dc *dc = host1x_client_to_dc(client); struct tegra_drm *tegra = drm->dev_private; @@ -1759,12 +1760,17 @@ static int tegra_dc_init(struct host1x_client *client) if (!dc->syncpt) dev_warn(dc->dev, "failed to allocate syncpoint\n"); - if (tegra->domain) { - err = iommu_attach_device(tegra->domain, dc->dev); - if (err < 0) { - dev_err(dc->dev, "failed to attach to domain: %d\n", - err); - return err; + if (group && tegra->domain) { + if (group != tegra->group) { + err = iommu_attach_group(tegra->domain, group); + if (err < 0) { + dev_err(dc->dev, + "failed to attach to domain: %d\n", + err); + return err; + } + + tegra->group = group; } dc->domain = tegra->domain; @@ -1825,8 +1831,8 @@ cleanup: if (!IS_ERR(primary)) drm_plane_cleanup(primary); - if (tegra->domain) { - iommu_detach_device(tegra->domain, dc->dev); + if (group && tegra->domain) { + iommu_detach_group(tegra->domain, group); dc->domain = NULL; } @@ -1835,6 +1841,7 @@ cleanup: static int tegra_dc_exit(struct host1x_client *client) { + struct iommu_group *group = iommu_group_get(client->dev); struct tegra_dc *dc = host1x_client_to_dc(client); int err; @@ -1846,8 +1853,8 @@ static int tegra_dc_exit(struct host1x_client *client) return err; } - if (dc->domain) { - iommu_detach_device(dc->domain, dc->dev); + if (group && dc->domain) { + iommu_detach_group(dc->domain, group); dc->domain = NULL; } diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index c52bc5978d1c..da3d8c141aee 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -60,6 +60,7 @@ struct tegra_drm { struct drm_device *drm; struct iommu_domain *domain; + struct iommu_group *group; struct mutex mm_lock; struct drm_mm mm; diff --git a/drivers/gpu/drm/tegra/vic.c b/drivers/gpu/drm/tegra/vic.c index d9664a34fb43..f5794dd49f3b 100644 --- a/drivers/gpu/drm/tegra/vic.c +++ b/drivers/gpu/drm/tegra/vic.c @@ -138,13 +138,14 @@ static const struct falcon_ops vic_falcon_ops = { static int vic_init(struct host1x_client *client) { struct tegra_drm_client *drm = host1x_to_drm_client(client); + struct iommu_group *group = iommu_group_get(client->dev); struct drm_device *dev = dev_get_drvdata(client->parent); struct tegra_drm *tegra = dev->dev_private; struct vic *vic = to_vic(drm); int err; - if (tegra->domain) { - err = iommu_attach_device(tegra->domain, vic->dev); + if (group && tegra->domain) { + err = iommu_attach_group(tegra->domain, group); if (err < 0) { dev_err(vic->dev, "failed to attach to domain: %d\n", err); @@ -158,13 +159,13 @@ static int vic_init(struct host1x_client *client) vic->falcon.data = tegra; err = falcon_load_firmware(&vic->falcon); if (err < 0) - goto detach_device; + goto detach; } vic->channel = host1x_channel_request(client->dev); if (!vic->channel) { err = -ENOMEM; - goto detach_device; + goto detach; } client->syncpts[0] = host1x_syncpt_request(client, 0); @@ -183,9 +184,9 @@ free_syncpt: host1x_syncpt_free(client->syncpts[0]); free_channel: host1x_channel_put(vic->channel); -detach_device: - if (tegra->domain) - iommu_detach_device(tegra->domain, vic->dev); +detach: + if (group && tegra->domain) + iommu_detach_group(tegra->domain, group); return err; } @@ -193,6 +194,7 @@ detach_device: static int vic_exit(struct host1x_client *client) { struct tegra_drm_client *drm = host1x_to_drm_client(client); + struct iommu_group *group = iommu_group_get(client->dev); struct drm_device *dev = dev_get_drvdata(client->parent); struct tegra_drm *tegra = dev->dev_private; struct vic *vic = to_vic(drm); @@ -206,7 +208,7 @@ static int vic_exit(struct host1x_client *client) host1x_channel_put(vic->channel); if (vic->domain) { - iommu_detach_device(vic->domain, vic->dev); + iommu_detach_group(vic->domain, group); vic->domain = NULL; } -- cgit v1.2.3 From 013e2b722aa9b18649dbae8422ced270c793f9d4 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 15 Dec 2017 13:51:52 +0100 Subject: drm/tegra: Mark Tegra186 display hub PM functions __maybe_unused The newly introduced driver has optional suspend/resume functions, causing a warning when CONFIG_PM is disabled: drivers/gpu/drm/tegra/hub.c:749:12: error: 'tegra_display_hub_resume' defined but not used [-Werror=unused-function] drivers/gpu/drm/tegra/hub.c:733:12: error: 'tegra_display_hub_suspend' defined but not used [-Werror=unused-function] This marks them __maybe_unused to shut up the warnings. Fixes: c4755fb9064f ("drm/tegra: Add Tegra186 display hub support") Signed-off-by: Arnd Bergmann Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/hub.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c index 90bcf24b1523..e10a47d57313 100644 --- a/drivers/gpu/drm/tegra/hub.c +++ b/drivers/gpu/drm/tegra/hub.c @@ -728,7 +728,7 @@ static int tegra_display_hub_remove(struct platform_device *pdev) return err; } -static int tegra_display_hub_suspend(struct device *dev) +static int __maybe_unused tegra_display_hub_suspend(struct device *dev) { struct tegra_display_hub *hub = dev_get_drvdata(dev); int err; @@ -744,7 +744,7 @@ static int tegra_display_hub_suspend(struct device *dev) return 0; } -static int tegra_display_hub_resume(struct device *dev) +static int __maybe_unused tegra_display_hub_resume(struct device *dev) { struct tegra_display_hub *hub = dev_get_drvdata(dev); int err; -- cgit v1.2.3 From 72ba4cf52410c0426a299f2ac82acd38c27c8207 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 14 Dec 2017 13:25:37 +0100 Subject: drm/tegra: dpaux: Keep reset defaults for hybrid pad parameters Keep the reset values for the common mode voltage, output driver impedance control and output driver current control parameters. This fixes errors seen during SCDC communication with HDMI sinks. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dpaux.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/tegra/dpaux.c b/drivers/gpu/drm/tegra/dpaux.c index 312b93c0323c..d84e81ff36ad 100644 --- a/drivers/gpu/drm/tegra/dpaux.c +++ b/drivers/gpu/drm/tegra/dpaux.c @@ -322,6 +322,9 @@ static int tegra_dpaux_pad_config(struct tegra_dpaux *dpaux, unsigned function) case DPAUX_PADCTL_FUNC_I2C: value = DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV | DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV | + DPAUX_HYBRID_PADCTL_AUX_CMH(2) | + DPAUX_HYBRID_PADCTL_AUX_DRVZ(4) | + DPAUX_HYBRID_PADCTL_AUX_DRVI(0x18) | DPAUX_HYBRID_PADCTL_MODE_I2C; break; -- cgit v1.2.3 From 39f55c61da49ba41bba4185043cba6f39229467f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 18 Dec 2017 14:44:46 +0100 Subject: drm/tegra: Fix non-debugfs builds The new debugfs registration fails to build when CONFIG_DEBUGFS is disabled, because the drm_crtc structure is lacking a member in that configuration: drivers/gpu/drm/tegra/dc.c: In function 'tegra_dc_late_register': drivers/gpu/drm/tegra/dc.c:1204:28: error: 'struct drm_crtc' has no member named 'debugfs_entry' Without CONFIG_DEBUGFS, the rest of the function already degrades into nothing, so we just avoid the one assignment. Fixes: b95800eeef09 ("drm/tegra: dc: Register debugfs in ->late_register()") Signed-off-by: Arnd Bergmann Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index dc91911094fc..a5fd18c45800 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1242,10 +1242,16 @@ static int tegra_dc_late_register(struct drm_crtc *crtc) { unsigned int i, count = ARRAY_SIZE(debugfs_files); struct drm_minor *minor = crtc->dev->primary; - struct dentry *root = crtc->debugfs_entry; + struct dentry *root; struct tegra_dc *dc = to_tegra_dc(crtc); int err; +#ifdef CONFIG_DEBUG_FS + root = crtc->debugfs_entry; +#else + root = NULL; +#endif + dc->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), GFP_KERNEL); if (!dc->debugfs_files) -- cgit v1.2.3 From f68ba6912bd24088f067da2470880892abc2ac58 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 20 Dec 2017 18:46:10 +0300 Subject: drm/tegra: dc: Link DC1 to DC0 on Tegra20 Hardware reset isn't actually broken on Tegra20, but there is a dependency on the first display controller to be taken out of reset for the second to be enabled successfully. Model this dependency using a PM device link. Signed-off-by: Dmitry Osipenko [treding@nvidia.com: minor cleanups, extend commit message] Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 94 +++++++++++++++++++++++++++++++--------------- drivers/gpu/drm/tegra/dc.h | 2 +- 2 files changed, 65 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index a5fd18c45800..2a0c1e93f82e 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1882,7 +1882,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = { .supports_blending = false, .pitch_align = 8, .has_powergate = false, - .broken_reset = true, + .coupled_pm = true, .has_nvdisplay = false, .num_primary_formats = ARRAY_SIZE(tegra20_primary_formats), .primary_formats = tegra20_primary_formats, @@ -1898,7 +1898,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = { .supports_blending = false, .pitch_align = 8, .has_powergate = false, - .broken_reset = false, + .coupled_pm = false, .has_nvdisplay = false, .num_primary_formats = ARRAY_SIZE(tegra20_primary_formats), .primary_formats = tegra20_primary_formats, @@ -1914,7 +1914,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = { .supports_blending = false, .pitch_align = 64, .has_powergate = true, - .broken_reset = false, + .coupled_pm = false, .has_nvdisplay = false, .num_primary_formats = ARRAY_SIZE(tegra114_primary_formats), .primary_formats = tegra114_primary_formats, @@ -1930,7 +1930,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = { .supports_blending = true, .pitch_align = 64, .has_powergate = true, - .broken_reset = false, + .coupled_pm = false, .has_nvdisplay = false, .num_primary_formats = ARRAY_SIZE(tegra124_primary_formats), .primary_formats = tegra114_primary_formats, @@ -1946,7 +1946,7 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = { .supports_blending = true, .pitch_align = 64, .has_powergate = true, - .broken_reset = false, + .coupled_pm = false, .has_nvdisplay = false, .num_primary_formats = ARRAY_SIZE(tegra114_primary_formats), .primary_formats = tegra114_primary_formats, @@ -1996,7 +1996,7 @@ static const struct tegra_dc_soc_info tegra186_dc_soc_info = { .supports_blending = true, .pitch_align = 64, .has_powergate = false, - .broken_reset = false, + .coupled_pm = false, .has_nvdisplay = true, .wgrps = tegra186_dc_wgrps, .num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps), @@ -2064,6 +2064,43 @@ static int tegra_dc_parse_dt(struct tegra_dc *dc) return 0; } +static int tegra_dc_match_by_pipe(struct device *dev, void *data) +{ + struct tegra_dc *dc = dev_get_drvdata(dev); + unsigned int pipe = (unsigned long)data; + + return dc->pipe == pipe; +} + +static int tegra_dc_couple(struct tegra_dc *dc) +{ + /* + * On Tegra20, DC1 requires DC0 to be taken out of reset in order to + * be enabled, otherwise CPU hangs on writing to CMD_DISPLAY_COMMAND / + * POWER_CONTROL registers during CRTC enabling. + */ + if (dc->soc->coupled_pm && dc->pipe == 1) { + u32 flags = DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE; + struct device_link *link; + struct device *partner; + + partner = driver_find_device(dc->dev->driver, NULL, 0, + tegra_dc_match_by_pipe); + if (!partner) + return -EPROBE_DEFER; + + link = device_link_add(dc->dev, partner, flags); + if (!link) { + dev_err(dc->dev, "failed to link controllers\n"); + return -EINVAL; + } + + dev_dbg(dc->dev, "coupled to %s\n", dev_name(partner)); + } + + return 0; +} + static int tegra_dc_probe(struct platform_device *pdev) { struct resource *regs; @@ -2083,6 +2120,10 @@ static int tegra_dc_probe(struct platform_device *pdev) if (err < 0) return err; + err = tegra_dc_couple(dc); + if (err < 0) + return err; + dc->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(dc->clk)) { dev_err(&pdev->dev, "failed to get clock\n"); @@ -2096,21 +2137,19 @@ static int tegra_dc_probe(struct platform_device *pdev) } /* assert reset and disable clock */ - if (!dc->soc->broken_reset) { - err = clk_prepare_enable(dc->clk); - if (err < 0) - return err; + err = clk_prepare_enable(dc->clk); + if (err < 0) + return err; - usleep_range(2000, 4000); + usleep_range(2000, 4000); - err = reset_control_assert(dc->rst); - if (err < 0) - return err; + err = reset_control_assert(dc->rst); + if (err < 0) + return err; - usleep_range(2000, 4000); + usleep_range(2000, 4000); - clk_disable_unprepare(dc->clk); - } + clk_disable_unprepare(dc->clk); if (dc->soc->has_powergate) { if (dc->pipe == 0) @@ -2184,12 +2223,10 @@ static int tegra_dc_suspend(struct device *dev) struct tegra_dc *dc = dev_get_drvdata(dev); int err; - if (!dc->soc->broken_reset) { - err = reset_control_assert(dc->rst); - if (err < 0) { - dev_err(dev, "failed to assert reset: %d\n", err); - return err; - } + err = reset_control_assert(dc->rst); + if (err < 0) { + dev_err(dev, "failed to assert reset: %d\n", err); + return err; } if (dc->soc->has_powergate) @@ -2219,13 +2256,10 @@ static int tegra_dc_resume(struct device *dev) return err; } - if (!dc->soc->broken_reset) { - err = reset_control_deassert(dc->rst); - if (err < 0) { - dev_err(dev, - "failed to deassert reset: %d\n", err); - return err; - } + err = reset_control_deassert(dc->rst); + if (err < 0) { + dev_err(dev, "failed to deassert reset: %d\n", err); + return err; } } diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index f4568e054a63..e2831e96ea96 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -58,7 +58,7 @@ struct tegra_dc_soc_info { bool supports_blending; unsigned int pitch_align; bool has_powergate; - bool broken_reset; + bool coupled_pm; bool has_nvdisplay; const struct tegra_windowgroup_soc *wgrps; unsigned int num_wgrps; -- cgit v1.2.3 From 04184b1fc990ac67777392be87e020e4f1af185c Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 20 Dec 2017 18:46:13 +0300 Subject: drm/tegra: gem: Correct iommu_map_sg() error checking iommu_map_sg() doesn't return a error value, but a size of the requested IOMMU mapping or zero in case of error. Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/gem.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index ab1e53d434e8..49b9bf28f872 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -114,7 +114,7 @@ static const struct host1x_bo_ops tegra_bo_ops = { static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) { int prot = IOMMU_READ | IOMMU_WRITE; - ssize_t err; + int err; if (bo->mm) return -EBUSY; @@ -128,22 +128,21 @@ static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) err = drm_mm_insert_node_generic(&tegra->mm, bo->mm, bo->gem.size, PAGE_SIZE, 0, 0); if (err < 0) { - dev_err(tegra->drm->dev, "out of I/O virtual memory: %zd\n", + dev_err(tegra->drm->dev, "out of I/O virtual memory: %d\n", err); goto unlock; } bo->paddr = bo->mm->start; - err = iommu_map_sg(tegra->domain, bo->paddr, bo->sgt->sgl, - bo->sgt->nents, prot); - if (err < 0) { - dev_err(tegra->drm->dev, "failed to map buffer: %zd\n", err); + bo->size = iommu_map_sg(tegra->domain, bo->paddr, bo->sgt->sgl, + bo->sgt->nents, prot); + if (!bo->size) { + dev_err(tegra->drm->dev, "failed to map buffer\n"); + err = -ENOMEM; goto remove; } - bo->size = err; - mutex_unlock(&tegra->mm_lock); return 0; -- cgit v1.2.3 From 4c69ac12e39d3d623686d1c421b05d604e1c6db9 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 20 Dec 2017 18:46:14 +0300 Subject: drm/tegra: Correct timeout in tegra_syncpt_wait host1x_syncpt_wait() takes timeout value in jiffies, but DRM passes it in milliseconds. Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/drm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 52468f669277..ff931d3508a9 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -661,7 +661,8 @@ static int tegra_syncpt_wait(struct drm_device *drm, void *data, if (!sp) return -EINVAL; - return host1x_syncpt_wait(sp, args->thresh, args->timeout, + return host1x_syncpt_wait(sp, args->thresh, + msecs_to_jiffies(args->timeout), &args->value); } -- cgit v1.2.3 From ebae8d07435ae91314f4a28d69b530d09c625815 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 20 Dec 2017 09:39:14 +0100 Subject: drm/tegra: dc: Implement legacy blending This implements alpha blending on legacy display controllers (Tegra20, Tegra30 and Tegra114). While it's theoretically possible to support the zpos property to enable userspace to specify the Z-order of each plane individually, this is not currently supported and the same fixed Z- order as previously defined is used. Reverts commit 71835caa00e8 ("drm/tegra: fb: Force alpha formats") since the opaque formats are now supported. Reported-by: Dmitry Osipenko Fixes: 7772fdaef939 ("drm/tegra: Support ARGB and ABGR formats") Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 81 +++++++++++++++++++++---- drivers/gpu/drm/tegra/dc.h | 12 ++++ drivers/gpu/drm/tegra/fb.c | 12 ---- drivers/gpu/drm/tegra/plane.c | 138 ++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/tegra/plane.h | 8 +++ 5 files changed, 226 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 2a0c1e93f82e..4507063029e0 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -154,30 +154,53 @@ static inline u32 compute_initial_dda(unsigned int in) static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane) { + u32 background[3] = { + BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE, + BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE, + BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE, + }; + u32 foreground = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255) | + BLEND_COLOR_KEY_NONE; + u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255); + struct tegra_plane_state *state; + unsigned int i; + + state = to_tegra_plane_state(plane->base.state); + + /* alpha contribution is 1 minus sum of overlapping windows */ + for (i = 0; i < 3; i++) { + if (state->dependent[i]) + background[i] |= BLEND_CONTROL_DEPENDENT; + } + + /* enable alpha blending if pixel format has an alpha component */ + if (!state->opaque) + foreground |= BLEND_CONTROL_ALPHA; + /* * Disable blending and assume Window A is the bottom-most window, * Window C is the top-most window and Window B is in the middle. */ - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_NOKEY); - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_1WIN); + tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY); + tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN); switch (plane->index) { case 0: - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_X); - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY); + tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y); + tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY); break; case 1: - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X); - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); - tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY); + tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y); + tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY); break; case 2: - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X); - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_Y); - tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_3WIN_XY); + tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X); + tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_Y); + tegra_plane_writel(plane, foreground, DC_WIN_BLEND_3WIN_XY); break; } } @@ -353,6 +376,11 @@ static const u32 tegra20_primary_formats[] = { DRM_FORMAT_RGBA5551, DRM_FORMAT_ABGR8888, DRM_FORMAT_ARGB8888, + /* non-native formats */ + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_XRGB8888, }; static const u32 tegra114_primary_formats[] = { @@ -409,18 +437,40 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, struct tegra_bo_tiling *tiling = &plane_state->tiling; struct tegra_plane *tegra = to_tegra_plane(plane); struct tegra_dc *dc = to_tegra_dc(state->crtc); + unsigned int format; int err; /* no need for further checks if the plane is being disabled */ if (!state->crtc) return 0; - err = tegra_plane_format(state->fb->format->format, - &plane_state->format, + err = tegra_plane_format(state->fb->format->format, &format, &plane_state->swap); if (err < 0) return err; + /* + * Tegra20 and Tegra30 are special cases here because they support + * only variants of specific formats with an alpha component, but not + * the corresponding opaque formats. However, the opaque formats can + * be emulated by disabling alpha blending for the plane. + */ + if (!dc->soc->supports_blending) { + if (!tegra_plane_format_has_alpha(format)) { + err = tegra_plane_format_get_alpha(format, &format); + if (err < 0) + return err; + + plane_state->opaque = true; + } else { + plane_state->opaque = false; + } + + tegra_plane_check_dependent(tegra, plane_state); + } + + plane_state->format = format; + err = tegra_fb_get_tiling(state->fb, tiling); if (err < 0) return err; @@ -737,6 +787,11 @@ static const u32 tegra20_overlay_formats[] = { DRM_FORMAT_RGBA5551, DRM_FORMAT_ABGR8888, DRM_FORMAT_ARGB8888, + /* non-native formats */ + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_XRGB8888, /* planar formats */ DRM_FORMAT_UYVY, DRM_FORMAT_YUYV, diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index e2831e96ea96..096a81ad6d8d 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -649,8 +649,20 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define DC_WIN_DV_CONTROL 0x70e #define DC_WIN_BLEND_NOKEY 0x70f +#define BLEND_WEIGHT1(x) (((x) & 0xff) << 16) +#define BLEND_WEIGHT0(x) (((x) & 0xff) << 8) + #define DC_WIN_BLEND_1WIN 0x710 +#define BLEND_CONTROL_FIX (0 << 2) +#define BLEND_CONTROL_ALPHA (1 << 2) +#define BLEND_COLOR_KEY_NONE (0 << 0) +#define BLEND_COLOR_KEY_0 (1 << 0) +#define BLEND_COLOR_KEY_1 (2 << 0) +#define BLEND_COLOR_KEY_BOTH (3 << 0) + #define DC_WIN_BLEND_2WIN_X 0x711 +#define BLEND_CONTROL_DEPENDENT (2 << 2) + #define DC_WIN_BLEND_2WIN_Y 0x712 #define DC_WIN_BLEND_3WIN_XY 0x713 diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 1af4ef9241f1..e05fde7172f8 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -254,18 +254,6 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper, cmd.pitches[0] = round_up(sizes->surface_width * bytes_per_pixel, tegra->pitch_align); - /* - * Early generations of Tegra (Tegra20 and Tegra30) do not support any - * of the X* or *X formats, only their A* or *A equivalents. Force the - * legacy framebuffer format to include an alpha component so that the - * framebuffer emulation can be supported on all generations. - */ - if (sizes->surface_bpp == 32 && sizes->surface_depth == 24) - sizes->surface_depth = 32; - - if (sizes->surface_bpp == 16 && sizes->surface_depth == 15) - sizes->surface_depth = 16; - cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c index 9146aead973b..154b4d337d0a 100644 --- a/drivers/gpu/drm/tegra/plane.c +++ b/drivers/gpu/drm/tegra/plane.c @@ -43,6 +43,7 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane) { struct tegra_plane_state *state = to_tegra_plane_state(plane->state); struct tegra_plane_state *copy; + unsigned int i; copy = kmalloc(sizeof(*copy), GFP_KERNEL); if (!copy) @@ -52,6 +53,10 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane) copy->tiling = state->tiling; copy->format = state->format; copy->swap = state->swap; + copy->opaque = state->opaque; + + for (i = 0; i < 3; i++) + copy->dependent[i] = state->dependent[i]; return ©->base; } @@ -238,3 +243,136 @@ bool tegra_plane_format_is_yuv(unsigned int format, bool *planar) return false; } + +static bool __drm_format_has_alpha(u32 format) +{ + switch (format) { + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_ARGB8888: + return true; + } + + return false; +} + +/* + * This is applicable to Tegra20 and Tegra30 only where the opaque formats can + * be emulated using the alpha formats and alpha blending disabled. + */ +bool tegra_plane_format_has_alpha(unsigned int format) +{ + switch (format) { + case WIN_COLOR_DEPTH_B5G5R5A1: + case WIN_COLOR_DEPTH_A1B5G5R5: + case WIN_COLOR_DEPTH_R8G8B8A8: + case WIN_COLOR_DEPTH_B8G8R8A8: + return true; + } + + return false; +} + +int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha) +{ + switch (opaque) { + case WIN_COLOR_DEPTH_B5G5R5X1: + *alpha = WIN_COLOR_DEPTH_B5G5R5A1; + return 0; + + case WIN_COLOR_DEPTH_X1B5G5R5: + *alpha = WIN_COLOR_DEPTH_A1B5G5R5; + return 0; + + case WIN_COLOR_DEPTH_R8G8B8X8: + *alpha = WIN_COLOR_DEPTH_R8G8B8A8; + return 0; + + case WIN_COLOR_DEPTH_B8G8R8X8: + *alpha = WIN_COLOR_DEPTH_B8G8R8A8; + return 0; + } + + return -EINVAL; +} + +unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane, + struct tegra_plane *other) +{ + unsigned int index = 0, i; + + WARN_ON(plane == other); + + for (i = 0; i < 3; i++) { + if (i == plane->index) + continue; + + if (i == other->index) + break; + + index++; + } + + return index; +} + +void tegra_plane_check_dependent(struct tegra_plane *tegra, + struct tegra_plane_state *state) +{ + struct drm_plane_state *old, *new; + struct drm_plane *plane; + unsigned int zpos[2]; + unsigned int i; + + for (i = 0; i < 3; i++) + state->dependent[i] = false; + + for (i = 0; i < 2; i++) + zpos[i] = 0; + + for_each_oldnew_plane_in_state(state->base.state, plane, old, new, i) { + struct tegra_plane *p = to_tegra_plane(plane); + unsigned index; + + /* skip this plane and planes on different CRTCs */ + if (p == tegra || new->crtc != state->base.crtc) + continue; + + index = tegra_plane_get_overlap_index(tegra, p); + + /* + * If any of the other planes is on top of this plane and uses + * a format with an alpha component, mark this plane as being + * dependent, meaning it's alpha value will be 1 minus the sum + * of alpha components of the overlapping planes. + */ + if (p->index > tegra->index) { + if (__drm_format_has_alpha(new->fb->format->format)) + state->dependent[index] = true; + + /* keep track of the Z position */ + zpos[index] = p->index; + } + } + + /* + * The region where three windows overlap is the intersection of the + * two regions where two windows overlap. It contributes to the area + * if any of the windows on top of it have an alpha component. + */ + for (i = 0; i < 2; i++) + state->dependent[2] = state->dependent[2] || + state->dependent[i]; + + /* + * However, if any of the windows on top of this window is opaque, it + * will completely conceal this window within that area, so avoid the + * window from contributing to the area. + */ + for (i = 0; i < 2; i++) { + if (zpos[i] > tegra->index) + state->dependent[2] = state->dependent[2] && + state->dependent[i]; + } +} diff --git a/drivers/gpu/drm/tegra/plane.h b/drivers/gpu/drm/tegra/plane.h index dca66cb95d25..6938719e7e5d 100644 --- a/drivers/gpu/drm/tegra/plane.h +++ b/drivers/gpu/drm/tegra/plane.h @@ -40,6 +40,10 @@ struct tegra_plane_state { struct tegra_bo_tiling tiling; u32 format; u32 swap; + + /* used for legacy blending support only */ + bool opaque; + bool dependent[3]; }; static inline struct tegra_plane_state * @@ -58,5 +62,9 @@ int tegra_plane_state_add(struct tegra_plane *plane, int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap); bool tegra_plane_format_is_yuv(unsigned int format, bool *planar); +bool tegra_plane_format_has_alpha(unsigned int format); +int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha); +void tegra_plane_check_dependent(struct tegra_plane *tegra, + struct tegra_plane_state *state); #endif /* TEGRA_PLANE_H */ -- cgit v1.2.3 From 5467a8b8da9890e6216dd786e7f65cce5f23d014 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 8 Jan 2018 13:40:44 +0100 Subject: drm/tegra: dc: Restore YUV overlay support Commit ebae8d07435a ("drm/tegra: dc: Implement legacy blending") broke support for YUV overlays by accident. The reason is that YUV formats are considered opaque because they have no alpha component, but on the other hand no corresponding format with an alpha component can be returned. In the case of YUV formats, the opaque format is the same as the alpha format, so add the special case to restore YUV overlay support. Reported-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/plane.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c index 154b4d337d0a..36a06a993698 100644 --- a/drivers/gpu/drm/tegra/plane.c +++ b/drivers/gpu/drm/tegra/plane.c @@ -276,6 +276,11 @@ bool tegra_plane_format_has_alpha(unsigned int format) int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha) { + if (tegra_plane_format_is_yuv(opaque, NULL)) { + *alpha = opaque; + return 0; + } + switch (opaque) { case WIN_COLOR_DEPTH_B5G5R5X1: *alpha = WIN_COLOR_DEPTH_B5G5R5A1; -- cgit v1.2.3 From 89f6501825b5bae3d4aaa2447636f9d3a4287a75 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 8 Jan 2018 16:11:00 +0100 Subject: drm/tegra: dc: Fix possible_crtcs mask for planes Cursor and overlay planes use a possible_crtcs mask based on the DC pipe number. However, DRM requires each bit in the mask to correspond to the index of the CRTC, which will be different from the DC pipe number for a configuration where the first display controller is disabled, or where a deferred probe leads to the first display controller being probed after the first. Suggested-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 4507063029e0..7a9c9ff8b4d7 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -572,8 +572,7 @@ static const struct drm_plane_helper_funcs tegra_plane_helper_funcs = { .atomic_update = tegra_plane_atomic_update, }; -static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, - struct tegra_dc *dc) +static unsigned long tegra_plane_get_possible_crtcs(struct drm_device *drm) { /* * Ideally this would use drm_crtc_mask(), but that would require the @@ -587,7 +586,13 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, * of CRTCs that have been registered, and should therefore always be * the same as drm_crtc_index() after registration. */ - unsigned long possible_crtcs = 1 << drm->mode_config.num_crtc; + return 1 << drm->mode_config.num_crtc; +} + +static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, + struct tegra_dc *dc) +{ + unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm); enum drm_plane_type type = DRM_PLANE_TYPE_PRIMARY; struct tegra_plane *plane; unsigned int num_formats; @@ -744,6 +749,7 @@ static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = { static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, struct tegra_dc *dc) { + unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm); struct tegra_plane *plane; unsigned int num_formats; const u32 *formats; @@ -766,7 +772,7 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, num_formats = ARRAY_SIZE(tegra_cursor_plane_formats); formats = tegra_cursor_plane_formats; - err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe, + err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, &tegra_plane_funcs, formats, num_formats, NULL, DRM_PLANE_TYPE_CURSOR, NULL); @@ -860,6 +866,7 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, struct tegra_dc *dc, unsigned int index) { + unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm); struct tegra_plane *plane; unsigned int num_formats; const u32 *formats; @@ -876,7 +883,7 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, num_formats = dc->soc->num_overlay_formats; formats = dc->soc->overlay_formats; - err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe, + err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, &tegra_plane_funcs, formats, num_formats, NULL, DRM_PLANE_TYPE_OVERLAY, NULL); -- cgit v1.2.3 From 8f62142e490d761ceb92b55a7c05bb79294d6c6c Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 8 Jan 2018 16:16:06 +0100 Subject: drm/tegra: dc: Properly cleanup overlay planes The first overlay plane can leak if initialization of the second overlay plane fails. Fix this by properly destroying the first overlay plane on error. Suggested-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 7a9c9ff8b4d7..b8403ed48285 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -937,20 +937,24 @@ static struct drm_plane *tegra_dc_add_shared_planes(struct drm_device *drm, static struct drm_plane *tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc) { - struct drm_plane *plane, *primary; + struct drm_plane *planes[2], *primary; unsigned int i; + int err; primary = tegra_primary_plane_create(drm, dc); if (IS_ERR(primary)) return primary; for (i = 0; i < 2; i++) { - plane = tegra_dc_overlay_plane_create(drm, dc, 1 + i); - if (IS_ERR(plane)) { - /* XXX tegra_plane_destroy() */ - drm_plane_cleanup(primary); - kfree(primary); - return plane; + planes[i] = tegra_dc_overlay_plane_create(drm, dc, 1 + i); + if (IS_ERR(planes[i])) { + err = PTR_ERR(planes[i]); + + while (i--) + tegra_plane_funcs.destroy(planes[i]); + + tegra_plane_funcs.destroy(primary); + return ERR_PTR(err); } } -- cgit v1.2.3