Newer
Older
/* Attach the panel-bridge to the dsi bridge */
return drm_bridge_attach(bridge->encoder, dsi->panel_bridge, bridge,
flags);
static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_reset = drm_atomic_helper_bridge_reset,
.atomic_pre_enable = dw_mipi_dsi_bridge_atomic_pre_enable,
.atomic_enable = dw_mipi_dsi_bridge_atomic_enable,
.atomic_post_disable = dw_mipi_dsi_bridge_post_atomic_disable,
.mode_set = dw_mipi_dsi_bridge_mode_set,
.mode_valid = dw_mipi_dsi_bridge_mode_valid,
.attach = dw_mipi_dsi_bridge_attach,
#ifdef CONFIG_DEBUG_FS
static int dw_mipi_dsi_debugfs_write(void *data, u64 val)
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
{
struct debugfs_entries *vpg = data;
struct dw_mipi_dsi *dsi;
u32 mode_cfg;
if (!vpg)
return -ENODEV;
dsi = vpg->dsi;
*vpg->reg = (bool)val;
mode_cfg = dsi_read(dsi, DSI_VID_MODE_CFG);
if (*vpg->reg)
mode_cfg |= vpg->mask;
else
mode_cfg &= ~vpg->mask;
dsi_write(dsi, DSI_VID_MODE_CFG, mode_cfg);
return 0;
}
static int dw_mipi_dsi_debugfs_show(void *data, u64 *val)
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
{
struct debugfs_entries *vpg = data;
if (!vpg)
return -ENODEV;
*val = *vpg->reg;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_x32, dw_mipi_dsi_debugfs_show,
dw_mipi_dsi_debugfs_write, "%llu\n");
static void debugfs_create_files(void *data)
{
struct dw_mipi_dsi *dsi = data;
struct debugfs_entries debugfs[] = {
REGISTER(vpg, VID_MODE_VPG_ENABLE, dsi),
REGISTER(vpg_horizontal, VID_MODE_VPG_HORIZONTAL, dsi),
REGISTER(vpg_ber_pattern, VID_MODE_VPG_MODE, dsi),
};
int i;
dsi->debugfs_vpg = kmemdup(debugfs, sizeof(debugfs), GFP_KERNEL);
if (!dsi->debugfs_vpg)
return;
for (i = 0; i < ARRAY_SIZE(debugfs); i++)
debugfs_create_file(dsi->debugfs_vpg[i].name, 0644,
dsi->debugfs, &dsi->debugfs_vpg[i],
&fops_x32);
}
static void dw_mipi_dsi_debugfs_init(struct dw_mipi_dsi *dsi)
{
dsi->debugfs = debugfs_create_dir(dev_name(dsi->dev), NULL);
if (IS_ERR(dsi->debugfs)) {
dev_err(dsi->dev, "failed to create debugfs root\n");
return;
}
debugfs_create_files(dsi);
}
static void dw_mipi_dsi_debugfs_remove(struct dw_mipi_dsi *dsi)
{
debugfs_remove_recursive(dsi->debugfs);
kfree(dsi->debugfs_vpg);
}
#else
static void dw_mipi_dsi_debugfs_init(struct dw_mipi_dsi *dsi) { }
static void dw_mipi_dsi_debugfs_remove(struct dw_mipi_dsi *dsi) { }
#endif /* CONFIG_DEBUG_FS */
static struct dw_mipi_dsi *
__dw_mipi_dsi_probe(struct platform_device *pdev,
const struct dw_mipi_dsi_plat_data *plat_data)
{
struct device *dev = &pdev->dev;
struct reset_control *apb_rst;
struct dw_mipi_dsi *dsi;
int ret;
dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
if (!dsi)
return ERR_PTR(-ENOMEM);
dsi->dev = dev;
dsi->plat_data = plat_data;
if (!plat_data->phy_ops->init || !plat_data->phy_ops->get_lane_mbps ||
!plat_data->phy_ops->get_timing) {
DRM_ERROR("Phy not properly configured\n");
return ERR_PTR(-ENODEV);
}
if (!plat_data->base) {

Markus Elfring
committed
dsi->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(dsi->base))
return ERR_PTR(-ENODEV);
} else {
dsi->base = plat_data->base;
}
dsi->pclk = devm_clk_get(dev, "pclk");
if (IS_ERR(dsi->pclk)) {
ret = PTR_ERR(dsi->pclk);
dev_err(dev, "Unable to get pclk: %d\n", ret);
return ERR_PTR(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_optional_exclusive(dev, "apb");
if (IS_ERR(apb_rst)) {
ret = PTR_ERR(apb_rst);
if (ret != -EPROBE_DEFER)
dev_err(dev, "Unable to get reset control: %d\n", ret);
return ERR_PTR(ret);
}
if (apb_rst) {
ret = clk_prepare_enable(dsi->pclk);
if (ret) {
dev_err(dev, "%s: Failed to enable pclk\n", __func__);
return ERR_PTR(ret);
}
reset_control_assert(apb_rst);
usleep_range(10, 20);
reset_control_deassert(apb_rst);
clk_disable_unprepare(dsi->pclk);
}
dw_mipi_dsi_debugfs_init(dsi);
pm_runtime_enable(dev);
dsi->dsi_host.ops = &dw_mipi_dsi_host_ops;
dsi->dsi_host.dev = dev;
ret = mipi_dsi_host_register(&dsi->dsi_host);
if (ret) {
dev_err(dev, "Failed to register MIPI host: %d\n", ret);
pm_runtime_disable(dev);
dw_mipi_dsi_debugfs_remove(dsi);
return ERR_PTR(ret);
}
dsi->bridge.driver_private = dsi;
dsi->bridge.funcs = &dw_mipi_dsi_bridge_funcs;
dsi->bridge.of_node = pdev->dev.of_node;
return dsi;
}
static void __dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi)
{

Heiko Stuebner
committed
mipi_dsi_host_unregister(&dsi->dsi_host);
pm_runtime_disable(dsi->dev);
dw_mipi_dsi_debugfs_remove(dsi);
void dw_mipi_dsi_set_slave(struct dw_mipi_dsi *dsi, struct dw_mipi_dsi *slave)
{
/* introduce controllers to each other */
dsi->slave = slave;
dsi->slave->master = dsi;
/* migrate settings for already attached displays */
dsi->slave->lanes = dsi->lanes;
dsi->slave->channel = dsi->channel;
dsi->slave->format = dsi->format;
dsi->slave->mode_flags = dsi->mode_flags;
}
EXPORT_SYMBOL_GPL(dw_mipi_dsi_set_slave);
struct drm_bridge *dw_mipi_dsi_get_bridge(struct dw_mipi_dsi *dsi)
{
return &dsi->bridge;
}
EXPORT_SYMBOL_GPL(dw_mipi_dsi_get_bridge);
/*
* Probe/remove API, used from platforms based on the DRM bridge API.
*/
struct dw_mipi_dsi *
dw_mipi_dsi_probe(struct platform_device *pdev,
const struct dw_mipi_dsi_plat_data *plat_data)
return __dw_mipi_dsi_probe(pdev, plat_data);
}
EXPORT_SYMBOL_GPL(dw_mipi_dsi_probe);
void dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi)
{
__dw_mipi_dsi_remove(dsi);
}
EXPORT_SYMBOL_GPL(dw_mipi_dsi_remove);
/*
* Bind/unbind API, used from platforms based on the component framework.
*/

Heiko Stuebner
committed
int dw_mipi_dsi_bind(struct dw_mipi_dsi *dsi, struct drm_encoder *encoder)
return drm_bridge_attach(encoder, &dsi->bridge, NULL, 0);
}
EXPORT_SYMBOL_GPL(dw_mipi_dsi_bind);
void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi)
{
}
EXPORT_SYMBOL_GPL(dw_mipi_dsi_unbind);
MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
MODULE_DESCRIPTION("DW MIPI DSI host controller driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:dw-mipi-dsi");