You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08:00
drm/msm: Initial add DSI connector support
This change adds the DSI connector support in msm drm driver. v1: Initial change v2: - Address comments from Archit + minor clean-ups - Rebase to not depend on msm_drm_sub_dev change [Rob's comment] v3: Fix issues when initialization is failed Signed-off-by: Hai Li <hali@codeaurora.org> Signed-off-by: Rob Clark <robdclark@gmail.com>
This commit is contained in:
@@ -35,3 +35,14 @@ config DRM_MSM_REGISTER_LOGGING
|
||||
Compile in support for logging register reads/writes in a format
|
||||
that can be parsed by envytools demsm tool. If enabled, register
|
||||
logging can be switched on via msm.reglog=y module param.
|
||||
|
||||
config DRM_MSM_DSI
|
||||
bool "Enable DSI support in MSM DRM driver"
|
||||
depends on DRM_MSM
|
||||
select DRM_PANEL
|
||||
select DRM_MIPI_DSI
|
||||
default y
|
||||
help
|
||||
Choose this option if you have a need for MIPI DSI connector
|
||||
support.
|
||||
|
||||
|
||||
@@ -50,5 +50,9 @@ msm-y := \
|
||||
|
||||
msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o
|
||||
msm-$(CONFIG_COMMON_CLK) += mdp/mdp4/mdp4_lvds_pll.o
|
||||
msm-$(CONFIG_DRM_MSM_DSI) += dsi/dsi.o \
|
||||
dsi/dsi_host.o \
|
||||
dsi/dsi_manager.o \
|
||||
dsi/dsi_phy.o
|
||||
|
||||
obj-$(CONFIG_DRM_MSM) += msm.o
|
||||
|
||||
212
drivers/gpu/drm/msm/dsi/dsi.c
Normal file
212
drivers/gpu/drm/msm/dsi/dsi.c
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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 and
|
||||
* only 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.
|
||||
*/
|
||||
|
||||
#include "dsi.h"
|
||||
|
||||
struct drm_encoder *msm_dsi_get_encoder(struct msm_dsi *msm_dsi)
|
||||
{
|
||||
if (!msm_dsi || !msm_dsi->panel)
|
||||
return NULL;
|
||||
|
||||
return (msm_dsi->panel_flags & MIPI_DSI_MODE_VIDEO) ?
|
||||
msm_dsi->encoders[MSM_DSI_VIDEO_ENCODER_ID] :
|
||||
msm_dsi->encoders[MSM_DSI_CMD_ENCODER_ID];
|
||||
}
|
||||
|
||||
static void dsi_destroy(struct msm_dsi *msm_dsi)
|
||||
{
|
||||
if (!msm_dsi)
|
||||
return;
|
||||
|
||||
msm_dsi_manager_unregister(msm_dsi);
|
||||
if (msm_dsi->host) {
|
||||
msm_dsi_host_destroy(msm_dsi->host);
|
||||
msm_dsi->host = NULL;
|
||||
}
|
||||
|
||||
platform_set_drvdata(msm_dsi->pdev, NULL);
|
||||
}
|
||||
|
||||
static struct msm_dsi *dsi_init(struct platform_device *pdev)
|
||||
{
|
||||
struct msm_dsi *msm_dsi = NULL;
|
||||
int ret;
|
||||
|
||||
if (!pdev) {
|
||||
dev_err(&pdev->dev, "no dsi device\n");
|
||||
ret = -ENXIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
msm_dsi = devm_kzalloc(&pdev->dev, sizeof(*msm_dsi), GFP_KERNEL);
|
||||
if (!msm_dsi) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
DBG("dsi probed=%p", msm_dsi);
|
||||
|
||||
msm_dsi->pdev = pdev;
|
||||
platform_set_drvdata(pdev, msm_dsi);
|
||||
|
||||
/* Init dsi host */
|
||||
ret = msm_dsi_host_init(msm_dsi);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
/* Register to dsi manager */
|
||||
ret = msm_dsi_manager_register(msm_dsi);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
return msm_dsi;
|
||||
|
||||
fail:
|
||||
if (msm_dsi)
|
||||
dsi_destroy(msm_dsi);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static int dsi_bind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
struct drm_device *drm = dev_get_drvdata(master);
|
||||
struct msm_drm_private *priv = drm->dev_private;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct msm_dsi *msm_dsi;
|
||||
|
||||
DBG("");
|
||||
msm_dsi = dsi_init(pdev);
|
||||
if (IS_ERR(msm_dsi))
|
||||
return PTR_ERR(msm_dsi);
|
||||
|
||||
priv->dsi[msm_dsi->id] = msm_dsi;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dsi_unbind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct drm_device *drm = dev_get_drvdata(master);
|
||||
struct msm_drm_private *priv = drm->dev_private;
|
||||
struct msm_dsi *msm_dsi = dev_get_drvdata(dev);
|
||||
int id = msm_dsi->id;
|
||||
|
||||
if (priv->dsi[id]) {
|
||||
dsi_destroy(msm_dsi);
|
||||
priv->dsi[id] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct component_ops dsi_ops = {
|
||||
.bind = dsi_bind,
|
||||
.unbind = dsi_unbind,
|
||||
};
|
||||
|
||||
static int dsi_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
return component_add(&pdev->dev, &dsi_ops);
|
||||
}
|
||||
|
||||
static int dsi_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
DBG("");
|
||||
component_del(&pdev->dev, &dsi_ops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id dt_match[] = {
|
||||
{ .compatible = "qcom,mdss-dsi-ctrl" },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver dsi_driver = {
|
||||
.probe = dsi_dev_probe,
|
||||
.remove = dsi_dev_remove,
|
||||
.driver = {
|
||||
.name = "msm_dsi",
|
||||
.of_match_table = dt_match,
|
||||
},
|
||||
};
|
||||
|
||||
void __init msm_dsi_register(void)
|
||||
{
|
||||
DBG("");
|
||||
platform_driver_register(&dsi_driver);
|
||||
}
|
||||
|
||||
void __exit msm_dsi_unregister(void)
|
||||
{
|
||||
DBG("");
|
||||
platform_driver_unregister(&dsi_driver);
|
||||
}
|
||||
|
||||
int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev,
|
||||
struct drm_encoder *encoders[MSM_DSI_ENCODER_NUM])
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
int ret, i;
|
||||
|
||||
if (WARN_ON(!encoders[MSM_DSI_VIDEO_ENCODER_ID] ||
|
||||
!encoders[MSM_DSI_CMD_ENCODER_ID]))
|
||||
return -EINVAL;
|
||||
|
||||
msm_dsi->dev = dev;
|
||||
|
||||
ret = msm_dsi_host_modeset_init(msm_dsi->host, dev);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to modeset init host: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
msm_dsi->bridge = msm_dsi_manager_bridge_init(msm_dsi->id);
|
||||
if (IS_ERR(msm_dsi->bridge)) {
|
||||
ret = PTR_ERR(msm_dsi->bridge);
|
||||
dev_err(dev->dev, "failed to create dsi bridge: %d\n", ret);
|
||||
msm_dsi->bridge = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
msm_dsi->connector = msm_dsi_manager_connector_init(msm_dsi->id);
|
||||
if (IS_ERR(msm_dsi->connector)) {
|
||||
ret = PTR_ERR(msm_dsi->connector);
|
||||
dev_err(dev->dev, "failed to create dsi connector: %d\n", ret);
|
||||
msm_dsi->connector = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i = 0; i < MSM_DSI_ENCODER_NUM; i++) {
|
||||
encoders[i]->bridge = msm_dsi->bridge;
|
||||
msm_dsi->encoders[i] = encoders[i];
|
||||
}
|
||||
|
||||
priv->bridges[priv->num_bridges++] = msm_dsi->bridge;
|
||||
priv->connectors[priv->num_connectors++] = msm_dsi->connector;
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
if (msm_dsi) {
|
||||
/* bridge/connector are normally destroyed by drm: */
|
||||
if (msm_dsi->bridge) {
|
||||
msm_dsi_manager_bridge_destroy(msm_dsi->bridge);
|
||||
msm_dsi->bridge = NULL;
|
||||
}
|
||||
if (msm_dsi->connector) {
|
||||
msm_dsi->connector->funcs->destroy(msm_dsi->connector);
|
||||
msm_dsi->connector = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
117
drivers/gpu/drm/msm/dsi/dsi.h
Normal file
117
drivers/gpu/drm/msm/dsi/dsi.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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 and
|
||||
* only 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.
|
||||
*/
|
||||
|
||||
#ifndef __DSI_CONNECTOR_H__
|
||||
#define __DSI_CONNECTOR_H__
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "drm_crtc.h"
|
||||
#include "drm_mipi_dsi.h"
|
||||
#include "drm_panel.h"
|
||||
|
||||
#include "msm_drv.h"
|
||||
|
||||
#define DSI_0 0
|
||||
#define DSI_1 1
|
||||
#define DSI_MAX 2
|
||||
|
||||
#define DSI_CLOCK_MASTER DSI_0
|
||||
#define DSI_CLOCK_SLAVE DSI_1
|
||||
|
||||
#define DSI_LEFT DSI_0
|
||||
#define DSI_RIGHT DSI_1
|
||||
|
||||
/* According to the current drm framework sequence, take the encoder of
|
||||
* DSI_1 as master encoder
|
||||
*/
|
||||
#define DSI_ENCODER_MASTER DSI_1
|
||||
#define DSI_ENCODER_SLAVE DSI_0
|
||||
|
||||
struct msm_dsi {
|
||||
struct drm_device *dev;
|
||||
struct platform_device *pdev;
|
||||
|
||||
struct drm_connector *connector;
|
||||
struct drm_bridge *bridge;
|
||||
|
||||
struct mipi_dsi_host *host;
|
||||
struct msm_dsi_phy *phy;
|
||||
struct drm_panel *panel;
|
||||
unsigned long panel_flags;
|
||||
bool phy_enabled;
|
||||
|
||||
/* the encoders we are hooked to (outside of dsi block) */
|
||||
struct drm_encoder *encoders[MSM_DSI_ENCODER_NUM];
|
||||
|
||||
int id;
|
||||
};
|
||||
|
||||
/* dsi manager */
|
||||
struct drm_bridge *msm_dsi_manager_bridge_init(u8 id);
|
||||
void msm_dsi_manager_bridge_destroy(struct drm_bridge *bridge);
|
||||
struct drm_connector *msm_dsi_manager_connector_init(u8 id);
|
||||
int msm_dsi_manager_phy_enable(int id,
|
||||
const unsigned long bit_rate, const unsigned long esc_rate,
|
||||
u32 *clk_pre, u32 *clk_post);
|
||||
void msm_dsi_manager_phy_disable(int id);
|
||||
int msm_dsi_manager_cmd_xfer(int id, const struct mipi_dsi_msg *msg);
|
||||
bool msm_dsi_manager_cmd_xfer_trigger(int id, u32 iova, u32 len);
|
||||
int msm_dsi_manager_register(struct msm_dsi *msm_dsi);
|
||||
void msm_dsi_manager_unregister(struct msm_dsi *msm_dsi);
|
||||
|
||||
/* msm dsi */
|
||||
struct drm_encoder *msm_dsi_get_encoder(struct msm_dsi *msm_dsi);
|
||||
|
||||
/* dsi host */
|
||||
int msm_dsi_host_xfer_prepare(struct mipi_dsi_host *host,
|
||||
const struct mipi_dsi_msg *msg);
|
||||
void msm_dsi_host_xfer_restore(struct mipi_dsi_host *host,
|
||||
const struct mipi_dsi_msg *msg);
|
||||
int msm_dsi_host_cmd_tx(struct mipi_dsi_host *host,
|
||||
const struct mipi_dsi_msg *msg);
|
||||
int msm_dsi_host_cmd_rx(struct mipi_dsi_host *host,
|
||||
const struct mipi_dsi_msg *msg);
|
||||
void msm_dsi_host_cmd_xfer_commit(struct mipi_dsi_host *host,
|
||||
u32 iova, u32 len);
|
||||
int msm_dsi_host_enable(struct mipi_dsi_host *host);
|
||||
int msm_dsi_host_disable(struct mipi_dsi_host *host);
|
||||
int msm_dsi_host_power_on(struct mipi_dsi_host *host);
|
||||
int msm_dsi_host_power_off(struct mipi_dsi_host *host);
|
||||
int msm_dsi_host_set_display_mode(struct mipi_dsi_host *host,
|
||||
struct drm_display_mode *mode);
|
||||
struct drm_panel *msm_dsi_host_get_panel(struct mipi_dsi_host *host,
|
||||
unsigned long *panel_flags);
|
||||
int msm_dsi_host_register(struct mipi_dsi_host *host, bool check_defer);
|
||||
void msm_dsi_host_unregister(struct mipi_dsi_host *host);
|
||||
void msm_dsi_host_destroy(struct mipi_dsi_host *host);
|
||||
int msm_dsi_host_modeset_init(struct mipi_dsi_host *host,
|
||||
struct drm_device *dev);
|
||||
int msm_dsi_host_init(struct msm_dsi *msm_dsi);
|
||||
|
||||
/* dsi phy */
|
||||
struct msm_dsi_phy;
|
||||
enum msm_dsi_phy_type {
|
||||
MSM_DSI_PHY_UNKNOWN,
|
||||
MSM_DSI_PHY_28NM,
|
||||
MSM_DSI_PHY_MAX
|
||||
};
|
||||
struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev,
|
||||
enum msm_dsi_phy_type type, int id);
|
||||
int msm_dsi_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel,
|
||||
const unsigned long bit_rate, const unsigned long esc_rate);
|
||||
int msm_dsi_phy_disable(struct msm_dsi_phy *phy);
|
||||
void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy,
|
||||
u32 *clk_pre, u32 *clk_post);
|
||||
#endif /* __DSI_CONNECTOR_H__ */
|
||||
|
||||
1993
drivers/gpu/drm/msm/dsi/dsi_host.c
Normal file
1993
drivers/gpu/drm/msm/dsi/dsi_host.c
Normal file
File diff suppressed because it is too large
Load Diff
705
drivers/gpu/drm/msm/dsi/dsi_manager.c
Normal file
705
drivers/gpu/drm/msm/dsi/dsi_manager.c
Normal file
File diff suppressed because it is too large
Load Diff
352
drivers/gpu/drm/msm/dsi/dsi_phy.c
Normal file
352
drivers/gpu/drm/msm/dsi/dsi_phy.c
Normal file
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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 and
|
||||
* only 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.
|
||||
*/
|
||||
|
||||
#include "dsi.h"
|
||||
#include "dsi.xml.h"
|
||||
|
||||
#define dsi_phy_read(offset) msm_readl((offset))
|
||||
#define dsi_phy_write(offset, data) msm_writel((data), (offset))
|
||||
|
||||
struct dsi_dphy_timing {
|
||||
u32 clk_pre;
|
||||
u32 clk_post;
|
||||
u32 clk_zero;
|
||||
u32 clk_trail;
|
||||
u32 clk_prepare;
|
||||
u32 hs_exit;
|
||||
u32 hs_zero;
|
||||
u32 hs_prepare;
|
||||
u32 hs_trail;
|
||||
u32 hs_rqst;
|
||||
u32 ta_go;
|
||||
u32 ta_sure;
|
||||
u32 ta_get;
|
||||
};
|
||||
|
||||
struct msm_dsi_phy {
|
||||
void __iomem *base;
|
||||
void __iomem *reg_base;
|
||||
int id;
|
||||
struct dsi_dphy_timing timing;
|
||||
int (*enable)(struct msm_dsi_phy *phy, bool is_dual_panel,
|
||||
const unsigned long bit_rate, const unsigned long esc_rate);
|
||||
int (*disable)(struct msm_dsi_phy *phy);
|
||||
};
|
||||
|
||||
#define S_DIV_ROUND_UP(n, d) \
|
||||
(((n) >= 0) ? (((n) + (d) - 1) / (d)) : (((n) - (d) + 1) / (d)))
|
||||
|
||||
static inline s32 linear_inter(s32 tmax, s32 tmin, s32 percent,
|
||||
s32 min_result, bool even)
|
||||
{
|
||||
s32 v;
|
||||
v = (tmax - tmin) * percent;
|
||||
v = S_DIV_ROUND_UP(v, 100) + tmin;
|
||||
if (even && (v & 0x1))
|
||||
return max_t(s32, min_result, v - 1);
|
||||
else
|
||||
return max_t(s32, min_result, v);
|
||||
}
|
||||
|
||||
static void dsi_dphy_timing_calc_clk_zero(struct dsi_dphy_timing *timing,
|
||||
s32 ui, s32 coeff, s32 pcnt)
|
||||
{
|
||||
s32 tmax, tmin, clk_z;
|
||||
s32 temp;
|
||||
|
||||
/* reset */
|
||||
temp = 300 * coeff - ((timing->clk_prepare >> 1) + 1) * 2 * ui;
|
||||
tmin = S_DIV_ROUND_UP(temp, ui) - 2;
|
||||
if (tmin > 255) {
|
||||
tmax = 511;
|
||||
clk_z = linear_inter(2 * tmin, tmin, pcnt, 0, true);
|
||||
} else {
|
||||
tmax = 255;
|
||||
clk_z = linear_inter(tmax, tmin, pcnt, 0, true);
|
||||
}
|
||||
|
||||
/* adjust */
|
||||
temp = (timing->hs_rqst + timing->clk_prepare + clk_z) & 0x7;
|
||||
timing->clk_zero = clk_z + 8 - temp;
|
||||
}
|
||||
|
||||
static int dsi_dphy_timing_calc(struct dsi_dphy_timing *timing,
|
||||
const unsigned long bit_rate, const unsigned long esc_rate)
|
||||
{
|
||||
s32 ui, lpx;
|
||||
s32 tmax, tmin;
|
||||
s32 pcnt0 = 10;
|
||||
s32 pcnt1 = (bit_rate > 1200000000) ? 15 : 10;
|
||||
s32 pcnt2 = 10;
|
||||
s32 pcnt3 = (bit_rate > 180000000) ? 10 : 40;
|
||||
s32 coeff = 1000; /* Precision, should avoid overflow */
|
||||
s32 temp;
|
||||
|
||||
if (!bit_rate || !esc_rate)
|
||||
return -EINVAL;
|
||||
|
||||
ui = mult_frac(NSEC_PER_MSEC, coeff, bit_rate / 1000);
|
||||
lpx = mult_frac(NSEC_PER_MSEC, coeff, esc_rate / 1000);
|
||||
|
||||
tmax = S_DIV_ROUND_UP(95 * coeff, ui) - 2;
|
||||
tmin = S_DIV_ROUND_UP(38 * coeff, ui) - 2;
|
||||
timing->clk_prepare = linear_inter(tmax, tmin, pcnt0, 0, true);
|
||||
|
||||
temp = lpx / ui;
|
||||
if (temp & 0x1)
|
||||
timing->hs_rqst = temp;
|
||||
else
|
||||
timing->hs_rqst = max_t(s32, 0, temp - 2);
|
||||
|
||||
/* Calculate clk_zero after clk_prepare and hs_rqst */
|
||||
dsi_dphy_timing_calc_clk_zero(timing, ui, coeff, pcnt2);
|
||||
|
||||
temp = 105 * coeff + 12 * ui - 20 * coeff;
|
||||
tmax = S_DIV_ROUND_UP(temp, ui) - 2;
|
||||
tmin = S_DIV_ROUND_UP(60 * coeff, ui) - 2;
|
||||
timing->clk_trail = linear_inter(tmax, tmin, pcnt3, 0, true);
|
||||
|
||||
temp = 85 * coeff + 6 * ui;
|
||||
tmax = S_DIV_ROUND_UP(temp, ui) - 2;
|
||||
temp = 40 * coeff + 4 * ui;
|
||||
tmin = S_DIV_ROUND_UP(temp, ui) - 2;
|
||||
timing->hs_prepare = linear_inter(tmax, tmin, pcnt1, 0, true);
|
||||
|
||||
tmax = 255;
|
||||
temp = ((timing->hs_prepare >> 1) + 1) * 2 * ui + 2 * ui;
|
||||
temp = 145 * coeff + 10 * ui - temp;
|
||||
tmin = S_DIV_ROUND_UP(temp, ui) - 2;
|
||||
timing->hs_zero = linear_inter(tmax, tmin, pcnt2, 24, true);
|
||||
|
||||
temp = 105 * coeff + 12 * ui - 20 * coeff;
|
||||
tmax = S_DIV_ROUND_UP(temp, ui) - 2;
|
||||
temp = 60 * coeff + 4 * ui;
|
||||
tmin = DIV_ROUND_UP(temp, ui) - 2;
|
||||
timing->hs_trail = linear_inter(tmax, tmin, pcnt3, 0, true);
|
||||
|
||||
tmax = 255;
|
||||
tmin = S_DIV_ROUND_UP(100 * coeff, ui) - 2;
|
||||
timing->hs_exit = linear_inter(tmax, tmin, pcnt2, 0, true);
|
||||
|
||||
tmax = 63;
|
||||
temp = ((timing->hs_exit >> 1) + 1) * 2 * ui;
|
||||
temp = 60 * coeff + 52 * ui - 24 * ui - temp;
|
||||
tmin = S_DIV_ROUND_UP(temp, 8 * ui) - 1;
|
||||
timing->clk_post = linear_inter(tmax, tmin, pcnt2, 0, false);
|
||||
|
||||
tmax = 63;
|
||||
temp = ((timing->clk_prepare >> 1) + 1) * 2 * ui;
|
||||
temp += ((timing->clk_zero >> 1) + 1) * 2 * ui;
|
||||
temp += 8 * ui + lpx;
|
||||
tmin = S_DIV_ROUND_UP(temp, 8 * ui) - 1;
|
||||
if (tmin > tmax) {
|
||||
temp = linear_inter(2 * tmax, tmin, pcnt2, 0, false) >> 1;
|
||||
timing->clk_pre = temp >> 1;
|
||||
temp = (2 * tmax - tmin) * pcnt2;
|
||||
} else {
|
||||
timing->clk_pre = linear_inter(tmax, tmin, pcnt2, 0, false);
|
||||
}
|
||||
|
||||
timing->ta_go = 3;
|
||||
timing->ta_sure = 0;
|
||||
timing->ta_get = 4;
|
||||
|
||||
DBG("PHY timings: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d",
|
||||
timing->clk_pre, timing->clk_post, timing->clk_zero,
|
||||
timing->clk_trail, timing->clk_prepare, timing->hs_exit,
|
||||
timing->hs_zero, timing->hs_prepare, timing->hs_trail,
|
||||
timing->hs_rqst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dsi_28nm_phy_regulator_ctrl(struct msm_dsi_phy *phy, bool enable)
|
||||
{
|
||||
void __iomem *base = phy->reg_base;
|
||||
|
||||
if (!enable) {
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CAL_PWR_CFG, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_0, 0x0);
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CAL_PWR_CFG, 1);
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_5, 0);
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_3, 0);
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_2, 0x3);
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_1, 0x9);
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_0, 0x7);
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_4, 0x20);
|
||||
}
|
||||
|
||||
static int dsi_28nm_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel,
|
||||
const unsigned long bit_rate, const unsigned long esc_rate)
|
||||
{
|
||||
struct dsi_dphy_timing *timing = &phy->timing;
|
||||
int i;
|
||||
void __iomem *base = phy->base;
|
||||
|
||||
DBG("");
|
||||
|
||||
if (dsi_dphy_timing_calc(timing, bit_rate, esc_rate)) {
|
||||
pr_err("%s: D-PHY timing calculation failed\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_STRENGTH_0, 0xff);
|
||||
|
||||
dsi_28nm_phy_regulator_ctrl(phy, true);
|
||||
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_LDO_CNTRL, 0x00);
|
||||
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_0,
|
||||
DSI_28nm_PHY_TIMING_CTRL_0_CLK_ZERO(timing->clk_zero));
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_1,
|
||||
DSI_28nm_PHY_TIMING_CTRL_1_CLK_TRAIL(timing->clk_trail));
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_2,
|
||||
DSI_28nm_PHY_TIMING_CTRL_2_CLK_PREPARE(timing->clk_prepare));
|
||||
if (timing->clk_zero & BIT(8))
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_3,
|
||||
DSI_28nm_PHY_TIMING_CTRL_3_CLK_ZERO_8);
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_4,
|
||||
DSI_28nm_PHY_TIMING_CTRL_4_HS_EXIT(timing->hs_exit));
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_5,
|
||||
DSI_28nm_PHY_TIMING_CTRL_5_HS_ZERO(timing->hs_zero));
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_6,
|
||||
DSI_28nm_PHY_TIMING_CTRL_6_HS_PREPARE(timing->hs_prepare));
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_7,
|
||||
DSI_28nm_PHY_TIMING_CTRL_7_HS_TRAIL(timing->hs_trail));
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_8,
|
||||
DSI_28nm_PHY_TIMING_CTRL_8_HS_RQST(timing->hs_rqst));
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_9,
|
||||
DSI_28nm_PHY_TIMING_CTRL_9_TA_GO(timing->ta_go) |
|
||||
DSI_28nm_PHY_TIMING_CTRL_9_TA_SURE(timing->ta_sure));
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_10,
|
||||
DSI_28nm_PHY_TIMING_CTRL_10_TA_GET(timing->ta_get));
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_11,
|
||||
DSI_28nm_PHY_TIMING_CTRL_11_TRIG3_CMD(0));
|
||||
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_CTRL_1, 0x00);
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_CTRL_0, 0x5f);
|
||||
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_STRENGTH_1, 0x6);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_0(i), 0);
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_1(i), 0);
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_2(i), 0);
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_3(i), 0);
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_LN_TEST_DATAPATH(i), 0);
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_LN_DEBUG_SEL(i), 0);
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_LN_TEST_STR_0(i), 0x1);
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_LN_TEST_STR_1(i), 0x97);
|
||||
}
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_4(0), 0);
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_4(1), 0x5);
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_4(2), 0xa);
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_4(3), 0xf);
|
||||
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_LNCK_CFG_1, 0xc0);
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_LNCK_TEST_STR0, 0x1);
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_LNCK_TEST_STR1, 0xbb);
|
||||
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_CTRL_0, 0x5f);
|
||||
|
||||
if (is_dual_panel && (phy->id != DSI_CLOCK_MASTER))
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_GLBL_TEST_CTRL, 0x00);
|
||||
else
|
||||
dsi_phy_write(base + REG_DSI_28nm_PHY_GLBL_TEST_CTRL, 0x01);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsi_28nm_phy_disable(struct msm_dsi_phy *phy)
|
||||
{
|
||||
dsi_phy_write(phy->base + REG_DSI_28nm_PHY_CTRL_0, 0);
|
||||
dsi_28nm_phy_regulator_ctrl(phy, false);
|
||||
|
||||
/*
|
||||
* Wait for the registers writes to complete in order to
|
||||
* ensure that the phy is completely disabled
|
||||
*/
|
||||
wmb();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define dsi_phy_func_init(name) \
|
||||
do { \
|
||||
phy->enable = dsi_##name##_phy_enable; \
|
||||
phy->disable = dsi_##name##_phy_disable; \
|
||||
} while (0)
|
||||
|
||||
struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev,
|
||||
enum msm_dsi_phy_type type, int id)
|
||||
{
|
||||
struct msm_dsi_phy *phy;
|
||||
|
||||
phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy)
|
||||
return NULL;
|
||||
|
||||
phy->base = msm_ioremap(pdev, "dsi_phy", "DSI_PHY");
|
||||
if (IS_ERR_OR_NULL(phy->base)) {
|
||||
pr_err("%s: failed to map phy base\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
phy->reg_base = msm_ioremap(pdev, "dsi_phy_regulator", "DSI_PHY_REG");
|
||||
if (IS_ERR_OR_NULL(phy->reg_base)) {
|
||||
pr_err("%s: failed to map phy regulator base\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case MSM_DSI_PHY_28NM:
|
||||
dsi_phy_func_init(28nm);
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: unsupported type, %d\n", __func__, type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
phy->id = id;
|
||||
|
||||
return phy;
|
||||
}
|
||||
|
||||
int msm_dsi_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel,
|
||||
const unsigned long bit_rate, const unsigned long esc_rate)
|
||||
{
|
||||
if (!phy || !phy->enable)
|
||||
return -EINVAL;
|
||||
return phy->enable(phy, is_dual_panel, bit_rate, esc_rate);
|
||||
}
|
||||
|
||||
int msm_dsi_phy_disable(struct msm_dsi_phy *phy)
|
||||
{
|
||||
if (!phy || !phy->disable)
|
||||
return -EINVAL;
|
||||
return phy->disable(phy);
|
||||
}
|
||||
|
||||
void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy,
|
||||
u32 *clk_pre, u32 *clk_post)
|
||||
{
|
||||
if (!phy)
|
||||
return;
|
||||
if (clk_pre)
|
||||
*clk_pre = phy->timing.clk_pre;
|
||||
if (clk_post)
|
||||
*clk_post = phy->timing.clk_post;
|
||||
}
|
||||
|
||||
@@ -82,6 +82,9 @@ struct msm_drm_private {
|
||||
*/
|
||||
struct msm_edp *edp;
|
||||
|
||||
/* DSI is shared by mdp4 and mdp5 */
|
||||
struct msm_dsi *dsi[2];
|
||||
|
||||
/* when we have more than one 'msm_gpu' these need to be an array: */
|
||||
struct msm_gpu *gpu;
|
||||
struct msm_file_private *lastctx;
|
||||
@@ -236,6 +239,32 @@ void __exit msm_edp_unregister(void);
|
||||
int msm_edp_modeset_init(struct msm_edp *edp, struct drm_device *dev,
|
||||
struct drm_encoder *encoder);
|
||||
|
||||
struct msm_dsi;
|
||||
enum msm_dsi_encoder_id {
|
||||
MSM_DSI_VIDEO_ENCODER_ID = 0,
|
||||
MSM_DSI_CMD_ENCODER_ID = 1,
|
||||
MSM_DSI_ENCODER_NUM = 2
|
||||
};
|
||||
#ifdef CONFIG_DRM_MSM_DSI
|
||||
void __init msm_dsi_register(void);
|
||||
void __exit msm_dsi_unregister(void);
|
||||
int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev,
|
||||
struct drm_encoder *encoders[MSM_DSI_ENCODER_NUM]);
|
||||
#else
|
||||
static inline void __init msm_dsi_register(void)
|
||||
{
|
||||
}
|
||||
static inline void __exit msm_dsi_unregister(void)
|
||||
{
|
||||
}
|
||||
static inline int msm_dsi_modeset_init(struct msm_dsi *msm_dsi,
|
||||
struct drm_device *dev,
|
||||
struct drm_encoder *encoders[MSM_DSI_ENCODER_NUM])
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m);
|
||||
void msm_gem_describe_objects(struct list_head *list, struct seq_file *m);
|
||||
|
||||
Reference in New Issue
Block a user