blob: 0b5d2f9f2ee9a8c42fe17a309df9229fe18b4de1 [file] [log] [blame]
/*
* Copyright (c) 2016, 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.
*
*/
#define pr_fmt(fmt) "dm-drm:[%s] " fmt, __func__
#include <linux/of_device.h>
#include <linux/err.h>
#include <linux/regulator/consumer.h>
#include <linux/clk.h>
#include <linux/msm-bus.h>
#include <linux/of_irq.h>
#include "msm_drv.h"
#include "msm_kms.h"
#include "msm_gpu.h"
#include "sde_connector.h"
#include "dsi_display.h"
#include "dsi_drm.h"
#include "sde_wb.h"
#include "display_manager.h"
/**
* _dm_cache_active_displays - determine display type based on index
* @disp_m: Pointer to display manager structure
* Returns: Number of active displays in the system
*/
static u32 _dm_cache_active_displays(struct display_manager *disp_m)
{
u32 count;
if (!disp_m)
return 0;
disp_m->display_count = 0;
/* query dsi displays */
disp_m->dsi_display_count = dsi_display_get_num_of_displays();
/* query hdmi displays */
disp_m->hdmi_display_count = 0;
/* query dp displays */
disp_m->dp_display_count = 0;
/* query wb displays */
disp_m->wb_display_count = sde_wb_get_num_of_displays();
DBG("wb display count=%d", disp_m->wb_display_count);
count = disp_m->dsi_display_count
+ disp_m->hdmi_display_count
+ disp_m->dp_display_count
+ disp_m->wb_display_count;
disp_m->displays = kcalloc(count, sizeof(void *), GFP_KERNEL);
if (!disp_m->displays) {
disp_m->dsi_displays = 0;
disp_m->dsi_display_count = 0;
disp_m->hdmi_displays = 0;
disp_m->hdmi_display_count = 0;
disp_m->dp_displays = 0;
disp_m->dp_display_count = 0;
disp_m->wb_displays = 0;
disp_m->wb_display_count = 0;
} else {
/* get final dsi display list */
disp_m->dsi_displays = disp_m->displays;
disp_m->dsi_display_count =
dsi_display_get_active_displays(disp_m->dsi_displays,
disp_m->dsi_display_count);
/* get final hdmi display list */
disp_m->hdmi_displays = disp_m->dsi_displays
+ disp_m->dsi_display_count;
disp_m->hdmi_display_count = 0;
/* get final dp display list */
disp_m->dp_displays = disp_m->hdmi_displays
+ disp_m->hdmi_display_count;
disp_m->dp_display_count = 0;
/* get final wb display list */
disp_m->wb_displays = disp_m->dp_displays
+ disp_m->dp_display_count;
disp_m->wb_display_count =
wb_display_get_displays(disp_m->wb_displays,
disp_m->wb_display_count);
}
/* set final display count */
disp_m->display_count = disp_m->dsi_display_count
+ disp_m->hdmi_display_count
+ disp_m->dp_display_count
+ disp_m->wb_display_count;
return disp_m->display_count;
}
/**
* _dm_get_type_by_index - determine display type based on index
* @disp_m: Pointer to display manager structure
* @display_index: Incoming display index
* Returns: DRM_MODE_CONNECTOR_ definition corresponding to display_index
*/
static int _dm_get_type_by_index(struct display_manager *disp_m,
u32 display_index)
{
if (disp_m) {
if (display_index < disp_m->dsi_display_count)
return DRM_MODE_CONNECTOR_DSI;
display_index -= disp_m->dsi_display_count;
if (display_index < disp_m->hdmi_display_count)
return DRM_MODE_CONNECTOR_HDMIA;
display_index -= disp_m->hdmi_display_count;
if (display_index < disp_m->dp_display_count)
return DRM_MODE_CONNECTOR_DisplayPort;
display_index -= disp_m->dp_display_count;
if (display_index < disp_m->wb_display_count)
return DRM_MODE_CONNECTOR_VIRTUAL;
display_index -= disp_m->wb_display_count;
}
return DRM_MODE_CONNECTOR_Unknown;
}
/**
* _dm_init_active_displays - initialize active display drivers
* @disp_m: Pointer to display manager structure
* Returns: Zero on success
*/
static int _dm_init_active_displays(struct display_manager *disp_m)
{
void *display;
int rc = 0;
int dsi_idx, wb_idx;
for (dsi_idx = 0; dsi_idx < disp_m->dsi_display_count; dsi_idx++) {
display = disp_m->dsi_displays[dsi_idx];
rc = dsi_display_dev_init(display);
if (rc) {
pr_err("failed to init dsi display, rc=%d\n", rc);
goto error_deinit_dsi_displays;
}
}
for (wb_idx = 0; wb_idx < disp_m->wb_display_count; wb_idx++) {
display = disp_m->wb_displays[wb_idx];
rc = sde_wb_dev_init(display);
if (rc) {
pr_err("failed to init wb display, rc=%d\n", rc);
goto error_deinit_sde_wb;
}
}
/* TODO: INIT HDMI and DP displays here */
return rc;
error_deinit_sde_wb:
for (wb_idx = wb_idx - 1; wb_idx >= 0; wb_idx--) {
display = disp_m->wb_displays[wb_idx];
(void)sde_wb_dev_deinit(display);
}
error_deinit_dsi_displays:
for (dsi_idx = dsi_idx - 1; dsi_idx >= 0; dsi_idx--) {
display = disp_m->dsi_displays[dsi_idx];
(void)dsi_display_dev_deinit(display);
}
return rc;
}
/**
* _dm_deinit_active_displays - deconstruct active display drivers
* @disp_m: Pointer to display manager structure
* Returns: Zero on success
*/
static void _dm_deinit_active_displays(struct display_manager *disp_m)
{
void *display;
int rc, i;
for (i = 0; i < disp_m->wb_display_count; i++) {
display = disp_m->wb_displays[i];
rc = sde_wb_dev_deinit(display);
if (rc)
pr_err("failed to deinit wb display, rc=%d\n", rc);
}
for (i = 0; i < disp_m->dsi_display_count; i++) {
display = disp_m->dsi_displays[i];
rc = dsi_display_dev_deinit(display);
if (rc)
pr_err("failed to deinit dsi display, rc=%d\n", rc);
}
/* TODO: DEINIT HDMI and DP displays here */
}
static int disp_manager_comp_ops_bind(struct device *dev,
struct device *master,
void *data)
{
struct drm_device *drm;
struct msm_drm_private *priv;
struct display_manager *disp_m;
void *display;
int dsi_idx, wb_idx;
int rc = -EINVAL;
if (master && dev) {
drm = dev_get_drvdata(master);
disp_m = platform_get_drvdata(to_platform_device(dev));
if (drm && drm->dev_private && disp_m)
rc = 0;
}
if (rc) {
pr_err("Invalid params\n");
return -EINVAL;
}
priv = drm->dev_private;
disp_m->drm_dev = drm;
/* DSI displays */
for (dsi_idx = 0; dsi_idx < disp_m->dsi_display_count; dsi_idx++) {
display = disp_m->dsi_displays[dsi_idx];
rc = dsi_display_bind(display, drm);
if (rc) {
if (rc != -EPROBE_DEFER)
pr_err("Failed to bind dsi display_%d, rc=%d\n",
dsi_idx, rc);
goto error_unbind_dsi;
}
}
/* WB displays */
for (wb_idx = 0; wb_idx < disp_m->wb_display_count; wb_idx++) {
display = disp_m->wb_displays[wb_idx];
rc = sde_wb_bind(display, drm);
if (rc) {
pr_err("Failed to bind wb display_%d, rc=%d\n",
wb_idx, rc);
goto error_unbind_wb;
}
}
/* TODO: BIND HDMI display here */
/* TODO: BIND DP display here */
priv->dm = disp_m;
return rc;
error_unbind_wb:
for (wb_idx = wb_idx - 1; wb_idx >= 0; wb_idx--) {
display = disp_m->wb_displays[wb_idx];
(void)sde_wb_unbind(display);
}
error_unbind_dsi:
for (dsi_idx = dsi_idx - 1; dsi_idx >= 0; dsi_idx--) {
display = disp_m->dsi_displays[dsi_idx];
(void)dsi_display_unbind(display);
}
return rc;
}
static void disp_manager_comp_ops_unbind(struct device *dev,
struct device *master,
void *data)
{
int rc = 0;
struct platform_device *pdev = to_platform_device(dev);
struct display_manager *disp_m;
void *display;
int i;
if (!dev) {
pr_err("Invalid params\n");
return;
}
disp_m = platform_get_drvdata(pdev);
/* WB displays */
for (i = 0; i < disp_m->wb_display_count; i++) {
display = disp_m->wb_displays[i];
rc = sde_wb_unbind(display);
if (rc)
pr_err("failed to unbind wb display_%d, rc=%d\n",
i, rc);
}
/* DSI displays */
for (i = 0; i < disp_m->dsi_display_count; i++) {
display = disp_m->dsi_displays[i];
rc = dsi_display_unbind(display);
if (rc)
pr_err("failed to unbind dsi display_%d, rc=%d\n",
i, rc);
}
/* TODO: UNBIND HDMI display here */
/* TODO: UNBIND DP display here */
}
static const struct of_device_id displays_dt_match[] = {
{.compatible = "qcom,dsi-display"},
{.compatible = "qcom,hdmi-display"},
{.compatible = "qcom,dp-display"},
{.compatible = "qcom,wb-display"},
{}
};
static const struct component_ops disp_manager_comp_ops = {
.bind = disp_manager_comp_ops_bind,
.unbind = disp_manager_comp_ops_unbind,
};
static int disp_manager_dev_probe(struct platform_device *pdev)
{
struct display_manager *disp_m;
int rc = 0;
if (!pdev || !pdev->dev.of_node) {
pr_err("pdev not found\n");
return -ENODEV;
}
disp_m = devm_kzalloc(&pdev->dev, sizeof(*disp_m), GFP_KERNEL);
if (!disp_m)
return -ENOMEM;
disp_m->name = "qcom,display-manager";
of_platform_populate(pdev->dev.of_node, displays_dt_match,
NULL, &pdev->dev);
disp_m->display_count = _dm_cache_active_displays(disp_m);
if (!disp_m->display_count) {
rc = -ENODEV;
pr_err("no displays found, rc=%d\n", rc);
goto error_free_disp_m;
}
rc = _dm_init_active_displays(disp_m);
if (rc) {
pr_err("failed to initialize displays, rc=%d\n", rc);
goto error_remove_displays;
}
rc = component_add(&pdev->dev, &disp_manager_comp_ops);
if (rc) {
pr_err("failed to add component, rc=%d\n", rc);
goto error_deinit_displays;
}
mutex_init(&disp_m->lock);
platform_set_drvdata(pdev, disp_m);
return rc;
error_deinit_displays:
_dm_deinit_active_displays(disp_m);
error_remove_displays:
of_platform_depopulate(&pdev->dev);
error_free_disp_m:
devm_kfree(&pdev->dev, disp_m);
return rc;
}
static int disp_manager_dev_remove(struct platform_device *pdev)
{
struct display_manager *disp_m;
if (!pdev) {
pr_err("invalid pdev argument\n");
return -ENODEV;
}
disp_m = platform_get_drvdata(pdev);
_dm_deinit_active_displays(disp_m);
of_platform_depopulate(&pdev->dev);
devm_kfree(&pdev->dev, disp_m);
return 0;
}
static const struct of_device_id disp_manager_dt_match[] = {
{.compatible = "qcom,display-manager"},
{}
};
static struct platform_driver disp_manager_driver = {
.probe = disp_manager_dev_probe,
.remove = disp_manager_dev_remove,
.driver = {
.name = "msm-display-manager",
.of_match_table = disp_manager_dt_match,
},
};
int display_manager_get_count(struct display_manager *disp_m)
{
int count;
if (!disp_m) {
pr_err("invalid params\n");
return 0;
}
mutex_lock(&disp_m->lock);
count = disp_m->display_count;
mutex_unlock(&disp_m->lock);
return count;
}
int display_manager_get_info_by_index(struct display_manager *disp_m,
u32 display_index,
struct msm_display_info *info)
{
void *display;
int rc = 0;
if (!disp_m || !info || (display_index >= disp_m->display_count)) {
pr_err("Invalid params\n");
return -EINVAL;
}
memset(info, 0, sizeof(*info));
mutex_lock(&disp_m->lock);
display = disp_m->displays[display_index];
switch (_dm_get_type_by_index(disp_m, display_index)) {
case DRM_MODE_CONNECTOR_DSI:
rc = dsi_display_get_info(info, display);
if (rc) {
pr_err("failed to get dsi info, rc=%d\n", rc);
rc = -EINVAL;
}
break;
case DRM_MODE_CONNECTOR_VIRTUAL:
rc = sde_wb_get_info(info, display);
if (rc) {
pr_err("failed to get wb info, rc=%d\n", rc);
rc = -EINVAL;
}
break;
default:
pr_err("invalid index %d\n", display_index);
rc = -EINVAL;
break;
}
mutex_unlock(&disp_m->lock);
return rc;
}
int display_manager_drm_init_by_index(struct display_manager *disp_m,
u32 display_index,
struct drm_encoder *encoder)
{
static const struct sde_connector_ops dsi_ops = {
.post_init = dsi_conn_post_init,
.detect = dsi_conn_detect,
.get_modes = dsi_connector_get_modes,
.mode_valid = dsi_conn_mode_valid,
.get_info = dsi_display_get_info,
};
static const struct sde_connector_ops wb_ops = {
.post_init = sde_wb_connector_post_init,
.detect = sde_wb_connector_detect,
.get_modes = sde_wb_connector_get_modes,
.set_property = sde_wb_connector_set_property,
.get_info = sde_wb_get_info,
};
void *display;
int rc = -EINVAL;
struct drm_connector *connector;
if (!disp_m || !encoder || (display_index >= disp_m->display_count)) {
pr_err("Invalid params\n");
return -EINVAL;
}
mutex_lock(&disp_m->lock);
display = disp_m->displays[display_index];
switch (_dm_get_type_by_index(disp_m, display_index)) {
case DRM_MODE_CONNECTOR_DSI:
rc = dsi_display_drm_bridge_init(display, encoder);
if (rc) {
pr_err("dsi bridge init failed\n");
break;
}
connector = sde_connector_init(disp_m->drm_dev,
encoder,
0,
display,
&dsi_ops,
DRM_CONNECTOR_POLL_HPD,
DRM_MODE_CONNECTOR_DSI);
if (!connector)
rc = -ENOMEM;
else if (IS_ERR(connector))
rc = PTR_ERR(connector);
break;
case DRM_MODE_CONNECTOR_VIRTUAL:
rc = sde_wb_drm_init(display, encoder);
if (rc) {
pr_err("writeback init failed\n");
break;
}
connector = sde_connector_init(disp_m->drm_dev,
encoder,
0,
display,
&wb_ops,
DRM_CONNECTOR_POLL_HPD,
DRM_MODE_CONNECTOR_VIRTUAL);
if (!connector)
rc = -ENOMEM;
else if (IS_ERR(connector))
rc = PTR_ERR(connector);
break;
default:
pr_err("invalid index %d\n", display_index);
break;
}
mutex_unlock(&disp_m->lock);
return rc;
}
int display_manager_drm_deinit_by_index(struct display_manager *disp_m,
u32 display_index)
{
void *display;
int rc = 0;
if (!disp_m) {
pr_err("Invalid params\n");
return -EINVAL;
}
mutex_lock(&disp_m->lock);
if (display_index < disp_m->display_count)
display = disp_m->displays[display_index];
switch (_dm_get_type_by_index(disp_m, display_index)) {
case DRM_MODE_CONNECTOR_DSI:
dsi_display_drm_bridge_deinit(display);
break;
default:
pr_err("invalid index\n");
rc = -EINVAL;
break;
}
mutex_unlock(&disp_m->lock);
return rc;
}
void display_manager_register(void)
{
dsi_phy_drv_register();
dsi_ctrl_drv_register();
dsi_display_register();
sde_wb_register();
platform_driver_register(&disp_manager_driver);
}
void display_manager_unregister(void)
{
platform_driver_unregister(&disp_manager_driver);
sde_wb_unregister();
dsi_display_unregister();
dsi_ctrl_drv_unregister();
dsi_phy_drv_unregister();
}