Commit 5c379b4f authored by Dave Airlie's avatar Dave Airlie

Merge tag 'drm-misc-next-2017-12-07' of git://anongit.freedesktop.org/drm/drm-misc into drm-next

UAPI Changes:

- Add "panel orientation" property to DRM to indicate orientation of the
panel vs the device's casing (Hans de Goede)

Core Changes:

- misc doc and bug fixes

Driver Changes:

- sun4i: Many improvements to the DE driver like multi-plane support and
YUV formats (Jernej Skrabec)

* tag 'drm-misc-next-2017-12-07' of git://anongit.freedesktop.org/drm/drm-misc: (50 commits)
  drm/sun4i: Fix uninitialized variables in vi layer
  drm/fb-helper: Fix potential NULL pointer dereference
  gpu: drm: stm: Adopt SPDX identifiers
  gpu: drm: sti: Adopt SPDX identifiers
  drm/fsl-dcu: Use drm_mode_config_helper_suspend/resume()
  drm/sun4i: Wire in DE2 YUV support
  drm/sun4i: Expand DE2 scaler lib with YUV support
  drm/sun4i: Add DE2 definitions for YUV formats
  drm/sun4i: Add DE2 CSC library
  drm/sun4i: Add CCSC property to DE2 configuration
  drm/sun4i: Add support for HW scaling to DE2
  drm/sun4i: Add scaler configuration to DE2 mixers
  drm/sun4i: Add support for DE2 VI planes
  drm/sun4i: Reorganize UI layer code in DE2
  drm/sun4i: Add support for all HW supported DE2 RGB formats
  drm/sun4i: Add multi plane support to DE2 driver
  drm/sun4i: Move interlace related code in DE2
  drm/sun4i: Move channel size related code in DE2
  drm/sun4i: Move line width setting in DE2
  drm/sun4i: Use values calculated by atomic check
  ...
