android_kernel_xiaomi_sm8350/rotator/sde_rotator_r1.c
Narendra Muppalla 3709853456 Display drivers kernel project initial snapshot
This change brings msm display driver including sde,
dp, dsi, rotator, dsi pll and dp pll from base 4.19 kernel
project. It is first source code snapshot from base kernel project.

Change-Id: Iec864c064ce5ea04e170f24414c728684002f284
Signed-off-by: Narendra Muppalla <NarendraM@codeaurora.org>
2019-04-14 22:20:59 -07:00

746 lines
18 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/delay.h>
#include <linux/debugfs.h>
#include <linux/interrupt.h>
#include "sde_rotator_r1_hwio.h"
#include "sde_rotator_core.h"
#include "sde_rotator_util.h"
#include "sde_rotator_r1_internal.h"
#include "sde_rotator_r1.h"
#include "sde_rotator_r1_debug.h"
struct sde_mdp_hw_resource {
struct sde_rot_hw_resource hw;
struct sde_mdp_ctl *ctl;
struct sde_mdp_mixer *mixer;
struct sde_mdp_pipe *pipe;
struct sde_mdp_writeback *wb;
};
struct sde_rotator_r1_data {
struct sde_rot_mgr *mgr;
int wb_id;
int ctl_id;
int irq_num;
struct sde_mdp_hw_resource *mdp_hw;
};
static u32 sde_hw_rotator_input_pixfmts[] = {
SDE_PIX_FMT_XRGB_8888,
SDE_PIX_FMT_ARGB_8888,
SDE_PIX_FMT_ABGR_8888,
SDE_PIX_FMT_RGBA_8888,
SDE_PIX_FMT_BGRA_8888,
SDE_PIX_FMT_RGBX_8888,
SDE_PIX_FMT_BGRX_8888,
SDE_PIX_FMT_XBGR_8888,
SDE_PIX_FMT_RGBA_5551,
SDE_PIX_FMT_ARGB_1555,
SDE_PIX_FMT_ABGR_1555,
SDE_PIX_FMT_BGRA_5551,
SDE_PIX_FMT_BGRX_5551,
SDE_PIX_FMT_RGBX_5551,
SDE_PIX_FMT_XBGR_1555,
SDE_PIX_FMT_XRGB_1555,
SDE_PIX_FMT_ARGB_4444,
SDE_PIX_FMT_RGBA_4444,
SDE_PIX_FMT_BGRA_4444,
SDE_PIX_FMT_ABGR_4444,
SDE_PIX_FMT_RGBX_4444,
SDE_PIX_FMT_XRGB_4444,
SDE_PIX_FMT_BGRX_4444,
SDE_PIX_FMT_XBGR_4444,
SDE_PIX_FMT_RGB_888,
SDE_PIX_FMT_BGR_888,
SDE_PIX_FMT_RGB_565,
SDE_PIX_FMT_BGR_565,
SDE_PIX_FMT_Y_CB_CR_H2V2,
SDE_PIX_FMT_Y_CR_CB_H2V2,
SDE_PIX_FMT_Y_CR_CB_GH2V2,
SDE_PIX_FMT_Y_CBCR_H2V2,
SDE_PIX_FMT_Y_CRCB_H2V2,
SDE_PIX_FMT_Y_CBCR_H1V2,
SDE_PIX_FMT_Y_CRCB_H1V2,
SDE_PIX_FMT_Y_CBCR_H2V1,
SDE_PIX_FMT_Y_CRCB_H2V1,
SDE_PIX_FMT_YCBYCR_H2V1,
SDE_PIX_FMT_Y_CBCR_H2V2_VENUS,
SDE_PIX_FMT_Y_CRCB_H2V2_VENUS,
SDE_PIX_FMT_RGBA_8888_UBWC,
SDE_PIX_FMT_RGBX_8888_UBWC,
SDE_PIX_FMT_RGB_565_UBWC,
SDE_PIX_FMT_Y_CBCR_H2V2_UBWC,
};
static u32 sde_hw_rotator_output_pixfmts[] = {
SDE_PIX_FMT_XRGB_8888,
SDE_PIX_FMT_ARGB_8888,
SDE_PIX_FMT_ABGR_8888,
SDE_PIX_FMT_RGBA_8888,
SDE_PIX_FMT_BGRA_8888,
SDE_PIX_FMT_RGBX_8888,
SDE_PIX_FMT_BGRX_8888,
SDE_PIX_FMT_XBGR_8888,
SDE_PIX_FMT_RGBA_5551,
SDE_PIX_FMT_ARGB_1555,
SDE_PIX_FMT_ABGR_1555,
SDE_PIX_FMT_BGRA_5551,
SDE_PIX_FMT_BGRX_5551,
SDE_PIX_FMT_RGBX_5551,
SDE_PIX_FMT_XBGR_1555,
SDE_PIX_FMT_XRGB_1555,
SDE_PIX_FMT_ARGB_4444,
SDE_PIX_FMT_RGBA_4444,
SDE_PIX_FMT_BGRA_4444,
SDE_PIX_FMT_ABGR_4444,
SDE_PIX_FMT_RGBX_4444,
SDE_PIX_FMT_XRGB_4444,
SDE_PIX_FMT_BGRX_4444,
SDE_PIX_FMT_XBGR_4444,
SDE_PIX_FMT_RGB_888,
SDE_PIX_FMT_BGR_888,
SDE_PIX_FMT_RGB_565,
SDE_PIX_FMT_BGR_565,
SDE_PIX_FMT_Y_CB_CR_H2V2,
SDE_PIX_FMT_Y_CR_CB_H2V2,
SDE_PIX_FMT_Y_CR_CB_GH2V2,
SDE_PIX_FMT_Y_CBCR_H2V2,
SDE_PIX_FMT_Y_CRCB_H2V2,
SDE_PIX_FMT_Y_CBCR_H1V2,
SDE_PIX_FMT_Y_CRCB_H1V2,
SDE_PIX_FMT_Y_CBCR_H2V1,
SDE_PIX_FMT_Y_CRCB_H2V1,
SDE_PIX_FMT_YCBYCR_H2V1,
SDE_PIX_FMT_Y_CBCR_H2V2_VENUS,
SDE_PIX_FMT_Y_CRCB_H2V2_VENUS,
SDE_PIX_FMT_RGBA_8888_UBWC,
SDE_PIX_FMT_RGBX_8888_UBWC,
SDE_PIX_FMT_RGB_565_UBWC,
SDE_PIX_FMT_Y_CBCR_H2V2_UBWC,
};
static struct sde_mdp_hw_resource *sde_rotator_hw_alloc(
struct sde_rot_mgr *mgr, u32 ctl_id, u32 wb_id, int irq_num)
{
struct sde_mdp_hw_resource *mdp_hw;
struct sde_rot_data_type *mdata = sde_rot_get_mdata();
int pipe_ndx, offset = ctl_id;
int ret = 0;
mdp_hw = devm_kzalloc(&mgr->pdev->dev,
sizeof(struct sde_mdp_hw_resource), GFP_KERNEL);
if (!mdp_hw)
return ERR_PTR(-ENOMEM);
mdp_hw->ctl = sde_mdp_ctl_alloc(mdata, offset);
if (IS_ERR_OR_NULL(mdp_hw->ctl)) {
SDEROT_ERR("unable to allocate ctl\n");
ret = -ENODEV;
goto error;
}
mdp_hw->ctl->irq_num = irq_num;
mdp_hw->wb = sde_mdp_wb_assign(wb_id, mdp_hw->ctl->num);
if (IS_ERR_OR_NULL(mdp_hw->wb)) {
SDEROT_ERR("unable to allocate wb\n");
ret = -ENODEV;
goto error;
}
mdp_hw->ctl->wb = mdp_hw->wb;
mdp_hw->mixer = sde_mdp_mixer_assign(mdp_hw->wb->num, true);
if (IS_ERR_OR_NULL(mdp_hw->mixer)) {
SDEROT_ERR("unable to allocate wb mixer\n");
ret = -ENODEV;
goto error;
}
mdp_hw->ctl->mixer_left = mdp_hw->mixer;
mdp_hw->mixer->ctl = mdp_hw->ctl;
mdp_hw->mixer->rotator_mode = true;
switch (mdp_hw->mixer->num) {
case SDE_MDP_WB_LAYERMIXER0:
mdp_hw->ctl->opmode = SDE_MDP_CTL_OP_ROT0_MODE;
break;
case SDE_MDP_WB_LAYERMIXER1:
mdp_hw->ctl->opmode = SDE_MDP_CTL_OP_ROT1_MODE;
break;
default:
SDEROT_ERR("invalid layer mixer=%d\n", mdp_hw->mixer->num);
ret = -EINVAL;
goto error;
}
mdp_hw->ctl->ops.start_fnc = sde_mdp_writeback_start;
mdp_hw->ctl->wb_type = SDE_MDP_WB_CTL_TYPE_BLOCK;
if (mdp_hw->ctl->ops.start_fnc)
ret = mdp_hw->ctl->ops.start_fnc(mdp_hw->ctl);
if (ret)
goto error;
/* override from dt */
pipe_ndx = wb_id;
mdp_hw->pipe = sde_mdp_pipe_assign(mdata, mdp_hw->mixer, pipe_ndx);
if (IS_ERR_OR_NULL(mdp_hw->pipe)) {
SDEROT_ERR("dma pipe allocation failed\n");
ret = -ENODEV;
goto error;
}
mdp_hw->pipe->mixer_left = mdp_hw->mixer;
mdp_hw->hw.wb_id = mdp_hw->wb->num;
mdp_hw->hw.pending_count = 0;
atomic_set(&mdp_hw->hw.num_active, 0);
mdp_hw->hw.max_active = 1;
init_waitqueue_head(&mdp_hw->hw.wait_queue);
return mdp_hw;
error:
if (!IS_ERR_OR_NULL(mdp_hw->pipe))
sde_mdp_pipe_destroy(mdp_hw->pipe);
if (!IS_ERR_OR_NULL(mdp_hw->ctl)) {
if (mdp_hw->ctl->ops.stop_fnc)
mdp_hw->ctl->ops.stop_fnc(mdp_hw->ctl, 0);
sde_mdp_ctl_free(mdp_hw->ctl);
}
devm_kfree(&mgr->pdev->dev, mdp_hw);
return ERR_PTR(ret);
}
static void sde_rotator_hw_free(struct sde_rot_mgr *mgr,
struct sde_mdp_hw_resource *mdp_hw)
{
struct sde_mdp_mixer *mixer;
struct sde_mdp_ctl *ctl;
if (!mgr || !mdp_hw)
return;
mixer = mdp_hw->pipe->mixer_left;
sde_mdp_pipe_destroy(mdp_hw->pipe);
ctl = sde_mdp_ctl_mixer_switch(mixer->ctl,
SDE_MDP_WB_CTL_TYPE_BLOCK);
if (ctl) {
if (ctl->ops.stop_fnc)
ctl->ops.stop_fnc(ctl, 0);
sde_mdp_ctl_free(ctl);
}
devm_kfree(&mgr->pdev->dev, mdp_hw);
}
static struct sde_rot_hw_resource *sde_rotator_hw_alloc_ext(
struct sde_rot_mgr *mgr, u32 pipe_id, u32 wb_id)
{
struct sde_mdp_hw_resource *mdp_hw;
struct sde_rotator_r1_data *hw_data;
if (!mgr || !mgr->hw_data)
return NULL;
hw_data = mgr->hw_data;
mdp_hw = hw_data->mdp_hw;
return &mdp_hw->hw;
}
static void sde_rotator_hw_free_ext(struct sde_rot_mgr *mgr,
struct sde_rot_hw_resource *hw)
{
/* currently nothing specific for this device */
}
static void sde_rotator_translate_rect(struct sde_rect *dst,
struct sde_rect *src)
{
dst->x = src->x;
dst->y = src->y;
dst->w = src->w;
dst->h = src->h;
}
static u32 sde_rotator_translate_flags(u32 input)
{
u32 output = 0;
if (input & SDE_ROTATION_NOP)
output |= SDE_ROT_NOP;
if (input & SDE_ROTATION_FLIP_LR)
output |= SDE_FLIP_LR;
if (input & SDE_ROTATION_FLIP_UD)
output |= SDE_FLIP_UD;
if (input & SDE_ROTATION_90)
output |= SDE_ROT_90;
if (input & SDE_ROTATION_DEINTERLACE)
output |= SDE_DEINTERLACE;
if (input & SDE_ROTATION_SECURE)
output |= SDE_SECURE_OVERLAY_SESSION;
return output;
}
static int sde_rotator_config_hw(struct sde_rot_hw_resource *hw,
struct sde_rot_entry *entry)
{
struct sde_mdp_hw_resource *mdp_hw;
struct sde_mdp_pipe *pipe;
struct sde_rotation_item *item;
int ret;
if (!hw || !entry) {
SDEROT_ERR("null hw resource/entry");
return -EINVAL;
}
mdp_hw = container_of(hw, struct sde_mdp_hw_resource, hw);
pipe = mdp_hw->pipe;
item = &entry->item;
pipe->flags = sde_rotator_translate_flags(item->flags);
pipe->src_fmt = sde_get_format_params(item->input.format);
pipe->img_width = item->input.width;
pipe->img_height = item->input.height;
sde_rotator_translate_rect(&pipe->src, &item->src_rect);
sde_rotator_translate_rect(&pipe->dst, &item->src_rect);
pipe->params_changed++;
ret = sde_mdp_pipe_queue_data(pipe, &entry->src_buf);
SDEROT_DBG("Config pipe. src{%u,%u,%u,%u}f=%u\n"
"dst{%u,%u,%u,%u}f=%u session_id=%u\n",
item->src_rect.x, item->src_rect.y,
item->src_rect.w, item->src_rect.h, item->input.format,
item->dst_rect.x, item->dst_rect.y,
item->dst_rect.w, item->dst_rect.h, item->output.format,
item->session_id);
return ret;
}
static int sde_rotator_cancel_hw(struct sde_rot_hw_resource *hw,
struct sde_rot_entry *entry)
{
return 0;
}
static int sde_rotator_abort_hw(struct sde_rot_hw_resource *hw,
struct sde_rot_entry *entry)
{
return 0;
}
static int sde_rotator_kickoff_entry(struct sde_rot_hw_resource *hw,
struct sde_rot_entry *entry)
{
struct sde_mdp_hw_resource *mdp_hw;
int ret;
struct sde_mdp_writeback_arg wb_args;
if (!hw || !entry) {
SDEROT_ERR("null hw resource/entry");
return -EINVAL;
}
wb_args.data = &entry->dst_buf;
wb_args.priv_data = entry;
mdp_hw = container_of(hw, struct sde_mdp_hw_resource, hw);
ret = sde_mdp_writeback_display_commit(mdp_hw->ctl, &wb_args);
return ret;
}
static int sde_rotator_wait_for_entry(struct sde_rot_hw_resource *hw,
struct sde_rot_entry *entry)
{
struct sde_mdp_hw_resource *mdp_hw;
int ret;
struct sde_mdp_ctl *ctl;
if (!hw || !entry) {
SDEROT_ERR("null hw resource/entry");
return -EINVAL;
}
mdp_hw = container_of(hw, struct sde_mdp_hw_resource, hw);
ctl = mdp_hw->ctl;
ret = sde_mdp_display_wait4comp(ctl);
return ret;
}
static int sde_rotator_hw_validate_entry(struct sde_rot_mgr *mgr,
struct sde_rot_entry *entry)
{
int ret = 0;
u16 src_w, src_h, dst_w, dst_h, bit;
struct sde_rotation_item *item = &entry->item;
struct sde_mdp_format_params *fmt;
src_w = item->src_rect.w;
src_h = item->src_rect.h;
if (item->flags & SDE_ROTATION_90) {
dst_w = item->dst_rect.h;
dst_h = item->dst_rect.w;
} else {
dst_w = item->dst_rect.w;
dst_h = item->dst_rect.h;
}
entry->dnsc_factor_w = 0;
entry->dnsc_factor_h = 0;
if ((src_w != dst_w) || (src_h != dst_h)) {
if ((src_w % dst_w) || (src_h % dst_h)) {
SDEROT_DBG("non integral scale not support\n");
ret = -EINVAL;
goto dnsc_err;
}
entry->dnsc_factor_w = src_w / dst_w;
bit = fls(entry->dnsc_factor_w);
if ((entry->dnsc_factor_w & ~BIT(bit - 1)) || (bit > 5)) {
SDEROT_DBG("non power-of-2 scale not support\n");
ret = -EINVAL;
goto dnsc_err;
}
entry->dnsc_factor_h = src_h / dst_h;
bit = fls(entry->dnsc_factor_h);
if ((entry->dnsc_factor_h & ~BIT(bit - 1)) || (bit > 5)) {
SDEROT_DBG("non power-of-2 dscale not support\n");
ret = -EINVAL;
goto dnsc_err;
}
}
fmt = sde_get_format_params(item->output.format);
if (sde_mdp_is_ubwc_format(fmt) &&
(entry->dnsc_factor_h || entry->dnsc_factor_w)) {
SDEROT_DBG("downscale with ubwc not support\n");
ret = -EINVAL;
}
dnsc_err:
/* Downscaler does not support asymmetrical dnsc */
if (entry->dnsc_factor_w != entry->dnsc_factor_h) {
SDEROT_DBG("asymmetric downscale not support\n");
ret = -EINVAL;
}
if (ret) {
entry->dnsc_factor_w = 0;
entry->dnsc_factor_h = 0;
}
return ret;
}
static ssize_t sde_rotator_hw_show_caps(struct sde_rot_mgr *mgr,
struct device_attribute *attr, char *buf, ssize_t len)
{
struct sde_rotator_r1_data *hw_data;
int cnt = 0;
if (!mgr || !buf)
return 0;
hw_data = mgr->hw_data;
#define SPRINT(fmt, ...) \
(cnt += scnprintf(buf + cnt, len - cnt, fmt, ##__VA_ARGS__))
SPRINT("wb_id=%d\n", hw_data->wb_id);
SPRINT("ctl_id=%d\n", hw_data->ctl_id);
return cnt;
}
static ssize_t sde_rotator_hw_show_state(struct sde_rot_mgr *mgr,
struct device_attribute *attr, char *buf, ssize_t len)
{
struct sde_rotator_r1_data *hw_data;
int cnt = 0;
if (!mgr || !buf)
return 0;
hw_data = mgr->hw_data;
#define SPRINT(fmt, ...) \
(cnt += scnprintf(buf + cnt, len - cnt, fmt, ##__VA_ARGS__))
if (hw_data && hw_data->mdp_hw) {
struct sde_rot_hw_resource *hw = &hw_data->mdp_hw->hw;
SPRINT("irq_num=%d\n", hw_data->irq_num);
SPRINT("max_active=%d\n", hw->max_active);
SPRINT("num_active=%d\n", atomic_read(&hw->num_active));
SPRINT("pending_cnt=%u\n", hw->pending_count);
}
return cnt;
}
/*
* sde_hw_rotator_get_pixfmt - get the indexed pixel format
* @mgr: Pointer to rotator manager
* @index: index of pixel format
* @input: true for input port; false for output port
* @mode: operating mode
*/
static u32 sde_hw_rotator_get_pixfmt(struct sde_rot_mgr *mgr,
int index, bool input, u32 mode)
{
if (input) {
if (index < ARRAY_SIZE(sde_hw_rotator_input_pixfmts))
return sde_hw_rotator_input_pixfmts[index];
else
return 0;
} else {
if (index < ARRAY_SIZE(sde_hw_rotator_output_pixfmts))
return sde_hw_rotator_output_pixfmts[index];
else
return 0;
}
}
/*
* sde_hw_rotator_is_valid_pixfmt - verify if the given pixel format is valid
* @mgr: Pointer to rotator manager
* @pixfmt: pixel format to be verified
* @input: true for input port; false for output port
* @mode: operating mode
*/
static int sde_hw_rotator_is_valid_pixfmt(struct sde_rot_mgr *mgr, u32 pixfmt,
bool input, u32 mode)
{
int i;
if (input) {
for (i = 0; i < ARRAY_SIZE(sde_hw_rotator_input_pixfmts); i++)
if (sde_hw_rotator_input_pixfmts[i] == pixfmt)
return true;
} else {
for (i = 0; i < ARRAY_SIZE(sde_hw_rotator_output_pixfmts); i++)
if (sde_hw_rotator_output_pixfmts[i] == pixfmt)
return true;
}
return false;
}
static int sde_rotator_hw_parse_dt(struct sde_rotator_r1_data *hw_data,
struct platform_device *dev)
{
int ret = 0;
u32 data;
if (!hw_data || !dev)
return -EINVAL;
ret = of_property_read_u32(dev->dev.of_node,
"qcom,mdss-wb-id", &data);
if (ret)
hw_data->wb_id = -1;
else
hw_data->wb_id = (int) data;
ret = of_property_read_u32(dev->dev.of_node,
"qcom,mdss-ctl-id", &data);
if (ret)
hw_data->ctl_id = -1;
else
hw_data->ctl_id = (int) data;
return ret;
}
static int sde_rotator_hw_rev_init(struct sde_rot_data_type *mdata)
{
if (!mdata) {
SDEROT_ERR("null rotator data\n");
return -EINVAL;
}
clear_bit(SDE_QOS_PER_PIPE_IB, mdata->sde_qos_map);
set_bit(SDE_QOS_OVERHEAD_FACTOR, mdata->sde_qos_map);
clear_bit(SDE_QOS_CDP, mdata->sde_qos_map);
set_bit(SDE_QOS_OTLIM, mdata->sde_qos_map);
set_bit(SDE_QOS_PER_PIPE_LUT, mdata->sde_qos_map);
clear_bit(SDE_QOS_SIMPLIFIED_PREFILL, mdata->sde_qos_map);
set_bit(SDE_CAPS_R1_WB, mdata->sde_caps_map);
return 0;
}
enum {
SDE_ROTATOR_INTR_WB_0,
SDE_ROTATOR_INTR_WB_1,
SDE_ROTATOR_INTR_MAX,
};
struct intr_callback {
void (*func)(void *data);
void *arg;
};
struct intr_callback sde_intr_cb[SDE_ROTATOR_INTR_MAX];
int sde_mdp_set_intr_callback(u32 intr_type, u32 intf_num,
void (*fnc_ptr)(void *), void *arg)
{
if (intf_num >= SDE_ROTATOR_INTR_MAX) {
SDEROT_WARN("invalid intr type=%u intf_num=%u\n",
intr_type, intf_num);
return -EINVAL;
}
sde_intr_cb[intf_num].func = fnc_ptr;
sde_intr_cb[intf_num].arg = arg;
return 0;
}
static irqreturn_t sde_irq_handler(int irq, void *ptr)
{
struct sde_rot_data_type *mdata = ptr;
irqreturn_t ret = IRQ_NONE;
u32 isr;
isr = readl_relaxed(mdata->mdp_base + SDE_MDP_REG_INTR_STATUS);
SDEROT_DBG("intr_status = %8.8x\n", isr);
if (isr & SDE_MDP_INTR_WB_0_DONE) {
struct intr_callback *cb = &sde_intr_cb[SDE_ROTATOR_INTR_WB_0];
if (cb->func) {
writel_relaxed(SDE_MDP_INTR_WB_0_DONE,
mdata->mdp_base + SDE_MDP_REG_INTR_CLEAR);
cb->func(cb->arg);
ret = IRQ_HANDLED;
}
}
if (isr & SDE_MDP_INTR_WB_1_DONE) {
struct intr_callback *cb = &sde_intr_cb[SDE_ROTATOR_INTR_WB_1];
if (cb->func) {
writel_relaxed(SDE_MDP_INTR_WB_1_DONE,
mdata->mdp_base + SDE_MDP_REG_INTR_CLEAR);
cb->func(cb->arg);
ret = IRQ_HANDLED;
}
}
return ret;
}
static void sde_rotator_hw_destroy(struct sde_rot_mgr *mgr)
{
struct sde_rot_data_type *mdata = sde_rot_get_mdata();
struct sde_rotator_r1_data *hw_data;
if (!mgr || !mgr->pdev || !mgr->hw_data)
return;
hw_data = mgr->hw_data;
if (hw_data->irq_num >= 0)
devm_free_irq(&mgr->pdev->dev, hw_data->irq_num, mdata);
sde_rotator_hw_free(mgr, hw_data->mdp_hw);
devm_kfree(&mgr->pdev->dev, mgr->hw_data);
mgr->hw_data = NULL;
}
int sde_rotator_r1_init(struct sde_rot_mgr *mgr)
{
struct sde_rot_data_type *mdata = sde_rot_get_mdata();
struct sde_rotator_r1_data *hw_data;
int ret;
if (!mgr || !mgr->pdev) {
SDEROT_ERR("null rotator manager/platform device");
return -EINVAL;
}
hw_data = devm_kzalloc(&mgr->pdev->dev,
sizeof(struct sde_rotator_r1_data), GFP_KERNEL);
if (hw_data == NULL)
return -ENOMEM;
mgr->hw_data = hw_data;
mgr->ops_config_hw = sde_rotator_config_hw;
mgr->ops_cancel_hw = sde_rotator_cancel_hw;
mgr->ops_abort_hw = sde_rotator_abort_hw;
mgr->ops_kickoff_entry = sde_rotator_kickoff_entry;
mgr->ops_wait_for_entry = sde_rotator_wait_for_entry;
mgr->ops_hw_alloc = sde_rotator_hw_alloc_ext;
mgr->ops_hw_free = sde_rotator_hw_free_ext;
mgr->ops_hw_destroy = sde_rotator_hw_destroy;
mgr->ops_hw_validate_entry = sde_rotator_hw_validate_entry;
mgr->ops_hw_show_caps = sde_rotator_hw_show_caps;
mgr->ops_hw_show_state = sde_rotator_hw_show_state;
mgr->ops_hw_create_debugfs = sde_rotator_r1_create_debugfs;
mgr->ops_hw_get_pixfmt = sde_hw_rotator_get_pixfmt;
mgr->ops_hw_is_valid_pixfmt = sde_hw_rotator_is_valid_pixfmt;
ret = sde_rotator_hw_parse_dt(mgr->hw_data, mgr->pdev);
if (ret)
goto error_parse_dt;
hw_data->irq_num = platform_get_irq(mgr->pdev, 0);
if (hw_data->irq_num < 0) {
SDEROT_ERR("fail to get rotator irq\n");
} else {
ret = devm_request_threaded_irq(&mgr->pdev->dev,
hw_data->irq_num,
sde_irq_handler, NULL,
0, "sde_rotator_r1", mdata);
if (ret) {
SDEROT_ERR("fail to request irq r:%d\n", ret);
hw_data->irq_num = -1;
} else {
disable_irq(hw_data->irq_num);
}
}
hw_data->mdp_hw = sde_rotator_hw_alloc(mgr, hw_data->ctl_id,
hw_data->wb_id, hw_data->irq_num);
if (IS_ERR_OR_NULL(hw_data->mdp_hw))
goto error_hw_alloc;
ret = sde_rotator_hw_rev_init(sde_rot_get_mdata());
if (ret)
goto error_hw_rev_init;
hw_data->mgr = mgr;
return 0;
error_hw_rev_init:
if (hw_data->irq_num >= 0)
devm_free_irq(&mgr->pdev->dev, hw_data->irq_num, mdata);
sde_rotator_hw_free(mgr, hw_data->mdp_hw);
error_hw_alloc:
devm_kfree(&mgr->pdev->dev, mgr->hw_data);
error_parse_dt:
return ret;
}