drm/rockchip: dsi: Add support for dual channel mode

Change-Id: I6fa8427c55b3efb611cf67087283bee7b95a4fd5
Signed-off-by: Wyon Bi <bivvy.bi@rock-chips.com>
This commit is contained in:
Wyon Bi
2019-07-10 08:53:59 +08:00
parent fb45f3f26c
commit 44246d97de
2 changed files with 183 additions and 157 deletions

View File

@@ -8,6 +8,9 @@ Required properties:
"rockchip,rk3399-mipi-dsi", "snps,dw-mipi-dsi".
- reg: Represent the physical address range of the controller.
- interrupts: Represent the controller's interrupt to the CPU(s).
- power-domains: a phandle to mipi dsi power domain node.
- resets: list of phandle + reset specifier pairs, as described in [3].
- reset-names: string reset name, must be "apb".
- clocks, clock-names: Phandles to the controller's pll reference
clock(ref) and APB clock(pclk). For RK3399, a phy config clock
(phy_cfg) and a grf clock(grf) are required. As described in [1].
@@ -16,10 +19,8 @@ Required properties:
For vopb,set the reg = <0> and set the reg = <1> for vopl.
Optional properties:
- power-domains: a phandle to mipi dsi power domain node.
- resets: list of phandle + reset specifier pairs, as described in [3].
- reset-names: string reset name, must be "apb".
- rockchip,lane-rate: optional override of the desired bandwidth.
- rockchip,dual-channel: for dual-channel mode, phandle to the slave channel.
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
[2] Documentation/devicetree/bindings/media/video-interfaces.txt

View File

