drm/rockchip: dsi: add dual mipi support

Add the Rockchip-sepcific dual-dsi setup and hook it into the VOP as well.
As described in the general dual-dsi devicetree binding, the panel should
define two input ports and point each of them to one of the used dsi-
controllers, as well as declare one of them as clock-master.
This is used to determine the dual-dsi state and get access to both
controller instances.

v6:
  handle master+slave component in dsi-attach
v5:
  use driver-internal mechanism to find dual dsi slave
v4:
  add component directly in probe when adding empty dsi slave controller

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>
Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20181001123845.11818-8-heiko@sntech.de
This commit is contained in:
Heiko Stuebner 2018-10-01 14:38:45 +02:00 committed by Andrzej Hajda
parent 739838b5f8
commit cf6d100dd2
5 changed files with 146 additions and 0 deletions

View File

@ -218,6 +218,10 @@ struct dw_mipi_dsi_rockchip {
struct clk *grf_clk;
struct clk *phy_cfg_clk;
/* dual-channel */
bool is_slave;
struct dw_mipi_dsi_rockchip *slave;
unsigned int lane_mbps; /* per lane */
u16 input_div;
u16 feedback_div;
@ -226,6 +230,7 @@ struct dw_mipi_dsi_rockchip {
struct dw_mipi_dsi *dmd;
const struct rockchip_dw_dsi_chip_data *cdata;
struct dw_mipi_dsi_plat_data pdata;
int devcnt;
};
struct dphy_pll_parameter_map {
@ -602,6 +607,8 @@ dw_mipi_dsi_encoder_atomic_check(struct drm_encoder *encoder,
}
s->output_type = DRM_MODE_CONNECTOR_DSI;
if (dsi->slave)
s->output_flags = ROCKCHIP_OUTPUT_DSI_DUAL;
return 0;
}
@ -617,6 +624,8 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
return;
pm_runtime_get_sync(dsi->dev);
if (dsi->slave)
pm_runtime_get_sync(dsi->slave->dev);
/*
* For the RK3399, the clk of grf must be enabled before writing grf
@ -630,6 +639,8 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
}
dw_mipi_dsi_rockchip_config(dsi, mux);
if (dsi->slave)
dw_mipi_dsi_rockchip_config(dsi->slave, mux);
clk_disable_unprepare(dsi->grf_clk);
}
@ -638,6 +649,8 @@ static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder)
{
struct dw_mipi_dsi_rockchip *dsi = to_dsi(encoder);
if (dsi->slave)
pm_runtime_put(dsi->slave->dev);
pm_runtime_put(dsi->dev);
}
@ -673,14 +686,113 @@ static int rockchip_dsi_drm_create_encoder(struct dw_mipi_dsi_rockchip *dsi,
return 0;
}
static struct device
*dw_mipi_dsi_rockchip_find_second(struct dw_mipi_dsi_rockchip *dsi)
{
const struct of_device_id *match;
struct device_node *node = NULL, *local;
match = of_match_device(dsi->dev->driver->of_match_table, dsi->dev);
local = of_graph_get_remote_node(dsi->dev->of_node, 1, 0);
if (!local)
return NULL;
while ((node = of_find_compatible_node(node, NULL,
match->compatible))) {
struct device_node *remote;
/* found ourself */
if (node == dsi->dev->of_node)
continue;
remote = of_graph_get_remote_node(node, 1, 0);
if (!remote)
continue;
/* same display device in port1-ep0 for both */
if (remote == local) {
struct dw_mipi_dsi_rockchip *dsi2;
struct platform_device *pdev;
pdev = of_find_device_by_node(node);
/*
* we have found the second, so will either return it
* or return with an error. In any case won't need the
* nodes anymore nor continue the loop.
*/
of_node_put(remote);
of_node_put(node);
of_node_put(local);
if (!pdev)
return ERR_PTR(-EPROBE_DEFER);
dsi2 = platform_get_drvdata(pdev);
if (!dsi2) {
platform_device_put(pdev);
return ERR_PTR(-EPROBE_DEFER);
}
return &pdev->dev;
}
of_node_put(remote);
}
of_node_put(local);
return NULL;
}
static int dw_mipi_dsi_rockchip_bind(struct device *dev,
struct device *master,
void *data)
{
struct dw_mipi_dsi_rockchip *dsi = dev_get_drvdata(dev);
struct drm_device *drm_dev = data;
struct device *second;
bool master1, master2;
int ret;
second = dw_mipi_dsi_rockchip_find_second(dsi);
if (IS_ERR(second))
return PTR_ERR(second);
if (second) {
master1 = of_property_read_bool(dsi->dev->of_node,
"clock-master");
master2 = of_property_read_bool(second->of_node,
"clock-master");
if (master1 && master2) {
DRM_DEV_ERROR(dsi->dev, "only one clock-master allowed\n");
return -EINVAL;
}
if (!master1 && !master2) {
DRM_DEV_ERROR(dsi->dev, "no clock-master defined\n");
return -EINVAL;
}
/* we are the slave in dual-DSI */
if (!master1) {
dsi->is_slave = true;
return 0;
}
dsi->slave = dev_get_drvdata(second);
if (!dsi->slave) {
DRM_DEV_ERROR(dev, "could not get slaves data\n");
return -ENODEV;
}
dsi->slave->is_slave = true;
dw_mipi_dsi_set_slave(dsi->dmd, dsi->slave->dmd);
put_device(second);
}
ret = clk_prepare_enable(dsi->pllref_clk);
if (ret) {
DRM_DEV_ERROR(dev, "Failed to enable pllref_clk: %d\n", ret);
@ -708,6 +820,9 @@ static void dw_mipi_dsi_rockchip_unbind(struct device *dev,
{
struct dw_mipi_dsi_rockchip *dsi = dev_get_drvdata(dev);
if (dsi->is_slave)
return;
dw_mipi_dsi_unbind(dsi->dmd);
clk_disable_unprepare(dsi->pllref_clk);
@ -722,6 +837,7 @@ static int dw_mipi_dsi_rockchip_host_attach(void *priv_data,
struct mipi_dsi_device *device)
{
struct dw_mipi_dsi_rockchip *dsi = priv_data;
struct device *second;
int ret;
ret = component_add(dsi->dev, &dw_mipi_dsi_rockchip_ops);
@ -731,6 +847,19 @@ static int dw_mipi_dsi_rockchip_host_attach(void *priv_data,
return ret;
}
second = dw_mipi_dsi_rockchip_find_second(dsi);
if (IS_ERR(second))
return PTR_ERR(second);
if (second) {
ret = component_add(second, &dw_mipi_dsi_rockchip_ops);
if (ret) {
DRM_DEV_ERROR(second,
"Failed to register component: %d\n",
ret);
return ret;
}
}
return 0;
}
@ -738,6 +867,11 @@ static int dw_mipi_dsi_rockchip_host_detach(void *priv_data,
struct mipi_dsi_device *device)
{
struct dw_mipi_dsi_rockchip *dsi = priv_data;
struct device *second;
second = dw_mipi_dsi_rockchip_find_second(dsi);
if (second && !IS_ERR(second))
component_del(second, &dw_mipi_dsi_rockchip_ops);
component_del(dsi->dev, &dw_mipi_dsi_rockchip_ops);
@ -846,6 +980,9 @@ static int dw_mipi_dsi_rockchip_remove(struct platform_device *pdev)
{
struct dw_mipi_dsi_rockchip *dsi = platform_get_drvdata(pdev);
if (dsi->devcnt == 0)
component_del(dsi->dev, &dw_mipi_dsi_rockchip_ops);
dw_mipi_dsi_remove(dsi->dmd);
return 0;

View File

@ -37,6 +37,7 @@ struct rockchip_crtc_state {
int output_type;
int output_mode;
int output_bpc;
int output_flags;
};
#define to_rockchip_crtc_state(s) \
container_of(s, struct rockchip_crtc_state, base)

View File

@ -916,6 +916,7 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) ?
BIT(VSYNC_POSITIVE) : 0;
VOP_REG_SET(vop, output, pin_pol, pin_pol);
VOP_REG_SET(vop, output, mipi_dual_channel_en, 0);
switch (s->output_type) {
case DRM_MODE_CONNECTOR_LVDS:
@ -933,6 +934,8 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
case DRM_MODE_CONNECTOR_DSI:
VOP_REG_SET(vop, output, mipi_pin_pol, pin_pol);
VOP_REG_SET(vop, output, mipi_en, 1);
VOP_REG_SET(vop, output, mipi_dual_channel_en,
!!(s->output_flags & ROCKCHIP_OUTPUT_DSI_DUAL));
break;
case DRM_MODE_CONNECTOR_DisplayPort:
pin_pol &= ~BIT(DCLK_INVERT);

View File

@ -60,6 +60,7 @@ struct vop_output {
struct vop_reg edp_en;
struct vop_reg hdmi_en;
struct vop_reg mipi_en;
struct vop_reg mipi_dual_channel_en;
struct vop_reg rgb_en;
};
@ -214,6 +215,9 @@ struct vop_data {
/* for use special outface */
#define ROCKCHIP_OUT_MODE_AAAA 15
/* output flags */
#define ROCKCHIP_OUTPUT_DSI_DUAL BIT(0)
enum alpha_mode {
ALPHA_STRAIGHT,
ALPHA_INVERSE,

View File

@ -634,6 +634,7 @@ static const struct vop_output rk3399_output = {
.hdmi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 13),
.edp_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 14),
.mipi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 15),
.mipi_dual_channel_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 3),
};
static const struct vop_data rk3399_vop_big = {