a0b168f7b3
This change enables display drivers code compilation for lahaina target and current location of header files is replacing the header files in usr/include/drm directory before installing display specific header files. This change ensures both the drm and msm_drv header files are exported to user mode clients. Change-Id: If6fc347598b902e670b7206dbcb82fe0740b3984 Signed-off-by: Shashank Babu Chinta Venkata <sbchin@codeaurora.org> Signed-off-by: Abhijit Kulkarni <kabhijit@codeaurora.org> Signed-off-by: Narendra Muppalla <NarendraM@codeaurora.org>
839 lines
18 KiB
C
839 lines
18 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__
|
|
|
|
#include <drm/sde_drm.h>
|
|
#include <drm/drm_probe_helper.h>
|
|
|
|
#include "msm_kms.h"
|
|
#include "sde_kms.h"
|
|
#include "sde_wb.h"
|
|
#include "sde_formats.h"
|
|
|
|
/* maximum display mode resolution if not available from catalog */
|
|
#define SDE_WB_MODE_MAX_WIDTH 4096
|
|
#define SDE_WB_MODE_MAX_HEIGHT 4096
|
|
|
|
/* Serialization lock for sde_wb_list */
|
|
static DEFINE_MUTEX(sde_wb_list_lock);
|
|
|
|
/* List of all writeback devices installed */
|
|
static LIST_HEAD(sde_wb_list);
|
|
|
|
/**
|
|
* sde_wb_is_format_valid - check if given format/modifier is supported
|
|
* @wb_dev: Pointer to writeback device
|
|
* @pixel_format: Fourcc pixel format
|
|
* @format_modifier: Format modifier
|
|
* Returns: true if valid; false otherwise
|
|
*/
|
|
static int sde_wb_is_format_valid(struct sde_wb_device *wb_dev,
|
|
u32 pixel_format, u64 format_modifier)
|
|
{
|
|
const struct sde_format_extended *fmts = wb_dev->wb_cfg->format_list;
|
|
int i;
|
|
|
|
if (!fmts)
|
|
return false;
|
|
|
|
for (i = 0; fmts[i].fourcc_format; i++)
|
|
if ((fmts[i].modifier == format_modifier) &&
|
|
(fmts[i].fourcc_format == pixel_format))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
enum drm_connector_status
|
|
sde_wb_connector_detect(struct drm_connector *connector,
|
|
bool force,
|
|
void *display)
|
|
{
|
|
enum drm_connector_status rc = connector_status_unknown;
|
|
|
|
SDE_DEBUG("\n");
|
|
|
|
if (display)
|
|
rc = ((struct sde_wb_device *)display)->detect_status;
|
|
|
|
return rc;
|
|
}
|
|
|
|
int sde_wb_connector_get_modes(struct drm_connector *connector, void *display,
|
|
const struct msm_resource_caps_info *avail_res)
|
|
{
|
|
struct sde_wb_device *wb_dev;
|
|
int num_modes = 0;
|
|
|
|
if (!connector || !display)
|
|
return 0;
|
|
|
|
wb_dev = display;
|
|
|
|
SDE_DEBUG("\n");
|
|
|
|
mutex_lock(&wb_dev->wb_lock);
|
|
if (wb_dev->count_modes && wb_dev->modes) {
|
|
struct drm_display_mode *mode;
|
|
int i, ret;
|
|
|
|
for (i = 0; i < wb_dev->count_modes; i++) {
|
|
mode = drm_mode_create(connector->dev);
|
|
if (!mode) {
|
|
SDE_ERROR("failed to create mode\n");
|
|
break;
|
|
}
|
|
ret = drm_mode_convert_umode(wb_dev->drm_dev, mode,
|
|
&wb_dev->modes[i]);
|
|
if (ret) {
|
|
SDE_ERROR("failed to convert mode %d\n", ret);
|
|
break;
|
|
}
|
|
|
|
drm_mode_probed_add(connector, mode);
|
|
num_modes++;
|
|
}
|
|
} else {
|
|
u32 max_width = (wb_dev->wb_cfg && wb_dev->wb_cfg->sblk) ?
|
|
wb_dev->wb_cfg->sblk->maxlinewidth :
|
|
SDE_WB_MODE_MAX_WIDTH;
|
|
|
|
num_modes = drm_add_modes_noedid(connector, max_width,
|
|
SDE_WB_MODE_MAX_HEIGHT);
|
|
}
|
|
mutex_unlock(&wb_dev->wb_lock);
|
|
return num_modes;
|
|
}
|
|
|
|
struct drm_framebuffer *
|
|
sde_wb_connector_state_get_output_fb(struct drm_connector_state *state)
|
|
{
|
|
if (!state || !state->connector ||
|
|
(state->connector->connector_type !=
|
|
DRM_MODE_CONNECTOR_VIRTUAL)) {
|
|
SDE_ERROR("invalid params\n");
|
|
return NULL;
|
|
}
|
|
|
|
SDE_DEBUG("\n");
|
|
|
|
return sde_connector_get_out_fb(state);
|
|
}
|
|
|
|
int sde_wb_connector_state_get_output_roi(struct drm_connector_state *state,
|
|
struct sde_rect *roi)
|
|
{
|
|
if (!state || !roi || !state->connector ||
|
|
(state->connector->connector_type !=
|
|
DRM_MODE_CONNECTOR_VIRTUAL)) {
|
|
SDE_ERROR("invalid params\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
SDE_DEBUG("\n");
|
|
|
|
roi->x = sde_connector_get_property(state, CONNECTOR_PROP_DST_X);
|
|
roi->y = sde_connector_get_property(state, CONNECTOR_PROP_DST_Y);
|
|
roi->w = sde_connector_get_property(state, CONNECTOR_PROP_DST_W);
|
|
roi->h = sde_connector_get_property(state, CONNECTOR_PROP_DST_H);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* sde_wb_connector_set_modes - set writeback modes and connection status
|
|
* @wb_dev: Pointer to write back device
|
|
* @count_modes: Count of modes
|
|
* @modes: Pointer to writeback mode requested
|
|
* @connected: Connection status requested
|
|
* Returns: 0 if success; error code otherwise
|
|
*/
|
|
static
|
|
int sde_wb_connector_set_modes(struct sde_wb_device *wb_dev,
|
|
u32 count_modes, struct drm_mode_modeinfo __user *modes,
|
|
bool connected)
|
|
{
|
|
struct drm_mode_modeinfo *modeinfo = NULL;
|
|
int ret = 0;
|
|
int i;
|
|
|
|
if (!wb_dev || !wb_dev->connector ||
|
|
(wb_dev->connector->connector_type !=
|
|
DRM_MODE_CONNECTOR_VIRTUAL)) {
|
|
SDE_ERROR("invalid params\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
SDE_DEBUG("\n");
|
|
|
|
if (connected) {
|
|
SDE_DEBUG("connect\n");
|
|
|
|
if (!count_modes || !modes) {
|
|
SDE_ERROR("invalid count_modes :%u and modes :%d\n",
|
|
count_modes, !modes);
|
|
return -EINVAL;
|
|
}
|
|
|
|
modeinfo = kcalloc(count_modes,
|
|
sizeof(struct drm_mode_modeinfo),
|
|
GFP_KERNEL);
|
|
if (!modeinfo) {
|
|
SDE_ERROR("invalid params\n");
|
|
ret = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
if (copy_from_user(modeinfo, modes,
|
|
count_modes *
|
|
sizeof(struct drm_mode_modeinfo))) {
|
|
SDE_ERROR("failed to copy modes\n");
|
|
kfree(modeinfo);
|
|
ret = -EFAULT;
|
|
goto error;
|
|
}
|
|
|
|
for (i = 0; i < count_modes; i++) {
|
|
struct drm_display_mode dispmode;
|
|
|
|
memset(&dispmode, 0, sizeof(dispmode));
|
|
ret = drm_mode_convert_umode(wb_dev->drm_dev,
|
|
&dispmode, &modeinfo[i]);
|
|
if (ret) {
|
|
SDE_ERROR(
|
|
"failed to convert mode %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x status:%d rc:%d\n",
|
|
i,
|
|
modeinfo[i].name,
|
|
modeinfo[i].vrefresh,
|
|
modeinfo[i].clock,
|
|
modeinfo[i].hdisplay,
|
|
modeinfo[i].hsync_start,
|
|
modeinfo[i].hsync_end,
|
|
modeinfo[i].htotal,
|
|
modeinfo[i].vdisplay,
|
|
modeinfo[i].vsync_start,
|
|
modeinfo[i].vsync_end,
|
|
modeinfo[i].vtotal,
|
|
modeinfo[i].type,
|
|
modeinfo[i].flags,
|
|
dispmode.status,
|
|
ret);
|
|
kfree(modeinfo);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if (wb_dev->modes) {
|
|
wb_dev->count_modes = 0;
|
|
|
|
kfree(wb_dev->modes);
|
|
wb_dev->modes = NULL;
|
|
}
|
|
|
|
wb_dev->count_modes = count_modes;
|
|
wb_dev->modes = modeinfo;
|
|
wb_dev->detect_status = connector_status_connected;
|
|
} else {
|
|
SDE_DEBUG("disconnect\n");
|
|
|
|
if (wb_dev->modes) {
|
|
wb_dev->count_modes = 0;
|
|
|
|
kfree(wb_dev->modes);
|
|
wb_dev->modes = NULL;
|
|
}
|
|
|
|
wb_dev->detect_status = connector_status_disconnected;
|
|
}
|
|
|
|
error:
|
|
return ret;
|
|
}
|
|
|
|
int sde_wb_connector_set_property(struct drm_connector *connector,
|
|
struct drm_connector_state *state,
|
|
int property_index,
|
|
uint64_t value,
|
|
void *display)
|
|
{
|
|
struct sde_wb_device *wb_dev = display;
|
|
struct drm_framebuffer *out_fb;
|
|
int rc = 0;
|
|
|
|
SDE_DEBUG("\n");
|
|
|
|
if (state && (property_index == CONNECTOR_PROP_OUT_FB)) {
|
|
const struct sde_format *sde_format;
|
|
|
|
out_fb = sde_connector_get_out_fb(state);
|
|
if (!out_fb)
|
|
goto done;
|
|
|
|
sde_format = sde_get_sde_format_ext(out_fb->format->format,
|
|
out_fb->modifier);
|
|
if (!sde_format) {
|
|
SDE_ERROR("failed to get sde format\n");
|
|
rc = -EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
if (!sde_wb_is_format_valid(wb_dev, out_fb->format->format,
|
|
out_fb->modifier)) {
|
|
SDE_ERROR("unsupported writeback format 0x%x/0x%llx\n",
|
|
out_fb->format->format,
|
|
out_fb->modifier);
|
|
rc = -EINVAL;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
return rc;
|
|
}
|
|
|
|
int sde_wb_get_info(struct drm_connector *connector,
|
|
struct msm_display_info *info, void *display)
|
|
{
|
|
struct sde_wb_device *wb_dev = display;
|
|
|
|
if (!info || !wb_dev) {
|
|
pr_err("invalid params\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
memset(info, 0, sizeof(struct msm_display_info));
|
|
info->intf_type = DRM_MODE_CONNECTOR_VIRTUAL;
|
|
info->num_of_h_tiles = 1;
|
|
info->h_tile_instance[0] = sde_wb_get_index(display);
|
|
info->is_connected = true;
|
|
info->capabilities = MSM_DISPLAY_CAP_HOT_PLUG | MSM_DISPLAY_CAP_EDID;
|
|
info->max_width = (wb_dev->wb_cfg && wb_dev->wb_cfg->sblk) ?
|
|
wb_dev->wb_cfg->sblk->maxlinewidth :
|
|
SDE_WB_MODE_MAX_WIDTH;
|
|
info->max_height = SDE_WB_MODE_MAX_HEIGHT;
|
|
return 0;
|
|
}
|
|
|
|
int sde_wb_get_mode_info(struct drm_connector *connector,
|
|
const struct drm_display_mode *drm_mode,
|
|
struct msm_mode_info *mode_info,
|
|
void *display, const struct msm_resource_caps_info *avail_res)
|
|
{
|
|
const u32 dual_lm = 2;
|
|
const u32 single_lm = 1;
|
|
const u32 single_intf = 1;
|
|
const u32 no_enc = 0;
|
|
struct msm_display_topology *topology;
|
|
struct sde_wb_device *wb_dev = display;
|
|
u16 hdisplay;
|
|
int i;
|
|
|
|
if (!drm_mode || !mode_info || !avail_res ||
|
|
!avail_res->max_mixer_width || !display) {
|
|
pr_err("invalid params\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
hdisplay = drm_mode->hdisplay;
|
|
|
|
/* find maximum display width to support */
|
|
for (i = 0; i < wb_dev->count_modes; i++)
|
|
hdisplay = max(hdisplay, wb_dev->modes[i].hdisplay);
|
|
|
|
topology = &mode_info->topology;
|
|
topology->num_lm = (avail_res->max_mixer_width <= hdisplay) ?
|
|
dual_lm : single_lm;
|
|
topology->num_enc = no_enc;
|
|
topology->num_intf = single_intf;
|
|
|
|
mode_info->comp_info.comp_type = MSM_DISPLAY_COMPRESSION_NONE;
|
|
mode_info->wide_bus_en = false;
|
|
mode_info->comp_info.comp_ratio = MSM_DISPLAY_COMPRESSION_RATIO_NONE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sde_wb_connector_set_info_blob(struct drm_connector *connector,
|
|
void *info, void *display, struct msm_mode_info *mode_info)
|
|
{
|
|
struct sde_wb_device *wb_dev = display;
|
|
const struct sde_format_extended *format_list;
|
|
|
|
if (!connector || !info || !display || !wb_dev->wb_cfg) {
|
|
SDE_ERROR("invalid params\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
format_list = wb_dev->wb_cfg->format_list;
|
|
|
|
/*
|
|
* Populate info buffer
|
|
*/
|
|
if (format_list) {
|
|
sde_kms_info_start(info, "pixel_formats");
|
|
while (format_list->fourcc_format) {
|
|
sde_kms_info_append_format(info,
|
|
format_list->fourcc_format,
|
|
format_list->modifier);
|
|
++format_list;
|
|
}
|
|
sde_kms_info_stop(info);
|
|
}
|
|
|
|
sde_kms_info_add_keyint(info,
|
|
"wb_intf_index",
|
|
wb_dev->wb_idx - WB_0);
|
|
|
|
sde_kms_info_add_keyint(info,
|
|
"maxlinewidth",
|
|
wb_dev->wb_cfg->sblk->maxlinewidth);
|
|
|
|
sde_kms_info_start(info, "features");
|
|
if (wb_dev->wb_cfg && (wb_dev->wb_cfg->features & BIT(SDE_WB_UBWC)))
|
|
sde_kms_info_append(info, "wb_ubwc");
|
|
sde_kms_info_stop(info);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sde_wb_connector_post_init(struct drm_connector *connector, void *display)
|
|
{
|
|
struct sde_connector *c_conn;
|
|
struct sde_wb_device *wb_dev = display;
|
|
static const struct drm_prop_enum_list e_fb_translation_mode[] = {
|
|
{SDE_DRM_FB_NON_SEC, "non_sec"},
|
|
{SDE_DRM_FB_SEC, "sec"},
|
|
};
|
|
|
|
if (!connector || !display || !wb_dev->wb_cfg) {
|
|
SDE_ERROR("invalid params\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
c_conn = to_sde_connector(connector);
|
|
wb_dev->connector = connector;
|
|
wb_dev->detect_status = connector_status_connected;
|
|
|
|
/*
|
|
* Add extra connector properties
|
|
*/
|
|
msm_property_install_range(&c_conn->property_info, "FB_ID",
|
|
0x0, 0, ~0, 0, CONNECTOR_PROP_OUT_FB);
|
|
msm_property_install_range(&c_conn->property_info, "DST_X",
|
|
0x0, 0, UINT_MAX, 0, CONNECTOR_PROP_DST_X);
|
|
msm_property_install_range(&c_conn->property_info, "DST_Y",
|
|
0x0, 0, UINT_MAX, 0, CONNECTOR_PROP_DST_Y);
|
|
msm_property_install_range(&c_conn->property_info, "DST_W",
|
|
0x0, 0, UINT_MAX, 0, CONNECTOR_PROP_DST_W);
|
|
msm_property_install_range(&c_conn->property_info, "DST_H",
|
|
0x0, 0, UINT_MAX, 0, CONNECTOR_PROP_DST_H);
|
|
msm_property_install_enum(&c_conn->property_info,
|
|
"fb_translation_mode",
|
|
0x0,
|
|
0, e_fb_translation_mode,
|
|
ARRAY_SIZE(e_fb_translation_mode),
|
|
CONNECTOR_PROP_FB_TRANSLATION_MODE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct drm_framebuffer *sde_wb_get_output_fb(struct sde_wb_device *wb_dev)
|
|
{
|
|
struct drm_framebuffer *fb;
|
|
|
|
if (!wb_dev || !wb_dev->connector) {
|
|
SDE_ERROR("invalid params\n");
|
|
return NULL;
|
|
}
|
|
|
|
SDE_DEBUG("\n");
|
|
|
|
mutex_lock(&wb_dev->wb_lock);
|
|
fb = sde_wb_connector_state_get_output_fb(wb_dev->connector->state);
|
|
mutex_unlock(&wb_dev->wb_lock);
|
|
|
|
return fb;
|
|
}
|
|
|
|
int sde_wb_get_output_roi(struct sde_wb_device *wb_dev, struct sde_rect *roi)
|
|
{
|
|
int rc;
|
|
|
|
if (!wb_dev || !wb_dev->connector || !roi) {
|
|
SDE_ERROR("invalid params\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
SDE_DEBUG("\n");
|
|
|
|
mutex_lock(&wb_dev->wb_lock);
|
|
rc = sde_wb_connector_state_get_output_roi(
|
|
wb_dev->connector->state, roi);
|
|
mutex_unlock(&wb_dev->wb_lock);
|
|
|
|
return rc;
|
|
}
|
|
|
|
u32 sde_wb_get_num_of_displays(void)
|
|
{
|
|
u32 count = 0;
|
|
struct sde_wb_device *wb_dev;
|
|
|
|
SDE_DEBUG("\n");
|
|
|
|
mutex_lock(&sde_wb_list_lock);
|
|
list_for_each_entry(wb_dev, &sde_wb_list, wb_list) {
|
|
count++;
|
|
}
|
|
mutex_unlock(&sde_wb_list_lock);
|
|
|
|
return count;
|
|
}
|
|
|
|
int wb_display_get_displays(void **display_array, u32 max_display_count)
|
|
{
|
|
struct sde_wb_device *curr;
|
|
int i = 0;
|
|
|
|
SDE_DEBUG("\n");
|
|
|
|
if (!display_array || !max_display_count) {
|
|
if (!display_array)
|
|
SDE_ERROR("invalid param\n");
|
|
return 0;
|
|
}
|
|
|
|
mutex_lock(&sde_wb_list_lock);
|
|
list_for_each_entry(curr, &sde_wb_list, wb_list) {
|
|
if (i >= max_display_count)
|
|
break;
|
|
display_array[i++] = curr;
|
|
}
|
|
mutex_unlock(&sde_wb_list_lock);
|
|
|
|
return i;
|
|
}
|
|
|
|
int sde_wb_config(struct drm_device *drm_dev, void *data,
|
|
struct drm_file *file_priv)
|
|
{
|
|
struct sde_drm_wb_cfg *config = data;
|
|
struct msm_drm_private *priv;
|
|
struct sde_wb_device *wb_dev = NULL;
|
|
struct sde_wb_device *curr;
|
|
struct drm_connector *connector;
|
|
uint32_t flags;
|
|
uint32_t connector_id;
|
|
uint32_t count_modes;
|
|
uint64_t modes;
|
|
int rc;
|
|
|
|
if (!drm_dev || !data) {
|
|
SDE_ERROR("invalid params\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
SDE_DEBUG("\n");
|
|
|
|
flags = config->flags;
|
|
connector_id = config->connector_id;
|
|
count_modes = config->count_modes;
|
|
modes = config->modes;
|
|
|
|
priv = drm_dev->dev_private;
|
|
|
|
connector = drm_connector_lookup(drm_dev, file_priv, connector_id);
|
|
if (!connector) {
|
|
SDE_ERROR("failed to find connector\n");
|
|
rc = -ENOENT;
|
|
goto fail;
|
|
}
|
|
|
|
mutex_lock(&sde_wb_list_lock);
|
|
list_for_each_entry(curr, &sde_wb_list, wb_list) {
|
|
if (curr->connector == connector) {
|
|
wb_dev = curr;
|
|
break;
|
|
}
|
|
}
|
|
mutex_unlock(&sde_wb_list_lock);
|
|
|
|
if (!wb_dev) {
|
|
SDE_ERROR("failed to find wb device\n");
|
|
rc = -ENOENT;
|
|
goto fail;
|
|
}
|
|
|
|
mutex_lock(&wb_dev->wb_lock);
|
|
|
|
rc = sde_wb_connector_set_modes(wb_dev, count_modes,
|
|
(struct drm_mode_modeinfo __user *) (uintptr_t) modes,
|
|
(flags & SDE_DRM_WB_CFG_FLAGS_CONNECTED) ? true : false);
|
|
|
|
mutex_unlock(&wb_dev->wb_lock);
|
|
drm_helper_hpd_irq_event(drm_dev);
|
|
fail:
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* _sde_wb_dev_init - perform device initialization
|
|
* @wb_dev: Pointer to writeback device
|
|
*/
|
|
static int _sde_wb_dev_init(struct sde_wb_device *wb_dev)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (!wb_dev) {
|
|
SDE_ERROR("invalid params\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
SDE_DEBUG("\n");
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* _sde_wb_dev_deinit - perform device de-initialization
|
|
* @wb_dev: Pointer to writeback device
|
|
*/
|
|
static int _sde_wb_dev_deinit(struct sde_wb_device *wb_dev)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (!wb_dev) {
|
|
SDE_ERROR("invalid params\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
SDE_DEBUG("\n");
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* sde_wb_bind - bind writeback device with controlling device
|
|
* @dev: Pointer to base of platform device
|
|
* @master: Pointer to container of drm device
|
|
* @data: Pointer to private data
|
|
* Returns: Zero on success
|
|
*/
|
|
static int sde_wb_bind(struct device *dev, struct device *master, void *data)
|
|
{
|
|
struct sde_wb_device *wb_dev;
|
|
|
|
if (!dev || !master) {
|
|
SDE_ERROR("invalid params\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
wb_dev = platform_get_drvdata(to_platform_device(dev));
|
|
if (!wb_dev) {
|
|
SDE_ERROR("invalid wb device\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
SDE_DEBUG("\n");
|
|
|
|
mutex_lock(&wb_dev->wb_lock);
|
|
wb_dev->drm_dev = dev_get_drvdata(master);
|
|
mutex_unlock(&wb_dev->wb_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* sde_wb_unbind - unbind writeback from controlling device
|
|
* @dev: Pointer to base of platform device
|
|
* @master: Pointer to container of drm device
|
|
* @data: Pointer to private data
|
|
*/
|
|
static void sde_wb_unbind(struct device *dev,
|
|
struct device *master, void *data)
|
|
{
|
|
struct sde_wb_device *wb_dev;
|
|
|
|
if (!dev) {
|
|
SDE_ERROR("invalid params\n");
|
|
return;
|
|
}
|
|
|
|
wb_dev = platform_get_drvdata(to_platform_device(dev));
|
|
if (!wb_dev) {
|
|
SDE_ERROR("invalid wb device\n");
|
|
return;
|
|
}
|
|
|
|
SDE_DEBUG("\n");
|
|
|
|
mutex_lock(&wb_dev->wb_lock);
|
|
wb_dev->drm_dev = NULL;
|
|
mutex_unlock(&wb_dev->wb_lock);
|
|
}
|
|
|
|
static const struct component_ops sde_wb_comp_ops = {
|
|
.bind = sde_wb_bind,
|
|
.unbind = sde_wb_unbind,
|
|
};
|
|
|
|
/**
|
|
* sde_wb_drm_init - perform DRM initialization
|
|
* @wb_dev: Pointer to writeback device
|
|
* @encoder: Pointer to associated encoder
|
|
*/
|
|
int sde_wb_drm_init(struct sde_wb_device *wb_dev, struct drm_encoder *encoder)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (!wb_dev || !wb_dev->drm_dev || !encoder) {
|
|
SDE_ERROR("invalid params\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
SDE_DEBUG("\n");
|
|
|
|
mutex_lock(&wb_dev->wb_lock);
|
|
|
|
if (wb_dev->drm_dev->dev_private) {
|
|
struct msm_drm_private *priv = wb_dev->drm_dev->dev_private;
|
|
struct sde_kms *sde_kms = to_sde_kms(priv->kms);
|
|
|
|
if (wb_dev->index < sde_kms->catalog->wb_count) {
|
|
wb_dev->wb_idx = sde_kms->catalog->wb[wb_dev->index].id;
|
|
wb_dev->wb_cfg = &sde_kms->catalog->wb[wb_dev->index];
|
|
}
|
|
}
|
|
|
|
wb_dev->drm_dev = encoder->dev;
|
|
wb_dev->encoder = encoder;
|
|
mutex_unlock(&wb_dev->wb_lock);
|
|
return rc;
|
|
}
|
|
|
|
int sde_wb_drm_deinit(struct sde_wb_device *wb_dev)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (!wb_dev) {
|
|
SDE_ERROR("invalid params\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
SDE_DEBUG("\n");
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* sde_wb_probe - load writeback module
|
|
* @pdev: Pointer to platform device
|
|
*/
|
|
static int sde_wb_probe(struct platform_device *pdev)
|
|
{
|
|
struct sde_wb_device *wb_dev;
|
|
int ret;
|
|
|
|
wb_dev = devm_kzalloc(&pdev->dev, sizeof(*wb_dev), GFP_KERNEL);
|
|
if (!wb_dev)
|
|
return -ENOMEM;
|
|
|
|
SDE_DEBUG("\n");
|
|
|
|
ret = of_property_read_u32(pdev->dev.of_node, "cell-index",
|
|
&wb_dev->index);
|
|
if (ret) {
|
|
SDE_DEBUG("cell index not set, default to 0\n");
|
|
wb_dev->index = 0;
|
|
}
|
|
|
|
wb_dev->name = of_get_property(pdev->dev.of_node, "label", NULL);
|
|
if (!wb_dev->name) {
|
|
SDE_DEBUG("label not set, default to unknown\n");
|
|
wb_dev->name = "unknown";
|
|
}
|
|
|
|
wb_dev->wb_idx = SDE_NONE;
|
|
|
|
mutex_init(&wb_dev->wb_lock);
|
|
platform_set_drvdata(pdev, wb_dev);
|
|
|
|
mutex_lock(&sde_wb_list_lock);
|
|
list_add(&wb_dev->wb_list, &sde_wb_list);
|
|
mutex_unlock(&sde_wb_list_lock);
|
|
|
|
if (!_sde_wb_dev_init(wb_dev)) {
|
|
ret = component_add(&pdev->dev, &sde_wb_comp_ops);
|
|
if (ret)
|
|
pr_err("component add failed\n");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* sde_wb_remove - unload writeback module
|
|
* @pdev: Pointer to platform device
|
|
*/
|
|
static int sde_wb_remove(struct platform_device *pdev)
|
|
{
|
|
struct sde_wb_device *wb_dev;
|
|
struct sde_wb_device *curr, *next;
|
|
|
|
wb_dev = platform_get_drvdata(pdev);
|
|
if (!wb_dev)
|
|
return 0;
|
|
|
|
SDE_DEBUG("\n");
|
|
|
|
(void)_sde_wb_dev_deinit(wb_dev);
|
|
|
|
mutex_lock(&sde_wb_list_lock);
|
|
list_for_each_entry_safe(curr, next, &sde_wb_list, wb_list) {
|
|
if (curr == wb_dev) {
|
|
list_del(&wb_dev->wb_list);
|
|
break;
|
|
}
|
|
}
|
|
mutex_unlock(&sde_wb_list_lock);
|
|
|
|
kfree(wb_dev->modes);
|
|
mutex_destroy(&wb_dev->wb_lock);
|
|
|
|
platform_set_drvdata(pdev, NULL);
|
|
devm_kfree(&pdev->dev, wb_dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id dt_match[] = {
|
|
{ .compatible = "qcom,wb-display"},
|
|
{}
|
|
};
|
|
|
|
static struct platform_driver sde_wb_driver = {
|
|
.probe = sde_wb_probe,
|
|
.remove = sde_wb_remove,
|
|
.driver = {
|
|
.name = "sde_wb",
|
|
.of_match_table = dt_match,
|
|
.suppress_bind_attrs = true,
|
|
},
|
|
};
|
|
|
|
static int __init sde_wb_register(void)
|
|
{
|
|
return platform_driver_register(&sde_wb_driver);
|
|
}
|
|
|
|
static void __exit sde_wb_unregister(void)
|
|
{
|
|
platform_driver_unregister(&sde_wb_driver);
|
|
}
|
|
|
|
module_init(sde_wb_register);
|
|
module_exit(sde_wb_unregister);
|