diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-07-09 18:48:37 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-07-09 18:48:37 -0700 |
commit | af3c8d98508d37541d4bf57f13a984a7f73a328c (patch) | |
tree | e8dd974d6ebccd38b1e373be8a5e4a2f8bf3c6ce /drivers/gpu/drm/sun4i/sun4i_tcon.c | |
parent | d3e3b7eac886fb1383db2f22b81550fa6d87f62f (diff) | |
parent | 00fc2c26bc46a64545cdf95a1511461ea9acecb4 (diff) |
Merge tag 'drm-for-v4.13' of git://people.freedesktop.org/~airlied/linux
Pull drm updates from Dave Airlie:
"This is the main pull request for the drm, I think I've got one later
driver pull for mediatek SoC driver, I'm undecided on if it needs to
go to you yet.
Otherwise summary below:
Core drm:
- Atomic add driver private objects
- Deprecate preclose hook in modern drivers
- MST bandwidth tracking
- Use kvmalloc in more places
- Add mode_valid hook for crtc/encoder/bridge
- Reduce sync_file construction time
- Documentation updates
- New DRM synchronisation object support
New drivers:
- pl111 - pl111 CLCD display controller
Panel:
- Innolux P079ZCA panel driver
- Add NL12880B20-05, NL192108AC18-02D, P320HVN03 panels
- panel-samsung-s6e3ha2: Add s6e3hf2 panel support
i915:
- SKL+ watermark fixes
- G4x/G33 reset improvements
- DP AUX backlight improvements
- Buffer based GuC/host communication
- New getparam for (sub)slice infomation
- Cannonlake and Coffeelake initial patches
- Execbuf optimisations
radeon/amdgpu:
- Lots of Vega10 bug fixes
- Preliminary raven support
- KIQ support for compute rings
- MEC queue management rework
- DCE6 Audio support
- SR-IOV improvements
- Better radeon/amdgpu selection support
nouveau:
- HDMI stereoscopic support
- Display code rework for >= GM20x GPUs
msm:
- GEM rework for fine-grained locking
- Per-process pagetable work
- HDMI fixes for Snapdragon 820.
vc4:
- Remove 256MB CMA limit from vc4
- Add out-fence support
- Add support for cygnus
- Get/set tiling ioctls support
- Add T-format tiling support for scanout
zte:
- add VGA support.
etnaviv:
- Thermal throttle support for newer GPUs
- Restore userspace buffer cache performance
- dma-buf sync fix
stm:
- add stm32f429 display support
exynos:
- Rework vblank handling
- Fixup sw-trigger code
sun4i:
- V3s display engine support
- HDMI support for older SoCs
- Preliminary work on dual-pipeline SoCs.
rcar-du:
- VSP work
imx-drm:
- Remove counter load enable from PRE
- Double read/write reduction flag support
tegra:
- Documentation for the host1x and drm driver.
- Lots of staging ioctl fixes due to grate project work.
omapdrm:
- dma-buf fence support
- TILER rotation fixes"
* tag 'drm-for-v4.13' of git://people.freedesktop.org/~airlied/linux: (1270 commits)
drm: Remove unused drm_file parameter to drm_syncobj_replace_fence()
drm/amd/powerplay: fix bug fail to remove sysfs when rmmod amdgpu.
amdgpu: Set cik/si_support to 1 by default if radeon isn't built
drm/amdgpu/gfx9: fix driver reload with KIQ
drm/amdgpu/gfx8: fix driver reload with KIQ
drm/amdgpu: Don't call amd_powerplay_destroy() if we don't have powerplay
drm/ttm: Fix use-after-free in ttm_bo_clean_mm
drm/amd/amdgpu: move get memory type function from early init to sw init
drm/amdgpu/cgs: always set reference clock in mode_info
drm/amdgpu: fix vblank_time when displays are off
drm/amd/powerplay: power value format change for Vega10
drm/amdgpu/gfx9: support the amdgpu.disable_cu option
drm/amd/powerplay: change PPSMC_MSG_GetCurrPkgPwr for Vega10
drm/amdgpu: Make amdgpu_cs_parser_init static (v2)
drm/amdgpu/cs: fix a typo in a comment
drm/amdgpu: Fix the exported always on CU bitmap
drm/amdgpu/gfx9: gfx_v9_0_enable_gfx_static_mg_power_gating() can be static
drm/amdgpu/psp: upper_32_bits/lower_32_bits for address setup
drm/amd/powerplay/cz: print message if smc message fails
drm/amdgpu: fix typo in amdgpu_debugfs_test_ib_init
...
Diffstat (limited to 'drivers/gpu/drm/sun4i/sun4i_tcon.c')
-rw-r--r-- | drivers/gpu/drm/sun4i/sun4i_tcon.c | 142 |
1 files changed, 127 insertions, 15 deletions
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index 9a83a85529ac..d9791292553e 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -30,6 +30,7 @@ #include "sun4i_drv.h" #include "sun4i_rgb.h" #include "sun4i_tcon.h" +#include "sunxi_engine.h" void sun4i_tcon_disable(struct sun4i_tcon *tcon) { @@ -54,6 +55,8 @@ EXPORT_SYMBOL(sun4i_tcon_enable); void sun4i_tcon_channel_disable(struct sun4i_tcon *tcon, int channel) { + DRM_DEBUG_DRIVER("Disabling TCON channel %d\n", channel); + /* Disable the TCON's channel */ if (channel == 0) { regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, @@ -71,6 +74,8 @@ EXPORT_SYMBOL(sun4i_tcon_channel_disable); void sun4i_tcon_channel_enable(struct sun4i_tcon *tcon, int channel) { + DRM_DEBUG_DRIVER("Enabling TCON channel %d\n", channel); + /* Enable the TCON's channel */ if (channel == 0) { regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, @@ -104,6 +109,29 @@ void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable) } EXPORT_SYMBOL(sun4i_tcon_enable_vblank); +void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel, + struct drm_encoder *encoder) +{ + u32 val; + + if (!tcon->quirks->has_unknown_mux) + return; + + if (channel != 1) + return; + + if (encoder->encoder_type == DRM_MODE_ENCODER_TVDAC) + val = 1; + else + val = 0; + + /* + * FIXME: Undocumented bits + */ + regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, val); +} +EXPORT_SYMBOL(sun4i_tcon_set_mux); + static int sun4i_tcon_get_clk_delay(struct drm_display_mode *mode, int channel) { @@ -129,6 +157,9 @@ void sun4i_tcon0_mode_set(struct sun4i_tcon *tcon, u8 clk_delay; u32 val = 0; + /* Configure the dot clock */ + clk_set_rate(tcon->dclk, mode->crtc_clock * 1000); + /* Adjust clock delay */ clk_delay = sun4i_tcon_get_clk_delay(mode, 0); regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, @@ -163,7 +194,7 @@ void sun4i_tcon0_mode_set(struct sun4i_tcon *tcon, /* Set vertical display timings */ regmap_write(tcon->regs, SUN4I_TCON0_BASIC2_REG, - SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal) | + SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal * 2) | SUN4I_TCON0_BASIC2_V_BACKPORCH(bp)); /* Set Hsync and Vsync length */ @@ -198,12 +229,15 @@ EXPORT_SYMBOL(sun4i_tcon0_mode_set); void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon, struct drm_display_mode *mode) { - unsigned int bp, hsync, vsync; + unsigned int bp, hsync, vsync, vtotal; u8 clk_delay; u32 val; WARN_ON(!tcon->quirks->has_channel_1); + /* Configure the dot clock */ + clk_set_rate(tcon->sclk1, mode->crtc_clock * 1000); + /* Adjust clock delay */ clk_delay = sun4i_tcon_get_clk_delay(mode, 1); regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, @@ -235,19 +269,37 @@ void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon, SUN4I_TCON1_BASIC2_Y(mode->crtc_vdisplay)); /* Set horizontal display timings */ - bp = mode->crtc_htotal - mode->crtc_hsync_end; + bp = mode->crtc_htotal - mode->crtc_hsync_start; DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n", mode->htotal, bp); regmap_write(tcon->regs, SUN4I_TCON1_BASIC3_REG, SUN4I_TCON1_BASIC3_H_TOTAL(mode->crtc_htotal) | SUN4I_TCON1_BASIC3_H_BACKPORCH(bp)); - /* Set vertical display timings */ - bp = mode->crtc_vtotal - mode->crtc_vsync_end; + bp = mode->crtc_vtotal - mode->crtc_vsync_start; DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n", - mode->vtotal, bp); + mode->crtc_vtotal, bp); + + /* + * The vertical resolution needs to be doubled in all + * cases. We could use crtc_vtotal and always multiply by two, + * but that leads to a rounding error in interlace when vtotal + * is odd. + * + * This happens with TV's PAL for example, where vtotal will + * be 625, crtc_vtotal 312, and thus crtc_vtotal * 2 will be + * 624, which apparently confuses the hardware. + * + * To work around this, we will always use vtotal, and + * multiply by two only if we're not in interlace. + */ + vtotal = mode->vtotal; + if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) + vtotal = vtotal * 2; + + /* Set vertical display timings */ regmap_write(tcon->regs, SUN4I_TCON1_BASIC4_REG, - SUN4I_TCON1_BASIC4_V_TOTAL(mode->vtotal) | + SUN4I_TCON1_BASIC4_V_TOTAL(vtotal) | SUN4I_TCON1_BASIC4_V_BACKPORCH(bp)); /* Set Hsync and Vsync length */ @@ -262,12 +314,6 @@ void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon, regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, SUN4I_TCON_GCTL_IOMAP_MASK, SUN4I_TCON_GCTL_IOMAP_TCON1); - - /* - * FIXME: Undocumented bits - */ - if (tcon->quirks->has_unknown_mux) - regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, 1); } EXPORT_SYMBOL(sun4i_tcon1_mode_set); @@ -402,21 +448,79 @@ static int sun4i_tcon_init_regmap(struct device *dev, return 0; } +/* + * On SoCs with the old display pipeline design (Display Engine 1.0), + * the TCON is always tied to just one backend. Hence we can traverse + * the of_graph upwards to find the backend our tcon is connected to, + * and take its ID as our own. + * + * We can either identify backends from their compatible strings, which + * means maintaining a large list of them. Or, since the backend is + * registered and binded before the TCON, we can just go through the + * list of registered backends and compare the device node. + * + * As the structures now store engines instead of backends, here this + * function in fact searches the corresponding engine, and the ID is + * requested via the get_id function of the engine. + */ +static struct sunxi_engine *sun4i_tcon_find_engine(struct sun4i_drv *drv, + struct device_node *node) +{ + struct device_node *port, *ep, *remote; + struct sunxi_engine *engine; + + port = of_graph_get_port_by_id(node, 0); + if (!port) + return ERR_PTR(-EINVAL); + + for_each_available_child_of_node(port, ep) { + remote = of_graph_get_remote_port_parent(ep); + if (!remote) + continue; + + /* does this node match any registered engines? */ + list_for_each_entry(engine, &drv->engine_list, list) { + if (remote == engine->node) { + of_node_put(remote); + of_node_put(port); + return engine; + } + } + + /* keep looking through upstream ports */ + engine = sun4i_tcon_find_engine(drv, remote); + if (!IS_ERR(engine)) { + of_node_put(remote); + of_node_put(port); + return engine; + } + } + + return ERR_PTR(-EINVAL); +} + static int sun4i_tcon_bind(struct device *dev, struct device *master, void *data) { struct drm_device *drm = data; struct sun4i_drv *drv = drm->dev_private; + struct sunxi_engine *engine; struct sun4i_tcon *tcon; int ret; + engine = sun4i_tcon_find_engine(drv, dev->of_node); + if (IS_ERR(engine)) { + dev_err(dev, "Couldn't find matching engine\n"); + return -EPROBE_DEFER; + } + tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL); if (!tcon) return -ENOMEM; dev_set_drvdata(dev, tcon); - drv->tcon = tcon; tcon->drm = drm; tcon->dev = dev; + tcon->id = engine->id; tcon->quirks = of_device_get_match_data(dev); tcon->lcd_rst = devm_reset_control_get(dev, "lcd"); @@ -459,7 +563,7 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, goto err_free_dotclock; } - tcon->crtc = sun4i_crtc_init(drm, drv->backend, tcon); + tcon->crtc = sun4i_crtc_init(drm, engine, tcon); if (IS_ERR(tcon->crtc)) { dev_err(dev, "Couldn't create our CRTC\n"); ret = PTR_ERR(tcon->crtc); @@ -470,6 +574,8 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, if (ret < 0) goto err_free_clocks; + list_add_tail(&tcon->list, &drv->tcon_list); + return 0; err_free_dotclock: @@ -486,6 +592,7 @@ static void sun4i_tcon_unbind(struct device *dev, struct device *master, { struct sun4i_tcon *tcon = dev_get_drvdata(dev); + list_del(&tcon->list); sun4i_dclk_free(tcon); sun4i_tcon_free_clocks(tcon); } @@ -533,11 +640,16 @@ static const struct sun4i_tcon_quirks sun8i_a33_quirks = { /* nothing is supported */ }; +static const struct sun4i_tcon_quirks sun8i_v3s_quirks = { + /* nothing is supported */ +}; + static const struct of_device_id sun4i_tcon_of_table[] = { { .compatible = "allwinner,sun5i-a13-tcon", .data = &sun5i_a13_quirks }, { .compatible = "allwinner,sun6i-a31-tcon", .data = &sun6i_a31_quirks }, { .compatible = "allwinner,sun6i-a31s-tcon", .data = &sun6i_a31s_quirks }, { .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks }, + { .compatible = "allwinner,sun8i-v3s-tcon", .data = &sun8i_v3s_quirks }, { } }; MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table); |