diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-10-29 01:49:53 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-10-29 01:49:53 +0100 |
commit | 53b3b6bbfde6aae8d1ededc86ad4e0e1e00eb5f8 (patch) | |
tree | b29473f21270aefd113b298c9402be8b4b3c91b4 /drivers/gpu/drm/sun4i | |
parent | Merge tag 'vla-v4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ke... (diff) | |
parent | Merge tag 'drm-intel-next-fixes-2018-10-18' of git://anongit.freedesktop.org/... (diff) | |
download | linux-53b3b6bbfde6aae8d1ededc86ad4e0e1e00eb5f8.tar.xz linux-53b3b6bbfde6aae8d1ededc86ad4e0e1e00eb5f8.zip |
Merge tag 'drm-next-2018-10-24' of git://anongit.freedesktop.org/drm/drm
Pull drm updates from Dave Airlie:
"This is going to rebuild more than drm as it adds a new helper to
list.h for doing bulk updates. Seemed like a reasonable addition to
me.
Otherwise the usual merge window stuff lots of i915 and amdgpu, not so
much nouveau, and piles of everything else.
Core:
- Adds a new list.h helper for doing bulk list updates for TTM.
- Don't leak fb address in smem_start to userspace (comes with EXPORT
workaround for people using mali out of tree hacks)
- udmabuf device to turn memfd regions into dma-buf
- Per-plane blend mode property
- ref/unref replacements with get/put
- fbdev conflicting framebuffers code cleaned up
- host-endian format variants
- panel orientation quirk for Acer One 10
bridge:
- TI SN65DSI86 chip support
vkms:
- GEM support.
- Cursor support
amdgpu:
- Merge amdkfd and amdgpu into one module
- CEC over DP AUX support
- Picasso APU support + VCN dynamic powergating
- Raven2 APU support
- Vega20 enablement + kfd support
- ACP powergating improvements
- ABGR/XBGR display support
- VCN jpeg support
- xGMI support
- DC i2c/aux cleanup
- Ycbcr 4:2:0 support
- GPUVM improvements
- Powerplay and powerplay endian fixes
- Display underflow fixes
vmwgfx:
- Move vmwgfx specific TTM code to vmwgfx
- Split out vmwgfx buffer/resource validation code
- Atomic operation rework
bochs:
- use more helpers
- format/byteorder improvements
qxl:
- use more helpers
i915:
- GGTT coherency getparam
- Turn off resource streamer API
- More Icelake enablement + DMC firmware
- Full PPGTT for Ivybridge, Haswell and Valleyview
- DDB distribution based on resolution
- Limited range DP display support
nouveau:
- CEC over DP AUX support
- Initial HDMI 2.0 support
virtio-gpu:
- vmap support for PRIME objects
tegra:
- Initial Tegra194 support
- DMA/IOMMU integration fixes
msm:
- a6xx perf improvements + clock prefix
- GPU preemption optimisations
- a6xx devfreq support
- cursor support
rockchip:
- PX30 support
- rgb output interface support
mediatek:
- HDMI output support on mt2701 and mt7623
rcar-du:
- Interlaced modes on Gen3
- LVDS on R8A77980
- D3 and E3 SoC support
hisilicon:
- misc fixes
mxsfb:
- runtime pm support
sun4i:
- R40 TCON support
- Allwinner A64 support
- R40 HDMI support
omapdrm:
- Driver rework changing display pipeline ordering to use common code
- DMM memory barrier and irq fixes
- Errata workarounds
exynos:
- out-bridge support for LVDS bridge driver
- Samsung 16x16 tiled format support
- Plane alpha and pixel blend mode support
tilcdc:
- suspend/resume update
mali-dp:
- misc updates"
* tag 'drm-next-2018-10-24' of git://anongit.freedesktop.org/drm/drm: (1382 commits)
firmware/dmc/icl: Add missing MODULE_FIRMWARE() for Icelake.
drm/i915/icl: Fix signal_levels
drm/i915/icl: Fix DDI/TC port clk_off bits
drm/i915/icl: create function to identify combophy port
drm/i915/gen9+: Fix initial readout for Y tiled framebuffers
drm/i915: Large page offsets for pread/pwrite
drm/i915/selftests: Disable shrinker across mmap-exhaustion
drm/i915/dp: Link train Fallback on eDP only if fallback link BW can fit panel's native mode
drm/i915: Fix intel_dp_mst_best_encoder()
drm/i915: Skip vcpi allocation for MSTB ports that are gone
drm/i915: Don't unset intel_connector->mst_port
drm/i915: Only reset seqno if actually idle
drm/i915: Use the correct crtc when sanitizing plane mapping
drm/i915: Restore vblank interrupts earlier
drm/i915: Check fb stride against plane max stride
drm/amdgpu/vcn:Fix uninitialized symbol error
drm: panel-orientation-quirks: Add quirk for Acer One 10 (S1003)
drm/amd/amdgpu: Fix debugfs error handling
drm/amdgpu: Update gc_9_0 golden settings.
drm/amd/powerplay: update PPtable with DC BTC and Tvr SocLimit fields
...
Diffstat (limited to 'drivers/gpu/drm/sun4i')
-rw-r--r-- | drivers/gpu/drm/sun4i/sun4i_backend.c | 81 | ||||
-rw-r--r-- | drivers/gpu/drm/sun4i/sun4i_backend.h | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/sun4i/sun4i_drv.c | 20 | ||||
-rw-r--r-- | drivers/gpu/drm/sun4i/sun4i_layer.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/sun4i/sun4i_tcon.c | 203 | ||||
-rw-r--r-- | drivers/gpu/drm/sun4i/sun4i_tcon.h | 27 | ||||
-rw-r--r-- | drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c | 17 | ||||
-rw-r--r-- | drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/sun4i/sun8i_mixer.c | 48 | ||||
-rw-r--r-- | drivers/gpu/drm/sun4i/sun8i_tcon_top.c | 4 |
11 files changed, 329 insertions, 92 deletions
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index d7950b52a1fd..bf49c55b0f2c 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c @@ -34,6 +34,9 @@ struct sun4i_backend_quirks { /* backend <-> TCON muxing selection done in backend */ bool needs_output_muxing; + + /* alpha at the lowest z position is not always supported */ + bool supports_lowest_plane_alpha; }; static const u32 sunxi_rgb2yuv_coef[12] = { @@ -60,32 +63,6 @@ static const u32 sunxi_bt601_yuv2rgb_coef[12] = { 0x000004a7, 0x00000812, 0x00000000, 0x00002eb1, }; -static inline bool sun4i_backend_format_is_planar_yuv(uint32_t format) -{ - switch (format) { - case DRM_FORMAT_YUV411: - case DRM_FORMAT_YUV422: - case DRM_FORMAT_YUV444: - return true; - default: - return false; - } -} - -static inline bool sun4i_backend_format_is_packed_yuv422(uint32_t format) -{ - switch (format) { - case DRM_FORMAT_YUYV: - case DRM_FORMAT_YVYU: - case DRM_FORMAT_UYVY: - case DRM_FORMAT_VYUY: - return true; - - default: - return false; - } -} - static void sun4i_backend_apply_color_correction(struct sunxi_engine *engine) { int i; @@ -215,7 +192,8 @@ static int sun4i_backend_update_yuv_format(struct sun4i_backend *backend, { struct drm_plane_state *state = plane->state; struct drm_framebuffer *fb = state->fb; - uint32_t format = fb->format->format; + const struct drm_format_info *format = fb->format; + const uint32_t fmt = format->format; u32 val = SUN4I_BACKEND_IYUVCTL_EN; int i; @@ -233,16 +211,16 @@ static int sun4i_backend_update_yuv_format(struct sun4i_backend *backend, SUN4I_BACKEND_ATTCTL_REG0_LAY_YUVEN); /* TODO: Add support for the multi-planar YUV formats */ - if (sun4i_backend_format_is_packed_yuv422(format)) + if (format->num_planes == 1) val |= SUN4I_BACKEND_IYUVCTL_FBFMT_PACKED_YUV422; else - DRM_DEBUG_DRIVER("Unsupported YUV format (0x%x)\n", format); + DRM_DEBUG_DRIVER("Unsupported YUV format (0x%x)\n", fmt); /* * Allwinner seems to list the pixel sequence from right to left, while * DRM lists it from left to right. */ - switch (format) { + switch (fmt) { case DRM_FORMAT_YUYV: val |= SUN4I_BACKEND_IYUVCTL_FBPS_VYUY; break; @@ -257,7 +235,7 @@ static int sun4i_backend_update_yuv_format(struct sun4i_backend *backend, break; default: DRM_DEBUG_DRIVER("Unsupported YUV pixel sequence (0x%x)\n", - format); + fmt); } regmap_write(backend->engine.regs, SUN4I_BACKEND_IYUVCTL_REG, val); @@ -457,12 +435,14 @@ static int sun4i_backend_atomic_check(struct sunxi_engine *engine, struct drm_crtc_state *crtc_state) { struct drm_plane_state *plane_states[SUN4I_BACKEND_NUM_LAYERS] = { 0 }; + struct sun4i_backend *backend = engine_to_sun4i_backend(engine); struct drm_atomic_state *state = crtc_state->state; struct drm_device *drm = state->dev; struct drm_plane *plane; unsigned int num_planes = 0; unsigned int num_alpha_planes = 0; unsigned int num_frontend_planes = 0; + unsigned int num_alpha_planes_max = 1; unsigned int num_yuv_planes = 0; unsigned int current_pipe = 0; unsigned int i; @@ -526,33 +506,40 @@ static int sun4i_backend_atomic_check(struct sunxi_engine *engine, * the layer with the highest priority. * * The second step is the actual alpha blending, that takes - * the two pipes as input, and uses the eventual alpha + * the two pipes as input, and uses the potential alpha * component to do the transparency between the two. * - * This two steps scenario makes us unable to guarantee a + * This two-step scenario makes us unable to guarantee a * robust alpha blending between the 4 layers in all * situations, since this means that we need to have one layer * with alpha at the lowest position of our two pipes. * - * However, we cannot even do that, since the hardware has a - * bug where the lowest plane of the lowest pipe (pipe 0, - * priority 0), if it has any alpha, will discard the pixel - * entirely and just display the pixels in the background - * color (black by default). + * However, we cannot even do that on every platform, since + * the hardware has a bug where the lowest plane of the lowest + * pipe (pipe 0, priority 0), if it has any alpha, will + * discard the pixel data entirely and just display the pixels + * in the background color (black by default). * - * This means that we effectively have only three valid - * configurations with alpha, all of them with the alpha being - * on pipe1 with the lowest position, which can be 1, 2 or 3 - * depending on the number of planes and their zpos. + * This means that on the affected platforms, we effectively + * have only three valid configurations with alpha, all of + * them with the alpha being on pipe1 with the lowest + * position, which can be 1, 2 or 3 depending on the number of + * planes and their zpos. */ - if (num_alpha_planes > SUN4I_BACKEND_NUM_ALPHA_LAYERS) { + + /* For platforms that are not affected by the issue described above. */ + if (backend->quirks->supports_lowest_plane_alpha) + num_alpha_planes_max++; + + if (num_alpha_planes > num_alpha_planes_max) { DRM_DEBUG_DRIVER("Too many planes with alpha, rejecting...\n"); return -EINVAL; } /* We can't have an alpha plane at the lowest position */ - if (plane_states[0]->fb->format->has_alpha || - (plane_states[0]->alpha != DRM_BLEND_ALPHA_OPAQUE)) + if (!backend->quirks->supports_lowest_plane_alpha && + (plane_states[0]->fb->format->has_alpha || + (plane_states[0]->alpha != DRM_BLEND_ALPHA_OPAQUE))) return -EINVAL; for (i = 1; i < num_planes; i++) { @@ -876,6 +863,8 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, : SUN4I_BACKEND_MODCTL_OUT_LCD0)); } + backend->quirks = quirks; + return 0; err_disable_ram_clk: @@ -935,9 +924,11 @@ static const struct sun4i_backend_quirks sun6i_backend_quirks = { static const struct sun4i_backend_quirks sun7i_backend_quirks = { .needs_output_muxing = true, + .supports_lowest_plane_alpha = true, }; static const struct sun4i_backend_quirks sun8i_a33_backend_quirks = { + .supports_lowest_plane_alpha = true, }; static const struct sun4i_backend_quirks sun9i_backend_quirks = { diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.h b/drivers/gpu/drm/sun4i/sun4i_backend.h index 4caee0392fa4..e3d4c6035eb2 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.h +++ b/drivers/gpu/drm/sun4i/sun4i_backend.h @@ -167,7 +167,6 @@ #define SUN4I_BACKEND_PIPE_OFF(p) (0x5000 + (0x400 * (p))) #define SUN4I_BACKEND_NUM_LAYERS 4 -#define SUN4I_BACKEND_NUM_ALPHA_LAYERS 1 #define SUN4I_BACKEND_NUM_FRONTEND_LAYERS 1 #define SUN4I_BACKEND_NUM_YUV_PLANES 1 @@ -187,6 +186,8 @@ struct sun4i_backend { /* Protects against races in the frontend teardown */ spinlock_t frontend_lock; bool frontend_teardown; + + const struct sun4i_backend_quirks *quirks; }; static inline struct sun4i_backend * diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index 8b0cd08034e0..1e41c3f5fd6d 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -61,22 +61,6 @@ static struct drm_driver sun4i_drv_driver = { /* Frame Buffer Operations */ }; -static void sun4i_remove_framebuffers(void) -{ - struct apertures_struct *ap; - - ap = alloc_apertures(1); - if (!ap) - return; - - /* The framebuffer can be located anywhere in RAM */ - ap->ranges[0].base = 0; - ap->ranges[0].size = ~0; - - drm_fb_helper_remove_conflicting_framebuffers(ap, "sun4i-drm-fb", false); - kfree(ap); -} - static int sun4i_drv_bind(struct device *dev) { struct drm_device *drm; @@ -119,7 +103,7 @@ static int sun4i_drv_bind(struct device *dev) drm->irq_enabled = true; /* Remove early framebuffers (ie. simplefb) */ - sun4i_remove_framebuffers(); + drm_fb_helper_remove_conflicting_framebuffers(NULL, "sun4i-drm-fb", false); /* Create our framebuffer */ ret = sun4i_framebuffer_init(drm); @@ -418,8 +402,10 @@ static const struct of_device_id sun4i_drv_of_table[] = { { .compatible = "allwinner,sun8i-a33-display-engine" }, { .compatible = "allwinner,sun8i-a83t-display-engine" }, { .compatible = "allwinner,sun8i-h3-display-engine" }, + { .compatible = "allwinner,sun8i-r40-display-engine" }, { .compatible = "allwinner,sun8i-v3s-display-engine" }, { .compatible = "allwinner,sun9i-a80-display-engine" }, + { .compatible = "allwinner,sun50i-a64-display-engine" }, { } }; MODULE_DEVICE_TABLE(of, sun4i_drv_of_table); diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c index 750ad24de1d7..78f77af8805a 100644 --- a/drivers/gpu/drm/sun4i/sun4i_layer.c +++ b/drivers/gpu/drm/sun4i/sun4i_layer.c @@ -35,9 +35,7 @@ static void sun4i_backend_layer_reset(struct drm_plane *plane) state = kzalloc(sizeof(*state), GFP_KERNEL); if (state) { - plane->state = &state->state; - plane->state->plane = plane; - plane->state->alpha = DRM_BLEND_ALPHA_OPAQUE; + __drm_atomic_helper_plane_reset(plane, &state->state); plane->state->zpos = layer->id; } } diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index 3fb084f802e2..c78cd35a1294 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -12,11 +12,13 @@ #include <drm/drmP.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_connector.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_encoder.h> #include <drm/drm_modes.h> #include <drm/drm_of.h> +#include <drm/drm_panel.h> #include <uapi/drm/drm_mode.h> @@ -35,6 +37,7 @@ #include "sun4i_rgb.h" #include "sun4i_tcon.h" #include "sun6i_mipi_dsi.h" +#include "sun8i_tcon_top.h" #include "sunxi_engine.h" static struct drm_connector *sun4i_tcon_get_connector(const struct drm_encoder *encoder) @@ -275,10 +278,64 @@ static void sun4i_tcon0_mode_set_common(struct sun4i_tcon *tcon, SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay)); } +static void sun4i_tcon0_mode_set_dithering(struct sun4i_tcon *tcon, + const struct drm_connector *connector) +{ + u32 bus_format = 0; + u32 val = 0; + + /* XXX Would this ever happen? */ + if (!connector) + return; + + /* + * FIXME: Undocumented bits + * + * The whole dithering process and these parameters are not + * explained in the vendor documents or BSP kernel code. + */ + regmap_write(tcon->regs, SUN4I_TCON0_FRM_SEED_PR_REG, 0x11111111); + regmap_write(tcon->regs, SUN4I_TCON0_FRM_SEED_PG_REG, 0x11111111); + regmap_write(tcon->regs, SUN4I_TCON0_FRM_SEED_PB_REG, 0x11111111); + regmap_write(tcon->regs, SUN4I_TCON0_FRM_SEED_LR_REG, 0x11111111); + regmap_write(tcon->regs, SUN4I_TCON0_FRM_SEED_LG_REG, 0x11111111); + regmap_write(tcon->regs, SUN4I_TCON0_FRM_SEED_LB_REG, 0x11111111); + regmap_write(tcon->regs, SUN4I_TCON0_FRM_TBL0_REG, 0x01010000); + regmap_write(tcon->regs, SUN4I_TCON0_FRM_TBL1_REG, 0x15151111); + regmap_write(tcon->regs, SUN4I_TCON0_FRM_TBL2_REG, 0x57575555); + regmap_write(tcon->regs, SUN4I_TCON0_FRM_TBL3_REG, 0x7f7f7777); + + /* Do dithering if panel only supports 6 bits per color */ + if (connector->display_info.bpc == 6) + val |= SUN4I_TCON0_FRM_CTL_EN; + + if (connector->display_info.num_bus_formats == 1) + bus_format = connector->display_info.bus_formats[0]; + + /* Check the connection format */ + switch (bus_format) { + case MEDIA_BUS_FMT_RGB565_1X16: + /* R and B components are only 5 bits deep */ + val |= SUN4I_TCON0_FRM_CTL_MODE_R; + val |= SUN4I_TCON0_FRM_CTL_MODE_B; + case MEDIA_BUS_FMT_RGB666_1X18: + case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: + /* Fall through: enable dithering */ + val |= SUN4I_TCON0_FRM_CTL_EN; + break; + } + + /* Write dithering settings */ + regmap_write(tcon->regs, SUN4I_TCON_FRM_CTL_REG, val); +} + static void sun4i_tcon0_mode_set_cpu(struct sun4i_tcon *tcon, - struct mipi_dsi_device *device, + const struct drm_encoder *encoder, const struct drm_display_mode *mode) { + /* TODO support normal CPU interface modes */ + struct sun6i_dsi *dsi = encoder_to_sun6i_dsi(encoder); + struct mipi_dsi_device *device = dsi->device; u8 bpp = mipi_dsi_pixel_format_to_bpp(device->format); u8 lanes = device->lanes; u32 block_space, start_delay; @@ -289,6 +346,9 @@ static void sun4i_tcon0_mode_set_cpu(struct sun4i_tcon *tcon, sun4i_tcon0_mode_set_common(tcon, mode); + /* Set dithering if needed */ + sun4i_tcon0_mode_set_dithering(tcon, sun4i_tcon_get_connector(encoder)); + regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, SUN4I_TCON0_CTL_IF_MASK, SUN4I_TCON0_CTL_IF_8080); @@ -354,6 +414,9 @@ static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon, tcon->dclk_max_div = 7; sun4i_tcon0_mode_set_common(tcon, mode); + /* Set dithering if needed */ + sun4i_tcon0_mode_set_dithering(tcon, sun4i_tcon_get_connector(encoder)); + /* Adjust clock delay */ clk_delay = sun4i_tcon_get_clk_delay(mode, 0); regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, @@ -427,6 +490,9 @@ static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, tcon->dclk_max_div = 127; sun4i_tcon0_mode_set_common(tcon, mode); + /* Set dithering if needed */ + sun4i_tcon0_mode_set_dithering(tcon, tcon->panel->connector); + /* Adjust clock delay */ clk_delay = sun4i_tcon_get_clk_delay(mode, 0); regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, @@ -474,6 +540,33 @@ static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, if (mode->flags & DRM_MODE_FLAG_PVSYNC) val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE; + /* + * On A20 and similar SoCs, the only way to achieve Positive Edge + * (Rising Edge), is setting dclk clock phase to 2/3(240°). + * By default TCON works in Negative Edge(Falling Edge), + * this is why phase is set to 0 in that case. + * Unfortunately there's no way to logically invert dclk through + * IO_POL register. + * The only acceptable way to work, triple checked with scope, + * is using clock phase set to 0° for Negative Edge and set to 240° + * for Positive Edge. + * On A33 and similar SoCs there would be a 90° phase option, + * but it divides also dclk by 2. + * Following code is a way to avoid quirks all around TCON + * and DOTCLOCK drivers. + */ + if (!IS_ERR(tcon->panel)) { + struct drm_panel *panel = tcon->panel; + struct drm_connector *connector = panel->connector; + struct drm_display_info display_info = connector->display_info; + + if (display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE) + clk_set_phase(tcon->dclk, 240); + + if (display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE) + clk_set_phase(tcon->dclk, 0); + } + regmap_update_bits(tcon->regs, SUN4I_TCON0_IO_POL_REG, SUN4I_TCON0_IO_POL_HSYNC_POSITIVE | SUN4I_TCON0_IO_POL_VSYNC_POSITIVE, val); @@ -581,16 +674,10 @@ void sun4i_tcon_mode_set(struct sun4i_tcon *tcon, const struct drm_encoder *encoder, const struct drm_display_mode *mode) { - struct sun6i_dsi *dsi; - switch (encoder->encoder_type) { case DRM_MODE_ENCODER_DSI: - /* - * This is not really elegant, but it's the "cleaner" - * way I could think of... - */ - dsi = encoder_to_sun6i_dsi(encoder); - sun4i_tcon0_mode_set_cpu(tcon, dsi->device, mode); + /* DSI is tied to special case of CPU interface */ + sun4i_tcon0_mode_set_cpu(tcon, encoder, mode); break; case DRM_MODE_ENCODER_LVDS: sun4i_tcon0_mode_set_lvds(tcon, encoder, mode); @@ -880,6 +967,37 @@ static struct sunxi_engine *sun4i_tcon_get_engine_by_id(struct sun4i_drv *drv, return ERR_PTR(-EINVAL); } +static bool sun4i_tcon_connected_to_tcon_top(struct device_node *node) +{ + struct device_node *remote; + bool ret = false; + + remote = of_graph_get_remote_node(node, 0, -1); + if (remote) { + ret = !!(IS_ENABLED(CONFIG_DRM_SUN8I_TCON_TOP) && + of_match_node(sun8i_tcon_top_of_table, remote)); + of_node_put(remote); + } + + return ret; +} + +static int sun4i_tcon_get_index(struct sun4i_drv *drv) +{ + struct list_head *pos; + int size = 0; + + /* + * Because TCON is added to the list at the end of the probe + * (after this function is called), index of the current TCON + * will be same as current TCON list size. + */ + list_for_each(pos, &drv->tcon_list) + ++size; + + return size; +} + /* * On SoCs with the old display pipeline design (Display Engine 1.0), * we assumed the TCON was always tied to just one backend. However @@ -928,8 +1046,24 @@ static struct sunxi_engine *sun4i_tcon_find_engine(struct sun4i_drv *drv, * connections between the backend and TCON? */ if (of_get_child_count(port) > 1) { - /* Get our ID directly from an upstream endpoint */ - int id = sun4i_tcon_of_get_id_from_port(port); + int id; + + /* + * When pipeline has the same number of TCONs and engines which + * are represented by frontends/backends (DE1) or mixers (DE2), + * we match them by their respective IDs. However, if pipeline + * contains TCON TOP, chances are that there are either more + * TCONs than engines (R40) or TCONs with non-consecutive ids. + * (H6). In that case it's easier just use TCON index in list + * as an id. That means that on R40, any 2 TCONs can be enabled + * in DT out of 4 (there are 2 mixers). Due to the design of + * TCON TOP, remaining 2 TCONs can't be connected to anything + * anyway. + */ + if (sun4i_tcon_connected_to_tcon_top(node)) + id = sun4i_tcon_get_index(drv); + else + id = sun4i_tcon_of_get_id_from_port(port); /* Get our engine by matching our ID */ engine = sun4i_tcon_get_engine_by_id(drv, id); @@ -1244,6 +1378,47 @@ static int sun6i_tcon_set_mux(struct sun4i_tcon *tcon, return 0; } +static int sun8i_r40_tcon_tv_set_mux(struct sun4i_tcon *tcon, + const struct drm_encoder *encoder) +{ + struct device_node *port, *remote; + struct platform_device *pdev; + int id, ret; + + /* find TCON TOP platform device and TCON id */ + + port = of_graph_get_port_by_id(tcon->dev->of_node, 0); + if (!port) + return -EINVAL; + + id = sun4i_tcon_of_get_id_from_port(port); + of_node_put(port); + + remote = of_graph_get_remote_node(tcon->dev->of_node, 0, -1); + if (!remote) + return -EINVAL; + + pdev = of_find_device_by_node(remote); + of_node_put(remote); + if (!pdev) + return -EINVAL; + + if (IS_ENABLED(CONFIG_DRM_SUN8I_TCON_TOP) && + encoder->encoder_type == DRM_MODE_ENCODER_TMDS) { + ret = sun8i_tcon_top_set_hdmi_src(&pdev->dev, id); + if (ret) + return ret; + } + + if (IS_ENABLED(CONFIG_DRM_SUN8I_TCON_TOP)) { + ret = sun8i_tcon_top_de_config(&pdev->dev, tcon->id, id); + if (ret) + return ret; + } + + return 0; +} + static const struct sun4i_tcon_quirks sun4i_a10_quirks = { .has_channel_0 = true, .has_channel_1 = true, @@ -1291,6 +1466,11 @@ static const struct sun4i_tcon_quirks sun8i_a83t_tv_quirks = { .has_channel_1 = true, }; +static const struct sun4i_tcon_quirks sun8i_r40_tv_quirks = { + .has_channel_1 = true, + .set_mux = sun8i_r40_tcon_tv_set_mux, +}; + static const struct sun4i_tcon_quirks sun8i_v3s_quirks = { .has_channel_0 = true, }; @@ -1315,6 +1495,7 @@ const struct of_device_id sun4i_tcon_of_table[] = { { .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks }, { .compatible = "allwinner,sun8i-a83t-tcon-lcd", .data = &sun8i_a83t_lcd_quirks }, { .compatible = "allwinner,sun8i-a83t-tcon-tv", .data = &sun8i_a83t_tv_quirks }, + { .compatible = "allwinner,sun8i-r40-tcon-tv", .data = &sun8i_r40_tv_quirks }, { .compatible = "allwinner,sun8i-v3s-tcon", .data = &sun8i_v3s_quirks }, { .compatible = "allwinner,sun9i-a80-tcon-lcd", .data = &sun9i_a80_tcon_lcd_quirks }, { .compatible = "allwinner,sun9i-a80-tcon-tv", .data = &sun9i_a80_tcon_tv_quirks }, diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h index f6a071cd5a6f..3d492c8be1fc 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.h +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h @@ -37,18 +37,21 @@ #define SUN4I_TCON_GINT1_REG 0x8 #define SUN4I_TCON_FRM_CTL_REG 0x10 -#define SUN4I_TCON_FRM_CTL_EN BIT(31) - -#define SUN4I_TCON_FRM_SEED_PR_REG 0x14 -#define SUN4I_TCON_FRM_SEED_PG_REG 0x18 -#define SUN4I_TCON_FRM_SEED_PB_REG 0x1c -#define SUN4I_TCON_FRM_SEED_LR_REG 0x20 -#define SUN4I_TCON_FRM_SEED_LG_REG 0x24 -#define SUN4I_TCON_FRM_SEED_LB_REG 0x28 -#define SUN4I_TCON_FRM_TBL0_REG 0x2c -#define SUN4I_TCON_FRM_TBL1_REG 0x30 -#define SUN4I_TCON_FRM_TBL2_REG 0x34 -#define SUN4I_TCON_FRM_TBL3_REG 0x38 +#define SUN4I_TCON0_FRM_CTL_EN BIT(31) +#define SUN4I_TCON0_FRM_CTL_MODE_R BIT(6) +#define SUN4I_TCON0_FRM_CTL_MODE_G BIT(5) +#define SUN4I_TCON0_FRM_CTL_MODE_B BIT(4) + +#define SUN4I_TCON0_FRM_SEED_PR_REG 0x14 +#define SUN4I_TCON0_FRM_SEED_PG_REG 0x18 +#define SUN4I_TCON0_FRM_SEED_PB_REG 0x1c +#define SUN4I_TCON0_FRM_SEED_LR_REG 0x20 +#define SUN4I_TCON0_FRM_SEED_LG_REG 0x24 +#define SUN4I_TCON0_FRM_SEED_LB_REG 0x28 +#define SUN4I_TCON0_FRM_TBL0_REG 0x2c +#define SUN4I_TCON0_FRM_TBL1_REG 0x30 +#define SUN4I_TCON0_FRM_TBL2_REG 0x34 +#define SUN4I_TCON0_FRM_TBL3_REG 0x38 #define SUN4I_TCON0_CTL_REG 0x40 #define SUN4I_TCON0_CTL_TCON_ENABLE BIT(31) diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c index 31875b636434..ed2983770e9c 100644 --- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c +++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c @@ -125,10 +125,22 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master, return PTR_ERR(hdmi->clk_tmds); } + hdmi->regulator = devm_regulator_get(dev, "hvcc"); + if (IS_ERR(hdmi->regulator)) { + dev_err(dev, "Couldn't get regulator\n"); + return PTR_ERR(hdmi->regulator); + } + + ret = regulator_enable(hdmi->regulator); + if (ret) { + dev_err(dev, "Failed to enable regulator\n"); + return ret; + } + ret = reset_control_deassert(hdmi->rst_ctrl); if (ret) { dev_err(dev, "Could not deassert ctrl reset control\n"); - return ret; + goto err_disable_regulator; } ret = clk_prepare_enable(hdmi->clk_tmds); @@ -183,6 +195,8 @@ err_disable_clk_tmds: clk_disable_unprepare(hdmi->clk_tmds); err_assert_ctrl_reset: reset_control_assert(hdmi->rst_ctrl); +err_disable_regulator: + regulator_disable(hdmi->regulator); return ret; } @@ -196,6 +210,7 @@ static void sun8i_dw_hdmi_unbind(struct device *dev, struct device *master, sun8i_hdmi_phy_remove(hdmi); clk_disable_unprepare(hdmi->clk_tmds); reset_control_assert(hdmi->rst_ctrl); + regulator_disable(hdmi->regulator); } static const struct component_ops sun8i_dw_hdmi_ops = { diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h index aadbe0a10b0c..7fdc1ecd2892 100644 --- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h +++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h @@ -10,6 +10,7 @@ #include <drm/drm_encoder.h> #include <linux/clk.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> #include <linux/reset.h> #define SUN8I_HDMI_PHY_DBG_CTRL_REG 0x0000 @@ -176,6 +177,7 @@ struct sun8i_dw_hdmi { struct drm_encoder encoder; struct sun8i_hdmi_phy *phy; struct dw_hdmi_plat_data plat_data; + struct regulator *regulator; struct reset_control *rst_ctrl; }; diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c index a564b5dfe082..471993097ced 100644 --- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c +++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c @@ -416,6 +416,14 @@ static const struct sun8i_hdmi_phy_variant sun8i_h3_hdmi_phy = { .phy_config = &sun8i_hdmi_phy_config_h3, }; +static const struct sun8i_hdmi_phy_variant sun8i_r40_hdmi_phy = { + .has_phy_clk = true, + .has_second_pll = true, + .phy_init = &sun8i_hdmi_phy_init_h3, + .phy_disable = &sun8i_hdmi_phy_disable_h3, + .phy_config = &sun8i_hdmi_phy_config_h3, +}; + static const struct of_device_id sun8i_hdmi_phy_of_table[] = { { .compatible = "allwinner,sun50i-a64-hdmi-phy", @@ -429,6 +437,10 @@ static const struct of_device_id sun8i_hdmi_phy_of_table[] = { .compatible = "allwinner,sun8i-h3-hdmi-phy", .data = &sun8i_h3_hdmi_phy, }, + { + .compatible = "allwinner,sun8i-r40-hdmi-phy", + .data = &sun8i_r40_hdmi_phy, + }, { /* sentinel */ } }; diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c index cb65b0ed53fd..8b3d02b146b7 100644 --- a/drivers/gpu/drm/sun4i/sun8i_mixer.c +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c @@ -545,6 +545,22 @@ static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = { .vi_num = 1, }; +static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = { + .ccsc = 0, + .mod_rate = 297000000, + .scaler_mask = 0xf, + .ui_num = 3, + .vi_num = 1, +}; + +static const struct sun8i_mixer_cfg sun8i_r40_mixer1_cfg = { + .ccsc = 1, + .mod_rate = 297000000, + .scaler_mask = 0x3, + .ui_num = 1, + .vi_num = 1, +}; + static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = { .vi_num = 2, .ui_num = 1, @@ -553,6 +569,22 @@ static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = { .mod_rate = 150000000, }; +static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = { + .ccsc = 0, + .mod_rate = 297000000, + .scaler_mask = 0xf, + .ui_num = 3, + .vi_num = 1, +}; + +static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = { + .ccsc = 1, + .mod_rate = 297000000, + .scaler_mask = 0x3, + .ui_num = 1, + .vi_num = 1, +}; + static const struct of_device_id sun8i_mixer_of_table[] = { { .compatible = "allwinner,sun8i-a83t-de2-mixer-0", @@ -567,9 +599,25 @@ static const struct of_device_id sun8i_mixer_of_table[] = { .data = &sun8i_h3_mixer0_cfg, }, { + .compatible = "allwinner,sun8i-r40-de2-mixer-0", + .data = &sun8i_r40_mixer0_cfg, + }, + { + .compatible = "allwinner,sun8i-r40-de2-mixer-1", + .data = &sun8i_r40_mixer1_cfg, + }, + { .compatible = "allwinner,sun8i-v3s-de2-mixer", .data = &sun8i_v3s_mixer_cfg, }, + { + .compatible = "allwinner,sun50i-a64-de2-mixer-0", + .data = &sun50i_a64_mixer0_cfg, + }, + { + .compatible = "allwinner,sun50i-a64-de2-mixer-1", + .data = &sun50i_a64_mixer1_cfg, + }, { } }; MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table); diff --git a/drivers/gpu/drm/sun4i/sun8i_tcon_top.c b/drivers/gpu/drm/sun4i/sun8i_tcon_top.c index d5240b777a8f..3040a79f298f 100644 --- a/drivers/gpu/drm/sun4i/sun8i_tcon_top.c +++ b/drivers/gpu/drm/sun4i/sun8i_tcon_top.c @@ -129,8 +129,7 @@ static int sun8i_tcon_top_bind(struct device *dev, struct device *master, if (!tcon_top) return -ENOMEM; - clk_data = devm_kzalloc(dev, sizeof(*clk_data) + - sizeof(*clk_data->hws) * CLK_NUM, + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, CLK_NUM), GFP_KERNEL); if (!clk_data) return -ENOMEM; @@ -253,6 +252,7 @@ static int sun8i_tcon_top_remove(struct platform_device *pdev) /* sun4i_drv uses this list to check if a device node is a TCON TOP */ const struct of_device_id sun8i_tcon_top_of_table[] = { + { .compatible = "allwinner,sun8i-r40-tcon-top" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sun8i_tcon_top_of_table); |