746 lines
18 KiB
C
Executable File
746 lines
18 KiB
C
Executable File
// 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;
|
|
}
|