Add 'techpack/display/' from tag 'LA.UM.9.14.r1-18300-LAHAINA.0'

git-subtree-dir: techpack/display
git-subtree-mainline: 2d46776923
git-subtree-split: 64f31403b4
Change-Id: I7f4c42a3ba6b11a8db861cdd171a52d8f58f2e06
This commit is contained in:
Michael Bestas 2022-05-19 00:04:46 +03:00
commit 23b65c3a24
No known key found for this signature in database
GPG Key ID: CC95044519BE6669
304 changed files with 212947 additions and 0 deletions

View File

@ -0,0 +1,40 @@
headers_src = [
"include/uapi/*/**/*.h",
]
display_headers_out = [
"display/drm/msm_drm_pp.h",
"display/drm/sde_drm.h",
"display/hdcp/msm_hdmi_hdcp_mgr.h",
"display/media/mmm_color_fmt.h",
"display/media/msm_sde_rotator.h",
]
display_kernel_headers_verbose = "--verbose "
genrule {
name: "qti_generate_display_kernel_headers",
tools: [
"headers_install.sh",
"unifdef"
],
tool_files: [
"display_kernel_headers.py",
],
srcs: headers_src,
cmd: "python3 $(location display_kernel_headers.py) " +
display_kernel_headers_verbose +
"--header_arch arm64 " +
"--gen_dir $(genDir) " +
"--display_include_uapi $(locations include/uapi/*/**/*.h) " +
"--unifdef $(location unifdef) " +
"--headers_install $(location headers_install.sh)",
out: display_headers_out,
}
cc_library_headers {
name: "qti_display_kernel_headers",
generated_headers: ["qti_generate_display_kernel_headers"],
export_generated_headers: ["qti_generate_display_kernel_headers"],
vendor: true,
recovery_available: true
}

71
techpack/display/Makefile Normal file
View File

@ -0,0 +1,71 @@
# SPDX-License-Identifier: GPL-2.0-only
# auto-detect subdirs
ifeq ($(CONFIG_ARCH_KONA), y)
include $(srctree)/techpack/display/config/konadisp.conf
endif
ifeq ($(CONFIG_ARCH_KONA), y)
LINUXINCLUDE += -include $(srctree)/techpack/display/config/konadispconf.h
endif
ifeq ($(CONFIG_ARCH_LAHAINA), y)
ifeq ($(CONFIG_QGKI), y)
include $(srctree)/techpack/display/config/lahainadisp.conf
LINUXINCLUDE += -include $(srctree)/techpack/display/config/lahainadispconf.h
else
include $(srctree)/techpack/display/config/gki_lahainadisp.conf
LINUXINCLUDE += -include $(srctree)/techpack/display/config/gki_lahainadispconf.h
endif
endif
ifeq ($(CONFIG_ARCH_HOLI), y)
ifeq ($(CONFIG_QGKI), y)
include $(srctree)/techpack/display/config/holidisp.conf
LINUXINCLUDE += -include $(srctree)/techpack/display/config/holidispconf.h
else
include $(srctree)/techpack/display/config/gki_holidisp.conf
LINUXINCLUDE += -include $(srctree)/techpack/display/config/gki_holidispconf.h
endif
endif
LINUXINCLUDE += \
-I$(srctree)/techpack/display/include/uapi/display \
-I$(srctree)/techpack/display/include
USERINCLUDE += -I$(srctree)/techpack/display/include/uapi/display
ifeq ($(CONFIG_ARCH_LITO), y)
include $(srctree)/techpack/display/config/saipdisp.conf
endif
ifeq ($(CONFIG_ARCH_LITO), y)
LINUXINCLUDE += -include $(srctree)/techpack/display/config/saipdispconf.h
endif
ifeq ($(CONFIG_ARCH_BENGAL), y)
include $(srctree)/techpack/display/config/bengaldisp.conf
endif
ifeq ($(CONFIG_ARCH_BENGAL), y)
LINUXINCLUDE += -include $(srctree)/techpack/display/config/bengaldispconf.h
endif
ifeq ($(CONFIG_ARCH_MONACO), y)
include $(srctree)/techpack/display/config/monacodisp.conf
endif
ifeq ($(CONFIG_ARCH_MONACO), y)
LINUXINCLUDE += -include $(srctree)/techpack/display/config/monacodispconf.h
endif
obj-$(CONFIG_DRM_MSM) += msm/
ifeq ($(CONFIG_ARCH_SDXLEMUR), y)
include $(srctree)/techpack/display/config/sdxlemurdisp.conf
endif
ifeq ($(CONFIG_ARCH_SDXLEMUR), y)
LINUXINCLUDE += -include $(srctree)/techpack/display/config/sdxlemurdispconf.h
endif
obj-$(CONFIG_DRM_QPIC_DISPLAY) += tinydrm/

47
techpack/display/NOTICE Normal file
View File

@ -0,0 +1,47 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*/
/*
* Copyright (c) 2014-2019, The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat
* Copyright (C) 2014 Red Hat
* Copyright (C) 2016 Red Hat
* Author: Rob Clark <robdclark@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Copyright © 2014 Red Hatt.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/

View File

@ -0,0 +1,16 @@
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (c) 2019, The Linux Foundation. All rights reserved.
export CONFIG_DRM_MSM=y
export CONFIG_DRM_MSM_SDE=y
export CONFIG_SYNC_FILE=y
export CONFIG_DRM_MSM_DSI=y
export CONFIG_DRM_MSM_DP=n
export CONFIG_QCOM_MDSS_DP_PLL=n
export CONFIG_DSI_PARSER=y
export CONFIG_DRM_SDE_WB=n
export CONFIG_DRM_MSM_REGISTER_LOGGING=y
export CONFIG_QCOM_MDSS_PLL=y
export CONFIG_MSM_SDE_ROTATOR=y
export CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y
export CONFIG_DRM_SDE_RSC=n

View File

@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
*/
#define CONFIG_DRM_MSM 1
#define CONFIG_DRM_MSM_SDE 1
#define CONFIG_SYNC_FILE 1
#define CONFIG_DRM_MSM_DSI 1
#define CONFIG_DSI_PARSER 1
#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
#define CONFIG_QCOM_MDSS_PLL 1
#define CONFIG_MSM_SDE_ROTATOR 1
#define CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG 1

View File

@ -0,0 +1,17 @@
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
export CONFIG_DRM_MSM=y
export CONFIG_DRM_MSM_SDE=y
export CONFIG_DRM_MSM_DP=n
export CONFIG_DRM_MSM_DP_MST=n
export CONFIG_SYNC_FILE=y
export CONFIG_DRM_MSM_DSI=y
export CONFIG_DSI_PARSER=y
export CONFIG_DRM_SDE_WB=n
export CONFIG_DRM_MSM_REGISTER_LOGGING=y
export CONFIG_QCOM_MDSS_PLL=y
export CONFIG_DRM_SDE_RSC=n
export CONFIG_DISPLAY_BUILD=m
export CONFIG_MSM_SDE_ROTATOR=y
export CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y

View File

@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
*/
#define CONFIG_DRM_MSM 1
#define CONFIG_DRM_MSM_SDE 1
#define CONFIG_SYNC_FILE 1
#define CONFIG_DRM_MSM_DSI 1
#define CONFIG_DSI_PARSER 1
#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
#define CONFIG_QCOM_MDSS_PLL 1
#define CONFIG_GKI_DISPLAY 1
#define CONFIG_MSM_SDE_ROTATOR 1
#define CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG 1

View File

@ -0,0 +1,12 @@
export CONFIG_DRM_MSM=y
export CONFIG_DRM_MSM_SDE=y
export CONFIG_SYNC_FILE=y
export CONFIG_DRM_MSM_DSI=y
export CONFIG_DRM_MSM_DP=y
export CONFIG_DRM_MSM_DP_MST=y
export CONFIG_DSI_PARSER=y
export CONFIG_QCOM_MDSS_PLL=y
export CONFIG_DRM_SDE_WB=y
export CONFIG_DRM_MSM_REGISTER_LOGGING=y
export CONFIG_DRM_SDE_RSC=y
export CONFIG_DISPLAY_BUILD=m

View File

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*/
#define CONFIG_DRM_MSM 1
#define CONFIG_DRM_MSM_SDE 1
#define CONFIG_SYNC_FILE 1
#define CONFIG_DRM_MSM_DSI 1
#define CONFIG_DRM_MSM_DP 1
#define CONFIG_DRM_MSM_DP_MST 1
#define CONFIG_DSI_PARSER 1
#define CONFIG_DRM_SDE_WB 1
#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
#define CONFIG_QCOM_MDSS_PLL 1
#define CONFIG_GKI_DISPLAY 1
#define CONFIG_DRM_SDE_RSC 1

View File

@ -0,0 +1,17 @@
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (c) 2020, The Linux Foundation. All rights reserved.
export CONFIG_DRM_MSM=y
export CONFIG_DRM_MSM_SDE=y
export CONFIG_DRM_MSM_DP=n
export CONFIG_DRM_MSM_DP_MST=n
export CONFIG_SYNC_FILE=y
export CONFIG_DRM_MSM_DSI=y
export CONFIG_DSI_PARSER=y
export CONFIG_DRM_SDE_WB=n
export CONFIG_DRM_MSM_REGISTER_LOGGING=y
export CONFIG_QCOM_MDSS_PLL=y
export CONFIG_DRM_SDE_RSC=n
export CONFIG_MSM_SDE_ROTATOR=y
export CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y
export CONFIG_DISPLAY_BUILD=y

View File

@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*/
#define CONFIG_DRM_MSM 1
#define CONFIG_DRM_MSM_SDE 1
#define CONFIG_SYNC_FILE 1
#define CONFIG_DRM_MSM_DSI 1
#define CONFIG_DSI_PARSER 1
#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
#define CONFIG_QCOM_MDSS_PLL 1
#define CONFIG_MSM_SDE_ROTATOR 1
#define CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG 1

View File

@ -0,0 +1,13 @@
export CONFIG_DRM_MSM=y
export CONFIG_DRM_MSM_SDE=y
export CONFIG_SYNC_FILE=y
export CONFIG_DRM_MSM_DSI=y
export CONFIG_DRM_MSM_DP=y
export CONFIG_QCOM_MDSS_DP_PLL=y
export CONFIG_DSI_PARSER=y
export CONFIG_DRM_SDE_WB=y
export CONFIG_DRM_MSM_REGISTER_LOGGING=y
export CONFIG_QCOM_MDSS_PLL=y
export CONFIG_MSM_SDE_ROTATOR=y
export CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y
export CONFIG_DRM_SDE_RSC=y

View File

@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
*/
#define CONFIG_DRM_MSM 1
#define CONFIG_DRM_MSM_SDE 1
#define CONFIG_SYNC_FILE 1
#define CONFIG_DRM_MSM_DSI 1
#define CONFIG_DRM_MSM_DP 1
#define CONFIG_QCOM_MDSS_DP_PLL 1
#define CONFIG_DSI_PARSER 1
#define CONFIG_DRM_SDE_WB 1
#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
#define CONFIG_QCOM_MDSS_PLL 1
#define CONFIG_MSM_SDE_ROTATOR 1
#define CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG 1
#define CONFIG_DRM_SDE_RSC 1

View File

@ -0,0 +1,13 @@
export CONFIG_DRM_MSM=y
export CONFIG_DRM_MSM_SDE=y
export CONFIG_DRM_MSM_DP=y
export CONFIG_DRM_MSM_DP_MST=y
export CONFIG_SYNC_FILE=y
export CONFIG_DRM_MSM_DSI=y
export CONFIG_DSI_PARSER=y
export CONFIG_DRM_SDE_WB=y
export CONFIG_DRM_MSM_REGISTER_LOGGING=y
export CONFIG_QCOM_MDSS_PLL=y
export CONFIG_DRM_SDE_RSC=y
export CONFIG_DISPLAY_BUILD=m
export CONFIG_DRM_SDE_VM=y

View File

@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*/
#define CONFIG_DRM_MSM 1
#define CONFIG_DRM_MSM_DP 1
#define CONFIG_DRM_MSM_DP_MST 1
#define CONFIG_DRM_MSM_SDE 1
#define CONFIG_SYNC_FILE 1
#define CONFIG_DRM_MSM_DSI 1
#define CONFIG_DSI_PARSER 1
#define CONFIG_DRM_SDE_WB 1
#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
#define CONFIG_QCOM_MDSS_PLL 1
#define CONFIG_DRM_SDE_RSC 1
#define CONFIG_DRM_SDE_VM 1

View File

@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (c) 2021, The Linux Foundation. All rights reserved.
export CONFIG_DRM_MSM=y
export CONFIG_DRM_MSM_SDE=y
export CONFIG_SYNC_FILE=y
export CONFIG_DRM_MSM_DSI=y
export CONFIG_DSI_PARSER=y
export CONFIG_DRM_SDE_WB=n
export CONFIG_DRM_MSM_REGISTER_LOGGING=y
export CONFIG_QCOM_MDSS_PLL=y
export CONFIG_MSM_SDE_ROTATOR=n
export CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=n
export CONFIG_DRM_SDE_RSC=n
export CONFIG_DISPLAY_BUILD=y

View File

@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#define CONFIG_DRM_MSM 1
#define CONFIG_DRM_MSM_SDE 1
#define CONFIG_SYNC_FILE 1
#define CONFIG_DRM_MSM_DSI 1
#define CONFIG_DSI_PARSER 1
#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
#define CONFIG_QCOM_MDSS_PLL 1

View File

@ -0,0 +1,13 @@
export CONFIG_DRM_MSM=y
export CONFIG_DRM_MSM_SDE=y
export CONFIG_SYNC_FILE=y
export CONFIG_DRM_MSM_DSI=y
export CONFIG_DRM_MSM_DP=y
export CONFIG_QCOM_MDSS_DP_PLL=y
export CONFIG_DSI_PARSER=y
export CONFIG_DRM_SDE_WB=y
export CONFIG_DRM_MSM_REGISTER_LOGGING=y
export CONFIG_QCOM_MDSS_PLL=y
export CONFIG_MSM_SDE_ROTATOR=y
export CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y
export CONFIG_DRM_SDE_RSC=y

View File

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
*/
#define CONFIG_DRM_MSM 1
#define CONFIG_DRM_MSM_SDE 1
#define CONFIG_SYNC_FILE 1
#define CONFIG_DRM_MSM_DSI 1
#define CONFIG_DRM_MSM_DP 1
#define CONFIG_QCOM_MDSS_DP_PLL 1
#define CONFIG_DSI_PARSER 1
#define CONFIG_DRM_SDE_WB 1
#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
#define CONFIG_QCOM_MDSS_PLL 1
#define CONFIG_MSM_SDE_ROTATOR 1
#define CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG 1
#define CONFIG_DRM_SDE_RSC 1

View File

@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (c) 2021, The Linux Foundation. All rights reserved.
export CONFIG_DRM_QPIC_DISPLAY=y

View File

@ -0,0 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#define CONFIG_DRM_QPIC_DISPLAY 1

View File

@ -0,0 +1,92 @@
# Copyright (c) 2020, The Linux Foundation. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 as published by
# the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.
import argparse
import filecmp
import os
import re
import subprocess
import sys
def run_headers_install(verbose, gen_dir, headers_install, unifdef, prefix, h):
if not h.startswith(prefix):
print('error: expected prefix [%s] on header [%s]' % (prefix, h))
return False
out_h = os.path.join(gen_dir, h[len(prefix):])
(out_h_dirname, out_h_basename) = os.path.split(out_h)
env = os.environ.copy()
env["LOC_UNIFDEF"] = unifdef
cmd = ["sh", headers_install, h, out_h]
if verbose:
print('run_headers_install: cmd is %s' % cmd)
result = subprocess.call(cmd, env=env)
if result != 0:
print('error: run_headers_install: cmd %s failed %d' % (cmd, result))
return False
return True
def gen_display_headers(verbose, gen_dir, headers_install, unifdef, display_include_uapi):
error_count = 0
for h in display_include_uapi:
display_uapi_include_prefix = os.path.join(h.split('/include/uapi')[0], 'include', 'uapi') + os.sep
if not run_headers_install(
verbose, gen_dir, headers_install, unifdef,
display_uapi_include_prefix, h): error_count += 1
return error_count
def main():
"""Parse command line arguments and perform top level control."""
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
# Arguments that apply to every invocation of this script.
parser.add_argument(
'--verbose', action='store_true',
help='Print output that describes the workings of this script.')
parser.add_argument(
'--header_arch', required=True,
help='The arch for which to generate headers.')
parser.add_argument(
'--gen_dir', required=True,
help='Where to place the generated files.')
parser.add_argument(
'--display_include_uapi', required=True, nargs='*',
help='The list of techpack/*/include/uapi header files.')
parser.add_argument(
'--headers_install', required=True,
help='The headers_install tool to process input headers.')
parser.add_argument(
'--unifdef',
required=True,
help='The unifdef tool used by headers_install.')
args = parser.parse_args()
if args.verbose:
print('header_arch [%s]' % args.header_arch)
print('gen_dir [%s]' % args.gen_dir)
print('display_include_uapi [%s]' % args.display_include_uapi)
print('headers_install [%s]' % args.headers_install)
print('unifdef [%s]' % args.unifdef)
return gen_display_headers(args.verbose, args.gen_dir,
args.headers_install, args.unifdef, args.display_include_uapi)
if __name__ == '__main__':
sys.exit(main())

View File

@ -0,0 +1,339 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "[msm-hdcp] %s: " fmt, __func__
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/list.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/msm_hdcp.h>
#include <linux/of.h>
#define CLASS_NAME "hdcp"
#define DRIVER_NAME "msm_hdcp"
struct msm_hdcp {
struct platform_device *pdev;
dev_t dev_num;
struct cdev cdev;
struct class *class;
struct device *device;
struct HDCP_V2V1_MSG_TOPOLOGY cached_tp;
u32 tp_msgid;
void *client_ctx;
void (*cb)(void *ctx, u8 data);
};
void msm_hdcp_register_cb(struct device *dev, void *ctx,
void (*cb)(void *ctx, u8 data))
{
struct msm_hdcp *hdcp = NULL;
if (!dev) {
pr_err("invalid device pointer\n");
return;
}
hdcp = dev_get_drvdata(dev);
if (!hdcp) {
pr_err("invalid driver pointer\n");
return;
}
hdcp->cb = cb;
hdcp->client_ctx = ctx;
}
EXPORT_SYMBOL(msm_hdcp_register_cb);
void msm_hdcp_notify_topology(struct device *dev)
{
char *envp[4];
char tp[SZ_16];
char ver[SZ_16];
struct msm_hdcp *hdcp = NULL;
if (!dev) {
pr_err("invalid device pointer\n");
return;
}
hdcp = dev_get_drvdata(dev);
if (!hdcp) {
pr_err("invalid driver pointer\n");
return;
}
snprintf(tp, SZ_16, "%d", DOWN_CHECK_TOPOLOGY);
snprintf(ver, SZ_16, "%d", HDCP_V1_TX);
envp[0] = "HDCP_MGR_EVENT=MSG_READY";
envp[1] = tp;
envp[2] = ver;
envp[3] = NULL;
kobject_uevent_env(&hdcp->device->kobj, KOBJ_CHANGE, envp);
}
EXPORT_SYMBOL(msm_hdcp_notify_topology);
void msm_hdcp_cache_repeater_topology(struct device *dev,
struct HDCP_V2V1_MSG_TOPOLOGY *tp)
{
struct msm_hdcp *hdcp = NULL;
if (!dev || !tp) {
pr_err("invalid input\n");
return;
}
hdcp = dev_get_drvdata(dev);
if (!hdcp) {
pr_err("invalid driver pointer\n");
return;
}
memcpy(&hdcp->cached_tp, tp,
sizeof(struct HDCP_V2V1_MSG_TOPOLOGY));
}
EXPORT_SYMBOL(msm_hdcp_cache_repeater_topology);
static ssize_t tp_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
ssize_t ret = 0;
struct msm_hdcp *hdcp = NULL;
if (!dev) {
pr_err("invalid device pointer\n");
return -ENODEV;
}
hdcp = dev_get_drvdata(dev);
if (!hdcp) {
pr_err("invalid driver pointer\n");
return -ENODEV;
}
switch (hdcp->tp_msgid) {
case DOWN_CHECK_TOPOLOGY:
case DOWN_REQUEST_TOPOLOGY:
buf[MSG_ID_IDX] = hdcp->tp_msgid;
buf[RET_CODE_IDX] = HDCP_AUTHED;
ret = HEADER_LEN;
memcpy(buf + HEADER_LEN, &hdcp->cached_tp,
sizeof(struct HDCP_V2V1_MSG_TOPOLOGY));
ret += sizeof(struct HDCP_V2V1_MSG_TOPOLOGY);
/* clear the flag once data is read back to user space*/
hdcp->tp_msgid = -1;
break;
default:
ret = -EINVAL;
}
return ret;
}
static ssize_t tp_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int msgid = 0;
ssize_t ret = count;
struct msm_hdcp *hdcp = NULL;
if (!dev) {
pr_err("invalid device pointer\n");
return -ENODEV;
}
hdcp = dev_get_drvdata(dev);
if (!hdcp) {
pr_err("invalid driver pointer\n");
return -ENODEV;
}
msgid = buf[0];
switch (msgid) {
case DOWN_CHECK_TOPOLOGY:
case DOWN_REQUEST_TOPOLOGY:
hdcp->tp_msgid = msgid;
break;
default:
ret = -EINVAL;
}
return ret;
}
static ssize_t min_level_change_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int rc;
int min_enc_lvl;
ssize_t ret = count;
struct msm_hdcp *hdcp = NULL;
if (!dev) {
pr_err("invalid device pointer\n");
return -ENODEV;
}
hdcp = dev_get_drvdata(dev);
if (!hdcp) {
pr_err("invalid driver pointer\n");
return -ENODEV;
}
rc = kstrtoint(buf, 10, &min_enc_lvl);
if (rc) {
pr_err("kstrtoint failed. rc=%d\n", rc);
return -EINVAL;
}
if (hdcp->cb && hdcp->client_ctx)
hdcp->cb(hdcp->client_ctx, min_enc_lvl);
return ret;
}
static DEVICE_ATTR_RW(tp);
static DEVICE_ATTR_WO(min_level_change);
static struct attribute *msm_hdcp_fs_attrs[] = {
&dev_attr_tp.attr,
&dev_attr_min_level_change.attr,
NULL
};
static struct attribute_group msm_hdcp_fs_attr_group = {
.attrs = msm_hdcp_fs_attrs
};
static int msm_hdcp_open(struct inode *inode, struct file *file)
{
return 0;
}
static int msm_hdcp_close(struct inode *inode, struct file *file)
{
return 0;
}
static const struct file_operations msm_hdcp_fops = {
.owner = THIS_MODULE,
.open = msm_hdcp_open,
.release = msm_hdcp_close,
};
static const struct of_device_id msm_hdcp_dt_match[] = {
{ .compatible = "qcom,msm-hdcp",},
{}
};
MODULE_DEVICE_TABLE(of, msm_hdcp_dt_match);
static int msm_hdcp_probe(struct platform_device *pdev)
{
int ret;
struct msm_hdcp *hdcp;
hdcp = devm_kzalloc(&pdev->dev, sizeof(struct msm_hdcp), GFP_KERNEL);
if (!hdcp)
return -ENOMEM;
hdcp->pdev = pdev;
platform_set_drvdata(pdev, hdcp);
ret = alloc_chrdev_region(&hdcp->dev_num, 0, 1, DRIVER_NAME);
if (ret < 0) {
pr_err("alloc_chrdev_region failed ret = %d\n", ret);
return ret;
}
hdcp->class = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(hdcp->class)) {
ret = PTR_ERR(hdcp->class);
pr_err("couldn't create class rc = %d\n", ret);
goto error_class_create;
}
hdcp->device = device_create(hdcp->class, NULL,
hdcp->dev_num, hdcp, DRIVER_NAME);
if (IS_ERR(hdcp->device)) {
ret = PTR_ERR(hdcp->device);
pr_err("device_create failed %d\n", ret);
goto error_class_device_create;
}
cdev_init(&hdcp->cdev, &msm_hdcp_fops);
ret = cdev_add(&hdcp->cdev, MKDEV(MAJOR(hdcp->dev_num), 0), 1);
if (ret < 0) {
pr_err("cdev_add failed %d\n", ret);
goto error_cdev_add;
}
ret = sysfs_create_group(&hdcp->device->kobj, &msm_hdcp_fs_attr_group);
if (ret)
pr_err("unable to register msm_hdcp sysfs nodes\n");
return 0;
error_cdev_add:
device_destroy(hdcp->class, hdcp->dev_num);
error_class_device_create:
class_destroy(hdcp->class);
error_class_create:
unregister_chrdev_region(hdcp->dev_num, 1);
return ret;
}
static int msm_hdcp_remove(struct platform_device *pdev)
{
struct msm_hdcp *hdcp;
hdcp = platform_get_drvdata(pdev);
if (!hdcp)
return -ENODEV;
sysfs_remove_group(&hdcp->device->kobj,
&msm_hdcp_fs_attr_group);
cdev_del(&hdcp->cdev);
device_destroy(hdcp->class, hdcp->dev_num);
class_destroy(hdcp->class);
unregister_chrdev_region(hdcp->dev_num, 1);
return 0;
}
static struct platform_driver msm_hdcp_driver = {
.probe = msm_hdcp_probe,
.remove = msm_hdcp_remove,
.driver = {
.name = "msm_hdcp",
.of_match_table = msm_hdcp_dt_match,
.pm = NULL,
}
};
void __init msm_hdcp_register(void)
{
platform_driver_register(&msm_hdcp_driver);
}
void __exit msm_hdcp_unregister(void)
{
platform_driver_unregister(&msm_hdcp_driver);
}

View File

@ -0,0 +1,2 @@
# Top-level Makefile calls into asm-$(ARCH)
# List only non-arch directories below

View File

@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note
header-y += msm_hdcp.h
header-y += sde_io_util.h
header-y += sde_rsc.h

View File

@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
#ifndef __MSM_HDCP_H
#define __MSM_HDCP_H
#include <linux/types.h>
#include "hdcp/msm_hdmi_hdcp_mgr.h"
#if IS_ENABLED(CONFIG_HDCP_QSEECOM)
void msm_hdcp_notify_topology(struct device *dev);
void msm_hdcp_cache_repeater_topology(struct device *dev,
struct HDCP_V2V1_MSG_TOPOLOGY *tp);
void msm_hdcp_register_cb(struct device *dev, void *ctx,
void (*cb)(void *ctx, u8 data));
#else
static inline void msm_hdcp_notify_topology(struct device *dev)
{
}
static inline void msm_hdcp_cache_repeater_topology(struct device *dev,
struct HDCP_V2V1_MSG_TOPOLOGY *tp)
{
}
static inline void msm_hdcp_register_cb(struct device *dev, void *ctx,
void (*cb)(void *ctx, u8 data))
{
}
#endif /* CONFIG_HDCP_QSEECOM*/
#endif /* __MSM_HDCP_H */

View File

@ -0,0 +1,114 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012, 2017-2020, The Linux Foundation. All rights reserved.
*/
#ifndef __SDE_IO_UTIL_H__
#define __SDE_IO_UTIL_H__
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/i2c.h>
#include <linux/types.h>
#ifdef DEBUG
#define DEV_DBG(fmt, args...) pr_err(fmt, ##args)
#else
#define DEV_DBG(fmt, args...) pr_debug(fmt, ##args)
#endif
#define DEV_INFO(fmt, args...) pr_info(fmt, ##args)
#define DEV_WARN(fmt, args...) pr_warn(fmt, ##args)
#define DEV_ERR(fmt, args...) pr_err(fmt, ##args)
struct dss_io_data {
u32 len;
void __iomem *base;
};
void dss_reg_w(struct dss_io_data *io, u32 offset, u32 value, u32 debug);
u32 dss_reg_r(struct dss_io_data *io, u32 offset, u32 debug);
void dss_reg_dump(void __iomem *base, u32 len, const char *prefix, u32 debug);
#define DSS_REG_W_ND(io, offset, val) dss_reg_w(io, offset, val, false)
#define DSS_REG_W(io, offset, val) dss_reg_w(io, offset, val, true)
#define DSS_REG_R_ND(io, offset) dss_reg_r(io, offset, false)
#define DSS_REG_R(io, offset) dss_reg_r(io, offset, true)
enum dss_vreg_type {
DSS_REG_LDO,
DSS_REG_VS,
};
struct dss_vreg {
struct regulator *vreg; /* vreg handle */
char vreg_name[32];
int min_voltage;
int max_voltage;
int enable_load;
int disable_load;
int pre_on_sleep;
int post_on_sleep;
int pre_off_sleep;
int post_off_sleep;
};
struct dss_gpio {
unsigned int gpio;
unsigned int value;
char gpio_name[32];
};
enum dss_clk_type {
DSS_CLK_AHB, /* no set rate. rate controlled through rpm */
DSS_CLK_PCLK,
DSS_CLK_OTHER,
};
struct dss_clk {
struct clk *clk; /* clk handle */
char clk_name[32];
enum dss_clk_type type;
unsigned long rate;
unsigned long max_rate;
};
struct dss_module_power {
unsigned int num_vreg;
struct dss_vreg *vreg_config;
unsigned int num_gpio;
struct dss_gpio *gpio_config;
unsigned int num_clk;
struct dss_clk *clk_config;
};
int msm_dss_ioremap_byname(struct platform_device *pdev,
struct dss_io_data *io_data, const char *name);
void msm_dss_iounmap(struct dss_io_data *io_data);
int msm_dss_get_io_mem(struct platform_device *pdev,
struct list_head *mem_list);
void msm_dss_clean_io_mem(struct list_head *mem_list);
int msm_dss_get_pmic_io_mem(struct platform_device *pdev,
struct list_head *mem_list);
int msm_dss_get_io_irq(struct platform_device *pdev,
struct list_head *irq_list, u32 label);
void msm_dss_clean_io_irq(struct list_head *irq_list);
int msm_dss_enable_gpio(struct dss_gpio *in_gpio, int num_gpio, int enable);
int msm_dss_gpio_enable(struct dss_gpio *in_gpio, int num_gpio, int enable);
int msm_dss_get_vreg(struct device *dev, struct dss_vreg *in_vreg,
int num_vreg, int enable);
int msm_dss_enable_vreg(struct dss_vreg *in_vreg, int num_vreg, int enable);
int msm_dss_get_clk(struct device *dev, struct dss_clk *clk_arry, int num_clk);
void msm_dss_put_clk(struct dss_clk *clk_arry, int num_clk);
int msm_dss_clk_set_rate(struct dss_clk *clk_arry, int num_clk);
int msm_dss_single_clk_set_rate(struct dss_clk *clk);
int msm_dss_enable_clk(struct dss_clk *clk_arry, int num_clk, int enable);
int sde_i2c_byte_read(struct i2c_client *client, uint8_t slave_addr,
uint8_t reg_offset, uint8_t *read_buf);
int sde_i2c_byte_write(struct i2c_client *client, uint8_t slave_addr,
uint8_t reg_offset, uint8_t *value);
#endif /* __SDE_IO_UTIL_H__ */

View File

@ -0,0 +1,360 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _SDE_RSC_H_
#define _SDE_RSC_H_
#include <linux/kernel.h>
/* primary display rsc index */
#define SDE_RSC_INDEX 0
#define MAX_RSC_CLIENT_NAME_LEN 128
#define NUM_RSC_PROFILING_COUNTERS 3
/* DRM Object IDs are numbered excluding 0, use 0 to indicate invalid CRTC */
#define SDE_RSC_INVALID_CRTC_ID 0
/**
* event will be triggered before sde core power collapse,
* mdss gdsc is still on
*/
#define SDE_RSC_EVENT_PRE_CORE_PC 0x1
/**
* event will be triggered after sde core collapse complete,
* mdss gdsc is off now
*/
#define SDE_RSC_EVENT_POST_CORE_PC 0x2
/**
* event will be triggered before restoring the sde core from power collapse,
* mdss gdsc is still off
*/
#define SDE_RSC_EVENT_PRE_CORE_RESTORE 0x4
/**
* event will be triggered after restoring the sde core from power collapse,
* mdss gdsc is on now
*/
#define SDE_RSC_EVENT_POST_CORE_RESTORE 0x8
/**
* event attached with solver state enabled
* all clients in clk_state or cmd_state
*/
#define SDE_RSC_EVENT_SOLVER_ENABLED 0x10
/**
* event attached with solver state disabled
* one of the client requested for vid state
*/
#define SDE_RSC_EVENT_SOLVER_DISABLED 0x20
/**
* sde_rsc_client_type: sde rsc client type information
* SDE_RSC_PRIMARY_DISP_CLIENT: A primary display client which can request
* vid or cmd state switch.
* SDE_RSC_EXTERNAL_DISPLAY_CLIENT:An external display client which can
* request only clk state switch.
* SDE_RSC_CLK_CLIENT: A clk client request for only rsc clocks
* enabled and mode_2 exit state.
*/
enum sde_rsc_client_type {
SDE_RSC_PRIMARY_DISP_CLIENT,
SDE_RSC_EXTERNAL_DISP_CLIENT,
SDE_RSC_CLK_CLIENT,
SDE_RSC_INVALID_CLIENT,
};
/**
* sde_rsc_state: sde rsc state information
* SDE_RSC_IDLE_STATE: A client requests for idle state when there is no
* pixel or cmd transfer expected. An idle vote from
* all clients lead to power collapse state.
* SDE_RSC_CLK_STATE: A client requests for clk state when it wants to
* only avoid mode-2 entry/exit. For ex: V4L2 driver,
* sde power handle, etc.
* SDE_RSC_CMD_STATE: A client requests for cmd state when it wants to
* enable the solver mode.
* SDE_RSC_VID_STATE: A client requests for vid state it wants to avoid
* solver enable because client is fetching data from
* continuously.
*/
enum sde_rsc_state {
SDE_RSC_IDLE_STATE,
SDE_RSC_CLK_STATE,
SDE_RSC_CMD_STATE,
SDE_RSC_VID_STATE,
};
/**
* struct sde_rsc_client: stores the rsc client for sde driver
* @name: name of the client
* @current_state: current client state
* @crtc_id: crtc_id associated with this rsc client.
* @rsc_index: rsc index of a client - only index "0" valid.
* @id: Index of client. It will be assigned during client_create call
* @client_type: check sde_rsc_client_type information
* @list: list to attach client master list
*/
struct sde_rsc_client {
char name[MAX_RSC_CLIENT_NAME_LEN];
short current_state;
int crtc_id;
u32 rsc_index;
u32 id;
enum sde_rsc_client_type client_type;
struct list_head list;
};
/**
* struct sde_rsc_event: local event registration entry structure
* @cb_func: Pointer to desired callback function
* @usr: User pointer to pass to callback on event trigger
* @rsc_index: rsc index of a client - only index "0" valid.
* @event_type: refer comments in event_register
* @list: list to attach event master list
*/
struct sde_rsc_event {
void (*cb_func)(uint32_t event_type, void *usr);
void *usr;
u32 rsc_index;
uint32_t event_type;
struct list_head list;
};
/**
* struct sde_rsc_cmd_config: provides panel configuration to rsc
* when client is command mode. It is not required to set it during
* video mode.
*
* @fps: panel te interval
* @vtotal: current vertical total (height + vbp + vfp)
* @jitter_numer: panel jitter numerator value. This config causes rsc/solver
* early before te. Default is 0.8% jitter.
* @jitter_denom: panel jitter denominator.
* @prefill_lines: max prefill lines based on panel
*/
struct sde_rsc_cmd_config {
u32 fps;
u32 vtotal;
u32 jitter_numer;
u32 jitter_denom;
u32 prefill_lines;
};
#if IS_ENABLED(CONFIG_DRM_SDE_RSC)
/**
* sde_rsc_client_create() - create the client for sde rsc.
* Different displays like DSI, HDMI, DP, WB, etc should call this
* api to register their vote for rpmh. They still need to vote for
* power handle to get the clocks.
* @rsc_index: A client will be created on this RSC. As of now only
* SDE_RSC_INDEX is valid rsc index.
* @name: Caller needs to provide some valid string to identify
* the client. "primary", "dp", "hdmi" are suggested name.
* @client_type: check client_type enum for information
* @vsync_source: This parameter is only valid for primary display. It provides
* vsync source information
*
* Return: client node pointer.
*/
struct sde_rsc_client *sde_rsc_client_create(u32 rsc_index, char *name,
enum sde_rsc_client_type client_type, u32 vsync_source);
/**
* sde_rsc_client_destroy() - Destroy the sde rsc client.
*
* @client: Client pointer provided by sde_rsc_client_create().
*
* Return: none
*/
void sde_rsc_client_destroy(struct sde_rsc_client *client);
/**
* sde_rsc_client_state_update() - rsc client state update
* Video mode, cmd mode and clk state are supported as modes. A client need to
* set this property during panel time. A switching client can set the
* property to change the state
*
* @client: Client pointer provided by sde_rsc_client_create().
* @state: Client state - video/cmd
* @config: fps, vtotal, porches, etc configuration for command mode
* panel
* @crtc_id: current client's crtc id
* @wait_vblank_crtc_id: Output parameter. If set to non-zero, rsc hw
* state update requires a wait for one vblank on
* the primary crtc. In that case, this output
* param will be set to the crtc on which to wait.
* If SDE_RSC_INVALID_CRTC_ID, no wait necessary
*
* Return: error code.
*/
int sde_rsc_client_state_update(struct sde_rsc_client *client,
enum sde_rsc_state state,
struct sde_rsc_cmd_config *config, int crtc_id,
int *wait_vblank_crtc_id);
/**
* sde_rsc_client_get_vsync_refcount() - returns the status of the vsync
* refcount, to signal if the client needs to reset the refcounting logic
* @client: Client pointer provided by sde_rsc_client_create().
*
* Return: true if the state update has completed.
*/
int sde_rsc_client_get_vsync_refcount(
struct sde_rsc_client *caller_client);
/**
* sde_rsc_client_reset_vsync_refcount() - reduces the refcounting
* logic that waits for the vsync.
* @client: Client pointer provided by sde_rsc_client_create().
*
* Return: true if the state update has completed.
*/
int sde_rsc_client_reset_vsync_refcount(
struct sde_rsc_client *caller_client);
/**
* sde_rsc_client_is_state_update_complete() - check if state update is complete
* RSC state transition is not complete until HW receives VBLANK signal. This
* function checks RSC HW to determine whether that signal has been received.
* @client: Client pointer provided by sde_rsc_client_create().
*
* Return: true if the state update has completed.
*/
bool sde_rsc_client_is_state_update_complete(
struct sde_rsc_client *caller_client);
/**
* sde_rsc_client_vote() - stores ab/ib vote for rsc client
*
* @client: Client pointer provided by sde_rsc_client_create().
* @bus_id: data bus identifier
* @ab: aggregated bandwidth vote from client.
* @ib: instant bandwidth vote from client.
*
* Return: error code.
*/
int sde_rsc_client_vote(struct sde_rsc_client *caller_client,
u32 bus_id, u64 ab_vote, u64 ib_vote);
/**
* sde_rsc_register_event - register a callback function for an event
* @rsc_index: A client will be created on this RSC. As of now only
* SDE_RSC_INDEX is valid rsc index.
* @event_type: event type to register; client sets 0x3 if it wants
* to register for CORE_PC and CORE_RESTORE - both events.
* @cb_func: Pointer to desired callback function
* @usr: User pointer to pass to callback on event trigger
* Returns: sde_rsc_event pointer on success
*/
struct sde_rsc_event *sde_rsc_register_event(int rsc_index, uint32_t event_type,
void (*cb_func)(uint32_t event_type, void *usr), void *usr);
/**
* sde_rsc_unregister_event - unregister callback for an event
* @sde_rsc_event: event returned by sde_rsc_register_event
*/
void sde_rsc_unregister_event(struct sde_rsc_event *event);
/**
* is_sde_rsc_available - check if display rsc available.
* @rsc_index: A client will be created on this RSC. As of now only
* SDE_RSC_INDEX is valid rsc index.
* Returns: true if rsc is available; false in all other cases
*/
bool is_sde_rsc_available(int rsc_index);
/**
* get_sde_rsc_current_state - gets the current state of sde rsc.
* @rsc_index: A client will be created on this RSC. As of now only
* SDE_RSC_INDEX is valid rsc index.
* Returns: current state if rsc available; SDE_RSC_IDLE_STATE for
* all other cases
*/
enum sde_rsc_state get_sde_rsc_current_state(int rsc_index);
/**
* sde_rsc_client_trigger_vote() - triggers ab/ib vote for rsc client
*
* @client: Client pointer provided by sde_rsc_client_create().
* @delta_vote: if bw vote is increased or decreased
*
* Return: error code.
*/
int sde_rsc_client_trigger_vote(struct sde_rsc_client *caller_client,
bool delta_vote);
#else
static inline struct sde_rsc_client *sde_rsc_client_create(u32 rsc_index,
char *name, enum sde_rsc_client_type client_type, u32 vsync_source)
{
return NULL;
}
static inline void sde_rsc_client_destroy(struct sde_rsc_client *client)
{
}
static inline int sde_rsc_client_state_update(struct sde_rsc_client *client,
enum sde_rsc_state state,
struct sde_rsc_cmd_config *config, int crtc_id,
int *wait_vblank_crtc_id)
{
return 0;
}
static inline int sde_rsc_client_get_vsync_refcount(
struct sde_rsc_client *caller_client)
{
return 0;
}
static inline int sde_rsc_client_reset_vsync_refcount(
struct sde_rsc_client *caller_client)
{
return 0;
}
static inline bool sde_rsc_client_is_state_update_complete(
struct sde_rsc_client *caller_client)
{
return false;
}
static inline int sde_rsc_client_vote(struct sde_rsc_client *caller_client,
u32 bus_id, u64 ab_vote, u64 ib_vote)
{
return 0;
}
static inline struct sde_rsc_event *sde_rsc_register_event(int rsc_index,
uint32_t event_type,
void (*cb_func)(uint32_t event_type, void *usr), void *usr)
{
return NULL;
}
static inline void sde_rsc_unregister_event(struct sde_rsc_event *event)
{
}
static inline bool is_sde_rsc_available(int rsc_index)
{
return false;
}
static inline enum sde_rsc_state get_sde_rsc_current_state(int rsc_index)
{
return SDE_RSC_IDLE_STATE;
}
static inline int sde_rsc_client_trigger_vote(
struct sde_rsc_client *caller_client, bool delta_vote)
{
return 0;
}
#endif /* CONFIG_DRM_SDE_RSC */
#endif /* _SDE_RSC_H_ */

View File

@ -0,0 +1,97 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*/
#ifndef __SDE_VM_EVENT_H__
#define __SDE_VM_EVENT_H__
#include <linux/list.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <drm/drm_device.h>
/**
* struct - msm_io_irq_entry - define irq item
* @label: hh_irq_label for the irq
* @irq_num: linux mapped irq num
* @list: list head pointer
*/
struct msm_io_irq_entry {
u32 label;
u32 irq_num;
struct list_head list;
};
/**
* struct - msm_io_mem_entry - define io memory item
* @base: reg base
* @size: size of the reg range
* @list: list head pointer
*/
struct msm_io_mem_entry {
phys_addr_t base;
phys_addr_t size;
struct list_head list;
};
/**
* struct - msm_io_res - represents the hw resources for vm sharing
* @irq: list of IRQ's of all the dislay sub-devices
* @mem: list of IO memory ranges of all the display sub-devices
*/
struct msm_io_res {
struct list_head irq;
struct list_head mem;
};
/**
* struct msm_vm_ops - hooks for communication with vm clients
* @vm_pre_hw_release: invoked before releasing the HW
* @vm_post_hw_acquire: invoked before pushing the first commit
* @vm_check: invoked to check the readiness of the vm_clients
* before releasing the HW
* @vm_get_io_resources: invoked to collect HW resources
*/
struct msm_vm_ops {
int (*vm_pre_hw_release)(void *priv_data);
int (*vm_post_hw_acquire)(void *priv_data);
int (*vm_check)(void *priv_data);
int (*vm_get_io_resources)(struct msm_io_res *io_res, void *priv_data);
};
/**
* msm_vm_client_entry - defines the vm client info
* @ops: client vm_ops
* @dev: clients device id. Used in unregister
* @data: client custom data
* @list: linked list entry
*/
struct msm_vm_client_entry {
struct msm_vm_ops ops;
struct device *dev;
void *data;
struct list_head list;
};
/**
* msm_register_vm_event - api for display dependent drivers(clients) to
* register for vm events
* @dev: msm device
* @client_dev: client device
* @ops: vm event hooks
* @priv_data: client custom data
*/
int msm_register_vm_event(struct device *dev, struct device *client_dev,
struct msm_vm_ops *ops, void *priv_data);
/**
* msm_unregister_vm_event - api for display dependent drivers(clients) to
* unregister from vm events
* @dev: msm device
* @client_dev: client device
*/
void msm_unregister_vm_event(struct device *dev, struct device *client_dev);
#endif //__SDE_VM_EVENT_H__

View File

@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note
# Top-level Makefile calls into asm-$(ARCH)
# List only non-arch directories below

View File

@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note
header-y += media/
header-y += drm/
header-y += hdcp/

View File

@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note
header-y += msm_drm_pp.h
header-y += sde_drm.h

View File

@ -0,0 +1,686 @@
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _MSM_DRM_PP_H_
#define _MSM_DRM_PP_H_
#include <linux/types.h>
/**
* struct drm_msm_pcc_coeff - PCC coefficient structure for each color
* component.
* @c: constant coefficient.
* @r: red coefficient.
* @g: green coefficient.
* @b: blue coefficient.
* @rg: red green coefficient.
* @gb: green blue coefficient.
* @rb: red blue coefficient.
* @rgb: red blue green coefficient.
*/
struct drm_msm_pcc_coeff {
__u32 c;
__u32 r;
__u32 g;
__u32 b;
__u32 rg;
__u32 gb;
__u32 rb;
__u32 rgb;
};
#define PCC_BEFORE (1 << 0)
/**
* struct drm_msm_pcc - pcc feature structure
* @flags: for customizing operations. Values can be
* - PCC_BEFORE: Operate PCC using a 'before' arrangement
* @r: red coefficients.
* @g: green coefficients.
* @b: blue coefficients.
* @r_rr: second order coefficients
* @r_gg: second order coefficients
* @r_bb: second order coefficients
* @g_rr: second order coefficients
* @g_gg: second order coefficients
* @g_bb: second order coefficients
* @b_rr: second order coefficients
* @b_gg: second order coefficients
* @b_bb: second order coefficients
*/
#define DRM_MSM_PCC3
struct drm_msm_pcc {
__u64 flags;
struct drm_msm_pcc_coeff r;
struct drm_msm_pcc_coeff g;
struct drm_msm_pcc_coeff b;
__u32 r_rr;
__u32 r_gg;
__u32 r_bb;
__u32 g_rr;
__u32 g_gg;
__u32 g_bb;
__u32 b_rr;
__u32 b_gg;
__u32 b_bb;
};
/* struct drm_msm_pa_vlut - picture adjustment vLUT structure
* flags: for customizing vlut operation
* val: vLUT values
*/
#define PA_VLUT_SIZE 256
struct drm_msm_pa_vlut {
__u64 flags;
__u32 val[PA_VLUT_SIZE];
};
#define PA_HSIC_HUE_ENABLE (1 << 0)
#define PA_HSIC_SAT_ENABLE (1 << 1)
#define PA_HSIC_VAL_ENABLE (1 << 2)
#define PA_HSIC_CONT_ENABLE (1 << 3)
/**
* struct drm_msm_pa_hsic - pa hsic feature structure
* @flags: flags for the feature customization, values can be:
* - PA_HSIC_HUE_ENABLE: Enable hue adjustment
* - PA_HSIC_SAT_ENABLE: Enable saturation adjustment
* - PA_HSIC_VAL_ENABLE: Enable value adjustment
* - PA_HSIC_CONT_ENABLE: Enable contrast adjustment
*
* @hue: hue setting
* @saturation: saturation setting
* @value: value setting
* @contrast: contrast setting
*/
#define DRM_MSM_PA_HSIC
struct drm_msm_pa_hsic {
__u64 flags;
__u32 hue;
__u32 saturation;
__u32 value;
__u32 contrast;
};
#define MEMCOL_PROT_HUE (1 << 0)
#define MEMCOL_PROT_SAT (1 << 1)
#define MEMCOL_PROT_VAL (1 << 2)
#define MEMCOL_PROT_CONT (1 << 3)
#define MEMCOL_PROT_SIXZONE (1 << 4)
#define MEMCOL_PROT_BLEND (1 << 5)
/* struct drm_msm_memcol - Memory color feature structure.
* Skin, sky, foliage features are supported.
* @prot_flags: Bit mask for enabling protection feature.
* @color_adjust_p0: Adjustment curve.
* @color_adjust_p1: Adjustment curve.
* @color_adjust_p2: Adjustment curve.
* @blend_gain: Blend gain weightage from othe PA features.
* @sat_hold: Saturation hold value.
* @val_hold: Value hold info.
* @hue_region: Hue qualifier.
* @sat_region: Saturation qualifier.
* @val_region: Value qualifier.
*/
#define DRM_MSM_MEMCOL
struct drm_msm_memcol {
__u64 prot_flags;
__u32 color_adjust_p0;
__u32 color_adjust_p1;
__u32 color_adjust_p2;
__u32 blend_gain;
__u32 sat_hold;
__u32 val_hold;
__u32 hue_region;
__u32 sat_region;
__u32 val_region;
};
#define DRM_MSM_SIXZONE
#define SIXZONE_LUT_SIZE 384
#define SIXZONE_HUE_ENABLE (1 << 0)
#define SIXZONE_SAT_ENABLE (1 << 1)
#define SIXZONE_VAL_ENABLE (1 << 2)
/* struct drm_msm_sixzone_curve - Sixzone HSV adjustment curve structure.
* @p0: Hue adjustment.
* @p1: Saturation/Value adjustment.
*/
struct drm_msm_sixzone_curve {
__u32 p1;
__u32 p0;
};
/* struct drm_msm_sixzone - Sixzone feature structure.
* @flags: for feature customization, values can be:
* - SIXZONE_HUE_ENABLE: Enable hue adjustment
* - SIXZONE_SAT_ENABLE: Enable saturation adjustment
* - SIXZONE_VAL_ENABLE: Enable value adjustment
* @threshold: threshold qualifier.
* @adjust_p0: Adjustment curve.
* @adjust_p1: Adjustment curve.
* @sat_hold: Saturation hold info.
* @val_hold: Value hold info.
* @curve: HSV adjustment curve lut.
*/
struct drm_msm_sixzone {
__u64 flags;
__u32 threshold;
__u32 adjust_p0;
__u32 adjust_p1;
__u32 sat_hold;
__u32 val_hold;
struct drm_msm_sixzone_curve curve[SIXZONE_LUT_SIZE];
};
#define GAMUT_3D_MODE_17 1
#define GAMUT_3D_MODE_5 2
#define GAMUT_3D_MODE_13 3
#define GAMUT_3D_MODE17_TBL_SZ 1229
#define GAMUT_3D_MODE5_TBL_SZ 32
#define GAMUT_3D_MODE13_TBL_SZ 550
#define GAMUT_3D_SCALE_OFF_SZ 16
#define GAMUT_3D_SCALEB_OFF_SZ 12
#define GAMUT_3D_TBL_NUM 4
#define GAMUT_3D_SCALE_OFF_TBL_NUM 3
#define GAMUT_3D_MAP_EN (1 << 0)
/**
* struct drm_msm_3d_col - 3d gamut color component structure
* @c0: Holds c0 value
* @c2_c1: Holds c2/c1 values
*/
struct drm_msm_3d_col {
__u32 c2_c1;
__u32 c0;
};
/**
* struct drm_msm_3d_gamut - 3d gamut feature structure
* @flags: flags for the feature values are:
* 0 - no map
* GAMUT_3D_MAP_EN - enable map
* @mode: lut mode can take following values:
* - GAMUT_3D_MODE_17
* - GAMUT_3D_MODE_5
* - GAMUT_3D_MODE_13
* @scale_off: Scale offset table
* @col: Color component tables
*/
struct drm_msm_3d_gamut {
__u64 flags;
__u32 mode;
__u32 scale_off[GAMUT_3D_SCALE_OFF_TBL_NUM][GAMUT_3D_SCALE_OFF_SZ];
struct drm_msm_3d_col col[GAMUT_3D_TBL_NUM][GAMUT_3D_MODE17_TBL_SZ];
};
#define PGC_TBL_LEN 512
#define PGC_8B_ROUND (1 << 0)
/**
* struct drm_msm_pgc_lut - pgc lut feature structure
* @flags: flags for the featue values can be:
* - PGC_8B_ROUND
* @c0: color0 component lut
* @c1: color1 component lut
* @c2: color2 component lut
*/
struct drm_msm_pgc_lut {
__u64 flags;
__u32 c0[PGC_TBL_LEN];
__u32 c1[PGC_TBL_LEN];
__u32 c2[PGC_TBL_LEN];
};
#define IGC_TBL_LEN 256
#define IGC_DITHER_ENABLE (1 << 0)
/**
* struct drm_msm_igc_lut - igc lut feature structure
* @flags: flags for the feature customization, values can be:
* - IGC_DITHER_ENABLE: Enable dither functionality
* @c0: color0 component lut
* @c1: color1 component lut
* @c2: color2 component lut
* @strength: dither strength, considered valid when IGC_DITHER_ENABLE
* is set in flags. Strength value based on source bit width.
* @c0_last: color0 lut_last component
* @c1_last: color1 lut_last component
* @c2_last: color2 lut_last component
*/
struct drm_msm_igc_lut {
__u64 flags;
__u32 c0[IGC_TBL_LEN];
__u32 c1[IGC_TBL_LEN];
__u32 c2[IGC_TBL_LEN];
__u32 strength;
__u32 c0_last;
__u32 c1_last;
__u32 c2_last;
};
#define LAST_LUT 2
#define HIST_V_SIZE 256
/**
* struct drm_msm_hist - histogram feature structure
* @flags: for customizing operations
* @data: histogram data
*/
struct drm_msm_hist {
__u64 flags;
__u32 data[HIST_V_SIZE];
};
#define AD4_LUT_GRP0_SIZE 33
#define AD4_LUT_GRP1_SIZE 32
/*
* struct drm_msm_ad4_init - ad4 init structure set by user-space client.
* Init param values can change based on tuning
* hence it is passed by user-space clients.
*/
struct drm_msm_ad4_init {
__u32 init_param_001[AD4_LUT_GRP0_SIZE];
__u32 init_param_002[AD4_LUT_GRP0_SIZE];
__u32 init_param_003[AD4_LUT_GRP0_SIZE];
__u32 init_param_004[AD4_LUT_GRP0_SIZE];
__u32 init_param_005[AD4_LUT_GRP1_SIZE];
__u32 init_param_006[AD4_LUT_GRP1_SIZE];
__u32 init_param_007[AD4_LUT_GRP0_SIZE];
__u32 init_param_008[AD4_LUT_GRP0_SIZE];
__u32 init_param_009;
__u32 init_param_010;
__u32 init_param_011;
__u32 init_param_012;
__u32 init_param_013;
__u32 init_param_014;
__u32 init_param_015;
__u32 init_param_016;
__u32 init_param_017;
__u32 init_param_018;
__u32 init_param_019;
__u32 init_param_020;
__u32 init_param_021;
__u32 init_param_022;
__u32 init_param_023;
__u32 init_param_024;
__u32 init_param_025;
__u32 init_param_026;
__u32 init_param_027;
__u32 init_param_028;
__u32 init_param_029;
__u32 init_param_030;
__u32 init_param_031;
__u32 init_param_032;
__u32 init_param_033;
__u32 init_param_034;
__u32 init_param_035;
__u32 init_param_036;
__u32 init_param_037;
__u32 init_param_038;
__u32 init_param_039;
__u32 init_param_040;
__u32 init_param_041;
__u32 init_param_042;
__u32 init_param_043;
__u32 init_param_044;
__u32 init_param_045;
__u32 init_param_046;
__u32 init_param_047;
__u32 init_param_048;
__u32 init_param_049;
__u32 init_param_050;
__u32 init_param_051;
__u32 init_param_052;
__u32 init_param_053;
__u32 init_param_054;
__u32 init_param_055;
__u32 init_param_056;
__u32 init_param_057;
__u32 init_param_058;
__u32 init_param_059;
__u32 init_param_060;
__u32 init_param_061;
__u32 init_param_062;
__u32 init_param_063;
__u32 init_param_064;
__u32 init_param_065;
__u32 init_param_066;
__u32 init_param_067;
__u32 init_param_068;
__u32 init_param_069;
__u32 init_param_070;
__u32 init_param_071;
__u32 init_param_072;
__u32 init_param_073;
__u32 init_param_074;
__u32 init_param_075;
};
/*
* struct drm_msm_ad4_cfg - ad4 config structure set by user-space client.
* Config param values can vary based on tuning,
* hence it is passed by user-space clients.
*/
struct drm_msm_ad4_cfg {
__u32 cfg_param_001;
__u32 cfg_param_002;
__u32 cfg_param_003;
__u32 cfg_param_004;
__u32 cfg_param_005;
__u32 cfg_param_006;
__u32 cfg_param_007;
__u32 cfg_param_008;
__u32 cfg_param_009;
__u32 cfg_param_010;
__u32 cfg_param_011;
__u32 cfg_param_012;
__u32 cfg_param_013;
__u32 cfg_param_014;
__u32 cfg_param_015;
__u32 cfg_param_016;
__u32 cfg_param_017;
__u32 cfg_param_018;
__u32 cfg_param_019;
__u32 cfg_param_020;
__u32 cfg_param_021;
__u32 cfg_param_022;
__u32 cfg_param_023;
__u32 cfg_param_024;
__u32 cfg_param_025;
__u32 cfg_param_026;
__u32 cfg_param_027;
__u32 cfg_param_028;
__u32 cfg_param_029;
__u32 cfg_param_030;
__u32 cfg_param_031;
__u32 cfg_param_032;
__u32 cfg_param_033;
__u32 cfg_param_034;
__u32 cfg_param_035;
__u32 cfg_param_036;
__u32 cfg_param_037;
__u32 cfg_param_038;
__u32 cfg_param_039;
__u32 cfg_param_040;
__u32 cfg_param_041;
__u32 cfg_param_042;
__u32 cfg_param_043;
__u32 cfg_param_044;
__u32 cfg_param_045;
__u32 cfg_param_046;
__u32 cfg_param_047;
__u32 cfg_param_048;
__u32 cfg_param_049;
__u32 cfg_param_050;
__u32 cfg_param_051;
__u32 cfg_param_052;
__u32 cfg_param_053;
};
#define DITHER_MATRIX_SZ 16
#define DITHER_LUMA_MODE (1 << 0)
/**
* struct drm_msm_dither - dither feature structure
* @flags: flags for the feature customization, values can be:
-DITHER_LUMA_MODE: Enable LUMA dither mode
* @temporal_en: temperal dither enable
* @c0_bitdepth: c0 component bit depth
* @c1_bitdepth: c1 component bit depth
* @c2_bitdepth: c2 component bit depth
* @c3_bitdepth: c2 component bit depth
* @matrix: dither strength matrix
*/
struct drm_msm_dither {
__u64 flags;
__u32 temporal_en;
__u32 c0_bitdepth;
__u32 c1_bitdepth;
__u32 c2_bitdepth;
__u32 c3_bitdepth;
__u32 matrix[DITHER_MATRIX_SZ];
};
/**
* struct drm_msm_pa_dither - dspp dither feature structure
* @flags: for customizing operations
* @strength: dither strength
* @offset_en: offset enable bit
* @matrix: dither data matrix
*/
#define DRM_MSM_PA_DITHER
struct drm_msm_pa_dither {
__u64 flags;
__u32 strength;
__u32 offset_en;
__u32 matrix[DITHER_MATRIX_SZ];
};
/**
* struct drm_msm_ad4_roi_cfg - ad4 roi params config set
* by user-space client.
* @h_x - hotizontal direction start
* @h_y - hotizontal direction end
* @v_x - vertical direction start
* @v_y - vertical direction end
* @factor_in - the alpha value for inside roi region
* @factor_out - the alpha value for outside roi region
*/
#define DRM_MSM_AD4_ROI
struct drm_msm_ad4_roi_cfg {
__u32 h_x;
__u32 h_y;
__u32 v_x;
__u32 v_y;
__u32 factor_in;
__u32 factor_out;
};
#define LTM_FEATURE_DEF 1
#define LTM_DATA_SIZE_0 32
#define LTM_DATA_SIZE_1 128
#define LTM_DATA_SIZE_2 256
#define LTM_DATA_SIZE_3 33
#define LTM_BUFFER_SIZE 5
#define LTM_GUARD_BYTES 255
#define LTM_BLOCK_SIZE 2
#define LTM_STATS_SAT (1 << 1)
#define LTM_STATS_MERGE_SAT (1 << 2)
#define LTM_HIST_CHECKSUM_SUPPORT (1 << 0)
/*
* struct drm_msm_ltm_stats_data - LTM stats data structure
*/
struct drm_msm_ltm_stats_data {
__u32 stats_01[LTM_DATA_SIZE_0][LTM_DATA_SIZE_1];
__u32 stats_02[LTM_DATA_SIZE_2];
__u32 stats_03[LTM_DATA_SIZE_0];
__u32 stats_04[LTM_DATA_SIZE_0];
__u32 stats_05[LTM_DATA_SIZE_0];
__u32 status_flag;
__u32 display_h;
__u32 display_v;
__u32 init_h[LTM_BLOCK_SIZE];
__u32 init_v;
__u32 inc_h;
__u32 inc_v;
__u32 portrait_en;
__u32 merge_en;
__u32 cfg_param_01;
__u32 cfg_param_02;
__u32 cfg_param_03;
__u32 cfg_param_04;
__u32 feature_flag;
__u32 checksum;
};
/*
* struct drm_msm_ltm_init_param - LTM init param structure
*/
struct drm_msm_ltm_init_param {
__u32 init_param_01;
__u32 init_param_02;
__u32 init_param_03;
__u32 init_param_04;
};
/*
* struct drm_msm_ltm_cfg_param - LTM config param structure
*/
struct drm_msm_ltm_cfg_param {
__u32 cfg_param_01;
__u32 cfg_param_02;
__u32 cfg_param_03;
__u32 cfg_param_04;
__u32 cfg_param_05;
__u32 cfg_param_06;
};
/*
* struct drm_msm_ltm_data - LTM data structure
*/
struct drm_msm_ltm_data {
__u32 data[LTM_DATA_SIZE_0][LTM_DATA_SIZE_3];
};
/*
* struct drm_msm_ltm_buffers_crtl - LTM buffer control structure.
* This struct will be used to init and
* de-init the LTM buffers in driver.
* @num_of_buffers: valid number of buffers used
* @fds: fd array to for all the valid buffers
*/
struct drm_msm_ltm_buffers_ctrl {
__u32 num_of_buffers;
__u32 fds[LTM_BUFFER_SIZE];
};
/*
* struct drm_msm_ltm_buffer - LTM buffer structure.
* This struct will be passed from driver to user
* space for LTM stats data notification.
* @fd: fd assicated with the buffer that has LTM stats data
* @offset: offset from base address that used for alignment
* @status status flag for error indication
*/
struct drm_msm_ltm_buffer {
__u32 fd;
__u32 offset;
__u32 status;
};
#define SPR_INIT_PARAM_SIZE_1 4
#define SPR_INIT_PARAM_SIZE_2 5
#define SPR_INIT_PARAM_SIZE_3 16
#define SPR_INIT_PARAM_SIZE_4 24
#define SPR_INIT_PARAM_SIZE_5 32
/**
* struct drm_msm_spr_init_cfg - SPR initial configuration structure
*
*/
struct drm_msm_spr_init_cfg {
__u64 flags;
__u16 cfg0;
__u16 cfg1;
__u16 cfg2;
__u16 cfg3;
__u16 cfg4;
__u16 cfg5;
__u16 cfg6;
__u16 cfg7;
__u16 cfg8;
__u16 cfg9;
__u32 cfg10;
__u16 cfg11[SPR_INIT_PARAM_SIZE_1];
__u16 cfg12[SPR_INIT_PARAM_SIZE_1];
__u16 cfg13[SPR_INIT_PARAM_SIZE_1];
__u16 cfg14[SPR_INIT_PARAM_SIZE_2];
__u16 cfg15[SPR_INIT_PARAM_SIZE_5];
int cfg16[SPR_INIT_PARAM_SIZE_3];
int cfg17[SPR_INIT_PARAM_SIZE_4];
};
#define FEATURE_DEM
#define CFG0_PARAM_LEN 8
#define CFG1_PARAM_LEN 8
#define CFG1_PARAM0_LEN 153
#define CFG0_PARAM2_LEN 256
#define CFG5_PARAM01_LEN 4
#define CFG3_PARAM01_LEN 4
struct drm_msm_dem_cfg {
__u64 flags;
__u32 pentile;
__u32 cfg0_en;
__u32 cfg0_param0_len;
__u32 cfg0_param0[CFG0_PARAM_LEN];
__u32 cfg0_param1_len;
__u32 cfg0_param1[CFG0_PARAM_LEN];
__u32 cfg0_param2_len;
__u64 cfg0_param2_c0[CFG0_PARAM2_LEN];
__u64 cfg0_param2_c1[CFG0_PARAM2_LEN];
__u64 cfg0_param2_c2[CFG0_PARAM2_LEN];
__u32 cfg0_param3_len;
__u32 cfg0_param3_c0[CFG0_PARAM_LEN];
__u32 cfg0_param3_c1[CFG0_PARAM_LEN];
__u32 cfg0_param3_c2[CFG0_PARAM_LEN];
__u32 cfg0_param4_len;
__u32 cfg0_param4[CFG0_PARAM_LEN];
__u32 cfg1_en;
__u32 cfg1_high_idx;
__u32 cfg1_low_idx;
__u32 cfg01_param0_len;
__u32 cfg01_param0[CFG1_PARAM_LEN];
__u32 cfg1_param0_len;
__u32 cfg1_param0_c0[CFG1_PARAM0_LEN];
__u32 cfg1_param0_c1[CFG1_PARAM0_LEN];
__u32 cfg1_param0_c2[CFG1_PARAM0_LEN];
__u32 cfg2_en;
__u32 cfg3_en;
__u32 cfg3_param0_len;
__u32 cfg3_param0_a[CFG3_PARAM01_LEN];
__u32 cfg3_param0_b[CFG3_PARAM01_LEN];
__u32 cfg3_ab_adj;
__u32 cfg4_en;
__u32 cfg5_en;
__u32 cfg5_param0_len;
__u32 cfg5_param0[CFG5_PARAM01_LEN];
__u32 cfg5_param1_len;
__u32 cfg5_param1[CFG5_PARAM01_LEN];
__u32 c0_depth;
__u32 c1_depth;
__u32 c2_depth;
__u32 src_id;
};
/**
* struct drm_msm_ad4_manual_str_cfg - ad4 manual strength config set
* by user-space client.
* @in_str - strength for inside roi region
* @out_str - strength for outside roi region
*/
#define DRM_MSM_AD4_MANUAL_STRENGTH
struct drm_msm_ad4_manual_str_cfg {
__u32 in_str;
__u32 out_str;
};
#define RC_DATA_SIZE_MAX 2720
#define RC_CFG_SIZE_MAX 4
struct drm_msm_rc_mask_cfg {
__u64 flags;
__u32 cfg_param_01;
__u32 cfg_param_02;
__u32 cfg_param_03;
__u32 cfg_param_04[RC_CFG_SIZE_MAX];
__u32 cfg_param_05[RC_CFG_SIZE_MAX];
__u32 cfg_param_06[RC_CFG_SIZE_MAX];
__u64 cfg_param_07;
__u32 cfg_param_08;
__u64 cfg_param_09[RC_DATA_SIZE_MAX];
};
#endif /* _MSM_DRM_PP_H_ */

View File

@ -0,0 +1,697 @@
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _SDE_DRM_H_
#define _SDE_DRM_H_
#include <drm/drm.h>
#if defined(__cplusplus)
extern "C" {
#endif
/* Total number of supported color planes */
#define SDE_MAX_PLANES 4
/* Total number of parameterized detail enhancer mapping curves */
#define SDE_MAX_DE_CURVES 3
/* Y/RGB and UV filter configuration */
#define FILTER_EDGE_DIRECTED_2D 0x0
#define FILTER_CIRCULAR_2D 0x1
#define FILTER_SEPARABLE_1D 0x2
#define FILTER_BILINEAR 0x3
/* Alpha filters */
#define FILTER_ALPHA_DROP_REPEAT 0x0
#define FILTER_ALPHA_BILINEAR 0x1
#define FILTER_ALPHA_2D 0x3
/* Blend filters */
#define FILTER_BLEND_CIRCULAR_2D 0x0
#define FILTER_BLEND_SEPARABLE_1D 0x1
/* LUT configuration flags */
#define SCALER_LUT_SWAP 0x1
#define SCALER_LUT_DIR_WR 0x2
#define SCALER_LUT_Y_CIR_WR 0x4
#define SCALER_LUT_UV_CIR_WR 0x8
#define SCALER_LUT_Y_SEP_WR 0x10
#define SCALER_LUT_UV_SEP_WR 0x20
/**
* Blend operations for "blend_op" property
*
* @SDE_DRM_BLEND_OP_NOT_DEFINED: No blend operation defined for the layer.
* @SDE_DRM_BLEND_OP_OPAQUE: Apply a constant blend operation. The layer
* would appear opaque in case fg plane alpha
* is 0xff.
* @SDE_DRM_BLEND_OP_PREMULTIPLIED: Apply source over blend rule. Layer already
* has alpha pre-multiplication done. If the fg
* plane alpha is less than 0xff, apply
* modulation as well. This operation is
* intended on layers having alpha channel.
* @SDE_DRM_BLEND_OP_COVERAGE: Apply source over blend rule. Layer is not
* alpha pre-multiplied. Apply
* pre-multiplication. If fg plane alpha is
* less than 0xff, apply modulation as well.
* @SDE_DRM_BLEND_OP_MAX: Used to track maximum blend operation
* possible by mdp.
* @SDE_DRM_BLEND_OP_SKIP: Skip staging the layer in the layer mixer.
*/
#define SDE_DRM_BLEND_OP_NOT_DEFINED 0
#define SDE_DRM_BLEND_OP_OPAQUE 1
#define SDE_DRM_BLEND_OP_PREMULTIPLIED 2
#define SDE_DRM_BLEND_OP_COVERAGE 3
#define SDE_DRM_BLEND_OP_MAX 4
#define SDE_DRM_BLEND_OP_SKIP 5
/**
* Bit masks for "src_config" property
* construct bitmask via (1UL << SDE_DRM_<flag>)
*/
#define SDE_DRM_DEINTERLACE 0 /* Specifies interlaced input */
/* DRM bitmasks are restricted to 0..63 */
#define SDE_DRM_BITMASK_COUNT 64
/**
* Framebuffer modes for "fb_translation_mode" PLANE and CONNECTOR property
*
* @SDE_DRM_FB_NON_SEC: IOMMU configuration for this framebuffer mode
* is non-secure domain and requires
* both stage I and stage II translations when
* this buffer is accessed by the display HW.
* This is the default mode of all frambuffers.
* @SDE_DRM_FB_SEC: IOMMU configuration for this framebuffer mode
* is secure domain and requires
* both stage I and stage II translations when
* this buffer is accessed by the display HW.
* @SDE_DRM_FB_NON_SEC_DIR_TRANS: IOMMU configuration for this framebuffer mode
* is non-secure domain and requires
* only stage II translation when
* this buffer is accessed by the display HW.
* @SDE_DRM_FB_SEC_DIR_TRANS: IOMMU configuration for this framebuffer mode
* is secure domain and requires
* only stage II translation when
* this buffer is accessed by the display HW.
*/
#define SDE_DRM_FB_NON_SEC 0
#define SDE_DRM_FB_SEC 1
#define SDE_DRM_FB_NON_SEC_DIR_TRANS 2
#define SDE_DRM_FB_SEC_DIR_TRANS 3
/**
* Secure levels for "security_level" CRTC property.
* CRTC property which specifies what plane types
* can be attached to this CRTC. Plane component
* derives the plane type based on the FB_MODE.
* @ SDE_DRM_SEC_NON_SEC: Both Secure and non-secure plane types can be
* attached to this CRTC. This is the default state of
* the CRTC.
* @ SDE_DRM_SEC_ONLY: Only secure planes can be added to this CRTC. If a
* CRTC is instructed to be in this mode it follows the
* platform dependent restrictions.
*/
#define SDE_DRM_SEC_NON_SEC 0
#define SDE_DRM_SEC_ONLY 1
/**
* struct sde_drm_pix_ext_v1 - version 1 of pixel ext structure
* @num_ext_pxls_lr: Number of total horizontal pixels
* @num_ext_pxls_tb: Number of total vertical lines
* @left_ftch: Number of extra pixels to overfetch from left
* @right_ftch: Number of extra pixels to overfetch from right
* @top_ftch: Number of extra lines to overfetch from top
* @btm_ftch: Number of extra lines to overfetch from bottom
* @left_rpt: Number of extra pixels to repeat from left
* @right_rpt: Number of extra pixels to repeat from right
* @top_rpt: Number of extra lines to repeat from top
* @btm_rpt: Number of extra lines to repeat from bottom
*/
struct sde_drm_pix_ext_v1 {
/*
* Number of pixels ext in left, right, top and bottom direction
* for all color components.
*/
__s32 num_ext_pxls_lr[SDE_MAX_PLANES];
__s32 num_ext_pxls_tb[SDE_MAX_PLANES];
/*
* Number of pixels needs to be overfetched in left, right, top
* and bottom directions from source image for scaling.
*/
__s32 left_ftch[SDE_MAX_PLANES];
__s32 right_ftch[SDE_MAX_PLANES];
__s32 top_ftch[SDE_MAX_PLANES];
__s32 btm_ftch[SDE_MAX_PLANES];
/*
* Number of pixels needs to be repeated in left, right, top and
* bottom directions for scaling.
*/
__s32 left_rpt[SDE_MAX_PLANES];
__s32 right_rpt[SDE_MAX_PLANES];
__s32 top_rpt[SDE_MAX_PLANES];
__s32 btm_rpt[SDE_MAX_PLANES];
};
/**
* struct sde_drm_scaler_v1 - version 1 of struct sde_drm_scaler
* @lr: Pixel extension settings for left/right
* @tb: Pixel extension settings for top/botton
* @init_phase_x: Initial scaler phase values for x
* @phase_step_x: Phase step values for x
* @init_phase_y: Initial scaler phase values for y
* @phase_step_y: Phase step values for y
* @horz_filter: Horizontal filter array
* @vert_filter: Vertical filter array
*/
struct sde_drm_scaler_v1 {
/*
* Pix ext settings
*/
struct sde_drm_pix_ext_v1 pe;
/*
* Phase settings
*/
__s32 init_phase_x[SDE_MAX_PLANES];
__s32 phase_step_x[SDE_MAX_PLANES];
__s32 init_phase_y[SDE_MAX_PLANES];
__s32 phase_step_y[SDE_MAX_PLANES];
/*
* Filter type to be used for scaling in horizontal and vertical
* directions
*/
__u32 horz_filter[SDE_MAX_PLANES];
__u32 vert_filter[SDE_MAX_PLANES];
};
/**
* struct sde_drm_de_v1 - version 1 of detail enhancer structure
* @enable: Enables/disables detail enhancer
* @sharpen_level1: Sharpening strength for noise
* @sharpen_level2: Sharpening strength for context
* @clip: Clip coefficient
* @limit: Detail enhancer limit factor
* @thr_quiet: Quite zone threshold
* @thr_dieout: Die-out zone threshold
* @thr_low: Linear zone left threshold
* @thr_high: Linear zone right threshold
* @prec_shift: Detail enhancer precision
* @adjust_a: Mapping curves A coefficients
* @adjust_b: Mapping curves B coefficients
* @adjust_c: Mapping curves C coefficients
*/
struct sde_drm_de_v1 {
__u32 enable;
__s16 sharpen_level1;
__s16 sharpen_level2;
__u16 clip;
__u16 limit;
__u16 thr_quiet;
__u16 thr_dieout;
__u16 thr_low;
__u16 thr_high;
__u16 prec_shift;
__s16 adjust_a[SDE_MAX_DE_CURVES];
__s16 adjust_b[SDE_MAX_DE_CURVES];
__s16 adjust_c[SDE_MAX_DE_CURVES];
};
/*
* Scaler configuration flags
*/
/* Disable dynamic expansion */
#define SDE_DYN_EXP_DISABLE 0x1
#define SDE_DRM_QSEED3LITE
#define SDE_DRM_QSEED4
#define SDE_DRM_INLINE_PREDOWNSCALE
/**
* struct sde_drm_scaler_v2 - version 2 of struct sde_drm_scaler
* @enable: Scaler enable
* @dir_en: Detail enhancer enable
* @pe: Pixel extension settings
* @horz_decimate: Horizontal decimation factor
* @vert_decimate: Vertical decimation factor
* @init_phase_x: Initial scaler phase values for x
* @phase_step_x: Phase step values for x
* @init_phase_y: Initial scaler phase values for y
* @phase_step_y: Phase step values for y
* @preload_x: Horizontal preload value
* @preload_y: Vertical preload value
* @src_width: Source width
* @src_height: Source height
* @dst_width: Destination width
* @dst_height: Destination height
* @y_rgb_filter_cfg: Y/RGB plane filter configuration
* @uv_filter_cfg: UV plane filter configuration
* @alpha_filter_cfg: Alpha filter configuration
* @blend_cfg: Selection of blend coefficients
* @lut_flag: LUT configuration flags
* @dir_lut_idx: 2d 4x4 LUT index
* @y_rgb_cir_lut_idx: Y/RGB circular LUT index
* @uv_cir_lut_idx: UV circular LUT index
* @y_rgb_sep_lut_idx: Y/RGB separable LUT index
* @uv_sep_lut_idx: UV separable LUT index
* @de: Detail enhancer settings
* @dir_weight: Directional Weight
* @unsharp_mask_blend: Unsharp Blend Filter Ratio
* @de_blend: Ratio of two unsharp mask filters
* @flags: Scaler configuration flags
* @pre_downscale_x_0 Pre-downscale ratio, x-direction, plane 0(Y/RGB)
* @pre_downscale_x_1 Pre-downscale ratio, x-direction, plane 1(UV)
* @pre_downscale_y_0 Pre-downscale ratio, y-direction, plane 0(Y/RGB)
* @pre_downscale_y_1 Pre-downscale ratio, y-direction, plane 1(UV)
*/
struct sde_drm_scaler_v2 {
/*
* General definitions
*/
__u32 enable;
__u32 dir_en;
/*
* Pix ext settings
*/
struct sde_drm_pix_ext_v1 pe;
/*
* Decimation settings
*/
__u32 horz_decimate;
__u32 vert_decimate;
/*
* Phase settings
*/
__s32 init_phase_x[SDE_MAX_PLANES];
__s32 phase_step_x[SDE_MAX_PLANES];
__s32 init_phase_y[SDE_MAX_PLANES];
__s32 phase_step_y[SDE_MAX_PLANES];
__u32 preload_x[SDE_MAX_PLANES];
__u32 preload_y[SDE_MAX_PLANES];
__u32 src_width[SDE_MAX_PLANES];
__u32 src_height[SDE_MAX_PLANES];
__u32 dst_width;
__u32 dst_height;
__u32 y_rgb_filter_cfg;
__u32 uv_filter_cfg;
__u32 alpha_filter_cfg;
__u32 blend_cfg;
__u32 lut_flag;
__u32 dir_lut_idx;
/* for Y(RGB) and UV planes*/
__u32 y_rgb_cir_lut_idx;
__u32 uv_cir_lut_idx;
__u32 y_rgb_sep_lut_idx;
__u32 uv_sep_lut_idx;
/*
* Detail enhancer settings
*/
struct sde_drm_de_v1 de;
__u32 dir_weight;
__u32 unsharp_mask_blend;
__u32 de_blend;
__u32 flags;
/*
* Inline pre-downscale settings
*/
__u32 pre_downscale_x_0;
__u32 pre_downscale_x_1;
__u32 pre_downscale_y_0;
__u32 pre_downscale_y_1;
};
/* Number of dest scalers supported */
#define SDE_MAX_DS_COUNT 2
/*
* Destination scaler flag config
*/
#define SDE_DRM_DESTSCALER_ENABLE 0x1
#define SDE_DRM_DESTSCALER_SCALE_UPDATE 0x2
#define SDE_DRM_DESTSCALER_ENHANCER_UPDATE 0x4
#define SDE_DRM_DESTSCALER_PU_ENABLE 0x8
/**
* struct sde_drm_dest_scaler_cfg - destination scaler config structure
* @flags: Flag to switch between mode for destination scaler
* refer to destination scaler flag config
* @index: Destination scaler selection index
* @lm_width: Layer mixer width configuration
* @lm_height: Layer mixer height configuration
* @scaler_cfg: The scaling parameters for all the mode except disable
* Userspace pointer to struct sde_drm_scaler_v2
*/
struct sde_drm_dest_scaler_cfg {
__u32 flags;
__u32 index;
__u32 lm_width;
__u32 lm_height;
__u64 scaler_cfg;
};
/**
* struct sde_drm_dest_scaler_data - destination scaler data struct
* @num_dest_scaler: Number of dest scalers to be configured
* @ds_cfg: Destination scaler block configuration
*/
struct sde_drm_dest_scaler_data {
__u32 num_dest_scaler;
struct sde_drm_dest_scaler_cfg ds_cfg[SDE_MAX_DS_COUNT];
};
/*
* Define constants for struct sde_drm_csc
*/
#define SDE_CSC_MATRIX_COEFF_SIZE 9
#define SDE_CSC_CLAMP_SIZE 6
#define SDE_CSC_BIAS_SIZE 3
/**
* struct sde_drm_csc_v1 - version 1 of struct sde_drm_csc
* @ctm_coeff: Matrix coefficients, in S31.32 format
* @pre_bias: Pre-bias array values
* @post_bias: Post-bias array values
* @pre_clamp: Pre-clamp array values
* @post_clamp: Post-clamp array values
*/
struct sde_drm_csc_v1 {
__s64 ctm_coeff[SDE_CSC_MATRIX_COEFF_SIZE];
__u32 pre_bias[SDE_CSC_BIAS_SIZE];
__u32 post_bias[SDE_CSC_BIAS_SIZE];
__u32 pre_clamp[SDE_CSC_CLAMP_SIZE];
__u32 post_clamp[SDE_CSC_CLAMP_SIZE];
};
/**
* struct sde_drm_color - struct to store the color and alpha values
* @color_0: Color 0 value
* @color_1: Color 1 value
* @color_2: Color 2 value
* @color_3: Color 3 value
*/
struct sde_drm_color {
__u32 color_0;
__u32 color_1;
__u32 color_2;
__u32 color_3;
};
/* Total number of supported dim layers */
#define SDE_MAX_DIM_LAYERS 7
/* SDE_DRM_DIM_LAYER_CONFIG_FLAG - flags for Dim Layer */
/* Color fill inside of the rect, including border */
#define SDE_DRM_DIM_LAYER_INCLUSIVE 0x1
/* Color fill outside of the rect, excluding border */
#define SDE_DRM_DIM_LAYER_EXCLUSIVE 0x2
/**
* struct sde_drm_dim_layer - dim layer cfg struct
* @flags: Refer SDE_DRM_DIM_LAYER_CONFIG_FLAG for possible values
* @stage: Blending stage of the dim layer
* @color_fill: Color fill for dim layer
* @rect: Dim layer coordinates
*/
struct sde_drm_dim_layer_cfg {
__u32 flags;
__u32 stage;
struct sde_drm_color color_fill;
struct drm_clip_rect rect;
};
/**
* struct sde_drm_dim_layer_v1 - version 1 of dim layer struct
* @num_layers: Numer of Dim Layers
* @layer: Dim layer user cfgs ptr for the num_layers
*/
struct sde_drm_dim_layer_v1 {
__u32 num_layers;
struct sde_drm_dim_layer_cfg layer_cfg[SDE_MAX_DIM_LAYERS];
};
/* Writeback Config version definition */
#define SDE_DRM_WB_CFG 0x1
/* SDE_DRM_WB_CONFIG_FLAGS - Writeback configuration flags */
#define SDE_DRM_WB_CFG_FLAGS_CONNECTED (1<<0)
/**
* struct sde_drm_wb_cfg - Writeback configuration structure
* @flags: see DRM_MSM_WB_CONFIG_FLAGS
* @connector_id: writeback connector identifier
* @count_modes: Count of modes in modes_ptr
* @modes: Pointer to struct drm_mode_modeinfo
*/
struct sde_drm_wb_cfg {
__u32 flags;
__u32 connector_id;
__u32 count_modes;
__u64 modes;
};
#define SDE_MAX_ROI_V1 4
/**
* struct sde_drm_roi_v1 - list of regions of interest for a drm object
* @num_rects: number of valid rectangles in the roi array
* @roi: list of roi rectangles
*/
struct sde_drm_roi_v1 {
__u32 num_rects;
struct drm_clip_rect roi[SDE_MAX_ROI_V1];
};
/**
* Define extended power modes supported by the SDE connectors.
*/
#define SDE_MODE_DPMS_ON 0
#define SDE_MODE_DPMS_LP1 1
#define SDE_MODE_DPMS_LP2 2
#define SDE_MODE_DPMS_STANDBY 3
#define SDE_MODE_DPMS_SUSPEND 4
#define SDE_MODE_DPMS_OFF 5
/**
* sde recovery events for notifying client
*/
#define SDE_RECOVERY_SUCCESS 0
#define SDE_RECOVERY_CAPTURE 1
#define SDE_RECOVERY_HARD_RESET 2
/*
* Colorimetry Data Block values
* These bit nums are defined as per the CTA spec
* and indicate the colorspaces supported by the sink
*/
#define DRM_EDID_CLRMETRY_xvYCC_601 (1 << 0)
#define DRM_EDID_CLRMETRY_xvYCC_709 (1 << 1)
#define DRM_EDID_CLRMETRY_sYCC_601 (1 << 2)
#define DRM_EDID_CLRMETRY_ADOBE_YCC_601 (1 << 3)
#define DRM_EDID_CLRMETRY_ADOBE_RGB (1 << 4)
#define DRM_EDID_CLRMETRY_BT2020_CYCC (1 << 5)
#define DRM_EDID_CLRMETRY_BT2020_YCC (1 << 6)
#define DRM_EDID_CLRMETRY_BT2020_RGB (1 << 7)
#define DRM_EDID_CLRMETRY_DCI_P3 (1 << 15)
/*
* HDR Metadata
* These are defined as per EDID spec and shall be used by the sink
* to set the HDR metadata for playback from userspace.
*/
#define HDR_PRIMARIES_COUNT 3
/* HDR EOTF */
#define HDR_EOTF_SDR_LUM_RANGE 0x0
#define HDR_EOTF_HDR_LUM_RANGE 0x1
#define HDR_EOTF_SMTPE_ST2084 0x2
#define HDR_EOTF_HLG 0x3
#define DRM_MSM_EXT_HDR_METADATA
#define DRM_MSM_EXT_HDR_PLUS_METADATA
struct drm_msm_ext_hdr_metadata {
__u32 hdr_state; /* HDR state */
__u32 eotf; /* electro optical transfer function */
__u32 hdr_supported; /* HDR supported */
__u32 display_primaries_x[HDR_PRIMARIES_COUNT]; /* Primaries x */
__u32 display_primaries_y[HDR_PRIMARIES_COUNT]; /* Primaries y */
__u32 white_point_x; /* white_point_x */
__u32 white_point_y; /* white_point_y */
__u32 max_luminance; /* Max luminance */
__u32 min_luminance; /* Min Luminance */
__u32 max_content_light_level; /* max content light level */
__u32 max_average_light_level; /* max average light level */
__u64 hdr_plus_payload; /* user pointer to dynamic HDR payload */
__u32 hdr_plus_payload_size;/* size of dynamic HDR payload data */
};
/**
* HDR sink properties
* These are defined as per EDID spec and shall be used by the userspace
* to determine the HDR properties to be set to the sink.
*/
#define DRM_MSM_EXT_HDR_PROPERTIES
#define DRM_MSM_EXT_HDR_PLUS_PROPERTIES
struct drm_msm_ext_hdr_properties {
__u8 hdr_metadata_type_one; /* static metadata type one */
__u32 hdr_supported; /* HDR supported */
__u32 hdr_eotf; /* electro optical transfer function */
__u32 hdr_max_luminance; /* Max luminance */
__u32 hdr_avg_luminance; /* Avg luminance */
__u32 hdr_min_luminance; /* Min Luminance */
__u32 hdr_plus_supported; /* HDR10+ supported */
};
/* HDR WRGB x and y index */
#define DISPLAY_PRIMARIES_WX 0
#define DISPLAY_PRIMARIES_WY 1
#define DISPLAY_PRIMARIES_RX 2
#define DISPLAY_PRIMARIES_RY 3
#define DISPLAY_PRIMARIES_GX 4
#define DISPLAY_PRIMARIES_GY 5
#define DISPLAY_PRIMARIES_BX 6
#define DISPLAY_PRIMARIES_BY 7
#define DISPLAY_PRIMARIES_MAX 8
struct drm_panel_hdr_properties {
__u32 hdr_enabled;
/* WRGB X and y values arrayed in format */
/* [WX, WY, RX, RY, GX, GY, BX, BY] */
__u32 display_primaries[DISPLAY_PRIMARIES_MAX];
/* peak brightness supported by panel */
__u32 peak_brightness;
/* Blackness level supported by panel */
__u32 blackness_level;
};
/**
* struct drm_msm_event_req - Payload to event enable/disable ioctls.
* @object_id: DRM object id. e.g.: for crtc pass crtc id.
* @object_type: DRM object type. e.g.: for crtc set it to DRM_MODE_OBJECT_CRTC.
* @event: Event for which notification is being enabled/disabled.
* e.g.: for Histogram set - DRM_EVENT_HISTOGRAM.
* @client_context: Opaque pointer that will be returned during event response
* notification.
* @index: Object index(e.g.: crtc index), optional for user-space to set.
* Driver will override value based on object_id and object_type.
*/
struct drm_msm_event_req {
__u32 object_id;
__u32 object_type;
__u32 event;
__u64 client_context;
__u32 index;
};
/**
* struct drm_msm_event_resp - payload returned when read is called for
* custom notifications.
* @base: Event type and length of complete notification payload.
* @info: Contains information about DRM that which raised this event.
* @data: Custom payload that driver returns for event type.
* size of data = base.length - (sizeof(base) + sizeof(info))
*/
struct drm_msm_event_resp {
struct drm_event base;
struct drm_msm_event_req info;
__u8 data[];
};
/**
* struct drm_msm_power_ctrl: Payload to enable/disable the power vote
* @enable: enable/disable the power vote
* @flags: operation control flags, for future use
*/
struct drm_msm_power_ctrl {
__u32 enable;
__u32 flags;
};
/**
* struct drm_msm_early_wakeup: Payload to early wake up display
* @wakeup_hint: early wakeup hint.
* @connector_id: connector id. e.g.: for connector pass connector id.
*/
struct drm_msm_early_wakeup {
__u32 wakeup_hint;
__u32 connector_id;
};
/**
* struct drm_msm_display_hint: Payload for display hint
* @hint_flags: display hint flags.
* @data: data struct. e.g.: for display hint parameter.
* Userspace pointer to struct base on hint flags.
*/
struct drm_msm_display_hint {
__u64 data;
__u32 hint_flags;
};
#define DRM_SDE_WB_CONFIG 0x40
#define DRM_MSM_REGISTER_EVENT 0x41
#define DRM_MSM_DEREGISTER_EVENT 0x42
#define DRM_MSM_RMFB2 0x43
#define DRM_MSM_POWER_CTRL 0x44
#define DRM_MSM_DISPLAY_HINT 0x45
/* sde custom events */
#define DRM_EVENT_HISTOGRAM 0x80000000
#define DRM_EVENT_AD_BACKLIGHT 0x80000001
#define DRM_EVENT_CRTC_POWER 0x80000002
#define DRM_EVENT_SYS_BACKLIGHT 0x80000003
#define DRM_EVENT_SDE_POWER 0x80000004
#define DRM_EVENT_IDLE_NOTIFY 0x80000005
#define DRM_EVENT_PANEL_DEAD 0x80000006 /* ESD event */
#define DRM_EVENT_SDE_HW_RECOVERY 0X80000007
#define DRM_EVENT_LTM_HIST 0X80000008
#define DRM_EVENT_LTM_WB_PB 0X80000009
#define DRM_EVENT_LTM_OFF 0X8000000A
/* display hint flags*/
#define DRM_MSM_DISPLAY_EARLY_WAKEUP_HINT 0x01
#define DRM_MSM_DISPLAY_POWER_COLLAPSE_HINT 0x02
#define DRM_MSM_DISPLAY_IDLE_TIMEOUT_HINT 0x04
#define DRM_MSM_DISPLAY_MODE_CHANGE_HINT 0x08
#define DRM_MSM_WAKE_UP_ALL_DISPLAYS 0xFFFFFFFF
#define DRM_IOCTL_SDE_WB_CONFIG \
DRM_IOW((DRM_COMMAND_BASE + DRM_SDE_WB_CONFIG), struct sde_drm_wb_cfg)
#define DRM_IOCTL_MSM_REGISTER_EVENT DRM_IOW((DRM_COMMAND_BASE + \
DRM_MSM_REGISTER_EVENT), struct drm_msm_event_req)
#define DRM_IOCTL_MSM_DEREGISTER_EVENT DRM_IOW((DRM_COMMAND_BASE + \
DRM_MSM_DEREGISTER_EVENT), struct drm_msm_event_req)
#define DRM_IOCTL_MSM_RMFB2 DRM_IOW((DRM_COMMAND_BASE + \
DRM_MSM_RMFB2), unsigned int)
#define DRM_IOCTL_MSM_POWER_CTRL DRM_IOW((DRM_COMMAND_BASE + \
DRM_MSM_POWER_CTRL), struct drm_msm_power_ctrl)
#define DRM_IOCTL_MSM_DISPLAY_HINT DRM_IOW((DRM_COMMAND_BASE + \
DRM_MSM_DISPLAY_HINT), struct drm_msm_display_hint)
#if defined(__cplusplus)
}
#endif
#endif /* _SDE_DRM_H_ */

View File

@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note
header-y += msm_hdmi_hdcp_mgr.h

View File

@ -0,0 +1,61 @@
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _UAPI__MSM_HDMI_HDCP_MGR_H
#define _UAPI__MSM_HDMI_HDCP_MGR_H
#include <linux/types.h>
enum DS_TYPE { /* type of downstream device */
DS_UNKNOWN,
DS_RECEIVER,
DS_REPEATER,
};
enum {
MSG_ID_IDX,
RET_CODE_IDX,
HEADER_LEN,
};
enum RET_CODE {
HDCP_NOT_AUTHED,
HDCP_AUTHED,
HDCP_DISABLE,
};
enum MSG_ID { /* List of functions expected to be called after it */
DOWN_CHECK_TOPOLOGY,
UP_REQUEST_TOPOLOGY,
UP_SEND_TOPOLOGY,
DOWN_REQUEST_TOPOLOGY,
MSG_NUM,
};
enum SOURCE_ID {
HDCP_V1_TX,
HDCP_V1_RX,
HDCP_V2_RX,
HDCP_V2_TX,
SRC_NUM,
};
/*
* how to parse sysfs params buffer
* from hdcp_tx driver.
*/
struct HDCP_V2V1_MSG_TOPOLOGY {
/* indicates downstream's type */
__u32 ds_type;
__u8 bksv[5];
__u8 dev_count;
__u8 depth;
__u8 ksv_list[5 * 127];
__u32 max_cascade_exceeded;
__u32 max_dev_exceeded;
};
#endif /* _UAPI__MSM_HDMI_HDCP_MGR_H */

View File

@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note
header-y += msm_sde_rotator.h
header-y += mmm_color_fmt.h

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,180 @@
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#ifndef __UAPI_MSM_SDE_ROTATOR_H__
#define __UAPI_MSM_SDE_ROTATOR_H__
#include <linux/videodev2.h>
#include <linux/types.h>
#include <linux/ioctl.h>
/* SDE Rotator pixel format definitions */
#define SDE_PIX_FMT_XRGB_8888 \
v4l2_fourcc('X', 'R', '2', '4') /* 32 BGRX-8-8-8-8 */
#define SDE_PIX_FMT_ARGB_8888 \
v4l2_fourcc('A', 'R', '2', '4') /* 32 BGRA-8-8-8-8 */
#define SDE_PIX_FMT_ABGR_8888 \
v4l2_fourcc('R', 'A', '2', '4') /* 32-bit ABGR 8:8:8:8 */
#define SDE_PIX_FMT_RGBA_8888 \
v4l2_fourcc('A', 'B', '2', '4') /* 32-bit RGBA 8:8:8:8 */
#define SDE_PIX_FMT_BGRA_8888 \
v4l2_fourcc('B', 'A', '2', '4') /* 32 ARGB-8-8-8-8 */
#define SDE_PIX_FMT_RGBX_8888 \
v4l2_fourcc('X', 'B', '2', '4') /* 32-bit RGBX 8:8:8:8 */
#define SDE_PIX_FMT_BGRX_8888 \
v4l2_fourcc('B', 'X', '2', '4') /* 32 XRGB-8-8-8-8 */
#define SDE_PIX_FMT_XBGR_8888 \
v4l2_fourcc('R', 'X', '2', '4') /* 32-bit XBGR 8:8:8:8 */
#define SDE_PIX_FMT_RGBA_5551 \
v4l2_fourcc('R', 'A', '1', '5') /* 16-bit RGBA 5:5:5:1 */
#define SDE_PIX_FMT_ARGB_1555 \
v4l2_fourcc('A', 'R', '1', '5') /* 16 ARGB-1-5-5-5 */
#define SDE_PIX_FMT_ABGR_1555 \
v4l2_fourcc('A', 'B', '1', '5') /* 16-bit ABGR 1:5:5:5 */
#define SDE_PIX_FMT_BGRA_5551 \
v4l2_fourcc('B', 'A', '1', '5') /* 16-bit BGRA 5:5:5:1 */
#define SDE_PIX_FMT_BGRX_5551 \
v4l2_fourcc('B', 'X', '1', '5') /* 16-bit BGRX 5:5:5:1 */
#define SDE_PIX_FMT_RGBX_5551 \
v4l2_fourcc('R', 'X', '1', '5') /* 16-bit RGBX 5:5:5:1 */
#define SDE_PIX_FMT_XBGR_1555 \
v4l2_fourcc('X', 'B', '1', '5') /* 16-bit XBGR 1:5:5:5 */
#define SDE_PIX_FMT_XRGB_1555 \
v4l2_fourcc('X', 'R', '1', '5') /* 16 XRGB-1-5-5-5 */
#define SDE_PIX_FMT_ARGB_4444 \
v4l2_fourcc('A', 'R', '1', '2') /* 16 aaaarrrr ggggbbbb */
#define SDE_PIX_FMT_RGBA_4444 \
v4l2_fourcc('R', 'A', '1', '2') /* 16-bit RGBA 4:4:4:4 */
#define SDE_PIX_FMT_BGRA_4444 \
v4l2_fourcc('b', 'A', '1', '2') /* 16-bit BGRA 4:4:4:4 */
#define SDE_PIX_FMT_ABGR_4444 \
v4l2_fourcc('A', 'B', '1', '2') /* 16-bit ABGR 4:4:4:4 */
#define SDE_PIX_FMT_RGBX_4444 \
v4l2_fourcc('R', 'X', '1', '2') /* 16-bit RGBX 4:4:4:4 */
#define SDE_PIX_FMT_XRGB_4444 \
v4l2_fourcc('X', 'R', '1', '2') /* 16 xxxxrrrr ggggbbbb */
#define SDE_PIX_FMT_BGRX_4444 \
v4l2_fourcc('B', 'X', '1', '2') /* 16-bit BGRX 4:4:4:4 */
#define SDE_PIX_FMT_XBGR_4444 \
v4l2_fourcc('X', 'B', '1', '2') /* 16-bit XBGR 4:4:4:4 */
#define SDE_PIX_FMT_RGB_888 \
v4l2_fourcc('R', 'G', 'B', '3') /* 24 RGB-8-8-8 */
#define SDE_PIX_FMT_BGR_888 \
v4l2_fourcc('B', 'G', 'R', '3') /* 24 BGR-8-8-8 */
#define SDE_PIX_FMT_RGB_565 \
v4l2_fourcc('R', 'G', 'B', 'P') /* 16 RGB-5-6-5 */
#define SDE_PIX_FMT_BGR_565 \
v4l2_fourcc('B', 'G', '1', '6') /* 16-bit BGR 5:6:5 */
#define SDE_PIX_FMT_Y_CB_CR_H2V2 \
v4l2_fourcc('Y', 'U', '1', '2') /* 12 YUV 4:2:0 */
#define SDE_PIX_FMT_Y_CR_CB_H2V2 \
v4l2_fourcc('Y', 'V', '1', '2') /* 12 YVU 4:2:0 */
#define SDE_PIX_FMT_Y_CR_CB_GH2V2 \
v4l2_fourcc('Y', 'U', '4', '2') /* Planar YVU 4:2:0 A16 */
#define SDE_PIX_FMT_Y_CBCR_H2V2 \
v4l2_fourcc('N', 'V', '1', '2') /* 12 Y/CbCr 4:2:0 */
#define SDE_PIX_FMT_Y_CRCB_H2V2 \
v4l2_fourcc('N', 'V', '2', '1') /* 12 Y/CrCb 4:2:0 */
#define SDE_PIX_FMT_Y_CBCR_H1V2 \
v4l2_fourcc('N', 'H', '1', '6') /* Y/CbCr 4:2:2 */
#define SDE_PIX_FMT_Y_CRCB_H1V2 \
v4l2_fourcc('N', 'H', '6', '1') /* Y/CrCb 4:2:2 */
#define SDE_PIX_FMT_Y_CBCR_H2V1 \
v4l2_fourcc('N', 'V', '1', '6') /* 16 Y/CbCr 4:2:2 */
#define SDE_PIX_FMT_Y_CRCB_H2V1 \
v4l2_fourcc('N', 'V', '6', '1') /* 16 Y/CrCb 4:2:2 */
#define SDE_PIX_FMT_YCBYCR_H2V1 \
v4l2_fourcc('Y', 'U', 'Y', 'V') /* 16 YUV 4:2:2 */
#define SDE_PIX_FMT_Y_CBCR_H2V2_VENUS \
v4l2_fourcc('Q', 'N', 'V', '2') /* Y/CbCr 4:2:0 Venus */
#define SDE_PIX_FMT_Y_CRCB_H2V2_VENUS \
v4l2_fourcc('Q', 'N', 'V', '1') /* Y/CrCb 4:2:0 Venus */
#define SDE_PIX_FMT_RGBA_8888_UBWC \
v4l2_fourcc('Q', 'R', 'G', 'B') /* RGBA 8:8:8:8 UBWC */
#define SDE_PIX_FMT_RGBX_8888_UBWC \
v4l2_fourcc('Q', 'X', 'B', '4') /* RGBX 8:8:8:8 UBWC */
#define SDE_PIX_FMT_RGB_565_UBWC \
v4l2_fourcc('Q', 'R', 'G', '6') /* RGB 5:6:5 UBWC */
#define SDE_PIX_FMT_Y_CBCR_H2V2_UBWC \
v4l2_fourcc('Q', '1', '2', '8') /* UBWC 8-bit Y/CbCr 4:2:0 */
#define SDE_PIX_FMT_RGBA_1010102 \
v4l2_fourcc('A', 'B', '3', '0') /* RGBA 10:10:10:2 */
#define SDE_PIX_FMT_RGBX_1010102 \
v4l2_fourcc('X', 'B', '3', '0') /* RGBX 10:10:10:2 */
#define SDE_PIX_FMT_ARGB_2101010 \
v4l2_fourcc('A', 'R', '3', '0') /* ARGB 2:10:10:10 */
#define SDE_PIX_FMT_XRGB_2101010 \
v4l2_fourcc('X', 'R', '3', '0') /* XRGB 2:10:10:10 */
#define SDE_PIX_FMT_BGRA_1010102 \
v4l2_fourcc('B', 'A', '3', '0') /* BGRA 10:10:10:2 */
#define SDE_PIX_FMT_BGRX_1010102 \
v4l2_fourcc('B', 'X', '3', '0') /* BGRX 10:10:10:2 */
#define SDE_PIX_FMT_ABGR_2101010 \
v4l2_fourcc('R', 'A', '3', '0') /* ABGR 2:10:10:10 */
#define SDE_PIX_FMT_XBGR_2101010 \
v4l2_fourcc('R', 'X', '3', '0') /* XBGR 2:10:10:10 */
#define SDE_PIX_FMT_RGBA_1010102_UBWC \
v4l2_fourcc('Q', 'R', 'B', 'A') /* RGBA 10:10:10:2 UBWC */
#define SDE_PIX_FMT_RGBX_1010102_UBWC \
v4l2_fourcc('Q', 'X', 'B', 'A') /* RGBX 10:10:10:2 UBWC */
#define SDE_PIX_FMT_Y_CBCR_H2V2_P010 \
v4l2_fourcc('P', '0', '1', '0') /* Y/CbCr 4:2:0 P10 */
#define SDE_PIX_FMT_Y_CBCR_H2V2_P010_VENUS \
v4l2_fourcc('Q', 'P', '1', '0') /* Y/CbCr 4:2:0 P10 Venus*/
#define SDE_PIX_FMT_Y_CBCR_H2V2_TP10 \
v4l2_fourcc('T', 'P', '1', '0') /* Y/CbCr 4:2:0 TP10 */
#define SDE_PIX_FMT_Y_CBCR_H2V2_TP10_UBWC \
v4l2_fourcc('Q', '1', '2', 'A') /* UBWC Y/CbCr 4:2:0 TP10 */
#define SDE_PIX_FMT_Y_CBCR_H2V2_P010_UBWC \
v4l2_fourcc('Q', '1', '2', 'B') /* UBWC Y/CbCr 4:2:0 P10 */
/*
* struct msm_sde_rotator_fence - v4l2 buffer fence info
* @index: id number of the buffer
* @type: enum v4l2_buf_type; buffer type
* @fd: file descriptor of the fence associated with this buffer
*/
struct msm_sde_rotator_fence {
__u32 index;
__u32 type;
__s32 fd;
__u32 reserved[5];
};
/*
* struct msm_sde_rotator_comp_ratio - v4l2 buffer compression ratio
* @index: id number of the buffer
* @type: enum v4l2_buf_type; buffer type
* @numer: numerator of the ratio
* @denom: denominator of the ratio
*/
struct msm_sde_rotator_comp_ratio {
__u32 index;
__u32 type;
__u32 numer;
__u32 denom;
__u32 reserved[4];
};
/* SDE Rotator private ioctl ID */
#define VIDIOC_G_SDE_ROTATOR_FENCE \
_IOWR('V', BASE_VIDIOC_PRIVATE + 10, struct msm_sde_rotator_fence)
#define VIDIOC_S_SDE_ROTATOR_FENCE \
_IOWR('V', BASE_VIDIOC_PRIVATE + 11, struct msm_sde_rotator_fence)
#define VIDIOC_G_SDE_ROTATOR_COMP_RATIO \
_IOWR('V', BASE_VIDIOC_PRIVATE + 12, struct msm_sde_rotator_comp_ratio)
#define VIDIOC_S_SDE_ROTATOR_COMP_RATIO \
_IOWR('V', BASE_VIDIOC_PRIVATE + 13, struct msm_sde_rotator_comp_ratio)
/* SDE Rotator private control ID's */
#define V4L2_CID_SDE_ROTATOR_SECURE (V4L2_CID_USER_BASE + 0x1000)
/*
* This control Id indicates this context is associated with the
* secure camera.
*/
#define V4L2_CID_SDE_ROTATOR_SECURE_CAMERA (V4L2_CID_USER_BASE + 0x2000)
#endif /* __UAPI_MSM_SDE_ROTATOR_H__ */

View File

@ -0,0 +1,160 @@
# SPDX-License-Identifier: GPL-2.0
ccflags-y := -I$(srctree)/include/drm -I$(srctree)/techpack/display/msm -I$(srctree)/techpack/display/msm/dsi -I$(srctree)/techpack/display/msm/dp
ccflags-y += -I$(srctree)/techpack/display/msm/sde
ccflags-y += -I$(srctree)/techpack/display/rotator
ccflags-y += -I$(srctree)/techpack/display/hdcp
ccflags-y += -I$(srctree)/drivers/clk/qcom/
msm_drm-$(CONFIG_DRM_MSM_DP) += dp/dp_altmode.o \
dp/dp_parser.o \
dp/dp_power.o \
dp/dp_catalog.o \
dp/dp_catalog_v420.o \
dp/dp_catalog_v200.o \
dp/dp_aux.o \
dp/dp_panel.o \
dp/dp_link.o \
dp/dp_ctrl.o \
dp/dp_audio.o \
dp/dp_debug.o \
dp/dp_hpd.o \
dp/dp_gpio_hpd.o \
dp/dp_lphw_hpd.o \
dp/dp_display.o \
dp/dp_drm.o \
dp/dp_hdcp2p2.o \
sde_hdcp_1x.o \
sde_hdcp_2x.o \
dp/dp_pll.o \
dp/dp_pll_5nm.o
msm_drm-$(CONFIG_DRM_MSM_DP_MST) += dp/dp_mst_drm.o
msm_drm-$(CONFIG_DRM_MSM_DP_USBPD_LEGACY) += dp/dp_usbpd.o
msm_drm-$(CONFIG_DRM_MSM_SDE) += sde/sde_crtc.o \
sde/sde_encoder.o \
sde/sde_encoder_dce.o \
sde/sde_encoder_phys_vid.o \
sde/sde_encoder_phys_cmd.o \
sde/sde_irq.o \
sde/sde_core_irq.o \
sde/sde_core_perf.o \
sde/sde_rm.o \
sde/sde_kms_utils.o \
sde/sde_kms.o \
sde/sde_plane.o \
sde/sde_connector.o \
sde/sde_color_processing.o \
sde/sde_vbif.o \
sde_io_util.o \
sde_vm_event.o \
sde/sde_hw_reg_dma_v1_color_proc.o \
sde/sde_hw_color_proc_v4.o \
sde/sde_hw_ad4.o \
sde/sde_hw_uidle.o \
sde_edid_parser.o \
sde/sde_hw_catalog.o \
sde/sde_hw_cdm.o \
sde/sde_hw_dspp.o \
sde/sde_hw_intf.o \
sde/sde_hw_lm.o \
sde/sde_hw_ctl.o \
sde/sde_hw_util.o \
sde/sde_hw_sspp.o \
sde/sde_hw_wb.o \
sde/sde_hw_pingpong.o \
sde/sde_hw_top.o \
sde/sde_hw_interrupts.o \
sde/sde_hw_vbif.o \
sde/sde_hw_blk.o \
sde/sde_formats.o \
sde_power_handle.o \
sde/sde_hw_color_processing_v1_7.o \
sde/sde_reg_dma.o \
sde/sde_hw_reg_dma_v1.o \
sde/sde_hw_dsc.o \
sde/sde_hw_dsc_1_2.o \
sde/sde_hw_vdc.o \
sde/sde_hw_ds.o \
sde/sde_fence.o \
sde/sde_hw_qdss.o \
sde_dsc_helper.o \
sde_vdc_helper.o \
sde/sde_hw_rc.o \
sde_dbg.o \
sde_dbg_evtlog.o \
msm_drm-$(CONFIG_DRM_SDE_VM) += sde/sde_vm_common.o \
sde/sde_vm_primary.o \
sde/sde_vm_trusted.o \
sde/sde_vm_msgq.o
msm_drm-$(CONFIG_DRM_SDE_WB) += sde/sde_wb.o \
sde/sde_encoder_phys_wb.o
msm_drm-$(CONFIG_DRM_SDE_RSC) += sde_rsc.o \
sde_rsc_hw.o \
sde_rsc_hw_v3.o
msm_drm-$(CONFIG_DRM_MSM_DSI) += dsi/dsi_phy.o \
dsi/dsi_pwr.o \
dsi/dsi_phy.o \
dsi/dsi_phy_hw_v2_0.o \
dsi/dsi_phy_hw_v3_0.o \
dsi/dsi_phy_hw_v4_0.o \
dsi/dsi_phy_timing_calc.o \
dsi/dsi_phy_timing_v2_0.o \
dsi/dsi_phy_timing_v3_0.o \
dsi/dsi_phy_timing_v4_0.o \
dsi/dsi_pll.o \
dsi/dsi_pll_5nm.o \
dsi/dsi_pll_10nm.o \
dsi/dsi_ctrl_hw_cmn.o \
dsi/dsi_ctrl_hw_1_4.o \
dsi/dsi_ctrl_hw_2_0.o \
dsi/dsi_ctrl_hw_2_2.o \
dsi/dsi_ctrl.o \
dsi/dsi_catalog.o \
dsi/dsi_drm.o \
dsi/dsi_display.o \
dsi/dsi_panel.o \
dsi/dsi_clk_manager.o \
dsi/dsi_display_test.o
msm_drm-$(CONFIG_DSI_PARSER) += dsi/dsi_parser.o
msm_drm-$(CONFIG_DRM_MSM) += \
msm_atomic.o \
msm_fb.o \
msm_iommu.o \
msm_drv.o \
msm_gem.o \
msm_gem_prime.o \
msm_gem_vma.o \
msm_smmu.o \
msm_cooling_device.o \
msm_prop.o
msm_drm-$(CONFIG_HDCP_QSEECOM) += ../hdcp/msm_hdcp.o \
msm_drm-$(CONFIG_MSM_SDE_ROTATOR) += ../rotator/sde_rotator_dev.o \
../rotator/sde_rotator_core.o \
../rotator/sde_rotator_base.o \
../rotator/sde_rotator_formats.o \
../rotator/sde_rotator_util.o \
../rotator/sde_rotator_io_util.o \
../rotator/sde_rotator_smmu.o \
../rotator/sde_rotator_r1_wb.o \
../rotator/sde_rotator_r1_pipe.o \
../rotator/sde_rotator_r1_ctl.o \
../rotator/sde_rotator_r1.o \
../rotator/sde_rotator_r3.o \
../rotator/sde_rotator_sync.o \
../rotator/sde_rotator_debug.o \
../rotator/sde_rotator_r1_debug.o \
../rotator/sde_rotator_r3_debug.o
obj-$(CONFIG_DISPLAY_BUILD) += msm_drm.o
obj-$(CONFIG_DRM_FBDEV_EMULATION) += msm_fbdev.o

View File

@ -0,0 +1,317 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
*/
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/soc/qcom/altmode-glink.h>
#include <linux/usb/dwc3-msm.h>
#include <linux/usb/pd_vdo.h>
#include "dp_altmode.h"
#include "dp_debug.h"
#include "sde_dbg.h"
#define ALTMODE_CONFIGURE_MASK (0x3f)
#define ALTMODE_HPD_STATE_MASK (0x40)
#define ALTMODE_HPD_IRQ_MASK (0x80)
struct dp_altmode_private {
bool forced_disconnect;
struct device *dev;
struct dp_hpd_cb *dp_cb;
struct dp_altmode dp_altmode;
struct altmode_client *amclient;
bool connected;
};
enum dp_altmode_pin_assignment {
DPAM_HPD_OUT,
DPAM_HPD_A,
DPAM_HPD_B,
DPAM_HPD_C,
DPAM_HPD_D,
DPAM_HPD_E,
DPAM_HPD_F,
};
static int dp_altmode_release_ss_lanes(struct dp_altmode_private *altmode,
bool multi_func)
{
int rc;
struct device_node *np;
struct device_node *usb_node;
struct platform_device *usb_pdev;
int timeout = 250;
if (!altmode || !altmode->dev) {
DP_ERR("invalid args\n");
return -EINVAL;
}
np = altmode->dev->of_node;
usb_node = of_parse_phandle(np, "usb-controller", 0);
if (!usb_node) {
DP_ERR("unable to get usb node\n");
return -EINVAL;
}
usb_pdev = of_find_device_by_node(usb_node);
if (!usb_pdev) {
of_node_put(usb_node);
DP_ERR("unable to get usb pdev\n");
return -EINVAL;
}
while (timeout) {
rc = dwc3_msm_release_ss_lane(&usb_pdev->dev, multi_func);
if (rc != -EBUSY)
break;
DP_WARN("USB busy, retry\n");
/* wait for hw recommended delay for usb */
msleep(20);
timeout--;
}
of_node_put(usb_node);
platform_device_put(usb_pdev);
if (rc)
DP_ERR("Error releasing SS lanes: %d\n", rc);
return rc;
}
static void dp_altmode_send_pan_ack(struct altmode_client *amclient,
u8 port_index)
{
int rc;
struct altmode_pan_ack_msg ack;
ack.cmd_type = ALTMODE_PAN_ACK;
ack.port_index = port_index;
rc = altmode_send_data(amclient, &ack, sizeof(ack));
if (rc < 0) {
DP_ERR("failed: %d\n", rc);
return;
}
DP_DEBUG("port=%d\n", port_index);
}
static int dp_altmode_notify(void *priv, void *data, size_t len)
{
int rc = 0;
struct dp_altmode_private *altmode =
(struct dp_altmode_private *) priv;
u8 port_index, dp_data, orientation;
u8 *payload = (u8 *) data;
u8 pin, hpd_state, hpd_irq;
bool force_multi_func = altmode->dp_altmode.base.force_multi_func;
port_index = payload[0];
orientation = payload[1];
dp_data = payload[8];
pin = dp_data & ALTMODE_CONFIGURE_MASK;
hpd_state = (dp_data & ALTMODE_HPD_STATE_MASK) >> 6;
hpd_irq = (dp_data & ALTMODE_HPD_IRQ_MASK) >> 7;
altmode->dp_altmode.base.hpd_high = !!hpd_state;
altmode->dp_altmode.base.hpd_irq = !!hpd_irq;
altmode->dp_altmode.base.multi_func = force_multi_func ? true :
!(pin == DPAM_HPD_C || pin == DPAM_HPD_E ||
pin == DPAM_HPD_OUT);
DP_DEBUG("payload=0x%x\n", dp_data);
DP_DEBUG("port_index=%d, orientation=%d, pin=%d, hpd_state=%d\n",
port_index, orientation, pin, hpd_state);
DP_DEBUG("multi_func=%d, hpd_high=%d, hpd_irq=%d\n",
altmode->dp_altmode.base.multi_func,
altmode->dp_altmode.base.hpd_high,
altmode->dp_altmode.base.hpd_irq);
DP_DEBUG("connected=%d\n", altmode->connected);
SDE_EVT32_EXTERNAL(dp_data, port_index, orientation, pin, hpd_state,
altmode->dp_altmode.base.multi_func,
altmode->dp_altmode.base.hpd_high,
altmode->dp_altmode.base.hpd_irq, altmode->connected);
if (!pin) {
/* Cable detach */
if (altmode->connected) {
altmode->connected = false;
altmode->dp_altmode.base.alt_mode_cfg_done = false;
altmode->dp_altmode.base.orientation = ORIENTATION_NONE;
if (altmode->dp_cb && altmode->dp_cb->disconnect)
altmode->dp_cb->disconnect(altmode->dev);
}
goto ack;
}
/* Configure */
if (!altmode->connected) {
altmode->connected = true;
altmode->dp_altmode.base.alt_mode_cfg_done = true;
altmode->forced_disconnect = false;
switch (orientation) {
case 0:
orientation = ORIENTATION_CC1;
break;
case 1:
orientation = ORIENTATION_CC2;
break;
case 2:
orientation = ORIENTATION_NONE;
break;
default:
orientation = ORIENTATION_NONE;
break;
}
altmode->dp_altmode.base.orientation = orientation;
rc = dp_altmode_release_ss_lanes(altmode,
altmode->dp_altmode.base.multi_func);
if (rc)
goto ack;
if (altmode->dp_cb && altmode->dp_cb->configure)
altmode->dp_cb->configure(altmode->dev);
goto ack;
}
/* Attention */
if (altmode->forced_disconnect)
goto ack;
if (altmode->dp_cb && altmode->dp_cb->attention)
altmode->dp_cb->attention(altmode->dev);
ack:
dp_altmode_send_pan_ack(altmode->amclient, port_index);
return rc;
}
static void dp_altmode_register(void *priv)
{
struct dp_altmode_private *altmode = priv;
struct altmode_client_data cd = {
.callback = &dp_altmode_notify,
};
cd.name = "displayport";
cd.svid = USB_SID_DISPLAYPORT;
cd.priv = altmode;
altmode->amclient = altmode_register_client(altmode->dev, &cd);
if (IS_ERR_OR_NULL(altmode->amclient))
DP_ERR("failed to register as client: %d\n",
PTR_ERR(altmode->amclient));
else
DP_DEBUG("success\n");
}
static int dp_altmode_simulate_connect(struct dp_hpd *dp_hpd, bool hpd)
{
struct dp_altmode *dp_altmode;
struct dp_altmode_private *altmode;
dp_altmode = container_of(dp_hpd, struct dp_altmode, base);
altmode = container_of(dp_altmode, struct dp_altmode_private,
dp_altmode);
dp_altmode->base.hpd_high = hpd;
altmode->forced_disconnect = !hpd;
altmode->dp_altmode.base.alt_mode_cfg_done = hpd;
if (hpd)
altmode->dp_cb->configure(altmode->dev);
else
altmode->dp_cb->disconnect(altmode->dev);
return 0;
}
static int dp_altmode_simulate_attention(struct dp_hpd *dp_hpd, int vdo)
{
struct dp_altmode *dp_altmode;
struct dp_altmode_private *altmode;
struct dp_altmode *status;
dp_altmode = container_of(dp_hpd, struct dp_altmode, base);
altmode = container_of(dp_altmode, struct dp_altmode_private,
dp_altmode);
status = &altmode->dp_altmode;
status->base.hpd_high = (vdo & BIT(7)) ? true : false;
status->base.hpd_irq = (vdo & BIT(8)) ? true : false;
if (altmode->dp_cb && altmode->dp_cb->attention)
altmode->dp_cb->attention(altmode->dev);
return 0;
}
struct dp_hpd *dp_altmode_get(struct device *dev, struct dp_hpd_cb *cb)
{
int rc = 0;
struct dp_altmode_private *altmode;
struct dp_altmode *dp_altmode;
if (!cb) {
DP_ERR("invalid cb data\n");
return ERR_PTR(-EINVAL);
}
altmode = kzalloc(sizeof(*altmode), GFP_KERNEL);
if (!altmode)
return ERR_PTR(-ENOMEM);
altmode->dev = dev;
altmode->dp_cb = cb;
dp_altmode = &altmode->dp_altmode;
dp_altmode->base.register_hpd = NULL;
dp_altmode->base.simulate_connect = dp_altmode_simulate_connect;
dp_altmode->base.simulate_attention = dp_altmode_simulate_attention;
rc = altmode_register_notifier(dev, dp_altmode_register, altmode);
if (rc < 0) {
DP_ERR("altmode probe notifier registration failed: %d\n", rc);
goto error;
}
DP_DEBUG("success\n");
return &dp_altmode->base;
error:
kfree(altmode);
return ERR_PTR(rc);
}
void dp_altmode_put(struct dp_hpd *dp_hpd)
{
struct dp_altmode *dp_altmode;
struct dp_altmode_private *altmode;
dp_altmode = container_of(dp_hpd, struct dp_altmode, base);
if (!dp_altmode)
return;
altmode = container_of(dp_altmode, struct dp_altmode_private,
dp_altmode);
altmode_deregister_client(altmode->amclient);
altmode_deregister_notifier(altmode->dev, altmode);
kfree(altmode);
}

View File

@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DP_ALTMODE_H_
#define _DP_ALTMODE_H_
#include <linux/types.h>
#include "dp_hpd.h"
struct device;
struct dp_altmode {
struct dp_hpd base;
};
struct dp_hpd *dp_altmode_get(struct device *dev, struct dp_hpd_cb *cb);
void dp_altmode_put(struct dp_hpd *pd);
#endif /* _DP_ALTMODE_H_ */

View File

@ -0,0 +1,899 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/of_platform.h>
#include <linux/msm_ext_display.h>
#include <drm/drm_dp_helper.h>
#include "dp_catalog.h"
#include "dp_audio.h"
#include "dp_panel.h"
#include "dp_debug.h"
struct dp_audio_private {
struct platform_device *ext_pdev;
struct platform_device *pdev;
struct dp_catalog_audio *catalog;
struct msm_ext_disp_init_data ext_audio_data;
struct dp_panel *panel;
bool ack_enabled;
atomic_t session_on;
bool engine_on;
u32 channels;
struct completion hpd_comp;
struct workqueue_struct *notify_workqueue;
struct delayed_work notify_delayed_work;
struct mutex ops_lock;
struct dp_audio dp_audio;
atomic_t acked;
};
static u32 dp_audio_get_header(struct dp_catalog_audio *catalog,
enum dp_catalog_audio_sdp_type sdp,
enum dp_catalog_audio_header_type header)
{
catalog->sdp_type = sdp;
catalog->sdp_header = header;
catalog->get_header(catalog);
return catalog->data;
}
static void dp_audio_set_header(struct dp_catalog_audio *catalog,
u32 data,
enum dp_catalog_audio_sdp_type sdp,
enum dp_catalog_audio_header_type header)
{
catalog->sdp_type = sdp;
catalog->sdp_header = header;
catalog->data = data;
catalog->set_header(catalog);
}
static void dp_audio_stream_sdp(struct dp_audio_private *audio)
{
struct dp_catalog_audio *catalog = audio->catalog;
u32 value, new_value;
u8 parity_byte;
/* Config header and parity byte 1 */
value = dp_audio_get_header(catalog,
DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_1);
value &= 0x0000ffff;
new_value = 0x02;
parity_byte = dp_header_get_parity(new_value);
value |= ((new_value << HEADER_BYTE_1_BIT)
| (parity_byte << PARITY_BYTE_1_BIT));
DP_DEBUG("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
value, parity_byte);
dp_audio_set_header(catalog, value,
DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_1);
/* Config header and parity byte 2 */
value = dp_audio_get_header(catalog,
DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_2);
value &= 0xffff0000;
new_value = 0x0;
parity_byte = dp_header_get_parity(new_value);
value |= ((new_value << HEADER_BYTE_2_BIT)
| (parity_byte << PARITY_BYTE_2_BIT));
DP_DEBUG("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
value, parity_byte);
dp_audio_set_header(catalog, value,
DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_2);
/* Config header and parity byte 3 */
value = dp_audio_get_header(catalog,
DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_3);
value &= 0x0000ffff;
new_value = audio->channels - 1;
parity_byte = dp_header_get_parity(new_value);
value |= ((new_value << HEADER_BYTE_3_BIT)
| (parity_byte << PARITY_BYTE_3_BIT));
DP_DEBUG("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
value, parity_byte);
dp_audio_set_header(catalog, value,
DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_3);
}
static void dp_audio_timestamp_sdp(struct dp_audio_private *audio)
{
struct dp_catalog_audio *catalog = audio->catalog;
u32 value, new_value;
u8 parity_byte;
/* Config header and parity byte 1 */
value = dp_audio_get_header(catalog,
DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_1);
value &= 0x0000ffff;
new_value = 0x1;
parity_byte = dp_header_get_parity(new_value);
value |= ((new_value << HEADER_BYTE_1_BIT)
| (parity_byte << PARITY_BYTE_1_BIT));
DP_DEBUG("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
value, parity_byte);
dp_audio_set_header(catalog, value,
DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_1);
/* Config header and parity byte 2 */
value = dp_audio_get_header(catalog,
DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_2);
value &= 0xffff0000;
new_value = 0x17;
parity_byte = dp_header_get_parity(new_value);
value |= ((new_value << HEADER_BYTE_2_BIT)
| (parity_byte << PARITY_BYTE_2_BIT));
DP_DEBUG("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
value, parity_byte);
dp_audio_set_header(catalog, value,
DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_2);
/* Config header and parity byte 3 */
value = dp_audio_get_header(catalog,
DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_3);
value &= 0x0000ffff;
new_value = (0x0 | (0x11 << 2));
parity_byte = dp_header_get_parity(new_value);
value |= ((new_value << HEADER_BYTE_3_BIT)
| (parity_byte << PARITY_BYTE_3_BIT));
DP_DEBUG("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
value, parity_byte);
dp_audio_set_header(catalog, value,
DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_3);
}
static void dp_audio_infoframe_sdp(struct dp_audio_private *audio)
{
struct dp_catalog_audio *catalog = audio->catalog;
u32 value, new_value;
u8 parity_byte;
/* Config header and parity byte 1 */
value = dp_audio_get_header(catalog,
DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_1);
value &= 0x0000ffff;
new_value = 0x84;
parity_byte = dp_header_get_parity(new_value);
value |= ((new_value << HEADER_BYTE_1_BIT)
| (parity_byte << PARITY_BYTE_1_BIT));
DP_DEBUG("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
value, parity_byte);
dp_audio_set_header(catalog, value,
DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_1);
/* Config header and parity byte 2 */
value = dp_audio_get_header(catalog,
DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_2);
value &= 0xffff0000;
new_value = 0x1b;
parity_byte = dp_header_get_parity(new_value);
value |= ((new_value << HEADER_BYTE_2_BIT)
| (parity_byte << PARITY_BYTE_2_BIT));
DP_DEBUG("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
value, parity_byte);
dp_audio_set_header(catalog, value,
DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_2);
/* Config header and parity byte 3 */
value = dp_audio_get_header(catalog,
DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_3);
value &= 0x0000ffff;
new_value = (0x0 | (0x11 << 2));
parity_byte = dp_header_get_parity(new_value);
value |= ((new_value << HEADER_BYTE_3_BIT)
| (parity_byte << PARITY_BYTE_3_BIT));
DP_DEBUG("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
new_value, parity_byte);
dp_audio_set_header(catalog, value,
DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_3);
}
static void dp_audio_copy_management_sdp(struct dp_audio_private *audio)
{
struct dp_catalog_audio *catalog = audio->catalog;
u32 value, new_value;
u8 parity_byte;
/* Config header and parity byte 1 */
value = dp_audio_get_header(catalog,
DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_1);
value &= 0x0000ffff;
new_value = 0x05;
parity_byte = dp_header_get_parity(new_value);
value |= ((new_value << HEADER_BYTE_1_BIT)
| (parity_byte << PARITY_BYTE_1_BIT));
DP_DEBUG("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
value, parity_byte);
dp_audio_set_header(catalog, value,
DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_1);
/* Config header and parity byte 2 */
value = dp_audio_get_header(catalog,
DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_2);
value &= 0xffff0000;
new_value = 0x0F;
parity_byte = dp_header_get_parity(new_value);
value |= ((new_value << HEADER_BYTE_2_BIT)
| (parity_byte << PARITY_BYTE_2_BIT));
DP_DEBUG("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
value, parity_byte);
dp_audio_set_header(catalog, value,
DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_2);
/* Config header and parity byte 3 */
value = dp_audio_get_header(catalog,
DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_3);
value &= 0x0000ffff;
new_value = 0x0;
parity_byte = dp_header_get_parity(new_value);
value |= ((new_value << HEADER_BYTE_3_BIT)
| (parity_byte << PARITY_BYTE_3_BIT));
DP_DEBUG("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
value, parity_byte);
dp_audio_set_header(catalog, value,
DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_3);
}
static void dp_audio_isrc_sdp(struct dp_audio_private *audio)
{
struct dp_catalog_audio *catalog = audio->catalog;
u32 value, new_value;
u8 parity_byte;
/* Config header and parity byte 1 */
value = dp_audio_get_header(catalog,
DP_AUDIO_SDP_ISRC, DP_AUDIO_SDP_HEADER_1);
value &= 0x0000ffff;
new_value = 0x06;
parity_byte = dp_header_get_parity(new_value);
value |= ((new_value << HEADER_BYTE_1_BIT)
| (parity_byte << PARITY_BYTE_1_BIT));
DP_DEBUG("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
value, parity_byte);
dp_audio_set_header(catalog, value,
DP_AUDIO_SDP_ISRC, DP_AUDIO_SDP_HEADER_1);
/* Config header and parity byte 2 */
value = dp_audio_get_header(catalog,
DP_AUDIO_SDP_ISRC, DP_AUDIO_SDP_HEADER_2);
value &= 0xffff0000;
new_value = 0x0F;
parity_byte = dp_header_get_parity(new_value);
value |= ((new_value << HEADER_BYTE_2_BIT)
| (parity_byte << PARITY_BYTE_2_BIT));
DP_DEBUG("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
value, parity_byte);
dp_audio_set_header(catalog, value,
DP_AUDIO_SDP_ISRC, DP_AUDIO_SDP_HEADER_2);
}
static void dp_audio_setup_sdp(struct dp_audio_private *audio)
{
if (!atomic_read(&audio->session_on)) {
DP_WARN("session inactive\n");
return;
}
/* always program stream 0 first before actual stream cfg */
audio->catalog->stream_id = DP_STREAM_0;
audio->catalog->config_sdp(audio->catalog);
if (audio->panel->stream_id == DP_STREAM_1) {
audio->catalog->stream_id = DP_STREAM_1;
audio->catalog->config_sdp(audio->catalog);
}
dp_audio_stream_sdp(audio);
dp_audio_timestamp_sdp(audio);
dp_audio_infoframe_sdp(audio);
dp_audio_copy_management_sdp(audio);
dp_audio_isrc_sdp(audio);
}
static void dp_audio_setup_acr(struct dp_audio_private *audio)
{
u32 select = 0;
struct dp_catalog_audio *catalog = audio->catalog;
if (!atomic_read(&audio->session_on)) {
DP_WARN("session inactive\n");
return;
}
switch (audio->dp_audio.bw_code) {
case DP_LINK_BW_1_62:
select = 0;
break;
case DP_LINK_BW_2_7:
select = 1;
break;
case DP_LINK_BW_5_4:
select = 2;
break;
case DP_LINK_BW_8_1:
select = 3;
break;
default:
DP_DEBUG("Unknown link rate\n");
select = 0;
break;
}
catalog->data = select;
catalog->config_acr(catalog);
}
static void dp_audio_enable(struct dp_audio_private *audio, bool enable)
{
struct dp_catalog_audio *catalog = audio->catalog;
audio->engine_on = enable;
if (!atomic_read(&audio->session_on)) {
DP_WARN("session inactive. enable=%d\n", enable);
return;
}
catalog->data = enable;
catalog->enable(catalog);
}
static struct dp_audio_private *dp_audio_get_data(struct platform_device *pdev)
{
struct msm_ext_disp_data *ext_data;
struct dp_audio *dp_audio;
if (!pdev) {
DP_ERR("invalid input\n");
return ERR_PTR(-ENODEV);
}
ext_data = platform_get_drvdata(pdev);
if (!ext_data) {
DP_ERR("invalid ext disp data\n");
return ERR_PTR(-EINVAL);
}
dp_audio = ext_data->intf_data;
if (!dp_audio) {
DP_ERR("invalid intf data\n");
return ERR_PTR(-EINVAL);
}
return container_of(dp_audio, struct dp_audio_private, dp_audio);
}
static int dp_audio_info_setup(struct platform_device *pdev,
struct msm_ext_disp_audio_setup_params *params)
{
int rc = 0;
struct dp_audio_private *audio;
audio = dp_audio_get_data(pdev);
if (IS_ERR(audio)) {
rc = PTR_ERR(audio);
return rc;
}
if (audio->dp_audio.tui_active) {
DP_DEBUG("TUI session active\n");
return 0;
}
mutex_lock(&audio->ops_lock);
audio->channels = params->num_of_channels;
if (audio->panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream id: %d\n",
audio->panel->stream_id);
rc = -EINVAL;
mutex_unlock(&audio->ops_lock);
return rc;
}
dp_audio_setup_sdp(audio);
dp_audio_setup_acr(audio);
dp_audio_enable(audio, true);
mutex_unlock(&audio->ops_lock);
DP_DEBUG("audio stream configured\n");
return rc;
}
static int dp_audio_get_edid_blk(struct platform_device *pdev,
struct msm_ext_disp_audio_edid_blk *blk)
{
int rc = 0;
struct dp_audio_private *audio;
struct sde_edid_ctrl *edid;
if (!blk) {
DP_ERR("invalid input\n");
return -EINVAL;
}
audio = dp_audio_get_data(pdev);
if (IS_ERR(audio)) {
rc = PTR_ERR(audio);
goto end;
}
if (!audio->panel || !audio->panel->edid_ctrl) {
DP_ERR("invalid panel data\n");
rc = -EINVAL;
goto end;
}
edid = audio->panel->edid_ctrl;
blk->audio_data_blk = edid->audio_data_block;
blk->audio_data_blk_size = edid->adb_size;
blk->spk_alloc_data_blk = edid->spkr_alloc_data_block;
blk->spk_alloc_data_blk_size = edid->sadb_size;
end:
return rc;
}
static int dp_audio_get_cable_status(struct platform_device *pdev, u32 vote)
{
int rc = 0;
struct dp_audio_private *audio;
audio = dp_audio_get_data(pdev);
if (IS_ERR(audio)) {
rc = PTR_ERR(audio);
goto end;
}
return atomic_read(&audio->session_on);
end:
return rc;
}
static int dp_audio_get_intf_id(struct platform_device *pdev)
{
int rc = 0;
struct dp_audio_private *audio;
audio = dp_audio_get_data(pdev);
if (IS_ERR(audio)) {
rc = PTR_ERR(audio);
goto end;
}
return EXT_DISPLAY_TYPE_DP;
end:
return rc;
}
static void dp_audio_teardown_done(struct platform_device *pdev)
{
struct dp_audio_private *audio;
audio = dp_audio_get_data(pdev);
if (IS_ERR(audio))
return;
if (audio->dp_audio.tui_active) {
DP_DEBUG("TUI session active\n");
return;
}
mutex_lock(&audio->ops_lock);
dp_audio_enable(audio, false);
mutex_unlock(&audio->ops_lock);
atomic_set(&audio->acked, 1);
complete_all(&audio->hpd_comp);
DP_DEBUG("audio engine disabled\n");
}
static int dp_audio_ack_done(struct platform_device *pdev, u32 ack)
{
int rc = 0, ack_hpd;
struct dp_audio_private *audio;
audio = dp_audio_get_data(pdev);
if (IS_ERR(audio)) {
rc = PTR_ERR(audio);
goto end;
}
if (ack & AUDIO_ACK_SET_ENABLE) {
audio->ack_enabled = ack & AUDIO_ACK_ENABLE ?
true : false;
DP_DEBUG("audio ack feature %s\n",
audio->ack_enabled ? "enabled" : "disabled");
goto end;
}
if (!audio->ack_enabled)
goto end;
ack_hpd = ack & AUDIO_ACK_CONNECT;
DP_DEBUG("acknowledging audio (%d)\n", ack_hpd);
if (!audio->engine_on) {
atomic_set(&audio->acked, 1);
complete_all(&audio->hpd_comp);
}
end:
return rc;
}
static int dp_audio_codec_ready(struct platform_device *pdev)
{
int rc = 0;
struct dp_audio_private *audio;
audio = dp_audio_get_data(pdev);
if (IS_ERR(audio)) {
DP_ERR("invalid input\n");
rc = PTR_ERR(audio);
goto end;
}
queue_delayed_work(audio->notify_workqueue,
&audio->notify_delayed_work, HZ/4);
end:
return rc;
}
static int dp_audio_register_ext_disp(struct dp_audio_private *audio)
{
int rc = 0;
struct device_node *pd = NULL;
const char *phandle = "qcom,ext-disp";
struct msm_ext_disp_init_data *ext;
struct msm_ext_disp_audio_codec_ops *ops;
ext = &audio->ext_audio_data;
ops = &ext->codec_ops;
ext->codec.type = EXT_DISPLAY_TYPE_DP;
ext->codec.ctrl_id = 0;
ext->codec.stream_id = audio->panel->stream_id;
ext->pdev = audio->pdev;
ext->intf_data = &audio->dp_audio;
ops->audio_info_setup = dp_audio_info_setup;
ops->get_audio_edid_blk = dp_audio_get_edid_blk;
ops->cable_status = dp_audio_get_cable_status;
ops->get_intf_id = dp_audio_get_intf_id;
ops->teardown_done = dp_audio_teardown_done;
ops->acknowledge = dp_audio_ack_done;
ops->ready = dp_audio_codec_ready;
if (!audio->pdev->dev.of_node) {
DP_ERR("cannot find audio dev.of_node\n");
rc = -ENODEV;
goto end;
}
pd = of_parse_phandle(audio->pdev->dev.of_node, phandle, 0);
if (!pd) {
DP_ERR("cannot parse %s handle\n", phandle);
rc = -ENODEV;
goto end;
}
audio->ext_pdev = of_find_device_by_node(pd);
if (!audio->ext_pdev) {
DP_ERR("cannot find %s pdev\n", phandle);
rc = -ENODEV;
goto end;
}
#if defined(CONFIG_MSM_EXT_DISPLAY)
rc = msm_ext_disp_register_intf(audio->ext_pdev, ext);
if (rc)
DP_ERR("failed to register disp\n");
#endif
end:
if (pd)
of_node_put(pd);
return rc;
}
static int dp_audio_deregister_ext_disp(struct dp_audio_private *audio)
{
int rc = 0;
struct device_node *pd = NULL;
const char *phandle = "qcom,ext-disp";
struct msm_ext_disp_init_data *ext;
ext = &audio->ext_audio_data;
if (!audio->pdev->dev.of_node) {
DP_ERR("cannot find audio dev.of_node\n");
rc = -ENODEV;
goto end;
}
pd = of_parse_phandle(audio->pdev->dev.of_node, phandle, 0);
if (!pd) {
DP_ERR("cannot parse %s handle\n", phandle);
rc = -ENODEV;
goto end;
}
audio->ext_pdev = of_find_device_by_node(pd);
if (!audio->ext_pdev) {
DP_ERR("cannot find %s pdev\n", phandle);
rc = -ENODEV;
goto end;
}
#if defined(CONFIG_MSM_EXT_DISPLAY)
rc = msm_ext_disp_deregister_intf(audio->ext_pdev, ext);
if (rc)
DP_ERR("failed to deregister disp\n");
#endif
end:
return rc;
}
static int dp_audio_notify(struct dp_audio_private *audio, u32 state)
{
int rc = 0;
struct msm_ext_disp_init_data *ext = &audio->ext_audio_data;
atomic_set(&audio->acked, 0);
if (!ext->intf_ops.audio_notify) {
DP_ERR("audio notify not defined\n");
goto end;
}
reinit_completion(&audio->hpd_comp);
rc = ext->intf_ops.audio_notify(audio->ext_pdev,
&ext->codec, state);
if (rc)
goto end;
if (atomic_read(&audio->acked))
goto end;
if (state == EXT_DISPLAY_CABLE_DISCONNECT && !audio->engine_on)
goto end;
if (state == EXT_DISPLAY_CABLE_CONNECT)
goto end;
rc = wait_for_completion_timeout(&audio->hpd_comp, HZ * 4);
if (!rc) {
DP_ERR("timeout. state=%d err=%d\n", state, rc);
rc = -ETIMEDOUT;
goto end;
}
DP_DEBUG("success\n");
end:
return rc;
}
static int dp_audio_config(struct dp_audio_private *audio, u32 state)
{
int rc = 0;
struct msm_ext_disp_init_data *ext = &audio->ext_audio_data;
if (!ext || !ext->intf_ops.audio_config) {
DP_ERR("audio_config not defined\n");
goto end;
}
/*
* DP Audio sets default STREAM_0 only, other streams are
* set by audio driver based on the hardware/software support.
*/
if (audio->panel->stream_id == DP_STREAM_0) {
rc = ext->intf_ops.audio_config(audio->ext_pdev,
&ext->codec, state);
if (rc)
DP_ERR("failed to config audio, err=%d\n",
rc);
}
end:
return rc;
}
static int dp_audio_on(struct dp_audio *dp_audio)
{
int rc = 0;
struct dp_audio_private *audio;
struct msm_ext_disp_init_data *ext;
if (!dp_audio) {
DP_ERR("invalid input\n");
return -EINVAL;
}
audio = container_of(dp_audio, struct dp_audio_private, dp_audio);
if (IS_ERR(audio)) {
DP_ERR("invalid input\n");
return -EINVAL;
}
dp_audio_register_ext_disp(audio);
ext = &audio->ext_audio_data;
atomic_set(&audio->session_on, 1);
rc = dp_audio_config(audio, EXT_DISPLAY_CABLE_CONNECT);
if (rc)
goto end;
rc = dp_audio_notify(audio, EXT_DISPLAY_CABLE_CONNECT);
if (rc)
goto end;
DP_DEBUG("success\n");
end:
return rc;
}
static int dp_audio_off(struct dp_audio *dp_audio)
{
int rc = 0;
struct dp_audio_private *audio;
struct msm_ext_disp_init_data *ext;
bool work_pending = false;
if (!dp_audio) {
DP_ERR("invalid input\n");
return -EINVAL;
}
audio = container_of(dp_audio, struct dp_audio_private, dp_audio);
if (!atomic_read(&audio->session_on)) {
DP_DEBUG("audio already off\n");
return rc;
}
ext = &audio->ext_audio_data;
work_pending = cancel_delayed_work_sync(&audio->notify_delayed_work);
if (work_pending)
DP_DEBUG("pending notification work completed\n");
rc = dp_audio_notify(audio, EXT_DISPLAY_CABLE_DISCONNECT);
if (rc)
goto end;
DP_DEBUG("success\n");
end:
dp_audio_config(audio, EXT_DISPLAY_CABLE_DISCONNECT);
atomic_set(&audio->session_on, 0);
audio->engine_on = false;
dp_audio_deregister_ext_disp(audio);
return rc;
}
static void dp_audio_notify_work_fn(struct work_struct *work)
{
struct dp_audio_private *audio;
struct delayed_work *dw = to_delayed_work(work);
audio = container_of(dw, struct dp_audio_private, notify_delayed_work);
dp_audio_notify(audio, EXT_DISPLAY_CABLE_CONNECT);
}
static int dp_audio_create_notify_workqueue(struct dp_audio_private *audio)
{
audio->notify_workqueue = create_workqueue("sdm_dp_audio_notify");
if (IS_ERR_OR_NULL(audio->notify_workqueue)) {
DP_ERR("Error creating notify_workqueue\n");
return -EPERM;
}
INIT_DELAYED_WORK(&audio->notify_delayed_work, dp_audio_notify_work_fn);
return 0;
}
static void dp_audio_destroy_notify_workqueue(struct dp_audio_private *audio)
{
if (audio->notify_workqueue)
destroy_workqueue(audio->notify_workqueue);
}
struct dp_audio *dp_audio_get(struct platform_device *pdev,
struct dp_panel *panel,
struct dp_catalog_audio *catalog)
{
int rc = 0;
struct dp_audio_private *audio;
struct dp_audio *dp_audio;
if (!pdev || !panel || !catalog) {
DP_ERR("invalid input\n");
rc = -EINVAL;
goto error;
}
audio = devm_kzalloc(&pdev->dev, sizeof(*audio), GFP_KERNEL);
if (!audio) {
rc = -ENOMEM;
goto error;
}
rc = dp_audio_create_notify_workqueue(audio);
if (rc)
goto error_notify_workqueue;
init_completion(&audio->hpd_comp);
audio->pdev = pdev;
audio->panel = panel;
audio->catalog = catalog;
atomic_set(&audio->acked, 0);
dp_audio = &audio->dp_audio;
mutex_init(&audio->ops_lock);
dp_audio->on = dp_audio_on;
dp_audio->off = dp_audio_off;
catalog->init(catalog);
return dp_audio;
error_notify_workqueue:
devm_kfree(&pdev->dev, audio);
error:
return ERR_PTR(rc);
}
void dp_audio_put(struct dp_audio *dp_audio)
{
struct dp_audio_private *audio;
if (!dp_audio)
return;
audio = container_of(dp_audio, struct dp_audio_private, dp_audio);
mutex_destroy(&audio->ops_lock);
dp_audio_destroy_notify_workqueue(audio);
devm_kfree(&audio->pdev->dev, audio);
}

View File

@ -0,0 +1,74 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DP_AUDIO_H_
#define _DP_AUDIO_H_
#include <linux/platform_device.h>
#include "dp_panel.h"
#include "dp_catalog.h"
/**
* struct dp_audio
* @lane_count: number of lanes configured in current session
* @bw_code: link rate's bandwidth code for current session
* @tui_active: set to true if TUI is active in the system
*/
struct dp_audio {
u32 lane_count;
u32 bw_code;
bool tui_active;
/**
* on()
*
* Notifies user mode clients that DP is powered on, and that audio
* playback can start on the external display.
*
* @dp_audio: an instance of struct dp_audio.
*
* Returns the error code in case of failure, 0 in success case.
*/
int (*on)(struct dp_audio *dp_audio);
/**
* off()
*
* Notifies user mode clients that DP is shutting down, and audio
* playback should be stopped on the external display.
*
* @dp_audio: an instance of struct dp_audio.
*
* Returns the error code in case of failure, 0 in success case.
*/
int (*off)(struct dp_audio *dp_audio);
};
/**
* dp_audio_get()
*
* Creates and instance of dp audio.
*
* @pdev: caller's platform device instance.
* @panel: an instance of dp_panel module.
* @catalog: an instance of dp_catalog_audio module.
*
* Returns the error code in case of failure, otherwize
* an instance of newly created dp_module.
*/
struct dp_audio *dp_audio_get(struct platform_device *pdev,
struct dp_panel *panel,
struct dp_catalog_audio *catalog);
/**
* dp_audio_put()
*
* Cleans the dp_audio instance.
*
* @dp_audio: an instance of dp_audio.
*/
void dp_audio_put(struct dp_audio *dp_audio);
#endif /* _DP_AUDIO_H_ */

View File

@ -0,0 +1,867 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/soc/qcom/fsa4480-i2c.h>
#include <linux/delay.h>
#include "dp_aux.h"
#include "dp_hpd.h"
#include "dp_debug.h"
#define DP_AUX_ENUM_STR(x) #x
enum {
DP_AUX_DATA_INDEX_WRITE = BIT(31),
};
struct dp_aux_private {
struct device *dev;
struct dp_aux dp_aux;
struct dp_catalog_aux *catalog;
struct dp_aux_cfg *cfg;
struct device_node *aux_switch_node;
struct mutex mutex;
struct completion comp;
struct drm_dp_aux drm_aux;
bool cmd_busy;
bool native;
bool read;
bool no_send_addr;
bool no_send_stop;
bool enabled;
u32 offset;
u32 segment;
u32 aux_error_num;
u32 retry_cnt;
atomic_t aborted;
u8 *dpcd;
u8 *edid;
};
#ifdef CONFIG_DYNAMIC_DEBUG
static void dp_aux_hex_dump(struct drm_dp_aux *drm_aux,
struct drm_dp_aux_msg *msg)
{
char prefix[64];
int i, linelen, remaining = msg->size;
const int rowsize = 16;
u8 linebuf[64];
struct dp_aux_private *aux = container_of(drm_aux,
struct dp_aux_private, drm_aux);
snprintf(prefix, sizeof(prefix), "%s %s %4xh(%2zu): ",
aux->native ? "NAT" : "I2C",
aux->read ? "RD" : "WR",
msg->address, msg->size);
for (i = 0; i < msg->size; i += rowsize) {
linelen = min(remaining, rowsize);
remaining -= rowsize;
hex_dump_to_buffer(msg->buffer + i, linelen, rowsize, 1,
linebuf, sizeof(linebuf), false);
DP_DEBUG("%s%s\n", prefix, linebuf);
}
}
#else
static void dp_aux_hex_dump(struct drm_dp_aux *drm_aux,
struct drm_dp_aux_msg *msg)
{
}
#endif
static char *dp_aux_get_error(u32 aux_error)
{
switch (aux_error) {
case DP_AUX_ERR_NONE:
return DP_AUX_ENUM_STR(DP_AUX_ERR_NONE);
case DP_AUX_ERR_ADDR:
return DP_AUX_ENUM_STR(DP_AUX_ERR_ADDR);
case DP_AUX_ERR_TOUT:
return DP_AUX_ENUM_STR(DP_AUX_ERR_TOUT);
case DP_AUX_ERR_NACK:
return DP_AUX_ENUM_STR(DP_AUX_ERR_NACK);
case DP_AUX_ERR_DEFER:
return DP_AUX_ENUM_STR(DP_AUX_ERR_DEFER);
case DP_AUX_ERR_NACK_DEFER:
return DP_AUX_ENUM_STR(DP_AUX_ERR_NACK_DEFER);
default:
return "unknown";
}
}
static u32 dp_aux_write(struct dp_aux_private *aux,
struct drm_dp_aux_msg *msg)
{
u32 data[4], reg, len;
u8 *msgdata = msg->buffer;
int const aux_cmd_fifo_len = 128;
int i = 0;
if (aux->read)
len = 4;
else
len = msg->size + 4;
/*
* cmd fifo only has depth of 144 bytes
* limit buf length to 128 bytes here
*/
if (len > aux_cmd_fifo_len) {
DP_ERR("buf len error\n");
return 0;
}
/* Pack cmd and write to HW */
data[0] = (msg->address >> 16) & 0xf; /* addr[19:16] */
if (aux->read)
data[0] |= BIT(4); /* R/W */
data[1] = (msg->address >> 8) & 0xff; /* addr[15:8] */
data[2] = msg->address & 0xff; /* addr[7:0] */
data[3] = (msg->size - 1) & 0xff; /* len[7:0] */
for (i = 0; i < len; i++) {
reg = (i < 4) ? data[i] : msgdata[i - 4];
reg = ((reg) << 8) & 0x0000ff00; /* index = 0, write */
if (i == 0)
reg |= DP_AUX_DATA_INDEX_WRITE;
aux->catalog->data = reg;
aux->catalog->write_data(aux->catalog);
}
aux->catalog->clear_trans(aux->catalog, false);
aux->catalog->clear_hw_interrupts(aux->catalog);
reg = 0; /* Transaction number == 1 */
if (!aux->native) { /* i2c */
reg |= BIT(8);
if (aux->no_send_addr)
reg |= BIT(10);
if (aux->no_send_stop)
reg |= BIT(11);
}
reg |= BIT(9);
aux->catalog->data = reg;
aux->catalog->write_trans(aux->catalog);
return len;
}
static int dp_aux_cmd_fifo_tx(struct dp_aux_private *aux,
struct drm_dp_aux_msg *msg)
{
u32 ret = 0, len = 0, timeout;
int const aux_timeout_ms = HZ/4;
reinit_completion(&aux->comp);
len = dp_aux_write(aux, msg);
if (len == 0) {
DP_ERR("DP AUX write failed\n");
return -EINVAL;
}
timeout = wait_for_completion_timeout(&aux->comp, aux_timeout_ms);
if (!timeout) {
DP_ERR("aux %s timeout\n", (aux->read ? "read" : "write"));
return -ETIMEDOUT;
}
if (aux->aux_error_num == DP_AUX_ERR_NONE) {
ret = len;
} else {
pr_err_ratelimited("aux err: %s\n",
dp_aux_get_error(aux->aux_error_num));
ret = -EINVAL;
}
return ret;
}
static void dp_aux_cmd_fifo_rx(struct dp_aux_private *aux,
struct drm_dp_aux_msg *msg)
{
u32 data;
u8 *dp;
u32 i, actual_i;
u32 len = msg->size;
aux->catalog->clear_trans(aux->catalog, true);
data = 0;
data |= DP_AUX_DATA_INDEX_WRITE; /* INDEX_WRITE */
data |= BIT(0); /* read */
aux->catalog->data = data;
aux->catalog->write_data(aux->catalog);
dp = msg->buffer;
/* discard first byte */
data = aux->catalog->read_data(aux->catalog);
for (i = 0; i < len; i++) {
data = aux->catalog->read_data(aux->catalog);
*dp++ = (u8)((data >> 8) & 0xff);
actual_i = (data >> 16) & 0xFF;
if (i != actual_i)
DP_WARN("Index mismatch: expected %d, found %d\n",
i, actual_i);
}
}
static void dp_aux_native_handler(struct dp_aux_private *aux)
{
u32 isr = aux->catalog->isr;
if (isr & DP_INTR_AUX_I2C_DONE)
aux->aux_error_num = DP_AUX_ERR_NONE;
else if (isr & DP_INTR_WRONG_ADDR)
aux->aux_error_num = DP_AUX_ERR_ADDR;
else if (isr & DP_INTR_TIMEOUT)
aux->aux_error_num = DP_AUX_ERR_TOUT;
if (isr & DP_INTR_NACK_DEFER)
aux->aux_error_num = DP_AUX_ERR_NACK;
if (isr & DP_INTR_AUX_ERROR) {
aux->aux_error_num = DP_AUX_ERR_PHY;
aux->catalog->clear_hw_interrupts(aux->catalog);
}
complete(&aux->comp);
}
static void dp_aux_i2c_handler(struct dp_aux_private *aux)
{
u32 isr = aux->catalog->isr;
if (isr & DP_INTR_AUX_I2C_DONE) {
if (isr & (DP_INTR_I2C_NACK | DP_INTR_I2C_DEFER))
aux->aux_error_num = DP_AUX_ERR_NACK;
else
aux->aux_error_num = DP_AUX_ERR_NONE;
} else {
if (isr & DP_INTR_WRONG_ADDR)
aux->aux_error_num = DP_AUX_ERR_ADDR;
else if (isr & DP_INTR_TIMEOUT)
aux->aux_error_num = DP_AUX_ERR_TOUT;
if (isr & DP_INTR_NACK_DEFER)
aux->aux_error_num = DP_AUX_ERR_NACK_DEFER;
if (isr & DP_INTR_I2C_NACK)
aux->aux_error_num = DP_AUX_ERR_NACK;
if (isr & DP_INTR_I2C_DEFER)
aux->aux_error_num = DP_AUX_ERR_DEFER;
if (isr & DP_INTR_AUX_ERROR) {
aux->aux_error_num = DP_AUX_ERR_PHY;
aux->catalog->clear_hw_interrupts(aux->catalog);
}
}
complete(&aux->comp);
}
static void dp_aux_isr(struct dp_aux *dp_aux)
{
struct dp_aux_private *aux;
if (!dp_aux) {
DP_ERR("invalid input\n");
return;
}
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
aux->catalog->get_irq(aux->catalog, aux->cmd_busy);
if (!aux->cmd_busy)
return;
if (aux->native)
dp_aux_native_handler(aux);
else
dp_aux_i2c_handler(aux);
}
static void dp_aux_reconfig(struct dp_aux *dp_aux)
{
struct dp_aux_private *aux;
if (!dp_aux) {
DP_ERR("invalid input\n");
return;
}
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
aux->catalog->update_aux_cfg(aux->catalog,
aux->cfg, PHY_AUX_CFG1);
aux->catalog->reset(aux->catalog);
}
static void dp_aux_abort_transaction(struct dp_aux *dp_aux, bool abort)
{
struct dp_aux_private *aux;
if (!dp_aux) {
DP_ERR("invalid input\n");
return;
}
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
atomic_set(&aux->aborted, abort);
}
static void dp_aux_update_offset_and_segment(struct dp_aux_private *aux,
struct drm_dp_aux_msg *input_msg)
{
u32 const edid_address = 0x50;
u32 const segment_address = 0x30;
bool i2c_read = input_msg->request &
(DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
u8 *data = NULL;
if (aux->native || i2c_read || ((input_msg->address != edid_address) &&
(input_msg->address != segment_address)))
return;
data = input_msg->buffer;
if (input_msg->address == segment_address)
aux->segment = *data;
else
aux->offset = *data;
}
/**
* dp_aux_transfer_helper() - helper function for EDID read transactions
*
* @aux: DP AUX private structure
* @input_msg: input message from DRM upstream APIs
* @send_seg: send the seg to sink
*
* return: void
*
* This helper function is used to fix EDID reads for non-compliant
* sinks that do not handle the i2c middle-of-transaction flag correctly.
*/
static void dp_aux_transfer_helper(struct dp_aux_private *aux,
struct drm_dp_aux_msg *input_msg, bool send_seg)
{
struct drm_dp_aux_msg helper_msg;
u32 const message_size = 0x10;
u32 const segment_address = 0x30;
u32 const edid_block_length = 0x80;
bool i2c_mot = input_msg->request & DP_AUX_I2C_MOT;
bool i2c_read = input_msg->request &
(DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
if (!i2c_mot || !i2c_read || (input_msg->size == 0))
return;
/*
* Sending the segment value and EDID offset will be performed
* from the DRM upstream EDID driver for each block. Avoid
* duplicate AUX transactions related to this while reading the
* first 16 bytes of each block.
*/
if (!(aux->offset % edid_block_length) || !send_seg)
goto end;
aux->read = false;
aux->cmd_busy = true;
aux->no_send_addr = true;
aux->no_send_stop = true;
/*
* Send the segment address for i2c reads for segment > 0 and for which
* the middle-of-transaction flag is set. This is required to support
* EDID reads of more than 2 blocks as the segment address is reset to 0
* since we are overriding the middle-of-transaction flag for read
* transactions.
*/
if (aux->segment) {
memset(&helper_msg, 0, sizeof(helper_msg));
helper_msg.address = segment_address;
helper_msg.buffer = &aux->segment;
helper_msg.size = 1;
dp_aux_cmd_fifo_tx(aux, &helper_msg);
}
/*
* Send the offset address for every i2c read in which the
* middle-of-transaction flag is set. This will ensure that the sink
* will update its read pointer and return the correct portion of the
* EDID buffer in the subsequent i2c read trasntion triggered in the
* native AUX transfer function.
*/
memset(&helper_msg, 0, sizeof(helper_msg));
helper_msg.address = input_msg->address;
helper_msg.buffer = &aux->offset;
helper_msg.size = 1;
dp_aux_cmd_fifo_tx(aux, &helper_msg);
end:
aux->offset += message_size;
if (aux->offset == 0x80 || aux->offset == 0x100)
aux->segment = 0x0; /* reset segment at end of block */
}
static int dp_aux_transfer_ready(struct dp_aux_private *aux,
struct drm_dp_aux_msg *msg, bool send_seg)
{
int ret = 0;
int const aux_cmd_native_max = 16;
int const aux_cmd_i2c_max = 128;
if (atomic_read(&aux->aborted)) {
ret = -ETIMEDOUT;
goto error;
}
aux->native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
/* Ignore address only message */
if ((msg->size == 0) || (msg->buffer == NULL)) {
msg->reply = aux->native ?
DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
goto error;
}
/* msg sanity check */
if ((aux->native && (msg->size > aux_cmd_native_max)) ||
(msg->size > aux_cmd_i2c_max)) {
DP_ERR("%s: invalid msg: size(%zu), request(%x)\n",
__func__, msg->size, msg->request);
ret = -EINVAL;
goto error;
}
dp_aux_update_offset_and_segment(aux, msg);
dp_aux_transfer_helper(aux, msg, send_seg);
aux->read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
if (aux->read) {
aux->no_send_addr = true;
aux->no_send_stop = false;
} else {
aux->no_send_addr = true;
aux->no_send_stop = true;
}
aux->cmd_busy = true;
error:
return ret;
}
static ssize_t dp_aux_transfer_debug(struct drm_dp_aux *drm_aux,
struct drm_dp_aux_msg *msg)
{
u32 timeout;
ssize_t ret;
struct dp_aux_private *aux = container_of(drm_aux,
struct dp_aux_private, drm_aux);
mutex_lock(&aux->mutex);
ret = dp_aux_transfer_ready(aux, msg, false);
if (ret)
goto end;
aux->aux_error_num = DP_AUX_ERR_NONE;
if (!aux->dpcd || !aux->edid) {
DP_ERR("invalid aux/dpcd structure\n");
goto end;
}
if ((msg->address + msg->size) > SZ_4K) {
DP_DEBUG("invalid dpcd access: addr=0x%x, size=0x%lx\n",
msg->address, msg->size);
goto address_error;
}
if (aux->native) {
mutex_lock(aux->dp_aux.access_lock);
aux->dp_aux.reg = msg->address;
aux->dp_aux.read = aux->read;
aux->dp_aux.size = msg->size;
if (!aux->read)
memcpy(aux->dpcd + msg->address,
msg->buffer, msg->size);
reinit_completion(&aux->comp);
mutex_unlock(aux->dp_aux.access_lock);
timeout = wait_for_completion_timeout(&aux->comp, HZ * 2);
if (!timeout) {
DP_ERR("%s timeout: 0x%x\n",
aux->read ? "read" : "write",
msg->address);
atomic_set(&aux->aborted, 1);
ret = -ETIMEDOUT;
goto end;
}
mutex_lock(aux->dp_aux.access_lock);
if (aux->read)
memcpy(msg->buffer, aux->dpcd + msg->address,
msg->size);
mutex_unlock(aux->dp_aux.access_lock);
aux->aux_error_num = DP_AUX_ERR_NONE;
} else {
if (aux->read && msg->address == 0x50) {
memcpy(msg->buffer,
aux->edid + aux->offset - 16,
msg->size);
}
}
if (aux->aux_error_num == DP_AUX_ERR_NONE) {
dp_aux_hex_dump(drm_aux, msg);
if (!aux->read)
memset(msg->buffer, 0, msg->size);
msg->reply = aux->native ?
DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
} else {
/* Reply defer to retry */
msg->reply = aux->native ?
DP_AUX_NATIVE_REPLY_DEFER : DP_AUX_I2C_REPLY_DEFER;
}
ret = msg->size;
goto end;
address_error:
memset(msg->buffer, 0, msg->size);
ret = msg->size;
end:
if (ret == -ETIMEDOUT)
aux->dp_aux.state |= DP_STATE_AUX_TIMEOUT;
aux->dp_aux.reg = 0xFFFF;
aux->dp_aux.read = true;
aux->dp_aux.size = 0;
mutex_unlock(&aux->mutex);
return ret;
}
/*
* This function does the real job to process an AUX transaction.
* It will call aux_reset() function to reset the AUX channel,
* if the waiting is timeout.
*/
static ssize_t dp_aux_transfer(struct drm_dp_aux *drm_aux,
struct drm_dp_aux_msg *msg)
{
ssize_t ret;
int const retry_count = 5;
struct dp_aux_private *aux = container_of(drm_aux,
struct dp_aux_private, drm_aux);
mutex_lock(&aux->mutex);
ret = dp_aux_transfer_ready(aux, msg, true);
if (ret)
goto unlock_exit;
if (!aux->cmd_busy) {
ret = msg->size;
goto unlock_exit;
}
ret = dp_aux_cmd_fifo_tx(aux, msg);
if ((ret < 0) && !atomic_read(&aux->aborted)) {
aux->retry_cnt++;
if (!(aux->retry_cnt % retry_count))
aux->catalog->update_aux_cfg(aux->catalog,
aux->cfg, PHY_AUX_CFG1);
aux->catalog->reset(aux->catalog);
goto unlock_exit;
} else if (ret < 0) {
goto unlock_exit;
}
if (aux->aux_error_num == DP_AUX_ERR_NONE) {
if (aux->read)
dp_aux_cmd_fifo_rx(aux, msg);
dp_aux_hex_dump(drm_aux, msg);
msg->reply = aux->native ?
DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
} else {
/* Reply defer to retry */
msg->reply = aux->native ?
DP_AUX_NATIVE_REPLY_DEFER : DP_AUX_I2C_REPLY_DEFER;
}
/* Return requested size for success or retry */
ret = msg->size;
aux->retry_cnt = 0;
unlock_exit:
aux->cmd_busy = false;
mutex_unlock(&aux->mutex);
return ret;
}
static void dp_aux_reset_phy_config_indices(struct dp_aux_cfg *aux_cfg)
{
int i = 0;
for (i = 0; i < PHY_AUX_CFG_MAX; i++)
aux_cfg[i].current_index = 0;
}
static void dp_aux_init(struct dp_aux *dp_aux, struct dp_aux_cfg *aux_cfg)
{
struct dp_aux_private *aux;
if (!dp_aux || !aux_cfg) {
DP_ERR("invalid input\n");
return;
}
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
if (aux->enabled)
return;
dp_aux_reset_phy_config_indices(aux_cfg);
aux->catalog->setup(aux->catalog, aux_cfg);
aux->catalog->reset(aux->catalog);
aux->catalog->enable(aux->catalog, true);
atomic_set(&aux->aborted, 0);
aux->retry_cnt = 0;
aux->enabled = true;
}
static void dp_aux_deinit(struct dp_aux *dp_aux)
{
struct dp_aux_private *aux;
if (!dp_aux) {
DP_ERR("invalid input\n");
return;
}
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
if (!aux->enabled)
return;
atomic_set(&aux->aborted, 1);
aux->catalog->enable(aux->catalog, false);
aux->enabled = false;
}
static int dp_aux_register(struct dp_aux *dp_aux)
{
struct dp_aux_private *aux;
int ret = 0;
if (!dp_aux) {
DP_ERR("invalid input\n");
ret = -EINVAL;
goto exit;
}
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
aux->drm_aux.name = "sde_dp_aux";
aux->drm_aux.dev = aux->dev;
aux->drm_aux.transfer = dp_aux_transfer;
atomic_set(&aux->aborted, 1);
ret = drm_dp_aux_register(&aux->drm_aux);
if (ret) {
DP_ERR("%s: failed to register drm aux: %d\n", __func__, ret);
goto exit;
}
dp_aux->drm_aux = &aux->drm_aux;
exit:
return ret;
}
static void dp_aux_deregister(struct dp_aux *dp_aux)
{
struct dp_aux_private *aux;
if (!dp_aux) {
DP_ERR("invalid input\n");
return;
}
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
drm_dp_aux_unregister(&aux->drm_aux);
}
static void dp_aux_dpcd_updated(struct dp_aux *dp_aux)
{
struct dp_aux_private *aux;
if (!dp_aux) {
DP_ERR("invalid input\n");
return;
}
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
/* make sure wait has started */
usleep_range(20, 30);
complete(&aux->comp);
}
static void dp_aux_set_sim_mode(struct dp_aux *dp_aux, bool en,
u8 *edid, u8 *dpcd)
{
struct dp_aux_private *aux;
if (!dp_aux) {
DP_ERR("invalid input\n");
return;
}
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
mutex_lock(&aux->mutex);
aux->edid = edid;
aux->dpcd = dpcd;
if (en) {
atomic_set(&aux->aborted, 0);
aux->drm_aux.transfer = dp_aux_transfer_debug;
} else {
aux->drm_aux.transfer = dp_aux_transfer;
}
mutex_unlock(&aux->mutex);
}
static int dp_aux_configure_aux_switch(struct dp_aux *dp_aux,
bool enable, int orientation)
{
struct dp_aux_private *aux;
int rc = 0;
enum fsa_function event = FSA_USBC_DISPLAYPORT_DISCONNECTED;
if (!dp_aux) {
DP_ERR("invalid input\n");
rc = -EINVAL;
goto end;
}
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
if (!aux->aux_switch_node) {
DP_DEBUG("undefined fsa4480 handle\n");
rc = -EINVAL;
goto end;
}
if (enable) {
switch (orientation) {
case ORIENTATION_CC1:
event = FSA_USBC_ORIENTATION_CC1;
break;
case ORIENTATION_CC2:
event = FSA_USBC_ORIENTATION_CC2;
break;
default:
DP_ERR("invalid orientation\n");
rc = -EINVAL;
goto end;
}
}
DP_DEBUG("enable=%d, orientation=%d, event=%d\n",
enable, orientation, event);
rc = fsa4480_switch_event(aux->aux_switch_node, event);
if (rc)
DP_ERR("failed to configure fsa4480 i2c device (%d)\n", rc);
end:
return rc;
}
struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
struct dp_parser *parser, struct device_node *aux_switch)
{
int rc = 0;
struct dp_aux_private *aux;
struct dp_aux *dp_aux;
if (!catalog || !parser ||
(!parser->no_aux_switch &&
!aux_switch &&
!parser->gpio_aux_switch)) {
DP_ERR("invalid input\n");
rc = -ENODEV;
goto error;
}
aux = devm_kzalloc(dev, sizeof(*aux), GFP_KERNEL);
if (!aux) {
rc = -ENOMEM;
goto error;
}
init_completion(&aux->comp);
aux->cmd_busy = false;
mutex_init(&aux->mutex);
aux->dev = dev;
aux->catalog = catalog;
aux->cfg = parser->aux_cfg;
aux->aux_switch_node = aux_switch;
dp_aux = &aux->dp_aux;
aux->retry_cnt = 0;
aux->dp_aux.reg = 0xFFFF;
dp_aux->isr = dp_aux_isr;
dp_aux->init = dp_aux_init;
dp_aux->deinit = dp_aux_deinit;
dp_aux->drm_aux_register = dp_aux_register;
dp_aux->drm_aux_deregister = dp_aux_deregister;
dp_aux->reconfig = dp_aux_reconfig;
dp_aux->abort = dp_aux_abort_transaction;
dp_aux->dpcd_updated = dp_aux_dpcd_updated;
dp_aux->set_sim_mode = dp_aux_set_sim_mode;
dp_aux->aux_switch = dp_aux_configure_aux_switch;
return dp_aux;
error:
return ERR_PTR(rc);
}
void dp_aux_put(struct dp_aux *dp_aux)
{
struct dp_aux_private *aux;
if (!dp_aux)
return;
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
mutex_destroy(&aux->mutex);
devm_kfree(aux->dev, aux);
}

View File

@ -0,0 +1,63 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DP_AUX_H_
#define _DP_AUX_H_
#include "dp_catalog.h"
#include "drm_dp_helper.h"
#define DP_STATE_NOTIFICATION_SENT BIT(0)
#define DP_STATE_TRAIN_1_STARTED BIT(1)
#define DP_STATE_TRAIN_1_SUCCEEDED BIT(2)
#define DP_STATE_TRAIN_1_FAILED BIT(3)
#define DP_STATE_TRAIN_2_STARTED BIT(4)
#define DP_STATE_TRAIN_2_SUCCEEDED BIT(5)
#define DP_STATE_TRAIN_2_FAILED BIT(6)
#define DP_STATE_CTRL_POWERED_ON BIT(7)
#define DP_STATE_CTRL_POWERED_OFF BIT(8)
#define DP_STATE_LINK_MAINTENANCE_STARTED BIT(9)
#define DP_STATE_LINK_MAINTENANCE_COMPLETED BIT(10)
#define DP_STATE_LINK_MAINTENANCE_FAILED BIT(11)
#define DP_STATE_AUX_TIMEOUT BIT(12)
#define DP_STATE_PLL_LOCKED BIT(13)
enum dp_aux_error {
DP_AUX_ERR_NONE = 0,
DP_AUX_ERR_ADDR = -1,
DP_AUX_ERR_TOUT = -2,
DP_AUX_ERR_NACK = -3,
DP_AUX_ERR_DEFER = -4,
DP_AUX_ERR_NACK_DEFER = -5,
DP_AUX_ERR_PHY = -6,
};
struct dp_aux {
u32 reg;
u32 size;
u32 state;
bool read;
struct mutex *access_lock;
struct drm_dp_aux *drm_aux;
int (*drm_aux_register)(struct dp_aux *aux);
void (*drm_aux_deregister)(struct dp_aux *aux);
void (*isr)(struct dp_aux *aux);
void (*init)(struct dp_aux *aux, struct dp_aux_cfg *aux_cfg);
void (*deinit)(struct dp_aux *aux);
void (*reconfig)(struct dp_aux *aux);
void (*abort)(struct dp_aux *aux, bool abort);
void (*dpcd_updated)(struct dp_aux *aux);
void (*set_sim_mode)(struct dp_aux *aux, bool en, u8 *edid, u8 *dpcd);
int (*aux_switch)(struct dp_aux *aux, bool enable, int orientation);
};
struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
struct dp_parser *parser, struct device_node *aux_switch);
void dp_aux_put(struct dp_aux *aux);
#endif /*__DP_AUX_H_*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,339 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DP_CATALOG_H_
#define _DP_CATALOG_H_
#include <drm/drm_dp_helper.h>
#include <drm/sde_drm.h>
#include "dp_parser.h"
/* interrupts */
#define DP_INTR_HPD BIT(0)
#define DP_INTR_AUX_I2C_DONE BIT(3)
#define DP_INTR_WRONG_ADDR BIT(6)
#define DP_INTR_TIMEOUT BIT(9)
#define DP_INTR_NACK_DEFER BIT(12)
#define DP_INTR_WRONG_DATA_CNT BIT(15)
#define DP_INTR_I2C_NACK BIT(18)
#define DP_INTR_I2C_DEFER BIT(21)
#define DP_INTR_PLL_UNLOCKED BIT(24)
#define DP_INTR_AUX_ERROR BIT(27)
#define DP_INTR_READY_FOR_VIDEO BIT(0)
#define DP_INTR_IDLE_PATTERN_SENT BIT(3)
#define DP_INTR_FRAME_END BIT(6)
#define DP_INTR_CRC_UPDATED BIT(9)
#define DP_INTR_MST_DP0_VCPF_SENT BIT(0)
#define DP_INTR_MST_DP1_VCPF_SENT BIT(3)
#define DP_MAX_TIME_SLOTS 64
/* stream id */
enum dp_stream_id {
DP_STREAM_0,
DP_STREAM_1,
DP_STREAM_MAX,
};
struct dp_catalog_vsc_sdp_colorimetry {
struct dp_sdp_header header;
u8 data[32];
};
struct dp_catalog_aux {
u32 data;
u32 isr;
u32 (*read_data)(struct dp_catalog_aux *aux);
int (*write_data)(struct dp_catalog_aux *aux);
int (*write_trans)(struct dp_catalog_aux *aux);
int (*clear_trans)(struct dp_catalog_aux *aux, bool read);
void (*reset)(struct dp_catalog_aux *aux);
void (*enable)(struct dp_catalog_aux *aux, bool enable);
void (*update_aux_cfg)(struct dp_catalog_aux *aux,
struct dp_aux_cfg *cfg, enum dp_phy_aux_config_type type);
void (*setup)(struct dp_catalog_aux *aux,
struct dp_aux_cfg *aux_cfg);
void (*get_irq)(struct dp_catalog_aux *aux, bool cmd_busy);
void (*clear_hw_interrupts)(struct dp_catalog_aux *aux);
};
struct dp_catalog_ctrl {
u32 isr;
u32 isr5;
void (*state_ctrl)(struct dp_catalog_ctrl *ctrl, u32 state);
void (*config_ctrl)(struct dp_catalog_ctrl *ctrl, u8 ln_cnt);
void (*lane_mapping)(struct dp_catalog_ctrl *ctrl, bool flipped,
char *lane_map);
void (*lane_pnswap)(struct dp_catalog_ctrl *ctrl, u8 ln_pnswap);
void (*mainlink_ctrl)(struct dp_catalog_ctrl *ctrl, bool enable);
void (*set_pattern)(struct dp_catalog_ctrl *ctrl, u32 pattern);
void (*reset)(struct dp_catalog_ctrl *ctrl);
void (*usb_reset)(struct dp_catalog_ctrl *ctrl, bool flip);
bool (*mainlink_ready)(struct dp_catalog_ctrl *ctrl);
void (*enable_irq)(struct dp_catalog_ctrl *ctrl, bool enable);
void (*phy_reset)(struct dp_catalog_ctrl *ctrl);
void (*phy_lane_cfg)(struct dp_catalog_ctrl *ctrl, bool flipped,
u8 lane_cnt);
void (*update_vx_px)(struct dp_catalog_ctrl *ctrl, u8 v_level,
u8 p_level, bool high);
void (*get_interrupt)(struct dp_catalog_ctrl *ctrl);
u32 (*read_hdcp_status)(struct dp_catalog_ctrl *ctrl);
void (*send_phy_pattern)(struct dp_catalog_ctrl *ctrl,
u32 pattern);
u32 (*read_phy_pattern)(struct dp_catalog_ctrl *ctrl);
void (*mst_config)(struct dp_catalog_ctrl *ctrl, bool enable);
void (*trigger_act)(struct dp_catalog_ctrl *ctrl);
void (*read_act_complete_sts)(struct dp_catalog_ctrl *ctrl, bool *sts);
void (*channel_alloc)(struct dp_catalog_ctrl *ctrl,
u32 ch, u32 ch_start_timeslot, u32 tot_ch_cnt);
void (*update_rg)(struct dp_catalog_ctrl *ctrl, u32 ch, u32 x_int,
u32 y_frac_enum);
void (*channel_dealloc)(struct dp_catalog_ctrl *ctrl,
u32 ch, u32 ch_start_timeslot, u32 tot_ch_cnt);
void (*fec_config)(struct dp_catalog_ctrl *ctrl, bool enable);
void (*mainlink_levels)(struct dp_catalog_ctrl *ctrl, u8 lane_cnt);
int (*late_phy_init)(struct dp_catalog_ctrl *ctrl,
u8 lane_cnt, bool flipped);
};
struct dp_catalog_hpd {
void (*config_hpd)(struct dp_catalog_hpd *hpd, bool en);
u32 (*get_interrupt)(struct dp_catalog_hpd *hpd);
};
#define HEADER_BYTE_2_BIT 0
#define PARITY_BYTE_2_BIT 8
#define HEADER_BYTE_1_BIT 16
#define PARITY_BYTE_1_BIT 24
#define HEADER_BYTE_3_BIT 16
#define PARITY_BYTE_3_BIT 24
enum dp_catalog_audio_sdp_type {
DP_AUDIO_SDP_STREAM,
DP_AUDIO_SDP_TIMESTAMP,
DP_AUDIO_SDP_INFOFRAME,
DP_AUDIO_SDP_COPYMANAGEMENT,
DP_AUDIO_SDP_ISRC,
DP_AUDIO_SDP_MAX,
};
enum dp_catalog_audio_header_type {
DP_AUDIO_SDP_HEADER_1,
DP_AUDIO_SDP_HEADER_2,
DP_AUDIO_SDP_HEADER_3,
DP_AUDIO_SDP_HEADER_MAX,
};
struct dp_catalog_audio {
enum dp_catalog_audio_sdp_type sdp_type;
enum dp_catalog_audio_header_type sdp_header;
u32 data;
enum dp_stream_id stream_id;
void (*init)(struct dp_catalog_audio *audio);
void (*enable)(struct dp_catalog_audio *audio);
void (*config_acr)(struct dp_catalog_audio *audio);
void (*config_sdp)(struct dp_catalog_audio *audio);
void (*set_header)(struct dp_catalog_audio *audio);
void (*get_header)(struct dp_catalog_audio *audio);
};
struct dp_dsc_cfg_data {
bool dsc_en;
bool continuous_pps;
char pps[128];
u32 pps_len;
u32 pps_word[32];
u32 pps_word_len;
u8 parity[32];
u8 parity_len;
u32 parity_word[8];
u32 parity_word_len;
u32 slice_per_pkt;
u32 bytes_per_pkt;
u32 eol_byte_num;
u32 be_in_lane;
u32 dto_en;
u32 dto_n;
u32 dto_d;
u32 dto_count;
};
struct dp_catalog_panel {
u32 total;
u32 sync_start;
u32 width_blanking;
u32 dp_active;
u8 *spd_vendor_name;
u8 *spd_product_description;
struct dp_catalog_vsc_sdp_colorimetry vsc_colorimetry;
struct dp_sdp_header dhdr_vsif_sdp;
struct dp_sdp_header shdr_if_sdp;
struct drm_msm_ext_hdr_metadata hdr_meta;
/* TPG */
u32 hsync_period;
u32 vsync_period;
u32 display_v_start;
u32 display_v_end;
u32 v_sync_width;
u32 hsync_ctl;
u32 display_hctl;
/* TU */
u32 dp_tu;
u32 valid_boundary;
u32 valid_boundary2;
u32 misc_val;
enum dp_stream_id stream_id;
bool widebus_en;
struct dp_dsc_cfg_data dsc;
int (*timing_cfg)(struct dp_catalog_panel *panel);
void (*config_hdr)(struct dp_catalog_panel *panel, bool en,
u32 dhdr_max_pkts, bool flush);
void (*config_sdp)(struct dp_catalog_panel *panel, bool en);
int (*set_colorspace)(struct dp_catalog_panel *panel,
bool vsc_supported);
void (*tpg_config)(struct dp_catalog_panel *panel, bool enable);
void (*config_spd)(struct dp_catalog_panel *panel);
void (*config_misc)(struct dp_catalog_panel *panel);
void (*config_msa)(struct dp_catalog_panel *panel,
u32 rate, u32 stream_rate_khz);
void (*update_transfer_unit)(struct dp_catalog_panel *panel);
void (*config_ctrl)(struct dp_catalog_panel *panel, u32 cfg);
void (*config_dto)(struct dp_catalog_panel *panel, bool ack);
void (*dsc_cfg)(struct dp_catalog_panel *panel);
void (*pps_flush)(struct dp_catalog_panel *panel);
void (*dhdr_flush)(struct dp_catalog_panel *panel);
bool (*dhdr_busy)(struct dp_catalog_panel *panel);
};
struct dp_catalog;
struct dp_catalog_sub {
u32 (*read)(struct dp_catalog *dp_catalog,
struct dp_io_data *io_data, u32 offset);
void (*write)(struct dp_catalog *dp_catalog,
struct dp_io_data *io_data, u32 offset, u32 data);
void (*put)(struct dp_catalog *catalog);
};
struct dp_catalog_io {
struct dp_io_data *dp_ahb;
struct dp_io_data *dp_aux;
struct dp_io_data *dp_link;
struct dp_io_data *dp_p0;
struct dp_io_data *dp_phy;
struct dp_io_data *dp_ln_tx0;
struct dp_io_data *dp_ln_tx1;
struct dp_io_data *dp_mmss_cc;
struct dp_io_data *dp_pll;
struct dp_io_data *usb3_dp_com;
struct dp_io_data *hdcp_physical;
struct dp_io_data *dp_p1;
struct dp_io_data *dp_tcsr;
};
struct dp_catalog {
struct dp_catalog_aux aux;
struct dp_catalog_ctrl ctrl;
struct dp_catalog_audio audio;
struct dp_catalog_panel panel;
struct dp_catalog_hpd hpd;
struct dp_catalog_sub *sub;
void (*set_exe_mode)(struct dp_catalog *dp_catalog, char *mode);
int (*get_reg_dump)(struct dp_catalog *dp_catalog,
char *mode, u8 **out_buf, u32 *out_buf_len);
};
static inline u8 dp_ecc_get_g0_value(u8 data)
{
u8 c[4];
u8 g[4];
u8 ret_data = 0;
u8 i;
for (i = 0; i < 4; i++)
c[i] = (data >> i) & 0x01;
g[0] = c[3];
g[1] = c[0] ^ c[3];
g[2] = c[1];
g[3] = c[2];
for (i = 0; i < 4; i++)
ret_data = ((g[i] & 0x01) << i) | ret_data;
return ret_data;
}
static inline u8 dp_ecc_get_g1_value(u8 data)
{
u8 c[4];
u8 g[4];
u8 ret_data = 0;
u8 i;
for (i = 0; i < 4; i++)
c[i] = (data >> i) & 0x01;
g[0] = c[0] ^ c[3];
g[1] = c[0] ^ c[1] ^ c[3];
g[2] = c[1] ^ c[2];
g[3] = c[2] ^ c[3];
for (i = 0; i < 4; i++)
ret_data = ((g[i] & 0x01) << i) | ret_data;
return ret_data;
}
static inline u8 dp_header_get_parity(u32 data)
{
u8 x0 = 0;
u8 x1 = 0;
u8 ci = 0;
u8 iData = 0;
u8 i = 0;
u8 parity_byte;
u8 num_byte = (data > 0xFF) ? 8 : 2;
for (i = 0; i < num_byte; i++) {
iData = (data >> i*4) & 0xF;
ci = iData ^ x1;
x1 = x0 ^ dp_ecc_get_g1_value(ci);
x0 = dp_ecc_get_g0_value(ci);
}
parity_byte = x1 | (x0 << 4);
return parity_byte;
}
struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser);
void dp_catalog_put(struct dp_catalog *catalog);
struct dp_catalog_sub *dp_catalog_get_v420(struct device *dev,
struct dp_catalog *catalog, struct dp_catalog_io *io);
struct dp_catalog_sub *dp_catalog_get_v200(struct device *dev,
struct dp_catalog *catalog, struct dp_catalog_io *io);
#endif /* _DP_CATALOG_H_ */

View File

@ -0,0 +1,272 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*/
#include <linux/delay.h>
#include "dp_catalog.h"
#include "dp_reg.h"
#include "dp_debug.h"
#define dp_catalog_get_priv_v200(x) ({ \
struct dp_catalog *catalog; \
catalog = container_of(x, struct dp_catalog, x); \
container_of(catalog->sub, \
struct dp_catalog_private_v200, sub); \
})
#define dp_read(x) ({ \
catalog->sub.read(catalog->dpc, io_data, x); \
})
#define dp_write(x, y) ({ \
catalog->sub.write(catalog->dpc, io_data, x, y); \
})
struct dp_catalog_private_v200 {
struct device *dev;
struct dp_catalog_io *io;
struct dp_catalog *dpc;
struct dp_catalog_sub sub;
};
static void dp_catalog_aux_clear_hw_int_v200(struct dp_catalog_aux *aux)
{
struct dp_catalog_private_v200 *catalog;
struct dp_io_data *io_data;
u32 data = 0;
if (!aux) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv_v200(aux);
io_data = catalog->io->dp_phy;
data = dp_read(DP_PHY_AUX_INTERRUPT_STATUS_V200);
dp_write(DP_PHY_AUX_INTERRUPT_CLEAR_V200, 0x1f);
wmb(); /* make sure 0x1f is written before next write */
dp_write(DP_PHY_AUX_INTERRUPT_CLEAR_V200, 0x9f);
wmb(); /* make sure 0x9f is written before next write */
dp_write(DP_PHY_AUX_INTERRUPT_CLEAR_V200, 0);
wmb(); /* make sure register is cleared */
}
static void dp_catalog_aux_setup_v200(struct dp_catalog_aux *aux,
struct dp_aux_cfg *cfg)
{
struct dp_catalog_private_v200 *catalog;
struct dp_io_data *io_data;
int i = 0, sw_reset = 0;
if (!aux || !cfg) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv_v200(aux);
io_data = catalog->io->dp_ahb;
sw_reset = dp_read(DP_SW_RESET);
sw_reset |= BIT(0);
dp_write(DP_SW_RESET, sw_reset);
usleep_range(1000, 1010); /* h/w recommended delay */
sw_reset &= ~BIT(0);
dp_write(DP_SW_RESET, sw_reset);
dp_write(DP_PHY_CTRL, 0x4); /* bit 2 */
udelay(1000);
dp_write(DP_PHY_CTRL, 0x0); /* bit 2 */
wmb(); /* make sure programming happened */
io_data = catalog->io->dp_tcsr;
dp_write(0x4c, 0x1); /* bit 0 & 2 */
wmb(); /* make sure programming happened */
io_data = catalog->io->dp_phy;
dp_write(DP_PHY_PD_CTL, 0x3c);
wmb(); /* make sure PD programming happened */
dp_write(DP_PHY_PD_CTL, 0x3d);
wmb(); /* make sure PD programming happened */
/* DP AUX CFG register programming */
io_data = catalog->io->dp_phy;
for (i = 0; i < PHY_AUX_CFG_MAX; i++)
dp_write(cfg[i].offset, cfg[i].lut[cfg[i].current_index]);
dp_write(DP_PHY_AUX_INTERRUPT_MASK_V200, 0x1F);
wmb(); /* make sure AUX configuration is done before enabling it */
}
static void dp_catalog_panel_config_msa_v200(struct dp_catalog_panel *panel,
u32 rate, u32 stream_rate_khz)
{
u32 pixel_m, pixel_n;
u32 mvid, nvid;
u32 const nvid_fixed = 0x8000;
u32 const link_rate_hbr2 = 540000;
u32 const link_rate_hbr3 = 810000;
struct dp_catalog_private_v200 *catalog;
struct dp_io_data *io_data;
u32 strm_reg_off = 0;
u32 mvid_reg_off = 0, nvid_reg_off = 0;
if (!panel) {
DP_ERR("invalid input\n");
return;
}
if (panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream_id:%d\n", panel->stream_id);
return;
}
catalog = dp_catalog_get_priv_v200(panel);
io_data = catalog->io->dp_mmss_cc;
if (panel->stream_id == DP_STREAM_1)
strm_reg_off = MMSS_DP_PIXEL1_M_V200 -
MMSS_DP_PIXEL_M_V200;
pixel_m = dp_read(MMSS_DP_PIXEL_M_V200 + strm_reg_off);
pixel_n = dp_read(MMSS_DP_PIXEL_N_V200 + strm_reg_off);
DP_DEBUG("pixel_m=0x%x, pixel_n=0x%x\n", pixel_m, pixel_n);
mvid = (pixel_m & 0xFFFF) * 5;
nvid = (0xFFFF & (~pixel_n)) + (pixel_m & 0xFFFF);
if (nvid < nvid_fixed) {
u32 temp;
temp = (nvid_fixed / nvid) * nvid;
mvid = (nvid_fixed / nvid) * mvid;
nvid = temp;
}
DP_DEBUG("rate = %d\n", rate);
if (panel->widebus_en)
mvid <<= 1;
if (link_rate_hbr2 == rate)
nvid *= 2;
if (link_rate_hbr3 == rate)
nvid *= 3;
io_data = catalog->io->dp_link;
if (panel->stream_id == DP_STREAM_1) {
mvid_reg_off = DP1_SOFTWARE_MVID - DP_SOFTWARE_MVID;
nvid_reg_off = DP1_SOFTWARE_NVID - DP_SOFTWARE_NVID;
}
DP_DEBUG("mvid=0x%x, nvid=0x%x\n", mvid, nvid);
dp_write(DP_SOFTWARE_MVID + mvid_reg_off, mvid);
dp_write(DP_SOFTWARE_NVID + nvid_reg_off, nvid);
}
static void dp_catalog_ctrl_lane_mapping_v200(struct dp_catalog_ctrl *ctrl,
bool flipped, char *lane_map)
{
struct dp_catalog_private_v200 *catalog;
struct dp_io_data *io_data;
u8 l_map[4] = { 0 }, i = 0, j = 0;
u32 lane_map_reg = 0;
if (!ctrl) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv_v200(ctrl);
io_data = catalog->io->dp_link;
/* For flip case, swap phy lanes with ML0 and ML3, ML1 and ML2 */
if (flipped) {
for (i = 0; i < DP_MAX_PHY_LN; i++) {
if (lane_map[i] == DP_ML0) {
for (j = 0; j < DP_MAX_PHY_LN; j++) {
if (lane_map[j] == DP_ML3) {
l_map[i] = DP_ML3;
l_map[j] = DP_ML0;
break;
}
}
} else if (lane_map[i] == DP_ML1) {
for (j = 0; j < DP_MAX_PHY_LN; j++) {
if (lane_map[j] == DP_ML2) {
l_map[i] = DP_ML2;
l_map[j] = DP_ML1;
break;
}
}
}
}
} else {
/* Normal orientation */
for (i = 0; i < DP_MAX_PHY_LN; i++)
l_map[i] = lane_map[i];
}
lane_map_reg = ((l_map[3]&3)<<6)|((l_map[2]&3)<<4)|((l_map[1]&3)<<2)
|(l_map[0]&3);
dp_write(DP_LOGICAL2PHYSICAL_LANE_MAPPING, lane_map_reg);
}
static void dp_catalog_ctrl_usb_reset_v200(struct dp_catalog_ctrl *ctrl,
bool flip)
{
}
static void dp_catalog_put_v200(struct dp_catalog *catalog)
{
struct dp_catalog_private_v200 *catalog_priv;
if (!catalog)
return;
catalog_priv = container_of(catalog->sub,
struct dp_catalog_private_v200, sub);
devm_kfree(catalog_priv->dev, catalog_priv);
}
struct dp_catalog_sub *dp_catalog_get_v200(struct device *dev,
struct dp_catalog *catalog, struct dp_catalog_io *io)
{
struct dp_catalog_private_v200 *catalog_priv;
if (!dev || !catalog) {
DP_ERR("invalid input\n");
return ERR_PTR(-EINVAL);
}
catalog_priv = devm_kzalloc(dev, sizeof(*catalog_priv), GFP_KERNEL);
if (!catalog_priv)
return ERR_PTR(-ENOMEM);
catalog_priv->dev = dev;
catalog_priv->io = io;
catalog_priv->dpc = catalog;
catalog_priv->sub.put = dp_catalog_put_v200;
catalog->aux.clear_hw_interrupts = dp_catalog_aux_clear_hw_int_v200;
catalog->aux.setup = dp_catalog_aux_setup_v200;
catalog->panel.config_msa = dp_catalog_panel_config_msa_v200;
catalog->ctrl.lane_mapping = dp_catalog_ctrl_lane_mapping_v200;
catalog->ctrl.usb_reset = dp_catalog_ctrl_usb_reset_v200;
return &catalog_priv->sub;
}

View File

@ -0,0 +1,362 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#include "dp_catalog.h"
#include "dp_reg.h"
#include "dp_debug.h"
#define dp_catalog_get_priv_v420(x) ({ \
struct dp_catalog *catalog; \
catalog = container_of(x, struct dp_catalog, x); \
container_of(catalog->sub, \
struct dp_catalog_private_v420, sub); \
})
#define dp_read(x) ({ \
catalog->sub.read(catalog->dpc, io_data, x); \
})
#define dp_write(x, y) ({ \
catalog->sub.write(catalog->dpc, io_data, x, y); \
})
#define MAX_VOLTAGE_LEVELS 4
#define MAX_PRE_EMP_LEVELS 4
static u8 const vm_pre_emphasis[MAX_VOLTAGE_LEVELS][MAX_PRE_EMP_LEVELS] = {
{0x00, 0x0E, 0x16, 0xFF}, /* pe0, 0 db */
{0x00, 0x0E, 0x16, 0xFF}, /* pe1, 3.5 db */
{0x00, 0x0E, 0xFF, 0xFF}, /* pe2, 6.0 db */
{0xFF, 0xFF, 0xFF, 0xFF} /* pe3, 9.5 db */
};
/* voltage swing, 0.2v and 1.0v are not support */
static u8 const vm_voltage_swing[MAX_VOLTAGE_LEVELS][MAX_PRE_EMP_LEVELS] = {
{0x07, 0x0F, 0x16, 0xFF}, /* sw0, 0.4v */
{0x11, 0x1E, 0x1F, 0xFF}, /* sw1, 0.6 v */
{0x1A, 0x1F, 0xFF, 0xFF}, /* sw1, 0.8 v */
{0xFF, 0xFF, 0xFF, 0xFF} /* sw1, 1.2 v, optional */
};
static u8 const dp_pre_emp_hbr2_hbr3[MAX_VOLTAGE_LEVELS][MAX_PRE_EMP_LEVELS] = {
{0x00, 0x0C, 0x15, 0x1B}, /* pe0, 0 db */
{0x02, 0x0E, 0x16, 0xFF}, /* pe1, 3.5 db */
{0x02, 0x11, 0xFF, 0xFF}, /* pe2, 6.0 db */
{0x04, 0xFF, 0xFF, 0xFF} /* pe3, 9.5 db */
};
static u8 const dp_swing_hbr2_hbr3[MAX_VOLTAGE_LEVELS][MAX_PRE_EMP_LEVELS] = {
{0x02, 0x12, 0x16, 0x1A}, /* sw0, 0.4v */
{0x09, 0x19, 0x1F, 0xFF}, /* sw1, 0.6v */
{0x10, 0x1F, 0xFF, 0xFF}, /* sw1, 0.8v */
{0x1F, 0xFF, 0xFF, 0xFF} /* sw1, 1.2v */
};
static u8 const dp_pre_emp_hbr_rbr[MAX_VOLTAGE_LEVELS][MAX_PRE_EMP_LEVELS] = {
{0x00, 0x0E, 0x15, 0x1B}, /* pe0, 0 db */
{0x00, 0x0E, 0x15, 0xFF}, /* pe1, 3.5 db */
{0x00, 0x0E, 0xFF, 0xFF}, /* pe2, 6.0 db */
{0x04, 0xFF, 0xFF, 0xFF} /* pe3, 9.5 db */
};
static u8 const dp_swing_hbr_rbr[MAX_VOLTAGE_LEVELS][MAX_PRE_EMP_LEVELS] = {
{0x08, 0x0F, 0x16, 0x1F}, /* sw0, 0.4v */
{0x11, 0x1E, 0x1F, 0xFF}, /* sw1, 0.6v */
{0x16, 0x1F, 0xFF, 0xFF}, /* sw1, 0.8v */
{0x1F, 0xFF, 0xFF, 0xFF} /* sw1, 1.2v */
};
struct dp_catalog_private_v420 {
struct device *dev;
struct dp_catalog_sub sub;
struct dp_catalog_io *io;
struct dp_catalog *dpc;
};
static void dp_catalog_aux_setup_v420(struct dp_catalog_aux *aux,
struct dp_aux_cfg *cfg)
{
struct dp_catalog_private_v420 *catalog;
struct dp_io_data *io_data;
int i = 0;
if (!aux || !cfg) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv_v420(aux);
io_data = catalog->io->dp_phy;
dp_write(DP_PHY_PD_CTL, 0x67);
wmb(); /* make sure PD programming happened */
/* Turn on BIAS current for PHY/PLL */
io_data = catalog->io->dp_pll;
dp_write(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x17);
wmb(); /* make sure BIAS programming happened */
io_data = catalog->io->dp_phy;
/* DP AUX CFG register programming */
for (i = 0; i < PHY_AUX_CFG_MAX; i++) {
DP_DEBUG("%s: offset=0x%08x, value=0x%08x\n",
dp_phy_aux_config_type_to_string(i),
cfg[i].offset, cfg[i].lut[cfg[i].current_index]);
dp_write(cfg[i].offset, cfg[i].lut[cfg[i].current_index]);
}
wmb(); /* make sure DP AUX CFG programming happened */
dp_write(DP_PHY_AUX_INTERRUPT_MASK_V420, 0x1F);
}
static void dp_catalog_aux_clear_hw_int_v420(struct dp_catalog_aux *aux)
{
struct dp_catalog_private_v420 *catalog;
struct dp_io_data *io_data;
u32 data = 0;
if (!aux) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv_v420(aux);
io_data = catalog->io->dp_phy;
data = dp_read(DP_PHY_AUX_INTERRUPT_STATUS_V420);
dp_write(DP_PHY_AUX_INTERRUPT_CLEAR_V420, 0x1f);
wmb(); /* make sure 0x1f is written before next write */
dp_write(DP_PHY_AUX_INTERRUPT_CLEAR_V420, 0x9f);
wmb(); /* make sure 0x9f is written before next write */
dp_write(DP_PHY_AUX_INTERRUPT_CLEAR_V420, 0);
wmb(); /* make sure register is cleared */
}
static void dp_catalog_panel_config_msa_v420(struct dp_catalog_panel *panel,
u32 rate, u32 stream_rate_khz)
{
u32 pixel_m, pixel_n;
u32 mvid, nvid, reg_off = 0, mvid_off = 0, nvid_off = 0;
u32 const nvid_fixed = 0x8000;
u32 const link_rate_hbr2 = 540000;
u32 const link_rate_hbr3 = 810000;
struct dp_catalog_private_v420 *catalog;
struct dp_io_data *io_data;
if (!panel || !rate) {
DP_ERR("invalid input\n");
return;
}
if (panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream id:%d\n", panel->stream_id);
return;
}
catalog = dp_catalog_get_priv_v420(panel);
io_data = catalog->io->dp_mmss_cc;
if (panel->stream_id == DP_STREAM_1)
reg_off = MMSS_DP_PIXEL1_M_V420 - MMSS_DP_PIXEL_M_V420;
pixel_m = dp_read(MMSS_DP_PIXEL_M_V420 + reg_off);
pixel_n = dp_read(MMSS_DP_PIXEL_N_V420 + reg_off);
DP_DEBUG("pixel_m=0x%x, pixel_n=0x%x\n", pixel_m, pixel_n);
mvid = (pixel_m & 0xFFFF) * 5;
nvid = (0xFFFF & (~pixel_n)) + (pixel_m & 0xFFFF);
if (nvid < nvid_fixed) {
u32 temp;
temp = (nvid_fixed / nvid) * nvid;
mvid = (nvid_fixed / nvid) * mvid;
nvid = temp;
}
DP_DEBUG("rate = %d\n", rate);
if (panel->widebus_en)
mvid <<= 1;
if (link_rate_hbr2 == rate)
nvid *= 2;
if (link_rate_hbr3 == rate)
nvid *= 3;
io_data = catalog->io->dp_link;
if (panel->stream_id == DP_STREAM_1) {
mvid_off = DP1_SOFTWARE_MVID - DP_SOFTWARE_MVID;
nvid_off = DP1_SOFTWARE_NVID - DP_SOFTWARE_NVID;
}
DP_DEBUG("mvid=0x%x, nvid=0x%x\n", mvid, nvid);
dp_write(DP_SOFTWARE_MVID + mvid_off, mvid);
dp_write(DP_SOFTWARE_NVID + nvid_off, nvid);
}
static void dp_catalog_ctrl_phy_lane_cfg_v420(struct dp_catalog_ctrl *ctrl,
bool flipped, u8 ln_cnt)
{
u32 info = 0x0;
struct dp_catalog_private_v420 *catalog;
struct dp_io_data *io_data;
u8 orientation = BIT(!!flipped);
if (!ctrl) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv_v420(ctrl);
io_data = catalog->io->dp_phy;
info |= (ln_cnt & 0x0F);
info |= ((orientation & 0x0F) << 4);
DP_DEBUG("Shared Info = 0x%x\n", info);
dp_write(DP_PHY_SPARE0_V420, info);
}
static void dp_catalog_ctrl_update_vx_px_v420(struct dp_catalog_ctrl *ctrl,
u8 v_level, u8 p_level, bool high)
{
struct dp_catalog_private_v420 *catalog;
struct dp_io_data *io_data;
u8 value0, value1;
u32 version;
if (!ctrl || !((v_level < MAX_VOLTAGE_LEVELS)
&& (p_level < MAX_PRE_EMP_LEVELS))) {
DP_ERR("invalid input\n");
return;
}
DP_DEBUG("hw: v=%d p=%d, high=%d\n", v_level, p_level, high);
catalog = dp_catalog_get_priv_v420(ctrl);
io_data = catalog->io->dp_ahb;
version = dp_read(DP_HW_VERSION);
DP_DEBUG("version: 0x%x\n", version);
/*
* For DP controller versions >= 1.2.3
*/
if (version >= 0x10020003) {
if (high) {
value0 = dp_swing_hbr2_hbr3[v_level][p_level];
value1 = dp_pre_emp_hbr2_hbr3[v_level][p_level];
} else {
value0 = dp_swing_hbr_rbr[v_level][p_level];
value1 = dp_pre_emp_hbr_rbr[v_level][p_level];
}
} else {
value0 = vm_voltage_swing[v_level][p_level];
value1 = vm_pre_emphasis[v_level][p_level];
}
/* program default setting first */
io_data = catalog->io->dp_ln_tx0;
dp_write(TXn_TX_DRV_LVL_V420, 0x2A);
dp_write(TXn_TX_EMP_POST1_LVL, 0x20);
io_data = catalog->io->dp_ln_tx1;
dp_write(TXn_TX_DRV_LVL_V420, 0x2A);
dp_write(TXn_TX_EMP_POST1_LVL, 0x20);
/* Enable MUX to use Cursor values from these registers */
value0 |= BIT(5);
value1 |= BIT(5);
/* Configure host and panel only if both values are allowed */
if (value0 != 0xFF && value1 != 0xFF) {
io_data = catalog->io->dp_ln_tx0;
dp_write(TXn_TX_DRV_LVL_V420, value0);
dp_write(TXn_TX_EMP_POST1_LVL, value1);
io_data = catalog->io->dp_ln_tx1;
dp_write(TXn_TX_DRV_LVL_V420, value0);
dp_write(TXn_TX_EMP_POST1_LVL, value1);
DP_DEBUG("hw: vx_value=0x%x px_value=0x%x\n",
value0, value1);
} else {
DP_ERR("invalid vx (0x%x=0x%x), px (0x%x=0x%x\n",
v_level, value0, p_level, value1);
}
}
static void dp_catalog_ctrl_lane_pnswap_v420(struct dp_catalog_ctrl *ctrl,
u8 ln_pnswap)
{
struct dp_catalog_private_v420 *catalog;
struct dp_io_data *io_data;
u32 cfg0, cfg1;
catalog = dp_catalog_get_priv_v420(ctrl);
cfg0 = 0x0a;
cfg1 = 0x0a;
cfg0 |= ((ln_pnswap >> 0) & 0x1) << 0;
cfg0 |= ((ln_pnswap >> 1) & 0x1) << 2;
cfg1 |= ((ln_pnswap >> 2) & 0x1) << 0;
cfg1 |= ((ln_pnswap >> 3) & 0x1) << 2;
io_data = catalog->io->dp_ln_tx0;
dp_write(TXn_TX_POL_INV_V420, cfg0);
io_data = catalog->io->dp_ln_tx1;
dp_write(TXn_TX_POL_INV_V420, cfg1);
}
static void dp_catalog_put_v420(struct dp_catalog *catalog)
{
struct dp_catalog_private_v420 *catalog_priv;
if (!catalog)
return;
catalog_priv = container_of(catalog->sub,
struct dp_catalog_private_v420, sub);
devm_kfree(catalog_priv->dev, catalog_priv);
}
struct dp_catalog_sub *dp_catalog_get_v420(struct device *dev,
struct dp_catalog *catalog, struct dp_catalog_io *io)
{
struct dp_catalog_private_v420 *catalog_priv;
if (!dev || !catalog) {
DP_ERR("invalid input\n");
return ERR_PTR(-EINVAL);
}
catalog_priv = devm_kzalloc(dev, sizeof(*catalog_priv), GFP_KERNEL);
if (!catalog_priv)
return ERR_PTR(-ENOMEM);
catalog_priv->dev = dev;
catalog_priv->io = io;
catalog_priv->dpc = catalog;
catalog_priv->sub.put = dp_catalog_put_v420;
catalog->aux.setup = dp_catalog_aux_setup_v420;
catalog->aux.clear_hw_interrupts = dp_catalog_aux_clear_hw_int_v420;
catalog->panel.config_msa = dp_catalog_panel_config_msa_v420;
catalog->ctrl.phy_lane_cfg = dp_catalog_ctrl_phy_lane_cfg_v420;
catalog->ctrl.update_vx_px = dp_catalog_ctrl_update_vx_px_v420;
catalog->ctrl.lane_pnswap = dp_catalog_ctrl_lane_pnswap_v420;
return &catalog_priv->sub;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,50 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DP_CTRL_H_
#define _DP_CTRL_H_
#include "dp_aux.h"
#include "dp_panel.h"
#include "dp_link.h"
#include "dp_parser.h"
#include "dp_power.h"
#include "dp_catalog.h"
#include "dp_debug.h"
struct dp_ctrl {
int (*init)(struct dp_ctrl *dp_ctrl, bool flip, bool reset);
void (*deinit)(struct dp_ctrl *dp_ctrl);
int (*on)(struct dp_ctrl *dp_ctrl, bool mst_mode, bool fec_en,
bool dsc_en, bool shallow);
void (*off)(struct dp_ctrl *dp_ctrl);
void (*abort)(struct dp_ctrl *dp_ctrl, bool abort);
void (*isr)(struct dp_ctrl *dp_ctrl);
bool (*handle_sink_request)(struct dp_ctrl *dp_ctrl);
void (*process_phy_test_request)(struct dp_ctrl *dp_ctrl);
int (*link_maintenance)(struct dp_ctrl *dp_ctrl);
int (*stream_on)(struct dp_ctrl *dp_ctrl, struct dp_panel *panel);
void (*stream_off)(struct dp_ctrl *dp_ctrl, struct dp_panel *panel);
void (*stream_pre_off)(struct dp_ctrl *dp_ctrl, struct dp_panel *panel);
void (*set_mst_channel_info)(struct dp_ctrl *dp_ctrl,
enum dp_stream_id strm,
u32 ch_start_slot, u32 ch_tot_slots);
void (*set_sim_mode)(struct dp_ctrl *dp_ctrl, bool en);
};
struct dp_ctrl_in {
struct device *dev;
struct dp_panel *panel;
struct dp_aux *aux;
struct dp_link *link;
struct dp_parser *parser;
struct dp_power *power;
struct dp_catalog_ctrl *catalog;
};
struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in);
void dp_ctrl_put(struct dp_ctrl *dp_ctrl);
#endif /* _DP_CTRL_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,151 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DP_DEBUG_H_
#define _DP_DEBUG_H_
#include "dp_panel.h"
#include "dp_ctrl.h"
#include "dp_link.h"
#include "dp_aux.h"
#include "dp_display.h"
#include "dp_pll.h"
#define DP_DEBUG(fmt, ...) \
do { \
if (unlikely(drm_debug & DRM_UT_KMS)) \
DRM_DEBUG("[msm-dp-debug][%-4d]"fmt, current->pid, \
##__VA_ARGS__); \
else \
pr_debug("[drm:%s][msm-dp-debug][%-4d]"fmt, __func__,\
current->pid, ##__VA_ARGS__); \
} while (0)
#define DP_INFO(fmt, ...) \
do { \
if (unlikely(drm_debug & DRM_UT_KMS)) \
DRM_INFO("[msm-dp-info][%-4d]"fmt, current->pid, \
##__VA_ARGS__); \
else \
pr_info("[drm:%s][msm-dp-info][%-4d]"fmt, __func__, \
current->pid, ##__VA_ARGS__); \
} while (0)
#define DP_WARN(fmt, ...) \
pr_warn("[drm:%s][msm-dp-warn][%-4d]"fmt, __func__, \
current->pid, ##__VA_ARGS__)
#define DP_ERR(fmt, ...) \
pr_err("[drm:%s][msm-dp-err][%-4d]"fmt, __func__, \
current->pid, ##__VA_ARGS__)
#define DEFAULT_DISCONNECT_DELAY_MS 0
#define MAX_DISCONNECT_DELAY_MS 10000
#define DEFAULT_CONNECT_NOTIFICATION_DELAY_MS 150
#define MAX_CONNECT_NOTIFICATION_DELAY_MS 5000
/**
* struct dp_debug
* @debug_en: specifies whether debug mode enabled
* @sim_mode: specifies whether sim mode enabled
* @psm_enabled: specifies whether psm enabled
* @hdcp_disabled: specifies if hdcp is disabled
* @hdcp_wait_sink_sync: used to wait for sink synchronization before HDCP auth
* @aspect_ratio: used to filter out aspect_ratio value
* @vdisplay: used to filter out vdisplay value
* @hdisplay: used to filter out hdisplay value
* @vrefresh: used to filter out vrefresh value
* @tpg_state: specifies whether tpg feature is enabled
* @max_pclk_khz: max pclk supported
* @force_encryption: enable/disable forced encryption for HDCP 2.2
* @skip_uevent: skip hotplug uevent to the user space
* @hdcp_status: string holding hdcp status information
* @dp_mst_connector_list: list containing all dp mst connectors
* @mst_hpd_sim: specifies whether simulated hpd enabled
* @mst_sim_add_con: specifies whether new sim connector is to be added
* @mst_sim_remove_con: specifies whether sim connector is to be removed
* @mst_sim_remove_con_id: specifies id of sim connector to be removed
* @mst_port_cnt: number of mst ports to be added during hpd
* @connect_notification_delay_ms: time (in ms) to wait for any attention
* messages before sending the connect notification uevent
* @disconnect_delay_ms: time (in ms) to wait before turning off the mainlink
* in response to HPD low of cable disconnect event
*/
struct dp_debug {
bool debug_en;
bool sim_mode;
bool psm_enabled;
bool hdcp_disabled;
bool hdcp_wait_sink_sync;
int aspect_ratio;
int vdisplay;
int hdisplay;
int vrefresh;
bool tpg_state;
u32 max_pclk_khz;
bool force_encryption;
bool skip_uevent;
char hdcp_status[SZ_128];
struct dp_mst_connector dp_mst_connector_list;
bool mst_hpd_sim;
bool mst_sim_add_con;
bool mst_sim_remove_con;
int mst_sim_remove_con_id;
u32 mst_port_cnt;
unsigned long connect_notification_delay_ms;
u32 disconnect_delay_ms;
struct dp_mst_connector mst_connector_cache;
u8 *(*get_edid)(struct dp_debug *dp_debug);
void (*abort)(struct dp_debug *dp_debug);
void (*set_mst_con)(struct dp_debug *dp_debug, int con_id);
};
/**
* struct dp_debug_in
* @dev: device instance of the caller
* @panel: instance of panel module
* @hpd: instance of hpd module
* @link: instance of link module
* @aux: instance of aux module
* @connector: double pointer to display connector
* @catalog: instance of catalog module
* @parser: instance of parser module
* @ctrl: instance of controller module
* @pll: instance of pll module
*/
struct dp_debug_in {
struct device *dev;
struct dp_panel *panel;
struct dp_hpd *hpd;
struct dp_link *link;
struct dp_aux *aux;
struct drm_connector **connector;
struct dp_catalog *catalog;
struct dp_parser *parser;
struct dp_ctrl *ctrl;
struct dp_pll *pll;
};
/**
* dp_debug_get() - configure and get the DisplayPlot debug module data
*
* @in: input structure containing data to initialize the debug module
* return: pointer to allocated debug module data
*
* This function sets up the debug module and provides a way
* for debugfs input to be communicated with existing modules
*/
struct dp_debug *dp_debug_get(struct dp_debug_in *in);
/**
* dp_debug_put()
*
* Cleans up dp_debug instance
*
* @dp_debug: instance of dp_debug
*/
void dp_debug_put(struct dp_debug *dp_debug);
#endif /* _DP_DEBUG_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,160 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DP_DISPLAY_H_
#define _DP_DISPLAY_H_
#include <linux/list.h>
#include <drm/drmP.h>
#include <drm/sde_drm.h>
#include "dp_panel.h"
#define DP_MST_SIM_MAX_PORTS 8
enum dp_drv_state {
PM_DEFAULT,
PM_SUSPEND,
};
struct dp_mst_hpd_info {
bool mst_protocol;
bool mst_hpd_sim;
u32 mst_port_cnt;
u8 *edid;
bool mst_sim_add_con;
bool mst_sim_remove_con;
int mst_sim_remove_con_id;
};
struct dp_mst_drm_cbs {
void (*hpd)(void *display, bool hpd_status);
void (*hpd_irq)(void *display, struct dp_mst_hpd_info *info);
void (*set_drv_state)(void *dp_display,
enum dp_drv_state mst_state);
int (*set_mgr_state)(void *dp_display, bool state,
struct dp_mst_hpd_info *info);
};
struct dp_mst_drm_install_info {
void *dp_mst_prv_info;
const struct dp_mst_drm_cbs *cbs;
};
struct dp_mst_caps {
bool has_mst;
u32 max_streams_supported;
u32 max_dpcd_transaction_bytes;
struct drm_dp_aux *drm_aux;
};
struct dp_mst_connector {
bool debug_en;
int con_id;
int hdisplay;
int vdisplay;
int vrefresh;
int aspect_ratio;
struct drm_connector *conn;
struct mutex lock;
struct list_head list;
enum drm_connector_status state;
};
struct dp_display {
struct drm_device *drm_dev;
struct dp_bridge *bridge;
struct drm_connector *base_connector;
void *base_dp_panel;
bool is_sst_connected;
bool is_mst_supported;
bool dsc_cont_pps;
u32 max_pclk_khz;
void *dp_mst_prv_info;
u32 max_mixer_count;
u32 max_dsc_count;
int (*enable)(struct dp_display *dp_display, void *panel);
int (*post_enable)(struct dp_display *dp_display, void *panel);
int (*pre_disable)(struct dp_display *dp_display, void *panel);
int (*disable)(struct dp_display *dp_display, void *panel);
int (*set_mode)(struct dp_display *dp_display, void *panel,
struct dp_display_mode *mode);
enum drm_mode_status (*validate_mode)(struct dp_display *dp_display,
void *panel, struct drm_display_mode *mode,
const struct msm_resource_caps_info *avail_res);
int (*get_modes)(struct dp_display *dp_display, void *panel,
struct dp_display_mode *dp_mode);
int (*prepare)(struct dp_display *dp_display, void *panel);
int (*unprepare)(struct dp_display *dp_display, void *panel);
int (*request_irq)(struct dp_display *dp_display);
struct dp_debug *(*get_debug)(struct dp_display *dp_display);
void (*post_open)(struct dp_display *dp_display);
int (*config_hdr)(struct dp_display *dp_display, void *panel,
struct drm_msm_ext_hdr_metadata *hdr_meta,
bool dhdr_update);
int (*set_colorspace)(struct dp_display *dp_display, void *panel,
u32 colorspace);
int (*post_init)(struct dp_display *dp_display);
int (*mst_install)(struct dp_display *dp_display,
struct dp_mst_drm_install_info *mst_install_info);
int (*mst_uninstall)(struct dp_display *dp_display);
int (*mst_connector_install)(struct dp_display *dp_display,
struct drm_connector *connector);
int (*mst_connector_uninstall)(struct dp_display *dp_display,
struct drm_connector *connector);
int (*mst_connector_update_edid)(struct dp_display *dp_display,
struct drm_connector *connector,
struct edid *edid);
int (*mst_connector_update_link_info)(struct dp_display *dp_display,
struct drm_connector *connector);
int (*mst_get_connector_info)(struct dp_display *dp_display,
struct drm_connector *connector,
struct dp_mst_connector *mst_conn);
int (*mst_get_fixed_topology_port)(struct dp_display *dp_display,
u32 strm_id, u32 *port_num);
int (*get_mst_caps)(struct dp_display *dp_display,
struct dp_mst_caps *mst_caps);
int (*set_stream_info)(struct dp_display *dp_display, void *panel,
u32 strm_id, u32 start_slot, u32 num_slots, u32 pbn,
int vcpi);
void (*convert_to_dp_mode)(struct dp_display *dp_display, void *panel,
const struct drm_display_mode *drm_mode,
struct dp_display_mode *dp_mode);
int (*update_pps)(struct dp_display *dp_display,
struct drm_connector *connector, char *pps_cmd);
void (*wakeup_phy_layer)(struct dp_display *dp_display,
bool wakeup);
int (*get_available_dp_resources)(struct dp_display *dp_display,
const struct msm_resource_caps_info *avail_res,
struct msm_resource_caps_info *max_dp_avail_res);
};
#if IS_ENABLED(CONFIG_DRM_MSM_DP)
int dp_display_get_num_of_displays(void);
int dp_display_get_displays(void **displays, int count);
int dp_display_get_num_of_streams(void);
#else
static inline int dp_display_get_num_of_displays(void)
{
return 0;
}
static inline int dp_display_get_displays(void **displays, int count)
{
return 0;
}
static inline int dp_display_get_num_of_streams(void)
{
return 0;
}
static inline int dp_connector_update_pps(struct drm_connector *connector,
char *pps_cmd, void *display)
{
return 0;
}
#endif /* CONFIG_DRM_MSM_DP */
#endif /* _DP_DISPLAY_H_ */

View File

@ -0,0 +1,739 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic.h>
#include <drm/drm_crtc.h>
#include "msm_drv.h"
#include "msm_kms.h"
#include "sde_connector.h"
#include "dp_drm.h"
#include "dp_mst_drm.h"
#include "dp_debug.h"
#define DP_MST_DEBUG(fmt, ...) DP_DEBUG(fmt, ##__VA_ARGS__)
#define to_dp_bridge(x) container_of((x), struct dp_bridge, base)
void convert_to_drm_mode(const struct dp_display_mode *dp_mode,
struct drm_display_mode *drm_mode)
{
u32 flags = 0;
memset(drm_mode, 0, sizeof(*drm_mode));
drm_mode->hdisplay = dp_mode->timing.h_active;
drm_mode->hsync_start = drm_mode->hdisplay +
dp_mode->timing.h_front_porch;
drm_mode->hsync_end = drm_mode->hsync_start +
dp_mode->timing.h_sync_width;
drm_mode->htotal = drm_mode->hsync_end + dp_mode->timing.h_back_porch;
drm_mode->hskew = dp_mode->timing.h_skew;
drm_mode->vdisplay = dp_mode->timing.v_active;
drm_mode->vsync_start = drm_mode->vdisplay +
dp_mode->timing.v_front_porch;
drm_mode->vsync_end = drm_mode->vsync_start +
dp_mode->timing.v_sync_width;
drm_mode->vtotal = drm_mode->vsync_end + dp_mode->timing.v_back_porch;
drm_mode->vrefresh = dp_mode->timing.refresh_rate;
drm_mode->clock = dp_mode->timing.pixel_clk_khz;
if (dp_mode->timing.h_active_low)
flags |= DRM_MODE_FLAG_NHSYNC;
else
flags |= DRM_MODE_FLAG_PHSYNC;
if (dp_mode->timing.v_active_low)
flags |= DRM_MODE_FLAG_NVSYNC;
else
flags |= DRM_MODE_FLAG_PVSYNC;
drm_mode->flags = flags;
drm_mode->type = 0x48;
drm_mode_set_name(drm_mode);
}
static int dp_bridge_attach(struct drm_bridge *dp_bridge)
{
struct dp_bridge *bridge = to_dp_bridge(dp_bridge);
if (!dp_bridge) {
DP_ERR("Invalid params\n");
return -EINVAL;
}
DP_DEBUG("[%d] attached\n", bridge->id);
return 0;
}
static void dp_bridge_pre_enable(struct drm_bridge *drm_bridge)
{
int rc = 0;
struct dp_bridge *bridge;
struct dp_display *dp;
if (!drm_bridge) {
DP_ERR("Invalid params\n");
return;
}
bridge = to_dp_bridge(drm_bridge);
dp = bridge->display;
if (!bridge->connector) {
DP_ERR("Invalid connector\n");
return;
}
if (!bridge->dp_panel) {
DP_ERR("Invalid dp_panel\n");
return;
}
/* By this point mode should have been validated through mode_fixup */
rc = dp->set_mode(dp, bridge->dp_panel, &bridge->dp_mode);
if (rc) {
DP_ERR("[%d] failed to perform a mode set, rc=%d\n",
bridge->id, rc);
return;
}
rc = dp->prepare(dp, bridge->dp_panel);
if (rc) {
DP_ERR("[%d] DP display prepare failed, rc=%d\n",
bridge->id, rc);
return;
}
/* for SST force stream id, start slot and total slots to 0 */
dp->set_stream_info(dp, bridge->dp_panel, 0, 0, 0, 0, 0);
rc = dp->enable(dp, bridge->dp_panel);
if (rc)
DP_ERR("[%d] DP display enable failed, rc=%d\n",
bridge->id, rc);
}
static void dp_bridge_enable(struct drm_bridge *drm_bridge)
{
int rc = 0;
struct dp_bridge *bridge;
struct dp_display *dp;
if (!drm_bridge) {
DP_ERR("Invalid params\n");
return;
}
bridge = to_dp_bridge(drm_bridge);
if (!bridge->connector) {
DP_ERR("Invalid connector\n");
return;
}
if (!bridge->dp_panel) {
DP_ERR("Invalid dp_panel\n");
return;
}
dp = bridge->display;
rc = dp->post_enable(dp, bridge->dp_panel);
if (rc)
DP_ERR("[%d] DP display post enable failed, rc=%d\n",
bridge->id, rc);
}
static void dp_bridge_disable(struct drm_bridge *drm_bridge)
{
int rc = 0;
struct dp_bridge *bridge;
struct dp_display *dp;
if (!drm_bridge) {
DP_ERR("Invalid params\n");
return;
}
bridge = to_dp_bridge(drm_bridge);
if (!bridge->connector) {
DP_ERR("Invalid connector\n");
return;
}
if (!bridge->dp_panel) {
DP_ERR("Invalid dp_panel\n");
return;
}
dp = bridge->display;
if (!dp) {
DP_ERR("dp is null\n");
return;
}
if (dp)
sde_connector_helper_bridge_disable(bridge->connector);
rc = dp->pre_disable(dp, bridge->dp_panel);
if (rc) {
DP_ERR("[%d] DP display pre disable failed, rc=%d\n",
bridge->id, rc);
}
}
static void dp_bridge_post_disable(struct drm_bridge *drm_bridge)
{
int rc = 0;
struct dp_bridge *bridge;
struct dp_display *dp;
if (!drm_bridge) {
DP_ERR("Invalid params\n");
return;
}
bridge = to_dp_bridge(drm_bridge);
if (!bridge->connector) {
DP_ERR("Invalid connector\n");
return;
}
if (!bridge->dp_panel) {
DP_ERR("Invalid dp_panel\n");
return;
}
dp = bridge->display;
rc = dp->disable(dp, bridge->dp_panel);
if (rc) {
DP_ERR("[%d] DP display disable failed, rc=%d\n",
bridge->id, rc);
return;
}
rc = dp->unprepare(dp, bridge->dp_panel);
if (rc) {
DP_ERR("[%d] DP display unprepare failed, rc=%d\n",
bridge->id, rc);
return;
}
}
static void dp_bridge_mode_set(struct drm_bridge *drm_bridge,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode)
{
struct dp_bridge *bridge;
struct dp_display *dp;
if (!drm_bridge || !mode || !adjusted_mode) {
DP_ERR("Invalid params\n");
return;
}
bridge = to_dp_bridge(drm_bridge);
if (!bridge->connector) {
DP_ERR("Invalid connector\n");
return;
}
if (!bridge->dp_panel) {
DP_ERR("Invalid dp_panel\n");
return;
}
dp = bridge->display;
dp->convert_to_dp_mode(dp, bridge->dp_panel, adjusted_mode,
&bridge->dp_mode);
}
static bool dp_bridge_mode_fixup(struct drm_bridge *drm_bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
bool ret = true;
struct dp_display_mode dp_mode;
struct dp_bridge *bridge;
struct dp_display *dp;
if (!drm_bridge || !mode || !adjusted_mode) {
DP_ERR("Invalid params\n");
ret = false;
goto end;
}
bridge = to_dp_bridge(drm_bridge);
if (!bridge->connector) {
DP_ERR("Invalid connector\n");
ret = false;
goto end;
}
if (!bridge->dp_panel) {
DP_ERR("Invalid dp_panel\n");
ret = false;
goto end;
}
dp = bridge->display;
dp->convert_to_dp_mode(dp, bridge->dp_panel, mode, &dp_mode);
convert_to_drm_mode(&dp_mode, adjusted_mode);
end:
return ret;
}
static const struct drm_bridge_funcs dp_bridge_ops = {
.attach = dp_bridge_attach,
.mode_fixup = dp_bridge_mode_fixup,
.pre_enable = dp_bridge_pre_enable,
.enable = dp_bridge_enable,
.disable = dp_bridge_disable,
.post_disable = dp_bridge_post_disable,
.mode_set = dp_bridge_mode_set,
};
int dp_connector_config_hdr(struct drm_connector *connector, void *display,
struct sde_connector_state *c_state)
{
struct dp_display *dp = display;
struct sde_connector *sde_conn;
if (!display || !c_state || !connector) {
DP_ERR("invalid params\n");
return -EINVAL;
}
sde_conn = to_sde_connector(connector);
if (!sde_conn->drv_panel) {
DP_ERR("invalid dp panel\n");
return -EINVAL;
}
return dp->config_hdr(dp, sde_conn->drv_panel, &c_state->hdr_meta,
c_state->dyn_hdr_meta.dynamic_hdr_update);
}
int dp_connector_set_colorspace(struct drm_connector *connector,
void *display)
{
struct dp_display *dp_display = display;
struct sde_connector *sde_conn;
if (!dp_display || !connector)
return -EINVAL;
sde_conn = to_sde_connector(connector);
if (!sde_conn->drv_panel) {
pr_err("invalid dp panel\n");
return -EINVAL;
}
return dp_display->set_colorspace(dp_display,
sde_conn->drv_panel, connector->state->colorspace);
}
int dp_connector_post_init(struct drm_connector *connector, void *display)
{
int rc;
struct dp_display *dp_display = display;
struct sde_connector *sde_conn;
if (!dp_display || !connector)
return -EINVAL;
dp_display->base_connector = connector;
dp_display->bridge->connector = connector;
if (dp_display->post_init) {
rc = dp_display->post_init(dp_display);
if (rc)
goto end;
}
sde_conn = to_sde_connector(connector);
dp_display->bridge->dp_panel = sde_conn->drv_panel;
rc = dp_mst_init(dp_display);
if (dp_display->dsc_cont_pps)
sde_conn->ops.update_pps = NULL;
end:
return rc;
}
int dp_connector_get_mode_info(struct drm_connector *connector,
const struct drm_display_mode *drm_mode,
struct msm_mode_info *mode_info,
void *display, const struct msm_resource_caps_info *avail_res)
{
const u32 single_intf = 1;
const u32 no_enc = 0;
struct msm_display_topology *topology;
struct sde_connector *sde_conn;
struct dp_panel *dp_panel;
struct dp_display_mode dp_mode;
struct dp_display *dp_disp = display;
struct msm_drm_private *priv;
struct msm_resource_caps_info avail_dp_res;
int rc = 0;
if (!drm_mode || !mode_info || !avail_res ||
!avail_res->max_mixer_width || !connector || !display ||
!connector->dev || !connector->dev->dev_private) {
DP_ERR("invalid params\n");
return -EINVAL;
}
memset(mode_info, 0, sizeof(*mode_info));
sde_conn = to_sde_connector(connector);
dp_panel = sde_conn->drv_panel;
priv = connector->dev->dev_private;
topology = &mode_info->topology;
rc = dp_disp->get_available_dp_resources(dp_disp, avail_res,
&avail_dp_res);
if (rc) {
DP_ERR("error getting max dp resources. rc:%d\n", rc);
return rc;
}
rc = msm_get_mixer_count(priv, drm_mode, &avail_dp_res,
&topology->num_lm);
if (rc) {
DP_ERR("error getting mixer count. rc:%d\n", rc);
return rc;
}
topology->num_enc = no_enc;
topology->num_intf = single_intf;
mode_info->frame_rate = drm_mode->vrefresh;
mode_info->vtotal = drm_mode->vtotal;
mode_info->wide_bus_en = dp_panel->widebus_en;
dp_disp->convert_to_dp_mode(dp_disp, dp_panel, drm_mode, &dp_mode);
if (dp_mode.timing.comp_info.comp_ratio > 1) {
memcpy(&mode_info->comp_info,
&dp_mode.timing.comp_info,
sizeof(mode_info->comp_info));
topology->num_enc = topology->num_lm;
topology->comp_type = mode_info->comp_info.comp_type;
}
return 0;
}
int dp_connector_get_info(struct drm_connector *connector,
struct msm_display_info *info, void *data)
{
struct dp_display *display = data;
if (!info || !display || !display->drm_dev) {
DP_ERR("invalid params\n");
return -EINVAL;
}
info->intf_type = DRM_MODE_CONNECTOR_DisplayPort;
info->num_of_h_tiles = 1;
info->h_tile_instance[0] = 0;
info->is_connected = display->is_sst_connected;
info->capabilities = MSM_DISPLAY_CAP_VID_MODE | MSM_DISPLAY_CAP_EDID |
MSM_DISPLAY_CAP_HOT_PLUG;
return 0;
}
enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
bool force,
void *display)
{
enum drm_connector_status status = connector_status_unknown;
struct msm_display_info info;
int rc;
if (!conn || !display)
return status;
/* get display dp_info */
memset(&info, 0x0, sizeof(info));
rc = dp_connector_get_info(conn, &info, display);
if (rc) {
DP_ERR("failed to get display info, rc=%d\n", rc);
return connector_status_disconnected;
}
if (info.capabilities & MSM_DISPLAY_CAP_HOT_PLUG)
status = (info.is_connected ? connector_status_connected :
connector_status_disconnected);
else
status = connector_status_connected;
conn->display_info.width_mm = info.width_mm;
conn->display_info.height_mm = info.height_mm;
return status;
}
void dp_connector_post_open(struct drm_connector *connector, void *display)
{
struct dp_display *dp;
if (!display) {
DP_ERR("invalid input\n");
return;
}
dp = display;
if (dp->post_open)
dp->post_open(dp);
}
int dp_connector_atomic_check(struct drm_connector *connector,
void *display,
struct drm_atomic_state *a_state)
{
struct sde_connector *sde_conn;
struct drm_connector_state *old_state;
struct drm_connector_state *c_state;
if (!connector || !display || !a_state)
return -EINVAL;
c_state = drm_atomic_get_new_connector_state(a_state, connector);
old_state =
drm_atomic_get_old_connector_state(a_state, connector);
if (!old_state || !c_state)
return -EINVAL;
sde_conn = to_sde_connector(connector);
/*
* Marking the colorspace has been changed
* the flag shall be checked in the pre_kickoff
* to configure the new colorspace in HW
*/
if (c_state->colorspace != old_state->colorspace) {
DP_DEBUG("colorspace has been updated\n");
sde_conn->colorspace_updated = true;
}
return 0;
}
int dp_connector_get_modes(struct drm_connector *connector,
void *display, const struct msm_resource_caps_info *avail_res)
{
int rc = 0;
struct dp_display *dp;
struct dp_display_mode *dp_mode = NULL;
struct drm_display_mode *m, drm_mode;
struct sde_connector *sde_conn;
if (!connector || !display)
return 0;
sde_conn = to_sde_connector(connector);
if (!sde_conn->drv_panel) {
DP_ERR("invalid dp panel\n");
return 0;
}
dp = display;
dp_mode = kzalloc(sizeof(*dp_mode), GFP_KERNEL);
if (!dp_mode)
return 0;
/* pluggable case assumes EDID is read when HPD */
if (dp->is_sst_connected) {
rc = dp->get_modes(dp, sde_conn->drv_panel, dp_mode);
if (!rc)
DP_ERR("failed to get DP sink modes, rc=%d\n", rc);
if (dp_mode->timing.pixel_clk_khz) { /* valid DP mode */
memset(&drm_mode, 0x0, sizeof(drm_mode));
convert_to_drm_mode(dp_mode, &drm_mode);
m = drm_mode_duplicate(connector->dev, &drm_mode);
if (!m) {
DP_ERR("failed to add mode %ux%u\n",
drm_mode.hdisplay,
drm_mode.vdisplay);
kfree(dp_mode);
return 0;
}
m->width_mm = connector->display_info.width_mm;
m->height_mm = connector->display_info.height_mm;
drm_mode_probed_add(connector, m);
}
} else {
DP_ERR("No sink connected\n");
}
kfree(dp_mode);
return rc;
}
int dp_drm_bridge_init(void *data, struct drm_encoder *encoder,
u32 max_mixer_count, u32 max_dsc_count)
{
int rc = 0;
struct dp_bridge *bridge;
struct drm_device *dev;
struct dp_display *display = data;
struct msm_drm_private *priv = NULL;
bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
if (!bridge) {
rc = -ENOMEM;
goto error;
}
dev = display->drm_dev;
bridge->display = display;
bridge->base.funcs = &dp_bridge_ops;
bridge->base.encoder = encoder;
priv = dev->dev_private;
rc = drm_bridge_attach(encoder, &bridge->base, NULL);
if (rc) {
DP_ERR("failed to attach bridge, rc=%d\n", rc);
goto error_free_bridge;
}
rc = display->request_irq(display);
if (rc) {
DP_ERR("request_irq failed, rc=%d\n", rc);
goto error_free_bridge;
}
encoder->bridge = &bridge->base;
priv->bridges[priv->num_bridges++] = &bridge->base;
display->bridge = bridge;
display->max_mixer_count = max_mixer_count;
display->max_dsc_count = max_dsc_count;
return 0;
error_free_bridge:
kfree(bridge);
error:
return rc;
}
void dp_drm_bridge_deinit(void *data)
{
struct dp_display *display = data;
struct dp_bridge *bridge = display->bridge;
if (bridge && bridge->base.encoder)
bridge->base.encoder->bridge = NULL;
kfree(bridge);
}
enum drm_mode_status dp_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode, void *display,
const struct msm_resource_caps_info *avail_res)
{
int rc = 0;
struct dp_display *dp_disp;
struct sde_connector *sde_conn;
struct msm_resource_caps_info avail_dp_res;
if (!mode || !display || !connector) {
DP_ERR("invalid params\n");
return MODE_ERROR;
}
sde_conn = to_sde_connector(connector);
if (!sde_conn->drv_panel) {
DP_ERR("invalid dp panel\n");
return MODE_ERROR;
}
dp_disp = display;
mode->vrefresh = drm_mode_vrefresh(mode);
rc = dp_disp->get_available_dp_resources(dp_disp, avail_res,
&avail_dp_res);
if (rc) {
DP_ERR("error getting max dp resources. rc:%d\n", rc);
return MODE_ERROR;
}
return dp_disp->validate_mode(dp_disp, sde_conn->drv_panel,
mode, &avail_dp_res);
}
int dp_connector_update_pps(struct drm_connector *connector,
char *pps_cmd, void *display)
{
struct dp_display *dp_disp;
struct sde_connector *sde_conn;
if (!display || !connector) {
DP_ERR("invalid params\n");
return -EINVAL;
}
sde_conn = to_sde_connector(connector);
if (!sde_conn->drv_panel) {
DP_ERR("invalid dp panel\n");
return MODE_ERROR;
}
dp_disp = display;
return dp_disp->update_pps(dp_disp, connector, pps_cmd);
}
int dp_connector_install_properties(void *display, struct drm_connector *conn)
{
struct dp_display *dp_display = display;
struct drm_connector *base_conn;
int rc;
if (!display || !conn) {
DP_ERR("invalid params\n");
return -EINVAL;
}
base_conn = dp_display->base_connector;
/*
* Create the property on the base connector during probe time and then
* attach the same property onto new connector objects created for MST
*/
if (!base_conn->colorspace_property) {
/* This is the base connector. create the drm property */
rc = drm_mode_create_dp_colorspace_property(base_conn);
if (rc)
return rc;
} else {
conn->colorspace_property = base_conn->colorspace_property;
}
drm_object_attach_property(&conn->base, conn->colorspace_property, 0);
return 0;
}

View File

@ -0,0 +1,258 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DP_DRM_H_
#define _DP_DRM_H_
#include <linux/types.h>
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include "msm_drv.h"
#include "dp_display.h"
struct dp_bridge {
struct drm_bridge base;
u32 id;
struct drm_connector *connector;
struct dp_display *display;
struct dp_display_mode dp_mode;
void *dp_panel;
};
#if IS_ENABLED(CONFIG_DRM_MSM_DP)
/**
* dp_connector_config_hdr - callback to configure HDR
* @connector: Pointer to drm connector structure
* @display: Pointer to private display handle
* @c_state: connect state data
* Returns: Zero on success
*/
int dp_connector_config_hdr(struct drm_connector *connector,
void *display,
struct sde_connector_state *c_state);
/**
* dp_connector_atomic_check - callback to perform atomic
* check for DP
* @connector: Pointer to drm connector structure
* @display: Pointer to private display handle
* @c_state: connect state data
* Returns: Zero on success
*/
int dp_connector_atomic_check(struct drm_connector *connector,
void *display,
struct drm_atomic_state *state);
/**
* dp_connector_set_colorspace - callback to set new colorspace
* @connector: Pointer to drm connector structure
* @display: Pointer to private display handle
* Returns: Zero on success
*/
int dp_connector_set_colorspace(struct drm_connector *connector,
void *display);
/**
* dp_connector_post_init - callback to perform additional initialization steps
* @connector: Pointer to drm connector structure
* @display: Pointer to private display handle
* Returns: Zero on success
*/
int dp_connector_post_init(struct drm_connector *connector, void *display);
/**
* dp_connector_detect - callback to determine if connector is connected
* @connector: Pointer to drm connector structure
* @force: Force detect setting from drm framework
* @display: Pointer to private display handle
* Returns: Connector 'is connected' status
*/
enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
bool force,
void *display);
/**
* dp_connector_get_modes - callback to add drm modes via drm_mode_probed_add()
* @connector: Pointer to drm connector structure
* @display: Pointer to private display handle
* @avail_res: Pointer with curr available resources
* Returns: Number of modes added
*/
int dp_connector_get_modes(struct drm_connector *connector,
void *display, const struct msm_resource_caps_info *avail_res);
/**
* dp_connector_mode_valid - callback to determine if specified mode is valid
* @connector: Pointer to drm connector structure
* @mode: Pointer to drm mode structure
* @display: Pointer to private display handle
* @avail_res: Pointer with curr available resources
* Returns: Validity status for specified mode
*/
enum drm_mode_status dp_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode,
void *display, const struct msm_resource_caps_info *avail_res);
/**
* dp_connector_get_mode_info - retrieve information of the mode selected
* @connector: Pointer to drm connector structure
* @drm_mode: Display mode set for the display
* @mode_info: Out parameter. Information of the mode
* @display: Pointer to private display structure
* @avail_res: Pointer with curr available resources
* Returns: zero on success
*/
int dp_connector_get_mode_info(struct drm_connector *connector,
const struct drm_display_mode *drm_mode,
struct msm_mode_info *mode_info,
void *display, const struct msm_resource_caps_info *avail_res);
/**
* dp_connector_get_info - retrieve connector display info
* @connector: Pointer to drm connector structure
* @info: Out parameter. Information of the connected display
* @display: Pointer to private display structure
* Returns: zero on success
*/
int dp_connector_get_info(struct drm_connector *connector,
struct msm_display_info *info, void *display);
/**
* dp_connector_post_open - handle the post open functionalities
* @connector: Pointer to drm connector structure
* @display: Pointer to private display structure
*/
void dp_connector_post_open(struct drm_connector *connector, void *display);
/**
* dp_drm_bridge_init- drm dp bridge initialize
* @display: Pointer to private display structure
* @encoder: encoder for this dp bridge
* @max_mixer_count: max available mixers for dp display
* @max_dsc_count: max available dsc for dp display
*/
int dp_drm_bridge_init(void *display, struct drm_encoder *encoder,
u32 max_mixer_count, u32 max_dsc_count);
void dp_drm_bridge_deinit(void *display);
/**
* convert_to_drm_mode - convert dp mode to drm mode
* @dp_mode: Point to dp mode
* @drm_mode: Pointer to drm mode
*/
void convert_to_drm_mode(const struct dp_display_mode *dp_mode,
struct drm_display_mode *drm_mode);
/**
* dp_connector_update_pps - update pps for given connector
* @dp_mode: Point to dp mode
* @pps_cmd: PPS packet
* @display: Pointer to private display structure
*/
int dp_connector_update_pps(struct drm_connector *connector,
char *pps_cmd, void *display);
/**
* dp_connector_install_properties - install drm properties
* @display: Pointer to private display structure
* @conn: Pointer to connector
*/
int dp_connector_install_properties(void *display,
struct drm_connector *conn);
#else
static inline int dp_connector_config_hdr(struct drm_connector *connector,
void *display, struct sde_connector_state *c_state)
{
return 0;
}
static inline int dp_connector_atomic_check(struct drm_connector *connector,
void *display, struct drm_atomic_state *state)
{
return 0;
}
static inline int dp_connector_set_colorspace(struct drm_connector *connector,
void *display)
{
return 0;
}
static inline int dp_connector_post_init(struct drm_connector *connector,
void *display)
{
return 0;
}
static inline enum drm_connector_status dp_connector_detect(
struct drm_connector *conn,
bool force,
void *display)
{
return 0;
}
static inline int dp_connector_get_modes(struct drm_connector *connector,
void *display, const struct msm_resource_caps_info *avail_res)
{
return 0;
}
static inline enum drm_mode_status dp_connector_mode_valid(
struct drm_connector *connector,
struct drm_display_mode *mode,
void *display, const struct msm_resource_caps_info *avail_res)
{
return MODE_OK;
}
static inline int dp_connector_get_mode_info(struct drm_connector *connector,
const struct drm_display_mode *drm_mode,
struct msm_mode_info *mode_info,
void *display, const struct msm_resource_caps_info *avail_res)
{
return 0;
}
static inline int dp_connector_get_info(struct drm_connector *connector,
struct msm_display_info *info, void *display)
{
return 0;
}
static inline void dp_connector_post_open(struct drm_connector *connector,
void *display)
{
}
static inline int dp_drm_bridge_init(void *display, struct drm_encoder *encoder,
u32 max_mixer_count, u32 max_dsc_count)
{
return 0;
}
static inline void dp_drm_bridge_deinit(void *display)
{
}
static inline void convert_to_drm_mode(const struct dp_display_mode *dp_mode,
struct drm_display_mode *drm_mode)
{
}
static int dp_connector_install_properties(void *display,
struct drm_connector *conn)
{
return 0;
}
#endif /* CONFIG_DRM_MSM_DP */
#endif /* _DP_DRM_H_ */

View File

@ -0,0 +1,296 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
*/
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/sde_io_util.h>
#include <linux/of_gpio.h>
#include "dp_gpio_hpd.h"
#include "dp_debug.h"
struct dp_gpio_hpd_private {
struct device *dev;
struct dp_hpd base;
struct dss_gpio gpio_cfg;
struct delayed_work work;
struct dp_hpd_cb *cb;
int irq;
bool hpd;
};
static int dp_gpio_hpd_connect(struct dp_gpio_hpd_private *gpio_hpd, bool hpd)
{
int rc = 0;
if (!gpio_hpd) {
DP_ERR("invalid input\n");
rc = -EINVAL;
goto error;
}
gpio_hpd->base.hpd_high = hpd;
gpio_hpd->base.alt_mode_cfg_done = hpd;
gpio_hpd->base.hpd_irq = false;
if (!gpio_hpd->cb ||
!gpio_hpd->cb->configure ||
!gpio_hpd->cb->disconnect) {
DP_ERR("invalid cb\n");
rc = -EINVAL;
goto error;
}
if (hpd)
rc = gpio_hpd->cb->configure(gpio_hpd->dev);
else
rc = gpio_hpd->cb->disconnect(gpio_hpd->dev);
error:
return rc;
}
static int dp_gpio_hpd_attention(struct dp_gpio_hpd_private *gpio_hpd)
{
int rc = 0;
if (!gpio_hpd) {
DP_ERR("invalid input\n");
rc = -EINVAL;
goto error;
}
gpio_hpd->base.hpd_irq = true;
if (gpio_hpd->cb && gpio_hpd->cb->attention)
rc = gpio_hpd->cb->attention(gpio_hpd->dev);
error:
return rc;
}
static irqreturn_t dp_gpio_isr(int unused, void *data)
{
struct dp_gpio_hpd_private *gpio_hpd = data;
u32 const disconnect_timeout_retry = 50;
bool hpd;
int i;
if (!gpio_hpd)
return IRQ_NONE;
hpd = gpio_get_value_cansleep(gpio_hpd->gpio_cfg.gpio);
if (!gpio_hpd->hpd && hpd) {
gpio_hpd->hpd = true;
queue_delayed_work(system_wq, &gpio_hpd->work, 0);
return IRQ_HANDLED;
}
if (!gpio_hpd->hpd)
return IRQ_HANDLED;
/* In DP 1.2 spec, 100msec is recommended for the detection
* of HPD connect event. Here we'll poll HPD status for
* 50x2ms = 100ms and if HPD is always low, we know DP is
* disconnected. If HPD is high, HPD_IRQ will be handled
*/
for (i = 0; i < disconnect_timeout_retry; i++) {
if (hpd) {
dp_gpio_hpd_attention(gpio_hpd);
return IRQ_HANDLED;
}
usleep_range(2000, 2100);
hpd = gpio_get_value_cansleep(gpio_hpd->gpio_cfg.gpio);
}
gpio_hpd->hpd = false;
queue_delayed_work(system_wq, &gpio_hpd->work, 0);
return IRQ_HANDLED;
}
static void dp_gpio_hpd_work(struct work_struct *work)
{
struct delayed_work *dw = to_delayed_work(work);
struct dp_gpio_hpd_private *gpio_hpd = container_of(dw,
struct dp_gpio_hpd_private, work);
int ret;
if (gpio_hpd->hpd) {
devm_free_irq(gpio_hpd->dev,
gpio_hpd->irq, gpio_hpd);
ret = devm_request_threaded_irq(gpio_hpd->dev,
gpio_hpd->irq, NULL,
dp_gpio_isr,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"dp-gpio-intp", gpio_hpd);
dp_gpio_hpd_connect(gpio_hpd, true);
} else {
devm_free_irq(gpio_hpd->dev,
gpio_hpd->irq, gpio_hpd);
ret = devm_request_threaded_irq(gpio_hpd->dev,
gpio_hpd->irq, NULL,
dp_gpio_isr,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
"dp-gpio-intp", gpio_hpd);
dp_gpio_hpd_connect(gpio_hpd, false);
}
if (ret < 0)
DP_ERR("Cannot claim IRQ dp-gpio-intp\n");
}
static int dp_gpio_hpd_simulate_connect(struct dp_hpd *dp_hpd, bool hpd)
{
int rc = 0;
struct dp_gpio_hpd_private *gpio_hpd;
if (!dp_hpd) {
DP_ERR("invalid input\n");
rc = -EINVAL;
goto error;
}
gpio_hpd = container_of(dp_hpd, struct dp_gpio_hpd_private, base);
dp_gpio_hpd_connect(gpio_hpd, hpd);
error:
return rc;
}
static int dp_gpio_hpd_simulate_attention(struct dp_hpd *dp_hpd, int vdo)
{
int rc = 0;
struct dp_gpio_hpd_private *gpio_hpd;
if (!dp_hpd) {
DP_ERR("invalid input\n");
rc = -EINVAL;
goto error;
}
gpio_hpd = container_of(dp_hpd, struct dp_gpio_hpd_private, base);
dp_gpio_hpd_attention(gpio_hpd);
error:
return rc;
}
int dp_gpio_hpd_register(struct dp_hpd *dp_hpd)
{
struct dp_gpio_hpd_private *gpio_hpd;
int edge;
int rc = 0;
if (!dp_hpd)
return -EINVAL;
gpio_hpd = container_of(dp_hpd, struct dp_gpio_hpd_private, base);
gpio_hpd->hpd = gpio_get_value_cansleep(gpio_hpd->gpio_cfg.gpio);
edge = gpio_hpd->hpd ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
rc = devm_request_threaded_irq(gpio_hpd->dev, gpio_hpd->irq, NULL,
dp_gpio_isr,
edge | IRQF_ONESHOT,
"dp-gpio-intp", gpio_hpd);
if (rc) {
DP_ERR("Failed to request INTP threaded IRQ: %d\n", rc);
return rc;
}
if (gpio_hpd->hpd)
queue_delayed_work(system_wq, &gpio_hpd->work, 0);
return rc;
}
struct dp_hpd *dp_gpio_hpd_get(struct device *dev,
struct dp_hpd_cb *cb)
{
int rc = 0;
const char *hpd_gpio_name = "qcom,dp-hpd-gpio";
struct dp_gpio_hpd_private *gpio_hpd;
struct dp_pinctrl pinctrl = {0};
if (!dev || !cb) {
DP_ERR("invalid device\n");
rc = -EINVAL;
goto error;
}
gpio_hpd = devm_kzalloc(dev, sizeof(*gpio_hpd), GFP_KERNEL);
if (!gpio_hpd) {
rc = -ENOMEM;
goto error;
}
pinctrl.pin = devm_pinctrl_get(dev);
if (!IS_ERR_OR_NULL(pinctrl.pin)) {
pinctrl.state_hpd_active = pinctrl_lookup_state(pinctrl.pin,
"mdss_dp_hpd_active");
if (!IS_ERR_OR_NULL(pinctrl.state_hpd_active)) {
rc = pinctrl_select_state(pinctrl.pin,
pinctrl.state_hpd_active);
if (rc) {
DP_ERR("failed to set hpd active state\n");
goto gpio_error;
}
}
}
gpio_hpd->gpio_cfg.gpio = of_get_named_gpio(dev->of_node,
hpd_gpio_name, 0);
if (!gpio_is_valid(gpio_hpd->gpio_cfg.gpio)) {
DP_ERR("%s gpio not specified\n", hpd_gpio_name);
rc = -EINVAL;
goto gpio_error;
}
strlcpy(gpio_hpd->gpio_cfg.gpio_name, hpd_gpio_name,
sizeof(gpio_hpd->gpio_cfg.gpio_name));
gpio_hpd->gpio_cfg.value = 0;
rc = gpio_request(gpio_hpd->gpio_cfg.gpio,
gpio_hpd->gpio_cfg.gpio_name);
if (rc) {
DP_ERR("%s: failed to request gpio\n", hpd_gpio_name);
goto gpio_error;
}
gpio_direction_input(gpio_hpd->gpio_cfg.gpio);
gpio_hpd->dev = dev;
gpio_hpd->cb = cb;
gpio_hpd->irq = gpio_to_irq(gpio_hpd->gpio_cfg.gpio);
INIT_DELAYED_WORK(&gpio_hpd->work, dp_gpio_hpd_work);
gpio_hpd->base.simulate_connect = dp_gpio_hpd_simulate_connect;
gpio_hpd->base.simulate_attention = dp_gpio_hpd_simulate_attention;
gpio_hpd->base.register_hpd = dp_gpio_hpd_register;
return &gpio_hpd->base;
gpio_error:
devm_kfree(dev, gpio_hpd);
error:
return ERR_PTR(rc);
}
void dp_gpio_hpd_put(struct dp_hpd *dp_hpd)
{
struct dp_gpio_hpd_private *gpio_hpd;
if (!dp_hpd)
return;
gpio_hpd = container_of(dp_hpd, struct dp_gpio_hpd_private, base);
gpio_free(gpio_hpd->gpio_cfg.gpio);
devm_kfree(gpio_hpd->dev, gpio_hpd);
}

View File

@ -0,0 +1,32 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
*/
#ifndef _DP_GPIO_HPD_H_
#define _DP_GPIO_HPD_H_
#include "dp_hpd.h"
/**
* dp_gpio_hpd_get() - configure and get the DisplayPlot HPD module data
*
* @dev: device instance of the caller
* return: pointer to allocated gpio hpd module data
*
* This function sets up the gpio hpd module
*/
struct dp_hpd *dp_gpio_hpd_get(struct device *dev,
struct dp_hpd_cb *cb);
/**
* dp_gpio_hpd_put()
*
* Cleans up dp_hpd instance
*
* @hpd: instance of gpio_hpd
*/
void dp_gpio_hpd_put(struct dp_hpd *hpd);
#endif /* _DP_GPIO_HPD_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,111 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/err.h>
#include "dp_hpd.h"
#include "dp_altmode.h"
#include "dp_usbpd.h"
#include "dp_gpio_hpd.h"
#include "dp_lphw_hpd.h"
#include "dp_debug.h"
static void dp_hpd_host_init(struct dp_hpd *dp_hpd,
struct dp_catalog_hpd *catalog)
{
if (!catalog) {
DP_ERR("invalid input\n");
return;
}
catalog->config_hpd(catalog, true);
}
static void dp_hpd_host_deinit(struct dp_hpd *dp_hpd,
struct dp_catalog_hpd *catalog)
{
if (!catalog) {
DP_ERR("invalid input\n");
return;
}
catalog->config_hpd(catalog, false);
}
static void dp_hpd_isr(struct dp_hpd *dp_hpd)
{
}
struct dp_hpd *dp_hpd_get(struct device *dev, struct dp_parser *parser,
struct dp_catalog_hpd *catalog, struct dp_hpd_cb *cb)
{
struct dp_hpd *dp_hpd;
if (parser->no_aux_switch && parser->lphw_hpd) {
dp_hpd = dp_lphw_hpd_get(dev, parser, catalog, cb);
if (IS_ERR_OR_NULL(dp_hpd)) {
DP_ERR("failed to get lphw hpd\n");
return dp_hpd;
}
dp_hpd->type = DP_HPD_LPHW;
} else if (parser->no_aux_switch) {
dp_hpd = dp_gpio_hpd_get(dev, cb);
if (IS_ERR_OR_NULL(dp_hpd)) {
DP_ERR("failed to get gpio hpd\n");
return dp_hpd;
}
dp_hpd->type = DP_HPD_GPIO;
} else {
dp_hpd = dp_altmode_get(dev, cb);
if (!IS_ERR_OR_NULL(dp_hpd)) {
dp_hpd->type = DP_HPD_ALTMODE;
goto config;
}
DP_WARN("dp_altmode failed (%ld), falling back to dp_usbpd\n",
PTR_ERR(dp_hpd));
dp_hpd = dp_usbpd_get(dev, cb);
if (IS_ERR_OR_NULL(dp_hpd)) {
DP_ERR("failed to get usbpd\n");
return dp_hpd;
}
dp_hpd->type = DP_HPD_USBPD;
}
config:
if (!dp_hpd->host_init)
dp_hpd->host_init = dp_hpd_host_init;
if (!dp_hpd->host_deinit)
dp_hpd->host_deinit = dp_hpd_host_deinit;
if (!dp_hpd->isr)
dp_hpd->isr = dp_hpd_isr;
return dp_hpd;
}
void dp_hpd_put(struct dp_hpd *dp_hpd)
{
if (!dp_hpd)
return;
switch (dp_hpd->type) {
case DP_HPD_USBPD:
dp_usbpd_put(dp_hpd);
break;
case DP_HPD_ALTMODE:
dp_altmode_put(dp_hpd);
break;
case DP_HPD_GPIO:
dp_gpio_hpd_put(dp_hpd);
break;
case DP_HPD_LPHW:
dp_lphw_hpd_put(dp_hpd);
break;
default:
DP_ERR("unknown hpd type %d\n", dp_hpd->type);
break;
}
}

View File

@ -0,0 +1,116 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DP_HPD_H_
#define _DP_HPD_H_
#include <linux/types.h>
#include "dp_parser.h"
#include "dp_catalog.h"
struct device;
/**
* enum dp_hpd_plug_orientation - plug orientation
* @ORIENTATION_NONE: Undefined or unspecified
* @ORIENTATION_CC1: CC1
* @ORIENTATION_CC2: CC2
*/
enum dp_hpd_plug_orientation {
ORIENTATION_NONE,
ORIENTATION_CC1,
ORIENTATION_CC2,
};
/**
* enum dp_hpd_type - dp hpd type
* @DP_HPD_ALTMODE: AltMode over G-Link based HPD
* @DP_HPD_USBPD: USB type-c based HPD
* @DP_HPD_GPIO: GPIO based HPD
* @DP_HPD_BUILTIN: Controller built-in HPD
*/
enum dp_hpd_type {
DP_HPD_ALTMODE,
DP_HPD_USBPD,
DP_HPD_GPIO,
DP_HPD_LPHW,
DP_HPD_BUILTIN,
};
/**
* struct dp_hpd_cb - callback functions provided by the client
*
* @configure: called when dp connection is ready.
* @disconnect: notify the cable disconnect event.
* @attention: notify any attention message event.
*/
struct dp_hpd_cb {
int (*configure)(struct device *dev);
int (*disconnect)(struct device *dev);
int (*attention)(struct device *dev);
};
/**
* struct dp_hpd - DisplayPort HPD status
*
* @type: type of HPD
* @orientation: plug orientation configuration, USBPD type only.
* @hpd_high: Hot Plug Detect signal is high.
* @hpd_irq: Change in the status since last message
* @alt_mode_cfg_done: bool to specify alt mode status
* @multi_func: multi-function preferred, USBPD type only
* @peer_usb_com: downstream supports usb data communication
* @force_multi_func: force multi-function preferred
* @isr: event interrupt, BUILTIN and LPHW type only
* @register_hpd: register hardware callback
* @host_init: source or host side setup for hpd
* @host_deinit: source or host side de-initializations
* @simulate_connect: simulate disconnect or connect for debug mode
* @simulate_attention: simulate attention messages for debug mode
* @wakeup_phy: wakeup USBPD phy layer
*/
struct dp_hpd {
enum dp_hpd_type type;
u32 orientation;
bool hpd_high;
bool hpd_irq;
bool alt_mode_cfg_done;
bool multi_func;
bool peer_usb_comm;
bool force_multi_func;
void (*isr)(struct dp_hpd *dp_hpd);
int (*register_hpd)(struct dp_hpd *dp_hpd);
void (*host_init)(struct dp_hpd *hpd, struct dp_catalog_hpd *catalog);
void (*host_deinit)(struct dp_hpd *hpd, struct dp_catalog_hpd *catalog);
int (*simulate_connect)(struct dp_hpd *dp_hpd, bool hpd);
int (*simulate_attention)(struct dp_hpd *dp_hpd, int vdo);
void (*wakeup_phy)(struct dp_hpd *dp_hpd, bool wakeup);
};
/**
* dp_hpd_get() - configure and get the DisplayPlot HPD module data
*
* @dev: device instance of the caller
* @parser: DP parser
* @cb: callback function for HPD response
* return: pointer to allocated hpd module data
*
* This function sets up the hpd module
*/
struct dp_hpd *dp_hpd_get(struct device *dev, struct dp_parser *parser,
struct dp_catalog_hpd *catalog, struct dp_hpd_cb *cb);
/**
* dp_hpd_put()
*
* Cleans up dp_hpd instance
*
* @dp_hpd: instance of dp_hpd
*/
void dp_hpd_put(struct dp_hpd *dp_hpd);
#endif /* _DP_HPD_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,206 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*/
#ifndef _DP_LINK_H_
#define _DP_LINK_H_
#include "dp_aux.h"
#define DS_PORT_STATUS_CHANGED 0x200
#define DP_TEST_BIT_DEPTH_UNKNOWN 0xFFFFFFFF
#define DP_LINK_ENUM_STR(x) #x
enum dp_link_voltage_level {
DP_LINK_VOLTAGE_LEVEL_0,
DP_LINK_VOLTAGE_LEVEL_1,
DP_LINK_VOLTAGE_LEVEL_2,
DP_LINK_VOLTAGE_MAX = DP_LINK_VOLTAGE_LEVEL_2,
};
enum dp_link_preemaphasis_level {
DP_LINK_PRE_EMPHASIS_LEVEL_0,
DP_LINK_PRE_EMPHASIS_LEVEL_1,
DP_LINK_PRE_EMPHASIS_LEVEL_2,
DP_LINK_PRE_EMPHASIS_LEVEL_3,
DP_LINK_PRE_EMPHASIS_MAX = DP_LINK_PRE_EMPHASIS_LEVEL_3,
};
struct dp_link_sink_count {
u32 count;
bool cp_ready;
};
struct dp_link_test_video {
u32 test_video_pattern;
u32 test_bit_depth;
u32 test_dyn_range;
u32 test_h_total;
u32 test_v_total;
u32 test_h_start;
u32 test_v_start;
u32 test_hsync_pol;
u32 test_hsync_width;
u32 test_vsync_pol;
u32 test_vsync_width;
u32 test_h_width;
u32 test_v_height;
u32 test_rr_d;
u32 test_rr_n;
};
struct dp_link_test_audio {
u32 test_audio_sampling_rate;
u32 test_audio_channel_count;
u32 test_audio_pattern_type;
u32 test_audio_period_ch_1;
u32 test_audio_period_ch_2;
u32 test_audio_period_ch_3;
u32 test_audio_period_ch_4;
u32 test_audio_period_ch_5;
u32 test_audio_period_ch_6;
u32 test_audio_period_ch_7;
u32 test_audio_period_ch_8;
};
struct dp_link_hdcp_status {
int hdcp_state;
int hdcp_version;
};
struct dp_link_phy_params {
u32 phy_test_pattern_sel;
u8 v_level;
u8 p_level;
};
struct dp_link_params {
u32 lane_count;
u32 bw_code;
};
static inline char *dp_link_get_test_name(u32 test_requested)
{
switch (test_requested) {
case DP_TEST_LINK_TRAINING:
return DP_LINK_ENUM_STR(DP_TEST_LINK_TRAINING);
case DP_TEST_LINK_VIDEO_PATTERN:
return DP_LINK_ENUM_STR(DP_TEST_LINK_VIDEO_PATTERN);
case DP_TEST_LINK_EDID_READ:
return DP_LINK_ENUM_STR(DP_TEST_LINK_EDID_READ);
case DP_TEST_LINK_PHY_TEST_PATTERN:
return DP_LINK_ENUM_STR(DP_TEST_LINK_PHY_TEST_PATTERN);
case DP_TEST_LINK_AUDIO_PATTERN:
return DP_LINK_ENUM_STR(DP_TEST_LINK_AUDIO_PATTERN);
case DS_PORT_STATUS_CHANGED:
return DP_LINK_ENUM_STR(DS_PORT_STATUS_CHANGED);
case DP_LINK_STATUS_UPDATED:
return DP_LINK_ENUM_STR(DP_LINK_STATUS_UPDATED);
default:
return "unknown";
}
}
struct dp_link {
u32 sink_request;
u32 test_response;
struct dp_link_sink_count sink_count;
struct dp_link_test_video test_video;
struct dp_link_test_audio test_audio;
struct dp_link_phy_params phy_params;
struct dp_link_params link_params;
struct dp_link_hdcp_status hdcp_status;
u32 (*get_test_bits_depth)(struct dp_link *dp_link, u32 bpp);
int (*process_request)(struct dp_link *dp_link);
int (*get_colorimetry_config)(struct dp_link *dp_link);
int (*adjust_levels)(struct dp_link *dp_link, u8 *link_status);
int (*send_psm_request)(struct dp_link *dp_link, bool req);
void (*send_test_response)(struct dp_link *dp_link);
int (*psm_config)(struct dp_link *dp_link,
struct drm_dp_link *link_info, bool enable);
void (*send_edid_checksum)(struct dp_link *dp_link, u8 checksum);
};
static inline char *dp_link_get_phy_test_pattern(u32 phy_test_pattern_sel)
{
switch (phy_test_pattern_sel) {
case DP_TEST_PHY_PATTERN_NONE:
return DP_LINK_ENUM_STR(DP_TEST_PHY_PATTERN_NONE);
case DP_TEST_PHY_PATTERN_D10_2_NO_SCRAMBLING:
return DP_LINK_ENUM_STR(
DP_TEST_PHY_PATTERN_D10_2_NO_SCRAMBLING);
case DP_TEST_PHY_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT:
return DP_LINK_ENUM_STR(
DP_TEST_PHY_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT);
case DP_TEST_PHY_PATTERN_PRBS7:
return DP_LINK_ENUM_STR(DP_TEST_PHY_PATTERN_PRBS7);
case DP_TEST_PHY_PATTERN_80_BIT_CUSTOM_PATTERN:
return DP_LINK_ENUM_STR(
DP_TEST_PHY_PATTERN_80_BIT_CUSTOM_PATTERN);
case DP_TEST_PHY_PATTERN_CP2520_PATTERN_1:
return DP_LINK_ENUM_STR(DP_TEST_PHY_PATTERN_CP2520_PATTERN_1);
case DP_TEST_PHY_PATTERN_CP2520_PATTERN_2:
return DP_LINK_ENUM_STR(DP_TEST_PHY_PATTERN_CP2520_PATTERN_2);
case DP_TEST_PHY_PATTERN_CP2520_PATTERN_3:
return DP_LINK_ENUM_STR(DP_TEST_PHY_PATTERN_CP2520_PATTERN_3);
default:
return "unknown";
}
}
/**
* mdss_dp_test_bit_depth_to_bpp() - convert test bit depth to bpp
* @tbd: test bit depth
*
* Returns the bits per pixel (bpp) to be used corresponding to the
* git bit depth value. This function assumes that bit depth has
* already been validated.
*/
static inline u32 dp_link_bit_depth_to_bpp(u32 tbd)
{
u32 bpp;
/*
* Few simplistic rules and assumptions made here:
* 1. Bit depth is per color component
* 2. If bit depth is unknown return 0
* 3. Assume 3 color components
*/
switch (tbd) {
case DP_TEST_BIT_DEPTH_6:
bpp = 18;
break;
case DP_TEST_BIT_DEPTH_8:
bpp = 24;
break;
case DP_TEST_BIT_DEPTH_10:
bpp = 30;
break;
case DP_TEST_BIT_DEPTH_UNKNOWN:
default:
bpp = 0;
}
return bpp;
}
/**
* dp_link_get() - get the functionalities of dp test module
*
*
* return: a pointer to dp_link struct
*/
struct dp_link *dp_link_get(struct device *dev, struct dp_aux *aux);
/**
* dp_link_put() - releases the dp test module's resources
*
* @dp_link: an instance of dp_link module
*
*/
void dp_link_put(struct dp_link *dp_link);
#endif /* _DP_LINK_H_ */

View File

@ -0,0 +1,421 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
*/
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/sde_io_util.h>
#include <linux/of_gpio.h>
#include "dp_lphw_hpd.h"
#include "dp_debug.h"
struct dp_lphw_hpd_private {
struct device *dev;
struct dp_hpd base;
struct dp_parser *parser;
struct dp_catalog_hpd *catalog;
struct dss_gpio gpio_cfg;
struct workqueue_struct *connect_wq;
struct delayed_work work;
struct work_struct connect;
struct work_struct disconnect;
struct work_struct attention;
struct dp_hpd_cb *cb;
int irq;
bool hpd;
};
static void dp_lphw_hpd_attention(struct work_struct *work)
{
struct dp_lphw_hpd_private *lphw_hpd = container_of(work,
struct dp_lphw_hpd_private, attention);
if (!lphw_hpd) {
DP_ERR("invalid input\n");
return;
}
lphw_hpd->base.hpd_irq = true;
if (lphw_hpd->cb && lphw_hpd->cb->attention)
lphw_hpd->cb->attention(lphw_hpd->dev);
}
static void dp_lphw_hpd_connect(struct work_struct *work)
{
struct dp_lphw_hpd_private *lphw_hpd = container_of(work,
struct dp_lphw_hpd_private, connect);
if (!lphw_hpd) {
DP_ERR("invalid input\n");
return;
}
lphw_hpd->base.hpd_high = true;
lphw_hpd->base.alt_mode_cfg_done = true;
lphw_hpd->base.hpd_irq = false;
if (lphw_hpd->cb && lphw_hpd->cb->configure)
lphw_hpd->cb->configure(lphw_hpd->dev);
}
static void dp_lphw_hpd_disconnect(struct work_struct *work)
{
struct dp_lphw_hpd_private *lphw_hpd = container_of(work,
struct dp_lphw_hpd_private, disconnect);
if (!lphw_hpd) {
DP_ERR("invalid input\n");
return;
}
lphw_hpd->base.hpd_high = false;
lphw_hpd->base.alt_mode_cfg_done = false;
lphw_hpd->base.hpd_irq = false;
if (lphw_hpd->cb && lphw_hpd->cb->disconnect)
lphw_hpd->cb->disconnect(lphw_hpd->dev);
}
static irqreturn_t dp_tlmm_isr(int unused, void *data)
{
struct dp_lphw_hpd_private *lphw_hpd = data;
bool hpd;
if (!lphw_hpd)
return IRQ_NONE;
/*
* According to the DP spec, HPD high event can be confirmed only after
* the HPD line has een asserted continuously for more than 100ms
*/
usleep_range(99000, 100000);
hpd = gpio_get_value_cansleep(lphw_hpd->gpio_cfg.gpio);
DP_DEBUG("lphw_hpd state = %d, new hpd state = %d\n",
lphw_hpd->hpd, hpd);
if (!lphw_hpd->hpd && hpd) {
lphw_hpd->hpd = true;
queue_work(lphw_hpd->connect_wq, &lphw_hpd->connect);
}
return IRQ_HANDLED;
}
static void dp_lphw_hpd_host_init(struct dp_hpd *dp_hpd,
struct dp_catalog_hpd *catalog)
{
struct dp_lphw_hpd_private *lphw_hpd;
if (!dp_hpd) {
DP_ERR("invalid input\n");
return;
}
lphw_hpd = container_of(dp_hpd, struct dp_lphw_hpd_private, base);
lphw_hpd->catalog->config_hpd(lphw_hpd->catalog, true);
/*
* Changing the gpio function to dp controller for the hpd line is not
* stopping the tlmm interrupts generation on function 0.
* So, as an additional step, disable the gpio interrupt irq also
*/
disable_irq(lphw_hpd->irq);
}
static void dp_lphw_hpd_host_deinit(struct dp_hpd *dp_hpd,
struct dp_catalog_hpd *catalog)
{
struct dp_lphw_hpd_private *lphw_hpd;
if (!dp_hpd) {
DP_ERR("invalid input\n");
return;
}
lphw_hpd = container_of(dp_hpd, struct dp_lphw_hpd_private, base);
/* Enable the tlmm interrupt irq which was disabled in host_init */
enable_irq(lphw_hpd->irq);
lphw_hpd->catalog->config_hpd(lphw_hpd->catalog, false);
}
static void dp_lphw_hpd_isr(struct dp_hpd *dp_hpd)
{
struct dp_lphw_hpd_private *lphw_hpd;
u32 isr = 0;
int rc = 0;
if (!dp_hpd) {
DP_ERR("invalid input\n");
return;
}
lphw_hpd = container_of(dp_hpd, struct dp_lphw_hpd_private, base);
isr = lphw_hpd->catalog->get_interrupt(lphw_hpd->catalog);
if (isr & DP_HPD_UNPLUG_INT_STATUS) { /* disconnect interrupt */
DP_DEBUG("disconnect interrupt, hpd isr state: 0x%x\n", isr);
if (lphw_hpd->base.hpd_high) {
lphw_hpd->hpd = false;
lphw_hpd->base.hpd_high = false;
lphw_hpd->base.alt_mode_cfg_done = false;
lphw_hpd->base.hpd_irq = false;
rc = queue_work(lphw_hpd->connect_wq,
&lphw_hpd->disconnect);
if (!rc)
DP_DEBUG("disconnect not queued\n");
} else {
DP_ERR("already disconnected\n");
}
} else if (isr & DP_IRQ_HPD_INT_STATUS) { /* attention interrupt */
DP_DEBUG("hpd_irq interrupt, hpd isr state: 0x%x\n", isr);
rc = queue_work(lphw_hpd->connect_wq, &lphw_hpd->attention);
if (!rc)
DP_DEBUG("attention not queued\n");
}
}
static int dp_lphw_hpd_simulate_connect(struct dp_hpd *dp_hpd, bool hpd)
{
struct dp_lphw_hpd_private *lphw_hpd;
if (!dp_hpd) {
DP_ERR("invalid input\n");
return -EINVAL;
}
lphw_hpd = container_of(dp_hpd, struct dp_lphw_hpd_private, base);
lphw_hpd->base.hpd_high = hpd;
lphw_hpd->base.alt_mode_cfg_done = hpd;
lphw_hpd->base.hpd_irq = false;
if (!lphw_hpd->cb || !lphw_hpd->cb->configure ||
!lphw_hpd->cb->disconnect) {
DP_ERR("invalid callback\n");
return -EINVAL;
}
if (hpd)
lphw_hpd->cb->configure(lphw_hpd->dev);
else
lphw_hpd->cb->disconnect(lphw_hpd->dev);
return 0;
}
static int dp_lphw_hpd_simulate_attention(struct dp_hpd *dp_hpd, int vdo)
{
struct dp_lphw_hpd_private *lphw_hpd;
if (!dp_hpd) {
DP_ERR("invalid input\n");
return -EINVAL;
}
lphw_hpd = container_of(dp_hpd, struct dp_lphw_hpd_private, base);
lphw_hpd->base.hpd_irq = true;
if (lphw_hpd->cb && lphw_hpd->cb->attention)
lphw_hpd->cb->attention(lphw_hpd->dev);
return 0;
}
int dp_lphw_hpd_register(struct dp_hpd *dp_hpd)
{
struct dp_lphw_hpd_private *lphw_hpd;
int rc = 0;
if (!dp_hpd)
return -EINVAL;
lphw_hpd = container_of(dp_hpd, struct dp_lphw_hpd_private, base);
lphw_hpd->hpd = gpio_get_value_cansleep(lphw_hpd->gpio_cfg.gpio);
rc = devm_request_threaded_irq(lphw_hpd->dev, lphw_hpd->irq, NULL,
dp_tlmm_isr,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
"dp-gpio-intp", lphw_hpd);
if (rc) {
DP_ERR("Failed to request INTP threaded IRQ: %d\n", rc);
return rc;
}
enable_irq_wake(lphw_hpd->irq);
if (lphw_hpd->hpd)
queue_work(lphw_hpd->connect_wq, &lphw_hpd->connect);
return rc;
}
static void dp_lphw_hpd_deinit(struct dp_lphw_hpd_private *lphw_hpd)
{
struct dp_parser *parser = lphw_hpd->parser;
int i = 0;
for (i = 0; i < parser->mp[DP_PHY_PM].num_vreg; i++) {
if (!strcmp(parser->mp[DP_PHY_PM].vreg_config[i].vreg_name,
"hpd-pwr")) {
/* disable the hpd-pwr voltage regulator */
if (msm_dss_enable_vreg(
&parser->mp[DP_PHY_PM].vreg_config[i], 1,
false))
DP_ERR("hpd-pwr vreg not disabled\n");
break;
}
}
}
static void dp_lphw_hpd_init(struct dp_lphw_hpd_private *lphw_hpd)
{
struct dp_pinctrl pinctrl = {0};
struct dp_parser *parser = lphw_hpd->parser;
int i = 0, rc = 0;
for (i = 0; i < parser->mp[DP_PHY_PM].num_vreg; i++) {
if (!strcmp(parser->mp[DP_PHY_PM].vreg_config[i].vreg_name,
"hpd-pwr")) {
/* enable the hpd-pwr voltage regulator */
if (msm_dss_enable_vreg(
&parser->mp[DP_PHY_PM].vreg_config[i], 1,
true))
DP_ERR("hpd-pwr vreg not enabled\n");
break;
}
}
pinctrl.pin = devm_pinctrl_get(lphw_hpd->dev);
if (!IS_ERR_OR_NULL(pinctrl.pin)) {
pinctrl.state_hpd_active = pinctrl_lookup_state(pinctrl.pin,
"mdss_dp_hpd_active");
if (!IS_ERR_OR_NULL(pinctrl.state_hpd_active)) {
rc = pinctrl_select_state(pinctrl.pin,
pinctrl.state_hpd_active);
if (rc)
DP_ERR("failed to set hpd_active state\n");
}
}
}
static int dp_lphw_hpd_create_workqueue(struct dp_lphw_hpd_private *lphw_hpd)
{
lphw_hpd->connect_wq = create_singlethread_workqueue("dp_lphw_work");
if (IS_ERR_OR_NULL(lphw_hpd->connect_wq)) {
DP_ERR("Error creating connect_wq\n");
return -EPERM;
}
INIT_WORK(&lphw_hpd->connect, dp_lphw_hpd_connect);
INIT_WORK(&lphw_hpd->disconnect, dp_lphw_hpd_disconnect);
INIT_WORK(&lphw_hpd->attention, dp_lphw_hpd_attention);
return 0;
}
struct dp_hpd *dp_lphw_hpd_get(struct device *dev, struct dp_parser *parser,
struct dp_catalog_hpd *catalog, struct dp_hpd_cb *cb)
{
int rc = 0;
const char *hpd_gpio_name = "qcom,dp-hpd-gpio";
struct dp_lphw_hpd_private *lphw_hpd;
if (!dev || !parser || !cb) {
DP_ERR("invalid device\n");
rc = -EINVAL;
goto error;
}
lphw_hpd = devm_kzalloc(dev, sizeof(*lphw_hpd), GFP_KERNEL);
if (!lphw_hpd) {
rc = -ENOMEM;
goto error;
}
lphw_hpd->gpio_cfg.gpio = of_get_named_gpio(dev->of_node,
hpd_gpio_name, 0);
if (!gpio_is_valid(lphw_hpd->gpio_cfg.gpio)) {
DP_ERR("%s gpio not specified\n", hpd_gpio_name);
rc = -EINVAL;
goto gpio_error;
}
strlcpy(lphw_hpd->gpio_cfg.gpio_name, hpd_gpio_name,
sizeof(lphw_hpd->gpio_cfg.gpio_name));
lphw_hpd->gpio_cfg.value = 0;
rc = gpio_request(lphw_hpd->gpio_cfg.gpio,
lphw_hpd->gpio_cfg.gpio_name);
if (rc) {
DP_ERR("%s: failed to request gpio\n", hpd_gpio_name);
goto gpio_error;
}
gpio_direction_input(lphw_hpd->gpio_cfg.gpio);
lphw_hpd->dev = dev;
lphw_hpd->cb = cb;
lphw_hpd->irq = gpio_to_irq(lphw_hpd->gpio_cfg.gpio);
rc = dp_lphw_hpd_create_workqueue(lphw_hpd);
if (rc) {
DP_ERR("Failed to create a dp_hpd workqueue\n");
goto gpio_error;
}
lphw_hpd->parser = parser;
lphw_hpd->catalog = catalog;
lphw_hpd->base.isr = dp_lphw_hpd_isr;
lphw_hpd->base.host_init = dp_lphw_hpd_host_init;
lphw_hpd->base.host_deinit = dp_lphw_hpd_host_deinit;
lphw_hpd->base.simulate_connect = dp_lphw_hpd_simulate_connect;
lphw_hpd->base.simulate_attention = dp_lphw_hpd_simulate_attention;
lphw_hpd->base.register_hpd = dp_lphw_hpd_register;
dp_lphw_hpd_init(lphw_hpd);
return &lphw_hpd->base;
gpio_error:
devm_kfree(dev, lphw_hpd);
error:
return ERR_PTR(rc);
}
void dp_lphw_hpd_put(struct dp_hpd *dp_hpd)
{
struct dp_lphw_hpd_private *lphw_hpd;
if (!dp_hpd)
return;
lphw_hpd = container_of(dp_hpd, struct dp_lphw_hpd_private, base);
dp_lphw_hpd_deinit(lphw_hpd);
gpio_free(lphw_hpd->gpio_cfg.gpio);
devm_kfree(lphw_hpd->dev, lphw_hpd);
}

View File

@ -0,0 +1,36 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
*/
#ifndef _DP_LPHW_HPD_H_
#define _DP_LPHW_HPD_H_
#include "dp_hpd.h"
#define DP_HPD_PLUG_INT_STATUS BIT(0)
#define DP_IRQ_HPD_INT_STATUS BIT(1)
#define DP_HPD_REPLUG_INT_STATUS BIT(2)
#define DP_HPD_UNPLUG_INT_STATUS BIT(3)
/**
* dp_lphw_hpd_get() - configure and get the DisplayPlot HPD module data
*
* @dev: device instance of the caller
* return: pointer to allocated gpio hpd module data
*
* This function sets up the lphw hpd module
*/
struct dp_hpd *dp_lphw_hpd_get(struct device *dev, struct dp_parser *parser,
struct dp_catalog_hpd *catalog, struct dp_hpd_cb *cb);
/**
* dp_lphw_hpd_put()
*
* Cleans up dp_hpd instance
*
* @hpd: instance of lphw_hpd
*/
void dp_lphw_hpd_put(struct dp_hpd *hpd);
#endif /* _DP_LPHW_HPD_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,65 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DP_MST_DRM_H_
#define _DP_MST_DRM_H_
#include <linux/types.h>
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include "dp_display.h"
#if IS_ENABLED(CONFIG_DRM_MSM_DP_MST)
/**
* dp_mst_drm_bridge_init - initialize mst bridge
* @display: Pointer to private display structure
* @encoder: Pointer to encoder for mst bridge mapping
*/
int dp_mst_drm_bridge_init(void *display,
struct drm_encoder *encoder);
/**
* dp_mst_drm_bridge_deinit - de-initialize mst bridges
* @display: Pointer to private display structure
*/
void dp_mst_drm_bridge_deinit(void *display);
/**
* dp_mst_init - initialize mst objects for the given display
* @display: Pointer to private display structure
*/
int dp_mst_init(struct dp_display *dp_display);
/**
* dp_mst_deinit - de-initialize mst objects for the given display
* @display: Pointer to private display structure
*/
void dp_mst_deinit(struct dp_display *dp_display);
#else
static inline int dp_mst_drm_bridge_init(void *display,
struct drm_encoder *encoder)
{
return 0;
}
static inline void dp_mst_drm_bridge_deinit(void *display)
{
}
static inline int dp_mst_init(struct dp_display *dp_display)
{
return 0;
}
static inline int dp_mst_deinit(struct dp_display *dp_display)
{
return 0;
}
#endif /* CONFIG_DRM_MSM_DP_MST */
#endif /* _DP_MST_DRM_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,235 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DP_PANEL_H_
#define _DP_PANEL_H_
#include <drm/sde_drm.h>
#include "dp_aux.h"
#include "dp_link.h"
#include "sde_edid_parser.h"
#include "sde_connector.h"
#include "msm_drv.h"
#define DP_RECEIVER_DSC_CAP_SIZE 15
#define DP_RECEIVER_FEC_STATUS_SIZE 3
#define DP_RECEIVER_EXT_CAP_SIZE 4
/*
* A source initiated power down flag is set
* when the DP is powered off while physical
* DP cable is still connected i.e. without
* HPD or not initiated by sink like HPD_IRQ.
* This can happen if framework reboots or
* device suspends.
*/
#define DP_PANEL_SRC_INITIATED_POWER_DOWN BIT(0)
#define DP_EXT_REC_CAP_FIELD BIT(7)
enum dp_lane_count {
DP_LANE_COUNT_1 = 1,
DP_LANE_COUNT_2 = 2,
DP_LANE_COUNT_4 = 4,
};
#define DP_MAX_DOWNSTREAM_PORTS 0x10
struct dp_panel_info {
u32 h_active;
u32 v_active;
u32 h_back_porch;
u32 h_front_porch;
u32 h_sync_width;
u32 h_active_low;
u32 v_back_porch;
u32 v_front_porch;
u32 v_sync_width;
u32 v_active_low;
u32 h_skew;
u32 refresh_rate;
u32 pixel_clk_khz;
u32 bpp;
bool widebus_en;
struct msm_compression_info comp_info;
s64 dsc_overhead_fp;
};
struct dp_display_mode {
struct dp_panel_info timing;
u32 capabilities;
s64 fec_overhead_fp;
s64 dsc_overhead_fp;
};
struct dp_panel;
struct dp_panel_in {
struct device *dev;
struct dp_aux *aux;
struct dp_link *link;
struct dp_catalog_panel *catalog;
struct drm_connector *connector;
struct dp_panel *base_panel;
struct dp_parser *parser;
};
struct dp_dsc_caps {
bool dsc_capable;
u8 version;
bool block_pred_en;
u8 color_depth;
};
struct dp_audio;
#define DP_PANEL_CAPS_DSC BIT(0)
struct dp_panel {
/* dpcd raw data */
u8 dpcd[DP_RECEIVER_CAP_SIZE + DP_RECEIVER_EXT_CAP_SIZE + 1];
u8 ds_ports[DP_MAX_DOWNSTREAM_PORTS];
u8 dsc_dpcd[DP_RECEIVER_DSC_CAP_SIZE + 1];
u8 fec_dpcd;
u8 fec_sts_dpcd[DP_RECEIVER_FEC_STATUS_SIZE + 1];
struct drm_dp_link link_info;
struct sde_edid_ctrl *edid_ctrl;
struct dp_panel_info pinfo;
bool video_test;
bool spd_enabled;
u32 vic;
u32 max_pclk_khz;
s64 mst_target_sc;
/* debug */
u32 max_bw_code;
u32 lane_count;
u32 link_bw_code;
/* By default, stream_id is assigned to DP_INVALID_STREAM.
* Client sets the stream id value using set_stream_id interface.
*/
enum dp_stream_id stream_id;
int vcpi;
u32 channel_start_slot;
u32 channel_total_slots;
u32 pbn;
u32 tot_dsc_blks_in_use;
/* DRM connector assosiated with this panel */
struct drm_connector *connector;
struct dp_audio *audio;
bool audio_supported;
struct dp_dsc_caps sink_dsc_caps;
bool dsc_feature_enable;
bool fec_feature_enable;
bool dsc_en;
bool fec_en;
bool widebus_en;
bool dsc_continuous_pps;
bool mst_state;
s64 fec_overhead_fp;
int (*init)(struct dp_panel *dp_panel);
int (*deinit)(struct dp_panel *dp_panel, u32 flags);
int (*hw_cfg)(struct dp_panel *dp_panel, bool enable);
int (*read_sink_caps)(struct dp_panel *dp_panel,
struct drm_connector *connector, bool multi_func);
u32 (*get_mode_bpp)(struct dp_panel *dp_panel, u32 mode_max_bpp,
u32 mode_pclk_khz);
int (*get_modes)(struct dp_panel *dp_panel,
struct drm_connector *connector, struct dp_display_mode *mode);
void (*handle_sink_request)(struct dp_panel *dp_panel);
int (*set_edid)(struct dp_panel *dp_panel, u8 *edid, size_t edid_size);
int (*set_dpcd)(struct dp_panel *dp_panel, u8 *dpcd);
int (*setup_hdr)(struct dp_panel *dp_panel,
struct drm_msm_ext_hdr_metadata *hdr_meta,
bool dhdr_update, u64 core_clk_rate, bool flush);
int (*set_colorspace)(struct dp_panel *dp_panel,
u32 colorspace);
void (*tpg_config)(struct dp_panel *dp_panel, bool enable);
int (*spd_config)(struct dp_panel *dp_panel);
bool (*hdr_supported)(struct dp_panel *dp_panel);
int (*set_stream_info)(struct dp_panel *dp_panel,
enum dp_stream_id stream_id, u32 ch_start_slot,
u32 ch_tot_slots, u32 pbn, int vcpi);
int (*read_sink_status)(struct dp_panel *dp_panel, u8 *sts, u32 size);
int (*update_edid)(struct dp_panel *dp_panel, struct edid *edid);
bool (*read_mst_cap)(struct dp_panel *dp_panel);
void (*convert_to_dp_mode)(struct dp_panel *dp_panel,
const struct drm_display_mode *drm_mode,
struct dp_display_mode *dp_mode);
void (*update_pps)(struct dp_panel *dp_panel, char *pps_cmd);
};
struct dp_tu_calc_input {
u64 lclk; /* 162, 270, 540 and 810 */
u64 pclk_khz; /* in KHz */
u64 hactive; /* active h-width */
u64 hporch; /* bp + fp + pulse */
int nlanes; /* no.of.lanes */
int bpp; /* bits */
int pixel_enc; /* 444, 420, 422 */
int dsc_en; /* dsc on/off */
int async_en; /* async mode */
int fec_en; /* fec */
int compress_ratio; /* 2:1 = 200, 3:1 = 300, 3.75:1 = 375 */
int num_of_dsc_slices; /* number of slices per line */
};
struct dp_vc_tu_mapping_table {
u32 vic;
u8 lanes;
u8 lrate; /* DP_LINK_RATE -> 162(6), 270(10), 540(20), 810 (30) */
u8 bpp;
u32 valid_boundary_link;
u32 delay_start_link;
bool boundary_moderation_en;
u32 valid_lower_boundary_link;
u32 upper_boundary_count;
u32 lower_boundary_count;
u32 tu_size_minus1;
};
/**
* is_link_rate_valid() - validates the link rate
* @lane_rate: link rate requested by the sink
*
* Returns true if the requested link rate is supported.
*/
static inline bool is_link_rate_valid(u32 bw_code)
{
return ((bw_code == DP_LINK_BW_1_62) ||
(bw_code == DP_LINK_BW_2_7) ||
(bw_code == DP_LINK_BW_5_4) ||
(bw_code == DP_LINK_BW_8_1));
}
/**
* dp_link_is_lane_count_valid() - validates the lane count
* @lane_count: lane count requested by the sink
*
* Returns true if the requested lane count is supported.
*/
static inline bool is_lane_count_valid(u32 lane_count)
{
return (lane_count == DP_LANE_COUNT_1) ||
(lane_count == DP_LANE_COUNT_2) ||
(lane_count == DP_LANE_COUNT_4);
}
struct dp_panel *dp_panel_get(struct dp_panel_in *in);
void dp_panel_put(struct dp_panel *dp_panel);
void dp_panel_calc_tu_test(struct dp_tu_calc_input *in,
struct dp_vc_tu_mapping_table *tu_table);
#endif /* _DP_PANEL_H_ */

View File

@ -0,0 +1,924 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include "dp_parser.h"
#include "dp_debug.h"
static void dp_parser_unmap_io_resources(struct dp_parser *parser)
{
int i = 0;
struct dp_io *io = &parser->io;
for (i = 0; i < io->len; i++)
msm_dss_iounmap(&io->data[i].io);
}
static int dp_parser_reg(struct dp_parser *parser)
{
int rc = 0, i = 0;
u32 reg_count;
struct platform_device *pdev = parser->pdev;
struct dp_io *io = &parser->io;
struct device *dev = &pdev->dev;
reg_count = of_property_count_strings(dev->of_node, "reg-names");
if (reg_count <= 0) {
DP_ERR("no reg defined\n");
return -EINVAL;
}
io->len = reg_count;
io->data = devm_kzalloc(dev, sizeof(struct dp_io_data) * reg_count,
GFP_KERNEL);
if (!io->data)
return -ENOMEM;
for (i = 0; i < reg_count; i++) {
of_property_read_string_index(dev->of_node,
"reg-names", i, &io->data[i].name);
rc = msm_dss_ioremap_byname(pdev, &io->data[i].io,
io->data[i].name);
if (rc) {
DP_ERR("unable to remap %s resources\n",
io->data[i].name);
goto err;
}
}
return 0;
err:
dp_parser_unmap_io_resources(parser);
return rc;
}
static const char *dp_get_phy_aux_config_property(u32 cfg_type)
{
switch (cfg_type) {
case PHY_AUX_CFG0:
return "qcom,aux-cfg0-settings";
case PHY_AUX_CFG1:
return "qcom,aux-cfg1-settings";
case PHY_AUX_CFG2:
return "qcom,aux-cfg2-settings";
case PHY_AUX_CFG3:
return "qcom,aux-cfg3-settings";
case PHY_AUX_CFG4:
return "qcom,aux-cfg4-settings";
case PHY_AUX_CFG5:
return "qcom,aux-cfg5-settings";
case PHY_AUX_CFG6:
return "qcom,aux-cfg6-settings";
case PHY_AUX_CFG7:
return "qcom,aux-cfg7-settings";
case PHY_AUX_CFG8:
return "qcom,aux-cfg8-settings";
case PHY_AUX_CFG9:
return "qcom,aux-cfg9-settings";
default:
return "unknown";
}
}
static void dp_parser_phy_aux_cfg_reset(struct dp_parser *parser)
{
int i = 0;
for (i = 0; i < PHY_AUX_CFG_MAX; i++)
parser->aux_cfg[i] = (const struct dp_aux_cfg){ 0 };
}
static int dp_parser_aux(struct dp_parser *parser)
{
struct device_node *of_node = parser->pdev->dev.of_node;
int len = 0, i = 0, j = 0, config_count = 0;
const char *data;
int const minimum_config_count = 1;
for (i = 0; i < PHY_AUX_CFG_MAX; i++) {
const char *property = dp_get_phy_aux_config_property(i);
data = of_get_property(of_node, property, &len);
if (!data) {
DP_ERR("Unable to read %s\n", property);
goto error;
}
config_count = len - 1;
if ((config_count < minimum_config_count) ||
(config_count > DP_AUX_CFG_MAX_VALUE_CNT)) {
DP_ERR("Invalid config count (%d) configs for %s\n",
config_count, property);
goto error;
}
parser->aux_cfg[i].offset = data[0];
parser->aux_cfg[i].cfg_cnt = config_count;
DP_DEBUG("%s offset=0x%x, cfg_cnt=%d\n",
property,
parser->aux_cfg[i].offset,
parser->aux_cfg[i].cfg_cnt);
for (j = 1; j < len; j++) {
parser->aux_cfg[i].lut[j - 1] = data[j];
DP_DEBUG("%s lut[%d]=0x%x\n",
property,
i,
parser->aux_cfg[i].lut[j - 1]);
}
}
return 0;
error:
dp_parser_phy_aux_cfg_reset(parser);
return -EINVAL;
}
static int dp_parser_misc(struct dp_parser *parser)
{
int rc = 0, len = 0, i = 0;
const char *data = NULL;
struct device_node *of_node = parser->pdev->dev.of_node;
data = of_get_property(of_node, "qcom,logical2physical-lane-map", &len);
if (data && (len == DP_MAX_PHY_LN)) {
for (i = 0; i < len; i++)
parser->l_map[i] = data[i];
}
data = of_get_property(of_node, "qcom,pn-swap-lane-map", &len);
if (data && (len == DP_MAX_PHY_LN)) {
for (i = 0; i < len; i++)
parser->l_pnswap |= (data[i] & 0x01) << i;
}
rc = of_property_read_u32(of_node,
"qcom,max-pclk-frequency-khz", &parser->max_pclk_khz);
if (rc)
parser->max_pclk_khz = DP_MAX_PIXEL_CLK_KHZ;
rc = of_property_read_u32(of_node,
"qcom,max-lclk-frequency-khz", &parser->max_lclk_khz);
if (rc)
parser->max_lclk_khz = DP_MAX_LINK_CLK_KHZ;
return 0;
}
static int dp_parser_msm_hdcp_dev(struct dp_parser *parser)
{
struct device_node *node;
struct platform_device *pdev;
node = of_find_compatible_node(NULL, NULL, "qcom,msm-hdcp");
if (!node) {
// This is a non-fatal error, module initialization can proceed
DP_WARN("couldn't find msm-hdcp node\n");
return 0;
}
pdev = of_find_device_by_node(node);
if (!pdev) {
// This is a non-fatal error, module initialization can proceed
DP_WARN("couldn't find msm-hdcp pdev\n");
return 0;
}
parser->msm_hdcp_dev = &pdev->dev;
return 0;
}
static int dp_parser_pinctrl(struct dp_parser *parser)
{
int rc = 0;
struct dp_pinctrl *pinctrl = &parser->pinctrl;
pinctrl->pin = devm_pinctrl_get(&parser->pdev->dev);
if (IS_ERR_OR_NULL(pinctrl->pin)) {
DP_DEBUG("failed to get pinctrl, rc=%d\n", rc);
goto error;
}
if (parser->no_aux_switch && parser->lphw_hpd) {
pinctrl->state_hpd_tlmm = pinctrl->state_hpd_ctrl = NULL;
pinctrl->state_hpd_tlmm = pinctrl_lookup_state(pinctrl->pin,
"mdss_dp_hpd_tlmm");
if (!IS_ERR_OR_NULL(pinctrl->state_hpd_tlmm)) {
pinctrl->state_hpd_ctrl = pinctrl_lookup_state(
pinctrl->pin, "mdss_dp_hpd_ctrl");
}
if (!pinctrl->state_hpd_tlmm || !pinctrl->state_hpd_ctrl) {
pinctrl->state_hpd_tlmm = NULL;
pinctrl->state_hpd_ctrl = NULL;
DP_DEBUG("tlmm or ctrl pinctrl state does not exist\n");
}
}
pinctrl->state_active = pinctrl_lookup_state(pinctrl->pin,
"mdss_dp_active");
if (IS_ERR_OR_NULL(pinctrl->state_active)) {
rc = PTR_ERR(pinctrl->state_active);
DP_ERR("failed to get pinctrl active state, rc=%d\n", rc);
goto error;
}
pinctrl->state_suspend = pinctrl_lookup_state(pinctrl->pin,
"mdss_dp_sleep");
if (IS_ERR_OR_NULL(pinctrl->state_suspend)) {
rc = PTR_ERR(pinctrl->state_suspend);
DP_ERR("failed to get pinctrl suspend state, rc=%d\n", rc);
goto error;
}
error:
return rc;
}
static int dp_parser_gpio(struct dp_parser *parser)
{
int i = 0;
struct device *dev = &parser->pdev->dev;
struct device_node *of_node = dev->of_node;
struct dss_module_power *mp = &parser->mp[DP_CORE_PM];
static const char * const dp_gpios[] = {
"qcom,aux-en-gpio",
"qcom,aux-sel-gpio",
"qcom,usbplug-cc-gpio",
};
if (of_find_property(of_node, "qcom,dp-hpd-gpio", NULL)) {
parser->no_aux_switch = true;
parser->lphw_hpd = of_find_property(of_node,
"qcom,dp-low-power-hw-hpd", NULL);
return 0;
}
if (of_find_property(of_node, "qcom,dp-gpio-aux-switch", NULL))
parser->gpio_aux_switch = true;
mp->gpio_config = devm_kzalloc(dev,
sizeof(struct dss_gpio) * ARRAY_SIZE(dp_gpios), GFP_KERNEL);
if (!mp->gpio_config)
return -ENOMEM;
mp->num_gpio = ARRAY_SIZE(dp_gpios);
for (i = 0; i < ARRAY_SIZE(dp_gpios); i++) {
mp->gpio_config[i].gpio = of_get_named_gpio(of_node,
dp_gpios[i], 0);
if (!gpio_is_valid(mp->gpio_config[i].gpio)) {
DP_DEBUG("%s gpio not specified\n", dp_gpios[i]);
/* In case any gpio was not specified, we think gpio
* aux switch also was not specified.
*/
parser->gpio_aux_switch = false;
continue;
}
strlcpy(mp->gpio_config[i].gpio_name, dp_gpios[i],
sizeof(mp->gpio_config[i].gpio_name));
mp->gpio_config[i].value = 0;
}
return 0;
}
static const char *dp_parser_supply_node_name(enum dp_pm_type module)
{
switch (module) {
case DP_CORE_PM: return "qcom,core-supply-entries";
case DP_CTRL_PM: return "qcom,ctrl-supply-entries";
case DP_PHY_PM: return "qcom,phy-supply-entries";
case DP_PLL_PM: return "qcom,pll-supply-entries";
default: return "???";
}
}
static int dp_parser_get_vreg(struct dp_parser *parser,
enum dp_pm_type module)
{
int i = 0, rc = 0;
u32 tmp = 0;
const char *pm_supply_name = NULL;
struct device_node *supply_node = NULL;
struct device_node *of_node = parser->pdev->dev.of_node;
struct device_node *supply_root_node = NULL;
struct dss_module_power *mp = &parser->mp[module];
mp->num_vreg = 0;
pm_supply_name = dp_parser_supply_node_name(module);
supply_root_node = of_get_child_by_name(of_node, pm_supply_name);
if (!supply_root_node) {
DP_WARN("no supply entry present: %s\n", pm_supply_name);
goto novreg;
}
mp->num_vreg = of_get_available_child_count(supply_root_node);
if (mp->num_vreg == 0) {
DP_DEBUG("no vreg\n");
goto novreg;
} else {
DP_DEBUG("vreg found. count=%d\n", mp->num_vreg);
}
mp->vreg_config = devm_kzalloc(&parser->pdev->dev,
sizeof(struct dss_vreg) * mp->num_vreg, GFP_KERNEL);
if (!mp->vreg_config) {
rc = -ENOMEM;
goto error;
}
for_each_child_of_node(supply_root_node, supply_node) {
const char *st = NULL;
/* vreg-name */
rc = of_property_read_string(supply_node,
"qcom,supply-name", &st);
if (rc) {
DP_ERR("error reading name. rc=%d\n",
rc);
goto error;
}
snprintf(mp->vreg_config[i].vreg_name,
ARRAY_SIZE((mp->vreg_config[i].vreg_name)), "%s", st);
/* vreg-min-voltage */
rc = of_property_read_u32(supply_node,
"qcom,supply-min-voltage", &tmp);
if (rc) {
DP_ERR("error reading min volt. rc=%d\n",
rc);
goto error;
}
mp->vreg_config[i].min_voltage = tmp;
/* vreg-max-voltage */
rc = of_property_read_u32(supply_node,
"qcom,supply-max-voltage", &tmp);
if (rc) {
DP_ERR("error reading max volt. rc=%d\n",
rc);
goto error;
}
mp->vreg_config[i].max_voltage = tmp;
/* enable-load */
rc = of_property_read_u32(supply_node,
"qcom,supply-enable-load", &tmp);
if (rc) {
DP_ERR("error reading enable load. rc=%d\n",
rc);
goto error;
}
mp->vreg_config[i].enable_load = tmp;
/* disable-load */
rc = of_property_read_u32(supply_node,
"qcom,supply-disable-load", &tmp);
if (rc) {
DP_ERR("error reading disable load. rc=%d\n",
rc);
goto error;
}
mp->vreg_config[i].disable_load = tmp;
DP_DEBUG("%s min=%d, max=%d, enable=%d, disable=%d\n",
mp->vreg_config[i].vreg_name,
mp->vreg_config[i].min_voltage,
mp->vreg_config[i].max_voltage,
mp->vreg_config[i].enable_load,
mp->vreg_config[i].disable_load
);
++i;
}
return rc;
error:
if (mp->vreg_config) {
devm_kfree(&parser->pdev->dev, mp->vreg_config);
mp->vreg_config = NULL;
}
novreg:
mp->num_vreg = 0;
return rc;
}
static void dp_parser_put_vreg_data(struct device *dev,
struct dss_module_power *mp)
{
if (!mp) {
DEV_ERR("invalid input\n");
return;
}
if (mp->vreg_config) {
devm_kfree(dev, mp->vreg_config);
mp->vreg_config = NULL;
}
mp->num_vreg = 0;
}
static int dp_parser_regulator(struct dp_parser *parser)
{
int i, rc = 0;
struct platform_device *pdev = parser->pdev;
/* Parse the regulator information */
for (i = DP_CORE_PM; i < DP_MAX_PM; i++) {
rc = dp_parser_get_vreg(parser, i);
if (rc) {
DP_ERR("get_dt_vreg_data failed for %s. rc=%d\n",
dp_parser_pm_name(i), rc);
i--;
for (; i >= DP_CORE_PM; i--)
dp_parser_put_vreg_data(&pdev->dev,
&parser->mp[i]);
break;
}
}
return rc;
}
static bool dp_parser_check_prefix(const char *clk_prefix, const char *clk_name)
{
return !!strnstr(clk_name, clk_prefix, strlen(clk_name));
}
static void dp_parser_put_clk_data(struct device *dev,
struct dss_module_power *mp)
{
if (!mp) {
DEV_ERR("%s: invalid input\n", __func__);
return;
}
if (mp->clk_config) {
devm_kfree(dev, mp->clk_config);
mp->clk_config = NULL;
}
mp->num_clk = 0;
}
static void dp_parser_put_gpio_data(struct device *dev,
struct dss_module_power *mp)
{
if (!mp) {
DEV_ERR("%s: invalid input\n", __func__);
return;
}
if (mp->gpio_config) {
devm_kfree(dev, mp->gpio_config);
mp->gpio_config = NULL;
}
mp->num_gpio = 0;
}
static int dp_parser_init_clk_data(struct dp_parser *parser)
{
int num_clk = 0, i = 0, rc = 0;
int core_clk_count = 0, link_clk_count = 0;
int strm0_clk_count = 0, strm1_clk_count = 0;
const char *core_clk = "core";
const char *strm0_clk = "strm0";
const char *strm1_clk = "strm1";
const char *link_clk = "link";
const char *clk_name;
struct device *dev = &parser->pdev->dev;
struct dss_module_power *core_power = &parser->mp[DP_CORE_PM];
struct dss_module_power *strm0_power = &parser->mp[DP_STREAM0_PM];
struct dss_module_power *strm1_power = &parser->mp[DP_STREAM1_PM];
struct dss_module_power *link_power = &parser->mp[DP_LINK_PM];
num_clk = of_property_count_strings(dev->of_node, "clock-names");
if (num_clk <= 0) {
DP_ERR("no clocks are defined\n");
rc = -EINVAL;
goto exit;
}
for (i = 0; i < num_clk; i++) {
of_property_read_string_index(dev->of_node,
"clock-names", i, &clk_name);
if (dp_parser_check_prefix(core_clk, clk_name))
core_clk_count++;
if (dp_parser_check_prefix(strm0_clk, clk_name))
strm0_clk_count++;
if (dp_parser_check_prefix(strm1_clk, clk_name))
strm1_clk_count++;
if (dp_parser_check_prefix(link_clk, clk_name))
link_clk_count++;
}
/* Initialize the CORE power module */
if (core_clk_count <= 0) {
DP_ERR("no core clocks are defined\n");
rc = -EINVAL;
goto exit;
}
core_power->num_clk = core_clk_count;
core_power->clk_config = devm_kzalloc(dev,
sizeof(struct dss_clk) * core_power->num_clk,
GFP_KERNEL);
if (!core_power->clk_config) {
rc = -EINVAL;
goto exit;
}
/* Initialize the STREAM0 power module */
if (strm0_clk_count <= 0) {
DP_DEBUG("no strm0 clocks are defined\n");
} else {
strm0_power->num_clk = strm0_clk_count;
strm0_power->clk_config = devm_kzalloc(dev,
sizeof(struct dss_clk) * strm0_power->num_clk,
GFP_KERNEL);
if (!strm0_power->clk_config) {
strm0_power->num_clk = 0;
rc = -EINVAL;
goto strm0_clock_error;
}
}
/* Initialize the STREAM1 power module */
if (strm1_clk_count <= 0) {
DP_DEBUG("no strm1 clocks are defined\n");
} else {
strm1_power->num_clk = strm1_clk_count;
strm1_power->clk_config = devm_kzalloc(dev,
sizeof(struct dss_clk) * strm1_power->num_clk,
GFP_KERNEL);
if (!strm1_power->clk_config) {
strm1_power->num_clk = 0;
rc = -EINVAL;
goto strm1_clock_error;
}
}
/* Initialize the link power module */
if (link_clk_count <= 0) {
DP_ERR("no link clocks are defined\n");
rc = -EINVAL;
goto link_clock_error;
}
link_power->num_clk = link_clk_count;
link_power->clk_config = devm_kzalloc(dev,
sizeof(struct dss_clk) * link_power->num_clk,
GFP_KERNEL);
if (!link_power->clk_config) {
link_power->num_clk = 0;
rc = -EINVAL;
goto link_clock_error;
}
return rc;
link_clock_error:
dp_parser_put_clk_data(dev, strm1_power);
strm1_clock_error:
dp_parser_put_clk_data(dev, strm0_power);
strm0_clock_error:
dp_parser_put_clk_data(dev, core_power);
exit:
return rc;
}
static int dp_parser_clock(struct dp_parser *parser)
{
int rc = 0, i = 0;
int num_clk = 0;
int core_clk_index = 0, link_clk_index = 0;
int core_clk_count = 0, link_clk_count = 0;
int strm0_clk_index = 0, strm1_clk_index = 0;
int strm0_clk_count = 0, strm1_clk_count = 0;
const char *clk_name;
const char *core_clk = "core";
const char *strm0_clk = "strm0";
const char *strm1_clk = "strm1";
const char *link_clk = "link";
struct device *dev = &parser->pdev->dev;
struct dss_module_power *core_power;
struct dss_module_power *strm0_power;
struct dss_module_power *strm1_power;
struct dss_module_power *link_power;
core_power = &parser->mp[DP_CORE_PM];
strm0_power = &parser->mp[DP_STREAM0_PM];
strm1_power = &parser->mp[DP_STREAM1_PM];
link_power = &parser->mp[DP_LINK_PM];
rc = dp_parser_init_clk_data(parser);
if (rc) {
DP_ERR("failed to initialize power data\n");
rc = -EINVAL;
goto exit;
}
core_clk_count = core_power->num_clk;
link_clk_count = link_power->num_clk;
strm0_clk_count = strm0_power->num_clk;
strm1_clk_count = strm1_power->num_clk;
num_clk = of_property_count_strings(dev->of_node, "clock-names");
for (i = 0; i < num_clk; i++) {
of_property_read_string_index(dev->of_node, "clock-names",
i, &clk_name);
if (dp_parser_check_prefix(core_clk, clk_name) &&
core_clk_index < core_clk_count) {
struct dss_clk *clk =
&core_power->clk_config[core_clk_index];
strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
clk->type = DSS_CLK_AHB;
core_clk_index++;
} else if (dp_parser_check_prefix(link_clk, clk_name) &&
link_clk_index < link_clk_count) {
struct dss_clk *clk =
&link_power->clk_config[link_clk_index];
strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
link_clk_index++;
if (!strcmp(clk_name, "link_clk"))
clk->type = DSS_CLK_PCLK;
else
clk->type = DSS_CLK_AHB;
} else if (dp_parser_check_prefix(strm0_clk, clk_name) &&
strm0_clk_index < strm0_clk_count) {
struct dss_clk *clk =
&strm0_power->clk_config[strm0_clk_index];
strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
strm0_clk_index++;
clk->type = DSS_CLK_PCLK;
} else if (dp_parser_check_prefix(strm1_clk, clk_name) &&
strm1_clk_index < strm1_clk_count) {
struct dss_clk *clk =
&strm1_power->clk_config[strm1_clk_index];
strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
strm1_clk_index++;
clk->type = DSS_CLK_PCLK;
}
}
DP_DEBUG("clock parsing successful\n");
exit:
return rc;
}
static int dp_parser_catalog(struct dp_parser *parser)
{
int rc;
u32 version;
struct device *dev = &parser->pdev->dev;
rc = of_property_read_u32(dev->of_node, "qcom,phy-version", &version);
if (!rc)
parser->hw_cfg.phy_version = version;
return 0;
}
static int dp_parser_mst(struct dp_parser *parser)
{
struct device *dev = &parser->pdev->dev;
int i;
parser->has_mst = of_property_read_bool(dev->of_node,
"qcom,mst-enable");
parser->has_mst_sideband = parser->has_mst;
DP_DEBUG("mst parsing successful. mst:%d\n", parser->has_mst);
for (i = 0; i < MAX_DP_MST_STREAMS; i++) {
of_property_read_u32_index(dev->of_node,
"qcom,mst-fixed-topology-ports", i,
&parser->mst_fixed_port[i]);
}
return 0;
}
static void dp_parser_dsc(struct dp_parser *parser)
{
struct device *dev = &parser->pdev->dev;
parser->dsc_feature_enable = of_property_read_bool(dev->of_node,
"qcom,dsc-feature-enable");
parser->dsc_continuous_pps = of_property_read_bool(dev->of_node,
"qcom,dsc-continuous-pps");
DP_DEBUG("dsc parsing successful. dsc:%d\n",
parser->dsc_feature_enable);
DP_DEBUG("cont_pps:%d\n",
parser->dsc_continuous_pps);
}
static void dp_parser_fec(struct dp_parser *parser)
{
struct device *dev = &parser->pdev->dev;
parser->fec_feature_enable = of_property_read_bool(dev->of_node,
"qcom,fec-feature-enable");
DP_DEBUG("fec parsing successful. fec:%d\n",
parser->fec_feature_enable);
}
static void dp_parser_widebus(struct dp_parser *parser)
{
struct device *dev = &parser->pdev->dev;
parser->has_widebus = of_property_read_bool(dev->of_node,
"qcom,widebus-enable");
DP_DEBUG("widebus parsing successful. widebus:%d\n",
parser->has_widebus);
}
static int dp_parser_parse(struct dp_parser *parser)
{
int rc = 0;
if (!parser) {
DP_ERR("invalid input\n");
rc = -EINVAL;
goto err;
}
rc = dp_parser_reg(parser);
if (rc)
goto err;
rc = dp_parser_aux(parser);
if (rc)
goto err;
rc = dp_parser_misc(parser);
if (rc)
goto err;
rc = dp_parser_clock(parser);
if (rc)
goto err;
rc = dp_parser_regulator(parser);
if (rc)
goto err;
rc = dp_parser_gpio(parser);
if (rc)
goto err;
rc = dp_parser_catalog(parser);
if (rc)
goto err;
rc = dp_parser_pinctrl(parser);
if (rc)
goto err;
rc = dp_parser_msm_hdcp_dev(parser);
if (rc)
goto err;
rc = dp_parser_mst(parser);
if (rc)
goto err;
dp_parser_dsc(parser);
dp_parser_fec(parser);
dp_parser_widebus(parser);
err:
return rc;
}
static struct dp_io_data *dp_parser_get_io(struct dp_parser *dp_parser,
char *name)
{
int i = 0;
struct dp_io *io;
if (!dp_parser) {
DP_ERR("invalid input\n");
goto err;
}
io = &dp_parser->io;
for (i = 0; i < io->len; i++) {
struct dp_io_data *data = &io->data[i];
if (!strcmp(data->name, name))
return data;
}
err:
return NULL;
}
static void dp_parser_get_io_buf(struct dp_parser *dp_parser, char *name)
{
int i = 0;
struct dp_io *io;
if (!dp_parser) {
DP_ERR("invalid input\n");
return;
}
io = &dp_parser->io;
for (i = 0; i < io->len; i++) {
struct dp_io_data *data = &io->data[i];
if (!strcmp(data->name, name)) {
if (!data->buf)
data->buf = devm_kzalloc(&dp_parser->pdev->dev,
data->io.len, GFP_KERNEL);
}
}
}
static void dp_parser_clear_io_buf(struct dp_parser *dp_parser)
{
int i = 0;
struct dp_io *io;
if (!dp_parser) {
DP_ERR("invalid input\n");
return;
}
io = &dp_parser->io;
for (i = 0; i < io->len; i++) {
struct dp_io_data *data = &io->data[i];
if (data->buf)
devm_kfree(&dp_parser->pdev->dev, data->buf);
data->buf = NULL;
}
}
struct dp_parser *dp_parser_get(struct platform_device *pdev)
{
struct dp_parser *parser;
parser = devm_kzalloc(&pdev->dev, sizeof(*parser), GFP_KERNEL);
if (!parser)
return ERR_PTR(-ENOMEM);
parser->parse = dp_parser_parse;
parser->get_io = dp_parser_get_io;
parser->get_io_buf = dp_parser_get_io_buf;
parser->clear_io_buf = dp_parser_clear_io_buf;
parser->pdev = pdev;
return parser;
}
void dp_parser_put(struct dp_parser *parser)
{
int i = 0;
struct dss_module_power *power = NULL;
if (!parser) {
DP_ERR("invalid parser module\n");
return;
}
power = parser->mp;
for (i = 0; i < DP_MAX_PM; i++) {
dp_parser_put_clk_data(&parser->pdev->dev, &power[i]);
dp_parser_put_vreg_data(&parser->pdev->dev, &power[i]);
dp_parser_put_gpio_data(&parser->pdev->dev, &power[i]);
}
dp_parser_clear_io_buf(parser);
devm_kfree(&parser->pdev->dev, parser->io.data);
devm_kfree(&parser->pdev->dev, parser);
}

View File

@ -0,0 +1,271 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DP_PARSER_H_
#define _DP_PARSER_H_
#include <linux/sde_io_util.h>
#define DP_LABEL "MDSS DP DISPLAY"
#define AUX_CFG_LEN 10
#define DP_MAX_PIXEL_CLK_KHZ 675000
#define DP_MAX_LINK_CLK_KHZ 810000
#define MAX_DP_MST_STREAMS 2
enum dp_pm_type {
DP_CORE_PM,
DP_CTRL_PM,
DP_PHY_PM,
DP_STREAM0_PM,
DP_STREAM1_PM,
DP_LINK_PM,
DP_PLL_PM,
DP_MAX_PM
};
static inline const char *dp_parser_pm_name(enum dp_pm_type module)
{
switch (module) {
case DP_CORE_PM: return "DP_CORE_PM";
case DP_CTRL_PM: return "DP_CTRL_PM";
case DP_PHY_PM: return "DP_PHY_PM";
case DP_STREAM0_PM: return "DP_STREAM0_PM";
case DP_STREAM1_PM: return "DP_STREAM1_PM";
case DP_LINK_PM: return "DP_LINK_PM";
case DP_PLL_PM: return "DP_PLL_PM";
default: return "???";
}
}
/**
* struct dp_display_data - display related device tree data.
*
* @ctrl_node: referece to controller device
* @phy_node: reference to phy device
* @is_active: is the controller currently active
* @name: name of the display
* @display_type: type of the display
*/
struct dp_display_data {
struct device_node *ctrl_node;
struct device_node *phy_node;
bool is_active;
const char *name;
const char *display_type;
};
/**
* struct dp_io_data - data structure to store DP IO related info
* @name: name of the IO
* @buf: buffer corresponding to IO for debugging
* @io: io data which give len and mapped address
*/
struct dp_io_data {
const char *name;
u8 *buf;
struct dss_io_data io;
};
/**
* struct dp_io - data struct to store array of DP IO info
* @len: total number of IOs
* @data: pointer to an array of DP IO data structures.
*/
struct dp_io {
u32 len;
struct dp_io_data *data;
};
/**
* struct dp_pinctrl - DP's pin control
*
* @pin: pin-controller's instance
* @state_active: active state pin control
* @state_hpd_active: hpd active state pin control
* @state_suspend: suspend state pin control
*/
struct dp_pinctrl {
struct pinctrl *pin;
struct pinctrl_state *state_active;
struct pinctrl_state *state_hpd_active;
struct pinctrl_state *state_hpd_tlmm;
struct pinctrl_state *state_hpd_ctrl;
struct pinctrl_state *state_suspend;
};
#define DP_ENUM_STR(x) #x
#define DP_AUX_CFG_MAX_VALUE_CNT 3
/**
* struct dp_aux_cfg - DP's AUX configuration settings
*
* @cfg_cnt: count of the configurable settings for the AUX register
* @current_index: current index of the AUX config lut
* @offset: register offset of the AUX config register
* @lut: look up table for the AUX config values for this register
*/
struct dp_aux_cfg {
u32 cfg_cnt;
u32 current_index;
u32 offset;
u32 lut[DP_AUX_CFG_MAX_VALUE_CNT];
};
/* PHY AUX config registers */
enum dp_phy_aux_config_type {
PHY_AUX_CFG0,
PHY_AUX_CFG1,
PHY_AUX_CFG2,
PHY_AUX_CFG3,
PHY_AUX_CFG4,
PHY_AUX_CFG5,
PHY_AUX_CFG6,
PHY_AUX_CFG7,
PHY_AUX_CFG8,
PHY_AUX_CFG9,
PHY_AUX_CFG_MAX,
};
/**
* enum dp_phy_version - version of the dp phy
* @DP_PHY_VERSION_UNKNOWN: Unknown controller version
* @DP_PHY_VERSION_4_2_0: DP phy v4.2.0 controller
* @DP_PHY_VERSION_MAX: max version
*/
enum dp_phy_version {
DP_PHY_VERSION_UNKNOWN,
DP_PHY_VERSION_2_0_0 = 0x200,
DP_PHY_VERSION_4_2_0 = 0x420,
DP_PHY_VERSION_MAX
};
/**
* struct dp_hw_cfg - DP HW specific configuration
*
* @phy_version: DP PHY HW version
*/
struct dp_hw_cfg {
enum dp_phy_version phy_version;
};
static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type)
{
switch (cfg_type) {
case PHY_AUX_CFG0:
return DP_ENUM_STR(PHY_AUX_CFG0);
case PHY_AUX_CFG1:
return DP_ENUM_STR(PHY_AUX_CFG1);
case PHY_AUX_CFG2:
return DP_ENUM_STR(PHY_AUX_CFG2);
case PHY_AUX_CFG3:
return DP_ENUM_STR(PHY_AUX_CFG3);
case PHY_AUX_CFG4:
return DP_ENUM_STR(PHY_AUX_CFG4);
case PHY_AUX_CFG5:
return DP_ENUM_STR(PHY_AUX_CFG5);
case PHY_AUX_CFG6:
return DP_ENUM_STR(PHY_AUX_CFG6);
case PHY_AUX_CFG7:
return DP_ENUM_STR(PHY_AUX_CFG7);
case PHY_AUX_CFG8:
return DP_ENUM_STR(PHY_AUX_CFG8);
case PHY_AUX_CFG9:
return DP_ENUM_STR(PHY_AUX_CFG9);
default:
return "unknown";
}
}
/**
* struct dp_parser - DP parser's data exposed to clients
*
* @pdev: platform data of the client
* @msm_hdcp_dev: device pointer for the HDCP driver
* @mp: gpio, regulator and clock related data
* @pinctrl: pin-control related data
* @disp_data: controller's display related data
* @l_pnswap: P/N swap status on each lane
* @max_pclk_khz: maximum pixel clock supported for the platform
* @max_lclk_khz: maximum link clock supported for the platform
* @hw_cfg: DP HW specific settings
* @has_mst: MST feature enable status
* @has_mst_sideband: MST sideband feature enable status
* @no_aux_switch: presence AUX switch status
* @gpio_aux_switch: presence GPIO AUX switch status
* @dsc_feature_enable: DSC feature enable status
* @fec_feature_enable: FEC feature enable status
* @dsc_continuous_pps: PPS sent every frame by HW
* @has_widebus: widebus (2PPC) feature eanble status
*@mst_fixed_port: mst port_num reserved for fixed topology
* @parse: function to be called by client to parse device tree.
* @get_io: function to be called by client to get io data.
* @get_io_buf: function to be called by client to get io buffers.
* @clear_io_buf: function to be called by client to clear io buffers.
*/
struct dp_parser {
struct platform_device *pdev;
struct device *msm_hdcp_dev;
struct dss_module_power mp[DP_MAX_PM];
struct dp_pinctrl pinctrl;
struct dp_io io;
struct dp_display_data disp_data;
u8 l_map[4];
u8 l_pnswap;
struct dp_aux_cfg aux_cfg[AUX_CFG_LEN];
u32 max_pclk_khz;
u32 max_lclk_khz;
struct dp_hw_cfg hw_cfg;
bool has_mst;
bool has_mst_sideband;
bool no_aux_switch;
bool dsc_feature_enable;
bool fec_feature_enable;
bool dsc_continuous_pps;
bool has_widebus;
bool gpio_aux_switch;
bool lphw_hpd;
u32 mst_fixed_port[MAX_DP_MST_STREAMS];
int (*parse)(struct dp_parser *parser);
struct dp_io_data *(*get_io)(struct dp_parser *parser, char *name);
void (*get_io_buf)(struct dp_parser *parser, char *name);
void (*clear_io_buf)(struct dp_parser *parser);
};
enum dp_phy_lane_num {
DP_PHY_LN0 = 0,
DP_PHY_LN1 = 1,
DP_PHY_LN2 = 2,
DP_PHY_LN3 = 3,
DP_MAX_PHY_LN = 4,
};
enum dp_mainlink_lane_num {
DP_ML0 = 0,
DP_ML1 = 1,
DP_ML2 = 2,
DP_ML3 = 3,
};
/**
* dp_parser_get() - get the DP's device tree parser module
*
* @pdev: platform data of the client
* return: pointer to dp_parser structure.
*
* This function provides client capability to parse the
* device tree and populate the data structures. The data
* related to clock, regulators, pin-control and other
* can be parsed using this module.
*/
struct dp_parser *dp_parser_get(struct platform_device *pdev);
/**
* dp_parser_put() - cleans the dp_parser module
*
* @parser: pointer to the parser's data.
*/
void dp_parser_put(struct dp_parser *parser);
#endif

View File

@ -0,0 +1,147 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
*/
#include <linux/err.h>
#include <linux/of_device.h>
#include "dp_debug.h"
#include "dp_pll.h"
static int dp_pll_fill_io(struct dp_pll *pll)
{
struct dp_parser *parser = pll->parser;
pll->io.dp_phy = parser->get_io(parser, "dp_phy");
if (!pll->io.dp_phy) {
DP_ERR("Invalid dp_phy resource\n");
return -ENOMEM;
}
pll->io.dp_pll = parser->get_io(parser, "dp_pll");
if (!pll->io.dp_pll) {
DP_ERR("Invalid dp_pll resource\n");
return -ENOMEM;
}
pll->io.dp_ln_tx0 = parser->get_io(parser, "dp_ln_tx0");
if (!pll->io.dp_ln_tx0) {
DP_ERR("Invalid dp_ln_tx1 resource\n");
return -ENOMEM;
}
pll->io.dp_ln_tx1 = parser->get_io(parser, "dp_ln_tx1");
if (!pll->io.dp_ln_tx1) {
DP_ERR("Invalid dp_ln_tx1 resource\n");
return -ENOMEM;
}
pll->io.gdsc = parser->get_io(parser, "gdsc");
if (!pll->io.gdsc) {
DP_ERR("Invalid gdsc resource\n");
return -ENOMEM;
}
return 0;
}
static int dp_pll_clock_register(struct dp_pll *pll)
{
int rc;
switch (pll->revision) {
case DP_PLL_5NM_V1:
case DP_PLL_5NM_V2:
case DP_PLL_7NM:
rc = dp_pll_clock_register_5nm(pll);
break;
default:
rc = -ENOTSUPP;
break;
}
return rc;
}
static void dp_pll_clock_unregister(struct dp_pll *pll)
{
switch (pll->revision) {
case DP_PLL_5NM_V1:
case DP_PLL_5NM_V2:
case DP_PLL_7NM:
dp_pll_clock_unregister_5nm(pll);
break;
default:
break;
}
}
struct dp_pll *dp_pll_get(struct dp_pll_in *in)
{
int rc = 0;
struct dp_pll *pll;
struct dp_parser *parser;
const char *label = NULL;
struct platform_device *pdev;
if (!in || !in->pdev || !in->pdev->dev.of_node || !in->parser) {
DP_ERR("Invalid resource pointers\n");
return ERR_PTR(-EINVAL);
}
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
if (!pll)
return ERR_PTR(-ENOMEM);
pll->pdev = in->pdev;
pll->parser = in->parser;
pll->aux = in->aux;
parser = pll->parser;
pdev = pll->pdev;
label = of_get_property(pdev->dev.of_node, "qcom,pll-revision", NULL);
if (label) {
if (!strcmp(label, "5nm-v1")) {
pll->revision = DP_PLL_5NM_V1;
} else if (!strcmp(label, "5nm-v2")) {
pll->revision = DP_PLL_5NM_V2;
} else if (!strcmp(label, "7nm")) {
pll->revision = DP_PLL_7NM;
} else {
DP_ERR("Unsupported pll revision\n");
rc = -ENOTSUPP;
goto error;
}
} else {
DP_ERR("pll revision not specified\n");
rc = -EINVAL;
goto error;
}
pll->ssc_en = of_property_read_bool(pdev->dev.of_node,
"qcom,ssc-feature-enable");
pll->bonding_en = of_property_read_bool(pdev->dev.of_node,
"qcom,bonding-feature-enable");
rc = dp_pll_fill_io(pll);
if (rc)
goto error;
rc = dp_pll_clock_register(pll);
if (rc)
goto error;
DP_INFO("revision=%s, ssc_en=%d, bonding_en=%d\n",
dp_pll_get_revision(pll->revision), pll->ssc_en,
pll->bonding_en);
return pll;
error:
kfree(pll);
return ERR_PTR(rc);
}
void dp_pll_put(struct dp_pll *pll)
{
dp_pll_clock_unregister(pll);
kfree(pll);
}

View File

@ -0,0 +1,144 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
*/
#ifndef __DP_PLL_H
#define __DP_PLL_H
#include <linux/io.h>
#include <linux/clk-provider.h>
#include <linux/of_device.h>
#include "dp_parser.h"
#include "sde_dbg.h"
#define DP_VCO_HSCLK_RATE_1620MHZDIV1000 1620000UL
#define DP_VCO_HSCLK_RATE_2700MHZDIV1000 2700000UL
#define DP_VCO_HSCLK_RATE_5400MHZDIV1000 5400000UL
#define DP_VCO_HSCLK_RATE_8100MHZDIV1000 8100000UL
#define dp_pll_get_base(x) pll->io.x->io.base
#define dp_pll_read(x, offset) ({ \
readl_relaxed((dp_pll_get_base(x)) + (offset)); \
})
#define dp_pll_write(x, offset, data) ({ \
DP_DEBUG(#offset", addr=0x%x, val=0x%x\n", \
(dp_pll_get_base(x)) + (offset), (data)); \
SDE_EVT32_VERBOSE((dp_pll_get_base(x)) + (offset), (data)); \
writel_relaxed((data), (dp_pll_get_base(x)) + (offset)); \
})
enum dp_pll_revision {
DP_PLL_UNKNOWN,
DP_PLL_5NM_V1,
DP_PLL_5NM_V2,
DP_PLL_7NM,
};
static inline const char *dp_pll_get_revision(enum dp_pll_revision rev)
{
switch (rev) {
case DP_PLL_UNKNOWN: return "DP_PLL_UNKNOWN";
case DP_PLL_5NM_V1: return "DP_PLL_5NM_V1";
case DP_PLL_5NM_V2: return "DP_PLL_5NM_V2";
case DP_PLL_7NM: return "DP_PLL_7NM";
default: return "???";
}
}
struct dp_pll_io {
struct dp_io_data *dp_phy;
struct dp_io_data *dp_pll;
struct dp_io_data *dp_ln_tx0;
struct dp_io_data *dp_ln_tx1;
struct dp_io_data *gdsc;
};
struct dp_pll_vco_clk {
struct clk_hw hw;
unsigned long rate; /* current vco rate */
u64 min_rate; /* min vco rate */
u64 max_rate; /* max vco rate */
void *priv;
};
struct dp_pll {
/*
* target pll revision information
*/
u32 revision;
/*
* Certain plls needs to update the same vco rate after resume in
* suspend/resume scenario. Cached the vco rate for such plls.
*/
unsigned long vco_cached_rate;
/*
* PLL index if multiple index are available. Eg. in case of
* DSI we have 2 plls.
*/
uint32_t index;
bool ssc_en;
bool bonding_en;
void *priv;
struct platform_device *pdev;
struct dp_parser *parser;
struct dp_power *power;
struct dp_aux *aux;
struct dp_pll_io io;
struct clk_onecell_data *clk_data;
};
struct dp_pll_db {
struct dp_pll *pll;
/* lane and orientation settings */
u8 lane_cnt;
u8 orientation;
/* COM PHY settings */
u32 hsclk_sel;
u32 dec_start_mode0;
u32 div_frac_start1_mode0;
u32 div_frac_start2_mode0;
u32 div_frac_start3_mode0;
u32 integloop_gain0_mode0;
u32 integloop_gain1_mode0;
u32 lock_cmp1_mode0;
u32 lock_cmp2_mode0;
u32 lock_cmp_en;
u32 ssc_step_size1_mode0;
u32 ssc_step_size2_mode0;
/* PHY vco divider */
u32 phy_vco_div;
};
static inline struct dp_pll_vco_clk *to_dp_vco_hw(struct clk_hw *hw)
{
return container_of(hw, struct dp_pll_vco_clk, hw);
}
static inline bool is_gdsc_disabled(struct dp_pll *pll)
{
return (dp_pll_read(gdsc, 0x0) & BIT(31)) ? false : true;
}
int dp_pll_clock_register_5nm(struct dp_pll *pll);
void dp_pll_clock_unregister_5nm(struct dp_pll *pll);
struct dp_pll_in {
struct platform_device *pdev;
struct dp_aux *aux;
struct dp_parser *parser;
};
struct dp_pll *dp_pll_get(struct dp_pll_in *in);
void dp_pll_put(struct dp_pll *pll);
#endif /* __DP_PLL_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,739 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
*/
#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <drm/drmP.h>
#include "dp_power.h"
#include "dp_catalog.h"
#include "dp_debug.h"
#include "dp_pll.h"
#define DP_CLIENT_NAME_SIZE 20
struct dp_power_private {
struct dp_parser *parser;
struct dp_pll *pll;
struct platform_device *pdev;
struct clk *pixel_clk_rcg;
struct clk *pixel_parent;
struct clk *pixel1_clk_rcg;
struct dp_power dp_power;
bool core_clks_on;
bool link_clks_on;
bool strm0_clks_on;
bool strm1_clks_on;
};
static int dp_power_regulator_init(struct dp_power_private *power)
{
int rc = 0, i = 0, j = 0;
struct platform_device *pdev;
struct dp_parser *parser;
parser = power->parser;
pdev = power->pdev;
for (i = DP_CORE_PM; !rc && (i < DP_MAX_PM); i++) {
rc = msm_dss_get_vreg(&pdev->dev,
parser->mp[i].vreg_config,
parser->mp[i].num_vreg, 1);
if (rc) {
DP_ERR("failed to init vregs for %s\n",
dp_parser_pm_name(i));
for (j = i - 1; j >= DP_CORE_PM; j--) {
msm_dss_get_vreg(&pdev->dev,
parser->mp[j].vreg_config,
parser->mp[j].num_vreg, 0);
}
goto error;
}
}
error:
return rc;
}
static void dp_power_regulator_deinit(struct dp_power_private *power)
{
int rc = 0, i = 0;
struct platform_device *pdev;
struct dp_parser *parser;
parser = power->parser;
pdev = power->pdev;
for (i = DP_CORE_PM; (i < DP_MAX_PM); i++) {
rc = msm_dss_get_vreg(&pdev->dev,
parser->mp[i].vreg_config,
parser->mp[i].num_vreg, 0);
if (rc)
DP_ERR("failed to deinit vregs for %s\n",
dp_parser_pm_name(i));
}
}
static int dp_power_regulator_ctrl(struct dp_power_private *power, bool enable)
{
int rc = 0, i = 0, j = 0;
struct dp_parser *parser;
parser = power->parser;
for (i = DP_CORE_PM; i < DP_MAX_PM; i++) {
/*
* The DP_PLL_PM regulator is controlled by dp_display based
* on the link configuration.
*/
if (i == DP_PLL_PM) {
DP_DEBUG("skipping: '%s' vregs for %s\n",
enable ? "enable" : "disable",
dp_parser_pm_name(i));
continue;
}
rc = msm_dss_enable_vreg(
parser->mp[i].vreg_config,
parser->mp[i].num_vreg, enable);
if (rc) {
DP_ERR("failed to '%s' vregs for %s\n",
enable ? "enable" : "disable",
dp_parser_pm_name(i));
if (enable) {
for (j = i-1; j >= DP_CORE_PM; j--) {
msm_dss_enable_vreg(
parser->mp[j].vreg_config,
parser->mp[j].num_vreg, 0);
}
}
goto error;
}
}
error:
return rc;
}
static int dp_power_pinctrl_set(struct dp_power_private *power, bool active)
{
int rc = -EFAULT;
struct pinctrl_state *pin_state;
struct dp_parser *parser;
parser = power->parser;
if (IS_ERR_OR_NULL(parser->pinctrl.pin))
return 0;
if (parser->no_aux_switch && parser->lphw_hpd) {
pin_state = active ? parser->pinctrl.state_hpd_ctrl
: parser->pinctrl.state_hpd_tlmm;
if (!IS_ERR_OR_NULL(pin_state)) {
rc = pinctrl_select_state(parser->pinctrl.pin,
pin_state);
if (rc) {
DP_ERR("cannot direct hpd line to %s\n",
active ? "ctrl" : "tlmm");
return rc;
}
}
}
if (parser->no_aux_switch)
return 0;
pin_state = active ? parser->pinctrl.state_active
: parser->pinctrl.state_suspend;
if (!IS_ERR_OR_NULL(pin_state)) {
rc = pinctrl_select_state(parser->pinctrl.pin,
pin_state);
if (rc)
DP_ERR("can not set %s pins\n",
active ? "dp_active"
: "dp_sleep");
} else {
DP_ERR("invalid '%s' pinstate\n",
active ? "dp_active"
: "dp_sleep");
}
return rc;
}
static void dp_power_clk_put(struct dp_power_private *power)
{
enum dp_pm_type module;
for (module = DP_CORE_PM; module < DP_MAX_PM; module++) {
struct dss_module_power *pm = &power->parser->mp[module];
if (!pm->num_clk)
continue;
msm_dss_put_clk(pm->clk_config, pm->num_clk);
}
}
static int dp_power_clk_init(struct dp_power_private *power, bool enable)
{
int rc = 0;
struct device *dev;
enum dp_pm_type module;
dev = &power->pdev->dev;
if (enable) {
for (module = DP_CORE_PM; module < DP_MAX_PM; module++) {
struct dss_module_power *pm =
&power->parser->mp[module];
if (!pm->num_clk)
continue;
rc = msm_dss_get_clk(dev, pm->clk_config, pm->num_clk);
if (rc) {
DP_ERR("failed to get %s clk. err=%d\n",
dp_parser_pm_name(module), rc);
goto exit;
}
}
power->pixel_clk_rcg = clk_get(dev, "pixel_clk_rcg");
if (IS_ERR(power->pixel_clk_rcg)) {
DP_ERR("Unable to get DP pixel clk RCG: %d\n",
PTR_ERR(power->pixel_clk_rcg));
rc = PTR_ERR(power->pixel_clk_rcg);
power->pixel_clk_rcg = NULL;
goto err_pixel_clk_rcg;
}
power->pixel_parent = clk_get(dev, "pixel_parent");
if (IS_ERR(power->pixel_parent)) {
DP_ERR("Unable to get DP pixel RCG parent: %d\n",
PTR_ERR(power->pixel_parent));
rc = PTR_ERR(power->pixel_parent);
power->pixel_parent = NULL;
goto err_pixel_parent;
}
if (power->parser->has_mst) {
power->pixel1_clk_rcg = clk_get(dev, "pixel1_clk_rcg");
if (IS_ERR(power->pixel1_clk_rcg)) {
DP_ERR("Unable to get DP pixel1 clk RCG: %d\n",
PTR_ERR(power->pixel1_clk_rcg));
rc = PTR_ERR(power->pixel1_clk_rcg);
power->pixel1_clk_rcg = NULL;
goto err_pixel1_clk_rcg;
}
}
} else {
if (power->pixel1_clk_rcg)
clk_put(power->pixel1_clk_rcg);
if (power->pixel_parent)
clk_put(power->pixel_parent);
if (power->pixel_clk_rcg)
clk_put(power->pixel_clk_rcg);
dp_power_clk_put(power);
}
return rc;
err_pixel1_clk_rcg:
clk_put(power->pixel_parent);
err_pixel_parent:
clk_put(power->pixel_clk_rcg);
err_pixel_clk_rcg:
dp_power_clk_put(power);
exit:
return rc;
}
static int dp_power_clk_set_rate(struct dp_power_private *power,
enum dp_pm_type module, bool enable)
{
int rc = 0;
struct dss_module_power *mp;
if (!power) {
DP_ERR("invalid power data\n");
rc = -EINVAL;
goto exit;
}
mp = &power->parser->mp[module];
if (enable) {
rc = msm_dss_clk_set_rate(mp->clk_config, mp->num_clk);
if (rc) {
DP_ERR("failed to set clks rate.\n");
goto exit;
}
rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, 1);
if (rc) {
DP_ERR("failed to enable clks\n");
goto exit;
}
} else {
rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, 0);
if (rc) {
DP_ERR("failed to disable clks\n");
goto exit;
}
}
exit:
return rc;
}
static int dp_power_clk_enable(struct dp_power *dp_power,
enum dp_pm_type pm_type, bool enable)
{
int rc = 0;
struct dss_module_power *mp;
struct dp_power_private *power;
if (!dp_power) {
DP_ERR("invalid power data\n");
rc = -EINVAL;
goto error;
}
power = container_of(dp_power, struct dp_power_private, dp_power);
mp = &power->parser->mp[pm_type];
if (pm_type >= DP_MAX_PM) {
DP_ERR("unsupported power module: %s\n",
dp_parser_pm_name(pm_type));
return -EINVAL;
}
if (enable) {
if (pm_type == DP_CORE_PM && power->core_clks_on) {
DP_DEBUG("core clks already enabled\n");
return 0;
}
if ((pm_type == DP_STREAM0_PM) && (power->strm0_clks_on)) {
DP_DEBUG("strm0 clks already enabled\n");
return 0;
}
if ((pm_type == DP_STREAM1_PM) && (power->strm1_clks_on)) {
DP_DEBUG("strm1 clks already enabled\n");
return 0;
}
if ((pm_type == DP_CTRL_PM) && (!power->core_clks_on)) {
DP_DEBUG("Need to enable core clks before link clks\n");
rc = dp_power_clk_set_rate(power, pm_type, enable);
if (rc) {
DP_ERR("failed to enable clks: %s. err=%d\n",
dp_parser_pm_name(DP_CORE_PM), rc);
goto error;
} else {
power->core_clks_on = true;
}
}
if (pm_type == DP_LINK_PM && power->link_clks_on) {
DP_DEBUG("links clks already enabled\n");
return 0;
}
}
rc = dp_power_clk_set_rate(power, pm_type, enable);
if (rc) {
DP_ERR("failed to '%s' clks for: %s. err=%d\n",
enable ? "enable" : "disable",
dp_parser_pm_name(pm_type), rc);
goto error;
}
if (pm_type == DP_CORE_PM)
power->core_clks_on = enable;
else if (pm_type == DP_STREAM0_PM)
power->strm0_clks_on = enable;
else if (pm_type == DP_STREAM1_PM)
power->strm1_clks_on = enable;
else if (pm_type == DP_LINK_PM)
power->link_clks_on = enable;
/*
* This log is printed only when user connects or disconnects
* a DP cable. As this is a user-action and not a frequent
* usecase, it is not going to flood the kernel logs. Also,
* helpful in debugging the NOC issues.
*/
DP_INFO("core:%s link:%s strm0:%s strm1:%s\n",
power->core_clks_on ? "on" : "off",
power->link_clks_on ? "on" : "off",
power->strm0_clks_on ? "on" : "off",
power->strm1_clks_on ? "on" : "off");
error:
return rc;
}
static int dp_power_request_gpios(struct dp_power_private *power)
{
int rc = 0, i;
struct device *dev;
struct dss_module_power *mp;
static const char * const gpio_names[] = {
"aux_enable", "aux_sel", "usbplug_cc",
};
if (!power) {
DP_ERR("invalid power data\n");
return -EINVAL;
}
dev = &power->pdev->dev;
mp = &power->parser->mp[DP_CORE_PM];
for (i = 0; i < ARRAY_SIZE(gpio_names); i++) {
unsigned int gpio = mp->gpio_config[i].gpio;
if (gpio_is_valid(gpio)) {
rc = gpio_request(gpio, gpio_names[i]);
if (rc) {
DP_ERR("request %s gpio failed, rc=%d\n",
gpio_names[i], rc);
goto error;
}
}
}
return 0;
error:
for (i = 0; i < ARRAY_SIZE(gpio_names); i++) {
unsigned int gpio = mp->gpio_config[i].gpio;
if (gpio_is_valid(gpio))
gpio_free(gpio);
}
return rc;
}
static bool dp_power_find_gpio(const char *gpio1, const char *gpio2)
{
return !!strnstr(gpio1, gpio2, strlen(gpio1));
}
static void dp_power_set_gpio(struct dp_power_private *power, bool flip)
{
int i;
struct dss_module_power *mp = &power->parser->mp[DP_CORE_PM];
struct dss_gpio *config = mp->gpio_config;
for (i = 0; i < mp->num_gpio; i++) {
if (dp_power_find_gpio(config->gpio_name, "aux-sel"))
config->value = flip;
if (gpio_is_valid(config->gpio)) {
DP_DEBUG("gpio %s, value %d\n", config->gpio_name,
config->value);
if (dp_power_find_gpio(config->gpio_name, "aux-en") ||
dp_power_find_gpio(config->gpio_name, "aux-sel"))
gpio_direction_output(config->gpio,
config->value);
else
gpio_set_value(config->gpio, config->value);
}
config++;
}
}
static int dp_power_config_gpios(struct dp_power_private *power, bool flip,
bool enable)
{
int rc = 0, i;
struct dss_module_power *mp;
struct dss_gpio *config;
if (power->parser->no_aux_switch)
return 0;
mp = &power->parser->mp[DP_CORE_PM];
config = mp->gpio_config;
if (enable) {
rc = dp_power_request_gpios(power);
if (rc) {
DP_ERR("gpio request failed\n");
return rc;
}
dp_power_set_gpio(power, flip);
} else {
for (i = 0; i < mp->num_gpio; i++) {
if (gpio_is_valid(config[i].gpio)) {
gpio_set_value(config[i].gpio, 0);
gpio_free(config[i].gpio);
}
}
}
return 0;
}
static int dp_power_client_init(struct dp_power *dp_power,
struct sde_power_handle *phandle, struct drm_device *drm_dev)
{
int rc = 0;
struct dp_power_private *power;
if (!drm_dev) {
DP_ERR("invalid drm_dev\n");
return -EINVAL;
}
power = container_of(dp_power, struct dp_power_private, dp_power);
rc = dp_power_regulator_init(power);
if (rc) {
DP_ERR("failed to init regulators\n");
goto error_power;
}
rc = dp_power_clk_init(power, true);
if (rc) {
DP_ERR("failed to init clocks\n");
goto error_clk;
}
dp_power->phandle = phandle;
dp_power->drm_dev = drm_dev;
return 0;
error_clk:
dp_power_regulator_deinit(power);
error_power:
return rc;
}
static void dp_power_client_deinit(struct dp_power *dp_power)
{
struct dp_power_private *power;
if (!dp_power) {
DP_ERR("invalid power data\n");
return;
}
power = container_of(dp_power, struct dp_power_private, dp_power);
dp_power_clk_init(power, false);
dp_power_regulator_deinit(power);
}
static int dp_power_set_pixel_clk_parent(struct dp_power *dp_power, u32 strm_id)
{
int rc = 0;
struct dp_power_private *power;
if (!dp_power || strm_id >= DP_STREAM_MAX) {
DP_ERR("invalid power data. stream %d\n", strm_id);
rc = -EINVAL;
goto exit;
}
power = container_of(dp_power, struct dp_power_private, dp_power);
if (strm_id == DP_STREAM_0) {
if (power->pixel_clk_rcg && power->pixel_parent)
rc = clk_set_parent(power->pixel_clk_rcg,
power->pixel_parent);
else
DP_WARN("skipped for strm_id=%d\n", strm_id);
} else if (strm_id == DP_STREAM_1) {
if (power->pixel1_clk_rcg && power->pixel_parent)
rc = clk_set_parent(power->pixel1_clk_rcg,
power->pixel_parent);
else
DP_WARN("skipped for strm_id=%d\n", strm_id);
}
if (rc)
DP_ERR("failed. strm_id=%d, rc=%d\n", strm_id, rc);
exit:
return rc;
}
static u64 dp_power_clk_get_rate(struct dp_power *dp_power, char *clk_name)
{
size_t i;
enum dp_pm_type j;
struct dss_module_power *mp;
struct dp_power_private *power;
bool clk_found = false;
u64 rate = 0;
if (!clk_name) {
DP_ERR("invalid pointer for clk_name\n");
return 0;
}
power = container_of(dp_power, struct dp_power_private, dp_power);
mp = &dp_power->phandle->mp;
for (i = 0; i < mp->num_clk; i++) {
if (!strcmp(mp->clk_config[i].clk_name, clk_name)) {
rate = clk_get_rate(mp->clk_config[i].clk);
clk_found = true;
break;
}
}
for (j = DP_CORE_PM; j < DP_MAX_PM && !clk_found; j++) {
mp = &power->parser->mp[j];
for (i = 0; i < mp->num_clk; i++) {
if (!strcmp(mp->clk_config[i].clk_name, clk_name)) {
rate = clk_get_rate(mp->clk_config[i].clk);
clk_found = true;
break;
}
}
}
return rate;
}
static int dp_power_init(struct dp_power *dp_power, bool flip)
{
int rc = 0;
struct dp_power_private *power;
if (!dp_power) {
DP_ERR("invalid power data\n");
rc = -EINVAL;
goto exit;
}
power = container_of(dp_power, struct dp_power_private, dp_power);
rc = dp_power_regulator_ctrl(power, true);
if (rc) {
DP_ERR("failed to enable regulators\n");
goto exit;
}
rc = dp_power_pinctrl_set(power, true);
if (rc) {
DP_ERR("failed to set pinctrl state\n");
goto err_pinctrl;
}
rc = dp_power_config_gpios(power, flip, true);
if (rc) {
DP_ERR("failed to enable gpios\n");
goto err_gpio;
}
rc = pm_runtime_get_sync(dp_power->drm_dev->dev);
if (rc < 0) {
DP_ERR("Power resource enable failed\n");
goto err_sde_power;
}
rc = dp_power_clk_enable(dp_power, DP_CORE_PM, true);
if (rc) {
DP_ERR("failed to enable DP core clocks\n");
goto err_clk;
}
return 0;
err_clk:
pm_runtime_put_sync(dp_power->drm_dev->dev);
err_sde_power:
dp_power_config_gpios(power, flip, false);
err_gpio:
dp_power_pinctrl_set(power, false);
err_pinctrl:
dp_power_regulator_ctrl(power, false);
exit:
return rc;
}
static int dp_power_deinit(struct dp_power *dp_power)
{
int rc = 0;
struct dp_power_private *power;
if (!dp_power) {
DP_ERR("invalid power data\n");
rc = -EINVAL;
goto exit;
}
power = container_of(dp_power, struct dp_power_private, dp_power);
if (power->link_clks_on)
dp_power_clk_enable(dp_power, DP_LINK_PM, false);
dp_power_clk_enable(dp_power, DP_CORE_PM, false);
pm_runtime_put_sync(dp_power->drm_dev->dev);
dp_power_config_gpios(power, false, false);
dp_power_pinctrl_set(power, false);
dp_power_regulator_ctrl(power, false);
exit:
return rc;
}
struct dp_power *dp_power_get(struct dp_parser *parser, struct dp_pll *pll)
{
int rc = 0;
struct dp_power_private *power;
struct dp_power *dp_power;
if (!parser || !pll) {
DP_ERR("invalid input\n");
rc = -EINVAL;
goto error;
}
power = kzalloc(sizeof(*power), GFP_KERNEL);
if (!power) {
rc = -ENOMEM;
goto error;
}
power->parser = parser;
power->pll = pll;
power->pdev = parser->pdev;
dp_power = &power->dp_power;
dp_power->init = dp_power_init;
dp_power->deinit = dp_power_deinit;
dp_power->clk_enable = dp_power_clk_enable;
dp_power->set_pixel_clk_parent = dp_power_set_pixel_clk_parent;
dp_power->clk_get_rate = dp_power_clk_get_rate;
dp_power->power_client_init = dp_power_client_init;
dp_power->power_client_deinit = dp_power_client_deinit;
return dp_power;
error:
return ERR_PTR(rc);
}
void dp_power_put(struct dp_power *dp_power)
{
struct dp_power_private *power = NULL;
if (!dp_power)
return;
power = container_of(dp_power, struct dp_power_private, dp_power);
kfree(power);
}

View File

@ -0,0 +1,58 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DP_POWER_H_
#define _DP_POWER_H_
#include "dp_parser.h"
#include "dp_pll.h"
#include "sde_power_handle.h"
/**
* sruct dp_power - DisplayPort's power related data
*
* @init: initializes the regulators/core clocks/GPIOs/pinctrl
* @deinit: turns off the regulators/core clocks/GPIOs/pinctrl
* @clk_enable: enable/disable the DP clocks
* @set_pixel_clk_parent: set the parent of DP pixel clock
* @clk_get_rate: get the current rate for provided clk_name
* @power_client_init: configures clocks and regulators
* @power_client_deinit: frees clock and regulator resources
*/
struct dp_power {
struct drm_device *drm_dev;
struct sde_power_handle *phandle;
int (*init)(struct dp_power *power, bool flip);
int (*deinit)(struct dp_power *power);
int (*clk_enable)(struct dp_power *power, enum dp_pm_type pm_type,
bool enable);
int (*set_pixel_clk_parent)(struct dp_power *power, u32 stream_id);
u64 (*clk_get_rate)(struct dp_power *power, char *clk_name);
int (*power_client_init)(struct dp_power *power,
struct sde_power_handle *phandle,
struct drm_device *drm_dev);
void (*power_client_deinit)(struct dp_power *power);
};
/**
* dp_power_get() - configure and get the DisplayPort power module data
*
* @parser: instance of parser module
* @pll: instance of pll module
* return: pointer to allocated power module data
*
* This API will configure the DisplayPort's power module and provides
* methods to be called by the client to configure the power related
* modueles.
*/
struct dp_power *dp_power_get(struct dp_parser *parser, struct dp_pll *pll);
/**
* dp_power_put() - release the power related resources
*
* @power: pointer to the power module's data
*/
void dp_power_put(struct dp_power *power);
#endif /* _DP_POWER_H_ */

View File

@ -0,0 +1,439 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DP_REG_H_
#define _DP_REG_H_
/* DP_TX Registers */
#define DP_HW_VERSION (0x00000000)
#define DP_SW_RESET (0x00000010)
#define DP_PHY_CTRL (0x00000014)
#define DP_CLK_CTRL (0x00000018)
#define DP_CLK_ACTIVE (0x0000001C)
#define DP_INTR_STATUS (0x00000020)
#define DP_INTR_STATUS2 (0x00000024)
#define DP_INTR_STATUS3 (0x00000028)
#define DP_INTR_STATUS5 (0x00000034)
#define DP_DP_HPD_CTRL (0x00000000)
#define DP_DP_HPD_INT_STATUS (0x00000004)
#define DP_DP_HPD_INT_ACK (0x00000008)
#define DP_DP_HPD_INT_MASK (0x0000000C)
#define DP_DP_HPD_REFTIMER (0x00000018)
#define DP_DP_HPD_EVENT_TIME_0 (0x0000001C)
#define DP_DP_HPD_EVENT_TIME_1 (0x00000020)
#define DP_AUX_CTRL (0x00000030)
#define DP_AUX_DATA (0x00000034)
#define DP_AUX_TRANS_CTRL (0x00000038)
#define DP_TIMEOUT_COUNT (0x0000003C)
#define DP_AUX_LIMITS (0x00000040)
#define DP_AUX_STATUS (0x00000044)
#define DP_DPCD_CP_IRQ (0x201)
#define DP_DPCD_RXSTATUS (0x69493)
#define DP_INTERRUPT_TRANS_NUM (0x000000A0)
#define DP_MAINLINK_CTRL (0x00000000)
#define DP_STATE_CTRL (0x00000004)
#define DP_CONFIGURATION_CTRL (0x00000008)
#define DP_SOFTWARE_MVID (0x00000010)
#define DP_SOFTWARE_NVID (0x00000018)
#define DP_TOTAL_HOR_VER (0x0000001C)
#define DP_START_HOR_VER_FROM_SYNC (0x00000020)
#define DP_HSYNC_VSYNC_WIDTH_POLARITY (0x00000024)
#define DP_ACTIVE_HOR_VER (0x00000028)
#define DP_MISC1_MISC0 (0x0000002C)
#define DP_VALID_BOUNDARY (0x00000030)
#define DP_VALID_BOUNDARY_2 (0x00000034)
#define DP_LOGICAL2PHYSICAL_LANE_MAPPING (0x00000038)
#define DP1_CONFIGURATION_CTRL (0x00000400)
#define DP_DP0_TIMESLOT_1_32 (0x00000404)
#define DP_DP0_TIMESLOT_33_63 (0x00000408)
#define DP_DP1_TIMESLOT_1_32 (0x0000040C)
#define DP_DP1_TIMESLOT_33_63 (0x00000410)
#define DP1_SOFTWARE_MVID (0x00000414)
#define DP1_SOFTWARE_NVID (0x00000418)
#define DP1_TOTAL_HOR_VER (0x0000041C)
#define DP1_START_HOR_VER_FROM_SYNC (0x00000420)
#define DP1_HSYNC_VSYNC_WIDTH_POLARITY (0x00000424)
#define DP1_ACTIVE_HOR_VER (0x00000428)
#define DP1_MISC1_MISC0 (0x0000042C)
#define DP_DP0_RG (0x000004F8)
#define DP_DP1_RG (0x000004FC)
#define DP_MST_ACT (0x00000500)
#define DP_MST_MAINLINK_READY (0x00000504)
#define DP_MAINLINK_READY (0x00000040)
#define DP_MAINLINK_LEVELS (0x00000044)
#define DP_TU (0x0000004C)
#define DP_HBR2_COMPLIANCE_SCRAMBLER_RESET (0x00000054)
#define DP_TEST_80BIT_CUSTOM_PATTERN_REG0 (0x000000C0)
#define DP_TEST_80BIT_CUSTOM_PATTERN_REG1 (0x000000C4)
#define DP_TEST_80BIT_CUSTOM_PATTERN_REG2 (0x000000C8)
#define MMSS_DP_MISC1_MISC0 (0x0000002C)
#define MMSS_DP_AUDIO_TIMING_GEN (0x00000080)
#define MMSS_DP_AUDIO_TIMING_RBR_32 (0x00000084)
#define MMSS_DP_AUDIO_TIMING_HBR_32 (0x00000088)
#define MMSS_DP_AUDIO_TIMING_RBR_44 (0x0000008C)
#define MMSS_DP_AUDIO_TIMING_HBR_44 (0x00000090)
#define MMSS_DP_AUDIO_TIMING_RBR_48 (0x00000094)
#define MMSS_DP_AUDIO_TIMING_HBR_48 (0x00000098)
#define MMSS_DP_PSR_CRC_RG (0x00000154)
#define MMSS_DP_PSR_CRC_B (0x00000158)
#define DP_COMPRESSION_MODE_CTRL (0x00000180)
#define DP_PPS_HB_0_3 (0x00000184)
#define DP_PPS_PB_0_3 (0x00000188)
#define DP_PPS_PB_4_7 (0x0000018C)
#define DP_PPS_PB_8_11 (0x00000190)
#define DP_PPS_PB_12_15 (0x00000194)
#define DP_PPS_PB_16_19 (0x00000198)
#define DP_PPS_PB_20_23 (0x0000019C)
#define DP_PPS_PB_24_27 (0x000001A0)
#define DP_PPS_PB_28_31 (0x000001A4)
#define DP_PPS_PPS_0_3 (0x000001A8)
#define DP_PPS_PPS_4_7 (0x000001AC)
#define DP_PPS_PPS_8_11 (0x000001B0)
#define DP_PPS_PPS_12_15 (0x000001B4)
#define DP_PPS_PPS_16_19 (0x000001B8)
#define DP_PPS_PPS_20_23 (0x000001BC)
#define DP_PPS_PPS_24_27 (0x000001C0)
#define DP_PPS_PPS_28_31 (0x000001C4)
#define DP_PPS_PPS_32_35 (0x000001C8)
#define DP_PPS_PPS_36_39 (0x000001CC)
#define DP_PPS_PPS_40_43 (0x000001D0)
#define DP_PPS_PPS_44_47 (0x000001D4)
#define DP_PPS_PPS_48_51 (0x000001D8)
#define DP_PPS_PPS_52_55 (0x000001DC)
#define DP_PPS_PPS_56_59 (0x000001E0)
#define DP_PPS_PPS_60_63 (0x000001E4)
#define DP_PPS_PPS_64_67 (0x000001E8)
#define DP_PPS_PPS_68_71 (0x000001EC)
#define DP_PPS_PPS_72_75 (0x000001F0)
#define DP_PPS_PPS_76_79 (0x000001F4)
#define DP_PPS_PPS_80_83 (0x000001F8)
#define DP_PPS_PPS_84_87 (0x000001FC)
#define MMSS_DP_AUDIO_CFG (0x00000200)
#define MMSS_DP_AUDIO_STATUS (0x00000204)
#define MMSS_DP_AUDIO_PKT_CTRL (0x00000208)
#define MMSS_DP_AUDIO_PKT_CTRL2 (0x0000020C)
#define MMSS_DP_AUDIO_ACR_CTRL (0x00000210)
#define MMSS_DP_AUDIO_CTRL_RESET (0x00000214)
#define MMSS_DP_SDP_CFG (0x00000228)
#define MMSS_DP_SDP_CFG2 (0x0000022C)
#define MMSS_DP_SDP_CFG3 (0x0000024C)
#define MMSS_DP_SDP_CFG4 (0x000004EC)
#define MMSS_DP_AUDIO_TIMESTAMP_0 (0x00000230)
#define MMSS_DP_AUDIO_TIMESTAMP_1 (0x00000234)
#define MMSS_DP_AUDIO_STREAM_0 (0x00000240)
#define MMSS_DP_AUDIO_STREAM_1 (0x00000244)
#define MMSS_DP_EXTENSION_0 (0x00000250)
#define MMSS_DP_EXTENSION_1 (0x00000254)
#define MMSS_DP_EXTENSION_2 (0x00000258)
#define MMSS_DP_EXTENSION_3 (0x0000025C)
#define MMSS_DP_EXTENSION_4 (0x00000260)
#define MMSS_DP_EXTENSION_5 (0x00000264)
#define MMSS_DP_EXTENSION_6 (0x00000268)
#define MMSS_DP_EXTENSION_7 (0x0000026C)
#define MMSS_DP_EXTENSION_8 (0x00000270)
#define MMSS_DP_EXTENSION_9 (0x00000274)
#define MMSS_DP_AUDIO_COPYMANAGEMENT_0 (0x00000278)
#define MMSS_DP_AUDIO_COPYMANAGEMENT_1 (0x0000027C)
#define MMSS_DP_AUDIO_COPYMANAGEMENT_2 (0x00000280)
#define MMSS_DP_AUDIO_COPYMANAGEMENT_3 (0x00000284)
#define MMSS_DP_AUDIO_COPYMANAGEMENT_4 (0x00000288)
#define MMSS_DP_AUDIO_COPYMANAGEMENT_5 (0x0000028C)
#define MMSS_DP_AUDIO_ISRC_0 (0x00000290)
#define MMSS_DP_AUDIO_ISRC_1 (0x00000294)
#define MMSS_DP_AUDIO_ISRC_2 (0x00000298)
#define MMSS_DP_AUDIO_ISRC_3 (0x0000029C)
#define MMSS_DP_AUDIO_ISRC_4 (0x000002A0)
#define MMSS_DP_AUDIO_ISRC_5 (0x000002A4)
#define MMSS_DP_AUDIO_INFOFRAME_0 (0x000002A8)
#define MMSS_DP_AUDIO_INFOFRAME_1 (0x000002AC)
#define MMSS_DP_AUDIO_INFOFRAME_2 (0x000002B0)
#define MMSS_DP_FLUSH (0x000002F8)
#define MMSS_DP1_FLUSH (0x000002FC)
#define MMSS_DP_GENERIC0_0 (0x00000300)
#define MMSS_DP_GENERIC0_1 (0x00000304)
#define MMSS_DP_GENERIC0_2 (0x00000308)
#define MMSS_DP_GENERIC0_3 (0x0000030C)
#define MMSS_DP_GENERIC0_4 (0x00000310)
#define MMSS_DP_GENERIC0_5 (0x00000314)
#define MMSS_DP_GENERIC0_6 (0x00000318)
#define MMSS_DP_GENERIC0_7 (0x0000031C)
#define MMSS_DP_GENERIC0_8 (0x00000320)
#define MMSS_DP_GENERIC0_9 (0x00000324)
#define MMSS_DP_GENERIC1_0 (0x00000328)
#define MMSS_DP_GENERIC1_1 (0x0000032C)
#define MMSS_DP_GENERIC1_2 (0x00000330)
#define MMSS_DP_GENERIC1_3 (0x00000334)
#define MMSS_DP_GENERIC1_4 (0x00000338)
#define MMSS_DP_GENERIC1_5 (0x0000033C)
#define MMSS_DP_GENERIC1_6 (0x00000340)
#define MMSS_DP_GENERIC1_7 (0x00000344)
#define MMSS_DP_GENERIC1_8 (0x00000348)
#define MMSS_DP_GENERIC1_9 (0x0000034C)
#define MMSS_DP1_GENERIC0_0 (0x00000490)
#define MMSS_DP1_GENERIC0_1 (0x00000494)
#define MMSS_DP1_GENERIC0_2 (0x00000498)
#define MMSS_DP1_GENERIC0_3 (0x0000049C)
#define MMSS_DP1_GENERIC0_4 (0x000004A0)
#define MMSS_DP1_GENERIC0_5 (0x000004A4)
#define MMSS_DP1_GENERIC0_6 (0x000004A8)
#define MMSS_DP1_GENERIC0_7 (0x000004AC)
#define MMSS_DP1_GENERIC0_8 (0x000004B0)
#define MMSS_DP1_GENERIC0_9 (0x000004B4)
#define MMSS_DP1_GENERIC1_0 (0x000004B8)
#define MMSS_DP1_GENERIC1_1 (0x000004BC)
#define MMSS_DP1_GENERIC1_2 (0x000004C0)
#define MMSS_DP1_GENERIC1_3 (0x000004C4)
#define MMSS_DP1_GENERIC1_4 (0x000004C8)
#define MMSS_DP1_GENERIC1_5 (0x000004CC)
#define MMSS_DP1_GENERIC1_6 (0x000004D0)
#define MMSS_DP1_GENERIC1_7 (0x000004D4)
#define MMSS_DP1_GENERIC1_8 (0x000004D8)
#define MMSS_DP1_GENERIC1_9 (0x000004DC)
#define MMSS_DP_GENERIC2_0 (0x000003d8)
#define MMSS_DP_GENERIC2_1 (0x000003dc)
#define MMSS_DP_GENERIC2_2 (0x000003e0)
#define MMSS_DP_GENERIC2_3 (0x000003e4)
#define MMSS_DP_GENERIC2_4 (0x000003e8)
#define MMSS_DP_GENERIC2_5 (0x000003ec)
#define MMSS_DP_GENERIC2_6 (0x000003f0)
#define MMSS_DP_GENERIC2_7 (0x000003f4)
#define MMSS_DP_GENERIC2_8 (0x000003f8)
#define MMSS_DP_GENERIC2_9 (0x000003fc)
#define MMSS_DP1_GENERIC2_0 (0x00000510)
#define MMSS_DP1_GENERIC2_1 (0x00000514)
#define MMSS_DP1_GENERIC2_2 (0x00000518)
#define MMSS_DP1_GENERIC2_3 (0x0000051c)
#define MMSS_DP1_GENERIC2_4 (0x00000520)
#define MMSS_DP1_GENERIC2_5 (0x00000524)
#define MMSS_DP1_GENERIC2_6 (0x00000528)
#define MMSS_DP1_GENERIC2_7 (0x0000052C)
#define MMSS_DP1_GENERIC2_8 (0x00000530)
#define MMSS_DP1_GENERIC2_9 (0x00000534)
#define MMSS_DP1_SDP_CFG (0x000004E0)
#define MMSS_DP1_SDP_CFG2 (0x000004E4)
#define MMSS_DP1_SDP_CFG3 (0x000004E8)
#define MMSS_DP1_SDP_CFG4 (0x000004F0)
#define DP1_COMPRESSION_MODE_CTRL (0x00000560)
#define DP1_PPS_HB_0_3 (0x00000564)
#define DP1_PPS_PB_0_3 (0x00000568)
#define DP1_PPS_PB_4_7 (0x0000056C)
#define DP1_PPS_PB_8_11 (0x00000570)
#define DP1_PPS_PB_12_15 (0x00000574)
#define DP1_PPS_PB_16_19 (0x00000578)
#define DP1_PPS_PB_20_23 (0x0000057C)
#define DP1_PPS_PB_24_27 (0x00000580)
#define DP1_PPS_PB_28_31 (0x00000584)
#define DP1_PPS_PPS_0_3 (0x00000588)
#define DP1_PPS_PPS_4_7 (0x0000058C)
#define DP1_PPS_PPS_8_11 (0x00000590)
#define DP1_PPS_PPS_12_15 (0x00000594)
#define DP1_PPS_PPS_16_19 (0x00000598)
#define DP1_PPS_PPS_20_23 (0x0000059C)
#define DP1_PPS_PPS_24_27 (0x000005A0)
#define DP1_PPS_PPS_28_31 (0x000005A4)
#define DP1_PPS_PPS_32_35 (0x000005A8)
#define DP1_PPS_PPS_36_39 (0x000005AC)
#define DP1_PPS_PPS_40_43 (0x000005B0)
#define DP1_PPS_PPS_44_47 (0x000005B4)
#define DP1_PPS_PPS_48_51 (0x000005B8)
#define DP1_PPS_PPS_52_55 (0x000005BC)
#define DP1_PPS_PPS_56_59 (0x000005C0)
#define DP1_PPS_PPS_60_63 (0x000005C4)
#define DP1_PPS_PPS_64_67 (0x000005C8)
#define DP1_PPS_PPS_68_71 (0x000005CC)
#define DP1_PPS_PPS_72_75 (0x000005D0)
#define DP1_PPS_PPS_76_79 (0x000005D4)
#define DP1_PPS_PPS_80_83 (0x000005D8)
#define DP1_PPS_PPS_84_87 (0x000005DC)
#define MMSS_DP_VSCEXT_0 (0x000002D0)
#define MMSS_DP_VSCEXT_1 (0x000002D4)
#define MMSS_DP_VSCEXT_2 (0x000002D8)
#define MMSS_DP_VSCEXT_3 (0x000002DC)
#define MMSS_DP_VSCEXT_4 (0x000002E0)
#define MMSS_DP_VSCEXT_5 (0x000002E4)
#define MMSS_DP_VSCEXT_6 (0x000002E8)
#define MMSS_DP_VSCEXT_7 (0x000002EC)
#define MMSS_DP_VSCEXT_8 (0x000002F0)
#define MMSS_DP_VSCEXT_9 (0x000002F4)
#define MMSS_DP1_VSCEXT_0 (0x00000468)
#define MMSS_DP1_VSCEXT_1 (0x0000046c)
#define MMSS_DP1_VSCEXT_2 (0x00000470)
#define MMSS_DP1_VSCEXT_3 (0x00000474)
#define MMSS_DP1_VSCEXT_4 (0x00000478)
#define MMSS_DP1_VSCEXT_5 (0x0000047c)
#define MMSS_DP1_VSCEXT_6 (0x00000480)
#define MMSS_DP1_VSCEXT_7 (0x00000484)
#define MMSS_DP1_VSCEXT_8 (0x00000488)
#define MMSS_DP1_VSCEXT_9 (0x0000048c)
#define MMSS_DP_BIST_ENABLE (0x00000000)
#define MMSS_DP_TIMING_ENGINE_EN (0x00000010)
#define MMSS_DP_INTF_CONFIG (0x00000014)
#define MMSS_DP_INTF_HSYNC_CTL (0x00000018)
#define MMSS_DP_INTF_VSYNC_PERIOD_F0 (0x0000001C)
#define MMSS_DP_INTF_VSYNC_PERIOD_F1 (0x00000020)
#define MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F0 (0x00000024)
#define MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F1 (0x00000028)
#define MMSS_INTF_DISPLAY_V_START_F0 (0x0000002C)
#define MMSS_INTF_DISPLAY_V_START_F1 (0x00000030)
#define MMSS_DP_INTF_DISPLAY_V_END_F0 (0x00000034)
#define MMSS_DP_INTF_DISPLAY_V_END_F1 (0x00000038)
#define MMSS_DP_INTF_ACTIVE_V_START_F0 (0x0000003C)
#define MMSS_DP_INTF_ACTIVE_V_START_F1 (0x00000040)
#define MMSS_DP_INTF_ACTIVE_V_END_F0 (0x00000044)
#define MMSS_DP_INTF_ACTIVE_V_END_F1 (0x00000048)
#define MMSS_DP_INTF_DISPLAY_HCTL (0x0000004C)
#define MMSS_DP_INTF_ACTIVE_HCTL (0x00000050)
#define MMSS_DP_INTF_POLARITY_CTL (0x00000058)
#define MMSS_DP_TPG_MAIN_CONTROL (0x00000060)
#define MMSS_DP_TPG_VIDEO_CONFIG (0x00000064)
#define MMSS_DP_DSC_DTO (0x0000007C)
#define MMSS_DP_DSC_DTO_COUNT (0x00000084)
#define MMSS_DP_ASYNC_FIFO_CONFIG (0x00000088)
#define MMSS_DP1_BIST_ENABLE (0x00000000)
#define MMSS_DP1_TIMING_ENGINE_EN (0x00000010)
#define MMSS_DP1_INTF_CONFIG (0x00000014)
#define MMSS_DP1_INTF_HSYNC_CTL (0x00000018)
#define MMSS_DP1_INTF_VSYNC_PERIOD_F0 (0x0000001C)
#define MMSS_DP1_INTF_VSYNC_PERIOD_F1 (0x00000020)
#define MMSS_DP1_INTF_VSYNC_PULSE_WIDTH_F0 (0x00000024)
#define MMSS_DP1_INTF_VSYNC_PULSE_WIDTH_F1 (0x00000028)
#define MMSS_DP1_INTF_DISPLAY_V_START_F0 (0x0000002C)
#define MMSS_DP1_INTF_DISPLAY_V_START_F1 (0x00000030)
#define MMSS_DP1_INTF_DISPLAY_V_END_F0 (0x00000034)
#define MMSS_DP1_INTF_DISPLAY_V_END_F1 (0x00000038)
#define MMSS_DP1_INTF_ACTIVE_V_START_F0 (0x0000003C)
#define MMSS_DP1_INTF_ACTIVE_V_START_F1 (0x00000040)
#define MMSS_DP1_INTF_ACTIVE_V_END_F0 (0x00000044)
#define MMSS_DP1_INTF_ACTIVE_V_END_F1 (0x00000048)
#define MMSS_DP1_INTF_DISPLAY_HCTL (0x0000004C)
#define MMSS_DP1_INTF_ACTIVE_HCTL (0x00000050)
#define MMSS_DP1_INTF_POLARITY_CTL (0x00000058)
#define MMSS_DP1_TPG_MAIN_CONTROL (0x00000060)
#define MMSS_DP1_TPG_VIDEO_CONFIG (0x00000064)
#define MMSS_DP1_DSC_DTO (0x0000007C)
#define MMSS_DP1_DSC_DTO_COUNT (0x00000084)
#define MMSS_DP1_ASYNC_FIFO_CONFIG (0x00000088)
/*DP PHY Register offsets */
#define DP_PHY_REVISION_ID0 (0x00000000)
#define DP_PHY_REVISION_ID1 (0x00000004)
#define DP_PHY_REVISION_ID2 (0x00000008)
#define DP_PHY_REVISION_ID3 (0x0000000C)
#define DP_PHY_CFG (0x00000010)
#define DP_PHY_PD_CTL (0x00000018)
#define DP_PHY_MODE (0x0000001C)
#define DP_PHY_AUX_CFG0 (0x00000020)
#define DP_PHY_AUX_CFG1 (0x00000024)
#define DP_PHY_AUX_CFG2 (0x00000028)
#define DP_PHY_AUX_CFG3 (0x0000002C)
#define DP_PHY_AUX_CFG4 (0x00000030)
#define DP_PHY_AUX_CFG5 (0x00000034)
#define DP_PHY_AUX_CFG6 (0x00000038)
#define DP_PHY_AUX_CFG7 (0x0000003C)
#define DP_PHY_AUX_CFG8 (0x00000040)
#define DP_PHY_AUX_CFG9 (0x00000044)
#define DP_PHY_AUX_INTERRUPT_MASK (0x00000048)
#define DP_PHY_AUX_INTERRUPT_CLEAR (0x0000004C)
#define DP_PHY_AUX_INTERRUPT_STATUS (0x000000BC)
#define DP_PHY_AUX_INTERRUPT_MASK_V200 (0x00000048)
#define DP_PHY_AUX_INTERRUPT_CLEAR_V200 (0x0000004C)
#define DP_PHY_AUX_INTERRUPT_STATUS_V200 (0x000000BC)
#define DP_PHY_SPARE0 (0x00AC)
#define TXn_TX_EMP_POST1_LVL (0x000C)
#define TXn_TX_DRV_LVL (0x001C)
#define TXn_TX_POL_INV (0x0064)
#define TXn_TRANSCEIVER_BIAS_EN (0x005C)
#define TXn_HIGHZ_DRVR_EN (0x0060)
#define DP_PHY_STATUS (0x00DC)
#define DP_PHY_AUX_INTERRUPT_MASK_V420 (0x0054)
#define DP_PHY_AUX_INTERRUPT_CLEAR_V420 (0x0058)
#define DP_PHY_AUX_INTERRUPT_STATUS_V420 (0x00D8)
#define DP_PHY_SPARE0_V420 (0x00C8)
#define TXn_TX_DRV_LVL_V420 (0x0014)
#define TXn_TRANSCEIVER_BIAS_EN_V420 (0x0054)
#define TXn_HIGHZ_DRVR_EN_V420 (0x0058)
#define TXn_TX_POL_INV_V420 (0x005C)
#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN (0x044)
/* DP MMSS_CC registers */
#define MMSS_DP_PIXEL_M (0x01B4)
#define MMSS_DP_PIXEL_N (0x01B8)
#define MMSS_DP_PIXEL1_M (0x01CC)
#define MMSS_DP_PIXEL1_N (0x01D0)
#define MMSS_DP_PIXEL_M_V200 (0x0130)
#define MMSS_DP_PIXEL_N_V200 (0x0134)
#define MMSS_DP_PIXEL1_M_V200 (0x0148)
#define MMSS_DP_PIXEL1_N_V200 (0x014C)
#define MMSS_DP_PIXEL_M_V420 (0x01B0)
#define MMSS_DP_PIXEL_N_V420 (0x01B4)
#define MMSS_DP_PIXEL1_M_V420 (0x01C8)
#define MMSS_DP_PIXEL1_N_V420 (0x01CC)
/* DP HDCP 1.3 registers */
#define DP_HDCP_CTRL (0x0A0)
#define DP_HDCP_STATUS (0x0A4)
#define DP_HDCP_SW_UPPER_AKSV (0x098)
#define DP_HDCP_SW_LOWER_AKSV (0x09C)
#define DP_HDCP_ENTROPY_CTRL0 (0x350)
#define DP_HDCP_ENTROPY_CTRL1 (0x35C)
#define DP_HDCP_SHA_STATUS (0x0C8)
#define DP_HDCP_RCVPORT_DATA2_0 (0x0B0)
#define DP_HDCP_RCVPORT_DATA3 (0x0A4)
#define DP_HDCP_RCVPORT_DATA4 (0x0A8)
#define DP_HDCP_RCVPORT_DATA5 (0x0C0)
#define DP_HDCP_RCVPORT_DATA6 (0x0C4)
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_SHA_CTRL (0x024)
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_SHA_DATA (0x028)
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA0 (0x004)
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA1 (0x008)
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA7 (0x00C)
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA8 (0x010)
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA9 (0x014)
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA10 (0x018)
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA11 (0x01C)
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA12 (0x020)
/* USB3 DP COM registers */
#define USB3_DP_COM_PHY_MODE_CTRL (0x00)
#define USB3_DP_COM_SW_RESET (0x04)
#define USB3_DP_COM_POWER_DOWN_CTRL (0x08)
#define USB3_DP_COM_SWI_CTRL (0x0C)
#define USB3_DP_COM_TYPEC_CTRL (0x10)
#define USB3_DP_COM_RESET_OVRD_CTRL (0x1C)
#endif /* _DP_REG_H_ */

View File

@ -0,0 +1,582 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/usb/usbpd.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/delay.h>
#include "dp_usbpd.h"
#include "dp_debug.h"
/* DP specific VDM commands */
#define DP_USBPD_VDM_STATUS 0x10
#define DP_USBPD_VDM_CONFIGURE 0x11
/* USBPD-TypeC specific Macros */
#define VDM_VERSION 0x0
#define USB_C_DP_SID 0xFF01
enum dp_usbpd_pin_assignment {
DP_USBPD_PIN_A,
DP_USBPD_PIN_B,
DP_USBPD_PIN_C,
DP_USBPD_PIN_D,
DP_USBPD_PIN_E,
DP_USBPD_PIN_F,
DP_USBPD_PIN_MAX,
};
enum dp_usbpd_events {
DP_USBPD_EVT_DISCOVER,
DP_USBPD_EVT_ENTER,
DP_USBPD_EVT_STATUS,
DP_USBPD_EVT_CONFIGURE,
DP_USBPD_EVT_CC_PIN_POLARITY,
DP_USBPD_EVT_EXIT,
DP_USBPD_EVT_ATTENTION,
};
enum dp_usbpd_alt_mode {
DP_USBPD_ALT_MODE_NONE = 0,
DP_USBPD_ALT_MODE_INIT = BIT(0),
DP_USBPD_ALT_MODE_DISCOVER = BIT(1),
DP_USBPD_ALT_MODE_ENTER = BIT(2),
DP_USBPD_ALT_MODE_STATUS = BIT(3),
DP_USBPD_ALT_MODE_CONFIGURE = BIT(4),
};
struct dp_usbpd_capabilities {
enum dp_usbpd_port port;
bool receptacle_state;
u8 ulink_pin_config;
u8 dlink_pin_config;
};
struct dp_usbpd_private {
bool forced_disconnect;
u32 vdo;
struct device *dev;
struct usbpd *pd;
struct usbpd_svid_handler svid_handler;
struct dp_hpd_cb *dp_cb;
struct dp_usbpd_capabilities cap;
struct dp_usbpd dp_usbpd;
enum dp_usbpd_alt_mode alt_mode;
u32 dp_usbpd_config;
};
static const char *dp_usbpd_pin_name(u8 pin)
{
switch (pin) {
case DP_USBPD_PIN_A: return "DP_USBPD_PIN_ASSIGNMENT_A";
case DP_USBPD_PIN_B: return "DP_USBPD_PIN_ASSIGNMENT_B";
case DP_USBPD_PIN_C: return "DP_USBPD_PIN_ASSIGNMENT_C";
case DP_USBPD_PIN_D: return "DP_USBPD_PIN_ASSIGNMENT_D";
case DP_USBPD_PIN_E: return "DP_USBPD_PIN_ASSIGNMENT_E";
case DP_USBPD_PIN_F: return "DP_USBPD_PIN_ASSIGNMENT_F";
default: return "UNKNOWN";
}
}
static const char *dp_usbpd_port_name(enum dp_usbpd_port port)
{
switch (port) {
case DP_USBPD_PORT_NONE: return "DP_USBPD_PORT_NONE";
case DP_USBPD_PORT_UFP_D: return "DP_USBPD_PORT_UFP_D";
case DP_USBPD_PORT_DFP_D: return "DP_USBPD_PORT_DFP_D";
case DP_USBPD_PORT_D_UFP_D: return "DP_USBPD_PORT_D_UFP_D";
default: return "DP_USBPD_PORT_NONE";
}
}
static const char *dp_usbpd_cmd_name(u8 cmd)
{
switch (cmd) {
case USBPD_SVDM_DISCOVER_MODES: return "USBPD_SVDM_DISCOVER_MODES";
case USBPD_SVDM_ENTER_MODE: return "USBPD_SVDM_ENTER_MODE";
case USBPD_SVDM_ATTENTION: return "USBPD_SVDM_ATTENTION";
case DP_USBPD_VDM_STATUS: return "DP_USBPD_VDM_STATUS";
case DP_USBPD_VDM_CONFIGURE: return "DP_USBPD_VDM_CONFIGURE";
default: return "DP_USBPD_VDM_ERROR";
}
}
static void dp_usbpd_init_port(enum dp_usbpd_port *port, u32 in_port)
{
switch (in_port) {
case 0:
*port = DP_USBPD_PORT_NONE;
break;
case 1:
*port = DP_USBPD_PORT_UFP_D;
break;
case 2:
*port = DP_USBPD_PORT_DFP_D;
break;
case 3:
*port = DP_USBPD_PORT_D_UFP_D;
break;
default:
*port = DP_USBPD_PORT_NONE;
}
DP_DEBUG("port:%s\n", dp_usbpd_port_name(*port));
}
static void dp_usbpd_get_capabilities(struct dp_usbpd_private *pd)
{
struct dp_usbpd_capabilities *cap = &pd->cap;
u32 buf = pd->vdo;
int port = buf & 0x3;
cap->receptacle_state = (buf & BIT(6)) ? true : false;
cap->dlink_pin_config = (buf >> 8) & 0xff;
cap->ulink_pin_config = (buf >> 16) & 0xff;
dp_usbpd_init_port(&cap->port, port);
}
static void dp_usbpd_get_status(struct dp_usbpd_private *pd)
{
struct dp_usbpd *status = &pd->dp_usbpd;
u32 buf = pd->vdo;
int port = buf & 0x3;
status->low_pow_st = (buf & BIT(2)) ? true : false;
status->adaptor_dp_en = (buf & BIT(3)) ? true : false;
status->base.multi_func = (buf & BIT(4)) ? true : false;
status->usb_config_req = (buf & BIT(5)) ? true : false;
status->exit_dp_mode = (buf & BIT(6)) ? true : false;
status->base.hpd_high = (buf & BIT(7)) ? true : false;
status->base.hpd_irq = (buf & BIT(8)) ? true : false;
DP_DEBUG("low_pow_st = %d, adaptor_dp_en = %d, multi_func = %d\n",
status->low_pow_st, status->adaptor_dp_en,
status->base.multi_func);
DP_DEBUG("usb_config_req = %d, exit_dp_mode = %d, hpd_high =%d\n",
status->usb_config_req,
status->exit_dp_mode, status->base.hpd_high);
DP_DEBUG("hpd_irq = %d\n", status->base.hpd_irq);
dp_usbpd_init_port(&status->port, port);
}
static u32 dp_usbpd_gen_config_pkt(struct dp_usbpd_private *pd)
{
u8 pin_cfg, pin;
u32 config = 0;
const u32 ufp_d_config = 0x2, dp_ver = 0x1;
if (pd->cap.receptacle_state)
pin_cfg = pd->cap.ulink_pin_config;
else
pin_cfg = pd->cap.dlink_pin_config;
for (pin = DP_USBPD_PIN_A; pin < DP_USBPD_PIN_MAX; pin++) {
if (pin_cfg & BIT(pin)) {
if (pd->dp_usbpd.base.multi_func) {
if (pin == DP_USBPD_PIN_D)
break;
} else {
break;
}
}
}
if (pin == DP_USBPD_PIN_MAX)
pin = DP_USBPD_PIN_C;
DP_DEBUG("pin assignment: %s\n", dp_usbpd_pin_name(pin));
config |= BIT(pin) << 8;
config |= (dp_ver << 2);
config |= ufp_d_config;
DP_DEBUG("config = 0x%x\n", config);
return config;
}
static void dp_usbpd_send_event(struct dp_usbpd_private *pd,
enum dp_usbpd_events event)
{
u32 config;
switch (event) {
case DP_USBPD_EVT_DISCOVER:
usbpd_send_svdm(pd->pd, USB_C_DP_SID,
USBPD_SVDM_DISCOVER_MODES,
SVDM_CMD_TYPE_INITIATOR, 0x0, 0x0, 0x0);
break;
case DP_USBPD_EVT_ENTER:
usbpd_send_svdm(pd->pd, USB_C_DP_SID,
USBPD_SVDM_ENTER_MODE,
SVDM_CMD_TYPE_INITIATOR, 0x1, 0x0, 0x0);
break;
case DP_USBPD_EVT_EXIT:
usbpd_send_svdm(pd->pd, USB_C_DP_SID,
USBPD_SVDM_EXIT_MODE,
SVDM_CMD_TYPE_INITIATOR, 0x1, 0x0, 0x0);
break;
case DP_USBPD_EVT_STATUS:
config = 0x1; /* DFP_D connected */
usbpd_send_svdm(pd->pd, USB_C_DP_SID, DP_USBPD_VDM_STATUS,
SVDM_CMD_TYPE_INITIATOR, 0x1, &config, 0x1);
break;
case DP_USBPD_EVT_CONFIGURE:
config = dp_usbpd_gen_config_pkt(pd);
usbpd_send_svdm(pd->pd, USB_C_DP_SID, DP_USBPD_VDM_CONFIGURE,
SVDM_CMD_TYPE_INITIATOR, 0x1, &config, 0x1);
break;
default:
DP_ERR("unknown event:%d\n", event);
}
}
static void dp_usbpd_connect_cb(struct usbpd_svid_handler *hdlr,
bool peer_usb_comm)
{
struct dp_usbpd_private *pd;
pd = container_of(hdlr, struct dp_usbpd_private, svid_handler);
if (!pd) {
DP_ERR("get_usbpd phandle failed\n");
return;
}
DP_DEBUG("peer_usb_comm: %d\n", peer_usb_comm);
pd->dp_usbpd.base.peer_usb_comm = peer_usb_comm;
dp_usbpd_send_event(pd, DP_USBPD_EVT_DISCOVER);
}
static void dp_usbpd_disconnect_cb(struct usbpd_svid_handler *hdlr)
{
struct dp_usbpd_private *pd;
pd = container_of(hdlr, struct dp_usbpd_private, svid_handler);
if (!pd) {
DP_ERR("get_usbpd phandle failed\n");
return;
}
pd->alt_mode = DP_USBPD_ALT_MODE_NONE;
pd->dp_usbpd.base.alt_mode_cfg_done = false;
DP_DEBUG("\n");
if (pd->dp_cb && pd->dp_cb->disconnect)
pd->dp_cb->disconnect(pd->dev);
}
static int dp_usbpd_validate_callback(u8 cmd,
enum usbpd_svdm_cmd_type cmd_type, int num_vdos)
{
int ret = 0;
if (cmd_type == SVDM_CMD_TYPE_RESP_NAK) {
DP_ERR("error: NACK\n");
ret = -EINVAL;
goto end;
}
if (cmd_type == SVDM_CMD_TYPE_RESP_BUSY) {
DP_ERR("error: BUSY\n");
ret = -EBUSY;
goto end;
}
if (cmd == USBPD_SVDM_ATTENTION) {
if (cmd_type != SVDM_CMD_TYPE_INITIATOR) {
DP_ERR("error: invalid cmd type for attention\n");
ret = -EINVAL;
goto end;
}
if (!num_vdos) {
DP_ERR("error: no vdo provided\n");
ret = -EINVAL;
goto end;
}
} else {
if (cmd_type != SVDM_CMD_TYPE_RESP_ACK) {
DP_ERR("error: invalid cmd type\n");
ret = -EINVAL;
}
}
end:
return ret;
}
static int dp_usbpd_get_ss_lanes(struct dp_usbpd_private *pd)
{
int rc = 0;
int timeout = 250;
/*
* By default, USB reserves two lanes for Super Speed.
* Which means DP has remaining two lanes to operate on.
* If multi-function is not supported, request USB to
* release the Super Speed lanes so that DP can use
* all four lanes in case DPCD indicates support for
* four lanes.
*/
if (!pd->dp_usbpd.base.multi_func) {
while (timeout) {
rc = pd->svid_handler.request_usb_ss_lane(
pd->pd, &pd->svid_handler);
if (rc != -EBUSY)
break;
DP_WARN("USB busy, retry\n");
/* wait for hw recommended delay for usb */
msleep(20);
timeout--;
}
}
return rc;
}
static void dp_usbpd_response_cb(struct usbpd_svid_handler *hdlr, u8 cmd,
enum usbpd_svdm_cmd_type cmd_type,
const u32 *vdos, int num_vdos)
{
struct dp_usbpd_private *pd;
int rc = 0;
pd = container_of(hdlr, struct dp_usbpd_private, svid_handler);
DP_DEBUG("callback -> cmd: %s, *vdos = 0x%x, num_vdos = %d\n",
dp_usbpd_cmd_name(cmd), *vdos, num_vdos);
if (dp_usbpd_validate_callback(cmd, cmd_type, num_vdos)) {
DP_DEBUG("invalid callback received\n");
return;
}
switch (cmd) {
case USBPD_SVDM_DISCOVER_MODES:
pd->vdo = *vdos;
dp_usbpd_get_capabilities(pd);
pd->alt_mode |= DP_USBPD_ALT_MODE_DISCOVER;
if (pd->cap.port & BIT(0))
dp_usbpd_send_event(pd, DP_USBPD_EVT_ENTER);
break;
case USBPD_SVDM_ENTER_MODE:
pd->alt_mode |= DP_USBPD_ALT_MODE_ENTER;
dp_usbpd_send_event(pd, DP_USBPD_EVT_STATUS);
break;
case USBPD_SVDM_ATTENTION:
if (pd->forced_disconnect)
break;
pd->vdo = *vdos;
dp_usbpd_get_status(pd);
if (!pd->dp_usbpd.base.alt_mode_cfg_done) {
if (pd->dp_usbpd.port & BIT(1))
dp_usbpd_send_event(pd, DP_USBPD_EVT_CONFIGURE);
break;
}
if (pd->dp_cb && pd->dp_cb->attention)
pd->dp_cb->attention(pd->dev);
break;
case DP_USBPD_VDM_STATUS:
pd->vdo = *vdos;
dp_usbpd_get_status(pd);
if (!(pd->alt_mode & DP_USBPD_ALT_MODE_CONFIGURE)) {
pd->alt_mode |= DP_USBPD_ALT_MODE_STATUS;
if (pd->dp_usbpd.port & BIT(1))
dp_usbpd_send_event(pd, DP_USBPD_EVT_CONFIGURE);
}
break;
case DP_USBPD_VDM_CONFIGURE:
pd->alt_mode |= DP_USBPD_ALT_MODE_CONFIGURE;
pd->dp_usbpd.base.alt_mode_cfg_done = true;
pd->forced_disconnect = false;
dp_usbpd_get_status(pd);
pd->dp_usbpd.base.orientation =
usbpd_get_plug_orientation(pd->pd);
rc = dp_usbpd_get_ss_lanes(pd);
if (rc) {
DP_ERR("failed to get SuperSpeed lanes\n");
break;
}
if (pd->dp_cb && pd->dp_cb->configure)
pd->dp_cb->configure(pd->dev);
break;
default:
DP_ERR("unknown cmd: %d\n", cmd);
break;
}
}
static int dp_usbpd_simulate_connect(struct dp_hpd *dp_hpd, bool hpd)
{
int rc = 0;
struct dp_usbpd *dp_usbpd;
struct dp_usbpd_private *pd;
if (!dp_hpd) {
DP_ERR("invalid input\n");
rc = -EINVAL;
goto error;
}
dp_usbpd = container_of(dp_hpd, struct dp_usbpd, base);
pd = container_of(dp_usbpd, struct dp_usbpd_private, dp_usbpd);
dp_usbpd->base.hpd_high = hpd;
pd->forced_disconnect = !hpd;
pd->dp_usbpd.base.alt_mode_cfg_done = hpd;
DP_DEBUG("hpd_high=%d, forced_disconnect=%d, orientation=%d\n",
dp_usbpd->base.hpd_high, pd->forced_disconnect,
pd->dp_usbpd.base.orientation);
if (hpd)
pd->dp_cb->configure(pd->dev);
else
pd->dp_cb->disconnect(pd->dev);
error:
return rc;
}
static int dp_usbpd_simulate_attention(struct dp_hpd *dp_hpd, int vdo)
{
int rc = 0;
struct dp_usbpd *dp_usbpd;
struct dp_usbpd_private *pd;
dp_usbpd = container_of(dp_hpd, struct dp_usbpd, base);
if (!dp_usbpd) {
DP_ERR("invalid input\n");
rc = -EINVAL;
goto error;
}
pd = container_of(dp_usbpd, struct dp_usbpd_private, dp_usbpd);
pd->vdo = vdo;
dp_usbpd_get_status(pd);
if (pd->dp_cb && pd->dp_cb->attention)
pd->dp_cb->attention(pd->dev);
error:
return rc;
}
int dp_usbpd_register(struct dp_hpd *dp_hpd)
{
struct dp_usbpd *dp_usbpd;
struct dp_usbpd_private *usbpd;
int rc = 0;
if (!dp_hpd)
return -EINVAL;
dp_usbpd = container_of(dp_hpd, struct dp_usbpd, base);
usbpd = container_of(dp_usbpd, struct dp_usbpd_private, dp_usbpd);
rc = usbpd_register_svid(usbpd->pd, &usbpd->svid_handler);
if (rc)
DP_ERR("pd registration failed\n");
return rc;
}
static void dp_usbpd_wakeup_phy(struct dp_hpd *dp_hpd, bool wakeup)
{
struct dp_usbpd *dp_usbpd;
struct dp_usbpd_private *usbpd;
dp_usbpd = container_of(dp_hpd, struct dp_usbpd, base);
usbpd = container_of(dp_usbpd, struct dp_usbpd_private, dp_usbpd);
if (!usbpd->pd) {
DP_ERR("usbpd pointer invalid");
return;
}
usbpd_vdm_in_suspend(usbpd->pd, wakeup);
}
struct dp_hpd *dp_usbpd_get(struct device *dev, struct dp_hpd_cb *cb)
{
int rc = 0;
const char *pd_phandle = "qcom,dp-usbpd-detection";
struct usbpd *pd = NULL;
struct dp_usbpd_private *usbpd;
struct dp_usbpd *dp_usbpd;
struct usbpd_svid_handler svid_handler = {
.svid = USB_C_DP_SID,
.vdm_received = NULL,
.connect = &dp_usbpd_connect_cb,
.svdm_received = &dp_usbpd_response_cb,
.disconnect = &dp_usbpd_disconnect_cb,
};
if (!cb) {
DP_ERR("invalid cb data\n");
rc = -EINVAL;
goto error;
}
pd = devm_usbpd_get_by_phandle(dev, pd_phandle);
if (IS_ERR(pd)) {
DP_ERR("usbpd phandle failed (%ld)\n", PTR_ERR(pd));
rc = PTR_ERR(pd);
goto error;
}
usbpd = devm_kzalloc(dev, sizeof(*usbpd), GFP_KERNEL);
if (!usbpd) {
rc = -ENOMEM;
goto error;
}
usbpd->dev = dev;
usbpd->pd = pd;
usbpd->svid_handler = svid_handler;
usbpd->dp_cb = cb;
dp_usbpd = &usbpd->dp_usbpd;
dp_usbpd->base.simulate_connect = dp_usbpd_simulate_connect;
dp_usbpd->base.simulate_attention = dp_usbpd_simulate_attention;
dp_usbpd->base.register_hpd = dp_usbpd_register;
dp_usbpd->base.wakeup_phy = dp_usbpd_wakeup_phy;
return &dp_usbpd->base;
error:
return ERR_PTR(rc);
}
void dp_usbpd_put(struct dp_hpd *dp_hpd)
{
struct dp_usbpd *dp_usbpd;
struct dp_usbpd_private *usbpd;
dp_usbpd = container_of(dp_hpd, struct dp_usbpd, base);
if (!dp_usbpd)
return;
usbpd = container_of(dp_usbpd, struct dp_usbpd_private, dp_usbpd);
usbpd_unregister_svid(usbpd->pd, &usbpd->svid_handler);
devm_kfree(usbpd->dev, usbpd);
}

View File

@ -0,0 +1,75 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DP_USBPD_H_
#define _DP_USBPD_H_
#include <linux/types.h>
#include "dp_hpd.h"
struct device;
/**
* enum dp_usbpd_port - usb/dp port type
* @DP_USBPD_PORT_NONE: port not configured
* @DP_USBPD_PORT_UFP_D: Upstream Facing Port - DisplayPort
* @DP_USBPD_PORT_DFP_D: Downstream Facing Port - DisplayPort
* @DP_USBPD_PORT_D_UFP_D: Both UFP & DFP - DisplayPort
*/
enum dp_usbpd_port {
DP_USBPD_PORT_NONE,
DP_USBPD_PORT_UFP_D,
DP_USBPD_PORT_DFP_D,
DP_USBPD_PORT_D_UFP_D,
};
/**
* struct dp_usbpd - DisplayPort status
*
* @port: port configured
* @low_pow_st: low power state
* @adaptor_dp_en: adaptor functionality enabled
* @usb_config_req: request to switch to usb
* @exit_dp_mode: request exit from displayport mode
* @debug_en: bool to specify debug mode
*/
struct dp_usbpd {
struct dp_hpd base;
enum dp_usbpd_port port;
bool low_pow_st;
bool adaptor_dp_en;
bool usb_config_req;
bool exit_dp_mode;
bool debug_en;
};
#if IS_ENABLED(CONFIG_DRM_MSM_DP_USBPD_LEGACY)
/**
* dp_usbpd_get() - setup usbpd module
*
* @dev: device instance of the caller
* @cb: struct containing callback function pointers.
*
* This function allows the client to initialize the usbpd
* module. The module will communicate with usb driver and
* handles the power delivery (PD) communication with the
* sink/usb device. This module will notify the client using
* the callback functions about the connection and status.
*/
struct dp_hpd *dp_usbpd_get(struct device *dev, struct dp_hpd_cb *cb);
void dp_usbpd_put(struct dp_hpd *pd);
#else
static inline struct dp_hpd *dp_usbpd_get(struct device *dev,
struct dp_hpd_cb *cb)
{
return ERR_PTR(-ENODEV);
}
static inline void dp_usbpd_put(struct dp_hpd *pd)
{
}
#endif /* CONFIG_DRM_MSM_DP_USBPD_LEGACY */
#endif /* _DP_USBPD_H_ */

View File

@ -0,0 +1,346 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/errno.h>
#include "dsi_catalog.h"
/**
* dsi_catalog_cmn_init() - catalog init for dsi controller v1.4
*/
static void dsi_catalog_cmn_init(struct dsi_ctrl_hw *ctrl,
enum dsi_ctrl_version version)
{
/* common functions */
ctrl->ops.host_setup = dsi_ctrl_hw_cmn_host_setup;
ctrl->ops.video_engine_en = dsi_ctrl_hw_cmn_video_engine_en;
ctrl->ops.video_engine_setup = dsi_ctrl_hw_cmn_video_engine_setup;
ctrl->ops.set_video_timing = dsi_ctrl_hw_cmn_set_video_timing;
ctrl->ops.set_timing_db = dsi_ctrl_hw_cmn_set_timing_db;
ctrl->ops.cmd_engine_setup = dsi_ctrl_hw_cmn_cmd_engine_setup;
ctrl->ops.setup_cmd_stream = dsi_ctrl_hw_cmn_setup_cmd_stream;
ctrl->ops.ctrl_en = dsi_ctrl_hw_cmn_ctrl_en;
ctrl->ops.cmd_engine_en = dsi_ctrl_hw_cmn_cmd_engine_en;
ctrl->ops.phy_sw_reset = dsi_ctrl_hw_cmn_phy_sw_reset;
ctrl->ops.soft_reset = dsi_ctrl_hw_cmn_soft_reset;
ctrl->ops.kickoff_command = dsi_ctrl_hw_cmn_kickoff_command;
ctrl->ops.kickoff_fifo_command = dsi_ctrl_hw_cmn_kickoff_fifo_command;
ctrl->ops.reset_cmd_fifo = dsi_ctrl_hw_cmn_reset_cmd_fifo;
ctrl->ops.trigger_command_dma = dsi_ctrl_hw_cmn_trigger_command_dma;
ctrl->ops.get_interrupt_status = dsi_ctrl_hw_cmn_get_interrupt_status;
ctrl->ops.poll_slave_dma_status = dsi_ctrl_hw_cmn_poll_slave_dma_status;
ctrl->ops.get_error_status = dsi_ctrl_hw_cmn_get_error_status;
ctrl->ops.clear_error_status = dsi_ctrl_hw_cmn_clear_error_status;
ctrl->ops.clear_interrupt_status =
dsi_ctrl_hw_cmn_clear_interrupt_status;
ctrl->ops.enable_status_interrupts =
dsi_ctrl_hw_cmn_enable_status_interrupts;
ctrl->ops.enable_error_interrupts =
dsi_ctrl_hw_cmn_enable_error_interrupts;
ctrl->ops.video_test_pattern_setup =
dsi_ctrl_hw_cmn_video_test_pattern_setup;
ctrl->ops.cmd_test_pattern_setup =
dsi_ctrl_hw_cmn_cmd_test_pattern_setup;
ctrl->ops.test_pattern_enable = dsi_ctrl_hw_cmn_test_pattern_enable;
ctrl->ops.trigger_cmd_test_pattern =
dsi_ctrl_hw_cmn_trigger_cmd_test_pattern;
ctrl->ops.clear_phy0_ln_err = dsi_ctrl_hw_dln0_phy_err;
ctrl->ops.phy_reset_config = dsi_ctrl_hw_cmn_phy_reset_config;
ctrl->ops.setup_misr = dsi_ctrl_hw_cmn_setup_misr;
ctrl->ops.collect_misr = dsi_ctrl_hw_cmn_collect_misr;
ctrl->ops.get_cmd_read_data = dsi_ctrl_hw_cmn_get_cmd_read_data;
ctrl->ops.clear_rdbk_register = dsi_ctrl_hw_cmn_clear_rdbk_reg;
ctrl->ops.ctrl_reset = dsi_ctrl_hw_cmn_ctrl_reset;
ctrl->ops.mask_error_intr = dsi_ctrl_hw_cmn_mask_error_intr;
ctrl->ops.error_intr_ctrl = dsi_ctrl_hw_cmn_error_intr_ctrl;
ctrl->ops.get_error_mask = dsi_ctrl_hw_cmn_get_error_mask;
ctrl->ops.get_hw_version = dsi_ctrl_hw_cmn_get_hw_version;
ctrl->ops.wait_for_cmd_mode_mdp_idle =
dsi_ctrl_hw_cmn_wait_for_cmd_mode_mdp_idle;
ctrl->ops.setup_avr = dsi_ctrl_hw_cmn_setup_avr;
ctrl->ops.set_continuous_clk = dsi_ctrl_hw_cmn_set_continuous_clk;
ctrl->ops.wait4dynamic_refresh_done =
dsi_ctrl_hw_cmn_wait4dynamic_refresh_done;
ctrl->ops.hs_req_sel = dsi_ctrl_hw_cmn_hs_req_sel;
ctrl->ops.vid_engine_busy = dsi_ctrl_hw_cmn_vid_engine_busy;
switch (version) {
case DSI_CTRL_VERSION_1_4:
ctrl->ops.setup_lane_map = dsi_ctrl_hw_14_setup_lane_map;
ctrl->ops.ulps_ops.ulps_request = dsi_ctrl_hw_cmn_ulps_request;
ctrl->ops.ulps_ops.ulps_exit = dsi_ctrl_hw_cmn_ulps_exit;
ctrl->ops.wait_for_lane_idle =
dsi_ctrl_hw_14_wait_for_lane_idle;
ctrl->ops.ulps_ops.get_lanes_in_ulps =
dsi_ctrl_hw_cmn_get_lanes_in_ulps;
ctrl->ops.clamp_enable = dsi_ctrl_hw_14_clamp_enable;
ctrl->ops.clamp_disable = dsi_ctrl_hw_14_clamp_disable;
ctrl->ops.reg_dump_to_buffer =
dsi_ctrl_hw_14_reg_dump_to_buffer;
ctrl->ops.schedule_dma_cmd = NULL;
ctrl->ops.kickoff_command_non_embedded_mode = NULL;
ctrl->ops.config_clk_gating = NULL;
ctrl->ops.configure_cmddma_window = NULL;
ctrl->ops.reset_trig_ctrl = NULL;
ctrl->ops.log_line_count = NULL;
break;
case DSI_CTRL_VERSION_2_0:
ctrl->ops.setup_lane_map = dsi_ctrl_hw_20_setup_lane_map;
ctrl->ops.wait_for_lane_idle =
dsi_ctrl_hw_20_wait_for_lane_idle;
ctrl->ops.reg_dump_to_buffer =
dsi_ctrl_hw_20_reg_dump_to_buffer;
ctrl->ops.ulps_ops.ulps_request = NULL;
ctrl->ops.ulps_ops.ulps_exit = NULL;
ctrl->ops.ulps_ops.get_lanes_in_ulps = NULL;
ctrl->ops.clamp_enable = NULL;
ctrl->ops.clamp_disable = NULL;
ctrl->ops.schedule_dma_cmd = NULL;
ctrl->ops.kickoff_command_non_embedded_mode = NULL;
ctrl->ops.config_clk_gating = NULL;
ctrl->ops.configure_cmddma_window = NULL;
ctrl->ops.reset_trig_ctrl = NULL;
ctrl->ops.log_line_count = NULL;
break;
case DSI_CTRL_VERSION_2_2:
case DSI_CTRL_VERSION_2_3:
case DSI_CTRL_VERSION_2_4:
case DSI_CTRL_VERSION_2_5:
ctrl->ops.phy_reset_config = dsi_ctrl_hw_22_phy_reset_config;
ctrl->ops.config_clk_gating = dsi_ctrl_hw_22_config_clk_gating;
ctrl->ops.setup_lane_map = dsi_ctrl_hw_22_setup_lane_map;
ctrl->ops.wait_for_lane_idle =
dsi_ctrl_hw_22_wait_for_lane_idle;
ctrl->ops.reg_dump_to_buffer =
dsi_ctrl_hw_22_reg_dump_to_buffer;
ctrl->ops.ulps_ops.ulps_request = dsi_ctrl_hw_cmn_ulps_request;
ctrl->ops.ulps_ops.ulps_exit = dsi_ctrl_hw_cmn_ulps_exit;
ctrl->ops.ulps_ops.get_lanes_in_ulps =
dsi_ctrl_hw_cmn_get_lanes_in_ulps;
ctrl->ops.clamp_enable = NULL;
ctrl->ops.clamp_disable = NULL;
ctrl->ops.schedule_dma_cmd = dsi_ctrl_hw_22_schedule_dma_cmd;
ctrl->ops.kickoff_command_non_embedded_mode =
dsi_ctrl_hw_kickoff_non_embedded_mode;
ctrl->ops.configure_cmddma_window =
dsi_ctrl_hw_22_configure_cmddma_window;
ctrl->ops.reset_trig_ctrl =
dsi_ctrl_hw_22_reset_trigger_controls;
ctrl->ops.log_line_count = dsi_ctrl_hw_22_log_line_count;
break;
default:
break;
}
}
/**
* dsi_catalog_ctrl_setup() - return catalog info for dsi controller
* @ctrl: Pointer to DSI controller hw object.
* @version: DSI controller version.
* @index: DSI controller instance ID.
* @phy_isolation_enabled: DSI controller works isolated from phy.
* @null_insertion_enabled: DSI controller inserts null packet.
*
* This function setups the catalog information in the dsi_ctrl_hw object.
*
* return: error code for failure and 0 for success.
*/
int dsi_catalog_ctrl_setup(struct dsi_ctrl_hw *ctrl,
enum dsi_ctrl_version version, u32 index,
bool phy_isolation_enabled, bool null_insertion_enabled)
{
int rc = 0;
if (version == DSI_CTRL_VERSION_UNKNOWN ||
version >= DSI_CTRL_VERSION_MAX) {
DSI_ERR("Unsupported version: %d\n", version);
return -ENOTSUPP;
}
ctrl->index = index;
ctrl->null_insertion_enabled = null_insertion_enabled;
set_bit(DSI_CTRL_VIDEO_TPG, ctrl->feature_map);
set_bit(DSI_CTRL_CMD_TPG, ctrl->feature_map);
set_bit(DSI_CTRL_VARIABLE_REFRESH_RATE, ctrl->feature_map);
set_bit(DSI_CTRL_DYNAMIC_REFRESH, ctrl->feature_map);
set_bit(DSI_CTRL_DESKEW_CALIB, ctrl->feature_map);
set_bit(DSI_CTRL_DPHY, ctrl->feature_map);
switch (version) {
case DSI_CTRL_VERSION_1_4:
dsi_catalog_cmn_init(ctrl, version);
break;
case DSI_CTRL_VERSION_2_0:
case DSI_CTRL_VERSION_2_2:
case DSI_CTRL_VERSION_2_3:
case DSI_CTRL_VERSION_2_4:
ctrl->phy_isolation_enabled = phy_isolation_enabled;
dsi_catalog_cmn_init(ctrl, version);
break;
case DSI_CTRL_VERSION_2_5:
ctrl->widebus_support = true;
ctrl->phy_isolation_enabled = phy_isolation_enabled;
dsi_catalog_cmn_init(ctrl, version);
break;
default:
return -ENOTSUPP;
}
return rc;
}
/**
* dsi_catalog_phy_2_0_init() - catalog init for DSI PHY 14nm
*/
static void dsi_catalog_phy_2_0_init(struct dsi_phy_hw *phy)
{
phy->ops.regulator_enable = dsi_phy_hw_v2_0_regulator_enable;
phy->ops.regulator_disable = dsi_phy_hw_v2_0_regulator_disable;
phy->ops.enable = dsi_phy_hw_v2_0_enable;
phy->ops.disable = dsi_phy_hw_v2_0_disable;
phy->ops.calculate_timing_params =
dsi_phy_hw_calculate_timing_params;
phy->ops.phy_idle_on = dsi_phy_hw_v2_0_idle_on;
phy->ops.phy_idle_off = dsi_phy_hw_v2_0_idle_off;
phy->ops.calculate_timing_params =
dsi_phy_hw_calculate_timing_params;
phy->ops.phy_timing_val = dsi_phy_hw_timing_val_v2_0;
phy->ops.clamp_ctrl = dsi_phy_hw_v2_0_clamp_ctrl;
phy->ops.dyn_refresh_ops.dyn_refresh_config =
dsi_phy_hw_v2_0_dyn_refresh_config;
phy->ops.dyn_refresh_ops.dyn_refresh_pipe_delay =
dsi_phy_hw_v2_0_dyn_refresh_pipe_delay;
phy->ops.dyn_refresh_ops.dyn_refresh_helper =
dsi_phy_hw_v2_0_dyn_refresh_helper;
phy->ops.dyn_refresh_ops.dyn_refresh_trigger_sel = NULL;
phy->ops.dyn_refresh_ops.cache_phy_timings =
dsi_phy_hw_v2_0_cache_phy_timings;
}
/**
* dsi_catalog_phy_3_0_init() - catalog init for DSI PHY 10nm
*/
static void dsi_catalog_phy_3_0_init(struct dsi_phy_hw *phy)
{
phy->ops.regulator_enable = dsi_phy_hw_v3_0_regulator_enable;
phy->ops.regulator_disable = dsi_phy_hw_v3_0_regulator_disable;
phy->ops.enable = dsi_phy_hw_v3_0_enable;
phy->ops.disable = dsi_phy_hw_v3_0_disable;
phy->ops.calculate_timing_params =
dsi_phy_hw_calculate_timing_params;
phy->ops.ulps_ops.wait_for_lane_idle =
dsi_phy_hw_v3_0_wait_for_lane_idle;
phy->ops.ulps_ops.ulps_request =
dsi_phy_hw_v3_0_ulps_request;
phy->ops.ulps_ops.ulps_exit =
dsi_phy_hw_v3_0_ulps_exit;
phy->ops.ulps_ops.get_lanes_in_ulps =
dsi_phy_hw_v3_0_get_lanes_in_ulps;
phy->ops.ulps_ops.is_lanes_in_ulps =
dsi_phy_hw_v3_0_is_lanes_in_ulps;
phy->ops.phy_timing_val = dsi_phy_hw_timing_val_v3_0;
phy->ops.clamp_ctrl = dsi_phy_hw_v3_0_clamp_ctrl;
phy->ops.phy_lane_reset = dsi_phy_hw_v3_0_lane_reset;
phy->ops.toggle_resync_fifo = dsi_phy_hw_v3_0_toggle_resync_fifo;
phy->ops.dyn_refresh_ops.dyn_refresh_config =
dsi_phy_hw_v3_0_dyn_refresh_config;
phy->ops.dyn_refresh_ops.dyn_refresh_pipe_delay =
dsi_phy_hw_v3_0_dyn_refresh_pipe_delay;
phy->ops.dyn_refresh_ops.dyn_refresh_helper =
dsi_phy_hw_v3_0_dyn_refresh_helper;
phy->ops.dyn_refresh_ops.dyn_refresh_trigger_sel = NULL;
phy->ops.dyn_refresh_ops.cache_phy_timings =
dsi_phy_hw_v3_0_cache_phy_timings;
}
/**
* dsi_catalog_phy_4_0_init() - catalog init for DSI PHY 7nm
*/
static void dsi_catalog_phy_4_0_init(struct dsi_phy_hw *phy)
{
phy->ops.regulator_enable = NULL;
phy->ops.regulator_disable = NULL;
phy->ops.enable = dsi_phy_hw_v4_0_enable;
phy->ops.disable = dsi_phy_hw_v4_0_disable;
phy->ops.calculate_timing_params =
dsi_phy_hw_calculate_timing_params;
phy->ops.ulps_ops.wait_for_lane_idle =
dsi_phy_hw_v4_0_wait_for_lane_idle;
phy->ops.ulps_ops.ulps_request =
dsi_phy_hw_v4_0_ulps_request;
phy->ops.ulps_ops.ulps_exit =
dsi_phy_hw_v4_0_ulps_exit;
phy->ops.ulps_ops.get_lanes_in_ulps =
dsi_phy_hw_v4_0_get_lanes_in_ulps;
phy->ops.ulps_ops.is_lanes_in_ulps =
dsi_phy_hw_v4_0_is_lanes_in_ulps;
phy->ops.phy_timing_val = dsi_phy_hw_timing_val_v4_0;
phy->ops.phy_lane_reset = dsi_phy_hw_v4_0_lane_reset;
phy->ops.toggle_resync_fifo = dsi_phy_hw_v4_0_toggle_resync_fifo;
phy->ops.reset_clk_en_sel = dsi_phy_hw_v4_0_reset_clk_en_sel;
phy->ops.dyn_refresh_ops.dyn_refresh_config =
dsi_phy_hw_v4_0_dyn_refresh_config;
phy->ops.dyn_refresh_ops.dyn_refresh_pipe_delay =
dsi_phy_hw_v4_0_dyn_refresh_pipe_delay;
phy->ops.dyn_refresh_ops.dyn_refresh_helper =
dsi_phy_hw_v4_0_dyn_refresh_helper;
phy->ops.dyn_refresh_ops.dyn_refresh_trigger_sel =
dsi_phy_hw_v4_0_dyn_refresh_trigger_sel;
phy->ops.dyn_refresh_ops.cache_phy_timings =
dsi_phy_hw_v4_0_cache_phy_timings;
phy->ops.set_continuous_clk = dsi_phy_hw_v4_0_set_continuous_clk;
phy->ops.commit_phy_timing = dsi_phy_hw_v4_0_commit_phy_timing;
}
/**
* dsi_catalog_phy_setup() - return catalog info for dsi phy hardware
* @ctrl: Pointer to DSI PHY hw object.
* @version: DSI PHY version.
* @index: DSI PHY instance ID.
*
* This function setups the catalog information in the dsi_phy_hw object.
*
* return: error code for failure and 0 for success.
*/
int dsi_catalog_phy_setup(struct dsi_phy_hw *phy,
enum dsi_phy_version version,
u32 index)
{
int rc = 0;
if (version == DSI_PHY_VERSION_UNKNOWN ||
version >= DSI_PHY_VERSION_MAX) {
DSI_ERR("Unsupported version: %d\n", version);
return -ENOTSUPP;
}
phy->index = index;
phy->version = version;
set_bit(DSI_PHY_DPHY, phy->feature_map);
dsi_phy_timing_calc_init(phy, version);
switch (version) {
case DSI_PHY_VERSION_2_0:
dsi_catalog_phy_2_0_init(phy);
break;
case DSI_PHY_VERSION_3_0:
dsi_catalog_phy_3_0_init(phy);
break;
case DSI_PHY_VERSION_4_0:
case DSI_PHY_VERSION_4_1:
case DSI_PHY_VERSION_4_2:
dsi_catalog_phy_4_0_init(phy);
break;
case DSI_PHY_VERSION_0_0_HPM:
case DSI_PHY_VERSION_0_0_LPM:
case DSI_PHY_VERSION_1_0:
default:
return -ENOTSUPP;
}
return rc;
}

View File

@ -0,0 +1,288 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DSI_CATALOG_H_
#define _DSI_CATALOG_H_
#include "dsi_ctrl_hw.h"
#include "dsi_phy_hw.h"
/**
* dsi_catalog_ctrl_setup() - return catalog info for dsi controller
* @ctrl: Pointer to DSI controller hw object.
* @version: DSI controller version.
* @index: DSI controller instance ID.
* @phy_isolation_enabled: DSI controller works isolated from phy.
* @null_insertion_enabled: DSI controller inserts null packet.
*
* This function setups the catalog information in the dsi_ctrl_hw object.
*
* return: error code for failure and 0 for success.
*/
int dsi_catalog_ctrl_setup(struct dsi_ctrl_hw *ctrl,
enum dsi_ctrl_version version, u32 index,
bool phy_isolation_enabled, bool null_insertion_enabled);
/**
* dsi_catalog_phy_setup() - return catalog info for dsi phy hardware
* @phy: Pointer to DSI PHY hw object.
* @version: DSI PHY version.
* @index: DSI PHY instance ID.
*
* This function setups the catalog information in the dsi_phy_hw object.
*
* return: error code for failure and 0 for success.
*/
int dsi_catalog_phy_setup(struct dsi_phy_hw *phy,
enum dsi_phy_version version,
u32 index);
/**
* dsi_phy_timing_calc_init() - initialize info for DSI PHY timing calculations
* @phy: Pointer to DSI PHY hw object.
* @version: DSI PHY version.
*
* This function setups the catalog information in the dsi_phy_hw object.
*
* return: error code for failure and 0 for success.
*/
int dsi_phy_timing_calc_init(struct dsi_phy_hw *phy,
enum dsi_phy_version version);
/**
* dsi_phy_hw_calculate_timing_params() - DSI PHY timing parameter calculations
* @phy: Pointer to DSI PHY hw object.
* @mode: DSI mode information.
* @host: DSI host configuration.
* @timing: DSI phy lane configurations.
* @use_mode_bit_clk: Boolean to indicate whether to recalculate bit clk.
*
* This function setups the catalog information in the dsi_phy_hw object.
*
* return: error code for failure and 0 for success.
*/
int dsi_phy_hw_calculate_timing_params(struct dsi_phy_hw *phy,
struct dsi_mode_info *mode,
struct dsi_host_common_cfg *host,
struct dsi_phy_per_lane_cfgs *timing,
bool use_mode_bit_clk);
/* Definitions for 14nm PHY hardware driver */
void dsi_phy_hw_v2_0_regulator_enable(struct dsi_phy_hw *phy,
struct dsi_phy_per_lane_cfgs *cfg);
void dsi_phy_hw_v2_0_regulator_disable(struct dsi_phy_hw *phy);
void dsi_phy_hw_v2_0_enable(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
void dsi_phy_hw_v2_0_disable(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
void dsi_phy_hw_v2_0_idle_on(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
void dsi_phy_hw_v2_0_idle_off(struct dsi_phy_hw *phy);
int dsi_phy_hw_timing_val_v2_0(struct dsi_phy_per_lane_cfgs *timing_cfg,
u32 *timing_val, u32 size);
void dsi_phy_hw_v2_0_clamp_ctrl(struct dsi_phy_hw *phy, bool enable);
void dsi_phy_hw_v2_0_dyn_refresh_helper(struct dsi_phy_hw *phy, u32 offset);
void dsi_phy_hw_v2_0_dyn_refresh_config(struct dsi_phy_hw *phy,
struct dsi_phy_cfg *cfg, bool is_master);
void dsi_phy_hw_v2_0_dyn_refresh_pipe_delay(struct dsi_phy_hw *phy,
struct dsi_dyn_clk_delay *delay);
int dsi_phy_hw_v2_0_cache_phy_timings(struct dsi_phy_per_lane_cfgs *timings,
u32 *dst, u32 size);
/* Definitions for 10nm PHY hardware driver */
void dsi_phy_hw_v3_0_regulator_enable(struct dsi_phy_hw *phy,
struct dsi_phy_per_lane_cfgs *cfg);
void dsi_phy_hw_v3_0_regulator_disable(struct dsi_phy_hw *phy);
void dsi_phy_hw_v3_0_enable(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
void dsi_phy_hw_v3_0_disable(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
int dsi_phy_hw_v3_0_wait_for_lane_idle(struct dsi_phy_hw *phy, u32 lanes);
void dsi_phy_hw_v3_0_ulps_request(struct dsi_phy_hw *phy,
struct dsi_phy_cfg *cfg, u32 lanes);
void dsi_phy_hw_v3_0_ulps_exit(struct dsi_phy_hw *phy,
struct dsi_phy_cfg *cfg, u32 lanes);
u32 dsi_phy_hw_v3_0_get_lanes_in_ulps(struct dsi_phy_hw *phy);
bool dsi_phy_hw_v3_0_is_lanes_in_ulps(u32 lanes, u32 ulps_lanes);
int dsi_phy_hw_timing_val_v3_0(struct dsi_phy_per_lane_cfgs *timing_cfg,
u32 *timing_val, u32 size);
void dsi_phy_hw_v3_0_clamp_ctrl(struct dsi_phy_hw *phy, bool enable);
int dsi_phy_hw_v3_0_lane_reset(struct dsi_phy_hw *phy);
void dsi_phy_hw_v3_0_toggle_resync_fifo(struct dsi_phy_hw *phy);
/* Definitions for 7nm PHY hardware driver */
void dsi_phy_hw_v4_0_enable(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
void dsi_phy_hw_v4_0_disable(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
int dsi_phy_hw_v4_0_wait_for_lane_idle(struct dsi_phy_hw *phy, u32 lanes);
void dsi_phy_hw_v4_0_ulps_request(struct dsi_phy_hw *phy,
struct dsi_phy_cfg *cfg, u32 lanes);
void dsi_phy_hw_v4_0_ulps_exit(struct dsi_phy_hw *phy,
struct dsi_phy_cfg *cfg, u32 lanes);
u32 dsi_phy_hw_v4_0_get_lanes_in_ulps(struct dsi_phy_hw *phy);
bool dsi_phy_hw_v4_0_is_lanes_in_ulps(u32 lanes, u32 ulps_lanes);
int dsi_phy_hw_timing_val_v4_0(struct dsi_phy_per_lane_cfgs *timing_cfg,
u32 *timing_val, u32 size);
int dsi_phy_hw_v4_0_lane_reset(struct dsi_phy_hw *phy);
void dsi_phy_hw_v4_0_toggle_resync_fifo(struct dsi_phy_hw *phy);
void dsi_phy_hw_v4_0_reset_clk_en_sel(struct dsi_phy_hw *phy);
void dsi_phy_hw_v4_0_set_continuous_clk(struct dsi_phy_hw *phy, bool enable);
void dsi_phy_hw_v4_0_commit_phy_timing(struct dsi_phy_hw *phy,
struct dsi_phy_per_lane_cfgs *timing);
/* DSI controller common ops */
u32 dsi_ctrl_hw_cmn_get_interrupt_status(struct dsi_ctrl_hw *ctrl);
u32 dsi_ctrl_hw_cmn_poll_slave_dma_status(struct dsi_ctrl_hw *ctrl);
void dsi_ctrl_hw_cmn_clear_interrupt_status(struct dsi_ctrl_hw *ctrl, u32 ints);
void dsi_ctrl_hw_cmn_enable_status_interrupts(struct dsi_ctrl_hw *ctrl,
u32 ints);
u64 dsi_ctrl_hw_cmn_get_error_status(struct dsi_ctrl_hw *ctrl);
void dsi_ctrl_hw_cmn_clear_error_status(struct dsi_ctrl_hw *ctrl, u64 errors);
void dsi_ctrl_hw_cmn_enable_error_interrupts(struct dsi_ctrl_hw *ctrl,
u64 errors);
void dsi_ctrl_hw_cmn_video_test_pattern_setup(struct dsi_ctrl_hw *ctrl,
enum dsi_test_pattern type,
u32 init_val);
void dsi_ctrl_hw_cmn_cmd_test_pattern_setup(struct dsi_ctrl_hw *ctrl,
enum dsi_test_pattern type,
u32 init_val,
u32 stream_id);
void dsi_ctrl_hw_cmn_test_pattern_enable(struct dsi_ctrl_hw *ctrl, bool enable);
void dsi_ctrl_hw_cmn_trigger_cmd_test_pattern(struct dsi_ctrl_hw *ctrl,
u32 stream_id);
void dsi_ctrl_hw_cmn_host_setup(struct dsi_ctrl_hw *ctrl,
struct dsi_host_common_cfg *config);
void dsi_ctrl_hw_cmn_video_engine_en(struct dsi_ctrl_hw *ctrl, bool on);
void dsi_ctrl_hw_cmn_video_engine_setup(struct dsi_ctrl_hw *ctrl,
struct dsi_host_common_cfg *common_cfg,
struct dsi_video_engine_cfg *cfg);
void dsi_ctrl_hw_cmn_setup_avr(struct dsi_ctrl_hw *ctrl, bool enable);
void dsi_ctrl_hw_cmn_set_video_timing(struct dsi_ctrl_hw *ctrl,
struct dsi_mode_info *mode);
void dsi_ctrl_hw_cmn_set_timing_db(struct dsi_ctrl_hw *ctrl,
bool enable);
void dsi_ctrl_hw_cmn_cmd_engine_setup(struct dsi_ctrl_hw *ctrl,
struct dsi_host_common_cfg *common_cfg,
struct dsi_cmd_engine_cfg *cfg);
void dsi_ctrl_hw_cmn_ctrl_en(struct dsi_ctrl_hw *ctrl, bool on);
void dsi_ctrl_hw_cmn_cmd_engine_en(struct dsi_ctrl_hw *ctrl, bool on);
void dsi_ctrl_hw_cmn_setup_cmd_stream(struct dsi_ctrl_hw *ctrl,
struct dsi_mode_info *mode,
struct dsi_host_common_cfg *cfg,
u32 vc_id,
struct dsi_rect *roi);
void dsi_ctrl_hw_cmn_phy_sw_reset(struct dsi_ctrl_hw *ctrl);
void dsi_ctrl_hw_cmn_soft_reset(struct dsi_ctrl_hw *ctrl);
void dsi_ctrl_hw_cmn_setup_misr(struct dsi_ctrl_hw *ctrl,
enum dsi_op_mode panel_mode,
bool enable, u32 frame_count);
u32 dsi_ctrl_hw_cmn_collect_misr(struct dsi_ctrl_hw *ctrl,
enum dsi_op_mode panel_mode);
void dsi_ctrl_hw_cmn_kickoff_command(struct dsi_ctrl_hw *ctrl,
struct dsi_ctrl_cmd_dma_info *cmd,
u32 flags);
void dsi_ctrl_hw_cmn_kickoff_fifo_command(struct dsi_ctrl_hw *ctrl,
struct dsi_ctrl_cmd_dma_fifo_info *cmd,
u32 flags);
void dsi_ctrl_hw_cmn_reset_cmd_fifo(struct dsi_ctrl_hw *ctrl);
void dsi_ctrl_hw_cmn_trigger_command_dma(struct dsi_ctrl_hw *ctrl);
void dsi_ctrl_hw_dln0_phy_err(struct dsi_ctrl_hw *ctrl);
void dsi_ctrl_hw_cmn_phy_reset_config(struct dsi_ctrl_hw *ctrl,
bool enable);
void dsi_ctrl_hw_22_phy_reset_config(struct dsi_ctrl_hw *ctrl,
bool enable);
u32 dsi_ctrl_hw_cmn_get_cmd_read_data(struct dsi_ctrl_hw *ctrl,
u8 *rd_buf,
u32 read_offset,
u32 rx_byte,
u32 pkt_size, u32 *hw_read_cnt);
void dsi_ctrl_hw_cmn_clear_rdbk_reg(struct dsi_ctrl_hw *ctrl);
void dsi_ctrl_hw_22_schedule_dma_cmd(struct dsi_ctrl_hw *ctrl, int line_on);
int dsi_ctrl_hw_cmn_ctrl_reset(struct dsi_ctrl_hw *ctrl,
int mask);
void dsi_ctrl_hw_cmn_mask_error_intr(struct dsi_ctrl_hw *ctrl, u32 idx,
bool en);
void dsi_ctrl_hw_cmn_error_intr_ctrl(struct dsi_ctrl_hw *ctrl, bool en);
u32 dsi_ctrl_hw_cmn_get_error_mask(struct dsi_ctrl_hw *ctrl);
u32 dsi_ctrl_hw_cmn_get_hw_version(struct dsi_ctrl_hw *ctrl);
int dsi_ctrl_hw_cmn_wait_for_cmd_mode_mdp_idle(struct dsi_ctrl_hw *ctrl);
/* Definitions specific to 1.4 DSI controller hardware */
int dsi_ctrl_hw_14_wait_for_lane_idle(struct dsi_ctrl_hw *ctrl, u32 lanes);
void dsi_ctrl_hw_14_setup_lane_map(struct dsi_ctrl_hw *ctrl,
struct dsi_lane_map *lane_map);
void dsi_ctrl_hw_cmn_ulps_request(struct dsi_ctrl_hw *ctrl, u32 lanes);
void dsi_ctrl_hw_cmn_ulps_exit(struct dsi_ctrl_hw *ctrl, u32 lanes);
u32 dsi_ctrl_hw_cmn_get_lanes_in_ulps(struct dsi_ctrl_hw *ctrl);
void dsi_ctrl_hw_14_clamp_enable(struct dsi_ctrl_hw *ctrl,
u32 lanes,
bool enable_ulps);
void dsi_ctrl_hw_14_clamp_disable(struct dsi_ctrl_hw *ctrl,
u32 lanes,
bool disable_ulps);
ssize_t dsi_ctrl_hw_14_reg_dump_to_buffer(struct dsi_ctrl_hw *ctrl,
char *buf,
u32 size);
/* Definitions specific to 2.0 DSI controller hardware */
void dsi_ctrl_hw_20_setup_lane_map(struct dsi_ctrl_hw *ctrl,
struct dsi_lane_map *lane_map);
int dsi_ctrl_hw_20_wait_for_lane_idle(struct dsi_ctrl_hw *ctrl, u32 lanes);
ssize_t dsi_ctrl_hw_20_reg_dump_to_buffer(struct dsi_ctrl_hw *ctrl,
char *buf,
u32 size);
void dsi_ctrl_hw_kickoff_non_embedded_mode(struct dsi_ctrl_hw *ctrl,
struct dsi_ctrl_cmd_dma_info *cmd,
u32 flags);
/* Definitions specific to 2.2 DSI controller hardware */
void dsi_ctrl_hw_22_setup_lane_map(struct dsi_ctrl_hw *ctrl,
struct dsi_lane_map *lane_map);
int dsi_ctrl_hw_22_wait_for_lane_idle(struct dsi_ctrl_hw *ctrl, u32 lanes);
ssize_t dsi_ctrl_hw_22_reg_dump_to_buffer(struct dsi_ctrl_hw *ctrl,
char *buf, u32 size);
void dsi_ctrl_hw_22_config_clk_gating(struct dsi_ctrl_hw *ctrl, bool enable,
enum dsi_clk_gate_type clk_selection);
void dsi_ctrl_hw_cmn_set_continuous_clk(struct dsi_ctrl_hw *ctrl, bool enable);
void dsi_ctrl_hw_cmn_hs_req_sel(struct dsi_ctrl_hw *ctrl, bool sel_phy);
/* dynamic refresh specific functions */
void dsi_phy_hw_v3_0_dyn_refresh_helper(struct dsi_phy_hw *phy, u32 offset);
void dsi_phy_hw_v3_0_dyn_refresh_config(struct dsi_phy_hw *phy,
struct dsi_phy_cfg *cfg, bool is_master);
void dsi_phy_hw_v3_0_dyn_refresh_pipe_delay(struct dsi_phy_hw *phy,
struct dsi_dyn_clk_delay *delay);
int dsi_ctrl_hw_cmn_wait4dynamic_refresh_done(struct dsi_ctrl_hw *ctrl);
bool dsi_ctrl_hw_cmn_vid_engine_busy(struct dsi_ctrl_hw *ctrl);
int dsi_phy_hw_v3_0_cache_phy_timings(struct dsi_phy_per_lane_cfgs *timings,
u32 *dst, u32 size);
void dsi_phy_hw_v4_0_dyn_refresh_trigger_sel(struct dsi_phy_hw *phy,
bool is_master);
void dsi_phy_hw_v4_0_dyn_refresh_helper(struct dsi_phy_hw *phy, u32 offset);
void dsi_phy_hw_v4_0_dyn_refresh_config(struct dsi_phy_hw *phy,
struct dsi_phy_cfg *cfg, bool is_master);
void dsi_phy_hw_v4_0_dyn_refresh_pipe_delay(struct dsi_phy_hw *phy,
struct dsi_dyn_clk_delay *delay);
int dsi_phy_hw_v4_0_cache_phy_timings(struct dsi_phy_per_lane_cfgs *timings,
u32 *dst, u32 size);
void dsi_ctrl_hw_22_configure_cmddma_window(struct dsi_ctrl_hw *ctrl,
struct dsi_ctrl_cmd_dma_info *cmd,
u32 line_no, u32 window);
void dsi_ctrl_hw_22_reset_trigger_controls(struct dsi_ctrl_hw *ctrl,
struct dsi_host_common_cfg *cfg);
u32 dsi_ctrl_hw_22_log_line_count(struct dsi_ctrl_hw *ctrl, bool cmd_mode);
#endif /* _DSI_CATALOG_H_ */

View File

@ -0,0 +1,332 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DSI_CLK_H_
#define _DSI_CLK_H_
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/clk.h>
#include <drm/drmP.h>
#define MAX_STRING_LEN 32
#define MAX_DSI_CTRL 2
enum dsi_clk_state {
DSI_CLK_OFF,
DSI_CLK_ON,
DSI_CLK_EARLY_GATE,
};
enum clk_req_client {
DSI_CLK_REQ_MDP_CLIENT = 0,
DSI_CLK_REQ_DSI_CLIENT,
};
enum dsi_link_clk_type {
DSI_LINK_ESC_CLK,
DSI_LINK_BYTE_CLK,
DSI_LINK_PIX_CLK,
DSI_LINK_BYTE_INTF_CLK,
DSI_LINK_CLK_MAX,
};
enum dsi_link_clk_op_type {
DSI_LINK_CLK_SET_RATE = BIT(0),
DSI_LINK_CLK_PREPARE = BIT(1),
DSI_LINK_CLK_ENABLE = BIT(2),
DSI_LINK_CLK_START = BIT(0) | BIT(1) | BIT(2),
};
enum dsi_clk_type {
DSI_CORE_CLK = BIT(0),
DSI_LINK_CLK = BIT(1),
DSI_ALL_CLKS = (BIT(0) | BIT(1)),
DSI_CLKS_MAX = BIT(2),
};
enum dsi_lclk_type {
DSI_LINK_NONE = 0,
DSI_LINK_LP_CLK = BIT(0),
DSI_LINK_HS_CLK = BIT(1),
};
struct dsi_clk_ctrl_info {
enum dsi_clk_type clk_type;
enum dsi_clk_state clk_state;
enum clk_req_client client;
};
struct clk_ctrl_cb {
void *priv;
int (*dsi_clk_cb)(void *priv, struct dsi_clk_ctrl_info clk_ctrl_info);
};
/**
* struct dsi_core_clk_info - Core clock information for DSI hardware
* @mdp_core_clk: Handle to MDP core clock.
* @iface_clk: Handle to MDP interface clock.
* @core_mmss_clk: Handle to MMSS core clock.
* @bus_clk: Handle to bus clock.
* @mnoc_clk: Handle to MMSS NOC clock.
* @drm: Pointer to drm device node
*/
struct dsi_core_clk_info {
struct clk *mdp_core_clk;
struct clk *iface_clk;
struct clk *core_mmss_clk;
struct clk *bus_clk;
struct clk *mnoc_clk;
struct drm_device *drm;
};
/**
* struct dsi_link_hs_clk_info - Set of high speed link clocks for DSI HW
* @byte_clk: Handle to DSI byte_clk.
* @pixel_clk: Handle to DSI pixel_clk.
* @byte_intf_clk: Handle to DSI byte intf. clock.
*/
struct dsi_link_hs_clk_info {
struct clk *byte_clk;
struct clk *pixel_clk;
struct clk *byte_intf_clk;
};
/**
* struct dsi_link_lp_clk_info - Set of low power link clocks for DSI HW.
* @esc_clk: Handle to DSI escape clock.
*/
struct dsi_link_lp_clk_info {
struct clk *esc_clk;
};
/**
* struct link_clk_freq - Clock frequency information for Link clocks
* @byte_clk_rate: Frequency of DSI byte_clk in Hz.
* @byte_intf_clk_rate: Frequency of DSI byte_intf_clk in Hz.
* @pixel_clk_rate: Frequency of DSI pixel_clk in Hz.
* @esc_clk_rate: Frequency of DSI escape clock in Hz.
*/
struct link_clk_freq {
u32 byte_clk_rate;
u32 byte_intf_clk_rate;
u32 pix_clk_rate;
u32 esc_clk_rate;
};
/**
* typedef *pre_clockoff_cb() - Callback before clock is turned off
* @priv: private data pointer.
* @clk_type: clock which is being turned off.
* @l_type: specifies if the clock is HS or LP type. Valid only for link clocks.
* @new_state: next state for the clock.
*
* @return: error code.
*/
typedef int (*pre_clockoff_cb)(void *priv,
enum dsi_clk_type clk_type,
enum dsi_lclk_type l_type,
enum dsi_clk_state new_state);
/**
* typedef *post_clockoff_cb() - Callback after clock is turned off
* @priv: private data pointer.
* @clk_type: clock which was turned off.
* @l_type: specifies if the clock is HS or LP type. Valid only for link clocks.
* @curr_state: current state for the clock.
*
* @return: error code.
*/
typedef int (*post_clockoff_cb)(void *priv,
enum dsi_clk_type clk_type,
enum dsi_lclk_type l_type,
enum dsi_clk_state curr_state);
/**
* typedef *post_clockon_cb() - Callback after clock is turned on
* @priv: private data pointer.
* @clk_type: clock which was turned on.
* @l_type: specifies if the clock is HS or LP type. Valid only for link clocks.
* @curr_state: current state for the clock.
*
* @return: error code.
*/
typedef int (*post_clockon_cb)(void *priv,
enum dsi_clk_type clk_type,
enum dsi_lclk_type l_type,
enum dsi_clk_state curr_state);
/**
* typedef *pre_clockon_cb() - Callback before clock is turned on
* @priv: private data pointer.
* @clk_type: clock which is being turned on.
* @l_type: specifies if the clock is HS or LP type.Valid only for link clocks.
* @new_state: next state for the clock.
*
* @return: error code.
*/
typedef int (*pre_clockon_cb)(void *priv,
enum dsi_clk_type clk_type,
enum dsi_lclk_type l_type,
enum dsi_clk_state new_state);
/**
* struct dsi_clk_info - clock information for DSI hardware.
* @name: client name.
* @c_clks[MAX_DSI_CTRL] array of core clock configurations
* @l_lp_clks[MAX_DSI_CTRL] array of low power(esc) clock configurations
* @l_hs_clks[MAX_DSI_CTRL] array of high speed clock configurations
* @ctrl_index[MAX_DSI_CTRL] array of DSI controller indexes mapped
* to core and link clock configurations
* @pre_clkoff_cb callback before clock is turned off
* @post_clkoff_cb callback after clock is turned off
* @post_clkon_cb callback after clock is turned on
* @pre_clkon_cb callback before clock is turned on
* @priv_data pointer to private data
* @master_ndx master DSI controller index
* @dsi_ctrl_count number of DSI controllers
*/
struct dsi_clk_info {
char name[MAX_STRING_LEN];
struct dsi_core_clk_info c_clks[MAX_DSI_CTRL];
struct dsi_link_lp_clk_info l_lp_clks[MAX_DSI_CTRL];
struct dsi_link_hs_clk_info l_hs_clks[MAX_DSI_CTRL];
u32 ctrl_index[MAX_DSI_CTRL];
pre_clockoff_cb pre_clkoff_cb;
post_clockoff_cb post_clkoff_cb;
post_clockon_cb post_clkon_cb;
pre_clockon_cb pre_clkon_cb;
void *priv_data;
u32 master_ndx;
u32 dsi_ctrl_count;
};
/**
* struct dsi_clk_link_set - Pair of clock handles to describe link clocks
* @byte_clk: Handle to DSi byte_clk.
* @pixel_clk: Handle to DSI pixel_clk.
*/
struct dsi_clk_link_set {
struct clk *byte_clk;
struct clk *pixel_clk;
};
/**
* dsi_display_clk_mngr_update_splash_status() - Update splash stattus
* @clk_mngr: Structure containing DSI clock information
* @status: Splash status
*/
void dsi_display_clk_mngr_update_splash_status(void *clk_mgr, bool status);
/**
* dsi_display_clk_mgr_register() - Register DSI clock manager
* @info: Structure containing DSI clock information
*/
void *dsi_display_clk_mngr_register(struct dsi_clk_info *info);
/**
* dsi_display_clk_mngr_deregister() - Deregister DSI clock manager
* @clk_mngr: DSI clock manager pointer
*/
int dsi_display_clk_mngr_deregister(void *clk_mngr);
/**
* dsi_register_clk_handle() - Register clock handle with DSI clock manager
* @clk_mngr: DSI clock manager pointer
* @client: DSI clock client pointer.
*/
void *dsi_register_clk_handle(void *clk_mngr, char *client);
/**
* dsi_deregister_clk_handle() - Deregister clock handle from DSI clock manager
* @client: DSI clock client pointer.
*
* return: error code in case of failure or 0 for success.
*/
int dsi_deregister_clk_handle(void *client);
/**
* dsi_display_link_clk_force_update_ctrl() - force to set link clks
* @handle: Handle of desired DSI clock client.
*
* return: error code in case of failure or 0 for success.
*/
int dsi_display_link_clk_force_update_ctrl(void *handle);
/**
* dsi_display_clk_ctrl() - set frequencies for link clks
* @handle: Handle of desired DSI clock client.
* @clk_type: Clock which is being controlled.
* @clk_state: Desired state of clock
*
* return: error code in case of failure or 0 for success.
*/
int dsi_display_clk_ctrl(void *handle, u32 clk_type, u32 clk_state);
/**
* dsi_clk_set_link_frequencies() - set frequencies for link clks
* @client: DSI clock client pointer.
* @freq: Structure containing link clock frequencies.
* @index: Index of the DSI controller.
*
* return: error code in case of failure or 0 for success.
*/
int dsi_clk_set_link_frequencies(void *client, struct link_clk_freq freq,
u32 index);
/**
* dsi_clk_set_pixel_clk_rate() - set frequency for pixel_clk
* @client: DSI clock client pointer.
* @pixel_clk: Pixel_clk rate in Hz.
* @index: Index of the DSI controller.
* return: error code in case of failure or 0 for success.
*/
int dsi_clk_set_pixel_clk_rate(void *client, u64 pixel_clk, u32 index);
/**
* dsi_clk_set_byte_clk_rate() - set frequency for byte clock
* @client: DSI clock client pointer.
* @byte_clk: Pixel clock rate in Hz.
* @byte_intf_clk: Byte interface clock rate in Hz.
* @index: Index of the DSI controller.
* return: error code in case of failure or 0 for success.
*/
int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk,
u64 byte_intf_clk, u32 index);
/**
* dsi_clk_update_parent() - update parent clocks for specified clock
* @parent: link clock pair which are set as parent.
* @child: link clock pair whose parent has to be set.
*/
int dsi_clk_update_parent(struct dsi_clk_link_set *parent,
struct dsi_clk_link_set *child);
/**
* dsi_clk_prepare_enable() - prepare and enable dsi src clocks
* @clk: list of src clocks.
*
* @return: Zero on success and err no on failure
*/
int dsi_clk_prepare_enable(struct dsi_clk_link_set *clk);
/**
* dsi_clk_disable_unprepare() - disable and unprepare dsi src clocks
* @clk: list of src clocks.
*/
void dsi_clk_disable_unprepare(struct dsi_clk_link_set *clk);
/**
* dsi_display_dump_clk_handle_state() - dump client clock state
* @client: DSI clock client pointer.
*/
int dsi_display_dump_clk_handle_state(void *client);
#endif /* _DSI_CLK_H_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,889 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DSI_CTRL_H_
#define _DSI_CTRL_H_
#include <linux/debugfs.h>
#include "dsi_defs.h"
#include "dsi_ctrl_hw.h"
#include "dsi_clk.h"
#include "dsi_pwr.h"
#include "drm_mipi_dsi.h"
/*
* DSI Command transfer modifiers
* @DSI_CTRL_CMD_READ: The current transfer involves reading data.
* @DSI_CTRL_CMD_BROADCAST: The current transfer needs to be done in
* broadcast mode to multiple slaves.
* @DSI_CTRL_CMD_BROADCAST_MASTER: This controller is the master and the slaves
* sync to this trigger.
* @DSI_CTRL_CMD_DEFER_TRIGGER: Defer the command trigger to later.
* @DSI_CTRL_CMD_FIFO_STORE: Use FIFO for command transfer in place of
* reading data from memory.
* @DSI_CTRL_CMD_FETCH_MEMORY: Fetch command from memory through AXI bus
* and transfer it.
* @DSI_CTRL_CMD_LAST_COMMAND: Trigger the DMA cmd transfer if this is last
* command in the batch.
* @DSI_CTRL_CMD_NON_EMBEDDED_MODE:Transfer cmd packets in non embedded mode.
* @DSI_CTRL_CMD_CUSTOM_DMA_SCHED: Use the dma scheduling line number defined in
* display panel dtsi file instead of default.
* @DSI_CTRL_CMD_ASYNC_WAIT: Command flag to indicate that the wait for done
* for this command is asynchronous and must be queued.
*/
#define DSI_CTRL_CMD_READ 0x1
#define DSI_CTRL_CMD_BROADCAST 0x2
#define DSI_CTRL_CMD_BROADCAST_MASTER 0x4
#define DSI_CTRL_CMD_DEFER_TRIGGER 0x8
#define DSI_CTRL_CMD_FIFO_STORE 0x10
#define DSI_CTRL_CMD_FETCH_MEMORY 0x20
#define DSI_CTRL_CMD_LAST_COMMAND 0x40
#define DSI_CTRL_CMD_NON_EMBEDDED_MODE 0x80
#define DSI_CTRL_CMD_CUSTOM_DMA_SCHED 0x100
#define DSI_CTRL_CMD_ASYNC_WAIT 0x200
/* DSI embedded mode fifo size
* If the command is greater than 256 bytes it is sent in non-embedded mode.
*/
#define DSI_EMBEDDED_MODE_DMA_MAX_SIZE_BYTES 256
/* max size supported for dsi cmd transfer using TPG */
#define DSI_CTRL_MAX_CMD_FIFO_STORE_SIZE 64
/*Default tearcheck window size as programmed by MDP*/
#define TEARCHECK_WINDOW_SIZE 5
/**
* enum dsi_power_state - defines power states for dsi controller.
* @DSI_CTRL_POWER_VREG_OFF: Digital and analog supplies for DSI controller
turned off
* @DSI_CTRL_POWER_VREG_ON: Digital and analog supplies for DSI controller
* @DSI_CTRL_POWER_MAX: Maximum value.
*/
enum dsi_power_state {
DSI_CTRL_POWER_VREG_OFF = 0,
DSI_CTRL_POWER_VREG_ON,
DSI_CTRL_POWER_MAX,
};
/**
* enum dsi_engine_state - define engine status for dsi controller.
* @DSI_CTRL_ENGINE_OFF: Engine is turned off.
* @DSI_CTRL_ENGINE_ON: Engine is turned on.
* @DSI_CTRL_ENGINE_MAX: Maximum value.
*/
enum dsi_engine_state {
DSI_CTRL_ENGINE_OFF = 0,
DSI_CTRL_ENGINE_ON,
DSI_CTRL_ENGINE_MAX,
};
/**
* enum dsi_ctrl_driver_ops - controller driver ops
*/
enum dsi_ctrl_driver_ops {
DSI_CTRL_OP_POWER_STATE_CHANGE,
DSI_CTRL_OP_CMD_ENGINE,
DSI_CTRL_OP_VID_ENGINE,
DSI_CTRL_OP_HOST_ENGINE,
DSI_CTRL_OP_CMD_TX,
DSI_CTRL_OP_HOST_INIT,
DSI_CTRL_OP_TPG,
DSI_CTRL_OP_PHY_SW_RESET,
DSI_CTRL_OP_ASYNC_TIMING,
DSI_CTRL_OP_MAX
};
/**
* struct dsi_ctrl_power_info - digital and analog power supplies for dsi host
* @digital: Digital power supply required to turn on DSI controller hardware.
* @host_pwr: Analog power supplies required to turn on DSI controller hardware.
* Even though DSI controller it self does not require an analog
* power supply, supplies required for PLL can be defined here to
* allow proper control over these supplies.
*/
struct dsi_ctrl_power_info {
struct dsi_regulator_info digital;
struct dsi_regulator_info host_pwr;
};
/**
* struct dsi_ctrl_clk_info - clock information for DSI controller
* @core_clks: Core clocks needed to access DSI controller registers.
* @hs_link_clks: Clocks required to transmit high speed data over DSI
* @lp_link_clks: Clocks required to perform low power ops over DSI
* @rcg_clks: Root clock generation clocks generated in MMSS_CC. The
* output of the PLL is set as parent for these root
* clocks. These clocks are specific to controller
* instance.
* @mux_clks: Mux clocks used for Dynamic refresh feature.
* @ext_clks: External byte/pixel clocks from the MMSS block. These
* clocks are set as parent to rcg clocks.
* @pll_op_clks: TODO:
* @shadow_clks: TODO:
*/
struct dsi_ctrl_clk_info {
/* Clocks parsed from DT */
struct dsi_core_clk_info core_clks;
struct dsi_link_hs_clk_info hs_link_clks;
struct dsi_link_lp_clk_info lp_link_clks;
struct dsi_clk_link_set rcg_clks;
/* Clocks set by DSI Manager */
struct dsi_clk_link_set mux_clks;
struct dsi_clk_link_set ext_clks;
struct dsi_clk_link_set pll_op_clks;
struct dsi_clk_link_set shadow_clks;
};
/**
* struct dsi_ctrl_state_info - current driver state information
* @power_state: Status of power states on DSI controller.
* @cmd_engine_state: Status of DSI command engine.
* @vid_engine_state: Status of DSI video engine.
* @controller_state: Status of DSI Controller engine.
* @host_initialized: Boolean to indicate status of DSi host Initialization
* @tpg_enabled: Boolean to indicate whether tpg is enabled.
*/
struct dsi_ctrl_state_info {
enum dsi_power_state power_state;
enum dsi_engine_state cmd_engine_state;
enum dsi_engine_state vid_engine_state;
enum dsi_engine_state controller_state;
bool host_initialized;
bool tpg_enabled;
};
/**
* struct dsi_ctrl_interrupts - define interrupt information
* @irq_lock: Spinlock for ISR handler.
* @irq_num: Linux interrupt number associated with device.
* @irq_stat_mask: Hardware mask of currently enabled interrupts.
* @irq_stat_refcount: Number of times each interrupt has been requested.
* @irq_stat_cb: Status IRQ callback definitions.
* @irq_err_cb: IRQ callback definition to handle DSI ERRORs.
* @cmd_dma_done: Completion signal for DSI_CMD_MODE_DMA_DONE interrupt
* @vid_frame_done: Completion signal for DSI_VIDEO_MODE_FRAME_DONE int.
* @cmd_frame_done: Completion signal for DSI_CMD_FRAME_DONE interrupt.
*/
struct dsi_ctrl_interrupts {
spinlock_t irq_lock;
int irq_num;
uint32_t irq_stat_mask;
int irq_stat_refcount[DSI_STATUS_INTERRUPT_COUNT];
struct dsi_event_cb_info irq_stat_cb[DSI_STATUS_INTERRUPT_COUNT];
struct dsi_event_cb_info irq_err_cb;
struct completion cmd_dma_done;
struct completion vid_frame_done;
struct completion cmd_frame_done;
struct completion bta_done;
};
/**
* struct dsi_ctrl - DSI controller object
* @pdev: Pointer to platform device.
* @cell_index: Instance cell id.
* @horiz_index: Index in physical horizontal CTRL layout, 0 = leftmost
* @name: Name of the controller instance.
* @refcount: ref counter.
* @ctrl_lock: Mutex for hardware and object access.
* @drm_dev: Pointer to DRM device.
* @version: DSI controller version.
* @hw: DSI controller hardware object.
* @current_state: Current driver and hardware state.
* @clk_cb: Callback for DSI clock control.
* @irq_info: Interrupt information.
* @recovery_cb: Recovery call back to SDE.
* @panel_id_cb: Callback for reporting panel id.
* @clk_info: Clock information.
* @clk_freq: DSi Link clock frequency information.
* @pwr_info: Power information.
* @host_config: Current host configuration.
* @mode_bounds: Boundaries of the default mode ROI.
* Origin is at top left of all CTRLs.
* @roi: Partial update region of interest.
* Origin is top left of this CTRL.
* @tx_cmd_buf: Tx command buffer.
* @cmd_buffer_iova: cmd buffer mapped address.
* @cmd_buffer_size: Size of command buffer.
* @vaddr: CPU virtual address of cmd buffer.
* @secure_mode: Indicates if secure-session is in progress
* @esd_check_underway: Indicates if esd status check is in progress
* @dma_cmd_wait: Work object waiting on DMA command transfer done.
* @dma_cmd_workq: Pointer to the workqueue of DMA command transfer done
* wait sequence.
* @dma_wait_queued: Indicates if any DMA command transfer wait work
* is queued.
* @dma_irq_trig: Atomic state to indicate DMA done IRQ
* triggered.
* @debugfs_root: Root for debugfs entries.
* @misr_enable: Frame MISR enable/disable
* @misr_cache: Cached Frame MISR value
* @frame_threshold_time_us: Frame threshold time in microseconds, where
* dsi data lane will be idle i.e from pingpong done to
* next TE for command mode.
* @phy_isolation_enabled: A boolean property allows to isolate the phy from
* dsi controller and run only dsi controller.
* @null_insertion_enabled: A boolean property to allow dsi controller to
* insert null packet.
* @modeupdated: Boolean to send new roi if mode is updated.
* @split_link_supported: Boolean to check if hw supports split link.
* @enable_cmd_dma_stats: Boolean to indicate the verbose logging during
* CMD transfer.
* count.
* @cmd_mode: Boolean to indicate if panel is running in
* command mode.
* @cmd_trigger_line: unsigned integer that indicates the line at
* which command gets triggered.
* @cmd_trigger_frame: unsigned integer that indicates the frame at
* which command gets triggered.
* @cmd_success_line: unsigned integer that indicates the line at
* which command transfer is successful.
* @cmd_success_frame: unsigned integer that indicates the frame at
* which command transfer is successful.
*/
struct dsi_ctrl {
struct platform_device *pdev;
u32 cell_index;
u32 horiz_index;
const char *name;
u32 refcount;
struct mutex ctrl_lock;
struct drm_device *drm_dev;
enum dsi_ctrl_version version;
struct dsi_ctrl_hw hw;
/* Current state */
struct dsi_ctrl_state_info current_state;
struct clk_ctrl_cb clk_cb;
struct dsi_ctrl_interrupts irq_info;
struct dsi_event_cb_info recovery_cb;
struct dsi_event_cb_info panel_id_cb;
/* Clock and power states */
struct dsi_ctrl_clk_info clk_info;
struct link_clk_freq clk_freq;
struct dsi_ctrl_power_info pwr_info;
struct dsi_host_config host_config;
struct dsi_rect mode_bounds;
struct dsi_rect roi;
/* Command tx and rx */
struct drm_gem_object *tx_cmd_buf;
u32 cmd_buffer_size;
u32 cmd_buffer_iova;
u32 cmd_len;
void *vaddr;
bool secure_mode;
bool esd_check_underway;
struct work_struct dma_cmd_wait;
struct workqueue_struct *dma_cmd_workq;
bool dma_wait_queued;
atomic_t dma_irq_trig;
/* Debug Information */
struct dentry *debugfs_root;
/* MISR */
bool misr_enable;
u32 misr_cache;
u32 frame_threshold_time_us;
/* Check for spurious interrupts */
unsigned long jiffies_start;
unsigned int error_interrupt_count;
bool phy_isolation_enabled;
bool null_insertion_enabled;
bool modeupdated;
bool split_link_supported;
bool enable_cmd_dma_stats;
bool cmd_mode;
u32 cmd_trigger_line;
u32 cmd_trigger_frame;
u32 cmd_success_line;
u32 cmd_success_frame;
};
/**
* dsi_ctrl_get() - get a dsi_ctrl handle from an of_node
* @of_node: of_node of the DSI controller.
*
* Gets the DSI controller handle for the corresponding of_node. The ref count
* is incremented to one and all subsequent gets will fail until the original
* clients calls a put.
*
* Return: DSI Controller handle.
*/
struct dsi_ctrl *dsi_ctrl_get(struct device_node *of_node);
/**
* dsi_ctrl_put() - releases a dsi controller handle.
* @dsi_ctrl: DSI controller handle.
*
* Releases the DSI controller. Driver will clean up all resources and puts back
* the DSI controller into reset state.
*/
void dsi_ctrl_put(struct dsi_ctrl *dsi_ctrl);
/**
* dsi_ctrl_drv_init() - initialize dsi controller driver.
* @dsi_ctrl: DSI controller handle.
* @parent: Parent directory for debug fs.
*
* Initializes DSI controller driver. Driver should be initialized after
* dsi_ctrl_get() succeeds.
*
* Return: error code.
*/
int dsi_ctrl_drv_init(struct dsi_ctrl *dsi_ctrl, struct dentry *parent);
/**
* dsi_ctrl_drv_deinit() - de-initializes dsi controller driver
* @dsi_ctrl: DSI controller handle.
*
* Releases all resources acquired by dsi_ctrl_drv_init().
*
* Return: error code.
*/
int dsi_ctrl_drv_deinit(struct dsi_ctrl *dsi_ctrl);
/**
* dsi_ctrl_validate_timing() - validate a video timing configuration
* @dsi_ctrl: DSI controller handle.
* @timing: Pointer to timing data.
*
* Driver will validate if the timing configuration is supported on the
* controller hardware.
*
* Return: error code if timing is not supported.
*/
int dsi_ctrl_validate_timing(struct dsi_ctrl *dsi_ctrl,
struct dsi_mode_info *timing);
/**
* dsi_ctrl_update_host_config() - update dsi host configuration
* @dsi_ctrl: DSI controller handle.
* @config: DSI host configuration.
* @mode: DSI host mode selected.
* @flags: dsi_mode_flags modifying the behavior
* @clk_handle: Clock handle for DSI clocks
*
* Updates driver with new Host configuration to use for host initialization.
* This function call will only update the software context. The stored
* configuration information will be used when the host is initialized.
*
* Return: error code.
*/
int dsi_ctrl_update_host_config(struct dsi_ctrl *dsi_ctrl,
struct dsi_host_config *config,
struct dsi_display_mode *mode, int flags,
void *clk_handle);
/**
* dsi_ctrl_timing_db_update() - update only controller Timing DB
* @dsi_ctrl: DSI controller handle.
* @enable: Enable/disable Timing DB register
*
* Update timing db register value during dfps usecases
*
* Return: error code.
*/
int dsi_ctrl_timing_db_update(struct dsi_ctrl *dsi_ctrl,
bool enable);
/**
* dsi_ctrl_async_timing_update() - update only controller timing
* @dsi_ctrl: DSI controller handle.
* @timing: New DSI timing info
*
* Updates host timing values to asynchronously transition to new timing
* For example, to update the porch values in a seamless/dynamic fps switch.
*
* Return: error code.
*/
int dsi_ctrl_async_timing_update(struct dsi_ctrl *dsi_ctrl,
struct dsi_mode_info *timing);
/**
* dsi_ctrl_phy_sw_reset() - perform a PHY software reset
* @dsi_ctrl: DSI controller handle.
*
* Performs a PHY software reset on the DSI controller. Reset should be done
* when the controller power state is DSI_CTRL_POWER_CORE_CLK_ON and the PHY is
* not enabled.
*
* This function will fail if driver is in any other state.
*
* Return: error code.
*/
int dsi_ctrl_phy_sw_reset(struct dsi_ctrl *dsi_ctrl);
/**
* dsi_ctrl_phy_reset_config() - Mask/unmask propagation of ahb reset signal
* to DSI PHY hardware.
* @dsi_ctrl: DSI controller handle.
* @enable: Mask/unmask the PHY reset signal.
*
* Return: error code.
*/
int dsi_ctrl_phy_reset_config(struct dsi_ctrl *dsi_ctrl, bool enable);
/**
* dsi_ctrl_config_clk_gating() - Enable/Disable DSI PHY clk gating
* @dsi_ctrl: DSI controller handle.
* @enable: Enable/disable DSI PHY clk gating
* @clk_selection: clock selection for gating
*
* Return: error code.
*/
int dsi_ctrl_config_clk_gating(struct dsi_ctrl *dsi_ctrl, bool enable,
enum dsi_clk_gate_type clk_selection);
/**
* dsi_ctrl_soft_reset() - perform a soft reset on DSI controller
* @dsi_ctrl: DSI controller handle.
*
* The video, command and controller engines will be disabled before the
* reset is triggered. After, the engines will be re-enabled to the same state
* as before the reset.
*
* If the reset is done while MDP timing engine is turned on, the video
* engine should be re-enabled only during the vertical blanking time.
*
* Return: error code
*/
int dsi_ctrl_soft_reset(struct dsi_ctrl *dsi_ctrl);
/**
* dsi_ctrl_host_timing_update - reinitialize host with new timing values
* @dsi_ctrl: DSI controller handle.
*
* Reinitialize DSI controller hardware with new display timing values
* when resolution is switched dynamically.
*
* Return: error code
*/
int dsi_ctrl_host_timing_update(struct dsi_ctrl *dsi_ctrl);
/**
* dsi_ctrl_host_init() - Initialize DSI host hardware.
* @dsi_ctrl: DSI controller handle.
* @skip_op: Boolean to indicate few operations can be skipped.
* Set during the cont-splash or trusted-vm enable case.
*
* Initializes DSI controller hardware with host configuration provided by
* dsi_ctrl_update_host_config(). Initialization can be performed only during
* DSI_CTRL_POWER_CORE_CLK_ON state and after the PHY SW reset has been
* performed.
*
* Return: error code.
*/
int dsi_ctrl_host_init(struct dsi_ctrl *dsi_ctrl, bool skip_op);
/**
* dsi_ctrl_host_deinit() - De-Initialize DSI host hardware.
* @dsi_ctrl: DSI controller handle.
*
* De-initializes DSI controller hardware. It can be performed only during
* DSI_CTRL_POWER_CORE_CLK_ON state after LINK clocks have been turned off.
*
* Return: error code.
*/
int dsi_ctrl_host_deinit(struct dsi_ctrl *dsi_ctrl);
/**
* dsi_ctrl_set_ulps() - set ULPS state for DSI lanes.
* @dsi_ctrl: DSI controller handle.
* @enable: enable/disable ULPS.
*
* ULPS can be enabled/disabled after DSI host engine is turned on.
*
* Return: error code.
*/
int dsi_ctrl_set_ulps(struct dsi_ctrl *dsi_ctrl, bool enable);
/**
* dsi_ctrl_timing_setup() - Setup DSI host config
* @dsi_ctrl: DSI controller handle.
*
* Initializes DSI controller hardware with host configuration provided by
* dsi_ctrl_update_host_config(). This is called while setting up DSI host
* through dsi_ctrl_setup() and after any ROI change.
*
* Also used to program the video mode timing values.
*
* Return: error code.
*/
int dsi_ctrl_timing_setup(struct dsi_ctrl *dsi_ctrl);
/**
* dsi_ctrl_setup() - Setup DSI host hardware while coming out of idle screen.
* @dsi_ctrl: DSI controller handle.
*
* Initialization of DSI controller hardware with host configuration and
* enabling required interrupts. Initialization can be performed only during
* DSI_CTRL_POWER_CORE_CLK_ON state and after the PHY SW reset has been
* performed.
*
* Return: error code.
*/
int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl);
/**
* dsi_ctrl_set_roi() - Set DSI controller's region of interest
* @dsi_ctrl: DSI controller handle.
* @roi: Region of interest rectangle, must be less than mode bounds
* @changed: Output parameter, set to true of the controller's ROI was
* dirtied by setting the new ROI, and DCS cmd update needed
*
* Return: error code.
*/
int dsi_ctrl_set_roi(struct dsi_ctrl *dsi_ctrl, struct dsi_rect *roi,
bool *changed);
/**
* dsi_ctrl_set_tpg_state() - enable/disable test pattern on the controller
* @dsi_ctrl: DSI controller handle.
* @on: enable/disable test pattern.
*
* Test pattern can be enabled only after Video engine (for video mode panels)
* or command engine (for cmd mode panels) is enabled.
*
* Return: error code.
*/
int dsi_ctrl_set_tpg_state(struct dsi_ctrl *dsi_ctrl, bool on);
/**
* dsi_ctrl_cmd_transfer() - Transfer commands on DSI link
* @dsi_ctrl: DSI controller handle.
* @msg: Message to transfer on DSI link.
* @flags: Modifiers for message transfer.
*
* Command transfer can be done only when command engine is enabled. The
* transfer API will until either the command transfer finishes or the timeout
* value is reached. If the trigger is deferred, it will return without
* triggering the transfer. Command parameters are programmed to hardware.
*
* Return: error code.
*/
int dsi_ctrl_cmd_transfer(struct dsi_ctrl *dsi_ctrl,
const struct mipi_dsi_msg *msg,
u32 *flags);
/**
* dsi_ctrl_cmd_tx_trigger() - Trigger a deferred command.
* @dsi_ctrl: DSI controller handle.
* @flags: Modifiers.
*
* Return: error code.
*/
int dsi_ctrl_cmd_tx_trigger(struct dsi_ctrl *dsi_ctrl, u32 flags);
/**
* dsi_ctrl_set_power_state() - set power state for dsi controller
* @dsi_ctrl: DSI controller handle.
* @state: Power state.
*
* Set power state for DSI controller. Power state can be changed only when
* Controller, Video and Command engines are turned off.
*
* Return: error code.
*/
int dsi_ctrl_set_power_state(struct dsi_ctrl *dsi_ctrl,
enum dsi_power_state state);
/**
* dsi_ctrl_set_cmd_engine_state() - set command engine state
* @dsi_ctrl: DSI Controller handle.
* @state: Engine state.
* @skip_op: Boolean to indicate few operations can be skipped.
* Set during the cont-splash or trusted-vm enable case.
*
* Command engine state can be modified only when DSI controller power state is
* set to DSI_CTRL_POWER_LINK_CLK_ON.
*
* Return: error code.
*/
int dsi_ctrl_set_cmd_engine_state(struct dsi_ctrl *dsi_ctrl,
enum dsi_engine_state state, bool skip_op);
/**
* dsi_ctrl_validate_host_state() - validate DSI ctrl host state
* @dsi_ctrl: DSI Controller handle.
*
* Validate DSI cotroller host state
*
* Return: boolean indicating whether host is not initialized.
*/
bool dsi_ctrl_validate_host_state(struct dsi_ctrl *dsi_ctrl);
/**
* dsi_ctrl_set_vid_engine_state() - set video engine state
* @dsi_ctrl: DSI Controller handle.
* @state: Engine state.
* @skip_op: Boolean to indicate few operations can be skipped.
* Set during the cont-splash or trusted-vm enable case.
*
* Video engine state can be modified only when DSI controller power state is
* set to DSI_CTRL_POWER_LINK_CLK_ON.
*
* Return: error code.
*/
int dsi_ctrl_set_vid_engine_state(struct dsi_ctrl *dsi_ctrl,
enum dsi_engine_state state, bool skip_op);
/**
* dsi_ctrl_set_host_engine_state() - set host engine state
* @dsi_ctrl: DSI Controller handle.
* @state: Engine state.
* @skip_op: Boolean to indicate few operations can be skipped.
* Set during the cont-splash or trusted-vm enable case.
*
* Host engine state can be modified only when DSI controller power state is
* set to DSI_CTRL_POWER_LINK_CLK_ON and cmd, video engines are disabled.
*
* Return: error code.
*/
int dsi_ctrl_set_host_engine_state(struct dsi_ctrl *dsi_ctrl,
enum dsi_engine_state state, bool skip_op);
/**
* dsi_ctrl_set_ulps() - set ULPS state for DSI lanes.
* @dsi_ctrl: DSI controller handle.
* @enable: enable/disable ULPS.
*
* ULPS can be enabled/disabled after DSI host engine is turned on.
*
* Return: error code.
*/
int dsi_ctrl_set_ulps(struct dsi_ctrl *dsi_ctrl, bool enable);
/**
* dsi_ctrl_clk_cb_register() - Register DSI controller clk control callback
* @dsi_ctrl: DSI controller handle.
* @clk__cb: Structure containing callback for clock control.
*
* Register call for DSI clock control
*
* Return: error code.
*/
int dsi_ctrl_clk_cb_register(struct dsi_ctrl *dsi_ctrl,
struct clk_ctrl_cb *clk_cb);
/**
* dsi_ctrl_set_clamp_state() - set clamp state for DSI phy
* @dsi_ctrl: DSI controller handle.
* @enable: enable/disable clamping.
* @ulps_enabled: ulps state.
*
* Clamps can be enabled/disabled while DSI controller is still turned on.
*
* Return: error code.
*/
int dsi_ctrl_set_clamp_state(struct dsi_ctrl *dsi_Ctrl,
bool enable, bool ulps_enabled);
/**
* dsi_ctrl_set_clock_source() - set clock source fpr dsi link clocks
* @dsi_ctrl: DSI controller handle.
* @source_clks: Source clocks for DSI link clocks.
*
* Clock source should be changed while link clocks are disabled.
*
* Return: error code.
*/
int dsi_ctrl_set_clock_source(struct dsi_ctrl *dsi_ctrl,
struct dsi_clk_link_set *source_clks);
/**
* dsi_ctrl_enable_status_interrupt() - enable status interrupts
* @dsi_ctrl: DSI controller handle.
* @intr_idx: Index interrupt to disable.
* @event_info: Pointer to event callback definition
*/
void dsi_ctrl_enable_status_interrupt(struct dsi_ctrl *dsi_ctrl,
uint32_t intr_idx, struct dsi_event_cb_info *event_info);
/**
* dsi_ctrl_disable_status_interrupt() - disable status interrupts
* @dsi_ctrl: DSI controller handle.
* @intr_idx: Index interrupt to disable.
*/
void dsi_ctrl_disable_status_interrupt(
struct dsi_ctrl *dsi_ctrl, uint32_t intr_idx);
/**
* dsi_ctrl_setup_misr() - Setup frame MISR
* @dsi_ctrl: DSI controller handle.
* @enable: enable/disable MISR.
* @frame_count: Number of frames to accumulate MISR.
*
* Return: error code.
*/
int dsi_ctrl_setup_misr(struct dsi_ctrl *dsi_ctrl,
bool enable,
u32 frame_count);
/**
* dsi_ctrl_collect_misr() - Read frame MISR
* @dsi_ctrl: DSI controller handle.
*
* Return: MISR value.
*/
u32 dsi_ctrl_collect_misr(struct dsi_ctrl *dsi_ctrl);
/**
* dsi_ctrl_cache_misr - Cache frame MISR value
* @dsi_ctrl: DSI controller handle.
*/
void dsi_ctrl_cache_misr(struct dsi_ctrl *dsi_ctrl);
/**
* dsi_ctrl_drv_register() - register platform driver for dsi controller
*/
void dsi_ctrl_drv_register(void);
/**
* dsi_ctrl_drv_unregister() - unregister platform driver
*/
void dsi_ctrl_drv_unregister(void);
/**
* dsi_ctrl_reset() - Reset DSI PHY CLK/DATA lane
* @dsi_ctrl: DSI controller handle.
* @mask: Mask to indicate if CLK and/or DATA lane needs reset.
*/
int dsi_ctrl_reset(struct dsi_ctrl *dsi_ctrl, int mask);
/**
* dsi_ctrl_get_hw_version() - read dsi controller hw revision
* @dsi_ctrl: DSI controller handle.
*/
int dsi_ctrl_get_hw_version(struct dsi_ctrl *dsi_ctrl);
/**
* dsi_ctrl_vid_engine_en() - Control DSI video engine HW state
* @dsi_ctrl: DSI controller handle.
* @on: variable to control video engine ON/OFF.
*/
int dsi_ctrl_vid_engine_en(struct dsi_ctrl *dsi_ctrl, bool on);
/**
* dsi_ctrl_setup_avr() - Set/Clear the AVR_SUPPORT_ENABLE bit
* @dsi_ctrl: DSI controller handle.
* @enable: variable to control AVR support ON/OFF.
*/
int dsi_ctrl_setup_avr(struct dsi_ctrl *dsi_ctrl, bool enable);
/**
* @dsi_ctrl: DSI controller handle.
* cmd_len: Length of command.
* flags: Config mode flags.
*/
void dsi_message_setup_tx_mode(struct dsi_ctrl *dsi_ctrl, u32 cmd_len,
u32 *flags);
/**
* @dsi_ctrl: DSI controller handle.
* cmd_len: Length of command.
* flags: Config mode flags.
*/
int dsi_message_validate_tx_mode(struct dsi_ctrl *dsi_ctrl, u32 cmd_len,
u32 *flags);
/**
* dsi_ctrl_isr_configure() - API to register/deregister dsi isr
* @dsi_ctrl: DSI controller handle.
* @enable: variable to control register/deregister isr
*/
void dsi_ctrl_isr_configure(struct dsi_ctrl *dsi_ctrl, bool enable);
/**
* dsi_ctrl_mask_error_status_interrupts() - API to mask dsi ctrl error status
* interrupts
* @dsi_ctrl: DSI controller handle.
* @idx: id indicating which interrupts to enable/disable.
* @mask_enable: boolean to enable/disable masking.
*/
void dsi_ctrl_mask_error_status_interrupts(struct dsi_ctrl *dsi_ctrl, u32 idx,
bool mask_enable);
/**
* dsi_ctrl_irq_update() - Put a irq vote to process DSI error
* interrupts at any time.
* @dsi_ctrl: DSI controller handle.
* @enable: variable to control enable/disable irq line
*/
void dsi_ctrl_irq_update(struct dsi_ctrl *dsi_ctrl, bool enable);
/**
* dsi_ctrl_get_host_engine_init_state() - Return host init state
*/
int dsi_ctrl_get_host_engine_init_state(struct dsi_ctrl *dsi_ctrl,
bool *state);
/**
* dsi_ctrl_wait_for_cmd_mode_mdp_idle() - Wait for command mode engine not to
* be busy sending data from display engine.
* @dsi_ctrl: DSI controller handle.
*/
int dsi_ctrl_wait_for_cmd_mode_mdp_idle(struct dsi_ctrl *dsi_ctrl);
/**
* dsi_ctrl_update_host_state() - Set the host state
*/
int dsi_ctrl_update_host_state(struct dsi_ctrl *dsi_ctrl,
enum dsi_ctrl_driver_ops op, bool en);
/**
* dsi_ctrl_pixel_format_to_bpp() - returns number of bits per pxl
*/
int dsi_ctrl_pixel_format_to_bpp(enum dsi_pixel_format dst_format);
/**
* dsi_ctrl_hs_req_sel() - API to enable continuous clk support through phy
* @dsi_ctrl: DSI controller handle.
* @sel_phy: Boolean to control whether to select phy or
* controller
*/
void dsi_ctrl_hs_req_sel(struct dsi_ctrl *dsi_ctrl, bool sel_phy);
/**
* dsi_ctrl_set_continuous_clk() - API to set/unset force clock lane HS request.
* @dsi_ctrl: DSI controller handle.
* @enable: variable to control continuous clock.
*/
void dsi_ctrl_set_continuous_clk(struct dsi_ctrl *dsi_ctrl, bool enable);
/**
* dsi_ctrl_wait4dynamic_refresh_done() - Poll for dynamic refresh done
* interrupt.
* @dsi_ctrl: DSI controller handle.
*/
int dsi_ctrl_wait4dynamic_refresh_done(struct dsi_ctrl *ctrl);
/**
* dsi_ctrl_get_io_resources() - reads associated register range
*
* @io_res: pointer to msm_io_res struct to populate the ranges
*
* Return: error code.
*/
int dsi_ctrl_get_io_resources(struct msm_io_res *io_res);
/**
* dsi_ctrl_mask_overflow() - API to mask/unmask overflow errors.
* @dsi_ctrl: DSI controller handle.
* @enable: variable to control masking/unmasking.
*/
void dsi_ctrl_mask_overflow(struct dsi_ctrl *dsi_ctrl, bool enable);
#endif /* _DSI_CTRL_H_ */

View File

@ -0,0 +1,929 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DSI_CTRL_HW_H_
#define _DSI_CTRL_HW_H_
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/bitmap.h>
#include "dsi_defs.h"
#define DSI_CTRL_HW_DBG(c, fmt, ...) DRM_DEV_DEBUG(NULL, "[msm-dsi-debug]: DSI_%d: "\
fmt, c ? c->index : -1, ##__VA_ARGS__)
#define DSI_CTRL_HW_ERR(c, fmt, ...) DRM_DEV_ERROR(NULL, "[msm-dsi-error]: DSI_%d: "\
fmt, c ? c->index : -1, ##__VA_ARGS__)
#define DSI_CTRL_HW_INFO(c, fmt, ...) DRM_DEV_INFO(NULL, "[msm-dsi-info]: DSI_%d: "\
fmt, c ? c->index : -1, ##__VA_ARGS__)
/**
* Modifier flag for command transmission. If this flag is set, command
* information is programmed to hardware and transmission is not triggered.
* Caller should call the trigger_command_dma() to start the transmission. This
* flag is valed for kickoff_command() and kickoff_fifo_command() operations.
*/
#define DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER 0x1
/**
* enum dsi_ctrl_version - version of the dsi host controller
* @DSI_CTRL_VERSION_UNKNOWN: Unknown controller version
* @DSI_CTRL_VERSION_1_3: DSI host v1.3 controller
* @DSI_CTRL_VERSION_1_4: DSI host v1.4 controller
* @DSI_CTRL_VERSION_2_0: DSI host v2.0 controller
* @DSI_CTRL_VERSION_2_2: DSI host v2.2 controller
* @DSI_CTRL_VERSION_2_3: DSI host v2.3 controller
* @DSI_CTRL_VERSION_2_4: DSI host v2.4 controller
* @DSI_CTRL_VERSION_2_5: DSI host v2.5 controller
* @DSI_CTRL_VERSION_MAX: max version
*/
enum dsi_ctrl_version {
DSI_CTRL_VERSION_UNKNOWN,
DSI_CTRL_VERSION_1_3,
DSI_CTRL_VERSION_1_4,
DSI_CTRL_VERSION_2_0,
DSI_CTRL_VERSION_2_2,
DSI_CTRL_VERSION_2_3,
DSI_CTRL_VERSION_2_4,
DSI_CTRL_VERSION_2_5,
DSI_CTRL_VERSION_MAX
};
/**
* enum dsi_ctrl_hw_features - features supported by dsi host controller
* @DSI_CTRL_VIDEO_TPG: Test pattern support for video mode.
* @DSI_CTRL_CMD_TPG: Test pattern support for command mode.
* @DSI_CTRL_VARIABLE_REFRESH_RATE: variable panel timing
* @DSI_CTRL_DYNAMIC_REFRESH: variable pixel clock rate
* @DSI_CTRL_NULL_PACKET_INSERTION: NULL packet insertion
* @DSI_CTRL_DESKEW_CALIB: Deskew calibration support
* @DSI_CTRL_DPHY: Controller support for DPHY
* @DSI_CTRL_CPHY: Controller support for CPHY
* @DSI_CTRL_MAX_FEATURES:
*/
enum dsi_ctrl_hw_features {
DSI_CTRL_VIDEO_TPG,
DSI_CTRL_CMD_TPG,
DSI_CTRL_VARIABLE_REFRESH_RATE,
DSI_CTRL_DYNAMIC_REFRESH,
DSI_CTRL_NULL_PACKET_INSERTION,
DSI_CTRL_DESKEW_CALIB,
DSI_CTRL_DPHY,
DSI_CTRL_CPHY,
DSI_CTRL_MAX_FEATURES
};
/**
* enum dsi_test_pattern - test pattern type
* @DSI_TEST_PATTERN_FIXED: Test pattern is fixed, based on init value.
* @DSI_TEST_PATTERN_INC: Incremental test pattern, base on init value.
* @DSI_TEST_PATTERN_POLY: Pattern generated from polynomial and init val.
* @DSI_TEST_PATTERN_MAX:
*/
enum dsi_test_pattern {
DSI_TEST_PATTERN_FIXED = 0,
DSI_TEST_PATTERN_INC,
DSI_TEST_PATTERN_POLY,
DSI_TEST_PATTERN_MAX
};
/**
* enum dsi_status_int_index - index of interrupts generated by DSI controller
* @DSI_SINT_CMD_MODE_DMA_DONE: Command mode DMA packets are sent out.
* @DSI_SINT_CMD_STREAM0_FRAME_DONE: A frame of cmd mode stream0 is sent out.
* @DSI_SINT_CMD_STREAM1_FRAME_DONE: A frame of cmd mode stream1 is sent out.
* @DSI_SINT_CMD_STREAM2_FRAME_DONE: A frame of cmd mode stream2 is sent out.
* @DSI_SINT_VIDEO_MODE_FRAME_DONE: A frame of video mode stream is sent out.
* @DSI_SINT_BTA_DONE: A BTA is completed.
* @DSI_SINT_CMD_FRAME_DONE: A frame of selected cmd mode stream is
* sent out by MDP.
* @DSI_SINT_DYN_REFRESH_DONE: The dynamic refresh operation completed.
* @DSI_SINT_DESKEW_DONE: The deskew calibration operation done.
* @DSI_SINT_DYN_BLANK_DMA_DONE: The dynamic blankin DMA operation has
* completed.
* @DSI_SINT_ERROR: DSI error has happened.
*/
enum dsi_status_int_index {
DSI_SINT_CMD_MODE_DMA_DONE = 0,
DSI_SINT_CMD_STREAM0_FRAME_DONE = 1,
DSI_SINT_CMD_STREAM1_FRAME_DONE = 2,
DSI_SINT_CMD_STREAM2_FRAME_DONE = 3,
DSI_SINT_VIDEO_MODE_FRAME_DONE = 4,
DSI_SINT_BTA_DONE = 5,
DSI_SINT_CMD_FRAME_DONE = 6,
DSI_SINT_DYN_REFRESH_DONE = 7,
DSI_SINT_DESKEW_DONE = 8,
DSI_SINT_DYN_BLANK_DMA_DONE = 9,
DSI_SINT_ERROR = 10,
DSI_STATUS_INTERRUPT_COUNT
};
/**
* enum dsi_status_int_type - status interrupts generated by DSI controller
* @DSI_CMD_MODE_DMA_DONE: Command mode DMA packets are sent out.
* @DSI_CMD_STREAM0_FRAME_DONE: A frame of command mode stream0 is sent out.
* @DSI_CMD_STREAM1_FRAME_DONE: A frame of command mode stream1 is sent out.
* @DSI_CMD_STREAM2_FRAME_DONE: A frame of command mode stream2 is sent out.
* @DSI_VIDEO_MODE_FRAME_DONE: A frame of video mode stream is sent out.
* @DSI_BTA_DONE: A BTA is completed.
* @DSI_CMD_FRAME_DONE: A frame of selected command mode stream is
* sent out by MDP.
* @DSI_DYN_REFRESH_DONE: The dynamic refresh operation has completed.
* @DSI_DESKEW_DONE: The deskew calibration operation has completed
* @DSI_DYN_BLANK_DMA_DONE: The dynamic blankin DMA operation has
* completed.
* @DSI_ERROR: DSI error has happened.
*/
enum dsi_status_int_type {
DSI_CMD_MODE_DMA_DONE = BIT(DSI_SINT_CMD_MODE_DMA_DONE),
DSI_CMD_STREAM0_FRAME_DONE = BIT(DSI_SINT_CMD_STREAM0_FRAME_DONE),
DSI_CMD_STREAM1_FRAME_DONE = BIT(DSI_SINT_CMD_STREAM1_FRAME_DONE),
DSI_CMD_STREAM2_FRAME_DONE = BIT(DSI_SINT_CMD_STREAM2_FRAME_DONE),
DSI_VIDEO_MODE_FRAME_DONE = BIT(DSI_SINT_VIDEO_MODE_FRAME_DONE),
DSI_BTA_DONE = BIT(DSI_SINT_BTA_DONE),
DSI_CMD_FRAME_DONE = BIT(DSI_SINT_CMD_FRAME_DONE),
DSI_DYN_REFRESH_DONE = BIT(DSI_SINT_DYN_REFRESH_DONE),
DSI_DESKEW_DONE = BIT(DSI_SINT_DESKEW_DONE),
DSI_DYN_BLANK_DMA_DONE = BIT(DSI_SINT_DYN_BLANK_DMA_DONE),
DSI_ERROR = BIT(DSI_SINT_ERROR)
};
/**
* enum dsi_error_int_index - index of error interrupts from DSI controller
* @DSI_EINT_RDBK_SINGLE_ECC_ERR: Single bit ECC error in read packet.
* @DSI_EINT_RDBK_MULTI_ECC_ERR: Multi bit ECC error in read packet.
* @DSI_EINT_RDBK_CRC_ERR: CRC error in read packet.
* @DSI_EINT_RDBK_INCOMPLETE_PKT: Incomplete read packet.
* @DSI_EINT_PERIPH_ERROR_PKT: Error packet returned from peripheral,
* @DSI_EINT_LP_RX_TIMEOUT: Low power reverse transmission timeout.
* @DSI_EINT_HS_TX_TIMEOUT: High speed fwd transmission timeout.
* @DSI_EINT_BTA_TIMEOUT: BTA timeout.
* @DSI_EINT_PLL_UNLOCK: PLL has unlocked.
* @DSI_EINT_DLN0_ESC_ENTRY_ERR: Incorrect LP Rx escape entry.
* @DSI_EINT_DLN0_ESC_SYNC_ERR: LP Rx data is not byte aligned.
* @DSI_EINT_DLN0_LP_CONTROL_ERR: Incorrect LP Rx state sequence.
* @DSI_EINT_PANEL_SPECIFIC_ERR: DSI Protocol violation error.
* @DSI_EINT_INTERLEAVE_OP_CONTENTION: Interleave operation contention.
* @DSI_EINT_CMD_DMA_FIFO_UNDERFLOW: Command mode DMA FIFO underflow.
* @DSI_EINT_CMD_MDP_FIFO_UNDERFLOW: Command MDP FIFO underflow (failed to
* receive one complete line from MDP).
* @DSI_EINT_DLN0_HS_FIFO_OVERFLOW: High speed FIFO data lane 0 overflows.
* @DSI_EINT_DLN1_HS_FIFO_OVERFLOW: High speed FIFO data lane 1 overflows.
* @DSI_EINT_DLN2_HS_FIFO_OVERFLOW: High speed FIFO data lane 2 overflows.
* @DSI_EINT_DLN3_HS_FIFO_OVERFLOW: High speed FIFO data lane 3 overflows.
* @DSI_EINT_DLN0_HS_FIFO_UNDERFLOW: High speed FIFO data lane 0 underflows.
* @DSI_EINT_DLN1_HS_FIFO_UNDERFLOW: High speed FIFO data lane 1 underflows.
* @DSI_EINT_DLN2_HS_FIFO_UNDERFLOW: High speed FIFO data lane 2 underflows.
* @DSI_EINT_DLN3_HS_FIFO_UNDERFLOW: High speed FIFO data lane 3 undeflows.
* @DSI_EINT_DLN0_LP0_CONTENTION: PHY level contention while lane 0 low.
* @DSI_EINT_DLN1_LP0_CONTENTION: PHY level contention while lane 1 low.
* @DSI_EINT_DLN2_LP0_CONTENTION: PHY level contention while lane 2 low.
* @DSI_EINT_DLN3_LP0_CONTENTION: PHY level contention while lane 3 low.
* @DSI_EINT_DLN0_LP1_CONTENTION: PHY level contention while lane 0 high.
* @DSI_EINT_DLN1_LP1_CONTENTION: PHY level contention while lane 1 high.
* @DSI_EINT_DLN2_LP1_CONTENTION: PHY level contention while lane 2 high.
* @DSI_EINT_DLN3_LP1_CONTENTION: PHY level contention while lane 3 high.
*/
enum dsi_error_int_index {
DSI_EINT_RDBK_SINGLE_ECC_ERR = 0,
DSI_EINT_RDBK_MULTI_ECC_ERR = 1,
DSI_EINT_RDBK_CRC_ERR = 2,
DSI_EINT_RDBK_INCOMPLETE_PKT = 3,
DSI_EINT_PERIPH_ERROR_PKT = 4,
DSI_EINT_LP_RX_TIMEOUT = 5,
DSI_EINT_HS_TX_TIMEOUT = 6,
DSI_EINT_BTA_TIMEOUT = 7,
DSI_EINT_PLL_UNLOCK = 8,
DSI_EINT_DLN0_ESC_ENTRY_ERR = 9,
DSI_EINT_DLN0_ESC_SYNC_ERR = 10,
DSI_EINT_DLN0_LP_CONTROL_ERR = 11,
DSI_EINT_PANEL_SPECIFIC_ERR = 12,
DSI_EINT_INTERLEAVE_OP_CONTENTION = 13,
DSI_EINT_CMD_DMA_FIFO_UNDERFLOW = 14,
DSI_EINT_CMD_MDP_FIFO_UNDERFLOW = 15,
DSI_EINT_DLN0_HS_FIFO_OVERFLOW = 16,
DSI_EINT_DLN1_HS_FIFO_OVERFLOW = 17,
DSI_EINT_DLN2_HS_FIFO_OVERFLOW = 18,
DSI_EINT_DLN3_HS_FIFO_OVERFLOW = 19,
DSI_EINT_DLN0_HS_FIFO_UNDERFLOW = 20,
DSI_EINT_DLN1_HS_FIFO_UNDERFLOW = 21,
DSI_EINT_DLN2_HS_FIFO_UNDERFLOW = 22,
DSI_EINT_DLN3_HS_FIFO_UNDERFLOW = 23,
DSI_EINT_DLN0_LP0_CONTENTION = 24,
DSI_EINT_DLN1_LP0_CONTENTION = 25,
DSI_EINT_DLN2_LP0_CONTENTION = 26,
DSI_EINT_DLN3_LP0_CONTENTION = 27,
DSI_EINT_DLN0_LP1_CONTENTION = 28,
DSI_EINT_DLN1_LP1_CONTENTION = 29,
DSI_EINT_DLN2_LP1_CONTENTION = 30,
DSI_EINT_DLN3_LP1_CONTENTION = 31,
DSI_ERROR_INTERRUPT_COUNT
};
/**
* enum dsi_error_int_type - error interrupts generated by DSI controller
* @DSI_RDBK_SINGLE_ECC_ERR: Single bit ECC error in read packet.
* @DSI_RDBK_MULTI_ECC_ERR: Multi bit ECC error in read packet.
* @DSI_RDBK_CRC_ERR: CRC error in read packet.
* @DSI_RDBK_INCOMPLETE_PKT: Incomplete read packet.
* @DSI_PERIPH_ERROR_PKT: Error packet returned from peripheral,
* @DSI_LP_RX_TIMEOUT: Low power reverse transmission timeout.
* @DSI_HS_TX_TIMEOUT: High speed forward transmission timeout.
* @DSI_BTA_TIMEOUT: BTA timeout.
* @DSI_PLL_UNLOCK: PLL has unlocked.
* @DSI_DLN0_ESC_ENTRY_ERR: Incorrect LP Rx escape entry.
* @DSI_DLN0_ESC_SYNC_ERR: LP Rx data is not byte aligned.
* @DSI_DLN0_LP_CONTROL_ERR: Incorrect LP Rx state sequence.
* @DSI_PANEL_SPECIFIC_ERR: DSI Protocol violation.
* @DSI_INTERLEAVE_OP_CONTENTION: Interleave operation contention.
* @DSI_CMD_DMA_FIFO_UNDERFLOW: Command mode DMA FIFO underflow.
* @DSI_CMD_MDP_FIFO_UNDERFLOW: Command MDP FIFO underflow (failed to
* receive one complete line from MDP).
* @DSI_DLN0_HS_FIFO_OVERFLOW: High speed FIFO for data lane 0 overflows.
* @DSI_DLN1_HS_FIFO_OVERFLOW: High speed FIFO for data lane 1 overflows.
* @DSI_DLN2_HS_FIFO_OVERFLOW: High speed FIFO for data lane 2 overflows.
* @DSI_DLN3_HS_FIFO_OVERFLOW: High speed FIFO for data lane 3 overflows.
* @DSI_DLN0_HS_FIFO_UNDERFLOW: High speed FIFO for data lane 0 underflows.
* @DSI_DLN1_HS_FIFO_UNDERFLOW: High speed FIFO for data lane 1 underflows.
* @DSI_DLN2_HS_FIFO_UNDERFLOW: High speed FIFO for data lane 2 underflows.
* @DSI_DLN3_HS_FIFO_UNDERFLOW: High speed FIFO for data lane 3 undeflows.
* @DSI_DLN0_LP0_CONTENTION: PHY level contention while lane 0 is low.
* @DSI_DLN1_LP0_CONTENTION: PHY level contention while lane 1 is low.
* @DSI_DLN2_LP0_CONTENTION: PHY level contention while lane 2 is low.
* @DSI_DLN3_LP0_CONTENTION: PHY level contention while lane 3 is low.
* @DSI_DLN0_LP1_CONTENTION: PHY level contention while lane 0 is high.
* @DSI_DLN1_LP1_CONTENTION: PHY level contention while lane 1 is high.
* @DSI_DLN2_LP1_CONTENTION: PHY level contention while lane 2 is high.
* @DSI_DLN3_LP1_CONTENTION: PHY level contention while lane 3 is high.
*/
enum dsi_error_int_type {
DSI_RDBK_SINGLE_ECC_ERR = BIT(DSI_EINT_RDBK_SINGLE_ECC_ERR),
DSI_RDBK_MULTI_ECC_ERR = BIT(DSI_EINT_RDBK_MULTI_ECC_ERR),
DSI_RDBK_CRC_ERR = BIT(DSI_EINT_RDBK_CRC_ERR),
DSI_RDBK_INCOMPLETE_PKT = BIT(DSI_EINT_RDBK_INCOMPLETE_PKT),
DSI_PERIPH_ERROR_PKT = BIT(DSI_EINT_PERIPH_ERROR_PKT),
DSI_LP_RX_TIMEOUT = BIT(DSI_EINT_LP_RX_TIMEOUT),
DSI_HS_TX_TIMEOUT = BIT(DSI_EINT_HS_TX_TIMEOUT),
DSI_BTA_TIMEOUT = BIT(DSI_EINT_BTA_TIMEOUT),
DSI_PLL_UNLOCK = BIT(DSI_EINT_PLL_UNLOCK),
DSI_DLN0_ESC_ENTRY_ERR = BIT(DSI_EINT_DLN0_ESC_ENTRY_ERR),
DSI_DLN0_ESC_SYNC_ERR = BIT(DSI_EINT_DLN0_ESC_SYNC_ERR),
DSI_DLN0_LP_CONTROL_ERR = BIT(DSI_EINT_DLN0_LP_CONTROL_ERR),
DSI_PANEL_SPECIFIC_ERR = BIT(DSI_EINT_PANEL_SPECIFIC_ERR),
DSI_INTERLEAVE_OP_CONTENTION = BIT(DSI_EINT_INTERLEAVE_OP_CONTENTION),
DSI_CMD_DMA_FIFO_UNDERFLOW = BIT(DSI_EINT_CMD_DMA_FIFO_UNDERFLOW),
DSI_CMD_MDP_FIFO_UNDERFLOW = BIT(DSI_EINT_CMD_MDP_FIFO_UNDERFLOW),
DSI_DLN0_HS_FIFO_OVERFLOW = BIT(DSI_EINT_DLN0_HS_FIFO_OVERFLOW),
DSI_DLN1_HS_FIFO_OVERFLOW = BIT(DSI_EINT_DLN1_HS_FIFO_OVERFLOW),
DSI_DLN2_HS_FIFO_OVERFLOW = BIT(DSI_EINT_DLN2_HS_FIFO_OVERFLOW),
DSI_DLN3_HS_FIFO_OVERFLOW = BIT(DSI_EINT_DLN3_HS_FIFO_OVERFLOW),
DSI_DLN0_HS_FIFO_UNDERFLOW = BIT(DSI_EINT_DLN0_HS_FIFO_UNDERFLOW),
DSI_DLN1_HS_FIFO_UNDERFLOW = BIT(DSI_EINT_DLN1_HS_FIFO_UNDERFLOW),
DSI_DLN2_HS_FIFO_UNDERFLOW = BIT(DSI_EINT_DLN2_HS_FIFO_UNDERFLOW),
DSI_DLN3_HS_FIFO_UNDERFLOW = BIT(DSI_EINT_DLN3_HS_FIFO_UNDERFLOW),
DSI_DLN0_LP0_CONTENTION = BIT(DSI_EINT_DLN0_LP0_CONTENTION),
DSI_DLN1_LP0_CONTENTION = BIT(DSI_EINT_DLN1_LP0_CONTENTION),
DSI_DLN2_LP0_CONTENTION = BIT(DSI_EINT_DLN2_LP0_CONTENTION),
DSI_DLN3_LP0_CONTENTION = BIT(DSI_EINT_DLN3_LP0_CONTENTION),
DSI_DLN0_LP1_CONTENTION = BIT(DSI_EINT_DLN0_LP1_CONTENTION),
DSI_DLN1_LP1_CONTENTION = BIT(DSI_EINT_DLN1_LP1_CONTENTION),
DSI_DLN2_LP1_CONTENTION = BIT(DSI_EINT_DLN2_LP1_CONTENTION),
DSI_DLN3_LP1_CONTENTION = BIT(DSI_EINT_DLN3_LP1_CONTENTION),
};
/**
* struct dsi_ctrl_cmd_dma_info - command buffer information
* @offset: IOMMU VA for command buffer address.
* @length: Length of the command buffer.
* @datatype: Datatype of cmd.
* @en_broadcast: Enable broadcast mode if set to true.
* @is_master: Is master in broadcast mode.
* @use_lpm: Use low power mode for command transmission.
*/
struct dsi_ctrl_cmd_dma_info {
u32 offset;
u32 length;
u8 datatype;
bool en_broadcast;
bool is_master;
bool use_lpm;
};
/**
* struct dsi_ctrl_cmd_dma_fifo_info - command payload tp be sent using FIFO
* @command: VA for command buffer.
* @size: Size of the command buffer.
* @en_broadcast: Enable broadcast mode if set to true.
* @is_master: Is master in broadcast mode.
* @use_lpm: Use low power mode for command transmission.
*/
struct dsi_ctrl_cmd_dma_fifo_info {
u32 *command;
u32 size;
bool en_broadcast;
bool is_master;
bool use_lpm;
};
struct dsi_ctrl_hw;
struct ctrl_ulps_config_ops {
/**
* ulps_request() - request ulps entry for specified lanes
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes (enum dsi_data_lanes) which need
* to enter ULPS.
*
* Caller should check if lanes are in ULPS mode by calling
* get_lanes_in_ulps() operation.
*/
void (*ulps_request)(struct dsi_ctrl_hw *ctrl, u32 lanes);
/**
* ulps_exit() - exit ULPS on specified lanes
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes (enum dsi_data_lanes) which need
* to exit ULPS.
*
* Caller should check if lanes are in active mode by calling
* get_lanes_in_ulps() operation.
*/
void (*ulps_exit)(struct dsi_ctrl_hw *ctrl, u32 lanes);
/**
* get_lanes_in_ulps() - returns the list of lanes in ULPS mode
* @ctrl: Pointer to the controller host hardware.
*
* Returns an ORed list of lanes (enum dsi_data_lanes) that are in ULPS
* state. If 0 is returned, all the lanes are active.
*
* Return: List of lanes in ULPS state.
*/
u32 (*get_lanes_in_ulps)(struct dsi_ctrl_hw *ctrl);
};
/**
* struct dsi_ctrl_hw_ops - operations supported by dsi host hardware
*/
struct dsi_ctrl_hw_ops {
/**
* host_setup() - Setup DSI host configuration
* @ctrl: Pointer to controller host hardware.
* @config: Configuration for DSI host controller
*/
void (*host_setup)(struct dsi_ctrl_hw *ctrl,
struct dsi_host_common_cfg *config);
/**
* video_engine_en() - enable DSI video engine
* @ctrl: Pointer to controller host hardware.
* @on: Enable/disabel video engine.
*/
void (*video_engine_en)(struct dsi_ctrl_hw *ctrl, bool on);
/**
* setup_avr() - set the AVR_SUPPORT_ENABLE bit in DSI_VIDEO_MODE_CTRL
* @ctrl: Pointer to controller host hardware.
* @enable: Controls whether this bit is set or cleared
*/
void (*setup_avr)(struct dsi_ctrl_hw *ctrl, bool enable);
/**
* video_engine_setup() - Setup dsi host controller for video mode
* @ctrl: Pointer to controller host hardware.
* @common_cfg: Common configuration parameters.
* @cfg: Video mode configuration.
*
* Set up DSI video engine with a specific configuration. Controller and
* video engine are not enabled as part of this function.
*/
void (*video_engine_setup)(struct dsi_ctrl_hw *ctrl,
struct dsi_host_common_cfg *common_cfg,
struct dsi_video_engine_cfg *cfg);
/**
* set_video_timing() - set up the timing for video frame
* @ctrl: Pointer to controller host hardware.
* @mode: Video mode information.
*
* Set up the video timing parameters for the DSI video mode operation.
*/
void (*set_video_timing)(struct dsi_ctrl_hw *ctrl,
struct dsi_mode_info *mode);
/**
* cmd_engine_setup() - setup dsi host controller for command mode
* @ctrl: Pointer to the controller host hardware.
* @common_cfg: Common configuration parameters.
* @cfg: Command mode configuration.
*
* Setup DSI CMD engine with a specific configuration. Controller and
* command engine are not enabled as part of this function.
*/
void (*cmd_engine_setup)(struct dsi_ctrl_hw *ctrl,
struct dsi_host_common_cfg *common_cfg,
struct dsi_cmd_engine_cfg *cfg);
/**
* setup_cmd_stream() - set up parameters for command pixel streams
* @ctrl: Pointer to controller host hardware.
* @mode: Pointer to mode information.
* @cfg: DSI host configuration that is common to both
* video and command modes.
* @vc_id: stream_id.
*
* Setup parameters for command mode pixel stream size.
*/
void (*setup_cmd_stream)(struct dsi_ctrl_hw *ctrl,
struct dsi_mode_info *mode,
struct dsi_host_common_cfg *cfg,
u32 vc_id,
struct dsi_rect *roi);
/**
* ctrl_en() - enable DSI controller engine
* @ctrl: Pointer to the controller host hardware.
* @on: turn on/off the DSI controller engine.
*/
void (*ctrl_en)(struct dsi_ctrl_hw *ctrl, bool on);
/**
* cmd_engine_en() - enable DSI controller command engine
* @ctrl: Pointer to the controller host hardware.
* @on: Turn on/off the DSI command engine.
*/
void (*cmd_engine_en)(struct dsi_ctrl_hw *ctrl, bool on);
/**
* phy_sw_reset() - perform a soft reset on the PHY.
* @ctrl: Pointer to the controller host hardware.
*/
void (*phy_sw_reset)(struct dsi_ctrl_hw *ctrl);
/**
* config_clk_gating() - enable/disable DSI PHY clk gating
* @ctrl: Pointer to the controller host hardware.
* @enable: enable/disable DSI PHY clock gating.
* @clk_selection: clock to enable/disable clock gating.
*/
void (*config_clk_gating)(struct dsi_ctrl_hw *ctrl, bool enable,
enum dsi_clk_gate_type clk_selection);
/**
* soft_reset() - perform a soft reset on DSI controller
* @ctrl: Pointer to the controller host hardware.
*
* The video, command and controller engines will be disabled before the
* reset is triggered. After, the engines will be re-enabled to the same
* state as before the reset.
*
* If the reset is done while MDP timing engine is turned on, the video
* engine should be re-enabled only during the vertical blanking time.
*/
void (*soft_reset)(struct dsi_ctrl_hw *ctrl);
/**
* setup_lane_map() - setup mapping between logical and physical lanes
* @ctrl: Pointer to the controller host hardware.
* @lane_map: Structure defining the mapping between DSI logical
* lanes and physical lanes.
*/
void (*setup_lane_map)(struct dsi_ctrl_hw *ctrl,
struct dsi_lane_map *lane_map);
/**
* kickoff_command() - transmits commands stored in memory
* @ctrl: Pointer to the controller host hardware.
* @cmd: Command information.
* @flags: Modifiers for command transmission.
*
* The controller hardware is programmed with address and size of the
* command buffer. The transmission is kicked off if
* DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER flag is not set. If this flag is
* set, caller should make a separate call to trigger_command_dma() to
* transmit the command.
*/
void (*kickoff_command)(struct dsi_ctrl_hw *ctrl,
struct dsi_ctrl_cmd_dma_info *cmd,
u32 flags);
/**
* kickoff_command_non_embedded_mode() - cmd in non embedded mode
* @ctrl: Pointer to the controller host hardware.
* @cmd: Command information.
* @flags: Modifiers for command transmission.
*
* If command length is greater than DMA FIFO size of 256 bytes we use
* this non- embedded mode.
* The controller hardware is programmed with address and size of the
* command buffer. The transmission is kicked off if
* DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER flag is not set. If this flag is
* set, caller should make a separate call to trigger_command_dma() to
* transmit the command.
*/
void (*kickoff_command_non_embedded_mode)(struct dsi_ctrl_hw *ctrl,
struct dsi_ctrl_cmd_dma_info *cmd,
u32 flags);
/**
* kickoff_fifo_command() - transmits a command using FIFO in dsi
* hardware.
* @ctrl: Pointer to the controller host hardware.
* @cmd: Command information.
* @flags: Modifiers for command transmission.
*
* The controller hardware FIFO is programmed with command header and
* payload. The transmission is kicked off if
* DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER flag is not set. If this flag is
* set, caller should make a separate call to trigger_command_dma() to
* transmit the command.
*/
void (*kickoff_fifo_command)(struct dsi_ctrl_hw *ctrl,
struct dsi_ctrl_cmd_dma_fifo_info *cmd,
u32 flags);
void (*reset_cmd_fifo)(struct dsi_ctrl_hw *ctrl);
/**
* trigger_command_dma() - trigger transmission of command buffer.
* @ctrl: Pointer to the controller host hardware.
*
* This trigger can be only used if there was a prior call to
* kickoff_command() of kickoff_fifo_command() with
* DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER flag.
*/
void (*trigger_command_dma)(struct dsi_ctrl_hw *ctrl);
/**
* get_cmd_read_data() - get data read from the peripheral
* @ctrl: Pointer to the controller host hardware.
* @rd_buf: Buffer where data will be read into.
* @read_offset: Offset from where to read.
* @rx_byte: Number of bytes to be read.
* @pkt_size: Size of response expected.
* @hw_read_cnt: Actual number of bytes read by HW.
*/
u32 (*get_cmd_read_data)(struct dsi_ctrl_hw *ctrl,
u8 *rd_buf,
u32 read_offset,
u32 rx_byte,
u32 pkt_size,
u32 *hw_read_cnt);
/**
* wait_for_lane_idle() - wait for DSI lanes to go to idle state
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes (enum dsi_data_lanes) which need
* to be checked to be in idle state.
*/
int (*wait_for_lane_idle)(struct dsi_ctrl_hw *ctrl, u32 lanes);
struct ctrl_ulps_config_ops ulps_ops;
/**
* clamp_enable() - enable DSI clamps
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes which need to have clamps released.
* @enable_ulps: ulps state.
*/
/**
* clamp_enable() - enable DSI clamps to keep PHY driving a stable link
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes which need to have clamps released.
* @enable_ulps: TODO:??
*/
void (*clamp_enable)(struct dsi_ctrl_hw *ctrl,
u32 lanes,
bool enable_ulps);
/**
* clamp_disable() - disable DSI clamps
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes which need to have clamps released.
* @disable_ulps: ulps state.
*/
void (*clamp_disable)(struct dsi_ctrl_hw *ctrl,
u32 lanes,
bool disable_ulps);
/**
* phy_reset_config() - Disable/enable propagation of reset signal
* from ahb domain to DSI PHY
* @ctrl: Pointer to the controller host hardware.
* @enable: True to mask the reset signal, false to unmask
*/
void (*phy_reset_config)(struct dsi_ctrl_hw *ctrl,
bool enable);
/**
* get_interrupt_status() - returns the interrupt status
* @ctrl: Pointer to the controller host hardware.
*
* Returns the ORed list of interrupts(enum dsi_status_int_type) that
* are active. This list does not include any error interrupts. Caller
* should call get_error_status for error interrupts.
*
* Return: List of active interrupts.
*/
u32 (*get_interrupt_status)(struct dsi_ctrl_hw *ctrl);
/**
* clear_interrupt_status() - clears the specified interrupts
* @ctrl: Pointer to the controller host hardware.
* @ints: List of interrupts to be cleared.
*/
void (*clear_interrupt_status)(struct dsi_ctrl_hw *ctrl, u32 ints);
/**
* poll_slave_dma_status()- API to poll slave DMA status
* @ctrl: Pointer to the controller host hardware.
*/
u32 (*poll_slave_dma_status)(struct dsi_ctrl_hw *ctrl);
/**
* enable_status_interrupts() - enable the specified interrupts
* @ctrl: Pointer to the controller host hardware.
* @ints: List of interrupts to be enabled.
*
* Enables the specified interrupts. This list will override the
* previous interrupts enabled through this function. Caller has to
* maintain the state of the interrupts enabled. To disable all
* interrupts, set ints to 0.
*/
void (*enable_status_interrupts)(struct dsi_ctrl_hw *ctrl, u32 ints);
/**
* get_error_status() - returns the error status
* @ctrl: Pointer to the controller host hardware.
*
* Returns the ORed list of errors(enum dsi_error_int_type) that are
* active. This list does not include any status interrupts. Caller
* should call get_interrupt_status for status interrupts.
*
* Return: List of active error interrupts.
*/
u64 (*get_error_status)(struct dsi_ctrl_hw *ctrl);
/**
* clear_error_status() - clears the specified errors
* @ctrl: Pointer to the controller host hardware.
* @errors: List of errors to be cleared.
*/
void (*clear_error_status)(struct dsi_ctrl_hw *ctrl, u64 errors);
/**
* enable_error_interrupts() - enable the specified interrupts
* @ctrl: Pointer to the controller host hardware.
* @errors: List of errors to be enabled.
*
* Enables the specified interrupts. This list will override the
* previous interrupts enabled through this function. Caller has to
* maintain the state of the interrupts enabled. To disable all
* interrupts, set errors to 0.
*/
void (*enable_error_interrupts)(struct dsi_ctrl_hw *ctrl, u64 errors);
/**
* video_test_pattern_setup() - setup test pattern engine for video mode
* @ctrl: Pointer to the controller host hardware.
* @type: Type of test pattern.
* @init_val: Initial value to use for generating test pattern.
*/
void (*video_test_pattern_setup)(struct dsi_ctrl_hw *ctrl,
enum dsi_test_pattern type,
u32 init_val);
/**
* cmd_test_pattern_setup() - setup test patttern engine for cmd mode
* @ctrl: Pointer to the controller host hardware.
* @type: Type of test pattern.
* @init_val: Initial value to use for generating test pattern.
* @stream_id: Stream Id on which packets are generated.
*/
void (*cmd_test_pattern_setup)(struct dsi_ctrl_hw *ctrl,
enum dsi_test_pattern type,
u32 init_val,
u32 stream_id);
/**
* test_pattern_enable() - enable test pattern engine
* @ctrl: Pointer to the controller host hardware.
* @enable: Enable/Disable test pattern engine.
*/
void (*test_pattern_enable)(struct dsi_ctrl_hw *ctrl, bool enable);
/**
* clear_phy0_ln_err() - clear DSI PHY lane-0 errors
* @ctrl: Pointer to the controller host hardware.
*/
void (*clear_phy0_ln_err)(struct dsi_ctrl_hw *ctrl);
/**
* trigger_cmd_test_pattern() - trigger a command mode frame update with
* test pattern
* @ctrl: Pointer to the controller host hardware.
* @stream_id: Stream on which frame update is sent.
*/
void (*trigger_cmd_test_pattern)(struct dsi_ctrl_hw *ctrl,
u32 stream_id);
ssize_t (*reg_dump_to_buffer)(struct dsi_ctrl_hw *ctrl,
char *buf,
u32 size);
/**
* setup_misr() - Setup frame MISR
* @ctrl: Pointer to the controller host hardware.
* @panel_mode: CMD or VIDEO mode indicator
* @enable: Enable/disable MISR.
* @frame_count: Number of frames to accumulate MISR.
*/
void (*setup_misr)(struct dsi_ctrl_hw *ctrl,
enum dsi_op_mode panel_mode,
bool enable, u32 frame_count);
/**
* collect_misr() - Read frame MISR
* @ctrl: Pointer to the controller host hardware.
* @panel_mode: CMD or VIDEO mode indicator
*/
u32 (*collect_misr)(struct dsi_ctrl_hw *ctrl,
enum dsi_op_mode panel_mode);
/**
* set_timing_db() - enable/disable Timing DB register
* @ctrl: Pointer to controller host hardware.
* @enable: Enable/Disable flag.
*
* Enable or Disabe the Timing DB register.
*/
void (*set_timing_db)(struct dsi_ctrl_hw *ctrl,
bool enable);
/**
* clear_rdbk_register() - Clear and reset read back register
* @ctrl: Pointer to the controller host hardware.
*/
void (*clear_rdbk_register)(struct dsi_ctrl_hw *ctrl);
/** schedule_dma_cmd() - Schdeule DMA command transfer on a
* particular blanking line.
* @ctrl: Pointer to the controller host hardware.
* @line_no: Blanking line number on whihch DMA command
* needs to be sent.
*/
void (*schedule_dma_cmd)(struct dsi_ctrl_hw *ctrl, int line_no);
/**
* ctrl_reset() - Reset DSI lanes to recover from DSI errors
* @ctrl: Pointer to the controller host hardware.
* @mask: Indicates the error type.
*/
int (*ctrl_reset)(struct dsi_ctrl_hw *ctrl, int mask);
/**
* mask_error_int() - Mask/Unmask particular DSI error interrupts
* @ctrl: Pointer to the controller host hardware.
* @idx: Indicates the errors to be masked.
* @en: Bool for mask or unmask of the error
*/
void (*mask_error_intr)(struct dsi_ctrl_hw *ctrl, u32 idx, bool en);
/**
* error_intr_ctrl() - Mask/Unmask master DSI error interrupt
* @ctrl: Pointer to the controller host hardware.
* @en: Bool for mask or unmask of DSI error
*/
void (*error_intr_ctrl)(struct dsi_ctrl_hw *ctrl, bool en);
/**
* get_error_mask() - get DSI error interrupt mask status
* @ctrl: Pointer to the controller host hardware.
*/
u32 (*get_error_mask)(struct dsi_ctrl_hw *ctrl);
/**
* get_hw_version() - get DSI controller hw version
* @ctrl: Pointer to the controller host hardware.
*/
u32 (*get_hw_version)(struct dsi_ctrl_hw *ctrl);
/**
* wait_for_cmd_mode_mdp_idle() - wait for command mode engine not to
* be busy sending data from display engine
* @ctrl: Pointer to the controller host hardware.
*/
int (*wait_for_cmd_mode_mdp_idle)(struct dsi_ctrl_hw *ctrl);
/**
* hw.ops.set_continuous_clk() - Set continuous clock
* @ctrl: Pointer to the controller host hardware.
* @enable: Bool to control continuous clock request.
*/
void (*set_continuous_clk)(struct dsi_ctrl_hw *ctrl, bool enable);
/**
* hw.ops.wait4dynamic_refresh_done() - Wait for dynamic refresh done
* @ctrl: Pointer to the controller host hardware.
*/
int (*wait4dynamic_refresh_done)(struct dsi_ctrl_hw *ctrl);
/**
* hw.ops.vid_engine_busy() - Returns true if vid engine is busy
* @ctrl: Pointer to the controller host hardware.
*/
bool (*vid_engine_busy)(struct dsi_ctrl_hw *ctrl);
/**
* hw.ops.hs_req_sel() - enable continuous clk support through phy
* @ctrl: Pointer to the controller host hardware.
* @sel_phy: Bool to control whether to select phy or controller
*/
void (*hs_req_sel)(struct dsi_ctrl_hw *ctrl, bool sel_phy);
/**
* hw.ops.configure_cmddma_window() - configure DMA window for CMD TX
* @ctrl: Pointer to the controller host hardware.
* @cmd: Pointer to the DSI DMA command info.
* @line_no: Line number at which the CMD needs to be triggered.
* @window: Width of the DMA CMD window.
*/
void (*configure_cmddma_window)(struct dsi_ctrl_hw *ctrl,
struct dsi_ctrl_cmd_dma_info *cmd,
u32 line_no, u32 window);
/**
* hw.ops.reset_trig_ctrl() - resets trigger control of DSI controller
* @ctrl: Pointer to the controller host hardware.
* @cfg: Common configuration parameters.
*/
void (*reset_trig_ctrl)(struct dsi_ctrl_hw *ctrl,
struct dsi_host_common_cfg *cfg);
/**
* hw.ops.log_line_count() - reads the MDP interface line count
* registers.
* @ctrl: Pointer to the controller host hardware.
* @cmd_mode: Boolean to indicate command mode operation.
*/
u32 (*log_line_count)(struct dsi_ctrl_hw *ctrl, bool cmd_mode);
};
/*
* struct dsi_ctrl_hw - DSI controller hardware object specific to an instance
* @base: VA for the DSI controller base address.
* @length: Length of the DSI controller register map.
* @mmss_misc_base: Base address of mmss_misc register map.
* @mmss_misc_length: Length of mmss_misc register map.
* @disp_cc_base: Base address of disp_cc register map.
* @disp_cc_length: Length of disp_cc register map.
* @mdp_intf_base: Base address of mdp_intf register map. Addresses of
* MDP_TEAR_INTF_TEAR_LINE_COUNT and MDP_TEAR_INTF_LINE_COUNT
* are mapped using the base address to test and validate
* the RD ptr value and line count value respectively when
* a CMD is triggered and it succeeds.
* @index: Instance ID of the controller.
* @feature_map: Features supported by the DSI controller.
* @ops: Function pointers to the operations supported by the
* controller.
* @supported_interrupts: Number of supported interrupts.
* @supported_errors: Number of supported errors.
* @phy_isolation_enabled: A boolean property allows to isolate the phy from
* dsi controller and run only dsi controller.
* @null_insertion_enabled: A boolean property to allow dsi controller to
* insert null packet.
* @widebus_support: 48 bit wide data bus is supported.
* @reset_trig_ctrl: Boolean to indicate if trigger control needs to
* be reset to default.
*/
struct dsi_ctrl_hw {
void __iomem *base;
u32 length;
void __iomem *mmss_misc_base;
u32 mmss_misc_length;
void __iomem *disp_cc_base;
u32 disp_cc_length;
void __iomem *mdp_intf_base;
u32 index;
/* features */
DECLARE_BITMAP(feature_map, DSI_CTRL_MAX_FEATURES);
struct dsi_ctrl_hw_ops ops;
/* capabilities */
u32 supported_interrupts;
u64 supported_errors;
bool phy_isolation_enabled;
bool null_insertion_enabled;
bool widebus_support;
bool reset_trig_ctrl;
};
#endif /* _DSI_CTRL_HW_H_ */

View File

@ -0,0 +1,475 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
*/
#include <linux/delay.h>
#include <linux/iopoll.h>
#include "dsi_ctrl_hw.h"
#include "dsi_ctrl_reg.h"
#include "dsi_hw.h"
#define MMSS_MISC_CLAMP_REG_OFF 0x0014
/**
* dsi_ctrl_hw_14_setup_lane_map() - setup mapping between
* logical and physical lanes
* @ctrl: Pointer to the controller host hardware.
* @lane_map: Structure defining the mapping between DSI logical
* lanes and physical lanes.
*/
void dsi_ctrl_hw_14_setup_lane_map(struct dsi_ctrl_hw *ctrl,
struct dsi_lane_map *lane_map)
{
DSI_W32(ctrl, DSI_LANE_SWAP_CTRL, lane_map->lane_map_v1);
DSI_CTRL_HW_DBG(ctrl, "Lane swap setup complete\n");
}
/**
* dsi_ctrl_hw_14_wait_for_lane_idle()
* This function waits for all the active DSI lanes to be idle by polling all
* the FIFO_EMPTY bits and polling he lane status to ensure that all the lanes
* are in stop state. This function assumes that the bus clocks required to
* access the registers are already turned on.
*
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes (enum dsi_data_lanes) which need
* to be stopped.
*
* return: Error code.
*/
int dsi_ctrl_hw_14_wait_for_lane_idle(struct dsi_ctrl_hw *ctrl, u32 lanes)
{
int rc = 0, val = 0;
u32 stop_state_mask = 0, fifo_empty_mask = 0;
u32 const sleep_us = 10;
u32 const timeout_us = 100;
if (lanes & DSI_DATA_LANE_0) {
stop_state_mask |= BIT(0);
fifo_empty_mask |= (BIT(12) | BIT(16));
}
if (lanes & DSI_DATA_LANE_1) {
stop_state_mask |= BIT(1);
fifo_empty_mask |= BIT(20);
}
if (lanes & DSI_DATA_LANE_2) {
stop_state_mask |= BIT(2);
fifo_empty_mask |= BIT(24);
}
if (lanes & DSI_DATA_LANE_3) {
stop_state_mask |= BIT(3);
fifo_empty_mask |= BIT(28);
}
DSI_CTRL_HW_DBG(ctrl, "polling for fifo empty, mask=0x%08x\n",
fifo_empty_mask);
rc = readl_poll_timeout(ctrl->base + DSI_FIFO_STATUS, val,
(val & fifo_empty_mask), sleep_us, timeout_us);
if (rc) {
DSI_CTRL_HW_ERR(ctrl, "fifo not empty, FIFO_STATUS=0x%08x\n",
val);
goto error;
}
DSI_CTRL_HW_DBG(ctrl, "polling for lanes to be in stop state, mask=0x%08x\n",
stop_state_mask);
rc = readl_poll_timeout(ctrl->base + DSI_LANE_STATUS, val,
(val & stop_state_mask), sleep_us, timeout_us);
if (rc) {
DSI_CTRL_HW_ERR(ctrl, "lanes not in stop state, LANE_STATUS=0x%08x\n",
val);
goto error;
}
error:
return rc;
}
/**
* ulps_request() - request ulps entry for specified lanes
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes (enum dsi_data_lanes) which need
* to enter ULPS.
*
* Caller should check if lanes are in ULPS mode by calling
* get_lanes_in_ulps() operation.
*/
void dsi_ctrl_hw_cmn_ulps_request(struct dsi_ctrl_hw *ctrl, u32 lanes)
{
u32 reg = 0;
reg = DSI_R32(ctrl, DSI_LANE_CTRL);
if (lanes & DSI_CLOCK_LANE)
reg |= BIT(4);
if (lanes & DSI_DATA_LANE_0)
reg |= BIT(0);
if (lanes & DSI_DATA_LANE_1)
reg |= BIT(1);
if (lanes & DSI_DATA_LANE_2)
reg |= BIT(2);
if (lanes & DSI_DATA_LANE_3)
reg |= BIT(3);
/*
* ULPS entry request. Wait for short time to make sure
* that the lanes enter ULPS. Recommended as per HPG.
*/
DSI_W32(ctrl, DSI_LANE_CTRL, reg);
usleep_range(100, 110);
DSI_CTRL_HW_DBG(ctrl, "ULPS requested for lanes 0x%x\n", lanes);
}
/**
* ulps_exit() - exit ULPS on specified lanes
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes (enum dsi_data_lanes) which need
* to exit ULPS.
*
* Caller should check if lanes are in active mode by calling
* get_lanes_in_ulps() operation.
*/
void dsi_ctrl_hw_cmn_ulps_exit(struct dsi_ctrl_hw *ctrl, u32 lanes)
{
u32 reg = 0;
u32 prev_reg = 0;
prev_reg = DSI_R32(ctrl, DSI_LANE_CTRL);
prev_reg &= BIT(24);
if (lanes & DSI_CLOCK_LANE)
reg |= BIT(12);
if (lanes & DSI_DATA_LANE_0)
reg |= BIT(8);
if (lanes & DSI_DATA_LANE_1)
reg |= BIT(9);
if (lanes & DSI_DATA_LANE_2)
reg |= BIT(10);
if (lanes & DSI_DATA_LANE_3)
reg |= BIT(11);
/*
* ULPS Exit Request
* Hardware requirement is to wait for at least 1ms
*/
DSI_W32(ctrl, DSI_LANE_CTRL, reg | prev_reg);
usleep_range(1000, 1010);
/*
* Sometimes when exiting ULPS, it is possible that some DSI
* lanes are not in the stop state which could lead to DSI
* commands not going through. To avoid this, force the lanes
* to be in stop state.
*/
DSI_W32(ctrl, DSI_LANE_CTRL, (reg << 8) | prev_reg);
wmb(); /* ensure lanes are put to stop state */
DSI_W32(ctrl, DSI_LANE_CTRL, 0x0 | prev_reg);
wmb(); /* ensure lanes are put to stop state */
DSI_CTRL_HW_DBG(ctrl, "ULPS exit request for lanes=0x%x\n", lanes);
}
/**
* get_lanes_in_ulps() - returns the list of lanes in ULPS mode
* @ctrl: Pointer to the controller host hardware.
*
* Returns an ORed list of lanes (enum dsi_data_lanes) that are in ULPS
* state. If 0 is returned, all the lanes are active.
*
* Return: List of lanes in ULPS state.
*/
u32 dsi_ctrl_hw_cmn_get_lanes_in_ulps(struct dsi_ctrl_hw *ctrl)
{
u32 reg = 0;
u32 lanes = 0;
reg = DSI_R32(ctrl, DSI_LANE_STATUS);
if (!(reg & BIT(8)))
lanes |= DSI_DATA_LANE_0;
if (!(reg & BIT(9)))
lanes |= DSI_DATA_LANE_1;
if (!(reg & BIT(10)))
lanes |= DSI_DATA_LANE_2;
if (!(reg & BIT(11)))
lanes |= DSI_DATA_LANE_3;
if (!(reg & BIT(12)))
lanes |= DSI_CLOCK_LANE;
DSI_CTRL_HW_DBG(ctrl, "lanes in ulps = 0x%x\n", lanes);
return lanes;
}
/**
* clamp_enable() - enable DSI clamps to keep PHY driving a stable link
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes which need to be clamped.
* @enable_ulps: Boolean to specify if ULPS is enabled in DSI controller
*/
void dsi_ctrl_hw_14_clamp_enable(struct dsi_ctrl_hw *ctrl,
u32 lanes,
bool enable_ulps)
{
u32 clamp_reg = 0;
u32 bit_shift = 0;
u32 reg = 0;
if (ctrl->index == 1)
bit_shift = 16;
if (lanes & DSI_CLOCK_LANE) {
clamp_reg |= BIT(9);
if (enable_ulps)
clamp_reg |= BIT(8);
}
if (lanes & DSI_DATA_LANE_0) {
clamp_reg |= BIT(7);
if (enable_ulps)
clamp_reg |= BIT(6);
}
if (lanes & DSI_DATA_LANE_1) {
clamp_reg |= BIT(5);
if (enable_ulps)
clamp_reg |= BIT(4);
}
if (lanes & DSI_DATA_LANE_2) {
clamp_reg |= BIT(3);
if (enable_ulps)
clamp_reg |= BIT(2);
}
if (lanes & DSI_DATA_LANE_3) {
clamp_reg |= BIT(1);
if (enable_ulps)
clamp_reg |= BIT(0);
}
reg = DSI_MMSS_MISC_R32(ctrl, MMSS_MISC_CLAMP_REG_OFF);
reg |= (clamp_reg << bit_shift);
DSI_MMSS_MISC_W32(ctrl, MMSS_MISC_CLAMP_REG_OFF, reg);
reg = DSI_MMSS_MISC_R32(ctrl, MMSS_MISC_CLAMP_REG_OFF);
reg |= (BIT(15) << bit_shift); /* Enable clamp */
DSI_MMSS_MISC_W32(ctrl, MMSS_MISC_CLAMP_REG_OFF, reg);
DSI_CTRL_HW_DBG(ctrl, "Clamps enabled for lanes=0x%x\n", lanes);
}
/**
* clamp_disable() - disable DSI clamps
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes which need to have clamps released.
* @disable_ulps: Boolean to specify if ULPS is enabled in DSI controller
*/
void dsi_ctrl_hw_14_clamp_disable(struct dsi_ctrl_hw *ctrl,
u32 lanes,
bool disable_ulps)
{
u32 clamp_reg = 0;
u32 bit_shift = 0;
u32 reg = 0;
if (ctrl->index == 1)
bit_shift = 16;
if (lanes & DSI_CLOCK_LANE) {
clamp_reg |= BIT(9);
if (disable_ulps)
clamp_reg |= BIT(8);
}
if (lanes & DSI_DATA_LANE_0) {
clamp_reg |= BIT(7);
if (disable_ulps)
clamp_reg |= BIT(6);
}
if (lanes & DSI_DATA_LANE_1) {
clamp_reg |= BIT(5);
if (disable_ulps)
clamp_reg |= BIT(4);
}
if (lanes & DSI_DATA_LANE_2) {
clamp_reg |= BIT(3);
if (disable_ulps)
clamp_reg |= BIT(2);
}
if (lanes & DSI_DATA_LANE_3) {
clamp_reg |= BIT(1);
if (disable_ulps)
clamp_reg |= BIT(0);
}
clamp_reg |= BIT(15); /* Enable clamp */
clamp_reg <<= bit_shift;
reg = DSI_MMSS_MISC_R32(ctrl, MMSS_MISC_CLAMP_REG_OFF);
reg &= ~(clamp_reg);
DSI_MMSS_MISC_W32(ctrl, MMSS_MISC_CLAMP_REG_OFF, reg);
DSI_CTRL_HW_DBG(ctrl, "Disable clamps for lanes=%d\n", lanes);
}
#define DUMP_REG_VALUE(off) "\t%-30s: 0x%08x\n", #off, DSI_R32(ctrl, off)
ssize_t dsi_ctrl_hw_14_reg_dump_to_buffer(struct dsi_ctrl_hw *ctrl,
char *buf,
u32 size)
{
u32 len = 0;
len += snprintf((buf + len), (size - len), "CONFIGURATION REGS:\n");
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_HW_VERSION));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_STATUS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_FIFO_STATUS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_SYNC_DATATYPE));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_PIXEL_DATATYPE));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_BLANKING_DATATYPE));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_DATA_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_ACTIVE_H));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_ACTIVE_V));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_TOTAL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_HSYNC));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_VSYNC));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_VSYNC_VPOS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_DMA_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_DCS_CMD_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_DMA_CMD_OFFSET));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_DMA_CMD_LENGTH));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_DMA_FIFO_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_DMA_NULL_PACKET_DATA));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM0_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM0_TOTAL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM1_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM1_TOTAL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_ACK_ERR_STATUS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RDBK_DATA0));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RDBK_DATA1));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RDBK_DATA2));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RDBK_DATA3));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RDBK_DATATYPE0));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RDBK_DATATYPE1));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_TRIG_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_EXT_MUX));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_EXT_MUX_TE_PULSE_DETECT_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_CMD_MODE_DMA_SW_TRIGGER));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_CMD_MODE_MDP_SW_TRIGGER));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_CMD_MODE_BTA_SW_TRIGGER));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RESET_SW_TRIGGER));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_LANE_STATUS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_LANE_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_LANE_SWAP_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_DLN0_PHY_ERR));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_LP_TIMER_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_HS_TIMER_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_TIMEOUT_STATUS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_CLKOUT_TIMING_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_EOT_PACKET));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_EOT_PACKET_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_GENERIC_ESC_TX_TRIGGER));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_ERR_INT_MASK0));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_INT_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_SOFT_RESET));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_CLK_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_CLK_STATUS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_PHY_SW_RESET));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_AXI2AHB_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_CTRL2));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM2_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM2_TOTAL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VBIF_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_AES_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RDBK_DATA_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_TEST_PATTERN_GEN_CMD_DMA_INIT_VAL2));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_TPG_DMA_FIFO_STATUS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_TPG_DMA_FIFO_WRITE_TRIGGER));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_DSI_TIMING_FLUSH));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_DSI_TIMING_DB_MODE));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_TPG_DMA_FIFO_RESET));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VERSION));
DSI_CTRL_HW_ERR(ctrl, "LLENGTH = %d\n", len);
return len;
}

View File

@ -0,0 +1,224 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
*/
#include <linux/delay.h>
#include <linux/iopoll.h>
#include "dsi_ctrl_hw.h"
#include "dsi_ctrl_reg.h"
#include "dsi_hw.h"
void dsi_ctrl_hw_20_setup_lane_map(struct dsi_ctrl_hw *ctrl,
struct dsi_lane_map *lane_map)
{
u32 reg_value = lane_map->lane_map_v2[DSI_LOGICAL_LANE_0] |
(lane_map->lane_map_v2[DSI_LOGICAL_LANE_1] << 4) |
(lane_map->lane_map_v2[DSI_LOGICAL_LANE_2] << 8) |
(lane_map->lane_map_v2[DSI_LOGICAL_LANE_3] << 12);
DSI_W32(ctrl, DSI_LANE_SWAP_CTRL, reg_value);
DSI_CTRL_HW_DBG(ctrl, "Lane swap setup complete\n");
}
int dsi_ctrl_hw_20_wait_for_lane_idle(struct dsi_ctrl_hw *ctrl,
u32 lanes)
{
int rc = 0, val = 0;
u32 fifo_empty_mask = 0;
u32 const sleep_us = 10;
u32 const timeout_us = 100;
if (lanes & DSI_DATA_LANE_0)
fifo_empty_mask |= (BIT(12) | BIT(16));
if (lanes & DSI_DATA_LANE_1)
fifo_empty_mask |= BIT(20);
if (lanes & DSI_DATA_LANE_2)
fifo_empty_mask |= BIT(24);
if (lanes & DSI_DATA_LANE_3)
fifo_empty_mask |= BIT(28);
DSI_CTRL_HW_DBG(ctrl, "polling for fifo empty, mask=0x%08x\n",
fifo_empty_mask);
rc = readl_poll_timeout(ctrl->base + DSI_FIFO_STATUS, val,
(val & fifo_empty_mask), sleep_us, timeout_us);
if (rc) {
DSI_CTRL_HW_ERR(ctrl, "fifo not empty, FIFO_STATUS=0x%08x\n",
val);
goto error;
}
error:
return rc;
}
#define DUMP_REG_VALUE(off) "\t%-30s: 0x%08x\n", #off, DSI_R32(ctrl, off)
ssize_t dsi_ctrl_hw_20_reg_dump_to_buffer(struct dsi_ctrl_hw *ctrl,
char *buf,
u32 size)
{
u32 len = 0;
len += snprintf((buf + len), (size - len), "CONFIGURATION REGS:\n");
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_HW_VERSION));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_STATUS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_FIFO_STATUS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_SYNC_DATATYPE));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_PIXEL_DATATYPE));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_BLANKING_DATATYPE));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_DATA_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_ACTIVE_H));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_ACTIVE_V));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_TOTAL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_HSYNC));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_VSYNC));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_VSYNC_VPOS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_DMA_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_DCS_CMD_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_DMA_CMD_OFFSET));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_DMA_CMD_LENGTH));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_DMA_FIFO_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_DMA_NULL_PACKET_DATA));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM0_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM0_TOTAL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM1_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM1_TOTAL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_ACK_ERR_STATUS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RDBK_DATA0));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RDBK_DATA1));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RDBK_DATA2));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RDBK_DATA3));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RDBK_DATATYPE0));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RDBK_DATATYPE1));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_TRIG_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_EXT_MUX));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_EXT_MUX_TE_PULSE_DETECT_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_CMD_MODE_DMA_SW_TRIGGER));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_CMD_MODE_MDP_SW_TRIGGER));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_CMD_MODE_BTA_SW_TRIGGER));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RESET_SW_TRIGGER));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_MISR_CMD_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_MISR_VIDEO_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_LANE_STATUS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_LANE_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_LANE_SWAP_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_DLN0_PHY_ERR));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_LP_TIMER_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_HS_TIMER_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_TIMEOUT_STATUS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_CLKOUT_TIMING_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_EOT_PACKET));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_EOT_PACKET_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_GENERIC_ESC_TX_TRIGGER));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_ERR_INT_MASK0));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_INT_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_SOFT_RESET));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_CLK_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_CLK_STATUS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_PHY_SW_RESET));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_AXI2AHB_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_MISR_CMD_MDP0_32BIT));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_MISR_CMD_MDP1_32BIT));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_MISR_VIDEO_32BIT));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_CTRL2));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM2_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM2_TOTAL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VBIF_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_AES_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RDBK_DATA_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_TEST_PATTERN_GEN_CMD_DMA_INIT_VAL2));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_TPG_DMA_FIFO_STATUS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_TPG_DMA_FIFO_WRITE_TRIGGER));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_DSI_TIMING_FLUSH));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_DSI_TIMING_DB_MODE));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_TPG_DMA_FIFO_RESET));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VERSION));
DSI_CTRL_HW_ERR(ctrl, "LLENGTH = %d\n", len);
return len;
}

View File

@ -0,0 +1,282 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/iopoll.h>
#include "dsi_ctrl_hw.h"
#include "dsi_ctrl_reg.h"
#include "dsi_hw.h"
#include "dsi_catalog.h"
#define DISP_CC_MISC_CMD_REG_OFF 0x00
/* register to configure DMA scheduling */
#define DSI_DMA_SCHEDULE_CTRL 0x100
#define DSI_DMA_SCHEDULE_CTRL2 0x0104
/* offset addresses of MDP INTF base register, to be mapped for debug feature */
#define MDP_INTF_TEAR_OFFSET 0x280
#define MDP_INTF_TEAR_LINE_COUNT_OFFSET 0x30
#define MDP_INTF_LINE_COUNT_OFFSET 0xB0
void dsi_ctrl_hw_22_setup_lane_map(struct dsi_ctrl_hw *ctrl,
struct dsi_lane_map *lane_map)
{
u32 reg_value = lane_map->lane_map_v2[DSI_LOGICAL_LANE_0] |
(lane_map->lane_map_v2[DSI_LOGICAL_LANE_1] << 4) |
(lane_map->lane_map_v2[DSI_LOGICAL_LANE_2] << 8) |
(lane_map->lane_map_v2[DSI_LOGICAL_LANE_3] << 12);
DSI_W32(ctrl, DSI_LANE_SWAP_CTRL, reg_value);
DSI_CTRL_HW_DBG(ctrl, "[DSI_%d] Lane swap setup complete\n",
ctrl->index);
}
int dsi_ctrl_hw_22_wait_for_lane_idle(struct dsi_ctrl_hw *ctrl,
u32 lanes)
{
int rc = 0, val = 0;
u32 fifo_empty_mask = 0;
u32 const sleep_us = 10;
u32 const timeout_us = 100;
if (lanes & DSI_DATA_LANE_0)
fifo_empty_mask |= (BIT(12) | BIT(16));
if (lanes & DSI_DATA_LANE_1)
fifo_empty_mask |= BIT(20);
if (lanes & DSI_DATA_LANE_2)
fifo_empty_mask |= BIT(24);
if (lanes & DSI_DATA_LANE_3)
fifo_empty_mask |= BIT(28);
DSI_CTRL_HW_DBG(ctrl, "%s: polling for fifo empty, mask=0x%08x\n",
__func__, fifo_empty_mask);
rc = readl_poll_timeout(ctrl->base + DSI_FIFO_STATUS, val,
(val & fifo_empty_mask), sleep_us, timeout_us);
if (rc) {
DSI_CTRL_HW_ERR(ctrl,
"%s: fifo not empty, FIFO_STATUS=0x%08x\n",
__func__, val);
goto error;
}
error:
return rc;
}
ssize_t dsi_ctrl_hw_22_reg_dump_to_buffer(struct dsi_ctrl_hw *ctrl,
char *buf,
u32 size)
{
return size;
}
/**
* dsi_ctrl_hw_22_phy_reset_config() - to configure clamp control during ulps
* @ctrl: Pointer to the controller host hardware.
* @enable: boolean to specify enable/disable.
*/
void dsi_ctrl_hw_22_phy_reset_config(struct dsi_ctrl_hw *ctrl,
bool enable)
{
u32 reg = 0;
reg = DSI_DISP_CC_R32(ctrl, DISP_CC_MISC_CMD_REG_OFF);
/* Mask/unmask disable PHY reset bit */
if (enable)
reg &= ~BIT(ctrl->index);
else
reg |= BIT(ctrl->index);
DSI_DISP_CC_W32(ctrl, DISP_CC_MISC_CMD_REG_OFF, reg);
}
/**
* dsi_ctrl_hw_22_schedule_dma_cmd() - to schedule DMA command transfer
* @ctrl: Pointer to the controller host hardware.
* @line_no: Line number at which command needs to be sent.
*/
void dsi_ctrl_hw_22_schedule_dma_cmd(struct dsi_ctrl_hw *ctrl, int line_no)
{
u32 reg = 0;
reg = DSI_R32(ctrl, DSI_DMA_SCHEDULE_CTRL);
reg |= BIT(28);
reg |= (line_no & 0xffff);
DSI_W32(ctrl, DSI_DMA_SCHEDULE_CTRL, reg);
ctrl->reset_trig_ctrl = true;
}
/*
* dsi_ctrl_hw_kickoff_non_embedded_mode()-Kickoff cmd in non-embedded mode
* @ctrl: - Pointer to the controller host hardware.
* @dsi_ctrl_cmd_dma_info: - command buffer information.
* @flags: - DSI CTRL Flags.
*/
void dsi_ctrl_hw_kickoff_non_embedded_mode(struct dsi_ctrl_hw *ctrl,
struct dsi_ctrl_cmd_dma_info *cmd,
u32 flags)
{
u32 reg = 0;
reg = DSI_R32(ctrl, DSI_COMMAND_MODE_DMA_CTRL);
reg &= ~BIT(31);/* disable broadcast */
reg &= ~BIT(30);
if (cmd->use_lpm)
reg |= BIT(26);
else
reg &= ~BIT(26);
/* Select non EMBEDDED_MODE, pick the packet header from register */
reg &= ~BIT(28);
reg |= BIT(24);/* long packet */
reg |= BIT(29);/* wc_sel = 1 */
reg |= (((cmd->datatype) & 0x03f) << 16);/* data type */
DSI_W32(ctrl, DSI_COMMAND_MODE_DMA_CTRL, reg);
/* Enable WRITE_WATERMARK_DISABLE and READ_WATERMARK_DISABLE bits */
reg = DSI_R32(ctrl, DSI_DMA_FIFO_CTRL);
reg |= BIT(20);
reg |= BIT(16);
reg |= 0x33;/* Set READ and WRITE watermark levels to maximum */
DSI_W32(ctrl, DSI_DMA_FIFO_CTRL, reg);
DSI_W32(ctrl, DSI_DMA_CMD_OFFSET, cmd->offset);
DSI_W32(ctrl, DSI_DMA_CMD_LENGTH, ((cmd->length) & 0xFFFFFF));
/* wait for writes to complete before kick off */
wmb();
if (!(flags & DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER))
DSI_W32(ctrl, DSI_CMD_MODE_DMA_SW_TRIGGER, 0x1);
}
/*
* dsi_ctrl_hw_22_config_clk_gating() - enable/disable clk gating on DSI PHY
* @ctrl: Pointer to the controller host hardware.
* @enable: bool to notify enable/disable.
* @clk_selection: clock to enable/disable clock gating.
*
*/
void dsi_ctrl_hw_22_config_clk_gating(struct dsi_ctrl_hw *ctrl, bool enable,
enum dsi_clk_gate_type clk_selection)
{
u32 reg = 0;
u32 enable_select = 0;
reg = DSI_DISP_CC_R32(ctrl, DISP_CC_MISC_CMD_REG_OFF);
if (clk_selection & PIXEL_CLK)
enable_select |= ctrl->index ? BIT(6) : BIT(5);
if (clk_selection & BYTE_CLK)
enable_select |= ctrl->index ? BIT(8) : BIT(7);
if (clk_selection & DSI_PHY)
enable_select |= ctrl->index ? BIT(10) : BIT(9);
if (enable)
reg |= enable_select;
else
reg &= ~enable_select;
DSI_DISP_CC_W32(ctrl, DISP_CC_MISC_CMD_REG_OFF, reg);
}
/**
* dsi_ctrl_hw_22_configure_cmddma_window() - configure DMA window for CMD TX
* @ctrl: Pointer to the controller host hardware.
* @cmd: Pointer to the DSI DMA command info.
* @line_no: Line number at which the CMD needs to be triggered.
* @window: Width of the DMA CMD window.
*/
void dsi_ctrl_hw_22_configure_cmddma_window(struct dsi_ctrl_hw *ctrl,
struct dsi_ctrl_cmd_dma_info *cmd,
u32 line_no, u32 window)
{
u32 reg = 0;
if (cmd->en_broadcast) {
reg = DSI_R32(ctrl, DSI_TRIG_CTRL);
if (cmd->is_master) {
reg &= ~0xF;
reg |= 0xc;
} else {
reg &= ~0xF;
reg |= BIT(16);
}
DSI_W32(ctrl, DSI_TRIG_CTRL, reg);
if (cmd->is_master) {
reg = 0;
reg |= line_no;
reg |= window << 16;
DSI_W32(ctrl, DSI_DMA_SCHEDULE_CTRL2, reg);
}
} else {
reg = DSI_R32(ctrl, DSI_TRIG_CTRL);
reg &= ~0xF;
reg |= 0xc;
DSI_W32(ctrl, DSI_TRIG_CTRL, reg);
reg = 0;
reg |= line_no;
reg |= window << 16;
DSI_W32(ctrl, DSI_DMA_SCHEDULE_CTRL2, reg);
}
ctrl->reset_trig_ctrl = true;
}
/**
* dsi_ctrl_hw_22_reset_trigger_controls() - reset dsi trigger configurations
* @ctrl: Pointer to the controller host hardware.
* @cfg: DSI host configuration that is common to both video and
* command modes.
*/
void dsi_ctrl_hw_22_reset_trigger_controls(struct dsi_ctrl_hw *ctrl,
struct dsi_host_common_cfg *cfg)
{
u32 reg = 0;
const u8 trigger_map[DSI_TRIGGER_MAX] = {
0x0, 0x2, 0x1, 0x4, 0x5, 0x6 };
reg |= (cfg->te_mode == DSI_TE_ON_EXT_PIN) ? BIT(31) : 0;
reg |= (trigger_map[cfg->dma_cmd_trigger] & 0x7);
reg |= (trigger_map[cfg->mdp_cmd_trigger] & 0x7) << 4;
DSI_W32(ctrl, DSI_TRIG_CTRL, reg);
DSI_W32(ctrl, DSI_DMA_SCHEDULE_CTRL2, 0x0);
DSI_W32(ctrl, DSI_DMA_SCHEDULE_CTRL, 0x0);
ctrl->reset_trig_ctrl = false;
}
/**
* dsi_ctrl_hw_22_log_line_count() - reads the MDP interface line count
* registers.
* @ctrl: Pointer to the controller host hardware.
* @cmd_mode: Boolean to indicate command mode operation.
*
* Return: INTF register value.
*/
u32 dsi_ctrl_hw_22_log_line_count(struct dsi_ctrl_hw *ctrl, bool cmd_mode)
{
u32 reg = 0;
if (IS_ERR_OR_NULL(ctrl->mdp_intf_base))
return reg;
if (cmd_mode)
reg = readl_relaxed(ctrl->mdp_intf_base + MDP_INTF_TEAR_OFFSET
+ MDP_INTF_TEAR_LINE_COUNT_OFFSET);
else
reg = readl_relaxed(ctrl->mdp_intf_base
+ MDP_INTF_LINE_COUNT_OFFSET);
return reg;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,152 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DSI_CTRL_REG_H_
#define _DSI_CTRL_REG_H_
#define DSI_HW_VERSION (0x0000)
#define DSI_CTRL (0x0004)
#define DSI_STATUS (0x0008)
#define DSI_FIFO_STATUS (0x000C)
#define DSI_VIDEO_MODE_CTRL (0x0010)
#define DSI_VIDEO_MODE_SYNC_DATATYPE (0x0014)
#define DSI_VIDEO_MODE_PIXEL_DATATYPE (0x0018)
#define DSI_VIDEO_MODE_BLANKING_DATATYPE (0x001C)
#define DSI_VIDEO_MODE_DATA_CTRL (0x0020)
#define DSI_VIDEO_MODE_ACTIVE_H (0x0024)
#define DSI_VIDEO_MODE_ACTIVE_V (0x0028)
#define DSI_VIDEO_MODE_TOTAL (0x002C)
#define DSI_VIDEO_MODE_HSYNC (0x0030)
#define DSI_VIDEO_MODE_VSYNC (0x0034)
#define DSI_VIDEO_MODE_VSYNC_VPOS (0x0038)
#define DSI_COMMAND_MODE_DMA_CTRL (0x003C)
#define DSI_COMMAND_MODE_MDP_CTRL (0x0040)
#define DSI_COMMAND_MODE_MDP_DCS_CMD_CTRL (0x0044)
#define DSI_DMA_CMD_OFFSET (0x0048)
#define DSI_DMA_CMD_LENGTH (0x004C)
#define DSI_DMA_FIFO_CTRL (0x0050)
#define DSI_DMA_NULL_PACKET_DATA (0x0054)
#define DSI_COMMAND_MODE_MDP_STREAM0_CTRL (0x0058)
#define DSI_COMMAND_MODE_MDP_STREAM0_TOTAL (0x005C)
#define DSI_COMMAND_MODE_MDP_STREAM1_CTRL (0x0060)
#define DSI_COMMAND_MODE_MDP_STREAM1_TOTAL (0x0064)
#define DSI_ACK_ERR_STATUS (0x0068)
#define DSI_RDBK_DATA0 (0x006C)
#define DSI_RDBK_DATA1 (0x0070)
#define DSI_RDBK_DATA2 (0x0074)
#define DSI_RDBK_DATA3 (0x0078)
#define DSI_RDBK_DATATYPE0 (0x007C)
#define DSI_RDBK_DATATYPE1 (0x0080)
#define DSI_TRIG_CTRL (0x0084)
#define DSI_EXT_MUX (0x0088)
#define DSI_EXT_MUX_TE_PULSE_DETECT_CTRL (0x008C)
#define DSI_CMD_MODE_DMA_SW_TRIGGER (0x0090)
#define DSI_CMD_MODE_MDP_SW_TRIGGER (0x0094)
#define DSI_CMD_MODE_BTA_SW_TRIGGER (0x0098)
#define DSI_RESET_SW_TRIGGER (0x009C)
#define DSI_MISR_CMD_CTRL (0x00A0)
#define DSI_MISR_VIDEO_CTRL (0x00A4)
#define DSI_LANE_STATUS (0x00A8)
#define DSI_LANE_CTRL (0x00AC)
#define DSI_LANE_SWAP_CTRL (0x00B0)
#define DSI_DLN0_PHY_ERR (0x00B4)
#define DSI_LP_TIMER_CTRL (0x00B8)
#define DSI_HS_TIMER_CTRL (0x00BC)
#define DSI_TIMEOUT_STATUS (0x00C0)
#define DSI_CLKOUT_TIMING_CTRL (0x00C4)
#define DSI_EOT_PACKET (0x00C8)
#define DSI_EOT_PACKET_CTRL (0x00CC)
#define DSI_GENERIC_ESC_TX_TRIGGER (0x00D0)
#define DSI_CAM_BIST_CTRL (0x00D4)
#define DSI_CAM_BIST_FRAME_SIZE (0x00D8)
#define DSI_CAM_BIST_BLOCK_SIZE (0x00DC)
#define DSI_CAM_BIST_FRAME_CONFIG (0x00E0)
#define DSI_CAM_BIST_LSFR_CTRL (0x00E4)
#define DSI_CAM_BIST_LSFR_INIT (0x00E8)
#define DSI_CAM_BIST_START (0x00EC)
#define DSI_CAM_BIST_STATUS (0x00F0)
#define DSI_ERR_INT_MASK0 (0x010C)
#define DSI_INT_CTRL (0x0110)
#define DSI_IOBIST_CTRL (0x0114)
#define DSI_SOFT_RESET (0x0118)
#define DSI_CLK_CTRL (0x011C)
#define DSI_CLK_STATUS (0x0120)
#define DSI_DEBUG_BUS_CTL (0x0124)
#define DSI_DEBUG_BUS_STATUS (0x0128)
#define DSI_PHY_SW_RESET (0x012C)
#define DSI_AXI2AHB_CTRL (0x0130)
#define DSI_MISR_CMD_MDP0_32BIT (0x0134)
#define DSI_MISR_CMD_MDP1_32BIT (0x0138)
#define DSI_MISR_CMD_DMA_32BIT (0x013C)
#define DSI_MISR_VIDEO_32BIT (0x0140)
#define DSI_LANE_MISR_CTRL (0x0144)
#define DSI_LANE0_MISR (0x0148)
#define DSI_LANE1_MISR (0x014C)
#define DSI_LANE2_MISR (0x0150)
#define DSI_LANE3_MISR (0x0154)
#define DSI_TEST_PATTERN_GEN_CTRL (0x015C)
#define DSI_TEST_PATTERN_GEN_VIDEO_POLY (0x0160)
#define DSI_TEST_PATTERN_GEN_VIDEO_INIT_VAL (0x0164)
#define DSI_TEST_PATTERN_GEN_CMD_MDP_STREAM0_POLY (0x0168)
#define DSI_TEST_PATTERN_GEN_CMD_MDP_INIT_VAL0 (0x016C)
#define DSI_TEST_PATTERN_GEN_CMD_MDP_STREAM1_POLY (0x0170)
#define DSI_TEST_PATTERN_GEN_CMD_MDP_INIT_VAL1 (0x0174)
#define DSI_TEST_PATTERN_GEN_CMD_DMA_POLY (0x0178)
#define DSI_TEST_PATTERN_GEN_CMD_DMA_INIT_VAL (0x017C)
#define DSI_TEST_PATTERN_GEN_VIDEO_ENABLE (0x0180)
#define DSI_TEST_PATTERN_GEN_CMD_STREAM0_TRIGGER (0x0184)
#define DSI_TEST_PATTERN_GEN_CMD_STREAM1_TRIGGER (0x0188)
#define DSI_TEST_PATTERN_GEN_CMD_MDP_INIT_VAL2 (0x018C)
#define DSI_TEST_PATTERN_GEN_CMD_MDP_STREAM2_POLY (0x0190)
#define DSI_TEST_PATTERN_GEN_CMD_MDP_STREAM2_POLY (0x0190)
#define DSI_COMMAND_MODE_MDP_IDLE_CTRL (0x0194)
#define DSI_TEST_PATTERN_GEN_CMD_STREAM2_TRIGGER (0x0198)
#define DSI_TPG_MAIN_CONTROL (0x019C)
#define DSI_TPG_MAIN_CONTROL2 (0x01A0)
#define DSI_TPG_VIDEO_CONFIG (0x01A4)
#define DSI_TPG_COMPONENT_LIMITS (0x01A8)
#define DSI_TPG_RECTANGLE (0x01AC)
#define DSI_TPG_BLACK_WHITE_PATTERN_FRAMES (0x01B0)
#define DSI_TPG_RGB_MAPPING (0x01B4)
#define DSI_COMMAND_MODE_MDP_CTRL2 (0x01B8)
#define DSI_COMMAND_MODE_MDP_STREAM2_CTRL (0x01BC)
#define DSI_COMMAND_MODE_MDP_STREAM2_TOTAL (0x01C0)
#define DSI_MISR_CMD_MDP2_8BIT (0x01C4)
#define DSI_MISR_CMD_MDP2_32BIT (0x01C8)
#define DSI_VBIF_CTRL (0x01CC)
#define DSI_AES_CTRL (0x01D0)
#define DSI_RDBK_DATA_CTRL (0x01D4)
#define DSI_TEST_PATTERN_GEN_CMD_DMA_INIT_VAL2 (0x01D8)
#define DSI_TPG_DMA_FIFO_STATUS (0x01DC)
#define DSI_TPG_DMA_FIFO_WRITE_TRIGGER (0x01E0)
#define DSI_DSI_TIMING_FLUSH (0x01E4)
#define DSI_DSI_TIMING_DB_MODE (0x01E8)
#define DSI_TPG_DMA_FIFO_RESET (0x01EC)
#define DSI_SCRATCH_REGISTER_0 (0x01F0)
#define DSI_VERSION (0x01F4)
#define DSI_SCRATCH_REGISTER_1 (0x01F8)
#define DSI_SCRATCH_REGISTER_2 (0x01FC)
#define DSI_DYNAMIC_REFRESH_CTRL (0x0200)
#define DSI_DYNAMIC_REFRESH_STATUS (0x0210)
#define DSI_VIDEO_COMPRESSION_MODE_CTRL (0x02A0)
#define DSI_VIDEO_COMPRESSION_MODE_CTRL2 (0x02A4)
#define DSI_COMMAND_COMPRESSION_MODE_CTRL (0x02A8)
#define DSI_COMMAND_COMPRESSION_MODE_CTRL2 (0x02AC)
#define DSI_COMMAND_COMPRESSION_MODE_CTRL3 (0x02B0)
#define DSI_COMMAND_MODE_NULL_INSERTION_CTRL (0x02B4)
#define DSI_READ_BACK_DISABLE_STATUS (0x02B8)
#define DSI_DESKEW_CTRL (0x02BC)
#define DSI_DESKEW_DELAY_CTRL (0x02C0)
#define DSI_DESKEW_SW_TRIGGER (0x02C4)
#define DSI_DEBUG_CTRL (0x02C8)
#define DSI_SECURE_DISPLAY_STATUS (0x02CC)
#define DSI_SECURE_DISPLAY_BLOCK_COMMAND_COLOR (0x02D0)
#define DSI_SECURE_DISPLAY_BLOCK_VIDEO_COLOR (0x02D4)
#define DSI_CPHY_MODE_CTRL (0x02D8)
#define DSI_LOGICAL_LANE_SWAP_CTRL (0x0310)
#define DSI_SPLIT_LINK (0x0330)
#endif /* _DSI_CTRL_REG_H_ */

View File

@ -0,0 +1,770 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
*/
#ifndef _DSI_DEFS_H_
#define _DSI_DEFS_H_
#include <linux/types.h>
#include <drm/drm_mipi_dsi.h>
#include "msm_drv.h"
#define DSI_H_TOTAL(t) (((t)->h_active) + ((t)->h_back_porch) + \
((t)->h_sync_width) + ((t)->h_front_porch))
#define DSI_V_TOTAL(t) (((t)->v_active) + ((t)->v_back_porch) + \
((t)->v_sync_width) + ((t)->v_front_porch))
#define DSI_H_SCALE(h, s) (DIV_ROUND_UP((h) * (s)->numer, (s)->denom))
#define DSI_DEBUG_NAME_LEN 32
#define display_for_each_ctrl(index, display) \
for (index = 0; (index < (display)->ctrl_count) &&\
(index < MAX_DSI_CTRLS_PER_DISPLAY); index++)
#define DSI_WARN(fmt, ...) DRM_WARN("[msm-dsi-warn]: "fmt, ##__VA_ARGS__)
#define DSI_ERR(fmt, ...) DRM_DEV_ERROR(NULL, "[msm-dsi-error]: " fmt, \
##__VA_ARGS__)
#define DSI_INFO(fmt, ...) DRM_DEV_INFO(NULL, "[msm-dsi-info]: "fmt, \
##__VA_ARGS__)
#define DSI_DEBUG(fmt, ...) DRM_DEV_DEBUG(NULL, "[msm-dsi-debug]: "fmt, \
##__VA_ARGS__)
/**
* enum dsi_pixel_format - DSI pixel formats
* @DSI_PIXEL_FORMAT_RGB565:
* @DSI_PIXEL_FORMAT_RGB666:
* @DSI_PIXEL_FORMAT_RGB666_LOOSE:
* @DSI_PIXEL_FORMAT_RGB888:
* @DSI_PIXEL_FORMAT_RGB111:
* @DSI_PIXEL_FORMAT_RGB332:
* @DSI_PIXEL_FORMAT_RGB444:
* @DSI_PIXEL_FORMAT_MAX:
*/
enum dsi_pixel_format {
DSI_PIXEL_FORMAT_RGB565 = 0,
DSI_PIXEL_FORMAT_RGB666,
DSI_PIXEL_FORMAT_RGB666_LOOSE,
DSI_PIXEL_FORMAT_RGB888,
DSI_PIXEL_FORMAT_RGB111,
DSI_PIXEL_FORMAT_RGB332,
DSI_PIXEL_FORMAT_RGB444,
DSI_PIXEL_FORMAT_MAX
};
/**
* enum dsi_op_mode - dsi operation mode
* @DSI_OP_VIDEO_MODE: DSI video mode operation
* @DSI_OP_CMD_MODE: DSI Command mode operation
* @DSI_OP_MODE_MAX:
*/
enum dsi_op_mode {
DSI_OP_VIDEO_MODE = 0,
DSI_OP_CMD_MODE,
DSI_OP_MODE_MAX
};
/**
* enum dsi_mode_flags - flags to signal other drm components via private flags
* @DSI_MODE_FLAG_SEAMLESS: Seamless transition requested by user
* @DSI_MODE_FLAG_DFPS: Seamless transition is DynamicFPS
* @DSI_MODE_FLAG_VBLANK_PRE_MODESET: Transition needs VBLANK before Modeset
* @DSI_MODE_FLAG_DMS: Seamless transition is dynamic mode switch
* @DSI_MODE_FLAG_VRR: Seamless transition is DynamicFPS.
* New timing values are sent from DAL.
* @DSI_MODE_FLAG_POMS:
* Seamless transition is dynamic panel operating mode switch
* @DSI_MODE_FLAG_DYN_CLK: Seamless transition is dynamic clock change
* @DSI_MODE_FLAG_DMS_FPS: Seamless fps only transition in Dynamic Mode Switch
*/
enum dsi_mode_flags {
DSI_MODE_FLAG_SEAMLESS = BIT(0),
DSI_MODE_FLAG_DFPS = BIT(1),
DSI_MODE_FLAG_VBLANK_PRE_MODESET = BIT(2),
DSI_MODE_FLAG_DMS = BIT(3),
DSI_MODE_FLAG_VRR = BIT(4),
DSI_MODE_FLAG_POMS = BIT(5),
DSI_MODE_FLAG_DYN_CLK = BIT(6),
DSI_MODE_FLAG_DMS_FPS = BIT(7),
};
/**
* enum dsi_logical_lane - dsi logical lanes
* @DSI_LOGICAL_LANE_0: Logical lane 0
* @DSI_LOGICAL_LANE_1: Logical lane 1
* @DSI_LOGICAL_LANE_2: Logical lane 2
* @DSI_LOGICAL_LANE_3: Logical lane 3
* @DSI_LOGICAL_CLOCK_LANE: Clock lane
* @DSI_LANE_MAX: Maximum lanes supported
*/
enum dsi_logical_lane {
DSI_LOGICAL_LANE_0 = 0,
DSI_LOGICAL_LANE_1,
DSI_LOGICAL_LANE_2,
DSI_LOGICAL_LANE_3,
DSI_LOGICAL_CLOCK_LANE,
DSI_LANE_MAX
};
/**
* enum dsi_data_lanes - BIT map for DSI data lanes
* This is used to identify the active DSI data lanes for
* various operations like DSI data lane enable/ULPS/clamp
* configurations.
* @DSI_DATA_LANE_0: BIT(DSI_LOGICAL_LANE_0)
* @DSI_DATA_LANE_1: BIT(DSI_LOGICAL_LANE_1)
* @DSI_DATA_LANE_2: BIT(DSI_LOGICAL_LANE_2)
* @DSI_DATA_LANE_3: BIT(DSI_LOGICAL_LANE_3)
* @DSI_CLOCK_LANE: BIT(DSI_LOGICAL_CLOCK_LANE)
*/
enum dsi_data_lanes {
DSI_DATA_LANE_0 = BIT(DSI_LOGICAL_LANE_0),
DSI_DATA_LANE_1 = BIT(DSI_LOGICAL_LANE_1),
DSI_DATA_LANE_2 = BIT(DSI_LOGICAL_LANE_2),
DSI_DATA_LANE_3 = BIT(DSI_LOGICAL_LANE_3),
DSI_CLOCK_LANE = BIT(DSI_LOGICAL_CLOCK_LANE)
};
/**
* enum dsi_phy_data_lanes - dsi physical lanes
* used for DSI logical to physical lane mapping
* @DSI_PHYSICAL_LANE_INVALID: Physical lane valid/invalid
* @DSI_PHYSICAL_LANE_0: Physical lane 0
* @DSI_PHYSICAL_LANE_1: Physical lane 1
* @DSI_PHYSICAL_LANE_2: Physical lane 2
* @DSI_PHYSICAL_LANE_3: Physical lane 3
*/
enum dsi_phy_data_lanes {
DSI_PHYSICAL_LANE_INVALID = 0,
DSI_PHYSICAL_LANE_0 = BIT(0),
DSI_PHYSICAL_LANE_1 = BIT(1),
DSI_PHYSICAL_LANE_2 = BIT(2),
DSI_PHYSICAL_LANE_3 = BIT(3)
};
enum dsi_lane_map_type_v1 {
DSI_LANE_MAP_0123,
DSI_LANE_MAP_3012,
DSI_LANE_MAP_2301,
DSI_LANE_MAP_1230,
DSI_LANE_MAP_0321,
DSI_LANE_MAP_1032,
DSI_LANE_MAP_2103,
DSI_LANE_MAP_3210,
};
/**
* lane_map: DSI logical <-> physical lane mapping
* lane_map_v1: Lane mapping for DSI controllers < v2.0
* lane_map_v2: Lane mapping for DSI controllers >= 2.0
*/
struct dsi_lane_map {
enum dsi_lane_map_type_v1 lane_map_v1;
u8 lane_map_v2[DSI_LANE_MAX - 1];
};
/**
* enum dsi_trigger_type - dsi trigger type
* @DSI_TRIGGER_NONE: No trigger.
* @DSI_TRIGGER_TE: TE trigger.
* @DSI_TRIGGER_SEOF: Start or End of frame.
* @DSI_TRIGGER_SW: Software trigger.
* @DSI_TRIGGER_SW_SEOF: Software trigger and start/end of frame.
* @DSI_TRIGGER_SW_TE: Software and TE triggers.
* @DSI_TRIGGER_MAX: Max trigger values.
*/
enum dsi_trigger_type {
DSI_TRIGGER_NONE = 0,
DSI_TRIGGER_TE,
DSI_TRIGGER_SEOF,
DSI_TRIGGER_SW,
DSI_TRIGGER_SW_SEOF,
DSI_TRIGGER_SW_TE,
DSI_TRIGGER_MAX
};
/**
* enum dsi_color_swap_mode - color swap mode
* @DSI_COLOR_SWAP_RGB:
* @DSI_COLOR_SWAP_RBG:
* @DSI_COLOR_SWAP_BGR:
* @DSI_COLOR_SWAP_BRG:
* @DSI_COLOR_SWAP_GRB:
* @DSI_COLOR_SWAP_GBR:
*/
enum dsi_color_swap_mode {
DSI_COLOR_SWAP_RGB = 0,
DSI_COLOR_SWAP_RBG,
DSI_COLOR_SWAP_BGR,
DSI_COLOR_SWAP_BRG,
DSI_COLOR_SWAP_GRB,
DSI_COLOR_SWAP_GBR
};
/**
* enum dsi_dfps_type - Dynamic FPS support type
* @DSI_DFPS_NONE: Dynamic FPS is not supported.
* @DSI_DFPS_SUSPEND_RESUME:
* @DSI_DFPS_IMMEDIATE_CLK:
* @DSI_DFPS_IMMEDIATE_HFP:
* @DSI_DFPS_IMMEDIATE_VFP:
* @DSI_DPFS_MAX:
*/
enum dsi_dfps_type {
DSI_DFPS_NONE = 0,
DSI_DFPS_SUSPEND_RESUME,
DSI_DFPS_IMMEDIATE_CLK,
DSI_DFPS_IMMEDIATE_HFP,
DSI_DFPS_IMMEDIATE_VFP,
DSI_DFPS_MAX
};
/**
* enum dsi_dyn_clk_feature_type - Dynamic clock feature support type
* @DSI_DYN_CLK_TYPE_LEGACY: Constant FPS is not supported
* @DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_HFP: Constant FPS supported with
* change in hfp
* @DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_VFP: Constant FPS supported with
* change in vfp
* @DSI_DYN_CLK_TYPE_MAX:
*/
enum dsi_dyn_clk_feature_type {
DSI_DYN_CLK_TYPE_LEGACY = 0,
DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_HFP,
DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_VFP,
DSI_DYN_CLK_TYPE_MAX
};
/**
* enum dsi_cmd_set_type - DSI command set type
* @DSI_CMD_SET_PRE_ON: Panel pre on
* @DSI_CMD_SET_ON: Panel on
* @DSI_CMD_SET_POST_ON: Panel post on
* @DSI_CMD_SET_PRE_OFF: Panel pre off
* @DSI_CMD_SET_OFF: Panel off
* @DSI_CMD_SET_POST_OFF: Panel post off
* @DSI_CMD_SET_PRE_RES_SWITCH: Pre resolution switch
* @DSI_CMD_SET_RES_SWITCH: Resolution switch
* @DSI_CMD_SET_POST_RES_SWITCH: Post resolution switch
* @DSI_CMD_SET_CMD_TO_VID_SWITCH: Cmd to video mode switch
* @DSI_CMD_SET_POST_CMD_TO_VID_SWITCH: Post cmd to vid switch
* @DSI_CMD_SET_VID_TO_CMD_SWITCH: Video to cmd mode switch
* @DSI_CMD_SET_POST_VID_TO_CMD_SWITCH: Post vid to cmd switch
* @DSI_CMD_SET_PANEL_STATUS: Panel status
* @DSI_CMD_SET_LP1: Low power mode 1
* @DSI_CMD_SET_LP2: Low power mode 2
* @DSI_CMD_SET_NOLP: Low power mode disable
* @DSI_CMD_SET_PPS: DSC PPS command
* @DSI_CMD_SET_ROI: Panel ROI update
* @DSI_CMD_SET_TIMING_SWITCH: Timing switch
* @DSI_CMD_SET_POST_TIMING_SWITCH: Post timing switch
* @DSI_CMD_SET_QSYNC_ON Enable qsync mode
* @DSI_CMD_SET_QSYNC_OFF Disable qsync mode
* @DSI_CMD_SET_MAX
*/
enum dsi_cmd_set_type {
DSI_CMD_SET_PRE_ON = 0,
DSI_CMD_SET_ON,
DSI_CMD_SET_POST_ON,
DSI_CMD_SET_PRE_OFF,
DSI_CMD_SET_OFF,
DSI_CMD_SET_POST_OFF,
DSI_CMD_SET_PRE_RES_SWITCH,
DSI_CMD_SET_RES_SWITCH,
DSI_CMD_SET_POST_RES_SWITCH,
DSI_CMD_SET_CMD_TO_VID_SWITCH,
DSI_CMD_SET_POST_CMD_TO_VID_SWITCH,
DSI_CMD_SET_VID_TO_CMD_SWITCH,
DSI_CMD_SET_POST_VID_TO_CMD_SWITCH,
DSI_CMD_SET_PANEL_STATUS,
DSI_CMD_SET_LP1,
DSI_CMD_SET_LP2,
DSI_CMD_SET_NOLP,
DSI_CMD_SET_PPS,
DSI_CMD_SET_ROI,
DSI_CMD_SET_TIMING_SWITCH,
DSI_CMD_SET_POST_TIMING_SWITCH,
DSI_CMD_SET_QSYNC_ON,
DSI_CMD_SET_QSYNC_OFF,
DSI_CMD_SET_MAX
};
/**
* enum dsi_cmd_set_state - command set state
* @DSI_CMD_SET_STATE_LP: dsi low power mode
* @DSI_CMD_SET_STATE_HS: dsi high speed mode
* @DSI_CMD_SET_STATE_MAX
*/
enum dsi_cmd_set_state {
DSI_CMD_SET_STATE_LP = 0,
DSI_CMD_SET_STATE_HS,
DSI_CMD_SET_STATE_MAX
};
/**
* enum dsi_clk_gate_type - Type of clock to be gated.
* @PIXEL_CLK: DSI pixel clock.
* @BYTE_CLK: DSI byte clock.
* @DSI_PHY: DSI PHY.
* @DSI_CLK_ALL: All available DSI clocks
* @DSI_CLK_NONE: None of the clocks should be gated
*/
enum dsi_clk_gate_type {
PIXEL_CLK = 1,
BYTE_CLK = 2,
DSI_PHY = 4,
DSI_CLK_ALL = (PIXEL_CLK | BYTE_CLK | DSI_PHY),
DSI_CLK_NONE = 8,
};
/**
* enum dsi_phy_type - DSI phy types
* @DSI_PHY_TYPE_DPHY:
* @DSI_PHY_TYPE_CPHY:
*/
enum dsi_phy_type {
DSI_PHY_TYPE_DPHY,
DSI_PHY_TYPE_CPHY
};
/**
* enum dsi_te_mode - dsi te source
* @DSI_TE_ON_DATA_LINK: TE read from DSI link
* @DSI_TE_ON_EXT_PIN: TE signal on an external GPIO
*/
enum dsi_te_mode {
DSI_TE_ON_DATA_LINK = 0,
DSI_TE_ON_EXT_PIN,
};
/**
* enum dsi_video_traffic_mode - video mode pixel transmission type
* @DSI_VIDEO_TRAFFIC_SYNC_PULSES: Non-burst mode with sync pulses.
* @DSI_VIDEO_TRAFFIC_SYNC_START_EVENTS: Non-burst mode with sync start events.
* @DSI_VIDEO_TRAFFIC_BURST_MODE: Burst mode using sync start events.
*/
enum dsi_video_traffic_mode {
DSI_VIDEO_TRAFFIC_SYNC_PULSES = 0,
DSI_VIDEO_TRAFFIC_SYNC_START_EVENTS,
DSI_VIDEO_TRAFFIC_BURST_MODE,
};
/**
* struct dsi_cmd_desc - description of a dsi command
* @msg: dsi mipi msg packet
* @last_command: indicates whether the cmd is the last one to send
* @post_wait_ms: post wait duration
*/
struct dsi_cmd_desc {
struct mipi_dsi_msg msg;
bool last_command;
u32 post_wait_ms;
};
/**
* struct dsi_panel_cmd_set - command set of the panel
* @type: type of the command
* @state: state of the command
* @count: number of cmds
* @ctrl_idx: index of the dsi control
* @cmds: arry of cmds
*/
struct dsi_panel_cmd_set {
enum dsi_cmd_set_type type;
enum dsi_cmd_set_state state;
u32 count;
u32 ctrl_idx;
struct dsi_cmd_desc *cmds;
};
/**
* struct dsi_mode_info - video mode information dsi frame
* @h_active: Active width of one frame in pixels.
* @h_back_porch: Horizontal back porch in pixels.
* @h_sync_width: HSYNC width in pixels.
* @h_front_porch: Horizontal fron porch in pixels.
* @h_skew:
* @h_sync_polarity: Polarity of HSYNC (false is active low).
* @v_active: Active height of one frame in lines.
* @v_back_porch: Vertical back porch in lines.
* @v_sync_width: VSYNC width in lines.
* @v_front_porch: Vertical front porch in lines.
* @v_sync_polarity: Polarity of VSYNC (false is active low).
* @refresh_rate: Refresh rate in Hz.
* @clk_rate_hz: DSI bit clock rate per lane in Hz.
* @min_dsi_clk_hz: Min DSI bit clock to transfer in vsync time.
* @mdp_transfer_time_us: Specifies the mdp transfer time for command mode
* panels in microseconds.
* @dsi_transfer_time_us: Specifies dsi transfer time for command mode.
* @dsc_enabled: DSC compression enabled.
* @vdc_enabled: VDC compression enabled.
* @dsc: DSC compression configuration.
* @vdc: VDC compression configuration.
* @pclk_scale: pclk scale factor, target bpp to source bpp
* @roi_caps: Panel ROI capabilities.
*/
struct dsi_mode_info {
u32 h_active;
u32 h_back_porch;
u32 h_sync_width;
u32 h_front_porch;
u32 h_skew;
bool h_sync_polarity;
u32 v_active;
u32 v_back_porch;
u32 v_sync_width;
u32 v_front_porch;
bool v_sync_polarity;
u32 refresh_rate;
u64 clk_rate_hz;
u64 min_dsi_clk_hz;
u32 mdp_transfer_time_us;
u32 dsi_transfer_time_us;
bool dsc_enabled;
bool vdc_enabled;
struct msm_display_dsc_info *dsc;
struct msm_display_vdc_info *vdc;
struct msm_ratio pclk_scale;
struct msm_roi_caps roi_caps;
};
/**
* struct dsi_split_link_config - Split Link Configuration
* @split_link_enabled: Split Link Enabled.
* @num_sublinks: Number of sublinks.
* @lanes_per_sublink: Number of lanes per sublink.
*/
struct dsi_split_link_config {
bool split_link_enabled;
u32 num_sublinks;
u32 lanes_per_sublink;
};
/**
* struct dsi_host_common_cfg - Host configuration common to video and cmd mode
* @dst_format: Destination pixel format.
* @data_lanes: Physical data lanes to be enabled.
* @num_data_lanes: Number of physical data lanes.
* @bpp: Number of bits per pixel.
* @en_crc_check: Enable CRC checks.
* @en_ecc_check: Enable ECC checks.
* @te_mode: Source for TE signalling.
* @mdp_cmd_trigger: MDP frame update trigger for command mode.
* @dma_cmd_trigger: Command DMA trigger.
* @cmd_trigger_stream: Command mode stream to trigger.
* @swap_mode: DSI color swap mode.
* @bit_swap_read: Is red color bit swapped.
* @bit_swap_green: Is green color bit swapped.
* @bit_swap_blue: Is blue color bit swapped.
* @t_clk_post: Number of byte clock cycles that the transmitter shall
* continue sending after last data lane has transitioned
* to LP mode.
* @t_clk_pre: Number of byte clock cycles that the high spped clock
* shall be driven prior to data lane transitions from LP
* to HS mode.
* @ignore_rx_eot: Ignore Rx EOT packets if set to true.
* @append_tx_eot: Append EOT packets for forward transmissions if set to
* true.
* @ext_bridge_mode: External bridge is connected.
* @force_hs_clk_lane: Send continuous clock to the panel.
* @phy_type: DPHY/CPHY is enabled for this panel.
* @dsi_split_link_config: Split Link Configuration.
* @byte_intf_clk_div: Determines the factor for calculating byte intf clock.
* @dma_sched_line: Line at which dma command gets triggered. In case of
* video mode it is the line number after vactive and for
* cmd it points to the line after TE.
* @dma_sched_window: Determines the width of the window during the
* DSI command will be sent by the HW.
*/
struct dsi_host_common_cfg {
enum dsi_pixel_format dst_format;
enum dsi_data_lanes data_lanes;
u8 num_data_lanes;
u8 bpp;
bool en_crc_check;
bool en_ecc_check;
enum dsi_te_mode te_mode;
enum dsi_trigger_type mdp_cmd_trigger;
enum dsi_trigger_type dma_cmd_trigger;
u32 cmd_trigger_stream;
enum dsi_color_swap_mode swap_mode;
bool bit_swap_red;
bool bit_swap_green;
bool bit_swap_blue;
u32 t_clk_post;
u32 t_clk_pre;
bool ignore_rx_eot;
bool append_tx_eot;
bool ext_bridge_mode;
bool force_hs_clk_lane;
enum dsi_phy_type phy_type;
struct dsi_split_link_config split_link;
u32 byte_intf_clk_div;
u32 dma_sched_line;
u32 dma_sched_window;
};
/**
* struct dsi_video_engine_cfg - DSI video engine configuration
* @last_line_interleave_en: Allow command mode op interleaved on last line of
* video stream.
* @pulse_mode_hsa_he: Send HSA and HE following VS/VE packet if set to
* true.
* @hfp_lp11_en: Enter low power stop mode (LP-11) during HFP.
* @hbp_lp11_en: Enter low power stop mode (LP-11) during HBP.
* @hsa_lp11_en: Enter low power stop mode (LP-11) during HSA.
* @eof_bllp_lp11_en: Enter low power stop mode (LP-11) during BLLP of
* last line of a frame.
* @bllp_lp11_en: Enter low power stop mode (LP-11) during BLLP.
* @traffic_mode: Traffic mode for video stream.
* @vc_id: Virtual channel identifier.
*/
struct dsi_video_engine_cfg {
bool last_line_interleave_en;
bool pulse_mode_hsa_he;
bool hfp_lp11_en;
bool hbp_lp11_en;
bool hsa_lp11_en;
bool eof_bllp_lp11_en;
bool bllp_lp11_en;
enum dsi_video_traffic_mode traffic_mode;
u32 vc_id;
};
/**
* struct dsi_cmd_engine_cfg - DSI command engine configuration
* @max_cmd_packets_interleave Maximum number of command mode RGB packets to
* send with in one horizontal blanking period
* of the video mode frame.
* @wr_mem_start: DCS command for write_memory_start.
* @wr_mem_continue: DCS command for write_memory_continue.
* @insert_dcs_command: Insert DCS command as first byte of payload
* of the pixel data.
* @mdp_idle_ctrl_en: Enable idle insertion between command mode mdp packets.
* @mdp_idle_ctrl_len: No. of dsi pclk cycles of idle time to insert between
* command mode mdp packets.
*/
struct dsi_cmd_engine_cfg {
u32 max_cmd_packets_interleave;
u32 wr_mem_start;
u32 wr_mem_continue;
bool insert_dcs_command;
bool mdp_idle_ctrl_en;
u32 mdp_idle_ctrl_len;
};
/**
* struct dsi_host_config - DSI host configuration parameters.
* @panel_mode: Operation mode for panel (video or cmd mode).
* @common_config: Host configuration common to both Video and Cmd mode.
* @video_engine: Video engine configuration if panel is in video mode.
* @cmd_engine: Cmd engine configuration if panel is in cmd mode.
* @esc_clk_rate_hz: Esc clock frequency in Hz.
* @bit_clk_rate_hz: Bit clock frequency in Hz.
* @bit_clk_rate_hz_override: DSI bit clk rate override from dt/sysfs.
* @video_timing: Video timing information of a frame.
* @lane_map: Mapping between logical and physical lanes.
*/
struct dsi_host_config {
enum dsi_op_mode panel_mode;
struct dsi_host_common_cfg common_config;
union {
struct dsi_video_engine_cfg video_engine;
struct dsi_cmd_engine_cfg cmd_engine;
} u;
u64 esc_clk_rate_hz;
u64 bit_clk_rate_hz;
u64 bit_clk_rate_hz_override;
struct dsi_mode_info video_timing;
struct dsi_lane_map lane_map;
};
/**
* struct dsi_display_mode_priv_info - private mode info that will be attached
* with each drm mode
* @cmd_sets: Command sets of the mode
* @phy_timing_val: Phy timing values
* @phy_timing_len: Phy timing array length
* @panel_jitter: Panel jitter for RSC backoff
* @panel_prefill_lines: Panel prefill lines for RSC
* @mdp_transfer_time_us: Specifies the mdp transfer time for command mode
* panels in microseconds.
* @dsi_transfer_time_us: Specifies the dsi transfer time for cmd panels.
* @clk_rate_hz: DSI bit clock per lane in hz.
* @min_dsi_clk_hz: Min dsi clk per lane to transfer frame in vsync time.
* @topology: Topology selected for the panel
* @dsc: DSC compression info
* @vdc: VDC compression info
* @dsc_enabled: DSC compression enabled
* @vdc_enabled: VDC compression enabled
* @pclk_scale: pclk scale factor, target bpp to source bpp
* @roi_caps: Panel ROI capabilities
* @widebus_support 48 bit wide data bus is supported by hw
* @allowed_mode_switch: BIT mask to mark allowed mode switches
*/
struct dsi_display_mode_priv_info {
struct dsi_panel_cmd_set cmd_sets[DSI_CMD_SET_MAX];
u32 *phy_timing_val;
u32 phy_timing_len;
u32 panel_jitter_numer;
u32 panel_jitter_denom;
u32 panel_prefill_lines;
u32 mdp_transfer_time_us;
u32 dsi_transfer_time_us;
u64 clk_rate_hz;
u64 min_dsi_clk_hz;
struct msm_display_topology topology;
struct msm_display_dsc_info dsc;
struct msm_display_vdc_info vdc;
bool dsc_enabled;
bool vdc_enabled;
struct msm_ratio pclk_scale;
struct msm_roi_caps roi_caps;
bool widebus_support;
u32 allowed_mode_switch;
};
/**
* struct dsi_display_mode - specifies mode for dsi display
* @timing: Timing parameters for the panel.
* @pixel_clk_khz: Pixel clock in Khz.
* @dsi_mode_flags: Flags to signal other drm components via private flags
* @panel_mode: Panel mode
* @is_preferred: Is mode preferred
* @priv_info: Mode private info
*/
struct dsi_display_mode {
struct dsi_mode_info timing;
u32 pixel_clk_khz;
u32 dsi_mode_flags;
enum dsi_op_mode panel_mode;
bool is_preferred;
struct dsi_display_mode_priv_info *priv_info;
};
/**
* struct dsi_rect - dsi rectangle representation
* Note: sde_rect is also using u16, this must be maintained for memcpy
*/
struct dsi_rect {
u16 x;
u16 y;
u16 w;
u16 h;
};
/**
* dsi_rect_intersect - intersect two rectangles
* @r1: first rectangle
* @r2: scissor rectangle
* @result: result rectangle, all 0's on no intersection found
*/
void dsi_rect_intersect(const struct dsi_rect *r1,
const struct dsi_rect *r2,
struct dsi_rect *result);
/**
* dsi_rect_is_equal - compares two rects
* @r1: rect value to compare
* @r2: rect value to compare
*
* Returns true if the rects are same
*/
static inline bool dsi_rect_is_equal(struct dsi_rect *r1,
struct dsi_rect *r2)
{
return r1->x == r2->x && r1->y == r2->y && r1->w == r2->w &&
r1->h == r2->h;
}
struct dsi_event_cb_info {
uint32_t event_idx;
void *event_usr_ptr;
int (*event_cb)(void *event_usr_ptr,
uint32_t event_idx, uint32_t instance_idx,
uint32_t data0, uint32_t data1,
uint32_t data2, uint32_t data3);
};
/**
* enum dsi_error_status - various dsi errors
* @DSI_FIFO_OVERFLOW: DSI FIFO Overflow error
* @DSI_FIFO_UNDERFLOW: DSI FIFO Underflow error
* @DSI_LP_Rx_TIMEOUT: DSI LP/RX Timeout error
* @DSI_PLL_UNLOCK_ERR: DSI PLL unlock error
*/
enum dsi_error_status {
DSI_FIFO_OVERFLOW = 1,
DSI_FIFO_UNDERFLOW,
DSI_LP_Rx_TIMEOUT,
DSI_PLL_UNLOCK_ERR,
DSI_ERR_INTR_ALL,
};
/* structure containing the delays required for dynamic clk */
struct dsi_dyn_clk_delay {
u32 pipe_delay;
u32 pipe_delay2;
u32 pll_delay;
};
/* dynamic refresh control bits */
enum dsi_dyn_clk_control_bits {
DYN_REFRESH_INTF_SEL = 1,
DYN_REFRESH_SYNC_MODE,
DYN_REFRESH_SW_TRIGGER,
DYN_REFRESH_SWI_CTRL,
};
/* convert dsi pixel format into bits per pixel */
static inline int dsi_pixel_format_to_bpp(enum dsi_pixel_format fmt)
{
switch (fmt) {
case DSI_PIXEL_FORMAT_RGB888:
case DSI_PIXEL_FORMAT_MAX:
return 24;
case DSI_PIXEL_FORMAT_RGB666:
case DSI_PIXEL_FORMAT_RGB666_LOOSE:
return 18;
case DSI_PIXEL_FORMAT_RGB565:
return 16;
case DSI_PIXEL_FORMAT_RGB111:
return 3;
case DSI_PIXEL_FORMAT_RGB332:
return 8;
case DSI_PIXEL_FORMAT_RGB444:
return 12;
}
return 24;
}
static inline u64 dsi_h_active_dce(struct dsi_mode_info *mode)
{
u64 h_active = 0;
if (mode->dsc_enabled && mode->dsc)
h_active = mode->dsc->pclk_per_line;
else if (mode->vdc_enabled && mode->vdc)
h_active = mode->vdc->pclk_per_line;
else
h_active = mode->h_active;
return h_active;
}
static inline u64 dsi_h_total_dce(struct dsi_mode_info *mode)
{
u64 h_total = dsi_h_active_dce(mode);
h_total += mode->h_back_porch + mode->h_front_porch +
mode->h_sync_width;
return h_total;
}
#endif /* _DSI_DEFS_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,802 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
*/
#ifndef _DSI_DISPLAY_H_
#define _DSI_DISPLAY_H_
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/debugfs.h>
#include <linux/of_device.h>
#include <linux/firmware.h>
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include "msm_drv.h"
#include "dsi_defs.h"
#include "dsi_ctrl.h"
#include "dsi_phy.h"
#include "dsi_panel.h"
#define MAX_DSI_CTRLS_PER_DISPLAY 2
#define DSI_CLIENT_NAME_SIZE 20
#define MAX_CMDLINE_PARAM_LEN 512
#define MAX_CMD_PAYLOAD_SIZE 256
/*
* DSI Validate Mode modifiers
* @DSI_VALIDATE_FLAG_ALLOW_ADJUST: Allow mode validation to also do fixup
*/
#define DSI_VALIDATE_FLAG_ALLOW_ADJUST 0x1
/**
* enum dsi_display_selection_type - enumerates DSI display selection types
* @DSI_PRIMARY: primary DSI display selected from module parameter
* @DSI_SECONDARY: Secondary DSI display selected from module parameter
* @MAX_DSI_ACTIVE_DISPLAY: Maximum acive displays that can be selected
*/
enum dsi_display_selection_type {
DSI_PRIMARY = 0,
DSI_SECONDARY,
MAX_DSI_ACTIVE_DISPLAY,
};
/**
* enum dsi_display_type - enumerates DSI display types
* @DSI_DISPLAY_SINGLE: A panel connected on a single DSI interface.
* @DSI_DISPLAY_EXT_BRIDGE: A bridge is connected between panel and DSI host.
* It utilizes a single DSI interface.
* @DSI_DISPLAY_SPLIT: A panel that utilizes more than one DSI
* interfaces.
* @DSI_DISPLAY_SPLIT_EXT_BRIDGE: A bridge is present between panel and DSI
* host. It utilizes more than one DSI interface.
*/
enum dsi_display_type {
DSI_DISPLAY_SINGLE = 0,
DSI_DISPLAY_EXT_BRIDGE,
DSI_DISPLAY_SPLIT,
DSI_DISPLAY_SPLIT_EXT_BRIDGE,
DSI_DISPLAY_MAX,
};
/**
* struct dsi_display_ctrl - dsi ctrl/phy information for the display
* @ctrl: Handle to the DSI controller device.
* @ctrl_of_node: pHandle to the DSI controller device.
* @dsi_ctrl_idx: DSI controller instance id.
* @power_state: Current power state of the DSI controller.
* @phy: Handle to the DSI PHY device.
* @phy_of_node: pHandle to the DSI PHY device.
* @phy_enabled: PHY power status.
*/
struct dsi_display_ctrl {
/* controller info */
struct dsi_ctrl *ctrl;
struct device_node *ctrl_of_node;
u32 dsi_ctrl_idx;
enum dsi_power_state power_state;
/* phy info */
struct msm_dsi_phy *phy;
struct device_node *phy_of_node;
bool phy_enabled;
};
/**
* struct dsi_display_boot_param - defines DSI boot display selection
* @name:Name of DSI display selected as a boot param.
* @boot_disp_en:bool to indicate dtsi availability of display node
* @is_primary:bool to indicate whether current display is primary display
* @length:length of DSI display.
* @cmdline_topology: Display topology shared from kernel command line.
*/
struct dsi_display_boot_param {
char name[MAX_CMDLINE_PARAM_LEN];
char *boot_param;
bool boot_disp_en;
int length;
struct device_node *node;
int cmdline_topology;
void *disp;
};
/**
* struct dsi_display_clk_info - dsi display clock source information
* @src_clks: Source clocks for DSI display.
* @mux_clks: Mux clocks used for DFPS.
* @shadow_clks: Used for D-phy clock switch.
* @shadow_cphy_clks: Used for C-phy clock switch.
*/
struct dsi_display_clk_info {
struct dsi_clk_link_set src_clks;
struct dsi_clk_link_set mux_clks;
struct dsi_clk_link_set cphy_clks;
struct dsi_clk_link_set shadow_clks;
struct dsi_clk_link_set shadow_cphy_clks;
};
/**
* struct dsi_display_ext_bridge - dsi display external bridge information
* @display: Pointer of DSI display.
* @node_of: Bridge node created from bridge driver.
* @bridge: Bridge created from bridge driver
* @orig_funcs: Bridge function from bridge driver (split mode only)
* @bridge_funcs: Overridden function from bridge driver (split mode only)
*/
struct dsi_display_ext_bridge {
void *display;
struct device_node *node_of;
struct drm_bridge *bridge;
const struct drm_bridge_funcs *orig_funcs;
struct drm_bridge_funcs bridge_funcs;
};
/**
* struct dsi_display - dsi display information
* @pdev: Pointer to platform device.
* @drm_dev: DRM device associated with the display.
* @drm_conn: Pointer to DRM connector associated with the display
* @ext_conn: Pointer to external connector attached to DSI connector
* @name: Name of the display.
* @display_type: Display type as defined in device tree.
* @list: List pointer.
* @is_active: Is display active.
* @is_cont_splash_enabled: Is continuous splash enabled
* @sw_te_using_wd: Is software te enabled
* @display_lock: Mutex for dsi_display interface.
* @disp_te_gpio: GPIO for panel TE interrupt.
* @is_te_irq_enabled:bool to specify whether TE interrupt is enabled.
* @esd_te_gate: completion gate to signal TE interrupt.
* @ctrl_count: Number of DSI interfaces required by panel.
* @ctrl: Controller information for DSI display.
* @panel: Handle to DSI panel.
* @panel_node: pHandle to DSI panel actually in use.
* @ext_bridge: External bridge information for DSI display.
* @ext_bridge_cnt: Number of external bridges
* @modes: Array of probed DSI modes
* @type: DSI display type.
* @clk_master_idx: The master controller for controlling clocks. This is an
* index into the ctrl[MAX_DSI_CTRLS_PER_DISPLAY] array.
* @cmd_master_idx: The master controller for sending DSI commands to panel.
* @video_master_idx: The master controller for enabling video engine.
* @cached_clk_rate: The cached DSI clock rate set dynamically by sysfs.
* @clkrate_change_pending: Flag indicating the pending DSI clock re-enabling.
* @clock_info: Clock sourcing for DSI display.
* @config: DSI host configuration information.
* @lane_map: Lane mapping between DSI host and Panel.
* @cmdline_topology: Display topology shared from kernel command line.
* @cmdline_timing: Display timing shared from kernel command line.
* @is_tpg_enabled: TPG state.
* @poms_pending; Flag indicating the pending panel operating mode switch.
* @ulps_enabled: ulps state.
* @clamp_enabled: clamp state.
* @phy_idle_power_off: PHY power state.
* @host: DRM MIPI DSI Host.
* @bridge: Pointer to DRM bridge object.
* @cmd_engine_refcount: Reference count enforcing single instance of cmd eng
* @clk_mngr: DSI clock manager.
* @dsi_clk_handle: DSI clock handle.
* @mdp_clk_handle: MDP clock handle.
* @root: Debugfs root directory
* @misr_enable Frame MISR enable/disable
* @misr_frame_count Number of frames to accumulate the MISR value
* @esd_trigger field indicating ESD trigger through debugfs
* @poms_te_work POMS delayed work for disabling panel TE
* @te_source vsync source pin information
* @clk_gating_config Clocks for which clock gating needs to be enabled
* @queue_cmd_waits Indicates if wait for dma commands done has to be queued.
* @dma_cmd_workq: Pointer to the workqueue of DMA command transfer done
* wait sequence.
* @is_active: status of the display
* @trusted_vm_env: Set to true, it the executing VM is Trusted VM.
* Set to false, otherwise.
* @hw_ownership: Indicates if VM owns the hardware resources.
* @tx_cmd_buf_ndx: Index to the DSI debugfs TX CMD buffer.
* @cmd_set: Debugfs TX cmd set.
* @enabled: Boolean to indicate display enabled.
*/
struct dsi_display {
struct platform_device *pdev;
struct drm_device *drm_dev;
struct drm_connector *drm_conn;
struct drm_connector *ext_conn;
const char *name;
const char *display_type;
struct list_head list;
bool is_cont_splash_enabled;
bool sw_te_using_wd;
struct mutex display_lock;
int disp_te_gpio;
bool is_te_irq_enabled;
struct completion esd_te_gate;
u32 ctrl_count;
struct dsi_display_ctrl ctrl[MAX_DSI_CTRLS_PER_DISPLAY];
/* panel info */
struct dsi_panel *panel;
struct device_node *panel_node;
struct device_node *parser_node;
/* external bridge */
struct dsi_display_ext_bridge ext_bridge[MAX_DSI_CTRLS_PER_DISPLAY];
u32 ext_bridge_cnt;
struct dsi_display_mode *modes;
enum dsi_display_type type;
u32 clk_master_idx;
u32 cmd_master_idx;
u32 video_master_idx;
/* dynamic DSI clock info*/
u32 cached_clk_rate;
atomic_t clkrate_change_pending;
struct dsi_display_clk_info clock_info;
struct dsi_host_config config;
struct dsi_lane_map lane_map;
int cmdline_topology;
int cmdline_timing;
bool is_tpg_enabled;
bool poms_pending;
bool ulps_enabled;
bool clamp_enabled;
bool phy_idle_power_off;
struct drm_gem_object *tx_cmd_buf;
u32 cmd_buffer_size;
u64 cmd_buffer_iova;
void *vaddr;
struct msm_gem_address_space *aspace;
struct mipi_dsi_host host;
struct dsi_bridge *bridge;
u32 cmd_engine_refcount;
void *clk_mngr;
void *dsi_clk_handle;
void *mdp_clk_handle;
/* DEBUG FS */
struct dentry *root;
bool misr_enable;
u32 misr_frame_count;
u32 esd_trigger;
/* multiple dsi error handlers */
struct workqueue_struct *err_workq;
struct work_struct fifo_underflow_work;
struct work_struct fifo_overflow_work;
struct work_struct lp_rx_timeout_work;
/* panel te delayed work */
struct delayed_work poms_te_work;
/* firmware panel data */
const struct firmware *fw;
void *parser;
struct dsi_display_boot_param *boot_disp;
u32 te_source;
u32 clk_gating_config;
bool queue_cmd_waits;
struct workqueue_struct *dma_cmd_workq;
/* panel id of the display */
u64 panel_id;
bool is_active;
bool trusted_vm_env;
bool hw_ownership;
int tx_cmd_buf_ndx;
struct dsi_panel_cmd_set cmd_set;
bool enabled;
};
int dsi_display_dev_probe(struct platform_device *pdev);
int dsi_display_dev_remove(struct platform_device *pdev);
/**
* dsi_display_get_num_of_displays() - returns number of display devices
* supported.
*
* Return: number of displays.
*/
int dsi_display_get_num_of_displays(void);
/**
* dsi_display_get_active_displays - returns pointers for active display devices
* @display_array: Pointer to display array to be filled
* @max_display_count: Size of display_array
* @Returns: Number of display entries filled
*/
int dsi_display_get_active_displays(void **display_array,
u32 max_display_count);
/**
* dsi_display_get_display_by_name()- finds display by name
* @name: name of the display.
*
* Return: handle to the display or error code.
*/
struct dsi_display *dsi_display_get_display_by_name(const char *name);
/**
* dsi_display_set_active_state() - sets the state of the display
* @display: Handle to display.
* @is_active: state
*/
void dsi_display_set_active_state(struct dsi_display *display, bool is_active);
/**
* dsi_display_drm_bridge_init() - initializes DRM bridge object for DSI
* @display: Handle to the display.
* @encoder: Pointer to the encoder object which is connected to the
* display.
*
* Return: error code.
*/
int dsi_display_drm_bridge_init(struct dsi_display *display,
struct drm_encoder *enc);
/**
* dsi_display_drm_bridge_deinit() - destroys DRM bridge for the display
* @display: Handle to the display.
*
* Return: error code.
*/
int dsi_display_drm_bridge_deinit(struct dsi_display *display);
/**
* dsi_display_drm_ext_bridge_init() - initializes DRM bridge for ext bridge
* @display: Handle to the display.
* @enc: Pointer to the encoder object which is connected to the
* display.
* @connector: Pointer to the connector object which is connected to
* the display.
*
* Return: error code.
*/
int dsi_display_drm_ext_bridge_init(struct dsi_display *display,
struct drm_encoder *enc, struct drm_connector *connector);
/**
* dsi_display_get_info() - returns the display properties
* @connector: Pointer to drm connector structure
* @info: Pointer to the structure where info is stored.
* @disp: Handle to the display.
*
* Return: error code.
*/
int dsi_display_get_info(struct drm_connector *connector,
struct msm_display_info *info, void *disp);
/**
* dsi_display_get_mode_count() - get number of modes supported by the display
* @display: Handle to display.
* @count: Number of modes supported
*
* Return: error code.
*/
int dsi_display_get_mode_count(struct dsi_display *display, u32 *count);
/**
* dsi_display_get_modes() - get modes supported by display
* @display: Handle to display.
* @modes; Output param, list of DSI modes. Number of modes matches
* count got from display->panel->num_display_modes;
*
* Return: error code.
*/
int dsi_display_get_modes(struct dsi_display *display,
struct dsi_display_mode **modes);
/**
* dsi_display_put_mode() - free up mode created for the display
* @display: Handle to display.
* @mode: Display mode to be freed up
*
* Return: error code.
*/
void dsi_display_put_mode(struct dsi_display *display,
struct dsi_display_mode *mode);
/**
* dsi_display_get_default_lms() - retrieve max number of lms used
* for dsi display by traversing through all topologies
* @display: Handle to display.
* @num_lm: Number of LMs used
*
* Return: error code.
*/
int dsi_display_get_default_lms(void *dsi_display, u32 *num_lm);
/**
* dsi_display_get_qsync_min_fps() - get qsync min fps for given fps
* @display: Handle to display.
* @mode_fps: Fps value of current mode
*
* Return: error code.
*/
int dsi_display_get_qsync_min_fps(void *dsi_display, u32 mode_fps);
/**
* dsi_conn_get_lm_from_mode() - retrieves LM count from dsi mode priv info
* @display: Handle to display.
* @mode: Pointer to DRM mode structure
*
* Return: LM count from dsi panel topology
*/
int dsi_conn_get_lm_from_mode(void *dsi_display, const struct drm_display_mode *mode);
/**
* dsi_display_find_mode() - retrieve cached DSI mode given relevant params
* @display: Handle to display.
* @cmp: Mode to use as comparison to find original
* @out_mode: Output parameter, pointer to retrieved mode
*
* Return: error code.
*/
int dsi_display_find_mode(struct dsi_display *display,
const struct dsi_display_mode *cmp,
struct dsi_display_mode **out_mode);
/**
* dsi_display_validate_mode() - validates if mode is supported by display
* @display: Handle to display.
* @mode: Mode to be validated.
* @flags: Modifier flags.
*
* Return: 0 if supported or error code.
*/
int dsi_display_validate_mode(struct dsi_display *display,
struct dsi_display_mode *mode,
u32 flags);
/**
* dsi_display_validate_mode_change() - validates mode if variable refresh case
* or dynamic clk change case
* @display: Handle to display.
* @mode: Mode to be validated..
*
* Return: 0 if error code.
*/
int dsi_display_validate_mode_change(struct dsi_display *display,
struct dsi_display_mode *cur_dsi_mode,
struct dsi_display_mode *mode);
/**
* dsi_display_set_mode() - Set mode on the display.
* @display: Handle to display.
* @mode: mode to be set.
* @flags: Modifier flags.
*
* Return: error code.
*/
int dsi_display_set_mode(struct dsi_display *display,
struct dsi_display_mode *mode,
u32 flags);
/**
* dsi_display_prepare() - prepare display
* @display: Handle to display.
*
* Prepare will perform power up sequences for the host and panel hardware.
* Power and clock resources might be turned on (depending on the panel mode).
* The video engine is not enabled.
*
* Return: error code.
*/
int dsi_display_prepare(struct dsi_display *display);
/**
* dsi_display_splash_res_cleanup() - cleanup for continuous splash
* @display: Pointer to dsi display
* Returns: Zero on success
*/
int dsi_display_splash_res_cleanup(struct dsi_display *display);
/**
* dsi_display_config_ctrl_for_cont_splash()- Enable engine modes for DSI
* controller during continuous splash
* @display: Handle to DSI display
*
* Return: returns error code
*/
int dsi_display_config_ctrl_for_cont_splash(struct dsi_display *display);
/**
* dsi_display_enable() - enable display
* @display: Handle to display.
*
* Enable will turn on the host engine and the panel. At the end of the enable
* function, Host and panel hardware are ready to accept pixel data from
* upstream.
*
* Return: error code.
*/
int dsi_display_enable(struct dsi_display *display);
/**
* dsi_display_post_enable() - perform post enable operations.
* @display: Handle to display.
*
* Some panels might require some commands to be sent after pixel data
* transmission has started. Such commands are sent as part of the post_enable
* function.
*
* Return: error code.
*/
int dsi_display_post_enable(struct dsi_display *display);
/**
* dsi_display_pre_disable() - perform pre disable operations.
* @display: Handle to display.
*
* If a panel requires commands to be sent before pixel data transmission is
* stopped, those can be sent as part of pre_disable.
*
* Return: error code.
*/
int dsi_display_pre_disable(struct dsi_display *display);
/**
* dsi_display_disable() - disable panel and host hardware.
* @display: Handle to display.
*
* Disable host and panel hardware and pixel data transmission can not continue.
*
* Return: error code.
*/
int dsi_display_disable(struct dsi_display *display);
/**
* dsi_pre_clkoff_cb() - Callback before clock is turned off
* @priv: private data pointer.
* @clk_type: clock which is being turned on.
* @l_type: specifies if the clock is HS or LP type. Valid only for link clocks.
* @new_state: next state for the clock.
*
* @return: error code.
*/
int dsi_pre_clkoff_cb(void *priv, enum dsi_clk_type clk_type,
enum dsi_lclk_type l_type,
enum dsi_clk_state new_state);
/**
* dsi_display_update_pps() - update PPS buffer.
* @pps_cmd: PPS buffer.
* @display: Handle to display.
*
* Copies new PPS buffer into display structure.
*
* Return: error code.
*/
int dsi_display_update_pps(char *pps_cmd, void *display);
/**
* dsi_post_clkoff_cb() - Callback after clock is turned off
* @priv: private data pointer.
* @clk_type: clock which is being turned on.
* @l_type: specifies if the clock is HS or LP type. Valid only for link clocks.
* @curr_state: current state for the clock.
*
* @return: error code.
*/
int dsi_post_clkoff_cb(void *priv, enum dsi_clk_type clk_type,
enum dsi_lclk_type l_type,
enum dsi_clk_state curr_state);
/**
* dsi_post_clkon_cb() - Callback after clock is turned on
* @priv: private data pointer.
* @clk_type: clock which is being turned on.
* @l_type: specifies if the clock is HS or LP type. Valid only for link clocks.
* @curr_state: current state for the clock.
*
* @return: error code.
*/
int dsi_post_clkon_cb(void *priv, enum dsi_clk_type clk_type,
enum dsi_lclk_type l_type,
enum dsi_clk_state curr_state);
/**
* dsi_pre_clkon_cb() - Callback before clock is turned on
* @priv: private data pointer.
* @clk_type: clock which is being turned on.
* @l_type: specifies if the clock is HS or LP type. Valid only for link clocks.
* @new_state: next state for the clock.
*
* @return: error code.
*/
int dsi_pre_clkon_cb(void *priv, enum dsi_clk_type clk_type,
enum dsi_lclk_type l_type,
enum dsi_clk_state new_state);
/**
* dsi_display_unprepare() - power off display hardware.
* @display: Handle to display.
*
* Host and panel hardware is turned off. Panel will be in reset state at the
* end of the function.
*
* Return: error code.
*/
int dsi_display_unprepare(struct dsi_display *display);
int dsi_display_set_tpg_state(struct dsi_display *display, bool enable);
int dsi_display_clock_gate(struct dsi_display *display, bool enable);
int dsi_dispaly_static_frame(struct dsi_display *display, bool enable);
/**
* dsi_display_get_drm_panel() - get drm_panel from display.
* @display: Handle to display.
* Get drm_panel which was inclued in dsi_display's dsi_panel.
*
* Return: drm_panel/NULL.
*/
struct drm_panel *dsi_display_get_drm_panel(struct dsi_display *display);
/**
* dsi_display_enable_event() - enable interrupt based connector event
* @connector: Pointer to drm connector structure
* @display: Handle to display.
* @event_idx: Event index.
* @event_info: Event callback definition.
* @enable: Whether to enable/disable the event interrupt.
*/
void dsi_display_enable_event(struct drm_connector *connector,
struct dsi_display *display,
uint32_t event_idx, struct dsi_event_cb_info *event_info,
bool enable);
/**
* dsi_display_set_backlight() - set backlight
* @connector: Pointer to drm connector structure
* @display: Handle to display.
* @bl_lvl: Backlight level.
* @event_info: Event callback definition.
* @enable: Whether to enable/disable the event interrupt.
*/
int dsi_display_set_backlight(struct drm_connector *connector,
void *display, u32 bl_lvl);
/**
* dsi_display_check_status() - check if panel is dead or alive
* @connector: Pointer to drm connector structure
* @display: Handle to display.
* @te_check_override: Whether check for TE from panel or default check
*/
int dsi_display_check_status(struct drm_connector *connector, void *display,
bool te_check_override);
/**
* dsi_display_cmd_transfer() - transfer command to the panel
* @connector: Pointer to drm connector structure
* @display: Handle to display.
* @cmd_buf: Command buffer
* @cmd_buf_len: Command buffer length in bytes
*/
int dsi_display_cmd_transfer(struct drm_connector *connector,
void *display, const char *cmd_buffer,
u32 cmd_buf_len);
/**
* dsi_display_cmd_receive() - receive response from the panel
* @display: Handle to display.
* @cmd_buf: Command buffer
* @cmd_buf_len: Command buffer length in bytes
* @recv_buf: Receive buffer
* @recv_buf_len: Receive buffer length in bytes
*/
int dsi_display_cmd_receive(void *display, const char *cmd_buf,
u32 cmd_buf_len, u8 *recv_buf, u32 recv_buf_len);
/**
* dsi_display_soft_reset() - perform a soft reset on DSI controller
* @display: Handle to display
*
* The video, command and controller engines will be disabled before the
* reset is triggered. After, the engines will be re-enabled to the same state
* as before the reset.
*
* If the reset is done while MDP timing engine is turned on, the video
* engine should be re-enabled only during the vertical blanking time.
*
* Return: error code
*/
int dsi_display_soft_reset(void *display);
/**
* dsi_display_set_power - update power/dpms setting
* @connector: Pointer to drm connector structure
* @power_mode: One of the following,
* SDE_MODE_DPMS_ON
* SDE_MODE_DPMS_LP1
* SDE_MODE_DPMS_LP2
* SDE_MODE_DPMS_STANDBY
* SDE_MODE_DPMS_SUSPEND
* SDE_MODE_DPMS_OFF
* @display: Pointer to private display structure
* Returns: Zero on success
*/
int dsi_display_set_power(struct drm_connector *connector,
int power_mode, void *display);
/*
* dsi_display_pre_kickoff - program kickoff-time features
* @connector: Pointer to drm connector structure
* @display: Pointer to private display structure
* @params: Parameters for kickoff-time programming
* Returns: Zero on success
*/
int dsi_display_pre_kickoff(struct drm_connector *connector,
struct dsi_display *display,
struct msm_display_kickoff_params *params);
/*
* dsi_display_pre_commit - program pre commit features
* @display: Pointer to private display structure
* @params: Parameters for pre commit time programming
* Returns: Zero on success
*/
int dsi_display_pre_commit(void *display,
struct msm_display_conn_params *params);
/**
* dsi_display_get_dst_format() - get dst_format from DSI display
* @connector: Pointer to drm connector structure
* @display: Handle to display
*
* Return: enum dsi_pixel_format type
*/
enum dsi_pixel_format dsi_display_get_dst_format(
struct drm_connector *connector,
void *display);
/**
* dsi_display_cont_splash_config() - initialize splash resources
* @display: Handle to display
*
* Return: Zero on Success
*/
int dsi_display_cont_splash_config(void *display);
/**
* dsi_display_cont_splash_res_disable() - Disable resource votes added in probe
* @display: Pointer to dsi display
* Returns: Zero on success
*/
int dsi_display_cont_splash_res_disable(void *display);
/*
* dsi_display_get_panel_vfp - get panel vsync
* @display: Pointer to private display structure
* @h_active: width
* @v_active: height
* Returns: v_front_porch on success error code on failure
*/
int dsi_display_get_panel_vfp(void *display,
int h_active, int v_active);
/**
* dsi_display_dump_clks_state() - dump clocks state to console
* @display: Handle to display
*
* Return: Zero on Success
*/
int dsi_display_dump_clks_state(struct dsi_display *display);
/**
* dsi_display_dfps_update_parent() - update dsi clock parent to src clock
* @display: Handle to display
*/
void dsi_display_dfps_update_parent(struct dsi_display *display);
#endif /* _DSI_DISPLAY_H_ */

View File

@ -0,0 +1,96 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
*/
#include <linux/delay.h>
#include <linux/slab.h>
#include "dsi_display_test.h"
static void dsi_display_test_dump_modes(struct dsi_display_mode *mode, u32
count)
{
}
static void dsi_display_test_work(struct work_struct *work)
{
struct dsi_display_test *test;
struct dsi_display *display;
struct dsi_display_mode *modes;
u32 count = 0;
int rc = 0;
test = container_of(work, struct dsi_display_test, test_work);
display = test->display;
rc = dsi_display_get_mode_count(display, &count);
if (rc) {
DSI_ERR("failed to get modes count, rc=%d\n", rc);
goto test_fail;
}
rc = dsi_display_get_modes(display, &modes);
if (rc) {
DSI_ERR("failed to get modes, rc=%d\n", rc);
goto test_fail_free_modes;
}
dsi_display_test_dump_modes(modes, count);
rc = dsi_display_set_mode(display, &modes[0], 0x0);
if (rc) {
DSI_ERR("failed to set mode, rc=%d\n", rc);
goto test_fail_free_modes;
}
rc = dsi_display_prepare(display);
if (rc) {
DSI_ERR("failed to prepare display, rc=%d\n", rc);
goto test_fail_free_modes;
}
rc = dsi_display_enable(display);
if (rc) {
DSI_ERR("failed to enable display, rc=%d\n", rc);
goto test_fail_unprep_disp;
}
return;
test_fail_unprep_disp:
if (rc) {
DSI_ERR("failed to unprep display, rc=%d\n", rc);
goto test_fail_free_modes;
}
test_fail_free_modes:
kfree(modes);
test_fail:
return;
}
int dsi_display_test_init(struct dsi_display *display)
{
static int done;
int rc = 0;
struct dsi_display_test *test;
if (done)
return rc;
done = 1;
if (!display) {
DSI_ERR("Invalid params\n");
return -EINVAL;
}
test = kzalloc(sizeof(*test), GFP_KERNEL);
if (!test)
return -ENOMEM;
test->display = display;
INIT_WORK(&test->test_work, dsi_display_test_work);
dsi_display_test_work(&test->test_work);
return rc;
}

View File

@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
*/
#ifndef _DSI_DISPLAY_TEST_H_
#define _DSI_DISPLAY_TEST_H_
#include "dsi_display.h"
#include "dsi_ctrl_hw.h"
#include "dsi_ctrl.h"
struct dsi_display_test {
struct dsi_display *display;
struct work_struct test_work;
};
int dsi_display_test_init(struct dsi_display *display);
#endif /* _DSI_DISPLAY_TEST_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,158 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*/
#ifndef _DSI_DRM_H_
#define _DSI_DRM_H_
#include <linux/types.h>
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include "msm_drv.h"
#include "dsi_display.h"
#define NO_OVERRIDE -1
struct dsi_bridge {
struct drm_bridge base;
u32 id;
struct dsi_display *display;
struct dsi_display_mode dsi_mode;
};
/**
* dsi_conn_set_info_blob - callback to perform info blob initialization
* @connector: Pointer to drm connector structure
* @info: Pointer to sde connector info structure
* @display: Pointer to private display handle
* @mode_info: Pointer to mode info structure
* Returns: Zero on success
*/
int dsi_conn_set_info_blob(struct drm_connector *connector,
void *info,
void *display,
struct msm_mode_info *mode_info);
/**
* dsi_conn_detect - callback to determine if connector is connected
* @connector: Pointer to drm connector structure
* @force: Force detect setting from drm framework
* @display: Pointer to private display handle
* Returns: Connector 'is connected' status
*/
enum drm_connector_status dsi_conn_detect(struct drm_connector *conn,
bool force,
void *display);
/**
* dsi_connector_get_modes - callback to add drm modes via drm_mode_probed_add()
* @connector: Pointer to drm connector structure
* @display: Pointer to private display handle
* @avail_res: Pointer with curr available resources
* Returns: Number of modes added
*/
int dsi_connector_get_modes(struct drm_connector *connector,
void *display, const struct msm_resource_caps_info *avail_res);
/**
* dsi_connector_put_modes - callback to free up drm modes of the connector
* @connector: Pointer to drm connector structure
* @display: Pointer to private display handle
*/
void dsi_connector_put_modes(struct drm_connector *connector,
void *display);
/**
* dsi_conn_get_mode_info - retrieve information on the mode selected
* @drm_mode: Display mode set for the display
* @mode_info: Out parameter. information of the mode.
* @display: Pointer to private display structure
* @avail_res: Pointer with curr available resources
* Returns: Zero on success
*/
int dsi_conn_get_mode_info(struct drm_connector *connector,
const struct drm_display_mode *drm_mode,
struct msm_mode_info *mode_info,
void *display, const struct msm_resource_caps_info *avail_res);
/**
* dsi_conn_mode_valid - callback to determine if specified mode is valid
* @connector: Pointer to drm connector structure
* @mode: Pointer to drm mode structure
* @display: Pointer to private display handle
* @avail_res: Pointer with curr available resources
* Returns: Validity status for specified mode
*/
enum drm_mode_status dsi_conn_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode,
void *display, const struct msm_resource_caps_info *avail_res);
/**
* dsi_conn_enable_event - callback to notify DSI driver of event registration
* @connector: Pointer to drm connector structure
* @event_idx: Connector event index
* @enable: Whether or not the event is enabled
* @display: Pointer to private display handle
*/
void dsi_conn_enable_event(struct drm_connector *connector,
uint32_t event_idx, bool enable, void *display);
struct dsi_bridge *dsi_drm_bridge_init(struct dsi_display *display,
struct drm_device *dev,
struct drm_encoder *encoder);
void dsi_drm_bridge_cleanup(struct dsi_bridge *bridge);
/**
* dsi_display_pre_kickoff - program kickoff-time features
* @connector: Pointer to drm connector structure
* @display: Pointer to private display structure
* @params: Parameters for kickoff-time programming
* Returns: Zero on success
*/
int dsi_conn_pre_kickoff(struct drm_connector *connector,
void *display,
struct msm_display_kickoff_params *params);
/**
* dsi_display_post_kickoff - program post kickoff-time features
* @connector: Pointer to drm connector structure
* @params: Parameters for post kickoff programming
* Returns: Zero on success
*/
int dsi_conn_post_kickoff(struct drm_connector *connector,
struct msm_display_conn_params *params);
/**
* dsi_convert_to_drm_mode - Update drm mode with dsi mode information
* @dsi_mode: input parameter. structure having dsi mode information.
* @drm_mode: output parameter. DRM mode set for the display
*/
void dsi_convert_to_drm_mode(const struct dsi_display_mode *dsi_mode,
struct drm_display_mode *drm_mode);
u64 dsi_drm_find_bit_clk_rate(void *display,
const struct drm_display_mode *drm_mode);
/**
* dsi_conn_prepare_commit - program pre commit time features
* @display: Pointer to private display structure
* @params: Parameters for pre commit programming
* Returns: Zero on success
*/
int dsi_conn_prepare_commit(void *display,
struct msm_display_conn_params *params);
/**
* dsi_set_allowed_mode_switch - set allowed mode switch bitmask
* @connector: Pointer to drm connector structure
* @display: Pointer to private display structure
*/
void dsi_conn_set_allowed_mode_switch(struct drm_connector *connector,
void *display);
#endif /* _DSI_DRM_H_ */

Some files were not shown because too many files have changed in this diff Show More