Files
android_kernel_samsung_sm86…/msm/sde/sde_wb.c
Steve Cohen 7f0c843da4 disp: msm: sde: move boolean flags in catalog to a bitmap
Move all the individual boolean flags from HW catalog into a
"features" bitmap. These flags are used to specify support of
various target specific features.

Change-Id: I2334b3b873f3737f91bbae4ef576408247710156
Signed-off-by: Steve Cohen <quic_cohens@quicinc.com>
2021-10-26 11:21:33 -07:00

965 lines
22 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015-2021, 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 5120
#define SDE_WB_MODE_MAX_HEIGHT 5120
static const struct drm_display_mode sde_custom_wb_modes[] = {
/* 5120x2160@60Hz */
{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 693264, 5120, 5128,
5160, 5200, 0, 2160, 2208, 2216, 2222, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
{ DRM_MODE("2160x5120", DRM_MODE_TYPE_DRIVER, 693264, 2160, 2208,
2216, 2222, 0, 5120, 5128, 5160, 5200, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
{ DRM_MODE("5120x2560", DRM_MODE_TYPE_DRIVER, 818064, 5120, 5128,
5160, 5200, 0, 2560, 2608, 2616, 2622, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
};
/* 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;
}
static int sde_wb_connector_add_custom_modes(struct drm_connector *connector,
u32 hdisplay, u32 vdisplay)
{
int i, num_modes = 0;
struct drm_display_mode *mode;
struct drm_device *dev = connector->dev;
if (!hdisplay || !vdisplay)
return 0;
if (hdisplay > SDE_WB_MODE_MAX_WIDTH)
hdisplay = SDE_WB_MODE_MAX_WIDTH;
if (vdisplay > SDE_WB_MODE_MAX_HEIGHT)
vdisplay = SDE_WB_MODE_MAX_HEIGHT;
for (i = 0; i < ARRAY_SIZE(sde_custom_wb_modes); i++) {
const struct drm_display_mode *ptr = &sde_custom_wb_modes[i];
if (ptr->hdisplay > hdisplay || ptr->vdisplay > vdisplay)
continue;
mode = drm_mode_duplicate(dev, ptr);
if (mode) {
drm_mode_probed_add(connector, mode);
num_modes++;
}
}
return num_modes;
}
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 = SDE_WB_MODE_MAX_WIDTH;
if (wb_dev->wb_cfg && wb_dev->wb_cfg->sblk)
max_width = max(wb_dev->wb_cfg->sblk->maxlinewidth,
wb_dev->wb_cfg->sblk->maxlinewidth_linear);
num_modes = drm_add_modes_noedid(connector, max_width,
SDE_WB_MODE_MAX_HEIGHT);
num_modes += sde_wb_connector_add_custom_modes(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;
u32 max_width = SDE_WB_MODE_MAX_WIDTH;
if (!info || !wb_dev) {
pr_err("invalid params\n");
return -EINVAL;
}
if (wb_dev->wb_cfg && wb_dev->wb_cfg->sblk)
max_width = max(wb_dev->wb_cfg->sblk->maxlinewidth,
wb_dev->wb_cfg->sblk->maxlinewidth_linear);
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 = 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_sub_mode *sub_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;
struct msm_drm_private *priv = NULL;
struct sde_kms *sde_kms = NULL;
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_add_keyint(info,
"maxlinewidth_linear",
wb_dev->wb_cfg->sblk->maxlinewidth_linear);
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);
if (wb_dev->drm_dev && wb_dev->drm_dev->dev_private) {
priv = wb_dev->drm_dev->dev_private;
if (!priv->kms) {
SDE_ERROR("invalid kms reference\n");
return -EINVAL;
}
sde_kms = to_sde_kms(priv->kms);
sde_kms_info_add_keyint(info, "has_cwb_dither", test_bit(SDE_FEATURE_CWB_DITHER,
sde_kms->catalog->features));
} else {
SDE_ERROR("invalid params %pK\n", wb_dev->drm_dev);
return -EINVAL;
}
return 0;
}
static void sde_wb_connector_install_dither_property(struct sde_wb_device *wb_dev,
struct sde_connector *c_conn)
{
char prop_name[DRM_PROP_NAME_LEN];
struct sde_kms *sde_kms = NULL;
struct msm_drm_private *priv = NULL;
struct sde_mdss_cfg *catalog = NULL;
u32 version = 0;
if (!wb_dev || !c_conn) {
SDE_ERROR("invalid args (s), wb_dev %pK, c_conn %pK\n", wb_dev, c_conn);
return;
}
if (!wb_dev->drm_dev) {
SDE_ERROR("invalid drm_dev is null\n");
return;
}
if (!wb_dev->drm_dev->dev_private) {
SDE_ERROR("invalid dev_private is null\n");
return;
}
priv = wb_dev->drm_dev->dev_private;
if (!priv->kms) {
SDE_ERROR("invalid kms reference is null\n");
return;
}
sde_kms = to_sde_kms(priv->kms);
catalog = sde_kms->catalog;
if (!test_bit(SDE_FEATURE_CWB_DITHER, catalog->features))
return;
version = SDE_COLOR_PROCESS_MAJOR(
catalog->pingpong[0].sblk->dither.version);
snprintf(prop_name, ARRAY_SIZE(prop_name), "%s%d",
"SDE_PP_CWB_DITHER_V", version);
switch (version) {
case 2:
msm_property_install_blob(&c_conn->property_info, prop_name,
DRM_MODE_PROP_BLOB, CONNECTOR_PROP_PP_CWB_DITHER);
break;
default:
SDE_ERROR("unsupported cwb dither version %d\n", version);
return;
}
}
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), 0,
CONNECTOR_PROP_FB_TRANSLATION_MODE);
sde_wb_connector_install_dither_property(wb_dev, c_conn);
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,
},
};
void __init sde_wb_register(void)
{
platform_driver_register(&sde_wb_driver);
}
void __exit sde_wb_unregister(void)
{
platform_driver_unregister(&sde_wb_driver);
}