@@ -246,13 +246,18 @@ struct dw_mipi_dsi {
struct drm_panel *panel;
struct drm_display_mode mode;
struct device *dev;
struct device_node *client;
struct regmap *grf;
struct clk *pclk;
struct mipi_dphy dphy;
struct regmap *regmap;
struct reset_control *rst;
int id;
int dpms_mode;
/* dual-channel */
struct dw_mipi_dsi *master;
struct dw_mipi_dsi *slave;
unsigned int lane_mbps; /* per lane */
u32 channel;
u32 lanes;
@@ -657,27 +662,24 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
{
struct dw_mipi_dsi *dsi = host_to_dsi(host);
if (device->lanes < 1 || device->lanes > 4)
if (dsi->master)
return 0;
if (device->lanes < 1 || device->lanes > 8)
return -EINVAL;
dsi->client = device->dev.of_node;
dsi->lanes = device->lanes;
dsi->channel = device->channel;
dsi->format = device->format;
dsi->mode_flags = device->mode_flags;
dsi->panel = of_drm_find_panel(device->dev.of_node);
if (!IS_ERR(dsi->panel))
return drm_panel_attach(dsi->panel, &dsi->connector);
return -EINVAL;
return 0;
}
static int dw_mipi_dsi_host_detach(struct mipi_dsi_host *host,
struct mipi_dsi_device *device)
{
struct dw_mipi_dsi *dsi = host_to_dsi(host);
drm_panel_detach(dsi->panel);
return 0;
}
@@ -902,6 +904,9 @@ static ssize_t dw_mipi_dsi_transfer(struct dw_mipi_dsi *dsi,
return ret;
}
if (dsi->slave)
dw_mipi_dsi_transfer(dsi->slave, msg);
return msg->tx_len;
}
@@ -940,7 +945,6 @@ static void dw_mipi_dsi_set_vid_mode(struct dw_mipi_dsi *dsi)
{
regmap_write(dsi->regmap, DSI_PWR_UP, RESET);
regmap_write(dsi->regmap, DSI_MODE_CFG, CMD_VIDEO_MODE(VIDEO_MODE));
dw_mipi_dsi_video_mode_config(dsi);
regmap_write(dsi->regmap, DSI_LPCLK_CTRL, PHY_TXREQUESTCLKHS);
regmap_write(dsi->regmap, DSI_PWR_UP, POWER_UP);
}
@@ -955,6 +959,23 @@ static void dw_mipi_dsi_set_cmd_mode(struct dw_mipi_dsi *dsi)
static void dw_mipi_dsi_disable(struct dw_mipi_dsi *dsi)
{
regmap_write(dsi->regmap, DSI_PWR_UP, RESET);
regmap_write(dsi->regmap, DSI_LPCLK_CTRL, 0);
regmap_write(dsi->regmap, DSI_MODE_CFG, CMD_VIDEO_MODE(COMMAND_MODE));
regmap_write(dsi->regmap, DSI_PWR_UP, POWER_UP);
if (dsi->slave)
dw_mipi_dsi_disable(dsi->slave);
}
static void dw_mipi_dsi_post_disable(struct dw_mipi_dsi *dsi)
{
regmap_write(dsi->regmap, DSI_PWR_UP, RESET);
mipi_dphy_power_off(dsi);
clk_disable_unprepare(dsi->pclk);
pm_runtime_put(dsi->dev);
if (dsi->slave)
dw_mipi_dsi_post_disable(dsi->slave);
}
static void dw_mipi_dsi_init(struct dw_mipi_dsi *dsi)
@@ -1021,9 +1042,14 @@ static void dw_mipi_dsi_packet_handler_config(struct dw_mipi_dsi *dsi)
static void dw_mipi_dsi_video_packet_config(struct dw_mipi_dsi *dsi)
{
struct drm_display_mode *mode = &dsi->mode;
u32 vid_pkt_size;
regmap_write(dsi->regmap, DSI_VID_PKT_SIZE,
VID_PKT_SIZE(mode->hdisplay));
if (dsi->slave || dsi->master)
vid_pkt_size = mode->hdisplay / 2;
else
vid_pkt_size = mode->hdisplay;
regmap_write(dsi->regmap, DSI_VID_PKT_SIZE, VID_PKT_SIZE(vid_pkt_size));
}
static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi)
@@ -1111,24 +1137,10 @@ static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder)
{
struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
if (dsi->dpms_mode != DRM_MODE_DPMS_ON)
return;
if (clk_prepare_enable(dsi->pclk)) {
DRM_DEV_ERROR(dsi->dev, "Failed to enable pclk\n");
return;
}
drm_panel_disable(dsi->panel);
dw_mipi_dsi_set_cmd_mode(dsi);
drm_panel_unprepare(dsi->panel);
dw_mipi_dsi_disable(dsi);
mipi_dphy_power_off(dsi);
pm_runtime_put(dsi->dev);
clk_disable_unprepare(dsi->pclk);
dsi->dpms_mode = DRM_MODE_DPMS_OFF;
drm_panel_unprepare(dsi->panel);
dw_mipi_dsi_post_disable(dsi);
}
static void dw_mipi_dsi_vop_routing(struct dw_mipi_dsi *dsi)
@@ -1139,6 +1151,8 @@ static void dw_mipi_dsi_vop_routing(struct dw_mipi_dsi *dsi)
&dsi->encoder);
grf_field_write(dsi, VOPSEL, pipe);
if (dsi->slave)
grf_field_write(dsi->slave, VOPSEL, pipe);
}
static unsigned long dw_mipi_dsi_get_lane_rate(struct dw_mipi_dsi *dsi)
@@ -1159,7 +1173,7 @@ static unsigned long dw_mipi_dsi_get_lane_rate(struct dw_mipi_dsi *dsi)
if (bpp < 0)
bpp = 24;
lanes = dsi->lanes;
lanes = dsi->slave ? dsi->lanes * 2 : dsi->lanes;
tmp = (u64)mode->clock * 1000 * bpp;
do_div(tmp, lanes);
@@ -1238,29 +1252,19 @@ static void dw_mipi_dsi_calc_pll_cfg(struct dw_mipi_dsi *dsi,
dsi->lane_mbps = best_freq / USEC_PER_SEC;
dphy->input_div = best_prediv;
dphy->feedback_div = best_fbdiv;
if (dsi->slave) {
dsi->slave->lane_mbps = dsi->lane_mbps;
dsi->slave->dphy.input_div = dphy->input_div;
dsi->slave->dphy.feedback_div = dphy->feedback_div;
}
}
static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
static void dw_mipi_dsi_pre_enable(struct dw_mipi_dsi *dsi)
{
struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
unsigned long lane_rate = dw_mipi_dsi_get_lane_rate(dsi);
if (dsi->dpms_mode == DRM_MODE_DPMS_ON)
return;
dw_mipi_dsi_calc_pll_cfg(dsi, lane_rate);
DRM_DEV_INFO(dsi->dev, "final DSI-Link bandwidth: %u x %d Mbps\n",
dsi->lane_mbps, dsi->lanes);
dw_mipi_dsi_vop_routing(dsi);
if (clk_prepare_enable(dsi->pclk)) {
DRM_DEV_ERROR(dsi->dev, "Failed to enable pclk\n");
return;
}
pm_runtime_get_sync(dsi->dev);
clk_prepare_enable(dsi->pclk);
dw_mipi_dsi_init(dsi);
dw_mipi_dsi_dpi_config(dsi);
dw_mipi_dsi_packet_handler_config(dsi);
@@ -1272,19 +1276,35 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
dw_mipi_dsi_dphy_timing_config(dsi);
dw_mipi_dsi_dphy_interface_config(dsi);
dw_mipi_dsi_clear_err(dsi);
mipi_dphy_power_on(dsi);
dw_mipi_dsi_set_cmd_mode(dsi);
if (drm_panel_prepare(dsi->panel))
DRM_DEV_ERROR(dsi->dev, "failed to prepare panel\n");
if (dsi->slave)
dw_mipi_dsi_pre_enable(dsi->slave);
}
static void dw_mipi_dsi_enable(struct dw_mipi_dsi *dsi)
{
dw_mipi_dsi_set_vid_mode(dsi);
if (dsi->slave)
dw_mipi_dsi_enable(dsi->slave);
}
static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
{
struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
unsigned long lane_rate = dw_mipi_dsi_get_lane_rate(dsi);
dw_mipi_dsi_calc_pll_cfg(dsi, lane_rate);
DRM_DEV_INFO(dsi->dev, "final DSI-Link bandwidth: %u x %d Mbps\n",
dsi->lane_mbps, dsi->slave ? dsi->lanes * 2 : dsi->lanes);
dw_mipi_dsi_vop_routing(dsi);
dw_mipi_dsi_pre_enable(dsi);
drm_panel_prepare(dsi->panel);
dw_mipi_dsi_enable(dsi);
drm_panel_enable(dsi->panel);
clk_disable_unprepare(dsi->pclk);
dsi->dpms_mode = DRM_MODE_DPMS_ON;
}
static int
@@ -1312,6 +1332,9 @@ 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_CHANNEL;
return 0;
}
@@ -1323,6 +1346,8 @@ dw_mipi_dsi_encoder_atomic_mode_set(struct drm_encoder *encoder,
struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
drm_mode_copy(&dsi->mode, &crtc_state->adjusted_mode);
if (dsi->slave)
drm_mode_copy(&dsi->slave->mode, &crtc_state->adjusted_mode);
}
static const struct drm_encoder_helper_funcs
@@ -1362,14 +1387,52 @@ static const struct drm_connector_funcs dw_mipi_dsi_atomic_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int dw_mipi_dsi_register(struct drm_device *drm,
struct dw_mipi_dsi *dsi)
static int dw_mipi_dsi_dual_channel_probe(struct dw_mipi_dsi *dsi)
{
struct device_node *np;
struct platform_device *secondary;
np = of_parse_phandle(dsi->dev->of_node, "rockchip,dual-channel", 0);
if (np) {
secondary = of_find_device_by_node(np);
dsi->slave = platform_get_drvdata(secondary);
of_node_put(np);
if (!dsi->slave)
return -EPROBE_DEFER;
dsi->slave->master = dsi;
dsi->lanes /= 2;
dsi->slave->lanes = dsi->lanes;
dsi->slave->channel = dsi->channel;
dsi->slave->format = dsi->format;
dsi->slave->mode_flags = dsi->mode_flags;
}
return 0;
}
static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
void *data)
{
struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
struct drm_device *drm = data;
struct drm_encoder *encoder = &dsi->encoder;
struct drm_connector *connector = &dsi->connector;
struct device *dev = dsi->dev;
int ret;
ret = dw_mipi_dsi_dual_channel_probe(dsi);
if (ret)
return ret;
if (dsi->master)
return 0;
dsi->panel = of_drm_find_panel(dsi->client);
if (IS_ERR(dsi->panel))
return -EPROBE_DEFER;
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm,
dev->of_node);
/*
@@ -1381,27 +1444,51 @@ static int dw_mipi_dsi_register(struct drm_device *drm,
if (encoder->possible_crtcs == 0)
return -EPROBE_DEFER;
drm_encoder_helper_add(&dsi->encoder,
&dw_mipi_dsi_encoder_helper_funcs);
ret = drm_encoder_init(drm, &dsi->encoder, &dw_mipi_dsi_encoder_funcs,
ret = drm_encoder_init(drm, encoder, &dw_mipi_dsi_encoder_funcs,
DRM_MODE_ENCODER_DSI, NULL);
if (ret) {
DRM_DEV_ERROR(dev, "Failed to initialize encoder with drm\n");
return ret;
}
drm_encoder_helper_add(encoder, &dw_mipi_dsi_encoder_helper_funcs);
drm_connector_init(drm, connector, &dw_mipi_dsi_atomic_connector_funcs,
DRM_MODE_CONNECTOR_DSI);
drm_connector_helper_add(connector,
&dw_mipi_dsi_connector_helper_funcs);
drm_connector_init(drm, &dsi->connector,
&dw_mipi_dsi_atomic_connector_funcs,
DRM_MODE_CONNECTOR_DSI);
drm_connector_attach_encoder(connector, encoder);
ret = drm_panel_attach(dsi->panel, connector);
if (ret) {
DRM_DEV_ERROR(dsi->dev, "Failed to attach panel: %d\n", ret);
goto err_cleanup;
}
return 0;
err_cleanup:
connector->funcs->destroy(connector);
encoder->funcs->destroy(encoder);
return ret;
}
static void dw_mipi_dsi_unbind(struct device *dev, struct device *master,
void *data)
{
struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
drm_panel_detach(dsi->panel);
dsi->connector.funcs->destroy(&dsi->connector);
dsi->encoder.funcs->destroy(&dsi->encoder);
}
static const struct component_ops dw_mipi_dsi_ops = {
.bind = dw_mipi_dsi_bind,
.unbind = dw_mipi_dsi_unbind,
};
static const struct regmap_config dw_mipi_dsi_regmap_config = {
.name = "host",
.reg_bits = 32,
@@ -1411,12 +1498,9 @@ static const struct regmap_config dw_mipi_dsi_regmap_config = {
.max_register = DSI_MAX_REGISGER,
};
static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
void *data)
static int dw_mipi_dsi_probe(struct platform_device *pdev)
{
struct platform_device *pdev = to_platform_device(dev);
struct reset_control *apb_rst;
struct drm_device *drm = data;
struct device *dev = &pdev->dev;
struct dw_mipi_dsi *dsi;
struct resource *res;
void __iomem *regs;
@@ -1434,13 +1518,20 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
dsi->dev = dev;
dsi->id = id;
dsi->pdata = of_device_get_match_data(dev);
dsi->dpms_mode = DRM_MODE_DPMS_OFF;
platform_set_drvdata(pdev, dsi);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(dev, res);
if (IS_ERR(regs))
return PTR_ERR(regs);
dsi->pclk = devm_clk_get(dev, "pclk");
if (IS_ERR(dsi->pclk)) {
ret = PTR_ERR(dsi->pclk);
DRM_DEV_ERROR(dev, "Unable to get pclk: %d\n", ret);
return ret;
}
dsi->regmap = devm_regmap_init_mmio(dev, regs,
&dw_mipi_dsi_regmap_config);
if (IS_ERR(dsi->regmap)) {
@@ -1449,17 +1540,6 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
return ret;
}
ret = mipi_dphy_attach(dsi);
if (ret)
return ret;
dsi->pclk = devm_clk_get(dev, "pclk");
if (IS_ERR(dsi->pclk)) {
ret = PTR_ERR(dsi->pclk);
DRM_DEV_ERROR(dev, "Unable to get pclk: %d\n", ret);
return ret;
}
dsi->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
"rockchip,grf");
if (IS_ERR(dsi->grf)) {
@@ -1468,93 +1548,38 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
return ret;
}
/*
* Note that the reset was not defined in the initial device tree, so
* we have to be prepared for it not being found.
*/
apb_rst = devm_reset_control_get(dev, "apb");
if (IS_ERR(apb_rst)) {
ret = PTR_ERR(apb_rst);
if (ret == -ENOENT) {
apb_rst = NULL;
} else {
DRM_DEV_ERROR(dev,
"Unable to get reset control: %d\n", ret);
return ret;
}
}
if (apb_rst) {
ret = clk_prepare_enable(dsi->pclk);
if (ret) {
DRM_DEV_ERROR(dev, "Failed to enable pclk\n");
return ret;
}
reset_control_assert(apb_rst);
usleep_range(10, 20);
reset_control_deassert(apb_rst);
clk_disable_unprepare(dsi->pclk);
}
ret = dw_mipi_dsi_register(drm, dsi);
if (ret) {
DRM_DEV_ERROR(dev, "Failed to register mipi_dsi: %d\n", ret);
dsi->rst = devm_reset_control_get(dev, "apb");
if (IS_ERR(dsi->rst)) {
ret = PTR_ERR(dsi->rst);
DRM_DEV_ERROR(dev,
"Unable to get reset control: %d\n", ret);
return ret;
}
ret = mipi_dphy_attach(dsi);
if (ret)
return ret;
dsi->host.ops = &dw_mipi_dsi_host_ops;
dsi->host.dev = dev;
ret = mipi_dsi_host_register(&dsi->host);
if (ret) {
DRM_DEV_ERROR(dev, "Failed to register MIPI host: %d\n", ret);
goto err_cleanup;
return ret;
}
if (!dsi->panel) {
ret = -EPROBE_DEFER;
goto err_mipi_dsi_host;
}
dev_set_drvdata(dev, dsi);
pm_runtime_enable(dev);
return 0;
err_mipi_dsi_host:
mipi_dsi_host_unregister(&dsi->host);
err_cleanup:
dsi->connector.funcs->destroy(&dsi->connector);
dsi->encoder.funcs->destroy(&dsi->encoder);
return ret;
}
static void dw_mipi_dsi_unbind(struct device *dev, struct device *master,
void *data)
{
struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
mipi_dsi_host_unregister(&dsi->host);
pm_runtime_disable(dev);
dsi->connector.funcs->destroy(&dsi->connector);
dsi->encoder.funcs->destroy(&dsi->encoder);
}
static const struct component_ops dw_mipi_dsi_ops = {
.bind = dw_mipi_dsi_bind,
.unbind = dw_mipi_dsi_unbind,
};
static int dw_mipi_dsi_probe(struct platform_device *pdev)
{
return component_add(&pdev->dev, &dw_mipi_dsi_ops);
}
static int dw_mipi_dsi_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &dw_mipi_dsi_ops);
struct dw_mipi_dsi *dsi = platform_get_drvdata(pdev);
component_del(dsi->dev, &dw_mipi_dsi_ops);
mipi_dsi_host_unregister(&dsi->host);
pm_runtime_disable(dsi->dev);
return 0;
}