diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-02-01 17:48:47 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-02-01 17:48:47 -0800 |
commit | 4bf772b14675411a69b3c807f73006de0fe4b649 (patch) | |
tree | b841e3ba0e3429695589cb0ab73871fa12f42c38 /drivers/gpu/drm/sun4i | |
parent | 3879ae653a3e98380fe2daf653338830b7ca0097 (diff) | |
parent | 24b8ef699e8221d2b7f813adaab13eec053e1507 (diff) | |
download | linux-4bf772b14675411a69b3c807f73006de0fe4b649.tar.bz2 |
Merge tag 'drm-for-v4.16' of git://people.freedesktop.org/~airlied/linux
Pull drm updates from Dave Airlie:
"This seems to have been a comparatively quieter merge window, I assume
due to holidays etc. The "biggest" change is AMD header cleanups, which
merge/remove a bunch of them. The AMD gpu scheduler is now being made generic
with the etnaviv driver wanting to reuse the code, hopefully other drivers
can go in the same direction.
Otherwise it's the usual lots of stuff in i915/amdgpu, not so much stuff
elsewhere.
Core:
- Add .last_close and .output_poll_changed helpers to reduce driver footprints
- Fix plane clipping
- Improved debug printing support
- Add panel orientation property
- Update edid derived properties at edid setting
- Reduction in fbdev driver footprint
- Move amdgpu scheduler into core for other drivers to use.
i915:
- Selftest and IGT improvements
- Fast boot prep work on IPS, pipe config
- HW workarounds for Cannonlake, Geminilake
- Cannonlake clock and HDMI2.0 fixes
- GPU cache invalidation and context switch improvements
- Display planes cleanup
- New PMU interface for perf queries
- New firmware support for KBL/SKL
- Geminilake HW workaround for perforamce
- Coffeelake stolen memory improvements
- GPU reset robustness work
- Cannonlake horizontal plane flipping
- GVT work
amdgpu/radeon:
- RV and Vega header file cleanups (lots of lines gone!)
- TTM operation context support
- 48-bit GPUVM support for Vega/RV
- ECC support for Vega
- Resizeable BAR support
- Multi-display sync support
- Enable swapout for reserved BOs during allocation
- S3 fixes on Raven
- GPU reset cleanup and fixes
- 2+1 level GPU page table
amdkfd:
- GFX7/8 SDMA user queues support
- Hardware scheduling for multiple processes
- dGPU prep work
rcar:
- Added R8A7743/5 support
- System suspend/resume support
sun4i:
- Multi-plane support for YUV formats
- A83T and LVDS support
msm:
- Devfreq support for GPU
tegra:
- Prep work for adding Tegra186 support
- Tegra186 HDMI support
- HDMI2.0 and zpos support by using generic helpers
tilcdc:
- Misc fixes
omapdrm:
- Support memory bandwidth limits
- DSI command mode panel cleanups
- DMM error handling
exynos:
- drop the old IPP subdriver.
etnaviv:
- Occlusion query fixes
- Job handling fixes
- Prep work for hooking in gpu scheduler
armada:
- Move closer to atomic modesetting
- Allow disabling primary plane if overlay is full screen
imx:
- Format modifier support
- Add tile prefetch to PRE
- Runtime PM support for PRG
ast:
- fix LUT loading"
* tag 'drm-for-v4.16' of git://people.freedesktop.org/~airlied/linux: (1471 commits)
drm/ast: Load lut in crtc_commit
drm: Check for lessee in DROP_MASTER ioctl
drm: fix gpu scheduler link order
drm/amd/display: Demote error print to debug print when ATOM impl missing
dma-buf: fix reservation_object_wait_timeout_rcu once more v2
drm/amdgpu: Avoid leaking PM domain on driver unbind (v2)
drm/amd/amdgpu: Add Polaris version check
drm/amdgpu: Reenable manual GPU reset from sysfs
drm/amdgpu: disable MMHUB power gating on raven
drm/ttm: Don't unreserve swapped BOs that were previously reserved
drm/ttm: Don't add swapped BOs to swap-LRU list
drm/amdgpu: only check for ECC on Vega10
drm/amd/powerplay: Fix smu_table_entry.handle type
drm/ttm: add VADDR_FLAG_UPDATED_COUNT to correctly update dma_page global count
drm: Fix PANEL_ORIENTATION_QUIRKS breaking the Kconfig DRM menuconfig
drm/radeon: fill in rb backend map on evergreen/ni.
drm/amdgpu/gfx9: fix ngg enablement to clear gds reserved memory (v2)
drm/ttm: only free pages rather than update global memory count together
drm/amdgpu: fix CPU based VM updates
drm/amdgpu: fix typo in amdgpu_vce_validate_bo
...
Diffstat (limited to 'drivers/gpu/drm/sun4i')
24 files changed, 3105 insertions, 470 deletions
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile index 0c2f8c7facae..2b37a6abbb1d 100644 --- a/drivers/gpu/drm/sun4i/Makefile +++ b/drivers/gpu/drm/sun4i/Makefile @@ -9,10 +9,13 @@ sun4i-drm-hdmi-y += sun4i_hdmi_enc.o sun4i-drm-hdmi-y += sun4i_hdmi_i2c.o sun4i-drm-hdmi-y += sun4i_hdmi_tmds_clk.o -sun8i-mixer-y += sun8i_mixer.o sun8i_layer.o +sun8i-mixer-y += sun8i_mixer.o sun8i_ui_layer.o \ + sun8i_vi_layer.o sun8i_ui_scaler.o \ + sun8i_vi_scaler.o sun8i_csc.o sun4i-tcon-y += sun4i_crtc.o sun4i-tcon-y += sun4i_dotclock.o +sun4i-tcon-y += sun4i_lvds.o sun4i-tcon-y += sun4i_tcon.o sun4i-tcon-y += sun4i_rgb.o diff --git a/drivers/gpu/drm/sun4i/sun4i_dotclock.c b/drivers/gpu/drm/sun4i/sun4i_dotclock.c index d401156490f3..023f39bda633 100644 --- a/drivers/gpu/drm/sun4i/sun4i_dotclock.c +++ b/drivers/gpu/drm/sun4i/sun4i_dotclock.c @@ -17,8 +17,9 @@ #include "sun4i_dotclock.h" struct sun4i_dclk { - struct clk_hw hw; - struct regmap *regmap; + struct clk_hw hw; + struct regmap *regmap; + struct sun4i_tcon *tcon; }; static inline struct sun4i_dclk *hw_to_dclk(struct clk_hw *hw) @@ -73,11 +74,13 @@ static unsigned long sun4i_dclk_recalc_rate(struct clk_hw *hw, static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { + struct sun4i_dclk *dclk = hw_to_dclk(hw); + struct sun4i_tcon *tcon = dclk->tcon; unsigned long best_parent = 0; u8 best_div = 1; int i; - for (i = 6; i <= 127; i++) { + for (i = tcon->dclk_min_div; i <= tcon->dclk_max_div; i++) { unsigned long ideal = rate * i; unsigned long rounded; @@ -167,6 +170,7 @@ int sun4i_dclk_create(struct device *dev, struct sun4i_tcon *tcon) dclk = devm_kzalloc(dev, sizeof(*dclk), GFP_KERNEL); if (!dclk) return -ENOMEM; + dclk->tcon = tcon; init.name = clk_name; init.ops = &sun4i_dclk_ops; diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index 75c76cdd82bc..4570da0227b4 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -26,20 +26,13 @@ #include "sun4i_framebuffer.h" #include "sun4i_tcon.h" -static void sun4i_drv_lastclose(struct drm_device *dev) -{ - struct sun4i_drv *drv = dev->dev_private; - - drm_fbdev_cma_restore_mode(drv->fbdev); -} - DEFINE_DRM_GEM_CMA_FOPS(sun4i_drv_fops); static struct drm_driver sun4i_drv_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, /* Generic Operations */ - .lastclose = sun4i_drv_lastclose, + .lastclose = drm_fb_helper_lastclose, .fops = &sun4i_drv_fops, .name = "sun4i-drm", .desc = "Allwinner sun4i Display Engine", @@ -126,10 +119,9 @@ static int sun4i_drv_bind(struct device *dev) sun4i_remove_framebuffers(); /* Create our framebuffer */ - drv->fbdev = sun4i_framebuffer_init(drm); - if (IS_ERR(drv->fbdev)) { + ret = sun4i_framebuffer_init(drm); + if (ret) { dev_err(drm->dev, "Couldn't create our framebuffer\n"); - ret = PTR_ERR(drv->fbdev); goto cleanup_mode_config; } @@ -187,13 +179,7 @@ static bool sun4i_drv_node_is_frontend(struct device_node *node) static bool sun4i_drv_node_is_tcon(struct device_node *node) { - return of_device_is_compatible(node, "allwinner,sun4i-a10-tcon") || - of_device_is_compatible(node, "allwinner,sun5i-a13-tcon") || - of_device_is_compatible(node, "allwinner,sun6i-a31-tcon") || - of_device_is_compatible(node, "allwinner,sun6i-a31s-tcon") || - of_device_is_compatible(node, "allwinner,sun7i-a20-tcon") || - of_device_is_compatible(node, "allwinner,sun8i-a33-tcon") || - of_device_is_compatible(node, "allwinner,sun8i-v3s-tcon"); + return !!of_match_node(sun4i_tcon_of_table, node); } static int compare_of(struct device *dev, void *data) @@ -353,6 +339,7 @@ static const struct of_device_id sun4i_drv_of_table[] = { { .compatible = "allwinner,sun6i-a31s-display-engine" }, { .compatible = "allwinner,sun7i-a20-display-engine" }, { .compatible = "allwinner,sun8i-a33-display-engine" }, + { .compatible = "allwinner,sun8i-a83t-display-engine" }, { .compatible = "allwinner,sun8i-v3s-display-engine" }, { } }; diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h b/drivers/gpu/drm/sun4i/sun4i_drv.h index a960c89270cc..2825f140da54 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.h +++ b/drivers/gpu/drm/sun4i/sun4i_drv.h @@ -20,8 +20,6 @@ struct sun4i_drv { struct list_head engine_list; struct list_head tcon_list; - - struct drm_fbdev_cma *fbdev; }; #endif /* _SUN4I_DRV_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun4i_framebuffer.c b/drivers/gpu/drm/sun4i/sun4i_framebuffer.c index 2992f0a6b349..38a36c0dfa2f 100644 --- a/drivers/gpu/drm/sun4i/sun4i_framebuffer.c +++ b/drivers/gpu/drm/sun4i/sun4i_framebuffer.c @@ -11,6 +11,7 @@ */ #include <drm/drm_atomic_helper.h> +#include <drm/drm_fb_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drmP.h> @@ -18,21 +19,14 @@ #include "sun4i_drv.h" #include "sun4i_framebuffer.h" -static void sun4i_de_output_poll_changed(struct drm_device *drm) -{ - struct sun4i_drv *drv = drm->dev_private; - - drm_fbdev_cma_hotplug_event(drv->fbdev); -} - static const struct drm_mode_config_funcs sun4i_de_mode_config_funcs = { - .output_poll_changed = sun4i_de_output_poll_changed, + .output_poll_changed = drm_fb_helper_output_poll_changed, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, .fb_create = drm_gem_fb_create, }; -struct drm_fbdev_cma *sun4i_framebuffer_init(struct drm_device *drm) +int sun4i_framebuffer_init(struct drm_device *drm) { drm_mode_config_reset(drm); @@ -41,12 +35,10 @@ struct drm_fbdev_cma *sun4i_framebuffer_init(struct drm_device *drm) drm->mode_config.funcs = &sun4i_de_mode_config_funcs; - return drm_fbdev_cma_init(drm, 32, drm->mode_config.num_connector); + return drm_fb_cma_fbdev_init(drm, 32, 0); } void sun4i_framebuffer_free(struct drm_device *drm) { - struct sun4i_drv *drv = drm->dev_private; - - drm_fbdev_cma_fini(drv->fbdev); + drm_fb_cma_fbdev_fini(drm); } diff --git a/drivers/gpu/drm/sun4i/sun4i_framebuffer.h b/drivers/gpu/drm/sun4i/sun4i_framebuffer.h index 3afd65252ee0..7ef0aed8384c 100644 --- a/drivers/gpu/drm/sun4i/sun4i_framebuffer.h +++ b/drivers/gpu/drm/sun4i/sun4i_framebuffer.h @@ -13,7 +13,7 @@ #ifndef _SUN4I_FRAMEBUFFER_H_ #define _SUN4I_FRAMEBUFFER_H_ -struct drm_fbdev_cma *sun4i_framebuffer_init(struct drm_device *drm); +int sun4i_framebuffer_init(struct drm_device *drm); void sun4i_framebuffer_free(struct drm_device *drm); #endif /* _SUN4I_FRAMEBUFFER_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.c b/drivers/gpu/drm/sun4i/sun4i_lvds.c new file mode 100644 index 000000000000..be3f14d7746d --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 Free Electrons + * Maxime Ripard <maxime.ripard@free-electrons.com> + */ + +#include <linux/clk.h> + +#include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_of.h> +#include <drm/drm_panel.h> + +#include "sun4i_crtc.h" +#include "sun4i_tcon.h" +#include "sun4i_lvds.h" + +struct sun4i_lvds { + struct drm_connector connector; + struct drm_encoder encoder; + + struct sun4i_tcon *tcon; +}; + +static inline struct sun4i_lvds * +drm_connector_to_sun4i_lvds(struct drm_connector *connector) +{ + return container_of(connector, struct sun4i_lvds, + connector); +} + +static inline struct sun4i_lvds * +drm_encoder_to_sun4i_lvds(struct drm_encoder *encoder) +{ + return container_of(encoder, struct sun4i_lvds, + encoder); +} + +static int sun4i_lvds_get_modes(struct drm_connector *connector) +{ + struct sun4i_lvds *lvds = + drm_connector_to_sun4i_lvds(connector); + struct sun4i_tcon *tcon = lvds->tcon; + + return drm_panel_get_modes(tcon->panel); +} + +static struct drm_connector_helper_funcs sun4i_lvds_con_helper_funcs = { + .get_modes = sun4i_lvds_get_modes, +}; + +static void +sun4i_lvds_connector_destroy(struct drm_connector *connector) +{ + struct sun4i_lvds *lvds = drm_connector_to_sun4i_lvds(connector); + struct sun4i_tcon *tcon = lvds->tcon; + + drm_panel_detach(tcon->panel); + drm_connector_cleanup(connector); +} + +static const struct drm_connector_funcs sun4i_lvds_con_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = sun4i_lvds_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static void sun4i_lvds_encoder_enable(struct drm_encoder *encoder) +{ + struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder); + struct sun4i_tcon *tcon = lvds->tcon; + + DRM_DEBUG_DRIVER("Enabling LVDS output\n"); + + if (!IS_ERR(tcon->panel)) { + drm_panel_prepare(tcon->panel); + drm_panel_enable(tcon->panel); + } +} + +static void sun4i_lvds_encoder_disable(struct drm_encoder *encoder) +{ + struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder); + struct sun4i_tcon *tcon = lvds->tcon; + + DRM_DEBUG_DRIVER("Disabling LVDS output\n"); + + if (!IS_ERR(tcon->panel)) { + drm_panel_disable(tcon->panel); + drm_panel_unprepare(tcon->panel); + } +} + +static const struct drm_encoder_helper_funcs sun4i_lvds_enc_helper_funcs = { + .disable = sun4i_lvds_encoder_disable, + .enable = sun4i_lvds_encoder_enable, +}; + +static const struct drm_encoder_funcs sun4i_lvds_enc_funcs = { + .destroy = drm_encoder_cleanup, +}; + +int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon) +{ + struct drm_encoder *encoder; + struct drm_bridge *bridge; + struct sun4i_lvds *lvds; + int ret; + + lvds = devm_kzalloc(drm->dev, sizeof(*lvds), GFP_KERNEL); + if (!lvds) + return -ENOMEM; + lvds->tcon = tcon; + encoder = &lvds->encoder; + + ret = drm_of_find_panel_or_bridge(tcon->dev->of_node, 1, 0, + &tcon->panel, &bridge); + if (ret) { + dev_info(drm->dev, "No panel or bridge found... LVDS output disabled\n"); + return 0; + } + + drm_encoder_helper_add(&lvds->encoder, + &sun4i_lvds_enc_helper_funcs); + ret = drm_encoder_init(drm, + &lvds->encoder, + &sun4i_lvds_enc_funcs, + DRM_MODE_ENCODER_LVDS, + NULL); + if (ret) { + dev_err(drm->dev, "Couldn't initialise the lvds encoder\n"); + goto err_out; + } + + /* The LVDS encoder can only work with the TCON channel 0 */ + lvds->encoder.possible_crtcs = BIT(drm_crtc_index(&tcon->crtc->crtc)); + + if (tcon->panel) { + drm_connector_helper_add(&lvds->connector, + &sun4i_lvds_con_helper_funcs); + ret = drm_connector_init(drm, &lvds->connector, + &sun4i_lvds_con_funcs, + DRM_MODE_CONNECTOR_LVDS); + if (ret) { + dev_err(drm->dev, "Couldn't initialise the lvds connector\n"); + goto err_cleanup_connector; + } + + drm_mode_connector_attach_encoder(&lvds->connector, + &lvds->encoder); + + ret = drm_panel_attach(tcon->panel, &lvds->connector); + if (ret) { + dev_err(drm->dev, "Couldn't attach our panel\n"); + goto err_cleanup_connector; + } + } + + if (bridge) { + ret = drm_bridge_attach(encoder, bridge, NULL); + if (ret) { + dev_err(drm->dev, "Couldn't attach our bridge\n"); + goto err_cleanup_connector; + } + } + + return 0; + +err_cleanup_connector: + drm_encoder_cleanup(&lvds->encoder); +err_out: + return ret; +} +EXPORT_SYMBOL(sun4i_lvds_init); diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.h b/drivers/gpu/drm/sun4i/sun4i_lvds.h new file mode 100644 index 000000000000..f3e90faa3082 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun4i_lvds.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 Free Electrons + * Maxime Ripard <maxime.ripard@free-electrons.com> + */ + +#ifndef _SUN4I_LVDS_H_ +#define _SUN4I_LVDS_H_ + +int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon); + +#endif /* _SUN4I_LVDS_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index f4284b51bdca..3c15cf24b503 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -31,10 +31,52 @@ #include "sun4i_crtc.h" #include "sun4i_dotclock.h" #include "sun4i_drv.h" +#include "sun4i_lvds.h" #include "sun4i_rgb.h" #include "sun4i_tcon.h" #include "sunxi_engine.h" +static struct drm_connector *sun4i_tcon_get_connector(const struct drm_encoder *encoder) +{ + struct drm_connector *connector; + struct drm_connector_list_iter iter; + + drm_connector_list_iter_begin(encoder->dev, &iter); + drm_for_each_connector_iter(connector, &iter) + if (connector->encoder == encoder) { + drm_connector_list_iter_end(&iter); + return connector; + } + drm_connector_list_iter_end(&iter); + + return NULL; +} + +static int sun4i_tcon_get_pixel_depth(const struct drm_encoder *encoder) +{ + struct drm_connector *connector; + struct drm_display_info *info; + + connector = sun4i_tcon_get_connector(encoder); + if (!connector) + return -EINVAL; + + info = &connector->display_info; + if (info->num_bus_formats != 1) + return -EINVAL; + + switch (info->bus_formats[0]) { + case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: + return 18; + + case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: + case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: + return 24; + } + + return -EINVAL; +} + static void sun4i_tcon_channel_set_status(struct sun4i_tcon *tcon, int channel, bool enabled) { @@ -65,13 +107,63 @@ static void sun4i_tcon_channel_set_status(struct sun4i_tcon *tcon, int channel, clk_disable_unprepare(clk); } +static void sun4i_tcon_lvds_set_status(struct sun4i_tcon *tcon, + const struct drm_encoder *encoder, + bool enabled) +{ + if (enabled) { + u8 val; + + regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_IF_REG, + SUN4I_TCON0_LVDS_IF_EN, + SUN4I_TCON0_LVDS_IF_EN); + + /* + * As their name suggest, these values only apply to the A31 + * and later SoCs. We'll have to rework this when merging + * support for the older SoCs. + */ + regmap_write(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG, + SUN6I_TCON0_LVDS_ANA0_C(2) | + SUN6I_TCON0_LVDS_ANA0_V(3) | + SUN6I_TCON0_LVDS_ANA0_PD(2) | + SUN6I_TCON0_LVDS_ANA0_EN_LDO); + udelay(2); + + regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG, + SUN6I_TCON0_LVDS_ANA0_EN_MB, + SUN6I_TCON0_LVDS_ANA0_EN_MB); + udelay(2); + + regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG, + SUN6I_TCON0_LVDS_ANA0_EN_DRVC, + SUN6I_TCON0_LVDS_ANA0_EN_DRVC); + + if (sun4i_tcon_get_pixel_depth(encoder) == 18) + val = 7; + else + val = 0xf; + + regmap_write_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG, + SUN6I_TCON0_LVDS_ANA0_EN_DRVD(0xf), + SUN6I_TCON0_LVDS_ANA0_EN_DRVD(val)); + } else { + regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_IF_REG, + SUN4I_TCON0_LVDS_IF_EN, 0); + } +} + void sun4i_tcon_set_status(struct sun4i_tcon *tcon, const struct drm_encoder *encoder, bool enabled) { + bool is_lvds = false; int channel; switch (encoder->encoder_type) { + case DRM_MODE_ENCODER_LVDS: + is_lvds = true; + /* Fallthrough */ case DRM_MODE_ENCODER_NONE: channel = 0; break; @@ -84,10 +176,16 @@ void sun4i_tcon_set_status(struct sun4i_tcon *tcon, return; } + if (is_lvds && !enabled) + sun4i_tcon_lvds_set_status(tcon, encoder, false); + regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, SUN4I_TCON_GCTL_TCON_ENABLE, enabled ? SUN4I_TCON_GCTL_TCON_ENABLE : 0); + if (is_lvds && enabled) + sun4i_tcon_lvds_set_status(tcon, encoder, true); + sun4i_tcon_channel_set_status(tcon, channel, enabled); } @@ -170,6 +268,75 @@ static void sun4i_tcon0_mode_set_common(struct sun4i_tcon *tcon, SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay)); } +static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon, + const struct drm_encoder *encoder, + const struct drm_display_mode *mode) +{ + unsigned int bp; + u8 clk_delay; + u32 reg, val = 0; + + tcon->dclk_min_div = 7; + tcon->dclk_max_div = 7; + sun4i_tcon0_mode_set_common(tcon, mode); + + /* Adjust clock delay */ + clk_delay = sun4i_tcon_get_clk_delay(mode, 0); + regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, + SUN4I_TCON0_CTL_CLK_DELAY_MASK, + SUN4I_TCON0_CTL_CLK_DELAY(clk_delay)); + + /* + * This is called a backporch in the register documentation, + * but it really is the back porch + hsync + */ + bp = mode->crtc_htotal - mode->crtc_hsync_start; + DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n", + mode->crtc_htotal, bp); + + /* Set horizontal display timings */ + regmap_write(tcon->regs, SUN4I_TCON0_BASIC1_REG, + SUN4I_TCON0_BASIC1_H_TOTAL(mode->htotal) | + SUN4I_TCON0_BASIC1_H_BACKPORCH(bp)); + + /* + * This is called a backporch in the register documentation, + * but it really is the back porch + hsync + */ + bp = mode->crtc_vtotal - mode->crtc_vsync_start; + DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n", + mode->crtc_vtotal, bp); + + /* Set vertical display timings */ + regmap_write(tcon->regs, SUN4I_TCON0_BASIC2_REG, + SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal * 2) | + SUN4I_TCON0_BASIC2_V_BACKPORCH(bp)); + + reg = SUN4I_TCON0_LVDS_IF_CLK_SEL_TCON0 | + SUN4I_TCON0_LVDS_IF_DATA_POL_NORMAL | + SUN4I_TCON0_LVDS_IF_CLK_POL_NORMAL; + if (sun4i_tcon_get_pixel_depth(encoder) == 24) + reg |= SUN4I_TCON0_LVDS_IF_BITWIDTH_24BITS; + else + reg |= SUN4I_TCON0_LVDS_IF_BITWIDTH_18BITS; + + regmap_write(tcon->regs, SUN4I_TCON0_LVDS_IF_REG, reg); + + /* Setup the polarity of the various signals */ + if (!(mode->flags & DRM_MODE_FLAG_PHSYNC)) + val |= SUN4I_TCON0_IO_POL_HSYNC_POSITIVE; + + if (!(mode->flags & DRM_MODE_FLAG_PVSYNC)) + val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE; + + regmap_write(tcon->regs, SUN4I_TCON0_IO_POL_REG, val); + + /* Map output pins to channel 0 */ + regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, + SUN4I_TCON_GCTL_IOMAP_MASK, + SUN4I_TCON_GCTL_IOMAP_TCON0); +} + static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, const struct drm_display_mode *mode) { @@ -177,6 +344,8 @@ static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, u8 clk_delay; u32 val = 0; + tcon->dclk_min_div = 6; + tcon->dclk_max_div = 127; sun4i_tcon0_mode_set_common(tcon, mode); /* Adjust clock delay */ @@ -334,6 +503,9 @@ void sun4i_tcon_mode_set(struct sun4i_tcon *tcon, const struct drm_display_mode *mode) { switch (encoder->encoder_type) { + case DRM_MODE_ENCODER_LVDS: + sun4i_tcon0_mode_set_lvds(tcon, encoder, mode); + break; case DRM_MODE_ENCODER_NONE: sun4i_tcon0_mode_set_rgb(tcon, mode); sun4i_tcon_set_mux(tcon, 0, encoder); @@ -665,7 +837,9 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, struct drm_device *drm = data; struct sun4i_drv *drv = drm->dev_private; struct sunxi_engine *engine; + struct device_node *remote; struct sun4i_tcon *tcon; + bool has_lvds_rst, has_lvds_alt, can_lvds; int ret; engine = sun4i_tcon_find_engine(drv, dev->of_node); @@ -696,6 +870,54 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, return ret; } + /* + * This can only be made optional since we've had DT nodes + * without the LVDS reset properties. + * + * If the property is missing, just disable LVDS, and print a + * warning. + */ + tcon->lvds_rst = devm_reset_control_get_optional(dev, "lvds"); + if (IS_ERR(tcon->lvds_rst)) { + dev_err(dev, "Couldn't get our reset line\n"); + return PTR_ERR(tcon->lvds_rst); + } else if (tcon->lvds_rst) { + has_lvds_rst = true; + reset_control_reset(tcon->lvds_rst); + } else { + has_lvds_rst = false; + } + + /* + * This can only be made optional since we've had DT nodes + * without the LVDS reset properties. + * + * If the property is missing, just disable LVDS, and print a + * warning. + */ + if (tcon->quirks->has_lvds_alt) { + tcon->lvds_pll = devm_clk_get(dev, "lvds-alt"); + if (IS_ERR(tcon->lvds_pll)) { + if (PTR_ERR(tcon->lvds_pll) == -ENOENT) { + has_lvds_alt = false; + } else { + dev_err(dev, "Couldn't get the LVDS PLL\n"); + return PTR_ERR(tcon->lvds_pll); + } + } else { + has_lvds_alt = true; + } + } + + if (!has_lvds_rst || (tcon->quirks->has_lvds_alt && !has_lvds_alt)) { + dev_warn(dev, + "Missing LVDS properties, Please upgrade your DT\n"); + dev_warn(dev, "LVDS output disabled\n"); + can_lvds = false; + } else { + can_lvds = true; + } + ret = sun4i_tcon_init_clocks(dev, tcon); if (ret) { dev_err(dev, "Couldn't init our TCON clocks\n"); @@ -727,7 +949,21 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, goto err_free_dotclock; } - ret = sun4i_rgb_init(drm, tcon); + /* + * If we have an LVDS panel connected to the TCON, we should + * just probe the LVDS connector. Otherwise, just probe RGB as + * we used to. + */ + remote = of_graph_get_remote_node(dev->of_node, 1, 0); + if (of_device_is_compatible(remote, "panel-lvds")) + if (can_lvds) + ret = sun4i_lvds_init(drm, tcon); + else + ret = -EINVAL; + else + ret = sun4i_rgb_init(drm, tcon); + of_node_put(remote); + if (ret < 0) goto err_free_dotclock; @@ -877,6 +1113,7 @@ static const struct sun4i_tcon_quirks sun5i_a13_quirks = { static const struct sun4i_tcon_quirks sun6i_a31_quirks = { .has_channel_1 = true, + .has_lvds_alt = true, .needs_de_be_mux = true, .set_mux = sun6i_tcon_set_mux, }; @@ -893,6 +1130,10 @@ static const struct sun4i_tcon_quirks sun7i_a20_quirks = { }; static const struct sun4i_tcon_quirks sun8i_a33_quirks = { + .has_lvds_alt = true, +}; + +static const struct sun4i_tcon_quirks sun8i_a83t_lcd_quirks = { /* nothing is supported */ }; @@ -900,17 +1141,20 @@ static const struct sun4i_tcon_quirks sun8i_v3s_quirks = { /* nothing is supported */ }; -static const struct of_device_id sun4i_tcon_of_table[] = { +/* sun4i_drv uses this list to check if a device node is a TCON */ +const struct of_device_id sun4i_tcon_of_table[] = { { .compatible = "allwinner,sun4i-a10-tcon", .data = &sun4i_a10_quirks }, { .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,sun7i-a20-tcon", .data = &sun7i_a20_quirks }, { .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks }, + { .compatible = "allwinner,sun8i-a83t-tcon-lcd", .data = &sun8i_a83t_lcd_quirks }, { .compatible = "allwinner,sun8i-v3s-tcon", .data = &sun8i_v3s_quirks }, { } }; MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table); +EXPORT_SYMBOL(sun4i_tcon_of_table); static struct platform_driver sun4i_tcon_platform_driver = { .probe = sun4i_tcon_probe, diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h index f61bf6d83b4a..b761c7b823c5 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.h +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h @@ -70,7 +70,21 @@ #define SUN4I_TCON0_TTL2_REG 0x78 #define SUN4I_TCON0_TTL3_REG 0x7c #define SUN4I_TCON0_TTL4_REG 0x80 + #define SUN4I_TCON0_LVDS_IF_REG 0x84 +#define SUN4I_TCON0_LVDS_IF_EN BIT(31) +#define SUN4I_TCON0_LVDS_IF_BITWIDTH_MASK BIT(26) +#define SUN4I_TCON0_LVDS_IF_BITWIDTH_18BITS (1 << 26) +#define SUN4I_TCON0_LVDS_IF_BITWIDTH_24BITS (0 << 26) +#define SUN4I_TCON0_LVDS_IF_CLK_SEL_MASK BIT(20) +#define SUN4I_TCON0_LVDS_IF_CLK_SEL_TCON0 (1 << 20) +#define SUN4I_TCON0_LVDS_IF_CLK_POL_MASK BIT(4) +#define SUN4I_TCON0_LVDS_IF_CLK_POL_NORMAL (1 << 4) +#define SUN4I_TCON0_LVDS_IF_CLK_POL_INV (0 << 4) +#define SUN4I_TCON0_LVDS_IF_DATA_POL_MASK GENMASK(3, 0) +#define SUN4I_TCON0_LVDS_IF_DATA_POL_NORMAL (0xf) +#define SUN4I_TCON0_LVDS_IF_DATA_POL_INV (0) + #define SUN4I_TCON0_IO_POL_REG 0x88 #define SUN4I_TCON0_IO_POL_DCLK_PHASE(phase) ((phase & 3) << 28) #define SUN4I_TCON0_IO_POL_HSYNC_POSITIVE BIT(25) @@ -131,6 +145,16 @@ #define SUN4I_TCON_CEU_RANGE_G_REG 0x144 #define SUN4I_TCON_CEU_RANGE_B_REG 0x148 #define SUN4I_TCON_MUX_CTRL_REG 0x200 + +#define SUN4I_TCON0_LVDS_ANA0_REG 0x220 +#define SUN6I_TCON0_LVDS_ANA0_EN_MB BIT(31) +#define SUN6I_TCON0_LVDS_ANA0_EN_LDO BIT(30) +#define SUN6I_TCON0_LVDS_ANA0_EN_DRVC BIT(24) +#define SUN6I_TCON0_LVDS_ANA0_EN_DRVD(x) (((x) & 0xf) << 20) +#define SUN6I_TCON0_LVDS_ANA0_C(x) (((x) & 3) << 17) +#define SUN6I_TCON0_LVDS_ANA0_V(x) (((x) & 3) << 8) +#define SUN6I_TCON0_LVDS_ANA0_PD(x) (((x) & 3) << 4) + #define SUN4I_TCON1_FILL_CTL_REG 0x300 #define SUN4I_TCON1_FILL_BEG0_REG 0x304 #define SUN4I_TCON1_FILL_END0_REG 0x308 @@ -149,6 +173,7 @@ struct sun4i_tcon; struct sun4i_tcon_quirks { bool has_channel_1; /* a33 does not have channel 1 */ + bool has_lvds_alt; /* Does the LVDS clock have a parent other than the TCON clock? */ bool needs_de_be_mux; /* sun6i needs mux to select backend */ /* callback to handle tcon muxing options */ @@ -167,11 +192,17 @@ struct sun4i_tcon { struct clk *sclk0; struct clk *sclk1; + /* Possible mux for the LVDS clock */ + struct clk *lvds_pll; + /* Pixel clock */ struct clk *dclk; + u8 dclk_max_div; + u8 dclk_min_div; /* Reset control */ struct reset_control *lcd_rst; + struct reset_control *lvds_rst; struct drm_panel *panel; @@ -197,4 +228,6 @@ void sun4i_tcon_mode_set(struct sun4i_tcon *tcon, void sun4i_tcon_set_status(struct sun4i_tcon *crtc, const struct drm_encoder *encoder, bool enable); +extern const struct of_device_id sun4i_tcon_of_table[]; + #endif /* __SUN4I_TCON_H__ */ diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.c b/drivers/gpu/drm/sun4i/sun8i_csc.c new file mode 100644 index 000000000000..b14925b40ccf --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun8i_csc.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <drm/drmP.h> + +#include "sun8i_csc.h" +#include "sun8i_mixer.h" + +static const u32 ccsc_base[2][2] = { + {CCSC00_OFFSET, CCSC01_OFFSET}, + {CCSC10_OFFSET, CCSC11_OFFSET}, +}; + +/* + * Factors are in two's complement format, 10 bits for fractinal part. + * First tree values in each line are multiplication factor and last + * value is constant, which is added at the end. + */ +static const u32 yuv2rgb[] = { + 0x000004A8, 0x00000000, 0x00000662, 0xFFFC845A, + 0x000004A8, 0xFFFFFE6F, 0xFFFFFCBF, 0x00021DF4, + 0x000004A8, 0x00000813, 0x00000000, 0xFFFBAC4A, +}; + +static const u32 yvu2rgb[] = { + 0x000004A8, 0x00000662, 0x00000000, 0xFFFC845A, + 0x000004A8, 0xFFFFFCBF, 0xFFFFFE6F, 0x00021DF4, + 0x000004A8, 0x00000000, 0x00000813, 0xFFFBAC4A, +}; + +static void sun8i_csc_set_coefficients(struct regmap *map, u32 base, + enum sun8i_csc_mode mode) +{ + const u32 *table; + int i, data; + + switch (mode) { + case SUN8I_CSC_MODE_YUV2RGB: + table = yuv2rgb; + break; + case SUN8I_CSC_MODE_YVU2RGB: + table = yvu2rgb; + break; + default: + DRM_WARN("Wrong CSC mode specified.\n"); + return; + } + + for (i = 0; i < 12; i++) { + data = table[i]; + /* For some reason, 0x200 must be added to constant parts */ + if (((i + 1) & 3) == 0) + data += 0x200; + regmap_write(map, SUN8I_CSC_COEFF(base, i), data); + } +} + +static void sun8i_csc_enable(struct regmap *map, u32 base, bool enable) +{ + u32 val; + + if (enable) + val = SUN8I_CSC_CTRL_EN; + else + val = 0; + + regmap_update_bits(map, SUN8I_CSC_CTRL(base), SUN8I_CSC_CTRL_EN, val); +} + +void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer, + enum sun8i_csc_mode mode) +{ + u32 base; + + base = ccsc_base[mixer->cfg->ccsc][layer]; + + sun8i_csc_set_coefficients(mixer->engine.regs, base, mode); +} + +void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable) +{ + u32 base; + + base = ccsc_base[mixer->cfg->ccsc][layer]; + + sun8i_csc_enable(mixer->engine.regs, base, enable); +} diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.h b/drivers/gpu/drm/sun4i/sun8i_csc.h new file mode 100644 index 000000000000..880e8fbb0855 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun8i_csc.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#ifndef _SUN8I_CSC_H_ +#define _SUN8I_CSC_H_ + +struct sun8i_mixer; + +/* VI channel CSC units offsets */ +#define CCSC00_OFFSET 0xAA050 +#define CCSC01_OFFSET 0xFA000 +#define CCSC10_OFFSET 0xA0000 +#define CCSC11_OFFSET 0xF0000 + +#define SUN8I_CSC_CTRL(base) (base + 0x0) +#define SUN8I_CSC_COEFF(base, i) (base + 0x10 + 4 * i) + +#define SUN8I_CSC_CTRL_EN BIT(0) + +enum sun8i_csc_mode { + SUN8I_CSC_MODE_OFF, + SUN8I_CSC_MODE_YUV2RGB, + SUN8I_CSC_MODE_YVU2RGB, +}; + +void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer, + enum sun8i_csc_mode mode); +void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable); + +#endif diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.c b/drivers/gpu/drm/sun4i/sun8i_layer.c deleted file mode 100644 index 23810ff72684..000000000000 --- a/drivers/gpu/drm/sun4i/sun8i_layer.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) Icenowy Zheng <icenowy@aosc.io> - * - * Based on sun4i_layer.h, which is: - * Copyright (C) 2015 Free Electrons - * Copyright (C) 2015 NextThing Co - * - * Maxime Ripard <maxime.ripard@free-electrons.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - */ - -#include <drm/drm_atomic_helper.h> -#include <drm/drm_plane_helper.h> -#include <drm/drmP.h> - -#include "sun8i_layer.h" -#include "sun8i_mixer.h" - -struct sun8i_plane_desc { - enum drm_plane_type type; - const uint32_t *formats; - uint32_t nformats; -}; - -static void sun8i_mixer_layer_atomic_disable(struct drm_plane *plane, - struct drm_plane_state *old_state) -{ - struct sun8i_layer *layer = plane_to_sun8i_layer(plane); - struct sun8i_mixer *mixer = layer->mixer; - - sun8i_mixer_layer_enable(mixer, layer->id, false); -} - -static void sun8i_mixer_layer_atomic_update(struct drm_plane *plane, - struct drm_plane_state *old_state) -{ - struct sun8i_layer *layer = plane_to_sun8i_layer(plane); - struct sun8i_mixer *mixer = layer->mixer; - - sun8i_mixer_update_layer_coord(mixer, layer->id, plane); - sun8i_mixer_update_layer_formats(mixer, layer->id, plane); - sun8i_mixer_update_layer_buffer(mixer, layer->id, plane); - sun8i_mixer_layer_enable(mixer, layer->id, true); -} - -static struct drm_plane_helper_funcs sun8i_mixer_layer_helper_funcs = { - .atomic_disable = sun8i_mixer_layer_atomic_disable, - .atomic_update = sun8i_mixer_layer_atomic_update, -}; - -static const struct drm_plane_funcs sun8i_mixer_layer_funcs = { - .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, - .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, - .destroy = drm_plane_cleanup, - .disable_plane = drm_atomic_helper_disable_plane, - .reset = drm_atomic_helper_plane_reset, - .update_plane = drm_atomic_helper_update_plane, -}; - -static const uint32_t sun8i_mixer_layer_formats[] = { - DRM_FORMAT_RGB888, - DRM_FORMAT_ARGB8888, - DRM_FORMAT_XRGB8888, -}; - -static const struct sun8i_plane_desc sun8i_mixer_planes[] = { - { - .type = DRM_PLANE_TYPE_PRIMARY, - .formats = sun8i_mixer_layer_formats, - .nformats = ARRAY_SIZE(sun8i_mixer_layer_formats), - }, -}; - -static struct sun8i_layer *sun8i_layer_init_one(struct drm_device *drm, - struct sun8i_mixer *mixer, - const struct sun8i_plane_desc *plane) -{ - struct sun8i_layer *layer; - int ret; - - layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL); - if (!layer) - return ERR_PTR(-ENOMEM); - - /* possible crtcs are set later */ - ret = drm_universal_plane_init(drm, &layer->plane, 0, - &sun8i_mixer_layer_funcs, - plane->formats, plane->nformats, - NULL, plane->type, NULL); - if (ret) { - dev_err(drm->dev, "Couldn't initialize layer\n"); - return ERR_PTR(ret); - } - - drm_plane_helper_add(&layer->plane, - &sun8i_mixer_layer_helper_funcs); - layer->mixer = mixer; - - return layer; -} - -struct drm_plane **sun8i_layers_init(struct drm_device *drm, - struct sunxi_engine *engine) -{ - struct drm_plane **planes; - struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine); - int i; - - planes = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes) + 1, - sizeof(*planes), GFP_KERNEL); - if (!planes) - return ERR_PTR(-ENOMEM); - - for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) { - const struct sun8i_plane_desc *plane = &sun8i_mixer_planes[i]; - struct sun8i_layer *layer; - - layer = sun8i_layer_init_one(drm, mixer, plane); - if (IS_ERR(layer)) { - dev_err(drm->dev, "Couldn't initialize %s plane\n", - i ? "overlay" : "primary"); - return ERR_CAST(layer); - }; - - layer->id = i; - planes[i] = &layer->plane; - }; - - return planes; -} diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.h b/drivers/gpu/drm/sun4i/sun8i_layer.h deleted file mode 100644 index e5eccd27cff0..000000000000 --- a/drivers/gpu/drm/sun4i/sun8i_layer.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) Icenowy Zheng <icenowy@aosc.io> - * - * Based on sun4i_layer.h, which is: - * Copyright (C) 2015 Free Electrons - * Copyright (C) 2015 NextThing Co - * - * Maxime Ripard <maxime.ripard@free-electrons.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - */ - -#ifndef _SUN8I_LAYER_H_ -#define _SUN8I_LAYER_H_ - -struct sunxi_engine; - -struct sun8i_layer { - struct drm_plane plane; - struct sun4i_drv *drv; - struct sun8i_mixer *mixer; - int id; -}; - -static inline struct sun8i_layer * -plane_to_sun8i_layer(struct drm_plane *plane) -{ - return container_of(plane, struct sun8i_layer, plane); -} - -struct drm_plane **sun8i_layers_init(struct drm_device *drm, - struct sunxi_engine *engine); -#endif /* _SUN8I_LAYER_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c index cb193c5f1686..2cbb2de6d39c 100644 --- a/drivers/gpu/drm/sun4i/sun8i_mixer.c +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c @@ -26,204 +26,288 @@ #include "sun4i_drv.h" #include "sun8i_mixer.h" -#include "sun8i_layer.h" +#include "sun8i_ui_layer.h" +#include "sun8i_vi_layer.h" #include "sunxi_engine.h" -static void sun8i_mixer_commit(struct sunxi_engine *engine) -{ - DRM_DEBUG_DRIVER("Committing changes\n"); - - regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF, - SUN8I_MIXER_GLOBAL_DBUFF_ENABLE); -} - -void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer, - int layer, bool enable) -{ - u32 val; - /* Currently the first UI channel is used */ - int chan = mixer->cfg->vi_num; - - DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan); - - if (enable) - val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN; - else - val = 0; - - regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer), - SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val); - - /* Set the alpha configuration */ - regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer), - SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK, - SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF); - regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer), - SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK, - SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF); -} - -static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane, - u32 format, u32 *mode) -{ - switch (format) { - case DRM_FORMAT_ARGB8888: - *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888; - break; - - case DRM_FORMAT_XRGB8888: - *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888; - break; - - case DRM_FORMAT_RGB888: - *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888; - break; - - default: - return -EINVAL; - } - - return 0; -} +static const struct de2_fmt_info de2_formats[] = { + { + .drm_fmt = DRM_FORMAT_ARGB8888, + .de2_fmt = SUN8I_MIXER_FBFMT_ARGB8888, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_ABGR8888, + .de2_fmt = SUN8I_MIXER_FBFMT_ABGR8888, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_RGBA8888, + .de2_fmt = SUN8I_MIXER_FBFMT_RGBA8888, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_BGRA8888, + .de2_fmt = SUN8I_MIXER_FBFMT_BGRA8888, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_XRGB8888, + .de2_fmt = SUN8I_MIXER_FBFMT_XRGB8888, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_XBGR8888, + .de2_fmt = SUN8I_MIXER_FBFMT_XBGR8888, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_RGBX8888, + .de2_fmt = SUN8I_MIXER_FBFMT_RGBX8888, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_BGRX8888, + .de2_fmt = SUN8I_MIXER_FBFMT_BGRX8888, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_RGB888, + .de2_fmt = SUN8I_MIXER_FBFMT_RGB888, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_BGR888, + .de2_fmt = SUN8I_MIXER_FBFMT_BGR888, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_RGB565, + .de2_fmt = SUN8I_MIXER_FBFMT_RGB565, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_BGR565, + .de2_fmt = SUN8I_MIXER_FBFMT_BGR565, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_ARGB4444, + .de2_fmt = SUN8I_MIXER_FBFMT_ARGB4444, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_ABGR4444, + .de2_fmt = SUN8I_MIXER_FBFMT_ABGR4444, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_RGBA4444, + .de2_fmt = SUN8I_MIXER_FBFMT_RGBA4444, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_BGRA4444, + .de2_fmt = SUN8I_MIXER_FBFMT_BGRA4444, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_ARGB1555, + .de2_fmt = SUN8I_MIXER_FBFMT_ARGB1555, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_ABGR1555, + .de2_fmt = SUN8I_MIXER_FBFMT_ABGR1555, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_RGBA5551, + .de2_fmt = SUN8I_MIXER_FBFMT_RGBA5551, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_BGRA5551, + .de2_fmt = SUN8I_MIXER_FBFMT_BGRA5551, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, + { + .drm_fmt = DRM_FORMAT_UYVY, + .de2_fmt = SUN8I_MIXER_FBFMT_UYVY, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_VYUY, + .de2_fmt = SUN8I_MIXER_FBFMT_VYUY, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_YUYV, + .de2_fmt = SUN8I_MIXER_FBFMT_YUYV, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_YVYU, + .de2_fmt = SUN8I_MIXER_FBFMT_YVYU, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_NV16, + .de2_fmt = SUN8I_MIXER_FBFMT_NV16, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_NV61, + .de2_fmt = SUN8I_MIXER_FBFMT_NV61, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_NV12, + .de2_fmt = SUN8I_MIXER_FBFMT_NV12, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_NV21, + .de2_fmt = SUN8I_MIXER_FBFMT_NV21, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_YUV444, + .de2_fmt = SUN8I_MIXER_FBFMT_RGB888, + .rgb = true, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_YUV422, + .de2_fmt = SUN8I_MIXER_FBFMT_YUV422, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_YUV420, + .de2_fmt = SUN8I_MIXER_FBFMT_YUV420, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_YUV411, + .de2_fmt = SUN8I_MIXER_FBFMT_YUV411, + .rgb = false, + .csc = SUN8I_CSC_MODE_YUV2RGB, + }, + { + .drm_fmt = DRM_FORMAT_YVU444, + .de2_fmt = SUN8I_MIXER_FBFMT_RGB888, + .rgb = true, + .csc = SUN8I_CSC_MODE_YVU2RGB, + }, + { + .drm_fmt = DRM_FORMAT_YVU422, + .de2_fmt = SUN8I_MIXER_FBFMT_YUV422, + .rgb = false, + .csc = SUN8I_CSC_MODE_YVU2RGB, + }, + { + .drm_fmt = DRM_FORMAT_YVU420, + .de2_fmt = SUN8I_MIXER_FBFMT_YUV420, + .rgb = false, + .csc = SUN8I_CSC_MODE_YVU2RGB, + }, + { + .drm_fmt = DRM_FORMAT_YVU411, + .de2_fmt = SUN8I_MIXER_FBFMT_YUV411, + .rgb = false, + .csc = SUN8I_CSC_MODE_YVU2RGB, + }, +}; -int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer, - int layer, struct drm_plane *plane) +const struct de2_fmt_info *sun8i_mixer_format_info(u32 format) { - struct drm_plane_state *state = plane->state; - struct drm_framebuffer *fb = state->fb; - /* Currently the first UI channel is used */ - int chan = mixer->cfg->vi_num; - - DRM_DEBUG_DRIVER("Updating layer %d\n", layer); - - if (plane->type == DRM_PLANE_TYPE_PRIMARY) { - DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n", - state->crtc_w, state->crtc_h); - regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_SIZE, - SUN8I_MIXER_SIZE(state->crtc_w, - state->crtc_h)); - DRM_DEBUG_DRIVER("Updating blender size\n"); - regmap_write(mixer->engine.regs, - SUN8I_MIXER_BLEND_ATTR_INSIZE(0), - SUN8I_MIXER_SIZE(state->crtc_w, - state->crtc_h)); - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE, - SUN8I_MIXER_SIZE(state->crtc_w, - state->crtc_h)); - DRM_DEBUG_DRIVER("Updating channel size\n"); - regmap_write(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan), - SUN8I_MIXER_SIZE(state->crtc_w, - state->crtc_h)); - } + unsigned int i; - /* Set the line width */ - DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]); - regmap_write(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer), - fb->pitches[0]); - - /* Set height and width */ - DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n", - state->crtc_w, state->crtc_h); - regmap_write(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer), - SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h)); - - /* Set base coordinates */ - DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n", - state->crtc_x, state->crtc_y); - regmap_write(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer), - SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y)); + for (i = 0; i < ARRAY_SIZE(de2_formats); ++i) + if (de2_formats[i].drm_fmt == format) + return &de2_formats[i]; - return 0; + return NULL; } -int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer, - int layer, struct drm_plane *plane) +static void sun8i_mixer_commit(struct sunxi_engine *engine) { - struct drm_plane_state *state = plane->state; - struct drm_framebuffer *fb = state->fb; - bool interlaced = false; - u32 val; - /* Currently the first UI channel is used */ - int chan = mixer->cfg->vi_num; - int ret; - - if (plane->state->crtc) - interlaced = plane->state->crtc->state->adjusted_mode.flags - & DRM_MODE_FLAG_INTERLACE; - - regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTCTL, - SUN8I_MIXER_BLEND_OUTCTL_INTERLACED, - interlaced ? - SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0); - - DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n", - interlaced ? "on" : "off"); - - ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format, - &val); - if (ret) { - DRM_DEBUG_DRIVER("Invalid format\n"); - return ret; - } - - regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer), - SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val); + DRM_DEBUG_DRIVER("Committing changes\n"); - return 0; + regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF, + SUN8I_MIXER_GLOBAL_DBUFF_ENABLE); } -int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer, - int layer, struct drm_plane *plane) +static struct drm_plane **sun8i_layers_init(struct drm_device *drm, + struct sunxi_engine *engine) { - struct drm_plane_state *state = plane->state; - struct drm_framebuffer *fb = state->fb; - struct drm_gem_cma_object *gem; - dma_addr_t paddr; - /* Currently the first UI channel is used */ - int chan = mixer->cfg->vi_num; - int bpp; - - /* Get the physical address of the buffer in memory */ - gem = drm_fb_cma_get_gem_obj(fb, 0); - - DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr); - - /* Compute the start of the displayed memory */ - bpp = fb->format->cpp[0]; - paddr = gem->paddr + fb->offsets[0]; - - /* Fixup framebuffer address for src coordinates */ - paddr += (state->src_x >> 16) * bpp; - paddr += (state->src_y >> 16) * fb->pitches[0]; - - /* - * The hardware cannot correctly deal with negative crtc - * coordinates, the display is cropped to the requested size, - * but the display content is not moved. - * Manually move the display content by fixup the framebuffer - * address when crtc_x or crtc_y is negative, like what we - * have did for src_x and src_y. - */ - if (state->crtc_x < 0) - paddr += -state->crtc_x * bpp; - if (state->crtc_y < 0) - paddr += -state->crtc_y * fb->pitches[0]; - - DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr); - - regmap_write(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer), - lower_32_bits(paddr)); - - return 0; + struct drm_plane **planes; + struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine); + int i; + + planes = devm_kcalloc(drm->dev, + mixer->cfg->vi_num + mixer->cfg->ui_num + 1, + sizeof(*planes), GFP_KERNEL); + if (!planes) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < mixer->cfg->vi_num; i++) { + struct sun8i_vi_layer *layer; + + layer = sun8i_vi_layer_init_one(drm, mixer, i); + if (IS_ERR(layer)) { + dev_err(drm->dev, + "Couldn't initialize overlay plane\n"); + return ERR_CAST(layer); + }; + + planes[i] = &layer->plane; + }; + + for (i = 0; i < mixer->cfg->ui_num; i++) { + struct sun8i_ui_layer *layer; + + layer = sun8i_ui_layer_init_one(drm, mixer, i); + if (IS_ERR(layer)) { + dev_err(drm->dev, "Couldn't initialize %s plane\n", + i ? "overlay" : "primary"); + return ERR_CAST(layer); + }; + + planes[mixer->cfg->vi_num + i] = &layer->plane; + }; + + return planes; } static const struct sunxi_engine_ops sun8i_engine_ops = { @@ -247,6 +331,7 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, struct sun8i_mixer *mixer; struct resource *res; void __iomem *regs; + int plane_cnt; int i, ret; /* @@ -313,6 +398,15 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, ret = PTR_ERR(mixer->mod_clk); goto err_disable_bus_clk; } + + /* + * It seems that we need to enforce that rate for whatever + * reason for the mixer to be functional. Make sure it's the + * case. + */ + if (mixer->cfg->mod_rate) + clk_set_rate(mixer->mod_clk, mixer->cfg->mod_rate); + clk_prepare_enable(mixer->mod_clk); list_add_tail(&mixer->engine.list, &drv->engine_list); @@ -325,27 +419,26 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL, SUN8I_MIXER_GLOBAL_CTL_RT_EN); - /* Initialize blender */ - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_FCOLOR_CTL, - SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF); - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PREMULTIPLY, - SUN8I_MIXER_BLEND_PREMULTIPLY_DEF); + /* Set background color to black */ regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR, - SUN8I_MIXER_BLEND_BKCOLOR_DEF); - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_MODE(0), - SUN8I_MIXER_BLEND_MODE_DEF); - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_CK_CTL, - SUN8I_MIXER_BLEND_CK_CTL_DEF); - - regmap_write(mixer->engine.regs, - SUN8I_MIXER_BLEND_ATTR_FCOLOR(0), - SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF); - - /* Select the first UI channel */ - DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n", - mixer->cfg->vi_num); - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ROUTE, - mixer->cfg->vi_num); + SUN8I_MIXER_BLEND_COLOR_BLACK); + + /* + * Set fill color of bottom plane to black. Generally not needed + * except when VI plane is at bottom (zpos = 0) and enabled. + */ + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL, + SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0)); + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(0), + SUN8I_MIXER_BLEND_COLOR_BLACK); + + /* Fixed zpos for now */ + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ROUTE, 0x43210); + + plane_cnt = mixer->cfg->vi_num + mixer->cfg->ui_num; + for (i = 0; i < plane_cnt; i++) + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_MODE(i), + SUN8I_MIXER_BLEND_MODE_DEF); return 0; @@ -385,13 +478,27 @@ static int sun8i_mixer_remove(struct platform_device *pdev) return 0; } +static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = { + .ccsc = 0, + .scaler_mask = 0xf, + .ui_num = 3, + .vi_num = 1, +}; + static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = { .vi_num = 2, .ui_num = 1, + .scaler_mask = 0x3, + .ccsc = 0, + .mod_rate = 150000000, }; static const struct of_device_id sun8i_mixer_of_table[] = { { + .compatible = "allwinner,sun8i-a83t-de2-mixer-0", + .data = &sun8i_a83t_mixer0_cfg, + }, + { .compatible = "allwinner,sun8i-v3s-de2-mixer", .data = &sun8i_v3s_mixer_cfg, }, diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h index 4785ac090b8c..f34e70c42adf 100644 --- a/drivers/gpu/drm/sun4i/sun8i_mixer.h +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h @@ -14,10 +14,9 @@ #include <linux/regmap.h> #include <linux/reset.h> +#include "sun8i_csc.h" #include "sunxi_engine.h" -#define SUN8I_MIXER_MAX_CHAN_COUNT 4 - #define SUN8I_MIXER_SIZE(w, h) (((h) - 1) << 16 | ((w) - 1)) #define SUN8I_MIXER_COORD(x, y) ((y) << 16 | (x)) @@ -26,14 +25,14 @@ #define SUN8I_MIXER_GLOBAL_DBUFF 0x8 #define SUN8I_MIXER_GLOBAL_SIZE 0xc -#define SUN8I_MIXER_GLOBAL_CTL_RT_EN 0x1 +#define SUN8I_MIXER_GLOBAL_CTL_RT_EN BIT(0) -#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE 0x1 +#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE BIT(0) -#define SUN8I_MIXER_BLEND_FCOLOR_CTL 0x1000 +#define SUN8I_MIXER_BLEND_PIPE_CTL 0x1000 #define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x) (0x1004 + 0x10 * (x) + 0x0) #define SUN8I_MIXER_BLEND_ATTR_INSIZE(x) (0x1004 + 0x10 * (x) + 0x4) -#define SUN8I_MIXER_BLEND_ATTR_OFFSET(x) (0x1004 + 0x10 * (x) + 0x8) +#define SUN8I_MIXER_BLEND_ATTR_COORD(x) (0x1004 + 0x10 * (x) + 0x8) #define SUN8I_MIXER_BLEND_ROUTE 0x1080 #define SUN8I_MIXER_BLEND_PREMULTIPLY 0x1084 #define SUN8I_MIXER_BLEND_BKCOLOR 0x1088 @@ -45,57 +44,56 @@ #define SUN8I_MIXER_BLEND_CK_MIN(x) (0x10e0 + 0x04 * (x)) #define SUN8I_MIXER_BLEND_OUTCTL 0x10fc +#define SUN8I_MIXER_BLEND_PIPE_CTL_EN(pipe) BIT(8 + pipe) +#define SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(pipe) BIT(pipe) +/* colors are always in AARRGGBB format */ +#define SUN8I_MIXER_BLEND_COLOR_BLACK 0xff000000 /* The following numbers are some still unknown magic numbers */ -#define SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF 0xff000000 -#define SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF 0x00000101 -#define SUN8I_MIXER_BLEND_PREMULTIPLY_DEF 0x0 -#define SUN8I_MIXER_BLEND_BKCOLOR_DEF 0xff000000 #define SUN8I_MIXER_BLEND_MODE_DEF 0x03010301 -#define SUN8I_MIXER_BLEND_CK_CTL_DEF 0x0 #define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED BIT(1) -/* - * VI channels are not used now, but the support of them may be introduced in - * the future. - */ - -#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0) -#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4) -#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8) -#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc) -#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10) -#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14) -#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18) -#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch) (0x2000 + 0x1000 * (ch) + 0x80) -#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch) (0x2000 + 0x1000 * (ch) + 0x84) -#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch) (0x2000 + 0x1000 * (ch) + 0x88) - -#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN BIT(0) -#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK GENMASK(2, 1) -#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK GENMASK(11, 8) -#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK GENMASK(31, 24) -#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF (1 << 1) -#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888 (0 << 8) -#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888 (4 << 8) -#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888 (8 << 8) -#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF (0xff << 24) +#define SUN8I_MIXER_FBFMT_ARGB8888 0 +#define SUN8I_MIXER_FBFMT_ABGR8888 1 +#define SUN8I_MIXER_FBFMT_RGBA8888 2 +#define SUN8I_MIXER_FBFMT_BGRA8888 3 +#define SUN8I_MIXER_FBFMT_XRGB8888 4 +#define SUN8I_MIXER_FBFMT_XBGR8888 5 +#define SUN8I_MIXER_FBFMT_RGBX8888 6 +#define SUN8I_MIXER_FBFMT_BGRX8888 7 +#define SUN8I_MIXER_FBFMT_RGB888 8 +#define SUN8I_MIXER_FBFMT_BGR888 9 +#define SUN8I_MIXER_FBFMT_RGB565 10 +#define SUN8I_MIXER_FBFMT_BGR565 11 +#define SUN8I_MIXER_FBFMT_ARGB4444 12 +#define SUN8I_MIXER_FBFMT_ABGR4444 13 +#define SUN8I_MIXER_FBFMT_RGBA4444 14 +#define SUN8I_MIXER_FBFMT_BGRA4444 15 +#define SUN8I_MIXER_FBFMT_ARGB1555 16 +#define SUN8I_MIXER_FBFMT_ABGR1555 17 +#define SUN8I_MIXER_FBFMT_RGBA5551 18 +#define SUN8I_MIXER_FBFMT_BGRA5551 19 + +#define SUN8I_MIXER_FBFMT_YUYV 0 +#define SUN8I_MIXER_FBFMT_UYVY 1 +#define SUN8I_MIXER_FBFMT_YVYU 2 +#define SUN8I_MIXER_FBFMT_VYUY 3 +#define SUN8I_MIXER_FBFMT_NV16 4 +#define SUN8I_MIXER_FBFMT_NV61 5 +#define SUN8I_MIXER_FBFMT_YUV422 6 +/* format 7 doesn't exist */ +#define SUN8I_MIXER_FBFMT_NV12 8 +#define SUN8I_MIXER_FBFMT_NV21 9 +#define SUN8I_MIXER_FBFMT_YUV420 10 +/* format 11 doesn't exist */ +/* format 12 is semi-planar YUV411 UVUV */ +/* format 13 is semi-planar YUV411 VUVU */ +#define SUN8I_MIXER_FBFMT_YUV411 14 /* * These sub-engines are still unknown now, the EN registers are here only to * be used to disable these sub-engines. */ -#define SUN8I_MIXER_VSU_EN 0x20000 -#define SUN8I_MIXER_GSU1_EN 0x30000 -#define SUN8I_MIXER_GSU2_EN 0x40000 -#define SUN8I_MIXER_GSU3_EN 0x50000 #define SUN8I_MIXER_FCE_EN 0xa0000 #define SUN8I_MIXER_BWS_EN 0xa2000 #define SUN8I_MIXER_LTI_EN 0xa4000 @@ -104,9 +102,34 @@ #define SUN8I_MIXER_FCC_EN 0xaa000 #define SUN8I_MIXER_DCSC_EN 0xb0000 +struct de2_fmt_info { + u32 drm_fmt; + u32 de2_fmt; + bool rgb; + enum sun8i_csc_mode csc; +}; + +/** + * struct sun8i_mixer_cfg - mixer HW configuration + * @vi_num: number of VI channels + * @ui_num: number of UI channels + * @scaler_mask: bitmask which tells which channel supports scaling + * First, scaler supports for VI channels is defined and after that, scaler + * support for UI channels. For example, if mixer has 2 VI channels without + * scaler and 2 UI channels with scaler, bitmask would be 0xC. + * @ccsc: select set of CCSC base addresses + * Set value to 0 if this is first mixer or second mixer with VEP support. + * Set value to 1 if this is second mixer without VEP support. Other values + * are invalid. + * @mod_rate: module clock rate that needs to be set in order to have + * a functional block. + */ struct sun8i_mixer_cfg { int vi_num; int ui_num; + int scaler_mask; + int ccsc; + unsigned long mod_rate; }; struct sun8i_mixer { @@ -126,12 +149,5 @@ engine_to_sun8i_mixer(struct sunxi_engine *engine) return container_of(engine, struct sun8i_mixer, engine); } -void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer, - int layer, bool enable); -int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer, - int layer, struct drm_plane *plane); -int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer, - int layer, struct drm_plane *plane); -int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer, - int layer, struct drm_plane *plane); +const struct de2_fmt_info *sun8i_mixer_format_info(u32 format); #endif /* _SUN8I_MIXER_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c new file mode 100644 index 000000000000..28d7c48d50fe --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c @@ -0,0 +1,349 @@ +/* + * Copyright (C) Icenowy Zheng <icenowy@aosc.io> + * + * Based on sun4i_layer.h, which is: + * Copyright (C) 2015 Free Electrons + * Copyright (C) 2015 NextThing Co + * + * Maxime Ripard <maxime.ripard@free-electrons.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_plane_helper.h> +#include <drm/drmP.h> + +#include "sun8i_ui_layer.h" +#include "sun8i_mixer.h" +#include "sun8i_ui_scaler.h" + +static void sun8i_ui_layer_enable(struct sun8i_mixer *mixer, int channel, + int overlay, bool enable) +{ + u32 val; + + DRM_DEBUG_DRIVER("%sabling channel %d overlay %d\n", + enable ? "En" : "Dis", channel, overlay); + + if (enable) + val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN; + else + val = 0; + + regmap_update_bits(mixer->engine.regs, + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(channel, overlay), + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val); + + if (enable) + val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel); + else + val = 0; + + regmap_update_bits(mixer->engine.regs, + SUN8I_MIXER_BLEND_PIPE_CTL, + SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel), val); +} + +static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel, + int overlay, struct drm_plane *plane) +{ + struct drm_plane_state *state = plane->state; + u32 src_w, src_h, dst_w, dst_h; + u32 outsize, insize; + u32 hphase, vphase; + + DRM_DEBUG_DRIVER("Updating UI channel %d overlay %d\n", + channel, overlay); + + src_w = drm_rect_width(&state->src) >> 16; + src_h = drm_rect_height(&state->src) >> 16; + dst_w = drm_rect_width(&state->dst); + dst_h = drm_rect_height(&state->dst); + + hphase = state->src.x1 & 0xffff; + vphase = state->src.y1 & 0xffff; + + insize = SUN8I_MIXER_SIZE(src_w, src_h); + outsize = SUN8I_MIXER_SIZE(dst_w, dst_h); + + if (plane->type == DRM_PLANE_TYPE_PRIMARY) { + bool interlaced = false; + u32 val; + + DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n", + dst_w, dst_h); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_GLOBAL_SIZE, + outsize); + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE, + outsize); + + if (state->crtc) + interlaced = state->crtc->state->adjusted_mode.flags + & DRM_MODE_FLAG_INTERLACE; + + if (interlaced) + val = SUN8I_MIXER_BLEND_OUTCTL_INTERLACED; + else + val = 0; + + regmap_update_bits(mixer->engine.regs, + SUN8I_MIXER_BLEND_OUTCTL, + SUN8I_MIXER_BLEND_OUTCTL_INTERLACED, + val); + + DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n", + interlaced ? "on" : "off"); + } + + /* Set height and width */ + DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n", + state->src.x1 >> 16, state->src.y1 >> 16); + DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_CHAN_UI_LAYER_SIZE(channel, overlay), + insize); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_CHAN_UI_OVL_SIZE(channel), + insize); + + if (insize != outsize || hphase || vphase) { + u32 hscale, vscale; + + DRM_DEBUG_DRIVER("HW scaling is enabled\n"); + + hscale = state->src_w / state->crtc_w; + vscale = state->src_h / state->crtc_h; + + sun8i_ui_scaler_setup(mixer, channel, src_w, src_h, dst_w, + dst_h, hscale, vscale, hphase, vphase); + sun8i_ui_scaler_enable(mixer, channel, true); + } else { + DRM_DEBUG_DRIVER("HW scaling is not needed\n"); + sun8i_ui_scaler_enable(mixer, channel, false); + } + + /* Set base coordinates */ + DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n", + state->dst.x1, state->dst.y1); + DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_BLEND_ATTR_COORD(channel), + SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1)); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_BLEND_ATTR_INSIZE(channel), + outsize); + + return 0; +} + +static int sun8i_ui_layer_update_formats(struct sun8i_mixer *mixer, int channel, + int overlay, struct drm_plane *plane) +{ + struct drm_plane_state *state = plane->state; + const struct de2_fmt_info *fmt_info; + u32 val; + + fmt_info = sun8i_mixer_format_info(state->fb->format->format); + if (!fmt_info || !fmt_info->rgb) { + DRM_DEBUG_DRIVER("Invalid format\n"); + return -EINVAL; + } + + val = fmt_info->de2_fmt << SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET; + regmap_update_bits(mixer->engine.regs, + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(channel, overlay), + SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val); + + return 0; +} + +static int sun8i_ui_layer_update_buffer(struct sun8i_mixer *mixer, int channel, + int overlay, struct drm_plane *plane) +{ + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + struct drm_gem_cma_object *gem; + dma_addr_t paddr; + int bpp; + + /* Get the physical address of the buffer in memory */ + gem = drm_fb_cma_get_gem_obj(fb, 0); + + DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr); + + /* Compute the start of the displayed memory */ + bpp = fb->format->cpp[0]; + paddr = gem->paddr + fb->offsets[0]; + + /* Fixup framebuffer address for src coordinates */ + paddr += (state->src.x1 >> 16) * bpp; + paddr += (state->src.y1 >> 16) * fb->pitches[0]; + + /* Set the line width */ + DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_CHAN_UI_LAYER_PITCH(channel, overlay), + fb->pitches[0]); + + DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr); + + regmap_write(mixer->engine.regs, + SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(channel, overlay), + lower_32_bits(paddr)); + + return 0; +} + +static int sun8i_ui_layer_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane); + struct drm_crtc *crtc = state->crtc; + struct drm_crtc_state *crtc_state; + int min_scale, max_scale; + struct drm_rect clip; + + if (!crtc) + return 0; + + crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); + if (WARN_ON(!crtc_state)) + return -EINVAL; + + clip.x1 = 0; + clip.y1 = 0; + clip.x2 = crtc_state->adjusted_mode.hdisplay; + clip.y2 = crtc_state->adjusted_mode.vdisplay; + + min_scale = DRM_PLANE_HELPER_NO_SCALING; + max_scale = DRM_PLANE_HELPER_NO_SCALING; + + if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) { + min_scale = SUN8I_UI_SCALER_SCALE_MIN; + max_scale = SUN8I_UI_SCALER_SCALE_MAX; + } + + return drm_atomic_helper_check_plane_state(state, crtc_state, &clip, + min_scale, max_scale, + true, true); +} + +static void sun8i_ui_layer_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane); + struct sun8i_mixer *mixer = layer->mixer; + + sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, false); +} + +static void sun8i_ui_layer_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane); + struct sun8i_mixer *mixer = layer->mixer; + + if (!plane->state->visible) { + sun8i_ui_layer_enable(mixer, layer->channel, + layer->overlay, false); + return; + } + + sun8i_ui_layer_update_coord(mixer, layer->channel, + layer->overlay, plane); + sun8i_ui_layer_update_formats(mixer, layer->channel, + layer->overlay, plane); + sun8i_ui_layer_update_buffer(mixer, layer->channel, + layer->overlay, plane); + sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, true); +} + +static struct drm_plane_helper_funcs sun8i_ui_layer_helper_funcs = { + .atomic_check = sun8i_ui_layer_atomic_check, + .atomic_disable = sun8i_ui_layer_atomic_disable, + .atomic_update = sun8i_ui_layer_atomic_update, +}; + +static const struct drm_plane_funcs sun8i_ui_layer_funcs = { + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .destroy = drm_plane_cleanup, + .disable_plane = drm_atomic_helper_disable_plane, + .reset = drm_atomic_helper_plane_reset, + .update_plane = drm_atomic_helper_update_plane, +}; + +static const u32 sun8i_ui_layer_formats[] = { + DRM_FORMAT_ABGR1555, + DRM_FORMAT_ABGR4444, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_BGR565, + DRM_FORMAT_BGR888, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_BGRA4444, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_BGRX8888, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGB888, + DRM_FORMAT_RGBA4444, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_XRGB8888, +}; + +struct sun8i_ui_layer *sun8i_ui_layer_init_one(struct drm_device *drm, + struct sun8i_mixer *mixer, + int index) +{ + enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY; + int channel = mixer->cfg->vi_num + index; + struct sun8i_ui_layer *layer; + int ret; + + layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL); + if (!layer) + return ERR_PTR(-ENOMEM); + + if (index == 0) + type = DRM_PLANE_TYPE_PRIMARY; + + /* possible crtcs are set later */ + ret = drm_universal_plane_init(drm, &layer->plane, 0, + &sun8i_ui_layer_funcs, + sun8i_ui_layer_formats, + ARRAY_SIZE(sun8i_ui_layer_formats), + NULL, type, NULL); + if (ret) { + dev_err(drm->dev, "Couldn't initialize layer\n"); + return ERR_PTR(ret); + } + + /* fixed zpos for now */ + ret = drm_plane_create_zpos_immutable_property(&layer->plane, channel); + if (ret) { + dev_err(drm->dev, "Couldn't add zpos property\n"); + return ERR_PTR(ret); + } + + drm_plane_helper_add(&layer->plane, &sun8i_ui_layer_helper_funcs); + layer->mixer = mixer; + layer->channel = channel; + layer->overlay = 0; + + return layer; +} diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.h b/drivers/gpu/drm/sun4i/sun8i_ui_layer.h new file mode 100644 index 000000000000..123b15ea9918 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) Icenowy Zheng <icenowy@aosc.io> + * + * Based on sun4i_layer.h, which is: + * Copyright (C) 2015 Free Electrons + * Copyright (C) 2015 NextThing Co + * + * Maxime Ripard <maxime.ripard@free-electrons.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#ifndef _SUN8I_UI_LAYER_H_ +#define _SUN8I_UI_LAYER_H_ + +#include <drm/drm_plane.h> + +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \ + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0) +#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \ + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4) +#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \ + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8) +#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \ + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc) +#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \ + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10) +#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \ + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14) +#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \ + (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18) +#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch) (0x2000 + 0x1000 * (ch) + 0x80) +#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch) (0x2000 + 0x1000 * (ch) + 0x84) +#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch) (0x2000 + 0x1000 * (ch) + 0x88) + +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN BIT(0) +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK GENMASK(2, 1) +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK GENMASK(12, 8) +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET 8 +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK GENMASK(31, 24) + +struct sun8i_mixer; + +struct sun8i_ui_layer { + struct drm_plane plane; + struct sun8i_mixer *mixer; + int channel; + int overlay; +}; + +static inline struct sun8i_ui_layer * +plane_to_sun8i_ui_layer(struct drm_plane *plane) +{ + return container_of(plane, struct sun8i_ui_layer, plane); +} + +struct sun8i_ui_layer *sun8i_ui_layer_init_one(struct drm_device *drm, + struct sun8i_mixer *mixer, + int index); +#endif /* _SUN8I_UI_LAYER_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c new file mode 100644 index 000000000000..6bb2aa164c8e --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2017 Jernej Skrabec <jernej.skrabec@siol.net> + * + * Coefficients are taken from BSP driver, which is: + * Copyright (C) 2014-2015 Allwinner + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include "sun8i_ui_scaler.h" + +static const u32 lan2coefftab16[240] = { + 0x00004000, 0x00033ffe, 0x00063efc, 0x000a3bfb, + 0xff0f37fb, 0xfe1433fb, 0xfd192ffb, 0xfd1f29fb, + 0xfc2424fc, 0xfb291ffd, 0xfb2f19fd, 0xfb3314fe, + 0xfb370fff, 0xfb3b0a00, 0xfc3e0600, 0xfe3f0300, + + 0xff053804, 0xff083801, 0xff0a3700, 0xff0e34ff, + 0xff1232fd, 0xfe162ffd, 0xfd1b2cfc, 0xfd1f28fc, + 0xfd2323fd, 0xfc281ffd, 0xfc2c1bfd, 0xfd2f16fe, + 0xfd3212ff, 0xff340eff, 0x00360a00, 0x02370700, + + 0xff083207, 0xff0a3205, 0xff0d3103, 0xfe113001, + 0xfe142e00, 0xfe182bff, 0xfe1b29fe, 0xfe1f25fe, + 0xfe2222fe, 0xfe251ffe, 0xfe291bfe, 0xff2b18fe, + 0x002e14fe, 0x013010ff, 0x03310dff, 0x05310a00, + + 0xff0a2e09, 0xff0c2e07, 0xff0f2d05, 0xff122c03, + 0xfe152b02, 0xfe182901, 0xfe1b2700, 0xff1e24ff, + 0xff2121ff, 0xff241eff, 0x00261bff, 0x012818ff, + 0x022a15ff, 0x032c12ff, 0x052d0fff, 0x072d0c00, + + 0xff0c2a0b, 0xff0e2a09, 0xff102a07, 0xff132905, + 0xff162803, 0xff182702, 0xff1b2501, 0xff1e2300, + 0x00202000, 0x01221d00, 0x01251bff, 0x032618ff, + 0x042815ff, 0x052913ff, 0x072a10ff, 0x092a0d00, + + 0xff0d280c, 0xff0f280a, 0xff112808, 0xff142706, + 0xff162605, 0xff192503, 0x001b2302, 0x001d2201, + 0x011f1f01, 0x01221d00, 0x02231b00, 0x04241800, + 0x052616ff, 0x072713ff, 0x08271100, 0x0a280e00, + + 0xff0e260d, 0xff10260b, 0xff122609, 0xff142508, + 0x00152506, 0x00182305, 0x001b2203, 0x011d2002, + 0x011f1f01, 0x02201d01, 0x03221b00, 0x04231801, + 0x06241600, 0x08251300, 0x09261100, 0x0b260f00, + + 0xff0e250e, 0xff10250c, 0x0011250a, 0x00142408, + 0x00162307, 0x00182206, 0x011a2104, 0x011c2003, + 0x021e1e02, 0x03201c01, 0x04211a01, 0x05221801, + 0x07231600, 0x08241400, 0x0a241200, 0x0c241000, + + 0x000e240e, 0x0010240c, 0x0013230a, 0x00142309, + 0x00162208, 0x01182106, 0x011a2005, 0x021b1f04, + 0x031d1d03, 0x041e1c02, 0x05201a01, 0x06211801, + 0x07221601, 0x09231400, 0x0a231300, 0x0c231100, + + 0x000f220f, 0x0011220d, 0x0013220b, 0x0015210a, + 0x01162108, 0x01182007, 0x02191f06, 0x031a1e05, + 0x041c1c04, 0x051d1b03, 0x061f1902, 0x07201801, + 0x08211601, 0x0a211500, 0x0b221300, 0x0d221100, + + 0x0010210f, 0x0011210e, 0x0013210c, 0x0114200b, + 0x01161f0a, 0x02171f08, 0x03181e07, 0x031a1d06, + 0x041c1c04, 0x051d1a04, 0x071d1903, 0x081e1802, + 0x091f1602, 0x0b1f1501, 0x0c211300, 0x0e201200, + + 0x00102010, 0x0012200e, 0x0013200d, 0x01151f0b, + 0x01161f0a, 0x02171e09, 0x03191d07, 0x041a1c06, + 0x051b1b05, 0x061c1a04, 0x071d1903, 0x081e1703, + 0x0a1f1601, 0x0b1f1501, 0x0d201300, 0x0e201200, + + 0x00102010, 0x00121f0f, 0x00141f0d, 0x01141f0c, + 0x02161e0a, 0x03171d09, 0x03181d08, 0x041a1c06, + 0x051b1b05, 0x061c1a04, 0x081c1903, 0x091d1703, + 0x0a1e1602, 0x0c1e1501, 0x0d1f1400, 0x0e1f1201, + + 0x00111e11, 0x00131e0f, 0x01131e0e, 0x02151d0c, + 0x02161d0b, 0x03171c0a, 0x04181b09, 0x05191b07, + 0x061a1a06, 0x071b1905, 0x091b1804, 0x0a1c1703, + 0x0b1d1602, 0x0c1d1502, 0x0e1d1401, 0x0f1e1300, + + 0x00111e11, 0x00131d10, 0x01141d0e, 0x02151c0d, + 0x03161c0b, 0x04171b0a, 0x05171b09, 0x06181a08, + 0x07191907, 0x081a1806, 0x091a1805, 0x0a1b1704, + 0x0b1c1603, 0x0d1c1502, 0x0e1d1401, 0x0f1d1301, +}; + +static int sun8i_ui_scaler_coef_index(unsigned int step) +{ + unsigned int scale, int_part, float_part; + + scale = step >> (SUN8I_UI_SCALER_SCALE_FRAC - 3); + int_part = scale >> 3; + float_part = scale & 0x7; + + switch (int_part) { + case 0: + return 0; + case 1: + return float_part; + case 2: + return 8 + (float_part >> 1); + case 3: + return 12; + case 4: + return 13; + default: + return 14; + } +} + +void sun8i_ui_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable) +{ + int vi_cnt = mixer->cfg->vi_num; + u32 val; + + if (WARN_ON(layer < vi_cnt)) + return; + + if (enable) + val = SUN8I_SCALER_GSU_CTRL_EN | + SUN8I_SCALER_GSU_CTRL_COEFF_RDY; + else + val = 0; + + regmap_write(mixer->engine.regs, + SUN8I_SCALER_GSU_CTRL(vi_cnt, layer - vi_cnt), val); +} + +void sun8i_ui_scaler_setup(struct sun8i_mixer *mixer, int layer, + u32 src_w, u32 src_h, u32 dst_w, u32 dst_h, + u32 hscale, u32 vscale, u32 hphase, u32 vphase) +{ + int vi_cnt = mixer->cfg->vi_num; + u32 insize, outsize; + int i, offset; + + if (WARN_ON(layer < vi_cnt)) + return; + + hphase <<= SUN8I_UI_SCALER_PHASE_FRAC - 16; + vphase <<= SUN8I_UI_SCALER_PHASE_FRAC - 16; + hscale <<= SUN8I_UI_SCALER_SCALE_FRAC - 16; + vscale <<= SUN8I_UI_SCALER_SCALE_FRAC - 16; + + insize = SUN8I_UI_SCALER_SIZE(src_w, src_h); + outsize = SUN8I_UI_SCALER_SIZE(dst_w, dst_h); + + layer -= vi_cnt; + + regmap_write(mixer->engine.regs, + SUN8I_SCALER_GSU_OUTSIZE(vi_cnt, layer), outsize); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_GSU_INSIZE(vi_cnt, layer), insize); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_GSU_HSTEP(vi_cnt, layer), hscale); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_GSU_VSTEP(vi_cnt, layer), vscale); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_GSU_HPHASE(vi_cnt, layer), hphase); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_GSU_VPHASE(vi_cnt, layer), vphase); + offset = sun8i_ui_scaler_coef_index(hscale) * + SUN8I_UI_SCALER_COEFF_COUNT; + for (i = 0; i < SUN8I_UI_SCALER_COEFF_COUNT; i++) + regmap_write(mixer->engine.regs, + SUN8I_SCALER_GSU_HCOEFF(vi_cnt, layer, i), + lan2coefftab16[offset + i]); +} diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_scaler.h b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.h new file mode 100644 index 000000000000..86295be8be78 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2017 Jernej Skrabec <jernej.skrabec@siol.net> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef _SUN8I_UI_SCALER_H_ +#define _SUN8I_UI_SCALER_H_ + +#include "sun8i_mixer.h" + +/* this two macros assumes 16 fractional bits which is standard in DRM */ +#define SUN8I_UI_SCALER_SCALE_MIN 1 +#define SUN8I_UI_SCALER_SCALE_MAX ((1UL << 20) - 1) + +#define SUN8I_UI_SCALER_SCALE_FRAC 20 +#define SUN8I_UI_SCALER_PHASE_FRAC 20 +#define SUN8I_UI_SCALER_COEFF_COUNT 16 +#define SUN8I_UI_SCALER_SIZE(w, h) (((h) - 1) << 16 | ((w) - 1)) + +#define SUN8I_SCALER_GSU_CTRL(vi_cnt, ui_idx) \ + (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x0) +#define SUN8I_SCALER_GSU_OUTSIZE(vi_cnt, ui_idx) \ + (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x40) +#define SUN8I_SCALER_GSU_INSIZE(vi_cnt, ui_idx) \ + (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x80) +#define SUN8I_SCALER_GSU_HSTEP(vi_cnt, ui_idx) \ + (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x88) +#define SUN8I_SCALER_GSU_VSTEP(vi_cnt, ui_idx) \ + (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x8c) +#define SUN8I_SCALER_GSU_HPHASE(vi_cnt, ui_idx) \ + (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x90) +#define SUN8I_SCALER_GSU_VPHASE(vi_cnt, ui_idx) \ + (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x98) +#define SUN8I_SCALER_GSU_HCOEFF(vi_cnt, ui_idx, index) \ + (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x200 + \ + 0x4 * (index)) + +#define SUN8I_SCALER_GSU_CTRL_EN BIT(0) +#define SUN8I_SCALER_GSU_CTRL_COEFF_RDY BIT(4) + +void sun8i_ui_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable); +void sun8i_ui_scaler_setup(struct sun8i_mixer *mixer, int layer, + u32 src_w, u32 src_h, u32 dst_w, u32 dst_h, + u32 hscale, u32 vscale, u32 hphase, u32 vphase); + +#endif diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c new file mode 100644 index 000000000000..40c3b303068a --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c @@ -0,0 +1,390 @@ +/* + * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_plane_helper.h> +#include <drm/drmP.h> + +#include "sun8i_vi_layer.h" +#include "sun8i_mixer.h" +#include "sun8i_vi_scaler.h" + +static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel, + int overlay, bool enable) +{ + u32 val; + + DRM_DEBUG_DRIVER("%sabling VI channel %d overlay %d\n", + enable ? "En" : "Dis", channel, overlay); + + if (enable) + val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN; + else + val = 0; + + regmap_update_bits(mixer->engine.regs, + SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay), + SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN, val); + + if (enable) + val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel); + else + val = 0; + + regmap_update_bits(mixer->engine.regs, + SUN8I_MIXER_BLEND_PIPE_CTL, + SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel), val); +} + +static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, + int overlay, struct drm_plane *plane) +{ + struct drm_plane_state *state = plane->state; + const struct drm_format_info *format = state->fb->format; + u32 src_w, src_h, dst_w, dst_h; + u32 outsize, insize; + u32 hphase, vphase; + bool subsampled; + + DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n", + channel, overlay); + + src_w = drm_rect_width(&state->src) >> 16; + src_h = drm_rect_height(&state->src) >> 16; + dst_w = drm_rect_width(&state->dst); + dst_h = drm_rect_height(&state->dst); + + hphase = state->src.x1 & 0xffff; + vphase = state->src.y1 & 0xffff; + + /* make coordinates dividable by subsampling factor */ + if (format->hsub > 1) { + int mask, remainder; + + mask = format->hsub - 1; + remainder = (state->src.x1 >> 16) & mask; + src_w = (src_w + remainder) & ~mask; + hphase += remainder << 16; + } + + if (format->vsub > 1) { + int mask, remainder; + + mask = format->vsub - 1; + remainder = (state->src.y1 >> 16) & mask; + src_h = (src_h + remainder) & ~mask; + vphase += remainder << 16; + } + + insize = SUN8I_MIXER_SIZE(src_w, src_h); + outsize = SUN8I_MIXER_SIZE(dst_w, dst_h); + + /* Set height and width */ + DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n", + (state->src.x1 >> 16) & ~(format->hsub - 1), + (state->src.y1 >> 16) & ~(format->vsub - 1)); + DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_CHAN_VI_LAYER_SIZE(channel, overlay), + insize); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_CHAN_VI_OVL_SIZE(channel), + insize); + + /* + * Scaler must be enabled for subsampled formats, so it scales + * chroma to same size as luma. + */ + subsampled = format->hsub > 1 || format->vsub > 1; + + if (insize != outsize || subsampled || hphase || vphase) { + u32 hscale, vscale; + + DRM_DEBUG_DRIVER("HW scaling is enabled\n"); + + hscale = state->src_w / state->crtc_w; + vscale = state->src_h / state->crtc_h; + + sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w, + dst_h, hscale, vscale, hphase, vphase, + format); + sun8i_vi_scaler_enable(mixer, channel, true); + } else { + DRM_DEBUG_DRIVER("HW scaling is not needed\n"); + sun8i_vi_scaler_enable(mixer, channel, false); + } + + /* Set base coordinates */ + DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n", + state->dst.x1, state->dst.y1); + DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_BLEND_ATTR_COORD(channel), + SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1)); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_BLEND_ATTR_INSIZE(channel), + outsize); + + return 0; +} + +static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel, + int overlay, struct drm_plane *plane) +{ + struct drm_plane_state *state = plane->state; + const struct de2_fmt_info *fmt_info; + u32 val; + + fmt_info = sun8i_mixer_format_info(state->fb->format->format); + if (!fmt_info) { + DRM_DEBUG_DRIVER("Invalid format\n"); + return -EINVAL; + } + + val = fmt_info->de2_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET; + regmap_update_bits(mixer->engine.regs, + SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay), + SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val); + + if (fmt_info->csc != SUN8I_CSC_MODE_OFF) { + sun8i_csc_set_ccsc_coefficients(mixer, channel, fmt_info->csc); + sun8i_csc_enable_ccsc(mixer, channel, true); + } else { + sun8i_csc_enable_ccsc(mixer, channel, false); + } + + if (fmt_info->rgb) + val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE; + else + val = 0; + + regmap_update_bits(mixer->engine.regs, + SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay), + SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE, val); + + return 0; +} + +static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel, + int overlay, struct drm_plane *plane) +{ + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + const struct drm_format_info *format = fb->format; + struct drm_gem_cma_object *gem; + u32 dx, dy, src_x, src_y; + dma_addr_t paddr; + int i; + + /* Adjust x and y to be dividable by subsampling factor */ + src_x = (state->src.x1 >> 16) & ~(format->hsub - 1); + src_y = (state->src.y1 >> 16) & ~(format->vsub - 1); + + for (i = 0; i < format->num_planes; i++) { + /* Get the physical address of the buffer in memory */ + gem = drm_fb_cma_get_gem_obj(fb, i); + + DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr); + + /* Compute the start of the displayed memory */ + paddr = gem->paddr + fb->offsets[i]; + + dx = src_x; + dy = src_y; + + if (i > 0) { + dx /= format->hsub; + dy /= format->vsub; + } + + /* Fixup framebuffer address for src coordinates */ + paddr += dx * format->cpp[i]; + paddr += dy * fb->pitches[i]; + + /* Set the line width */ + DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n", + i + 1, fb->pitches[i]); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_CHAN_VI_LAYER_PITCH(channel, + overlay, i), + fb->pitches[i]); + + DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n", + i + 1, &paddr); + + regmap_write(mixer->engine.regs, + SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(channel, + overlay, i), + lower_32_bits(paddr)); + } + + return 0; +} + +static int sun8i_vi_layer_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); + struct drm_crtc *crtc = state->crtc; + struct drm_crtc_state *crtc_state; + int min_scale, max_scale; + struct drm_rect clip; + + if (!crtc) + return 0; + + crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); + if (WARN_ON(!crtc_state)) + return -EINVAL; + + clip.x1 = 0; + clip.y1 = 0; + clip.x2 = crtc_state->adjusted_mode.hdisplay; + clip.y2 = crtc_state->adjusted_mode.vdisplay; + + min_scale = DRM_PLANE_HELPER_NO_SCALING; + max_scale = DRM_PLANE_HELPER_NO_SCALING; + + if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) { + min_scale = SUN8I_VI_SCALER_SCALE_MIN; + max_scale = SUN8I_VI_SCALER_SCALE_MAX; + } + + return drm_atomic_helper_check_plane_state(state, crtc_state, &clip, + min_scale, max_scale, + true, true); +} + +static void sun8i_vi_layer_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); + struct sun8i_mixer *mixer = layer->mixer; + + sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false); +} + +static void sun8i_vi_layer_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); + struct sun8i_mixer *mixer = layer->mixer; + + if (!plane->state->visible) { + sun8i_vi_layer_enable(mixer, layer->channel, + layer->overlay, false); + return; + } + + sun8i_vi_layer_update_coord(mixer, layer->channel, + layer->overlay, plane); + sun8i_vi_layer_update_formats(mixer, layer->channel, + layer->overlay, plane); + sun8i_vi_layer_update_buffer(mixer, layer->channel, + layer->overlay, plane); + sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, true); +} + +static struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = { + .atomic_check = sun8i_vi_layer_atomic_check, + .atomic_disable = sun8i_vi_layer_atomic_disable, + .atomic_update = sun8i_vi_layer_atomic_update, +}; + +static const struct drm_plane_funcs sun8i_vi_layer_funcs = { + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .destroy = drm_plane_cleanup, + .disable_plane = drm_atomic_helper_disable_plane, + .reset = drm_atomic_helper_plane_reset, + .update_plane = drm_atomic_helper_update_plane, +}; + +/* + * While all RGB formats are supported, VI planes don't support + * alpha blending, so there is no point having formats with alpha + * channel if their opaque analog exist. + */ +static const u32 sun8i_vi_layer_formats[] = { + DRM_FORMAT_ABGR1555, + DRM_FORMAT_ABGR4444, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_ARGB4444, + DRM_FORMAT_BGR565, + DRM_FORMAT_BGR888, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_BGRA4444, + DRM_FORMAT_BGRX8888, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGB888, + DRM_FORMAT_RGBA4444, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_XRGB8888, + + DRM_FORMAT_NV16, + DRM_FORMAT_NV12, + DRM_FORMAT_NV21, + DRM_FORMAT_NV61, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, + DRM_FORMAT_YUV411, + DRM_FORMAT_YUV420, + DRM_FORMAT_YUV422, + DRM_FORMAT_YUV444, + DRM_FORMAT_YVU411, + DRM_FORMAT_YVU420, + DRM_FORMAT_YVU422, + DRM_FORMAT_YVU444, +}; + +struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm, + struct sun8i_mixer *mixer, + int index) +{ + struct sun8i_vi_layer *layer; + int ret; + + layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL); + if (!layer) + return ERR_PTR(-ENOMEM); + + /* possible crtcs are set later */ + ret = drm_universal_plane_init(drm, &layer->plane, 0, + &sun8i_vi_layer_funcs, + sun8i_vi_layer_formats, + ARRAY_SIZE(sun8i_vi_layer_formats), + NULL, DRM_PLANE_TYPE_OVERLAY, NULL); + if (ret) { + dev_err(drm->dev, "Couldn't initialize layer\n"); + return ERR_PTR(ret); + } + + /* fixed zpos for now */ + ret = drm_plane_create_zpos_immutable_property(&layer->plane, index); + if (ret) { + dev_err(drm->dev, "Couldn't add zpos property\n"); + return ERR_PTR(ret); + } + + drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs); + layer->mixer = mixer; + layer->channel = index; + layer->overlay = 0; + + return layer; +} diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.h b/drivers/gpu/drm/sun4i/sun8i_vi_layer.h new file mode 100644 index 000000000000..6996627a0a76 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#ifndef _SUN8I_VI_LAYER_H_ +#define _SUN8I_VI_LAYER_H_ + +#include <drm/drm_plane.h> + +#define SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch, layer) \ + (0x2000 + 0x1000 * (ch) + 0x30 * (layer) + 0x0) +#define SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch, layer) \ + (0x2000 + 0x1000 * (ch) + 0x30 * (layer) + 0x4) +#define SUN8I_MIXER_CHAN_VI_LAYER_COORD(ch, layer) \ + (0x2000 + 0x1000 * (ch) + 0x30 * (layer) + 0x8) +#define SUN8I_MIXER_CHAN_VI_LAYER_PITCH(ch, layer, plane) \ + (0x2000 + 0x1000 * (ch) + 0x30 * (layer) + 0xc + 4 * (plane)) +#define SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(ch, layer, plane) \ + (0x2000 + 0x1000 * (ch) + 0x30 * (layer) + 0x18 + 4 * (plane)) +#define SUN8I_MIXER_CHAN_VI_OVL_SIZE(ch) (0x2000 + 0x1000 * (ch) + 0xe8) + +#define SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN BIT(0) +/* RGB mode should be set for RGB formats and cleared for YCbCr */ +#define SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE BIT(15) +#define SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET 8 +#define SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK GENMASK(12, 8) + +struct sun8i_mixer; + +struct sun8i_vi_layer { + struct drm_plane plane; + struct sun8i_mixer *mixer; + int channel; + int overlay; +}; + +static inline struct sun8i_vi_layer * +plane_to_sun8i_vi_layer(struct drm_plane *plane) +{ + return container_of(plane, struct sun8i_vi_layer, plane); +} + +struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm, + struct sun8i_mixer *mixer, + int index); +#endif /* _SUN8I_VI_LAYER_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c new file mode 100644 index 000000000000..d3f1acb234b7 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c @@ -0,0 +1,971 @@ +/* + * Copyright (C) 2017 Jernej Skrabec <jernej.skrabec@siol.net> + * + * Coefficients are taken from BSP driver, which is: + * Copyright (C) 2014-2015 Allwinner + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include "sun8i_vi_scaler.h" + +static const u32 lan3coefftab32_left[480] = { + 0x40000000, 0x40fe0000, 0x3ffd0100, 0x3efc0100, + 0x3efb0100, 0x3dfa0200, 0x3cf90200, 0x3bf80200, + 0x39f70200, 0x37f70200, 0x35f70200, 0x33f70200, + 0x31f70200, 0x2ef70200, 0x2cf70200, 0x2af70200, + 0x27f70200, 0x24f80100, 0x22f80100, 0x1ef90100, + 0x1cf90100, 0x19fa0100, 0x17fa0100, 0x14fb0100, + 0x11fc0000, 0x0ffc0000, 0x0cfd0000, 0x0afd0000, + 0x08fe0000, 0x05ff0000, 0x03ff0000, 0x02000000, + + 0x3806fc02, 0x3805fc02, 0x3803fd01, 0x3801fe01, + 0x3700fe01, 0x35ffff01, 0x35fdff01, 0x34fc0001, + 0x34fb0000, 0x33fa0000, 0x31fa0100, 0x2ff90100, + 0x2df80200, 0x2bf80200, 0x2af70200, 0x28f70200, + 0x27f70200, 0x24f70300, 0x22f70300, 0x1ff70300, + 0x1ef70300, 0x1cf70300, 0x1af70300, 0x18f70300, + 0x16f80300, 0x13f80300, 0x11f90300, 0x0ef90300, + 0x0efa0200, 0x0cfa0200, 0x0afb0200, 0x08fb0200, + + 0x320bfa02, 0x3309fa02, 0x3208fb02, 0x3206fb02, + 0x3205fb02, 0x3104fc02, 0x3102fc01, 0x3001fd01, + 0x3000fd01, 0x2ffffd01, 0x2efefe01, 0x2dfdfe01, + 0x2bfcff01, 0x29fcff01, 0x28fbff01, 0x27fa0001, + 0x26fa0000, 0x24f90000, 0x22f90100, 0x20f90100, + 0x1ff80100, 0x1ef80100, 0x1cf80100, 0x1af80200, + 0x18f80200, 0x17f80200, 0x15f80200, 0x12f80200, + 0x11f90200, 0x0ff90200, 0x0df90200, 0x0cfa0200, + + 0x2e0efa01, 0x2f0dfa01, 0x2f0bfa01, 0x2e0afa01, + 0x2e09fa01, 0x2e07fb01, 0x2d06fb01, 0x2d05fb01, + 0x2c04fb01, 0x2b03fc01, 0x2a02fc01, 0x2a01fc01, + 0x2800fd01, 0x28fffd01, 0x26fefd01, 0x25fefe01, + 0x24fdfe01, 0x23fcfe01, 0x21fcff01, 0x20fbff01, + 0x1efbff01, 0x1efbff00, 0x1cfa0000, 0x1bfa0000, + 0x19fa0000, 0x18fa0000, 0x17f90000, 0x15f90100, + 0x14f90100, 0x12f90100, 0x11f90100, 0x0ff90100, + + 0x2b10fa00, 0x2b0ffa00, 0x2b0efa00, 0x2b0cfa00, + 0x2b0bfa00, 0x2a0afb01, 0x2a09fb01, 0x2908fb01, + 0x2807fb01, 0x2806fb01, 0x2805fb01, 0x2604fc01, + 0x2503fc01, 0x2502fc01, 0x2401fc01, 0x2301fc01, + 0x2100fd01, 0x21fffd01, 0x21fffd01, 0x20fefd01, + 0x1dfefe01, 0x1cfdfe01, 0x1cfdfe00, 0x1bfcfe00, + 0x19fcff00, 0x19fbff00, 0x17fbff00, 0x16fbff00, + 0x15fbff00, 0x14fb0000, 0x13fa0000, 0x11fa0000, + + 0x2811fcff, 0x2810fcff, 0x280ffbff, 0x280efbff, + 0x270dfb00, 0x270cfb00, 0x270bfb00, 0x260afb00, + 0x2609fb00, 0x2508fb00, 0x2507fb00, 0x2407fb00, + 0x2406fc00, 0x2305fc00, 0x2204fc00, 0x2203fc00, + 0x2103fc00, 0x2002fc00, 0x1f01fd00, 0x1e01fd00, + 0x1d00fd00, 0x1dfffd00, 0x1cfffd00, 0x1bfefd00, + 0x1afefe00, 0x19fefe00, 0x18fdfe00, 0x17fdfe00, + 0x16fdfe00, 0x15fcff00, 0x13fcff00, 0x12fcff00, + + 0x2512fdfe, 0x2511fdff, 0x2410fdff, 0x240ffdff, + 0x240efcff, 0x240dfcff, 0x240dfcff, 0x240cfcff, + 0x230bfcff, 0x230afc00, 0x2209fc00, 0x2108fc00, + 0x2108fc00, 0x2007fc00, 0x2006fc00, 0x2005fc00, + 0x1f05fc00, 0x1e04fc00, 0x1e03fc00, 0x1c03fd00, + 0x1c02fd00, 0x1b02fd00, 0x1b01fd00, 0x1a00fd00, + 0x1900fd00, 0x1800fd00, 0x17fffe00, 0x16fffe00, + 0x16fefe00, 0x14fefe00, 0x13fefe00, 0x13fdfe00, + + 0x2212fffe, 0x2211fefe, 0x2211fefe, 0x2110fefe, + 0x210ffeff, 0x220efdff, 0x210dfdff, 0x210dfdff, + 0x210cfdff, 0x210bfdff, 0x200afdff, 0x200afdff, + 0x1f09fdff, 0x1f08fdff, 0x1d08fd00, 0x1c07fd00, + 0x1d06fd00, 0x1b06fd00, 0x1b05fd00, 0x1c04fd00, + 0x1b04fd00, 0x1a03fd00, 0x1a03fd00, 0x1902fd00, + 0x1802fd00, 0x1801fd00, 0x1701fd00, 0x1600fd00, + 0x1400fe00, 0x1400fe00, 0x14fffe00, 0x13fffe00, + + 0x201200fe, 0x201100fe, 0x1f11fffe, 0x2010fffe, + 0x1f0ffffe, 0x1e0ffffe, 0x1f0efeff, 0x1f0dfeff, + 0x1f0dfeff, 0x1e0cfeff, 0x1e0bfeff, 0x1d0bfeff, + 0x1d0afeff, 0x1d09fdff, 0x1d09fdff, 0x1c08fdff, + 0x1c07fdff, 0x1b07fd00, 0x1b06fd00, 0x1a06fd00, + 0x1a05fd00, 0x1805fd00, 0x1904fd00, 0x1804fd00, + 0x1703fd00, 0x1703fd00, 0x1602fe00, 0x1502fe00, + 0x1501fe00, 0x1401fe00, 0x1301fe00, 0x1300fe00, + + 0x1c1202fe, 0x1c1102fe, 0x1b1102fe, 0x1c1001fe, + 0x1b1001fe, 0x1b0f01ff, 0x1b0e00ff, 0x1b0e00ff, + 0x1b0d00ff, 0x1a0d00ff, 0x1a0c00ff, 0x1a0cffff, + 0x1a0bffff, 0x1a0bffff, 0x1a0affff, 0x180affff, + 0x1909ffff, 0x1809ffff, 0x1808ffff, 0x1808feff, + 0x1807feff, 0x1707fe00, 0x1606fe00, 0x1506fe00, + 0x1605fe00, 0x1505fe00, 0x1504fe00, 0x1304fe00, + 0x1304fe00, 0x1303fe00, 0x1203fe00, 0x1203fe00, + + 0x181104ff, 0x191103ff, 0x191003ff, 0x181003ff, + 0x180f03ff, 0x190f02ff, 0x190e02ff, 0x180e02ff, + 0x180d02ff, 0x180d01ff, 0x180d01ff, 0x180c01ff, + 0x180c01ff, 0x180b00ff, 0x170b00ff, 0x170a00ff, + 0x170a00ff, 0x170900ff, 0x160900ff, 0x160900ff, + 0x1608ffff, 0x1508ffff, 0x1507ff00, 0x1507ff00, + 0x1407ff00, 0x1306ff00, 0x1306ff00, 0x1305ff00, + 0x1205ff00, 0x1105ff00, 0x1204ff00, 0x1104ff00, + + 0x171005ff, 0x171005ff, 0x171004ff, 0x170f04ff, + 0x160f04ff, 0x170f03ff, 0x170e03ff, 0x160e03ff, + 0x160d03ff, 0x160d02ff, 0x160d02ff, 0x160c02ff, + 0x160c02ff, 0x160c02ff, 0x160b01ff, 0x150b01ff, + 0x150a01ff, 0x150a01ff, 0x150a01ff, 0x140901ff, + 0x14090000, 0x14090000, 0x14080000, 0x13080000, + 0x13070000, 0x12070000, 0x12070000, 0x12060000, + 0x11060000, 0x11060000, 0x11050000, 0x1105ff00, + + 0x14100600, 0x15100500, 0x150f0500, 0x150f0500, + 0x140f0500, 0x150e0400, 0x140e0400, 0x130e0400, + 0x140d0400, 0x150d0300, 0x130d0300, 0x140c0300, + 0x140c0300, 0x140c0200, 0x140b0200, 0x130b0200, + 0x120b0200, 0x130a0200, 0x130a0200, 0x130a0100, + 0x13090100, 0x12090100, 0x11090100, 0x12080100, + 0x11080100, 0x10080100, 0x11070100, 0x11070000, + 0x10070000, 0x11060000, 0x10060000, 0x10060000, + + 0x140f0600, 0x140f0600, 0x130f0600, 0x140f0500, + 0x140e0500, 0x130e0500, 0x130e0500, 0x140d0400, + 0x140d0400, 0x130d0400, 0x120d0400, 0x130c0400, + 0x130c0300, 0x130c0300, 0x130b0300, 0x130b0300, + 0x110b0300, 0x130a0200, 0x120a0200, 0x120a0200, + 0x120a0200, 0x12090200, 0x10090200, 0x11090100, + 0x11080100, 0x11080100, 0x10080100, 0x10080100, + 0x10070100, 0x10070100, 0x0f070100, 0x10060100, + + 0x120f0701, 0x130f0601, 0x130e0601, 0x130e0601, + 0x120e0601, 0x130e0501, 0x130e0500, 0x130d0500, + 0x120d0500, 0x120d0500, 0x130c0400, 0x130c0400, + 0x120c0400, 0x110c0400, 0x120b0400, 0x120b0300, + 0x120b0300, 0x120b0300, 0x120a0300, 0x110a0300, + 0x110a0200, 0x11090200, 0x11090200, 0x10090200, + 0x10090200, 0x10080200, 0x10080200, 0x10080100, + 0x0f080100, 0x10070100, 0x0f070100, 0x0f070100 +}; + +static const u32 lan3coefftab32_right[480] = { + 0x00000000, 0x00000002, 0x0000ff04, 0x0000ff06, + 0x0000fe08, 0x0000fd0a, 0x0000fd0c, 0x0000fc0f, + 0x0000fc12, 0x0001fb14, 0x0001fa17, 0x0001fa19, + 0x0001f91c, 0x0001f91f, 0x0001f822, 0x0001f824, + 0x0002f727, 0x0002f72a, 0x0002f72c, 0x0002f72f, + 0x0002f731, 0x0002f733, 0x0002f735, 0x0002f737, + 0x0002f73a, 0x0002f83b, 0x0002f93c, 0x0002fa3d, + 0x0001fb3e, 0x0001fc3f, 0x0001fd40, 0x0000fe40, + + 0x0002fc06, 0x0002fb08, 0x0002fb0a, 0x0002fa0c, + 0x0002fa0e, 0x0003f910, 0x0003f912, 0x0003f814, + 0x0003f816, 0x0003f719, 0x0003f71a, 0x0003f71d, + 0x0003f71f, 0x0003f721, 0x0003f723, 0x0003f725, + 0x0002f727, 0x0002f729, 0x0002f72b, 0x0002f82d, + 0x0002f82e, 0x0001f930, 0x0001fa31, 0x0000fa34, + 0x0000fb34, 0x0100fc35, 0x01fffd36, 0x01ffff37, + 0x01fe0037, 0x01fe0138, 0x01fd0338, 0x02fc0538, + + 0x0002fa0b, 0x0002fa0c, 0x0002f90e, 0x0002f910, + 0x0002f911, 0x0002f813, 0x0002f816, 0x0002f817, + 0x0002f818, 0x0002f81a, 0x0001f81c, 0x0001f81e, + 0x0001f820, 0x0001f921, 0x0001f923, 0x0000f925, + 0x0000fa26, 0x0100fa28, 0x01fffb29, 0x01fffc2a, + 0x01fffc2c, 0x01fefd2d, 0x01fefe2e, 0x01fdff2f, + 0x01fd0030, 0x01fd0130, 0x01fc0232, 0x02fc0432, + 0x02fb0532, 0x02fb0633, 0x02fb0833, 0x02fa0933, + + 0x0001fa0e, 0x0001f90f, 0x0001f911, 0x0001f913, + 0x0001f914, 0x0001f915, 0x0000f918, 0x0000fa18, + 0x0000fa1a, 0x0000fa1b, 0x0000fa1d, 0x00fffb1e, + 0x01fffb1f, 0x01fffb20, 0x01fffc22, 0x01fefc23, + 0x01fefd24, 0x01fefe25, 0x01fdfe27, 0x01fdff28, + 0x01fd0029, 0x01fc012a, 0x01fc022b, 0x01fc032b, + 0x01fb042d, 0x01fb052d, 0x01fb062e, 0x01fb072e, + 0x01fa092e, 0x01fa0a2f, 0x01fa0b2f, 0x01fa0d2f, + + 0x0000fa11, 0x0000fa12, 0x0000fa13, 0x0000fb14, + 0x00fffb16, 0x00fffb16, 0x00fffb17, 0x00fffb19, + 0x00fffc1a, 0x00fefc1c, 0x00fefd1c, 0x01fefd1d, + 0x01fefe1e, 0x01fdfe20, 0x01fdff21, 0x01fdff22, + 0x01fd0023, 0x01fc0124, 0x01fc0124, 0x01fc0225, + 0x01fc0326, 0x01fc0427, 0x01fb0528, 0x01fb0629, + 0x01fb0729, 0x01fb0829, 0x01fb092a, 0x01fb0a2a, + 0x00fa0b2c, 0x00fa0c2b, 0x00fa0e2b, 0x00fa0f2c, + + 0x00fffc11, 0x00fffc12, 0x00fffc14, 0x00fffc15, + 0x00fefd16, 0x00fefd17, 0x00fefd18, 0x00fefe19, + 0x00fefe1a, 0x00fdfe1d, 0x00fdff1d, 0x00fdff1e, + 0x00fd001d, 0x00fd011e, 0x00fd0120, 0x00fc0221, + 0x00fc0321, 0x00fc0323, 0x00fc0423, 0x00fc0523, + 0x00fc0624, 0x00fb0725, 0x00fb0726, 0x00fb0827, + 0x00fb0926, 0x00fb0a26, 0x00fb0b27, 0x00fb0c27, + 0x00fb0d27, 0xfffb0e28, 0xfffb0f29, 0xfffc1028, + + 0x00fefd13, 0x00fefd13, 0x00fefe14, 0x00fefe15, + 0x00fefe17, 0x00feff17, 0x00feff17, 0x00fd0018, + 0x00fd001a, 0x00fd001a, 0x00fd011b, 0x00fd021c, + 0x00fd021c, 0x00fd031d, 0x00fc031f, 0x00fc041f, + 0x00fc051f, 0x00fc0521, 0x00fc0621, 0x00fc0721, + 0x00fc0821, 0x00fc0822, 0x00fc0922, 0x00fc0a23, + 0xfffc0b24, 0xfffc0c24, 0xfffc0d24, 0xfffc0d25, + 0xfffc0e25, 0xfffd0f25, 0xfffd1025, 0xfffd1125, + + 0x00feff12, 0x00feff14, 0x00feff14, 0x00fe0015, + 0x00fe0015, 0x00fd0017, 0x00fd0118, 0x00fd0118, + 0x00fd0218, 0x00fd0219, 0x00fd031a, 0x00fd031a, + 0x00fd041b, 0x00fd041c, 0x00fd051c, 0x00fd061d, + 0x00fd061d, 0x00fd071e, 0x00fd081e, 0xfffd081f, + 0xfffd091f, 0xfffd0a20, 0xfffd0a20, 0xfffd0b21, + 0xfffd0c21, 0xfffd0d21, 0xfffd0d22, 0xfffd0e23, + 0xfffe0f22, 0xfefe1022, 0xfefe1122, 0xfefe1123, + + 0x00fe0012, 0x00fe0013, 0x00fe0114, 0x00fe0114, + 0x00fe0116, 0x00fe0216, 0x00fe0216, 0x00fd0317, + 0x00fd0317, 0x00fd0418, 0x00fd0419, 0x00fd0519, + 0x00fd051a, 0x00fd061b, 0x00fd061b, 0x00fd071c, + 0xfffd071e, 0xfffd081d, 0xfffd091d, 0xfffd091e, + 0xfffe0a1d, 0xfffe0b1e, 0xfffe0b1e, 0xfffe0c1e, + 0xfffe0d1f, 0xfffe0d1f, 0xfffe0e1f, 0xfeff0f1f, + 0xfeff0f20, 0xfeff1020, 0xfeff1120, 0xfe001120, + + 0x00fe0212, 0x00fe0312, 0x00fe0313, 0x00fe0314, + 0x00fe0414, 0x00fe0414, 0x00fe0416, 0x00fe0515, + 0x00fe0516, 0x00fe0616, 0x00fe0617, 0x00fe0717, + 0xfffe0719, 0xfffe0818, 0xffff0818, 0xffff0919, + 0xffff0919, 0xffff0a19, 0xffff0a1a, 0xffff0b1a, + 0xffff0b1b, 0xffff0c1a, 0xff000c1b, 0xff000d1b, + 0xff000d1b, 0xff000e1b, 0xff000e1c, 0xff010f1c, + 0xfe01101c, 0xfe01101d, 0xfe02111c, 0xfe02111c, + + 0x00ff0411, 0x00ff0411, 0x00ff0412, 0x00ff0512, + 0x00ff0513, 0x00ff0513, 0x00ff0613, 0x00ff0614, + 0x00ff0714, 0x00ff0715, 0x00ff0715, 0xffff0816, + 0xffff0816, 0xff000916, 0xff000917, 0xff000918, + 0xff000a17, 0xff000a18, 0xff000b18, 0xff000b18, + 0xff010c18, 0xff010c19, 0xff010d18, 0xff010d18, + 0xff020d18, 0xff020e19, 0xff020e19, 0xff020f19, + 0xff030f19, 0xff031019, 0xff031019, 0xff031119, + + 0x00ff0511, 0x00ff0511, 0x00000511, 0x00000611, + 0x00000612, 0x00000612, 0x00000712, 0x00000713, + 0x00000714, 0x00000814, 0x00000814, 0x00000914, + 0x00000914, 0xff010914, 0xff010a15, 0xff010a16, + 0xff010a17, 0xff010b16, 0xff010b16, 0xff020c16, + 0xff020c16, 0xff020c16, 0xff020d16, 0xff020d17, + 0xff030d17, 0xff030e17, 0xff030e17, 0xff030f17, + 0xff040f17, 0xff040f17, 0xff041017, 0xff051017, + + 0x00000610, 0x00000610, 0x00000611, 0x00000611, + 0x00000711, 0x00000712, 0x00010712, 0x00010812, + 0x00010812, 0x00010812, 0x00010913, 0x00010913, + 0x00010913, 0x00010a13, 0x00020a13, 0x00020a14, + 0x00020b14, 0x00020b14, 0x00020b14, 0x00020c14, + 0x00030c14, 0x00030c15, 0x00030d15, 0x00030d15, + 0x00040d15, 0x00040e15, 0x00040e15, 0x00040e16, + 0x00050f15, 0x00050f15, 0x00050f16, 0x00051015, + + 0x00000611, 0x00010610, 0x00010710, 0x00010710, + 0x00010711, 0x00010811, 0x00010811, 0x00010812, + 0x00010812, 0x00010912, 0x00020912, 0x00020912, + 0x00020a12, 0x00020a12, 0x00020a13, 0x00020a13, + 0x00030b13, 0x00030b13, 0x00030b14, 0x00030c13, + 0x00030c13, 0x00040c13, 0x00040d14, 0x00040d14, + 0x00040d15, 0x00040d15, 0x00050e14, 0x00050e14, + 0x00050e15, 0x00050f14, 0x00060f14, 0x00060f14, + + 0x0001070f, 0x0001070f, 0x00010710, 0x00010710, + 0x00010810, 0x00010810, 0x00020810, 0x00020811, + 0x00020911, 0x00020911, 0x00020912, 0x00020912, + 0x00020a12, 0x00030a12, 0x00030a12, 0x00030b12, + 0x00030b12, 0x00030b12, 0x00040b12, 0x00040c12, + 0x00040c13, 0x00040c14, 0x00040c14, 0x00050d13, + 0x00050d13, 0x00050d14, 0x00050e13, 0x01050e13, + 0x01060e13, 0x01060e13, 0x01060e14, 0x01060f13 +}; + +static const u32 lan2coefftab32[480] = { + 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd, + 0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb, + 0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb, + 0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc, + 0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd, + 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff, + 0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff, + 0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100, + + 0xff053804, 0xff063803, 0xff083801, 0xff093701, + 0xff0a3700, 0xff0c3500, 0xff0e34ff, 0xff1033fe, + 0xff1232fd, 0xfe1431fd, 0xfe162ffd, 0xfe182dfd, + 0xfd1b2cfc, 0xfd1d2afc, 0xfd1f28fc, 0xfd2126fc, + 0xfd2323fd, 0xfc2621fd, 0xfc281ffd, 0xfc2a1dfd, + 0xfc2c1bfd, 0xfd2d18fe, 0xfd2f16fe, 0xfd3114fe, + 0xfd3212ff, 0xfe3310ff, 0xff340eff, 0x00350cff, + 0x00360a00, 0x01360900, 0x02370700, 0x03370600, + + 0xff083207, 0xff093206, 0xff0a3205, 0xff0c3203, + 0xff0d3103, 0xff0e3102, 0xfe113001, 0xfe132f00, + 0xfe142e00, 0xfe162dff, 0xfe182bff, 0xfe192aff, + 0xfe1b29fe, 0xfe1d27fe, 0xfe1f25fe, 0xfd2124fe, + 0xfe2222fe, 0xfe2421fd, 0xfe251ffe, 0xfe271dfe, + 0xfe291bfe, 0xff2a19fe, 0xff2b18fe, 0xff2d16fe, + 0x002e14fe, 0x002f12ff, 0x013010ff, 0x02300fff, + 0x03310dff, 0x04310cff, 0x05310a00, 0x06310900, + + 0xff0a2e09, 0xff0b2e08, 0xff0c2e07, 0xff0e2d06, + 0xff0f2d05, 0xff102d04, 0xff122c03, 0xfe142c02, + 0xfe152b02, 0xfe172a01, 0xfe182901, 0xfe1a2800, + 0xfe1b2700, 0xfe1d2500, 0xff1e24ff, 0xfe2023ff, + 0xff2121ff, 0xff2320fe, 0xff241eff, 0x00251dfe, + 0x00261bff, 0x00281afe, 0x012818ff, 0x012a16ff, + 0x022a15ff, 0x032b13ff, 0x032c12ff, 0x052c10ff, + 0x052d0fff, 0x062d0d00, 0x072d0c00, 0x082d0b00, + + 0xff0c2a0b, 0xff0d2a0a, 0xff0e2a09, 0xff0f2a08, + 0xff102a07, 0xff112a06, 0xff132905, 0xff142904, + 0xff162803, 0xff172703, 0xff182702, 0xff1a2601, + 0xff1b2501, 0xff1c2401, 0xff1e2300, 0xff1f2200, + 0x00202000, 0x00211f00, 0x01221d00, 0x01231c00, + 0x01251bff, 0x02251aff, 0x032618ff, 0x032717ff, + 0x042815ff, 0x052814ff, 0x052913ff, 0x06291100, + 0x072a10ff, 0x082a0e00, 0x092a0d00, 0x0a2a0c00, + + 0xff0d280c, 0xff0e280b, 0xff0f280a, 0xff102809, + 0xff112808, 0xff122708, 0xff142706, 0xff152705, + 0xff162605, 0xff172604, 0xff192503, 0xff1a2403, + 0x001b2302, 0x001c2202, 0x001d2201, 0x001e2101, + 0x011f1f01, 0x01211e00, 0x01221d00, 0x02221c00, + 0x02231b00, 0x03241900, 0x04241800, 0x04251700, + 0x052616ff, 0x06261400, 0x072713ff, 0x08271100, + 0x08271100, 0x09271000, 0x0a280e00, 0x0b280d00, + + 0xff0e260d, 0xff0f260c, 0xff10260b, 0xff11260a, + 0xff122609, 0xff132608, 0xff142508, 0xff152507, + 0x00152506, 0x00172405, 0x00182305, 0x00192304, + 0x001b2203, 0x001c2103, 0x011d2002, 0x011d2002, + 0x011f1f01, 0x021f1e01, 0x02201d01, 0x03211c00, + 0x03221b00, 0x04221a00, 0x04231801, 0x05241700, + 0x06241600, 0x07241500, 0x08251300, 0x09251200, + 0x09261100, 0x0a261000, 0x0b260f00, 0x0c260e00, + + 0xff0e250e, 0xff0f250d, 0xff10250c, 0xff11250b, + 0x0011250a, 0x00132409, 0x00142408, 0x00152407, + 0x00162307, 0x00172306, 0x00182206, 0x00192205, + 0x011a2104, 0x011b2004, 0x011c2003, 0x021c1f03, + 0x021e1e02, 0x031e1d02, 0x03201c01, 0x04201b01, + 0x04211a01, 0x05221900, 0x05221801, 0x06231700, + 0x07231600, 0x07241500, 0x08241400, 0x09241300, + 0x0a241200, 0x0b241100, 0x0c241000, 0x0d240f00, + + 0x000e240e, 0x000f240d, 0x0010240c, 0x0011240b, + 0x0013230a, 0x0013230a, 0x00142309, 0x00152308, + 0x00162208, 0x00172207, 0x01182106, 0x01192105, + 0x011a2005, 0x021b1f04, 0x021b1f04, 0x021d1e03, + 0x031d1d03, 0x031e1d02, 0x041e1c02, 0x041f1b02, + 0x05201a01, 0x05211901, 0x06211801, 0x07221700, + 0x07221601, 0x08231500, 0x09231400, 0x0a231300, + 0x0a231300, 0x0b231200, 0x0c231100, 0x0d231000, + + 0x000f220f, 0x0010220e, 0x0011220d, 0x0012220c, + 0x0013220b, 0x0013220b, 0x0015210a, 0x0015210a, + 0x01162108, 0x01172008, 0x01182007, 0x02191f06, + 0x02191f06, 0x021a1e06, 0x031a1e05, 0x031c1d04, + 0x041c1c04, 0x041d1c03, 0x051d1b03, 0x051e1a03, + 0x061f1902, 0x061f1902, 0x07201801, 0x08201701, + 0x08211601, 0x09211501, 0x0a211500, 0x0b211400, + 0x0b221300, 0x0c221200, 0x0d221100, 0x0e221000, + + 0x0010210f, 0x0011210e, 0x0011210e, 0x0012210d, + 0x0013210c, 0x0014200c, 0x0114200b, 0x0115200a, + 0x01161f0a, 0x01171f09, 0x02171f08, 0x02181e08, + 0x03181e07, 0x031a1d06, 0x031a1d06, 0x041b1c05, + 0x041c1c04, 0x051c1b04, 0x051d1a04, 0x061d1a03, + 0x071d1903, 0x071e1803, 0x081e1802, 0x081f1702, + 0x091f1602, 0x0a201501, 0x0b1f1501, 0x0b201401, + 0x0c211300, 0x0d211200, 0x0e201200, 0x0e211100, + + 0x00102010, 0x0011200f, 0x0012200e, 0x0013200d, + 0x0013200d, 0x01141f0c, 0x01151f0b, 0x01151f0b, + 0x01161f0a, 0x02171e09, 0x02171e09, 0x03181d08, + 0x03191d07, 0x03191d07, 0x041a1c06, 0x041b1c05, + 0x051b1b05, 0x051c1b04, 0x061c1a04, 0x071d1903, + 0x071d1903, 0x081d1803, 0x081e1703, 0x091e1702, + 0x0a1f1601, 0x0a1f1502, 0x0b1f1501, 0x0c1f1401, + 0x0d201300, 0x0d201300, 0x0e201200, 0x0f201100, + + 0x00102010, 0x0011200f, 0x00121f0f, 0x00131f0e, + 0x00141f0d, 0x01141f0c, 0x01141f0c, 0x01151e0c, + 0x02161e0a, 0x02171e09, 0x03171d09, 0x03181d08, + 0x03181d08, 0x04191c07, 0x041a1c06, 0x051a1b06, + 0x051b1b05, 0x061b1a05, 0x061c1a04, 0x071c1904, + 0x081c1903, 0x081d1803, 0x091d1703, 0x091e1702, + 0x0a1e1602, 0x0b1e1502, 0x0c1e1501, 0x0c1f1401, + 0x0d1f1400, 0x0e1f1300, 0x0e1f1201, 0x0f1f1200, + + 0x00111e11, 0x00121e10, 0x00131e0f, 0x00131e0f, + 0x01131e0e, 0x01141d0e, 0x02151d0c, 0x02151d0c, + 0x02161d0b, 0x03161c0b, 0x03171c0a, 0x04171c09, + 0x04181b09, 0x05181b08, 0x05191b07, 0x06191a07, + 0x061a1a06, 0x071a1906, 0x071b1905, 0x081b1805, + 0x091b1804, 0x091c1704, 0x0a1c1703, 0x0a1c1604, + 0x0b1d1602, 0x0c1d1502, 0x0c1d1502, 0x0d1d1402, + 0x0e1d1401, 0x0e1e1301, 0x0f1e1300, 0x101e1200, + + 0x00111e11, 0x00121e10, 0x00131d10, 0x01131d0f, + 0x01141d0e, 0x01141d0e, 0x02151c0d, 0x02151c0d, + 0x03161c0b, 0x03161c0b, 0x04171b0a, 0x04171b0a, + 0x05171b09, 0x05181a09, 0x06181a08, 0x06191a07, + 0x07191907, 0x071a1906, 0x081a1806, 0x081a1806, + 0x091a1805, 0x0a1b1704, 0x0a1b1704, 0x0b1c1603, + 0x0b1c1603, 0x0c1c1503, 0x0d1c1502, 0x0d1d1402, + 0x0e1d1401, 0x0f1d1301, 0x0f1d1301, 0x101e1200, +}; + +static const u32 bicubic8coefftab32_left[480] = { + 0x40000000, 0x40ff0000, 0x3ffe0000, 0x3efe0000, + 0x3dfd0000, 0x3cfc0000, 0x3bfc0000, 0x39fc0000, + 0x36fc0000, 0x35fb0000, 0x33fb0000, 0x31fb0000, + 0x2ffb0000, 0x2cfb0000, 0x29fc0000, 0x27fc0000, + 0x24fc0000, 0x21fc0000, 0x1efd0000, 0x1cfd0000, + 0x19fd0000, 0x16fe0000, 0x14fe0000, 0x11fe0000, + 0x0dff0000, 0x0cff0000, 0x0aff0000, 0x08ff0000, + 0x05000000, 0x03000000, 0x02000000, 0x01000000, + + 0x3904ff00, 0x3903ff00, 0x3902ff00, 0x38010000, + 0x37000000, 0x36ff0000, 0x35ff0000, 0x34fe0000, + 0x32fe0000, 0x31fd0000, 0x30fd0000, 0x2efc0000, + 0x2cfc0000, 0x2afc0000, 0x28fc0000, 0x26fc0000, + 0x24fc0000, 0x22fc0000, 0x20fc0000, 0x1efc0000, + 0x1cfc0000, 0x19fc0000, 0x17fc0000, 0x15fd0000, + 0x12fd0000, 0x11fd0000, 0x0ffd0000, 0x0dfe0000, + 0x0bfe0000, 0x09fe0000, 0x08fe0000, 0x06ff0000, + + 0x3209fe00, 0x3407fe00, 0x3306fe00, 0x3305fe00, + 0x3204fe00, 0x3102ff00, 0x3102ff00, 0x3001ff00, + 0x2f00ff00, 0x2effff00, 0x2cff0000, 0x2bfe0000, + 0x29fe0000, 0x28fe0000, 0x26fd0000, 0x24fd0000, + 0x23fd0000, 0x21fd0000, 0x20fc0000, 0x1efc0000, + 0x1dfc0000, 0x1bfc0000, 0x19fc0000, 0x17fc0000, + 0x16fc0000, 0x14fc0000, 0x12fc0000, 0x10fd0000, + 0x0ffd0000, 0x0dfd0000, 0x0cfd0000, 0x0afd0000, + + 0x2e0cfd00, 0x2e0bfd00, 0x2e09fd00, 0x2e08fd00, + 0x2e07fd00, 0x2c06fe00, 0x2c05fe00, 0x2b04fe00, + 0x2b03fe00, 0x2a02fe00, 0x2901fe00, 0x2701ff00, + 0x2700ff00, 0x26ffff00, 0x24ffff00, 0x23ffff00, + 0x22feff00, 0x20fe0000, 0x1ffe0000, 0x1efd0000, + 0x1dfd0000, 0x1bfd0000, 0x1afd0000, 0x19fd0000, + 0x17fd0000, 0x15fd0000, 0x13fd0000, 0x12fd0000, + 0x11fd0000, 0x10fd0000, 0x0ffd0000, 0x0cfd0000, + + 0x2a0efd00, 0x2a0dfd00, 0x2a0cfd00, 0x290bfd00, + 0x290afd00, 0x2909fd00, 0x2908fd00, 0x2807fd00, + 0x2706fd00, 0x2705fd00, 0x2604fe00, 0x2603fe00, + 0x2502fe00, 0x2402fe00, 0x2401fe00, 0x2200fe00, + 0x2200fe00, 0x2000ff00, 0x1fffff00, 0x1effff00, + 0x1dfeff00, 0x1cfeff00, 0x1afeff00, 0x19feff00, + 0x17fe0000, 0x16fd0000, 0x15fd0000, 0x14fd0000, + 0x12fd0000, 0x11fd0000, 0x10fd0000, 0x0ffd0000, + + 0x2610fd00, 0x260ffd00, 0x260efd00, 0x260dfd00, + 0x260cfd00, 0x260bfd00, 0x260afd00, 0x2609fd00, + 0x2508fd00, 0x2507fd00, 0x2406fd00, 0x2406fd00, + 0x2305fd00, 0x2304fd00, 0x2203fe00, 0x2103fe00, + 0x2002fe00, 0x1f01fe00, 0x1e01fe00, 0x1e00fe00, + 0x1c00fe00, 0x1b00fe00, 0x1afffe00, 0x19ffff00, + 0x18ffff00, 0x17feff00, 0x16feff00, 0x15feff00, + 0x14feff00, 0x13feff00, 0x11feff00, 0x10fd0000, + + 0x2411feff, 0x2410feff, 0x240ffeff, 0x230efeff, + 0x240dfeff, 0x240cfeff, 0x230cfd00, 0x230bfd00, + 0x230afd00, 0x2309fd00, 0x2208fd00, 0x2108fd00, + 0x2007fd00, 0x2106fd00, 0x2005fd00, 0x1f05fd00, + 0x1f04fd00, 0x1e03fd00, 0x1d03fe00, 0x1c02fe00, + 0x1b02fe00, 0x1a01fe00, 0x1a01fe00, 0x1900fe00, + 0x1800fe00, 0x1700fe00, 0x16fffe00, 0x15fffe00, + 0x13ffff00, 0x12ffff00, 0x12feff00, 0x11feff00, + + 0x2212fffe, 0x2211fffe, 0x2210ffff, 0x220ffeff, + 0x220efeff, 0x210efeff, 0x210dfeff, 0x210cfeff, + 0x210bfeff, 0x200bfeff, 0x200afeff, 0x1f09feff, + 0x1f08feff, 0x1d08fe00, 0x1e07fd00, 0x1e06fd00, + 0x1d06fd00, 0x1c05fd00, 0x1b04fe00, 0x1a04fe00, + 0x1a03fe00, 0x1903fe00, 0x1802fe00, 0x1802fe00, + 0x1701fe00, 0x1601fe00, 0x1501fe00, 0x1500fe00, + 0x1400fe00, 0x1400fe00, 0x13fffe00, 0x12fffe00, + + 0x201200fe, 0x201100fe, 0x1f1100fe, 0x2010fffe, + 0x200ffffe, 0x1f0ffffe, 0x1f0efffe, 0x1e0dffff, + 0x1f0cfeff, 0x1e0cfeff, 0x1e0bfeff, 0x1e0afeff, + 0x1d0afeff, 0x1d09feff, 0x1c08feff, 0x1b08feff, + 0x1b07feff, 0x1a07feff, 0x1a06feff, 0x1a05feff, + 0x1805fe00, 0x1904fe00, 0x1704fe00, 0x1703fe00, + 0x1603fe00, 0x1602fe00, 0x1402fe00, 0x1402fe00, + 0x1401fe00, 0x1301fe00, 0x1201fe00, 0x1200fe00, + + 0x1c1202fe, 0x1c1102fe, 0x1b1102fe, 0x1c1001fe, + 0x1b1001fe, 0x1c0f01fe, 0x1b0f00fe, 0x1b0e00fe, + 0x1b0e00fe, 0x1b0d00fe, 0x1b0c00fe, 0x1a0cfffe, + 0x1a0bfffe, 0x1a0bfffe, 0x190afffe, 0x190afffe, + 0x1909fffe, 0x1709ffff, 0x1808ffff, 0x1708feff, + 0x1707feff, 0x1707feff, 0x1606feff, 0x1506feff, + 0x1505feff, 0x1505feff, 0x1404feff, 0x1404feff, + 0x1404feff, 0x1303feff, 0x1203feff, 0x1202feff, + + 0x191104fe, 0x191104fe, 0x191003fe, 0x191003fe, + 0x171003fe, 0x180f03fe, 0x180f02fe, 0x180e02fe, + 0x180e02fe, 0x180d01fe, 0x180d01fe, 0x180d01fe, + 0x170c01fe, 0x160c01fe, 0x170b00fe, 0x170b00fe, + 0x160a00fe, 0x160a00fe, 0x160a00fe, 0x150900fe, + 0x1509fffe, 0x1508fffe, 0x1508fffe, 0x1408fffe, + 0x1407fffe, 0x1307ffff, 0x1306ffff, 0x1206ffff, + 0x1206ffff, 0x1205ffff, 0x1205ffff, 0x1104feff, + + 0x161006ff, 0x161005ff, 0x161005ff, 0x160f05ff, + 0x160f04ff, 0x150f04ff, 0x150e04ff, 0x150e04ff, + 0x150e03ff, 0x150d03ff, 0x150d03ff, 0x150d02ff, + 0x140c02ff, 0x150c02fe, 0x150c02fe, 0x150b02fe, + 0x140b01fe, 0x140b01fe, 0x140a01fe, 0x140a01fe, + 0x140a01fe, 0x130900fe, 0x130900fe, 0x130900fe, + 0x130800fe, 0x120800fe, 0x120800fe, 0x120700fe, + 0x120700fe, 0x1107fffe, 0x1106fffe, 0x1106fffe, + + 0x140f0700, 0x140f0600, 0x140f0600, 0x140f0600, + 0x140e0600, 0x130e0500, 0x140e05ff, 0x130e05ff, + 0x140d05ff, 0x130d04ff, 0x130d04ff, 0x120d04ff, + 0x130c04ff, 0x130c03ff, 0x130c03ff, 0x120c03ff, + 0x120b03ff, 0x120b02ff, 0x120b02ff, 0x120a02ff, + 0x120a02ff, 0x110a02ff, 0x110a01ff, 0x120901ff, + 0x100901ff, 0x100901ff, 0x110801ff, 0x110801ff, + 0x100800ff, 0x100800ff, 0x100700ff, 0x100700fe, + + 0x120f0701, 0x120e0701, 0x120e0701, 0x120e0701, + 0x120e0600, 0x110e0600, 0x120d0600, 0x120d0600, + 0x120d0500, 0x120d0500, 0x110d0500, 0x110c0500, + 0x110c0500, 0x110c0400, 0x110c0400, 0x110b04ff, + 0x110b04ff, 0x110b04ff, 0x110b03ff, 0x110b03ff, + 0x110a03ff, 0x110a03ff, 0x100a03ff, 0x110a02ff, + 0x100902ff, 0x100902ff, 0x100902ff, 0x0f0902ff, + 0x0e0902ff, 0x100801ff, 0x0f0801ff, 0x0f0801ff, + + 0x100e0802, 0x100e0802, 0x110e0702, 0x110d0701, + 0x110d0701, 0x100d0701, 0x100d0701, 0x110d0601, + 0x110d0601, 0x110c0601, 0x110c0601, 0x100c0600, + 0x100c0500, 0x100c0500, 0x100c0500, 0x100b0500, + 0x100b0500, 0x100b0400, 0x100b0400, 0x0f0b0400, + 0x100a0400, 0x0f0a0400, 0x0f0a0400, 0x0f0a0300, + 0x0f0a03ff, 0x0f0903ff, 0x0f0903ff, 0x0f0903ff, + 0x0f0903ff, 0x0f0902ff, 0x0f0902ff, 0x0f0802ff +}; + +static const u32 bicubic8coefftab32_right[480] = { + 0x00000000, 0x00000001, 0x00000003, 0x00000004, + 0x00000006, 0x0000ff09, 0x0000ff0a, 0x0000ff0c, + 0x0000ff0f, 0x0000fe12, 0x0000fe14, 0x0000fe16, + 0x0000fd19, 0x0000fd1c, 0x0000fd1e, 0x0000fc21, + 0x0000fc24, 0x0000fc27, 0x0000fc29, 0x0000fb2c, + 0x0000fb2f, 0x0000fb31, 0x0000fb33, 0x0000fb36, + 0x0000fc38, 0x0000fc39, 0x0000fc3b, 0x0000fc3d, + 0x0000fd3e, 0x0000fe3f, 0x0000fe40, 0x0000ff40, + + 0x0000ff05, 0x0000ff06, 0x0000fe08, 0x0000fe09, + 0x0000fe0b, 0x0000fe0d, 0x0000fd0f, 0x0000fd11, + 0x0000fd13, 0x0000fd15, 0x0000fc17, 0x0000fc1a, + 0x0000fc1c, 0x0000fc1e, 0x0000fc20, 0x0000fc22, + 0x0000fc24, 0x0000fc26, 0x0000fc28, 0x0000fc2a, + 0x0000fc2c, 0x0000fc2f, 0x0000fd30, 0x0000fd31, + 0x0000fe33, 0x0000fe34, 0x0000ff35, 0x0000ff36, + 0x00000037, 0x00000138, 0x00ff0239, 0x00ff0339, + + 0x0000fe09, 0x0000fd0a, 0x0000fd0c, 0x0000fd0d, + 0x0000fd0f, 0x0000fd11, 0x0000fc12, 0x0000fc14, + 0x0000fc16, 0x0000fc18, 0x0000fc19, 0x0000fc1b, + 0x0000fc1d, 0x0000fc1e, 0x0000fc21, 0x0000fd22, + 0x0000fd23, 0x0000fd25, 0x0000fd27, 0x0000fe28, + 0x0000fe29, 0x0000fe2b, 0x0000ff2c, 0x00ffff2f, + 0x00ff002f, 0x00ff0130, 0x00ff0231, 0x00ff0232, + 0x00fe0432, 0x00fe0533, 0x00fe0633, 0x00fe0734, + + 0x0000fd0c, 0x0000fd0d, 0x0000fd0f, 0x0000fd10, + 0x0000fd11, 0x0000fd13, 0x0000fd14, 0x0000fd16, + 0x0000fd17, 0x0000fd19, 0x0000fd1b, 0x0000fd1c, + 0x0000fd1d, 0x0000fd1f, 0x0000fe20, 0x0000fe21, + 0x00fffe24, 0x00ffff24, 0x00ffff25, 0x00ffff27, + 0x00ff0027, 0x00ff0128, 0x00fe012a, 0x00fe022a, + 0x00fe032b, 0x00fe042c, 0x00fe052d, 0x00fe062d, + 0x00fd072e, 0x00fd082e, 0x00fd092e, 0x00fd0b2f, + + 0x0000fd0e, 0x0000fd0f, 0x0000fd10, 0x0000fd12, + 0x0000fd13, 0x0000fd14, 0x0000fd15, 0x0000fd17, + 0x0000fe18, 0x00fffe1a, 0x00fffe1b, 0x00fffe1c, + 0x00fffe1e, 0x00ffff1e, 0x00ffff1f, 0x00ff0021, + 0x00fe0022, 0x00fe0023, 0x00fe0124, 0x00fe0224, + 0x00fe0226, 0x00fe0326, 0x00fe0427, 0x00fd0528, + 0x00fd0628, 0x00fd0729, 0x00fd0829, 0x00fd0929, + 0x00fd0a2a, 0x00fd0b2a, 0x00fd0c2a, 0x00fd0d2a, + + 0x0000fd10, 0x0000fd11, 0x00fffe12, 0x00fffe13, + 0x00fffe14, 0x00fffe15, 0x00fffe16, 0x00fffe17, + 0x00ffff18, 0x00ffff19, 0x00feff1c, 0x00fe001b, + 0x00fe001d, 0x00fe001e, 0x00fe011e, 0x00fe011f, + 0x00fe0220, 0x00fe0321, 0x00fe0322, 0x00fd0423, + 0x00fd0524, 0x00fd0624, 0x00fd0626, 0x00fd0725, + 0x00fd0825, 0x00fd0926, 0x00fd0a26, 0x00fd0b26, + 0x00fd0c26, 0x00fd0d26, 0x00fd0e27, 0x00fd0f27, + + 0x00fffe11, 0x00fffe12, 0x00fffe13, 0x00ffff14, + 0x00ffff14, 0x00feff16, 0x00feff17, 0x00fe0017, + 0x00fe0018, 0x00fe0019, 0x00fe011a, 0x00fe011b, + 0x00fe021c, 0x00fe021c, 0x00fe031d, 0x00fd031f, + 0x00fd041f, 0x00fd0520, 0x00fd0520, 0x00fd0621, + 0x00fd0721, 0x00fd0822, 0x00fd0822, 0x00fd0923, + 0x00fd0a23, 0x00fd0b23, 0x00fd0b25, 0x00fe0c24, + 0x00fe0d24, 0x00fe0e24, 0x00fe0f24, 0x00fe1024, + + 0x00feff12, 0x00feff13, 0x00feff13, 0x00fe0014, + 0x00fe0015, 0x00fe0016, 0x00fe0116, 0x00fe0117, + 0x00fe0118, 0x00fe0218, 0x00fe0219, 0x00fe031a, + 0x00fe031b, 0x00fe041b, 0x00fd041d, 0x00fd051d, + 0x00fd061d, 0x00fd061f, 0x00fe071e, 0x00fe081e, + 0x00fe081f, 0x00fe091f, 0x00fe0a20, 0x00fe0a20, + 0x00fe0b21, 0x00fe0c21, 0x00fe0d21, 0x00fe0d22, + 0x00fe0e22, 0x00fe0f21, 0x00ff1021, 0x00ff1022, + + 0x00fe0012, 0x00fe0013, 0x00fe0113, 0x00fe0114, + 0x00fe0115, 0x00fe0215, 0x00fe0216, 0x00fe0217, + 0x00fe0317, 0x00fe0318, 0x00fe0418, 0x00fe0419, + 0x00fe0519, 0x00fe051a, 0x00fe061b, 0x00fe071b, + 0x00fe071c, 0x00fe081c, 0x00fe081d, 0x00fe091d, + 0x00fe0a1d, 0x00fe0a1d, 0x00fe0b1e, 0x00fe0c1e, + 0x00ff0c1e, 0x00ff0d1e, 0x00ff0e1f, 0x00ff0e1f, + 0x00ff0f1f, 0x00ff0f20, 0x0000101f, 0x0000111f, + + 0x00fe0212, 0x00fe0312, 0x00fe0313, 0x00fe0314, + 0x00fe0414, 0x00fe0414, 0x00fe0515, 0x00fe0516, + 0x00fe0516, 0x00fe0616, 0x00fe0617, 0x00fe0718, + 0x00fe0719, 0x00fe0818, 0x00ff0819, 0x00ff0918, + 0x00ff0919, 0x00ff0a19, 0x00ff0a19, 0x00ff0b1a, + 0x00ff0b1b, 0x00ff0c1a, 0x00000c1b, 0x00000d1b, + 0x00000d1c, 0x00000e1b, 0x00000e1d, 0x00010f1b, + 0x00010f1b, 0x0001101c, 0x0001101d, 0x0002111c, + + 0x00fe0412, 0x00fe0412, 0x00ff0512, 0x00ff0512, + 0x00ff0613, 0x00ff0613, 0x00ff0614, 0x00ff0714, + 0x00ff0714, 0x00ff0815, 0x00ff0815, 0x00ff0815, + 0x00ff0916, 0x00000916, 0x00000a16, 0x00000a16, + 0x00000a18, 0x00000b17, 0x00000b17, 0x00010c17, + 0x00010c18, 0x00010d18, 0x00010d18, 0x00010d19, + 0x00020e18, 0x00020e18, 0x00020f18, 0x00030f18, + 0x00030f18, 0x00031018, 0x00031018, 0x00041119, + + 0x00ff0610, 0x00ff0611, 0x00ff0611, 0x00ff0711, + 0x00000711, 0x00000712, 0x00000812, 0x00000812, + 0x00000813, 0x00000913, 0x00000913, 0x00000914, + 0x00010a14, 0x00010a14, 0x00010a14, 0x00010b14, + 0x00010b16, 0x00020b15, 0x00020c15, 0x00020c15, + 0x00020c15, 0x00020d17, 0x00030d16, 0x00030d16, + 0x00030e16, 0x00040e16, 0x00040e16, 0x00040f16, + 0x00040f16, 0x00050f17, 0x00051017, 0x00051017, + + 0x0000070f, 0x00000710, 0x00000710, 0x00000710, + 0x00000810, 0x00010811, 0x00010811, 0x00010911, + 0x00010911, 0x00010913, 0x00010913, 0x00020a12, + 0x00020a12, 0x00020a13, 0x00020b12, 0x00020b13, + 0x00030b13, 0x00030c13, 0x00030c13, 0x00030c14, + 0x00040c13, 0x00040d13, 0x00040d14, 0x00040d14, + 0x00050e14, 0x00050e14, 0x00050e14, 0x00050e14, + 0x00060f14, 0x00060f14, 0x00060f15, 0x00061015, + + 0x0001070f, 0x0001080f, 0x0001080f, 0x0001080f, + 0x00010811, 0x00020910, 0x00020910, 0x00020910, + 0x00020911, 0x00020a10, 0x00030a10, 0x00030a11, + 0x00030a11, 0x00030b11, 0x00030b11, 0x00040b12, + 0x00040b12, 0x00040c11, 0x00040c12, 0x00040c12, + 0x00050c12, 0x00050c12, 0x00050d12, 0x00050d12, + 0x00060d13, 0x00060d13, 0x00060e12, 0x00060e13, + 0x00070e13, 0x00070e13, 0x00070f13, 0x00070f13, + + 0x0002080e, 0x0002080e, 0x0002080e, 0x00020810, + 0x0002090f, 0x0003090f, 0x0003090f, 0x0003090f, + 0x0003090f, 0x00030a0f, 0x00030a0f, 0x00040a10, + 0x00040a11, 0x00040b10, 0x00040b10, 0x00040b11, + 0x00050b10, 0x00050b11, 0x00050c10, 0x00050c11, + 0x00050c11, 0x00060c11, 0x00060c11, 0x00060d11, + 0x00060d12, 0x00070d12, 0x00070d12, 0x00070e11, + 0x00070e11, 0x00070e12, 0x00080e11, 0x00080e12 +}; + +static const u32 bicubic4coefftab32[480] = { + 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd, + 0x00063dfd, 0xff083dfc, 0xff0a3bfc, 0xff0c39fc, + 0xff0e37fc, 0xfe1136fb, 0xfe1433fb, 0xfe1631fb, + 0xfd192ffb, 0xfd1c2cfb, 0xfd1e29fc, 0xfc2127fc, + 0xfc2424fc, 0xfc2721fc, 0xfc291efd, 0xfb2c1cfd, + 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfb3611fe, + 0xfc370eff, 0xfc390cff, 0xfc3b0aff, 0xfc3d08ff, + 0xfd3d0600, 0xfd3f0400, 0xfe3f0300, 0xff400100, + + 0xfe053904, 0xfe063903, 0xfe083901, 0xfe0a3800, + 0xfd0b3800, 0xfe0d36ff, 0xfd0f35ff, 0xfd1134fe, + 0xfd1332fe, 0xfd1531fd, 0xfc1730fd, 0xfc1a2efc, + 0xfc1c2cfc, 0xfc1e2afc, 0xfc2028fc, 0xfc2226fc, + 0xfc2424fc, 0xfc2622fc, 0xfc2820fc, 0xfc2a1efc, + 0xfc2c1cfc, 0xfc2e1afc, 0xfd3017fc, 0xfd3115fd, + 0xfe3213fd, 0xfe3411fd, 0xff350ffd, 0xff360dfe, + 0x00370bfe, 0x013809fe, 0x023808fe, 0x033806ff, + + 0xfd093208, 0xfd0a3207, 0xfd0c3205, 0xfd0d3204, + 0xfc0f3203, 0xfc113102, 0xfc123002, 0xfc143000, + 0xfc152f00, 0xfc172d00, 0xfc192cff, 0xfc1b2bfe, + 0xfc1d29fe, 0xfc1e28fe, 0xfc2027fd, 0xfd2125fd, + 0xfd2323fd, 0xfd2521fd, 0xfd2720fc, 0xfe281efc, + 0xfe291dfc, 0xfe2b1bfc, 0xff2c19fc, 0x002d17fc, + 0x002e16fc, 0x012f14fc, 0x022f12fd, 0x023110fd, + 0x03310ffd, 0x05310dfd, 0x06320bfd, 0x07320afd, + + 0xfc0c2d0b, 0xfc0d2d0a, 0xfc0e2d09, 0xfc102d07, + 0xfc112c07, 0xfc132c05, 0xfc142c04, 0xfc162b03, + 0xfc172a03, 0xfc192a01, 0xfc1a2901, 0xfd1b2800, + 0xfd1c2700, 0xfd1e2500, 0xfe1f24ff, 0xfe2023ff, + 0xfe2222fe, 0xff2320fe, 0xff241ffe, 0x00251efd, + 0x00271cfd, 0x01271bfd, 0x01281afd, 0x022918fd, + 0x032a16fd, 0x032b15fd, 0x042b14fd, 0x052c12fd, + 0x072c10fd, 0x082c0ffd, 0x092c0efd, 0x0a2c0dfd, + + 0xfd0d290d, 0xfd0e290c, 0xfd0f290b, 0xfd11280a, + 0xfd122809, 0xfd132808, 0xfd142807, 0xfd162706, + 0xfd172705, 0xfd192604, 0xfe1a2503, 0xfe1b2502, + 0xfe1c2402, 0xfe1d2302, 0xff1e2201, 0xff1f2101, + 0x00202000, 0x00211f00, 0x01221eff, 0x02221dff, + 0x02241cfe, 0x03241bfe, 0x042519fe, 0x042618fe, + 0x052617fe, 0x062716fd, 0x072714fe, 0x082713fe, + 0x092812fd, 0x0a2811fd, 0x0b2810fd, 0x0c280ffd, + + 0xfd0f250f, 0xfd10250e, 0xfd11250d, 0xfd12250c, + 0xfd13250b, 0xfe13250a, 0xfe152409, 0xfe162408, + 0xfe172308, 0xff182306, 0xff192305, 0xff1a2205, + 0x001b2104, 0x001c2103, 0x001d2003, 0x011e1f02, + 0x011f1f01, 0x021f1e01, 0x03201d00, 0x03211c00, + 0x04211b00, 0x05221aff, 0x062219ff, 0x062318ff, + 0x082316ff, 0x082316ff, 0x092415fe, 0x0a2414fe, + 0x0b2413fe, 0x0c2412fe, 0x0d2411fe, 0x0e2410fe, + + 0xfe10230f, 0xfe11230e, 0xfe12220e, 0xfe13220d, + 0xfe14220c, 0xff14220b, 0xff15220a, 0xff16210a, + 0x00162109, 0x00172108, 0x00182008, 0x01192006, + 0x011a1f06, 0x021a1f05, 0x021b1e05, 0x031c1d04, + 0x031d1d03, 0x041d1c03, 0x041e1b03, 0x051e1b02, + 0x061f1a01, 0x06201901, 0x07201801, 0x08201800, + 0x09201700, 0x0a211500, 0x0b2115ff, 0x0c2114ff, + 0x0c2213ff, 0x0d2212ff, 0x0e2211ff, 0x0f2211fe, + + 0xff112010, 0xff12200f, 0xff12200f, 0xff13200e, + 0x0013200d, 0x0014200c, 0x00151f0c, 0x00161f0b, + 0x01161f0a, 0x01171e0a, 0x02171e09, 0x02181e08, + 0x03191d07, 0x03191d07, 0x041a1c06, 0x041b1c05, + 0x051b1b05, 0x051c1b04, 0x061c1a04, 0x071c1a03, + 0x071d1903, 0x081e1802, 0x091d1802, 0x091e1702, + 0x0a1f1601, 0x0b1f1600, 0x0b1f1501, 0x0c201400, + 0x0d1f1400, 0x0e2013ff, 0x0f1f1200, 0x102011ff, + + 0x00111f10, 0x00121e10, 0x00131e0f, 0x00131e0f, + 0x01131e0e, 0x01141e0d, 0x01151d0d, 0x02151d0c, + 0x02161d0b, 0x03161d0a, 0x03171c0a, 0x04171c09, + 0x04181c08, 0x05181b08, 0x05191b07, 0x06191a07, + 0x061a1a06, 0x071a1906, 0x071b1905, 0x081b1805, + 0x081c1804, 0x091c1704, 0x0a1c1703, 0x0a1d1603, + 0x0b1d1602, 0x0c1d1502, 0x0c1d1502, 0x0d1e1401, + 0x0e1d1401, 0x0e1e1301, 0x0f1e1300, 0x101e1200, + + 0x02111c11, 0x02121c10, 0x02131b10, 0x03131b0f, + 0x03131b0f, 0x03141b0e, 0x04141b0d, 0x04151a0d, + 0x05151a0c, 0x05151a0c, 0x05161a0b, 0x0616190b, + 0x0616190b, 0x0716190a, 0x0717180a, 0x08171809, + 0x08181808, 0x09181708, 0x09181708, 0x0a181707, + 0x0a191607, 0x0b191606, 0x0b1a1605, 0x0c1a1505, + 0x0c1a1505, 0x0d1a1504, 0x0d1b1404, 0x0e1b1403, + 0x0f1b1303, 0x0f1b1303, 0x101b1302, 0x101c1202, + + 0x04111a11, 0x04121911, 0x04131910, 0x0513190f, + 0x0513190f, 0x0513190f, 0x0613190e, 0x0614180e, + 0x0714180d, 0x0714180d, 0x0715180c, 0x0814180c, + 0x0815170c, 0x0816170b, 0x0916170a, 0x0916170a, + 0x0a16160a, 0x0a171609, 0x0a171609, 0x0b171608, + 0x0b171509, 0x0c171508, 0x0c181507, 0x0d171507, + 0x0d181407, 0x0e181406, 0x0e181406, 0x0e191306, + 0x0f191305, 0x0f191305, 0x10191304, 0x10191205, + + 0x05121811, 0x06121810, 0x06121810, 0x06131710, + 0x0713170f, 0x0713170f, 0x0713170f, 0x0813170e, + 0x0813170e, 0x0814170d, 0x0914160d, 0x0914160d, + 0x0914160d, 0x0a14160c, 0x0a15160b, 0x0a15150c, + 0x0b15150b, 0x0b15150b, 0x0b16150a, 0x0c15150a, + 0x0c16140a, 0x0d161409, 0x0d161409, 0x0d171408, + 0x0e161408, 0x0e171308, 0x0e171308, 0x0f171307, + 0x0f171307, 0x10171306, 0x10181206, 0x10181206, + + 0x07111711, 0x07121710, 0x07121611, 0x08121610, + 0x08121610, 0x0813160f, 0x0912160f, 0x0913160e, + 0x0913160e, 0x0913160e, 0x0a14150d, 0x0a14150d, + 0x0a14150d, 0x0b14150c, 0x0b14150c, 0x0b14150c, + 0x0c14140c, 0x0c15140b, 0x0c15140b, 0x0c15140b, + 0x0d15140a, 0x0d15140a, 0x0d15140a, 0x0e161309, + 0x0e161309, 0x0e161309, 0x0f151309, 0x0f161308, + 0x0f161209, 0x10161208, 0x10161208, 0x10171207, + + 0x0a111411, 0x0b111410, 0x0b111410, 0x0b111410, + 0x0b111410, 0x0b12140f, 0x0b12140f, 0x0c12130f, + 0x0c12130f, 0x0c12130f, 0x0c12130f, 0x0c12130f, + 0x0d12130e, 0x0d12130e, 0x0d12130e, 0x0d13130d, + 0x0d13130d, 0x0d13130d, 0x0e12130d, 0x0e13120d, + 0x0e13120d, 0x0e13120d, 0x0e13120d, 0x0f13120c, + 0x0f13120c, 0x0f13120c, 0x0f14120b, 0x0f14120b, + 0x1013120b, 0x1013120b, 0x1013120b, 0x1014110b, + + 0x0c111310, 0x0c111310, 0x0c111310, 0x0d101310, + 0x0d101310, 0x0d111210, 0x0d111210, 0x0d111210, + 0x0d12120f, 0x0d12120f, 0x0d12120f, 0x0d12120f, + 0x0e11120f, 0x0e12120e, 0x0e12120e, 0x0e12120e, + 0x0e12120e, 0x0e12120e, 0x0e12120e, 0x0e12120e, + 0x0f11120e, 0x0f12120d, 0x0f12120d, 0x0f12120d, + 0x0f12120d, 0x0f12110e, 0x0f12110e, 0x0f12110e, + 0x1012110d, 0x1012110d, 0x1013110c, 0x1013110c, +}; + +static int sun8i_vi_scaler_coef_index(unsigned int step) +{ + unsigned int scale, int_part, float_part; + + scale = step >> (SUN8I_VI_SCALER_SCALE_FRAC - 3); + int_part = scale >> 3; + float_part = scale & 0x7; + + switch (int_part) { + case 0: + return 0; + case 1: + return float_part; + case 2: + return 8 + (float_part >> 1); + case 3: + return 12; + case 4: + return 13; + default: + return 14; + } +} + +static void sun8i_vi_scaler_set_coeff(struct regmap *map, int layer, + u32 hstep, u32 vstep, + const struct drm_format_info *format) +{ + const u32 *ch_left, *ch_right, *cy; + int offset, i; + + if (format->hsub == 1 && format->vsub == 1) { + ch_left = lan3coefftab32_left; + ch_right = lan3coefftab32_right; + cy = lan2coefftab32; + } else { + ch_left = bicubic8coefftab32_left; + ch_right = bicubic8coefftab32_right; + cy = bicubic4coefftab32; + } + + offset = sun8i_vi_scaler_coef_index(hstep) * + SUN8I_VI_SCALER_COEFF_COUNT; + for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) { + regmap_write(map, SUN8I_SCALER_VSU_YHCOEFF0(layer, i), + lan3coefftab32_left[offset + i]); + regmap_write(map, SUN8I_SCALER_VSU_YHCOEFF1(layer, i), + lan3coefftab32_right[offset + i]); + regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF0(layer, i), + ch_left[offset + i]); + regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF1(layer, i), + ch_right[offset + i]); + } + + offset = sun8i_vi_scaler_coef_index(hstep) * + SUN8I_VI_SCALER_COEFF_COUNT; + for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) { + regmap_write(map, SUN8I_SCALER_VSU_YVCOEFF(layer, i), + lan2coefftab32[offset + i]); + regmap_write(map, SUN8I_SCALER_VSU_CVCOEFF(layer, i), + cy[offset + i]); + } +} + +void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable) +{ + u32 val; + + if (enable) + val = SUN8I_SCALER_VSU_CTRL_EN | + SUN8I_SCALER_VSU_CTRL_COEFF_RDY; + else + val = 0; + + regmap_write(mixer->engine.regs, SUN8I_SCALER_VSU_CTRL(layer), val); +} + +void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer, + u32 src_w, u32 src_h, u32 dst_w, u32 dst_h, + u32 hscale, u32 vscale, u32 hphase, u32 vphase, + const struct drm_format_info *format) +{ + u32 chphase, cvphase; + u32 insize, outsize; + + hphase <<= SUN8I_VI_SCALER_PHASE_FRAC - 16; + vphase <<= SUN8I_VI_SCALER_PHASE_FRAC - 16; + hscale <<= SUN8I_VI_SCALER_SCALE_FRAC - 16; + vscale <<= SUN8I_VI_SCALER_SCALE_FRAC - 16; + + insize = SUN8I_VI_SCALER_SIZE(src_w, src_h); + outsize = SUN8I_VI_SCALER_SIZE(dst_w, dst_h); + + /* + * This is chroma V/H phase calculation as it appears in + * BSP driver. There is no detailed explanation. YUV 420 + * chroma is threated specialy for some reason. + */ + if (format->hsub == 2 && format->vsub == 2) { + chphase = hphase >> 1; + cvphase = (vphase >> 1) - + (1UL << (SUN8I_VI_SCALER_SCALE_FRAC - 2)); + } else { + chphase = hphase; + cvphase = vphase; + } + + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_OUTSIZE(layer), outsize); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_YINSIZE(layer), insize); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_YHSTEP(layer), hscale); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_YVSTEP(layer), vscale); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_YHPHASE(layer), hphase); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_YVPHASE(layer), vphase); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_CINSIZE(layer), + SUN8I_VI_SCALER_SIZE(src_w / format->hsub, + src_h / format->vsub)); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_CHSTEP(layer), + hscale / format->hsub); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_CVSTEP(layer), + vscale / format->vsub); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_CHPHASE(layer), chphase); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_CVPHASE(layer), cvphase); + sun8i_vi_scaler_set_coeff(mixer->engine.regs, layer, + hscale, vscale, format); +} diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h new file mode 100644 index 000000000000..a595ab643a5a --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2017 Jernej Skrabec <jernej.skrabec@siol.net> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef _SUN8I_VI_SCALER_H_ +#define _SUN8I_VI_SCALER_H_ + +#include <drm/drm_fourcc.h> +#include "sun8i_mixer.h" + +/* this two macros assumes 16 fractional bits which is standard in DRM */ +#define SUN8I_VI_SCALER_SCALE_MIN 1 +#define SUN8I_VI_SCALER_SCALE_MAX ((1UL << 20) - 1) + +#define SUN8I_VI_SCALER_SCALE_FRAC 20 +#define SUN8I_VI_SCALER_PHASE_FRAC 20 +#define SUN8I_VI_SCALER_COEFF_COUNT 32 +#define SUN8I_VI_SCALER_SIZE(w, h) (((h) - 1) << 16 | ((w) - 1)) + +#define SUN8I_SCALER_VSU_CTRL(ch) (0x20000 + 0x20000 * (ch) + 0x0) +#define SUN8I_SCALER_VSU_OUTSIZE(ch) (0x20000 + 0x20000 * (ch) + 0x40) +#define SUN8I_SCALER_VSU_YINSIZE(ch) (0x20000 + 0x20000 * (ch) + 0x80) +#define SUN8I_SCALER_VSU_YHSTEP(ch) (0x20000 + 0x20000 * (ch) + 0x88) +#define SUN8I_SCALER_VSU_YVSTEP(ch) (0x20000 + 0x20000 * (ch) + 0x8c) +#define SUN8I_SCALER_VSU_YHPHASE(ch) (0x20000 + 0x20000 * (ch) + 0x90) +#define SUN8I_SCALER_VSU_YVPHASE(ch) (0x20000 + 0x20000 * (ch) + 0x98) +#define SUN8I_SCALER_VSU_CINSIZE(ch) (0x20000 + 0x20000 * (ch) + 0xc0) +#define SUN8I_SCALER_VSU_CHSTEP(ch) (0x20000 + 0x20000 * (ch) + 0xc8) +#define SUN8I_SCALER_VSU_CVSTEP(ch) (0x20000 + 0x20000 * (ch) + 0xcc) +#define SUN8I_SCALER_VSU_CHPHASE(ch) (0x20000 + 0x20000 * (ch) + 0xd0) +#define SUN8I_SCALER_VSU_CVPHASE(ch) (0x20000 + 0x20000 * (ch) + 0xd8) +#define SUN8I_SCALER_VSU_YHCOEFF0(ch, i) \ + (0x20000 + 0x20000 * (ch) + 0x200 + 0x4 * (i)) +#define SUN8I_SCALER_VSU_YHCOEFF1(ch, i) \ + (0x20000 + 0x20000 * (ch) + 0x300 + 0x4 * (i)) +#define SUN8I_SCALER_VSU_YVCOEFF(ch, i) \ + (0x20000 + 0x20000 * (ch) + 0x400 + 0x4 * (i)) +#define SUN8I_SCALER_VSU_CHCOEFF0(ch, i) \ + (0x20000 + 0x20000 * (ch) + 0x600 + 0x4 * (i)) +#define SUN8I_SCALER_VSU_CHCOEFF1(ch, i) \ + (0x20000 + 0x20000 * (ch) + 0x700 + 0x4 * (i)) +#define SUN8I_SCALER_VSU_CVCOEFF(ch, i) \ + (0x20000 + 0x20000 * (ch) + 0x800 + 0x4 * (i)) + +#define SUN8I_SCALER_VSU_CTRL_EN BIT(0) +#define SUN8I_SCALER_VSU_CTRL_COEFF_RDY BIT(4) + +void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable); +void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer, + u32 src_w, u32 src_h, u32 dst_w, u32 dst_h, + u32 hscale, u32 vscale, u32 hphase, u32 vphase, + const struct drm_format_info *format); + +#endif |