parents 9c606cd4 bc29489f
Ilitek ILI9225 display panels
This binding is for display panels using an Ilitek ILI9225 controller in SPI
mode.
Required properties:
- compatible: "ilitek,ili9225-2.2in-176x220"
- rs-gpios: Register select signal
- reset-gpios: Reset pin
The node for this driver must be a child node of a SPI controller, hence
all mandatory properties described in ../spi/spi-bus.txt must be specified.
Optional properties:
- rotation: panel rotation in degrees counter clockwise (0,90,180,270)
Example:
display@0{
compatible = "ilitek,ili9225-2.2in-176x220";
reg = <0>;
spi-max-frequency = <12000000>;
rs-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio0 8 GPIO_ACTIVE_HIGH>;
rotation = <270>;
};
......@@ -154,6 +154,7 @@ i2se I2SE GmbH
ibm International Business Machines (IBM)
idt Integrated Device Technologies, Inc.
ifi Ingenieurburo Fur Ic-Technologie (I/F/I)
ilitek ILI Technology Corporation (ILITEK)
img Imagination Technologies Ltd.
infineon Infineon Technologies
inforce Inforce Computing
......
......@@ -163,6 +163,9 @@ Panel Helper Reference
.. kernel-doc:: drivers/gpu/drm/drm_panel.c
:export:
.. kernel-doc:: drivers/gpu/drm/drm_panel_orientation_quirks.c
:export:
Display Port Helper Functions Reference
=======================================
......
......@@ -185,6 +185,15 @@ are better.
Contact: Sean Paul, Maintainer of the driver you plan to convert
Convert drivers to use simple modeset suspend/resume
----------------------------------------------------
Most drivers (except i915 and nouveau) that use
drm_atomic_helper_suspend/resume() can probably be converted to use
drm_mode_config_helper_suspend/resume().
Contact: Maintainer of the driver you plan to convert
Core refactorings
=================
......@@ -404,11 +413,6 @@ those drivers as simple as possible, so lots of room for refactoring:
a drm_device wrong. Doesn't matter, since everyone else gets it wrong
too :-)
- With the fbdev pointer in dev->mode_config we could also make
suspend/resume helpers entirely generic, at least if we add a
dev->mode_config.suspend_state. We could even provide a generic pm_ops
structure with those.
- also rework the drm_framebuffer_funcs->dirty hook wire-up, see above.
Contact: Noralf Trønnes, Daniel Vetter
......
......@@ -4461,6 +4461,12 @@ T: git git://anongit.freedesktop.org/drm/drm-misc
S: Maintained
F: drivers/gpu/drm/tve200/
DRM DRIVER FOR ILITEK ILI9225 PANELS
M: David Lechner <david@lechnology.com>
S: Maintained
F: drivers/gpu/drm/tinydrm/ili9225.c
F: Documentation/devicetree/bindings/display/ili9225.txt
DRM DRIVER FOR INTEL I810 VIDEO CARDS
S: Orphan / Obsolete
F: drivers/gpu/drm/i810/
......
......@@ -7,6 +7,7 @@
menuconfig DRM
tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)"
depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && HAS_DMA
select DRM_PANEL_ORIENTATION_QUIRKS
select HDMI
select FB_CMDLINE
select I2C
......@@ -26,6 +27,10 @@ config DRM_MIPI_DSI
bool
depends on DRM
# Separate option because drm_panel_orientation_quirks.c is shared with fbdev
config DRM_PANEL_ORIENTATION_QUIRKS
tristate
config DRM_DP_AUX_CHARDEV
bool "DRM DP AUX Interface"
depends on DRM
......
......@@ -47,6 +47,7 @@ obj-$(CONFIG_DRM_DEBUG_MM_SELFTEST) += selftests/
obj-$(CONFIG_DRM) += drm.o
obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o
obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o
obj-$(CONFIG_DRM_ARM) += arm/
obj-$(CONFIG_DRM_TTM) += ttm/
obj-$(CONFIG_DRM_TDFX) += tdfx/
......
......@@ -27,6 +27,7 @@
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_modeset_helper.h>
#include <drm/drm_of.h>
#include "malidp_drv.h"
......@@ -745,34 +746,15 @@ static int malidp_platform_remove(struct platform_device *pdev)
static int __maybe_unused malidp_pm_suspend(struct device *dev)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct malidp_drm *malidp = drm->dev_private;
drm_kms_helper_poll_disable(drm);
console_lock();
drm_fbdev_cma_set_suspend(malidp->fbdev, 1);
console_unlock();
malidp->pm_state = drm_atomic_helper_suspend(drm);
if (IS_ERR(malidp->pm_state)) {
console_lock();
drm_fbdev_cma_set_suspend(malidp->fbdev, 0);
console_unlock();
drm_kms_helper_poll_enable(drm);
return PTR_ERR(malidp->pm_state);
}
return 0;
return drm_mode_config_helper_suspend(drm);
}
static int __maybe_unused malidp_pm_resume(struct device *dev)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct malidp_drm *malidp = drm->dev_private;
drm_atomic_helper_resume(drm, malidp->pm_state);
console_lock();
drm_fbdev_cma_set_suspend(malidp->fbdev, 0);
console_unlock();
drm_kms_helper_poll_enable(drm);
drm_mode_config_helper_resume(drm);
return 0;
}
......
......@@ -24,7 +24,6 @@ struct malidp_drm {
struct drm_crtc crtc;
wait_queue_head_t wq;
atomic_t config_valid;
struct drm_atomic_state *pm_state;
u32 core_id;
};
......
......@@ -24,6 +24,7 @@
#include <drm/drm_connector.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder.h>
#include <drm/drm_utils.h>
#include "drm_crtc_internal.h"
#include "drm_internal.h"
......@@ -212,6 +213,8 @@ int drm_connector_init(struct drm_device *dev,
mutex_init(&connector->mutex);
connector->edid_blob_ptr = NULL;
connector->status = connector_status_unknown;
connector->display_info.panel_orientation =
DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
drm_connector_get_cmdline_mode(connector);
......@@ -668,6 +671,13 @@ static const struct drm_prop_enum_list drm_aspect_ratio_enum_list[] = {
{ DRM_MODE_PICTURE_ASPECT_16_9, "16:9" },
};
static const struct drm_prop_enum_list drm_panel_orientation_enum_list[] = {
{ DRM_MODE_PANEL_ORIENTATION_NORMAL, "Normal" },
{ DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP, "Upside Down" },
{ DRM_MODE_PANEL_ORIENTATION_LEFT_UP, "Left Side Up" },
{ DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, "Right Side Up" },
};
static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = {
{ DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
{ DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */
......@@ -776,6 +786,18 @@ DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
*
* CRTC_ID:
* Mode object ID of the &drm_crtc this connector should be connected to.
*
* Connectors for LCD panels may also have one standardized property:
*
* panel orientation:
* On some devices the LCD panel is mounted in the casing in such a way
* that the up/top side of the panel does not match with the top side of
* the device. Userspace can use this property to check for this.
* Note that input coordinates from touchscreens (input devices with
* INPUT_PROP_DIRECT) will still map 1:1 to the actual LCD panel
* coordinates, so if userspace rotates the picture to adjust for
* the orientation it must also apply the same transformation to the
* touchscreen input coordinates.
*/
int drm_connector_create_standard_properties(struct drm_device *dev)
......@@ -1251,6 +1273,57 @@ void drm_mode_connector_set_link_status_property(struct drm_connector *connector
}
EXPORT_SYMBOL(drm_mode_connector_set_link_status_property);
/**
* drm_connector_init_panel_orientation_property -
* initialize the connecters panel_orientation property
* @connector: connector for which to init the panel-orientation property.
* @width: width in pixels of the panel, used for panel quirk detection
* @height: height in pixels of the panel, used for panel quirk detection
*
* This function should only be called for built-in panels, after setting
* connector->display_info.panel_orientation first (if known).
*
* This function will check for platform specific (e.g. DMI based) quirks
* overriding display_info.panel_orientation first, then if panel_orientation
* is not DRM_MODE_PANEL_ORIENTATION_UNKNOWN it will attach the
* "panel orientation" property to the connector.
*
* Returns:
* Zero on success, negative errno on failure.
*/
int drm_connector_init_panel_orientation_property(
struct drm_connector *connector, int width, int height)
{
struct drm_device *dev = connector->dev;
struct drm_display_info *info = &connector->display_info;
struct drm_property *prop;
int orientation_quirk;
orientation_quirk = drm_get_panel_orientation_quirk(width, height);
if (orientation_quirk != DRM_MODE_PANEL_ORIENTATION_UNKNOWN)
info->panel_orientation = orientation_quirk;
if (info->panel_orientation == DRM_MODE_PANEL_ORIENTATION_UNKNOWN)
return 0;
prop = dev->mode_config.panel_orientation_property;
if (!prop) {
prop = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
"panel orientation",
drm_panel_orientation_enum_list,
ARRAY_SIZE(drm_panel_orientation_enum_list));
if (!prop)
return -ENOMEM;
dev->mode_config.panel_orientation_property = prop;
}
drm_object_attach_property(&connector->base, prop,
info->panel_orientation);
return 0;
}
EXPORT_SYMBOL(drm_connector_init_panel_orientation_property);
int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj,
struct drm_property *property,
uint64_t value)
......
......@@ -41,6 +41,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include "drm_crtc_internal.h"
#include "drm_crtc_helper_internal.h"
static bool drm_fbdev_emulation = true;
......@@ -177,7 +178,7 @@ EXPORT_SYMBOL(drm_fb_helper_add_one_connector);
*/
int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
{
struct drm_device *dev = fb_helper->dev;
struct drm_device *dev;
struct drm_connector *connector;
struct drm_connector_list_iter conn_iter;
int i, ret = 0;
......@@ -185,6 +186,8 @@ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
if (!drm_fbdev_emulation || !fb_helper)
return 0;
dev = fb_helper->dev;
mutex_lock(&fb_helper->lock);
drm_connector_list_iter_begin(dev, &conn_iter);
drm_for_each_connector_iter(connector, &conn_iter) {
......@@ -356,6 +359,7 @@ EXPORT_SYMBOL(drm_fb_helper_debug_leave);
static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper, bool active)
{
struct drm_device *dev = fb_helper->dev;
struct drm_plane_state *plane_state;
struct drm_plane *plane;
struct drm_atomic_state *state;
int i, ret;
......@@ -374,8 +378,6 @@ static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper, bool activ
retry:
plane_mask = 0;
drm_for_each_plane(plane, dev) {
struct drm_plane_state *plane_state;
plane_state = drm_atomic_get_plane_state(state, plane);
if (IS_ERR(plane_state)) {
ret = PTR_ERR(plane_state);
......@@ -398,6 +400,11 @@ static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper, bool activ
for (i = 0; i < fb_helper->crtc_count; i++) {
struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
struct drm_plane *primary = mode_set->crtc->primary;
/* Cannot fail as we've already gotten the plane state above */
plane_state = drm_atomic_get_new_plane_state(state, primary);
plane_state->rotation = fb_helper->crtc_info[i].rotation;
ret = __drm_atomic_helper_set_config(mode_set, state);
if (ret != 0)
......@@ -829,6 +836,7 @@ int drm_fb_helper_init(struct drm_device *dev,
if (!fb_helper->crtc_info[i].mode_set.connectors)
goto out_free;
fb_helper->crtc_info[i].mode_set.num_connectors = 0;
fb_helper->crtc_info[i].rotation = DRM_MODE_ROTATE_0;
}
i = 0;
......@@ -2357,6 +2365,62 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
return best_score;
}
/*
* This function checks if rotation is necessary because of panel orientation
* and if it is, if it is supported.
* If rotation is necessary and supported, its gets set in fb_crtc.rotation.
* If rotation is necessary but not supported, a DRM_MODE_ROTATE_* flag gets
* or-ed into fb_helper->sw_rotations. In drm_setup_crtcs_fb() we check if only
* one bit is set and then we set fb_info.fbcon_rotate_hint to make fbcon do
* the unsupported rotation.
*/
static void drm_setup_crtc_rotation(struct drm_fb_helper *fb_helper,
struct drm_fb_helper_crtc *fb_crtc,
struct drm_connector *connector)
{
struct drm_plane *plane = fb_crtc->mode_set.crtc->primary;
uint64_t valid_mask = 0;
int i, rotation;
fb_crtc->rotation = DRM_MODE_ROTATE_0;
switch (connector->display_info.panel_orientation) {
case DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP:
rotation = DRM_MODE_ROTATE_180;
break;
case DRM_MODE_PANEL_ORIENTATION_LEFT_UP:
rotation = DRM_MODE_ROTATE_90;
break;
case DRM_MODE_PANEL_ORIENTATION_RIGHT_UP:
rotation = DRM_MODE_ROTATE_270;
break;
default:
rotation = DRM_MODE_ROTATE_0;
}
/*
* TODO: support 90 / 270 degree hardware rotation,
* depending on the hardware this may require the framebuffer
* to be in a specific tiling format.
*/
if (rotation != DRM_MODE_ROTATE_180 || !plane->rotation_property) {
fb_helper->sw_rotations |= rotation;
return;
}
for (i = 0; i < plane->rotation_property->num_values; i++)
valid_mask |= (1ULL << plane->rotation_property->values[i]);
if (!(rotation & valid_mask)) {
fb_helper->sw_rotations |= rotation;
return;
}
fb_crtc->rotation = rotation;
/* Rotating in hardware, fbcon should not rotate */
fb_helper->sw_rotations |= DRM_MODE_ROTATE_0;
}
static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
u32 width, u32 height)
{
......@@ -2416,6 +2480,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
drm_fb_helper_modeset_release(fb_helper,
&fb_helper->crtc_info[i].mode_set);
fb_helper->sw_rotations = 0;
drm_fb_helper_for_each_connector(fb_helper, i) {
struct drm_display_mode *mode = modes[i];
struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
......@@ -2435,6 +2500,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
modeset->mode = drm_mode_duplicate(dev,
fb_crtc->desired_mode);
drm_connector_get(connector);
drm_setup_crtc_rotation(fb_helper, fb_crtc, connector);
modeset->connectors[modeset->num_connectors++] = connector;
modeset->x = offset->x;
modeset->y = offset->y;
......@@ -2476,6 +2542,28 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper)
}
}
mutex_unlock(&fb_helper->dev->mode_config.mutex);
switch (fb_helper->sw_rotations) {
case DRM_MODE_ROTATE_0:
info->fbcon_rotate_hint = FB_ROTATE_UR;
break;
case DRM_MODE_ROTATE_90:
info->fbcon_rotate_hint = FB_ROTATE_CCW;
break;
case DRM_MODE_ROTATE_180:
info->fbcon_rotate_hint = FB_ROTATE_UD;
break;
case DRM_MODE_ROTATE_270:
info->fbcon_rotate_hint = FB_ROTATE_CW;
break;
default:
/*
* Multiple bits are set / multiple rotations requested
* fbcon cannot handle separate rotation settings per
* output, so fallback to unrotated.
*/
info->fbcon_rotate_hint = FB_ROTATE_UR;
}
}
/* Note: Drops fb_helper->lock before returning. */
......
......@@ -20,6 +20,9 @@
* OF THIS SOFTWARE.
*/
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_modeset_helper.h>
#include <drm/drm_plane_helper.h>
......@@ -156,3 +159,76 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
NULL);
}
EXPORT_SYMBOL(drm_crtc_init);
/**
* drm_mode_config_helper_suspend - Modeset suspend helper
* @dev: DRM device
*
* This helper function takes care of suspending the modeset side. It disables
* output polling if initialized, suspends fbdev if used and finally calls
* drm_atomic_helper_suspend().
* If suspending fails, fbdev and polling is re-enabled.
*
* Returns:
* Zero on success, negative error code on error.
*
* See also:
* drm_kms_helper_poll_disable() and drm_fb_helper_set_suspend_unlocked().
*/
int drm_mode_config_helper_suspend(struct drm_device *dev)
{
struct drm_atomic_state *state;
if (!dev)
return 0;
drm_kms_helper_poll_disable(dev);
drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 1);
state = drm_atomic_helper_suspend(dev);
if (IS_ERR(state)) {
drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 0);
drm_kms_helper_poll_enable(dev);
return PTR_ERR(state);
}
dev->mode_config.suspend_state = state;
return 0;
}
EXPORT_SYMBOL(drm_mode_config_helper_suspend);
/**
* drm_mode_config_helper_resume - Modeset resume helper
* @dev: DRM device
*
* This helper function takes care of resuming the modeset side. It calls
* drm_atomic_helper_resume(), resumes fbdev if used and enables output polling
* if initiaized.
*
* Returns:
* Zero on success, negative error code on error.
*
* See also:
* drm_fb_helper_set_suspend_unlocked() and drm_kms_helper_poll_enable().
*/
int drm_mode_config_helper_resume(struct drm_device *dev)
{
int ret;
if (!dev)
return 0;
if (WARN_ON(!dev->mode_config.suspend_state))
return -EINVAL;
ret = drm_atomic_helper_resume(dev, dev->mode_config.suspend_state);
if (ret)
DRM_ERROR("Failed to resume (%d)\n", ret);
dev->mode_config.suspend_state = NULL;
drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 0);
drm_kms_helper_poll_enable(dev);
return ret;
}
EXPORT_SYMBOL(drm_mode_config_helper_resume);
/* SPDX-License-Identifier: MIT */
/*
* fbcon_dmi_quirks.c -- DMI based quirk detection for fbcon
* drm_panel_orientation_quirks.c -- Quirks for non-normal panel orientation
*
* Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
* Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
* Note the quirks in this file are shared with fbdev/efifb and as such
* must not depend on other drm code.
*/
#include <linux/dmi.h>
#include <linux/fb.h>
#include <linux/kernel.h>
#include "fbcon.h"
#include <drm/drm_connector.h>
#ifdef CONFIG_DMI
/*
* Some x86 clamshell design devices use portrait tablet screens and a display
......@@ -21,56 +21,56 @@
* and a list of known BIOS dates to avoid false positives.
*/
struct fbcon_dmi_rotate_data {
struct drm_dmi_panel_orientation_data {
int width;
int height;
const char * const *bios_dates;
int rotate;
int orientation;
};
static const struct fbcon_dmi_rotate_data rotate_data_asus_t100ha = {
static const struct drm_dmi_panel_orientation_data asus_t100ha = {
.width = 800,
.height = 1280,
.rotate = FB_ROTATE_CCW,
.orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP,
};
static const struct fbcon_dmi_rotate_data rotate_data_gpd_pocket = {
static const struct drm_dmi_panel_orientation_data gpd_pocket = {
.width = 1200,
.height = 1920,
.bios_dates = (const char * const []){ "05/26/2017", "06/28/2017",
"07/05/2017", "08/07/2017", NULL },
.rotate = FB_ROTATE_CW,
.orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
};
static const struct fbcon_dmi_rotate_data rotate_data_gpd_win = {
static const struct drm_dmi_panel_orientation_data gpd_win = {
.width = 720,
.height = 1280,
.bios_dates = (const char * const []){
"10/25/2016", "11/18/2016", "12/23/2016", "12/26/2016",
"02/21/2017", "03/20/2017", "05/25/2017", NULL },
.rotate = FB_ROTATE_CW,
.orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
};
static const struct fbcon_dmi_rotate_data rotate_data_itworks_tw891 = {
static const struct drm_dmi_panel_orientation_data itworks_tw891 = {
.width = 800,
.height = 1280,
.bios_dates = (const char * const []){ "10/16/2015", NULL },
.rotate = FB_ROTATE_CW,
.orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
};
static const struct fbcon_dmi_rotate_data rotate_data_vios_lth17 = {
static const struct drm_dmi_panel_orientation_data vios_lth17 = {
.width = 800,
.height = 1280,
.rotate = FB_ROTATE_CW,
.orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
};
static const struct dmi_system_id rotate_data[] = {
static const struct dmi_system_id orientation_data[] = {
{ /* Asus T100HA */
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100HAN"),
},
.driver_data = (void *)&rotate_data_asus_t100ha,
.driver_data = (void *)&asus_t100ha,
}, { /*
* GPD Pocket, note that the the DMI data is less generic then
* it seems, devices with a board-vendor of "AMI Corporation"
......@@ -83,7 +83,7 @@ static const struct dmi_system_id rotate_data[] = {
DMI_EXACT_MATCH(DMI_BOARD_SERIAL, "Default string"),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
},
.driver_data = (void *)&rotate_data_gpd_pocket,
.driver_data = (void *)&gpd_pocket,
}, { /* GPD Win (same note on DMI match as GPD Pocket) */
.matches = {
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
......@@ -91,7 +91,7 @@ static const struct dmi_system_id rotate_data[] = {
DMI_EXACT_MATCH(DMI_BOARD_SERIAL, "Default string"),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
},
.driver_data = (void *)&rotate_data_gpd_win,
.driver_data = (void *)&gpd_win,
}, { /* I.T.Works TW891 */
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."),
......@@ -99,37 +99,54 @@ static const struct dmi_system_id rotate_data[] = {
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "To be filled by O.E.M."),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "TW891"),
},
.driver_data = (void *)&rotate_data_itworks_tw891,
.driver_data = (void *)&itworks_tw891,
}, { /* VIOS LTH17 */
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "VIOS"),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "LTH17"),
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "VIOS"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "LTH17"),
},
.driver_data = (void *)&rotate_data_vios_lth17,
.driver_data = (void *)&vios_lth17,
},
{}
};
int fbcon_platform_get_rotate(struct fb_info *info)
/**
* drm_get_panel_orientation_quirk - Check for panel orientation quirks
* @width: width in pixels of the panel
* @height: height in pixels of the panel
*
* This function checks for platform specific (e.g. DMI based) quirks
* providing info on panel_orientation for systems where this cannot be
* probed from the hard-/firm-ware. To avoid false-positive this function
* takes the panel resolution as argument and checks that against the
* resolution expected by the quirk-table entry.
*
* Note this function is also used outside of the drm-subsys, by for example
* the efifb code. Because of this this function gets compiled into its own
* kernel-module when built as a module.
*
* Returns:
* A DRM_MODE_PANEL_ORIENTATION_* value if there is a quirk for this system,
* or DRM_MODE_PANEL_ORIENTATION_UNKNOWN if there is no quirk.
*/
int drm_get_panel_orientation_quirk(int width, int height)
{
const struct dmi_system_id *match;
const struct fbcon_dmi_rotate_data *data;
const struct drm_dmi_panel_orientation_data *data;
const char *bios_date;
int i;
for (match = dmi_first_match(rotate_data);
for (match = dmi_first_match(orientation_data);
match;
match = dmi_first_match(match + 1)) {
data = match->driver_data;
if (data->width != info->var.xres ||
data->height != info->var.yres)
if (data->width != width ||
data->height != height)
continue;
if (!data->bios_dates)
return data->rotate;
return data->orientation;
bios_date = dmi_get_system_info(DMI_BIOS_DATE);
if (!bios_date)
......@@ -137,9 +154,21 @@ int fbcon_platform_get_rotate(struct fb_info *info)
for (i = 0; data->bios_dates[i]; i++) {
if (!strcmp(data->bios_dates[i], bios_date))
return data->rotate;
return data->orientation;
}
}
return FB_ROTATE_UR;
return DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
}
EXPORT_SYMBOL(drm_get_panel_orientation_quirk);
#else
/* There are no quirks for non x86 devices yet */
int drm_get_panel_orientation_quirk(int width, int height)
{
return DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
}
EXPORT_SYMBOL(drm_get_panel_orientation_quirk);
#endif
......@@ -218,8 +218,9 @@ static void drm_gem_map_detach(struct dma_buf *dma_buf,
sgt = prime_attach->sgt;
if (sgt) {
if (prime_attach->dir != DMA_NONE)
dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents,
prime_attach->dir);
dma_unmap_sg_attrs(attach->dev, sgt->sgl, sgt->nents,
prime_attach->dir,
DMA_ATTR_SKIP_CPU_SYNC);
sg_free_table(sgt);
}
......@@ -277,7 +278,8 @@ static struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach,
sgt = obj->dev->driver->gem_prime_get_sg_table(obj);
if (!IS_ERR(sgt)) {
if (!dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir)) {
if (!dma_map_sg_attrs(attach->dev, sgt->sgl, sgt->nents, dir,
DMA_ATTR_SKIP_CPU_SYNC)) {
sg_free_table(sgt);
kfree(sgt);
sgt = ERR_PTR(-ENOMEM);
......
......@@ -216,8 +216,7 @@ enum drm_mode_status drm_connector_mode_valid(struct drm_connector *connector,
* suspend/resume.
*
* Drivers can call this helper from their device resume implementation. It is
* an error to call this when the output polling support has not yet been set
* up.
* not an error to call this even when output polling isn't enabled.
*
* Note that calls to enable and disable polling must be strictly ordered, which
* is automatically the case when they're only call from suspend/resume
......
......@@ -27,6 +27,7 @@
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_modeset_helper.h>
#include "fsl_dcu_drm_crtc.h"
#include "fsl_dcu_drm_drv.h"
......@@ -188,26 +189,17 @@ static struct drm_driver fsl_dcu_drm_driver = {
static int fsl_dcu_drm_pm_suspend(struct device *dev)
{
struct fsl_dcu_drm_device *fsl_dev = dev_get_drvdata(dev);
int ret;
if (!fsl_dev)
return 0;
disable_irq(fsl_dev->irq);
drm_kms_helper_poll_disable(fsl_dev->drm);
console_lock();
drm_fbdev_cma_set_suspend(fsl_dev->fbdev, 1);
console_unlock();
fsl_dev->state = drm_atomic_helper_suspend(fsl_dev->drm);
if (IS_ERR(fsl_dev->state)) {
console_lock();
drm_fbdev_cma_set_suspend(fsl_dev->fbdev, 0);
console_unlock();
drm_kms_helper_poll_enable(fsl_dev->drm);
ret = drm_mode_config_helper_suspend(fsl_dev->drm);
if (ret) {
enable_irq(fsl_dev->irq);
return PTR_ERR(fsl_dev->state);
return ret;
}
clk_disable_unprepare(fsl_dev->clk);
......@@ -233,13 +225,8 @@ static int fsl_dcu_drm_pm_resume(struct device *dev)
fsl_tcon_bypass_enable(fsl_dev->tcon);
fsl_dcu_drm_init_planes(fsl_dev->drm);
enable_irq(fsl_dev->irq);
drm_atomic_helper_resume(fsl_dev->drm, fsl_dev->state);
console_lock();
drm_fbdev_cma_set_suspend(fsl_dev->fbdev, 0);
console_unlock();
drm_kms_helper_poll_enable(fsl_dev->drm);
drm_mode_config_helper_resume(fsl_dev->drm);
return 0;
}
......
......@@ -196,7 +196,6 @@ struct fsl_dcu_drm_device {
struct drm_encoder encoder;
struct fsl_dcu_drm_connector connector;
const struct fsl_dcu_soc_data *soc;
struct drm_atomic_state *state;
};
int fsl_dcu_drm_modeset_init(struct fsl_dcu_drm_device *fsl_dev);
......
......@@ -1666,6 +1666,27 @@ static const struct drm_connector_funcs intel_dsi_connector_funcs = {
.atomic_duplicate_state = intel_digital_connector_duplicate_state,
};
static int intel_dsi_get_panel_orientation(struct intel_connector *connector)
{
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
int orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL;
enum plane plane;
u32 val;
if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
if (connector->encoder->crtc_mask == BIT(PIPE_B))
plane = PLANE_B;
else
plane = PLANE_A;
val = I915_READ(DSPCNTR(plane));
if (val & DISPPLANE_ROTATE_180)
orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP;
}
return orientation;
}
static void intel_dsi_add_properties(struct intel_connector *connector)
{
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
......@@ -1681,6 +1702,13 @@ static void intel_dsi_add_properties(struct intel_connector *connector)
allowed_scalers);
connector->base.state->scaling_mode = DRM_MODE_SCALE_ASPECT;
connector->base.display_info.panel_orientation =
intel_dsi_get_panel_orientation(connector);
drm_connector_init_panel_orientation_property(
&connector->base,
connector->panel.fixed_mode->hdisplay,
connector->panel.fixed_mode->vdisplay);
}
}
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2014
* Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#include "sti_awg_utils.h"
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2014
* Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#ifndef _STI_AWG_UTILS_H_
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2014
* Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
* Fabien Dessenne <fabien.dessenne@st.com>
* for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#include <linux/component.h>
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2014
* Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
* Fabien Dessenne <fabien.dessenne@st.com>
* for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#ifndef _STI_COMPOSITOR_H_
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2014
* Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
* Fabien Dessenne <fabien.dessenne@st.com>
* for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#include <linux/clk.h>
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2014
* Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#ifndef _STI_CRTC_H_
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2014
* Authors: Vincent Abriou <vincent.abriou@st.com>
* Fabien Dessenne <fabien.dessenne@st.com>
* for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#include <linux/seq_file.h>
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2013
* Authors: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#ifndef _STI_CURSOR_H_
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2014
* Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#include <drm/drmP.h>
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2014
* Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#ifndef _STI_DRV_H_
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2014
* Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#include <linux/clk.h>
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2014
* Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
* Fabien Dessenne <fabien.dessenne@st.com>
* for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#include <linux/seq_file.h>
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2014
* Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
* Fabien Dessenne <fabien.dessenne@st.com>
* for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#ifndef _STI_GDP_H_
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2014
* Author: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#include <linux/clk.h>
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2014
* Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#include <linux/clk.h>
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2014
* Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#ifndef _STI_HDMI_H_
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2014
* Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#include "sti_hdmi_tx3g4c28phy.h"
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2014
* Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#ifndef _STI_HDMI_TX3G4C28PHY_H_
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2014
* Authors: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#include <linux/component.h>
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2014
* Authors: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#ifndef _STI_HQVDP_LUT_H_
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2014
* Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
* Fabien Dessenne <fabien.dessenne@st.com>
* for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#include <linux/seq_file.h>
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2014
* Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
* Fabien Dessenne <fabien.dessenne@st.com>
* for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#ifndef _STI_MIXER_H_
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2014
* Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
* Fabien Dessenne <fabien.dessenne@st.com>
* for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#include <drm/drmP.h>
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2014
* Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#ifndef _STI_PLANE_H_
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2014
* Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
* Vincent Abriou <vincent.abriou@st.com>
* for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#include <linux/clk.h>
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2014
* Author: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#include <linux/seq_file.h>
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2014
* Author: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#ifndef _STI_VID_H_
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2014
* Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
* Fabien Dessenne <fabien.dessenne@st.com>
* Vincent Abriou <vincent.abriou@st.com>
* for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#include <linux/module.h>
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2014
* Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2
*/
#ifndef _STI_VTG_H_
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2017
*
......@@ -5,8 +6,6 @@
* Yannick Fertre <yannick.fertre@st.com>
* Fabien Dessenne <fabien.dessenne@st.com>
* Mickael Reulier <mickael.reulier@st.com>
*
* License terms: GNU General Public License (GPL), version 2
*/
#include <linux/component.h>
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2017
*
* Authors: Philippe Cornu <philippe.cornu@st.com>
* Yannick Fertre <yannick.fertre@st.com>
*
* License terms: GNU General Public License (GPL), version 2
*/
#include <linux/clk.h>
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics SA 2017
*
......@@ -5,8 +6,6 @@
* Yannick Fertre <yannick.fertre@st.com>
* Fabien Dessenne <fabien.dessenne@st.com>
* Mickael Reulier <mickael.reulier@st.com>
*
* License terms: GNU General Public License (GPL), version 2
*/
#include <linux/clk.h>
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) STMicroelectronics SA 2017
*
......@@ -5,8 +6,6 @@
* Yannick Fertre <yannick.fertre@st.com>
* Fabien Dessenne <fabien.dessenne@st.com>
* Mickael Reulier <mickael.reulier@st.com>
*
* License terms: GNU General Public License (GPL), version 2
*/
#ifndef _LTDC_H_
......
......@@ -9,7 +9,9 @@ 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
......
......@@ -187,13 +187,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)
......
......@@ -900,7 +900,8 @@ 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 },
......@@ -911,6 +912,7 @@ static const struct of_device_id sun4i_tcon_of_table[] = {
{ }
};
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,
......
......@@ -197,4 +197,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__ */
/*
* 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);
}
/*
* 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
/*
* 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;
}
/*
* 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_ */
......@@ -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;
/*
......@@ -325,27 +410,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;
......@@ -388,6 +472,8 @@ static int sun8i_mixer_remove(struct platform_device *pdev)
static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
.vi_num = 2,
.ui_num = 1,
.scaler_mask = 0x3,
.ccsc = 0,
};
static const struct of_device_id sun8i_mixer_of_table[] = {
......
......@@ -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,31 @@
#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.
*/
struct sun8i_mixer_cfg {
int vi_num;
int ui_num;
int scaler_mask;
int ccsc;
};
struct sun8i_mixer {
......@@ -126,12 +146,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_ */
/*
* 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;
}
/*
* 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_ */
/*
* 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]);
}
/*
* 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
/*
* 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;
}
/*
* 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_ */
/*
* 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);
}
/*
* 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
......@@ -12,6 +12,16 @@ menuconfig DRM_TINYDRM
config TINYDRM_MIPI_DBI
tristate
config TINYDRM_ILI9225
tristate "DRM support for ILI9225 display panels"
depends on DRM_TINYDRM && SPI
select TINYDRM_MIPI_DBI
help
DRM driver for the following Ilitek ILI9225 panels:
* No-name 2.2" color screen module
If M is selected the module will be called ili9225.
config TINYDRM_MI0283QT
tristate "DRM support for MI0283QT"
depends on DRM_TINYDRM && SPI
......
......@@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_TINYDRM) += core/
obj-$(CONFIG_TINYDRM_MIPI_DBI) += mipi-dbi.o
# Displays
obj-$(CONFIG_TINYDRM_ILI9225) += ili9225.o
obj-$(CONFIG_TINYDRM_MI0283QT) += mi0283qt.o
obj-$(CONFIG_TINYDRM_REPAPER) += repaper.o
obj-$(CONFIG_TINYDRM_ST7586) += st7586.o
......@@ -292,71 +292,4 @@ void tinydrm_shutdown(struct tinydrm_device *tdev)
}
EXPORT_SYMBOL(tinydrm_shutdown);
/**
* tinydrm_suspend - Suspend tinydrm
* @tdev: tinydrm device
*
* Used in driver PM operations to suspend tinydrm.
* Suspends fbdev and DRM.
* Resume with tinydrm_resume().
*
* Returns:
* Zero on success, negative error code on failure.
*/
int tinydrm_suspend(struct tinydrm_device *tdev)
{
struct drm_atomic_state *state;
if (tdev->suspend_state) {
DRM_ERROR("Failed to suspend: state already set\n");
return -EINVAL;
}
drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 1);
state = drm_atomic_helper_suspend(tdev->drm);
if (IS_ERR(state)) {
drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 0);
return PTR_ERR(state);
}
tdev->suspend_state = state;
return 0;
}
EXPORT_SYMBOL(tinydrm_suspend);
/**
* tinydrm_resume - Resume tinydrm
* @tdev: tinydrm device
*
* Used in driver PM operations to resume tinydrm.
* Suspend with tinydrm_suspend().
*
* Returns:
* Zero on success, negative error code on failure.
*/
int tinydrm_resume(struct tinydrm_device *tdev)
{
struct drm_atomic_state *state = tdev->suspend_state;
int ret;
if (!state) {
DRM_ERROR("Failed to resume: state is not set\n");
return -EINVAL;
}
tdev->suspend_state = NULL;
ret = drm_atomic_helper_resume(tdev->drm, state);
if (ret) {
DRM_ERROR("Error resuming state: %d\n", ret);
return ret;
}
drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 0);
return 0;
}
EXPORT_SYMBOL(tinydrm_resume);
MODULE_LICENSE("GPL");
/*
* DRM driver for Ilitek ILI9225 panels
*
* Copyright 2017 David Lechner <david@lechnology.com>
*
* Some code copied from mipi-dbi.c
* Copyright 2016 Noralf Trønnes
*
* 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 <linux/delay.h>
#include <linux/dma-buf.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/spi/spi.h>
#include <video/mipi_display.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/tinydrm/mipi-dbi.h>
#include <drm/tinydrm/tinydrm-helpers.h>
#define ILI9225_DRIVER_READ_CODE 0x00
#define ILI9225_DRIVER_OUTPUT_CONTROL 0x01
#define ILI9225_LCD_AC_DRIVING_CONTROL 0x02
#define ILI9225_ENTRY_MODE 0x03
#define ILI9225_DISPLAY_CONTROL_1 0x07
#define ILI9225_BLANK_PERIOD_CONTROL_1 0x08
#define ILI9225_FRAME_CYCLE_CONTROL 0x0b
#define ILI9225_INTERFACE_CONTROL 0x0c
#define ILI9225_OSCILLATION_CONTROL 0x0f
#define ILI9225_POWER_CONTROL_1 0x10
#define ILI9225_POWER_CONTROL_2 0x11
#define ILI9225_POWER_CONTROL_3 0x12
#define ILI9225_POWER_CONTROL_4 0x13
#define ILI9225_POWER_CONTROL_5 0x14
#define ILI9225_VCI_RECYCLING 0x15
#define ILI9225_RAM_ADDRESS_SET_1 0x20
#define ILI9225_RAM_ADDRESS_SET_2 0x21
#define ILI9225_WRITE_DATA_TO_GRAM 0x22
#define ILI9225_SOFTWARE_RESET 0x28
#define ILI9225_GATE_SCAN_CONTROL 0x30
#define ILI9225_VERTICAL_SCROLL_1 0x31
#define ILI9225_VERTICAL_SCROLL_2 0x32
#define ILI9225_VERTICAL_SCROLL_3 0x33
#define ILI9225_PARTIAL_DRIVING_POS_1 0x34
#define ILI9225_PARTIAL_DRIVING_POS_2 0x35
#define ILI9225_HORIZ_WINDOW_ADDR_1 0x36
#define ILI9225_HORIZ_WINDOW_ADDR_2 0x37
#define ILI9225_VERT_WINDOW_ADDR_1 0x38
#define ILI9225_VERT_WINDOW_ADDR_2 0x39
#define ILI9225_GAMMA_CONTROL_1 0x50
#define ILI9225_GAMMA_CONTROL_2 0x51
#define ILI9225_GAMMA_CONTROL_3 0x52
#define ILI9225_GAMMA_CONTROL_4 0x53
#define ILI9225_GAMMA_CONTROL_5 0x54
#define ILI9225_GAMMA_CONTROL_6 0x55
#define ILI9225_GAMMA_CONTROL_7 0x56
#define ILI9225_GAMMA_CONTROL_8 0x57
#define ILI9225_GAMMA_CONTROL_9 0x58
#define ILI9225_GAMMA_CONTROL_10 0x59
static inline int ili9225_command(struct mipi_dbi *mipi, u8 cmd, u16 data)
{
u8 par[2] = { data >> 8, data & 0xff };
return mipi_dbi_command_buf(mipi, cmd, par, 2);
}
static int ili9225_fb_dirty(struct drm_framebuffer *fb,
struct drm_file *file_priv, unsigned int flags,
unsigned int color, struct drm_clip_rect *clips,
unsigned int num_clips)
{
struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
struct tinydrm_device *tdev = fb->dev->dev_private;
struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
bool swap = mipi->swap_bytes;
struct drm_clip_rect clip;
u16 x_start, y_start;
u16 x1, x2, y1, y2;
int ret = 0;
bool full;
void *tr;
mutex_lock(&tdev->dirty_lock);
if (!mipi->enabled)
goto out_unlock;
/* fbdev can flush even when we're not interested */
if (tdev->pipe.plane.fb != fb)
goto out_unlock;
full = tinydrm_merge_clips(&clip, clips, num_clips, flags,
fb->width, fb->height);
DRM_DEBUG("Flushing [FB:%d] x1=%u, x2=%u, y1=%u, y2=%u\n", fb->base.id,
clip.x1, clip.x2, clip.y1, clip.y2);
if (!mipi->dc || !full || swap ||
fb->format->format == DRM_FORMAT_XRGB8888) {
tr = mipi->tx_buf;
ret = mipi_dbi_buf_copy(mipi->tx_buf, fb, &clip, swap);
if (ret)
goto out_unlock;
} else {
tr = cma_obj->vaddr;
}
switch (mipi->rotation) {
default:
x1 = clip.x1;
x2 = clip.x2 - 1;
y1 = clip.y1;
y2 = clip.y2 - 1;
x_start = x1;
y_start = y1;
break;
case 90:
x1 = clip.y1;
x2 = clip.y2 - 1;
y1 = fb->width - clip.x2;
y2 = fb->width - clip.x1 - 1;
x_start = x1;
y_start = y2;
break;
case 180:
x1 = fb->width - clip.x2;
x2 = fb->width - clip.x1 - 1;
y1 = fb->height - clip.y2;
y2 = fb->height - clip.y1 - 1;
x_start = x2;
y_start = y2;
break;
case 270:
x1 = fb->height - clip.y2;
x2 = fb->height - clip.y1 - 1;
y1 = clip.x1;
y2 = clip.x2 - 1;
x_start = x2;
y_start = y1;
break;
}
ili9225_command(mipi, ILI9225_HORIZ_WINDOW_ADDR_1, x2);
ili9225_command(mipi, ILI9225_HORIZ_WINDOW_ADDR_2, x1);
ili9225_command(mipi, ILI9225_VERT_WINDOW_ADDR_1, y2);
ili9225_command(mipi, ILI9225_VERT_WINDOW_ADDR_2, y1);
ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_1, x_start);
ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_2, y_start);
ret = mipi_dbi_command_buf(mipi, ILI9225_WRITE_DATA_TO_GRAM, tr,
(clip.x2 - clip.x1) * (clip.y2 - clip.y1) * 2);
out_unlock:
mutex_unlock(&tdev->dirty_lock);
if (ret)
dev_err_once(fb->dev->dev, "Failed to update display %d\n",
ret);
return ret;
}
static const struct drm_framebuffer_funcs ili9225_fb_funcs = {
.destroy = drm_gem_fb_destroy,
.create_handle = drm_gem_fb_create_handle,
.dirty = ili9225_fb_dirty,
};
static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
struct drm_crtc_state *crtc_state)
{
struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
struct drm_framebuffer *fb = pipe->plane.fb;
struct device *dev = tdev->drm->dev;
int ret;
u8 am_id;
DRM_DEBUG_KMS("\n");
mipi_dbi_hw_reset(mipi);
/*
* There don't seem to be two example init sequences that match, so
* using the one from the popular Arduino library for this display.
* https://github.com/Nkawu/TFT_22_ILI9225/blob/master/src/TFT_22_ILI9225.cpp
*/
ret = ili9225_command(mipi, ILI9225_POWER_CONTROL_1, 0x0000);
if (ret) {
DRM_DEV_ERROR(dev, "Error sending command %d\n", ret);
return;
}
ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x0000);
ili9225_command(mipi, ILI9225_POWER_CONTROL_3, 0x0000);
ili9225_command(mipi, ILI9225_POWER_CONTROL_4, 0x0000);
ili9225_command(mipi, ILI9225_POWER_CONTROL_5, 0x0000);
msleep(40);
ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x0018);
ili9225_command(mipi, ILI9225_POWER_CONTROL_3, 0x6121);
ili9225_command(mipi, ILI9225_POWER_CONTROL_4, 0x006f);
ili9225_command(mipi, ILI9225_POWER_CONTROL_5, 0x495f);
ili9225_command(mipi, ILI9225_POWER_CONTROL_1, 0x0800);
msleep(10);
ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x103b);
msleep(50);
switch (mipi->rotation) {
default:
am_id = 0x30;
break;
case 90:
am_id = 0x18;
break;
case 180:
am_id = 0x00;
break;
case 270:
am_id = 0x28;
break;
}
ili9225_command(mipi, ILI9225_DRIVER_OUTPUT_CONTROL, 0x011c);
ili9225_command(mipi, ILI9225_LCD_AC_DRIVING_CONTROL, 0x0100);
ili9225_command(mipi, ILI9225_ENTRY_MODE, 0x1000 | am_id);
ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x0000);
ili9225_command(mipi, ILI9225_BLANK_PERIOD_CONTROL_1, 0x0808);
ili9225_command(mipi, ILI9225_FRAME_CYCLE_CONTROL, 0x1100);
ili9225_command(mipi, ILI9225_INTERFACE_CONTROL, 0x0000);
ili9225_command(mipi, ILI9225_OSCILLATION_CONTROL, 0x0d01);
ili9225_command(mipi, ILI9225_VCI_RECYCLING, 0x0020);
ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_1, 0x0000);
ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_2, 0x0000);
ili9225_command(mipi, ILI9225_GATE_SCAN_CONTROL, 0x0000);
ili9225_command(mipi, ILI9225_VERTICAL_SCROLL_1, 0x00db);
ili9225_command(mipi, ILI9225_VERTICAL_SCROLL_2, 0x0000);
ili9225_command(mipi, ILI9225_VERTICAL_SCROLL_3, 0x0000);
ili9225_command(mipi, ILI9225_PARTIAL_DRIVING_POS_1, 0x00db);
ili9225_command(mipi, ILI9225_PARTIAL_DRIVING_POS_2, 0x0000);
ili9225_command(mipi, ILI9225_GAMMA_CONTROL_1, 0x0000);
ili9225_command(mipi, ILI9225_GAMMA_CONTROL_2, 0x0808);
ili9225_command(mipi, ILI9225_GAMMA_CONTROL_3, 0x080a);
ili9225_command(mipi, ILI9225_GAMMA_CONTROL_4, 0x000a);
ili9225_command(mipi, ILI9225_GAMMA_CONTROL_5, 0x0a08);
ili9225_command(mipi, ILI9225_GAMMA_CONTROL_6, 0x0808);
ili9225_command(mipi, ILI9225_GAMMA_CONTROL_7, 0x0000);
ili9225_command(mipi, ILI9225_GAMMA_CONTROL_8, 0x0a00);
ili9225_command(mipi, ILI9225_GAMMA_CONTROL_9, 0x0710);
ili9225_command(mipi, ILI9225_GAMMA_CONTROL_10, 0x0710);
ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x0012);
msleep(50);
ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x1017);
mipi->enabled = true;
if (fb)
fb->funcs->dirty(fb, NULL, 0, 0, NULL, 0);
}
static void ili9225_pipe_disable(struct drm_simple_display_pipe *pipe)
{
struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
DRM_DEBUG_KMS("\n");
if (!mipi->enabled)
return;
ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x0000);
msleep(50);
ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x0007);
msleep(50);
ili9225_command(mipi, ILI9225_POWER_CONTROL_1, 0x0a02);
mipi->enabled = false;
}
static int ili9225_dbi_command(struct mipi_dbi *mipi, u8 cmd, u8 *par,
size_t num)
{
struct spi_device *spi = mipi->spi;
unsigned int bpw = 8;
u32 speed_hz;
int ret;
gpiod_set_value_cansleep(mipi->dc, 0);
speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1);
ret = tinydrm_spi_transfer(spi, speed_hz, NULL, 8, &cmd, 1);
if (ret || !num)
return ret;
if (cmd == ILI9225_WRITE_DATA_TO_GRAM && !mipi->swap_bytes)
bpw = 16;
gpiod_set_value_cansleep(mipi->dc, 1);
speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num);
return tinydrm_spi_transfer(spi, speed_hz, NULL, bpw, par, num);
}
static const u32 ili9225_formats[] = {
DRM_FORMAT_RGB565,
DRM_FORMAT_XRGB8888,
};
static int ili9225_init(struct device *dev, struct mipi_dbi *mipi,
const struct drm_simple_display_pipe_funcs *pipe_funcs,
struct drm_driver *driver,
const struct drm_display_mode *mode,
unsigned int rotation)
{
size_t bufsize = mode->vdisplay * mode->hdisplay * sizeof(u16);
struct tinydrm_device *tdev = &mipi->tinydrm;
int ret;
if (!mipi->command)
return -EINVAL;
mutex_init(&mipi->cmdlock);
mipi->tx_buf = devm_kmalloc(dev, bufsize, GFP_KERNEL);
if (!mipi->tx_buf)
return -ENOMEM;
ret = devm_tinydrm_init(dev, tdev, &ili9225_fb_funcs, driver);
if (ret)
return ret;
ret = tinydrm_display_pipe_init(tdev, pipe_funcs,
DRM_MODE_CONNECTOR_VIRTUAL,
ili9225_formats,
ARRAY_SIZE(ili9225_formats), mode,
rotation);
if (ret)
return ret;
tdev->drm->mode_config.preferred_depth = 16;
mipi->rotation = rotation;
drm_mode_config_reset(tdev->drm);
DRM_DEBUG_KMS("preferred_depth=%u, rotation = %u\n",
tdev->drm->mode_config.preferred_depth, rotation);
return 0;
}
static const struct drm_simple_display_pipe_funcs ili9225_pipe_funcs = {
.enable = ili9225_pipe_enable,
.disable = ili9225_pipe_disable,
.update = tinydrm_display_pipe_update,
.prepare_fb = tinydrm_display_pipe_prepare_fb,
};
static const struct drm_display_mode ili9225_mode = {
TINYDRM_MODE(176, 220, 35, 44),
};
DEFINE_DRM_GEM_CMA_FOPS(ili9225_fops);
static struct drm_driver ili9225_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
DRIVER_ATOMIC,
.fops = &ili9225_fops,
TINYDRM_GEM_DRIVER_OPS,
.lastclose = tinydrm_lastclose,
.name = "ili9225",
.desc = "Ilitek ILI9225",
.date = "20171106",
.major = 1,
.minor = 0,
};
static const struct of_device_id ili9225_of_match[] = {
{ .compatible = "ilitek,ili9225-2.2in-176x220" },
{},
};
MODULE_DEVICE_TABLE(of, ili9225_of_match);
static const struct spi_device_id ili9225_id[] = {
{ "ili9225-2.2in-176x220", 0 },
{ },
};
MODULE_DEVICE_TABLE(spi, ili9225_id);
static int ili9225_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
struct mipi_dbi *mipi;
struct gpio_desc *rs;
u32 rotation = 0;
int ret;
mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
if (!mipi)
return -ENOMEM;
mipi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(mipi->reset)) {
DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
return PTR_ERR(mipi->reset);
}
rs = devm_gpiod_get(dev, "rs", GPIOD_OUT_LOW);
if (IS_ERR(rs)) {
DRM_DEV_ERROR(dev, "Failed to get gpio 'rs'\n");
return PTR_ERR(rs);
}
device_property_read_u32(dev, "rotation", &rotation);
ret = mipi_dbi_spi_init(spi, mipi, rs);
if (ret)
return ret;
/* override the command function set in mipi_dbi_spi_init() */
mipi->command = ili9225_dbi_command;
ret = ili9225_init(&spi->dev, mipi, &ili9225_pipe_funcs,
&ili9225_driver, &ili9225_mode, rotation);
if (ret)
return ret;
spi_set_drvdata(spi, mipi);
return devm_tinydrm_register(&mipi->tinydrm);
}
static void ili9225_shutdown(struct spi_device *spi)
{
struct mipi_dbi *mipi = spi_get_drvdata(spi);
tinydrm_shutdown(&mipi->tinydrm);
}
static struct spi_driver ili9225_spi_driver = {
.driver = {
.name = "ili9225",
.owner = THIS_MODULE,
.of_match_table = ili9225_of_match,
},
.id_table = ili9225_id,
.probe = ili9225_probe,
.shutdown = ili9225_shutdown,
};
module_spi_driver(ili9225_spi_driver);
MODULE_DESCRIPTION("Ilitek ILI9225 DRM driver");
MODULE_AUTHOR("David Lechner <david@lechnology.com>");
MODULE_LICENSE("GPL");
......@@ -9,6 +9,7 @@
* (at your option) any later version.
*/
#include <drm/drm_modeset_helper.h>
#include <drm/tinydrm/ili9341.h>
#include <drm/tinydrm/mipi-dbi.h>
#include <drm/tinydrm/tinydrm-helpers.h>
......@@ -231,7 +232,7 @@ static int __maybe_unused mi0283qt_pm_suspend(struct device *dev)
struct mipi_dbi *mipi = dev_get_drvdata(dev);
int ret;
ret = tinydrm_suspend(&mipi->tinydrm);
ret = drm_mode_config_helper_suspend(mipi->tinydrm.drm);
if (ret)
return ret;
......@@ -249,7 +250,9 @@ static int __maybe_unused mi0283qt_pm_resume(struct device *dev)
if (ret)
return ret;
return tinydrm_resume(&mipi->tinydrm);
drm_mode_config_helper_resume(mipi->tinydrm.drm);
return 0;
}
static const struct dev_pm_ops mi0283qt_pm_ops = {
......
......@@ -154,8 +154,18 @@ int mipi_dbi_command_buf(struct mipi_dbi *mipi, u8 cmd, u8 *data, size_t len)
}
EXPORT_SYMBOL(mipi_dbi_command_buf);
static int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
struct drm_clip_rect *clip, bool swap)
/**
* mipi_dbi_buf_copy - Copy a framebuffer, transforming it if necessary
* @dst: The destination buffer
* @fb: The source framebuffer
* @clip: Clipping rectangle of the area to be copied
* @swap: When true, swap MSB/LSB of 16-bit values
*
* Returns:
* Zero on success, negative error code on failure.
*/
int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
struct drm_clip_rect *clip, bool swap)
{
struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
struct dma_buf_attachment *import_attach = cma_obj->base.import_attach;
......@@ -192,6 +202,7 @@ static int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
DMA_FROM_DEVICE);
return ret;
}
EXPORT_SYMBOL(mipi_dbi_buf_copy);
static int mipi_dbi_fb_dirty(struct drm_framebuffer *fb,
struct drm_file *file_priv,
......@@ -444,18 +455,23 @@ EXPORT_SYMBOL(mipi_dbi_display_is_on);
#if IS_ENABLED(CONFIG_SPI)
/*
/**
* mipi_dbi_spi_cmd_max_speed - get the maximum SPI bus speed
* @spi: SPI device
* @len: The transfer buffer length.
*
* Many controllers have a max speed of 10MHz, but can be pushed way beyond
* that. Increase reliability by running pixel data at max speed and the rest
* at 10MHz, preventing transfer glitches from messing up the init settings.
*/
static u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len)
u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len)
{
if (len > 64)
return 0; /* use default */
return min_t(u32, 10000000, spi->max_speed_hz);
}
EXPORT_SYMBOL(mipi_dbi_spi_cmd_max_speed);
/*
* MIPI DBI Type C Option 1
......
......@@ -772,6 +772,7 @@ config FB_VESA
config FB_EFI
bool "EFI-based Framebuffer Support"
depends on (FB = y) && !IA64 && EFI
select DRM_PANEL_ORIENTATION_QUIRKS
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
......
......@@ -15,9 +15,6 @@ ifeq ($(CONFIG_FRAMEBUFFER_CONSOLE_ROTATION),y)
fb-y += fbcon_rotate.o fbcon_cw.o fbcon_ud.o \
fbcon_ccw.o
endif
ifeq ($(CONFIG_DMI),y)
fb-y += fbcon_dmi_quirks.o
endif
endif
fb-objs := $(fb-y)
......
......@@ -964,10 +964,13 @@ static const char *fbcon_startup(void)
ops->cur_blink_jiffies = HZ / 5;
ops->info = info;
info->fbcon_par = ops;
if (initial_rotation != -1)
p->con_rotate = initial_rotation;
else
p->con_rotate = fbcon_platform_get_rotate(info);
p->con_rotate = initial_rotation;
if (p->con_rotate == -1)
p->con_rotate = info->fbcon_rotate_hint;
if (p->con_rotate == -1)
p->con_rotate = FB_ROTATE_UR;
set_blitting_type(vc, info);
if (info->fix.type != FB_TYPE_TEXT) {
......@@ -1104,10 +1107,13 @@ static void fbcon_init(struct vc_data *vc, int init)
ops = info->fbcon_par;
ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
if (initial_rotation != -1)
p->con_rotate = initial_rotation;
else
p->con_rotate = fbcon_platform_get_rotate(info);
p->con_rotate = initial_rotation;
if (p->con_rotate == -1)
p->con_rotate = info->fbcon_rotate_hint;
if (p->con_rotate == -1)
p->con_rotate = FB_ROTATE_UR;
set_blitting_type(vc, info);
cols = vc->vc_cols;
......
......@@ -262,10 +262,4 @@ extern void fbcon_set_rotate(struct fbcon_ops *ops);
#define fbcon_set_rotate(x) do {} while(0)
#endif /* CONFIG_FRAMEBUFFER_CONSOLE_ROTATION */
#ifdef CONFIG_DMI
int fbcon_platform_get_rotate(struct fb_info *info);
#else
#define fbcon_platform_get_rotate(i) FB_ROTATE_UR
#endif /* CONFIG_DMI */
#endif /* _VIDEO_FBCON_H */
......@@ -58,6 +58,7 @@ struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
info->par = p + fb_info_size;
info->device = dev;
info->fbcon_rotate_hint = -1;
#ifdef CONFIG_FB_BACKLIGHT
mutex_init(&info->bl_curve_mutex);
......
......@@ -16,6 +16,8 @@
#include <linux/screen_info.h>
#include <video/vga.h>
#include <asm/efi.h>
#include <drm/drm_utils.h> /* For drm_get_panel_orientation_quirk */
#include <drm/drm_connector.h> /* For DRM_MODE_PANEL_ORIENTATION_* */
static bool request_mem_succeeded = false;
static bool nowc = false;
......@@ -157,7 +159,7 @@ static u64 bar_offset;
static int efifb_probe(struct platform_device *dev)
{
struct fb_info *info;
int err;
int err, orientation;
unsigned int size_vmode;
unsigned int size_remap;
unsigned int size_total;
......@@ -329,6 +331,23 @@ static int efifb_probe(struct platform_device *dev)
info->fix = efifb_fix;
info->flags = FBINFO_FLAG_DEFAULT | FBINFO_MISC_FIRMWARE;
orientation = drm_get_panel_orientation_quirk(efifb_defined.xres,
efifb_defined.yres);
switch (orientation) {
default:
info->fbcon_rotate_hint = FB_ROTATE_UR;
break;
case DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP:
info->fbcon_rotate_hint = FB_ROTATE_UD;
break;
case DRM_MODE_PANEL_ORIENTATION_LEFT_UP:
info->fbcon_rotate_hint = FB_ROTATE_CCW;
break;
case DRM_MODE_PANEL_ORIENTATION_RIGHT_UP:
info->fbcon_rotate_hint = FB_ROTATE_CW;
break;
}
err = sysfs_create_groups(&dev->dev.kobj, efifb_groups);
if (err) {
pr_err("efifb: cannot add sysfs attrs\n");
......
......@@ -175,6 +175,35 @@ enum drm_link_status {
DRM_LINK_STATUS_BAD = DRM_MODE_LINK_STATUS_BAD,
};
/**
* enum drm_panel_orientation - panel_orientation info for &drm_display_info
*
* This enum is used to track the (LCD) panel orientation. There are no
* separate #defines for the uapi!
*
* @DRM_MODE_PANEL_ORIENTATION_UNKNOWN: The drm driver has not provided any
* panel orientation information (normal
* for non panels) in this case the "panel
* orientation" connector prop will not be
* attached.
* @DRM_MODE_PANEL_ORIENTATION_NORMAL: The top side of the panel matches the
* top side of the device's casing.
* @DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP: The top side of the panel matches the
* bottom side of the device's casing, iow
* the panel is mounted upside-down.
* @DRM_MODE_PANEL_ORIENTATION_LEFT_UP: The left side of the panel matches the
* top side of the device's casing.
* @DRM_MODE_PANEL_ORIENTATION_RIGHT_UP: The right side of the panel matches the
* top side of the device's casing.
*/
enum drm_panel_orientation {
DRM_MODE_PANEL_ORIENTATION_UNKNOWN = -1,
DRM_MODE_PANEL_ORIENTATION_NORMAL = 0,
DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP,
DRM_MODE_PANEL_ORIENTATION_LEFT_UP,
DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
};
/**
* struct drm_display_info - runtime data about the connected sink
*
......@@ -222,6 +251,15 @@ struct drm_display_info {
#define DRM_COLOR_FORMAT_YCRCB422 (1<<2)
#define DRM_COLOR_FORMAT_YCRCB420 (1<<3)
/**
* @panel_orientation: Read only connector property for built-in panels,
* indicating the orientation of the panel vs the device's casing.
* drm_connector_init() sets this to DRM_MODE_PANEL_ORIENTATION_UNKNOWN.
* When not UNKNOWN this gets used by the drm_fb_helpers to rotate the
* fb to compensate and gets exported as prop to userspace.
*/
int panel_orientation;
/**
* @color_formats: HDMI Color formats, selects between RGB and YCrCb
* modes. Used DRM_COLOR_FORMAT\_ defines, which are _not_ the same ones
......@@ -1035,6 +1073,8 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector,
const struct edid *edid);
void drm_mode_connector_set_link_status_property(struct drm_connector *connector,
uint64_t link_status);
int drm_connector_init_panel_orientation_property(
struct drm_connector *connector, int width, int height);
/**
* struct drm_tile_group - Tile group metadata
......
......@@ -48,6 +48,7 @@ struct drm_fb_helper_crtc {
struct drm_mode_set mode_set;
struct drm_display_mode *desired_mode;
int x, y;
int rotation;
};
/**
......@@ -158,6 +159,13 @@ struct drm_fb_helper {
struct drm_fb_helper_crtc *crtc_info;
int connector_count;
int connector_info_alloc_count;
/**
* @sw_rotations:
* Bitmask of all rotations requested for panel-orientation which
* could not be handled in hardware. If only one bit is set
* fbdev->fbcon_rotate_hint gets set to the requested rotation.
*/
int sw_rotations;
/**
* @connector_info:
*
......
......@@ -735,6 +735,13 @@ struct drm_mode_config {
*/
struct drm_property *non_desktop_property;
/**
* @panel_orientation_property: Optional connector property indicating
* how the lcd-panel is mounted inside the casing (e.g. normal or
* upside-down).
*/
struct drm_property *panel_orientation_property;
/* dumb ioctl parameters */
uint32_t preferred_depth, prefer_shadow;
......@@ -760,6 +767,15 @@ struct drm_mode_config {
/* cursor size */
uint32_t cursor_width, cursor_height;
/**
* @suspend_state:
*
* Atomic state when suspended.
* Set by drm_mode_config_helper_suspend() and cleared by
* drm_mode_config_helper_resume().
*/
struct drm_atomic_state *suspend_state;
const struct drm_mode_config_helper_funcs *helper_private;
};
......
......@@ -34,4 +34,7 @@ void drm_helper_mode_fill_fb_struct(struct drm_device *dev,
int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
const struct drm_crtc_funcs *funcs);
int drm_mode_config_helper_suspend(struct drm_device *dev);
int drm_mode_config_helper_resume(struct drm_device *dev);
#endif
/* SPDX-License-Identifier: MIT */
/*
* Function prototypes for misc. drm utility functions.
* Specifically this file is for function prototypes for functions which
* may also be used outside of drm code (e.g. in fbdev drivers).
*
* Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
*/
#ifndef __DRM_UTILS_H__
#define __DRM_UTILS_H__
int drm_get_panel_orientation_quirk(int width, int height);
#endif
......@@ -72,10 +72,12 @@ void mipi_dbi_pipe_enable(struct drm_simple_display_pipe *pipe,
void mipi_dbi_pipe_disable(struct drm_simple_display_pipe *pipe);
void mipi_dbi_hw_reset(struct mipi_dbi *mipi);
bool mipi_dbi_display_is_on(struct mipi_dbi *mipi);
u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len);
int mipi_dbi_command_read(struct mipi_dbi *mipi, u8 cmd, u8 *val);
int mipi_dbi_command_buf(struct mipi_dbi *mipi, u8 cmd, u8 *data, size_t len);
int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
struct drm_clip_rect *clip, bool swap);
/**
* mipi_dbi_command - MIPI DCS command with optional parameter(s)
* @mipi: MIPI structure
......
......@@ -20,7 +20,6 @@
* @pipe: Display pipe structure
* @dirty_lock: Serializes framebuffer flushing
* @fbdev_cma: CMA fbdev structure
* @suspend_state: Atomic state when suspended
* @fb_funcs: Framebuffer functions used when creating framebuffers
*/
struct tinydrm_device {
......@@ -28,7 +27,6 @@ struct tinydrm_device {
struct drm_simple_display_pipe pipe;
struct mutex dirty_lock;
struct drm_fbdev_cma *fbdev_cma;
struct drm_atomic_state *suspend_state;
const struct drm_framebuffer_funcs *fb_funcs;
};
......@@ -93,8 +91,6 @@ int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
struct drm_driver *driver);
int devm_tinydrm_register(struct tinydrm_device *tdev);
void tinydrm_shutdown(struct tinydrm_device *tdev);
int tinydrm_suspend(struct tinydrm_device *tdev);
int tinydrm_resume(struct tinydrm_device *tdev);
void tinydrm_display_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *old_state);
......
......@@ -465,6 +465,11 @@ struct fb_info {
atomic_t count;
int node;
int flags;
/*
* -1 by default, set to a FB_ROTATE_* value by the driver, if it knows
* a lcd is not mounted upright and fbcon should rotate to compensate.
*/
int fbcon_rotate_hint;
struct mutex lock; /* Lock for open/release/ioctl funcs */
struct mutex mm_lock; /* Lock for fb_mmap and smem_* fields */
struct fb_var_screeninfo var; /* Current var */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment