drm/rockchip: vop2: Add vop2 internal pd support for rk3588

There are 7 internal power domains on rk3588 vop:

Cluster0/1/2/3 each have one, and Cluster0 power domain act
as parent pd of Cluster1/2/3.

Esmart0/1/2/3 share on pd.

DSC_8K/DSC_4K each have one.

Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
Change-Id: If2c3c79980d2690761d12e64a486aca9be992e4b
This commit is contained in:
Andy Yan
2021-10-22 20:30:50 +08:00
committed by Tao Huang
parent e976889d1a
commit aa3aee14d0
4 changed files with 359 additions and 22 deletions

View File

@@ -35,7 +35,7 @@
#define VOP_FEATURE_ALPHA_HDR10 BIT(3)
#define VOP_FEATURE_ALPHA_NEXT_HDR BIT(4)
/* a feature to splice two windows and two vps to support resolution > 4096 */
#define VOP_FEATURE_SPLICE BIT(5)
#define VOP_FEATURE_SPLICE BIT(5)
#define VOP_FEATURE_OUTPUT_10BIT VOP_FEATURE_OUTPUT_RGB10
@@ -97,6 +97,19 @@ enum vop2_win_dly_mode {
VOP2_DLY_MODE_MAX,
};
/*
* vop2 internal power domain id,
* should be all none zero, 0 will be
* treat as invalid;
*/
#define VOP2_PD_CLUSTER0 BIT(0)
#define VOP2_PD_CLUSTER1 BIT(1)
#define VOP2_PD_CLUSTER2 BIT(2)
#define VOP2_PD_CLUSTER3 BIT(3)
#define VOP2_PD_ESMART0 BIT(4)
#define VOP2_PD_DSC_8K BIT(5)
#define VOP2_PD_DSC_4K BIT(6)
#define DSP_BG_SWAP 0x1
#define DSP_RB_SWAP 0x2
#define DSP_RG_SWAP 0x4
@@ -657,6 +670,11 @@ struct vop2_video_port_regs {
struct vop_reg splice_en;
};
struct vop2_power_domain_regs {
struct vop_reg pd;
struct vop_reg status;
};
struct vop2_dsc_regs {
struct vop_reg rst_deassert;
struct vop_reg port_mux;
@@ -678,6 +696,12 @@ struct vop2_wb_regs {
struct vop_reg axi_uv_id;
};
struct vop2_power_domain_data {
uint8_t id;
uint8_t parent_id;
const struct vop2_power_domain_regs *regs;
};
/*
* connector interface(RGB/HDMI/eDP/DP/MIPI) data
*/
@@ -698,6 +722,7 @@ struct vop2_win_data {
const char *name;
uint8_t phys_id;
uint8_t splice_win_id;
uint8_t pd_id;
uint32_t base;
enum drm_plane_type type;
@@ -731,6 +756,7 @@ struct vop2_win_data {
struct vop2_dsc_data {
char id;
uint8_t pd_id;
const struct vop2_dsc_regs *regs;
};
@@ -924,6 +950,8 @@ struct vop2_data {
uint8_t nr_axi_intr;
uint8_t nr_gammas;
uint8_t nr_conns;
uint8_t nr_pds;
bool delayed_pd;
const struct vop_intr *axi_intr;
const struct vop2_ctrl *ctrl;
const struct vop2_dsc_data *dsc;
@@ -932,6 +960,7 @@ struct vop2_data {
const struct vop2_connector_if_data *conn;
const struct vop2_wb_data *wb;
const struct vop2_layer_data *layer;
const struct vop2_power_domain_data *pd;
const struct vop_csc_table *csc_table;
const struct vop_hdr_table *hdr_table;
const struct vop_grf_ctrl *grf_ctrl;

View File

@@ -199,6 +199,26 @@ enum vop2_layer_phy_id {
ROCKCHIP_VOP2_PHY_ID_INVALID = -1,
};
struct vop2_power_domain {
struct vop2_power_domain *parent;
struct vop2 *vop2;
/*
* @lock: protect power up/down procedure.
* power on take effect immediately,
* power down take effect by vsync.
* we must check power_domain_status register
* to make sure the power domain is down before
* send a power on request.
*
*/
struct mutex lock;
unsigned int ref_count;
bool on;
const struct vop2_power_domain_data *data;
struct list_head list;
struct delayed_work power_off_work;
};
struct vop2_zpos {
struct drm_plane *plane;
int win_phys_id;
@@ -308,6 +328,10 @@ struct vop2_win {
uint8_t splice_win_id;
struct vop2_power_domain *pd;
bool enabled;
/**
* @phys_id: physical id for cluster0/1, esmart0/1, smart0/1
* Will be used as a identification for some register
@@ -419,6 +443,7 @@ struct vop2_wb {
struct vop2_dsc {
uint8_t id;
const struct vop2_dsc_regs *regs;
struct vop2_power_domain *pd;
};
enum vop2_wb_format {
@@ -643,6 +668,7 @@ struct vop2 {
/* list_head of internal clk */
struct list_head clk_list_head;
struct list_head pd_list_head;
struct vop2_layer layers[ROCKCHIP_MAX_LAYER];
/* must put at the end of the struct */
@@ -870,6 +896,18 @@ static struct vop2_win *vop2_find_win_by_phys_id(struct vop2 *vop2, uint8_t phys
return NULL;
}
static struct vop2_power_domain *vop2_find_pd_by_id(struct vop2 *vop2, uint8_t id)
{
struct vop2_power_domain *pd, *n;
list_for_each_entry_safe(pd, n, &vop2->pd_list_head, list) {
if (pd->data->id == id)
return pd;
}
return NULL;
}
static const struct vop2_connector_if_data *vop2_find_connector_if_data(struct vop2 *vop2, int id)
{
const struct vop2_connector_if_data *if_data;
@@ -1218,6 +1256,119 @@ static inline void vop2_cfg_done(struct drm_crtc *crtc)
return rk3588_vop2_cfg_done(crtc);
}
static uint32_t vop2_power_domain_status(struct vop2_power_domain *pd)
{
struct vop2 *vop2 = pd->vop2;
return vop2_read_reg(vop2, 0, &pd->data->regs->status);
}
static void vop2_wait_power_domain_off(struct vop2_power_domain *pd)
{
struct vop2 *vop2 = pd->vop2;
int val;
int ret;
ret = readx_poll_timeout(vop2_power_domain_status, pd, val, val, 100, 50 * 1000);
if (ret)
DRM_DEV_ERROR(vop2->dev, "wait pd off timeout\n");
}
static void vop2_wait_power_domain_on(struct vop2_power_domain *pd)
{
struct vop2 *vop2 = pd->vop2;
int val;
int ret;
ret = readx_poll_timeout_atomic(vop2_power_domain_status, pd, val, !val, 0, 50 * 1000);
if (ret)
DRM_DEV_ERROR(vop2->dev, "wait pd on timeout\n");
}
/*
* Power domain on take effect immediately
*/
static void vop2_power_domain_on(struct vop2_power_domain *pd)
{
struct vop2 *vop2 = pd->vop2;
if (!pd->on) {
vop2_wait_power_domain_off(pd);
VOP_MODULE_SET(vop2, pd->data, pd, 0);
vop2_wait_power_domain_on(pd);
pd->on = true;
dev_dbg(vop2->dev, "pd%d on\n", pd->data->id);
}
}
/*
* Power domain off take effect by vsync.
*/
static void vop2_power_domain_off(struct vop2_power_domain *pd)
{
struct vop2 *vop2 = pd->vop2;
dev_dbg(vop2->dev, "pd%d off\n", pd->data->id);
pd->on = false;
VOP_MODULE_SET(vop2, pd->data, pd, 1);
}
static void vop2_power_domain_get(struct vop2_power_domain *pd)
{
mutex_lock(&pd->lock);
if (pd->parent)
vop2_power_domain_get(pd->parent);
if (pd->ref_count == 0) {
if (pd->vop2->data->delayed_pd)
cancel_delayed_work(&pd->power_off_work);
vop2_power_domain_on(pd);
}
pd->ref_count++;
mutex_unlock(&pd->lock);
}
static void vop2_power_domain_put(struct vop2_power_domain *pd)
{
mutex_lock(&pd->lock);
if (--pd->ref_count == 0) {
if (pd->vop2->data->delayed_pd)
schedule_delayed_work(&pd->power_off_work, msecs_to_jiffies(2500));
else
vop2_power_domain_off(pd);
}
if (pd->parent)
vop2_power_domain_put(pd->parent);
mutex_unlock(&pd->lock);
}
/*
* Called if the pd ref_count reach 0 after 2.5
* seconds.
*/
static void vop2_power_domain_off_work(struct work_struct *work)
{
struct vop2_power_domain *pd;
pd = container_of(to_delayed_work(work), struct vop2_power_domain, power_off_work);
mutex_lock(&pd->lock);
if (pd->ref_count == 0)
vop2_power_domain_off(pd);
mutex_unlock(&pd->lock);
}
static void vop2_win_enable(struct vop2_win *win)
{
if (!win->enabled) {
if (win->pd)
vop2_power_domain_get(win->pd);
win->enabled = true;
}
}
static void vop2_win_multi_area_disable(struct vop2_win *parent)
{
struct vop2 *vop2 = parent->vop2;
@@ -1238,27 +1389,33 @@ static void vop2_win_disable(struct vop2_win *win)
win->left_win = NULL;
win->splice_win = NULL;
win->splice_mode_right = false;
VOP_WIN_SET(vop2, win, enable, 0);
if (win->feature & WIN_FEATURE_CLUSTER_MAIN) {
struct vop2_win *sub_win;
int i = 0;
for (i = 0; i < vop2->registered_num_wins; i++) {
sub_win = &vop2->win[i];
if (win->enabled) {
VOP_WIN_SET(vop2, win, enable, 0);
if (win->feature & WIN_FEATURE_CLUSTER_MAIN) {
struct vop2_win *sub_win;
int i = 0;
if ((sub_win->phys_id == win->phys_id) &&
(sub_win->feature & WIN_FEATURE_CLUSTER_SUB))
VOP_WIN_SET(vop2, sub_win, enable, 0);
for (i = 0; i < vop2->registered_num_wins; i++) {
sub_win = &vop2->win[i];
if ((sub_win->phys_id == win->phys_id) &&
(sub_win->feature & WIN_FEATURE_CLUSTER_SUB))
VOP_WIN_SET(vop2, sub_win, enable, 0);
}
VOP_CLUSTER_SET(vop2, win, enable, 0);
}
VOP_CLUSTER_SET(vop2, win, enable, 0);
/*
* disable all other multi area win if we want disable area0 here
*/
if (!win->parent && (win->feature & WIN_FEATURE_MULTI_AREA))
vop2_win_multi_area_disable(win);
if (win->pd)
vop2_power_domain_put(win->pd);
win->enabled = false;
}
/*
* disable all other multi area win if we want disable area0 here
*/
if (!win->parent && (win->feature & WIN_FEATURE_MULTI_AREA))
vop2_win_multi_area_disable(win);
}
static inline void vop2_write_lut(struct vop2 *vop2, uint32_t offset, uint32_t v)
@@ -2760,8 +2917,6 @@ static void rk3588_vop2_regsbak(struct vop2 *vop2)
uint32_t *base = vop2->regs;
int i = 0;
vop2_writel(vop2, RK3568_SYS_PD_CTRL, 0);
/*
* No need to backup DSC/GAMMA_LUT/BPP_LUT/MMU
*/
@@ -2769,6 +2924,15 @@ static void rk3588_vop2_regsbak(struct vop2 *vop2)
vop2->regsbak[i] = base[i];
}
static void vop2_power_domain_init(struct vop2 *vop2)
{
struct vop2_power_domain *pd, *n;
list_for_each_entry_safe(pd, n, &vop2->pd_list_head, list) {
vop2_power_domain_on(pd);
}
}
static void vop2_initial(struct drm_crtc *crtc)
{
struct vop2_video_port *vp = to_vop2_video_port(crtc);
@@ -2797,6 +2961,8 @@ static void vop2_initial(struct drm_crtc *crtc)
if (vop2_soc_is_rk3566())
VOP_CTRL_SET(vop2, otp_en, 1);
vop2_power_domain_init(vop2);
/* dsc must deassert rst before its register can accessed */
for (i = 0; i < vop2_data->nr_dscs; i++) {
dsc = &vop2->dscs[i];
@@ -3385,6 +3551,7 @@ static void vop2_win_atomic_update(struct vop2_win *win, struct drm_rect *src, s
afbc_half_block_en = vop2_afbc_half_block_enable(vpstate);
vop2_win_enable(win);
spin_lock(&vop2->reg_lock);
DRM_DEV_DEBUG(vop2->dev, "vp%d update %s[%dx%d->%dx%d@%dx%d] fmt[%.4s_%s] addr[%pad]\n",
vp->id, win->name, actual_w, actual_h, dsp_w, dsp_h,
@@ -7085,6 +7252,37 @@ static void vop2_destroy_crtc(struct drm_crtc *crtc)
drm_flip_work_cleanup(&vp->fb_unref_work);
}
static int vop2_pd_data_init(struct vop2 *vop2)
{
const struct vop2_data *vop2_data = vop2->data;
const struct vop2_power_domain_data *pd_data;
struct vop2_power_domain *pd;
int i;
INIT_LIST_HEAD(&vop2->pd_list_head);
for (i = 0; i < vop2_data->nr_pds; i++) {
pd_data = &vop2_data->pd[i];
pd = devm_kzalloc(vop2->dev, sizeof(*pd), GFP_KERNEL);
if (!pd)
return -ENOMEM;
pd->vop2 = vop2;
pd->data = pd_data;
mutex_init(&pd->lock);
list_add_tail(&pd->list, &vop2->pd_list_head);
INIT_DELAYED_WORK(&pd->power_off_work, vop2_power_domain_off_work);
if (pd_data->parent_id) {
pd->parent = vop2_find_pd_by_id(vop2, pd_data->parent_id);
if (!pd->parent) {
DRM_DEV_ERROR(vop2->dev, "no parent pd find for pd%d\n", pd->data->id);
return -EINVAL;
}
}
}
return 0;
}
static void vop2_dsc_data_init(struct vop2 *vop2)
{
const struct vop2_data *vop2_data = vop2->data;
@@ -7097,6 +7295,8 @@ static void vop2_dsc_data_init(struct vop2 *vop2)
dsc_data = &vop2_data->dsc[i];
dsc->id = dsc_data->id;
dsc->regs = dsc_data->regs;
if (dsc_data->pd_id)
dsc->pd = vop2_find_pd_by_id(vop2, dsc_data->pd_id);
}
}
@@ -7141,6 +7341,9 @@ static int vop2_win_init(struct vop2 *vop2)
win->zpos = i;
win->vop2 = vop2;
if (win_data->pd_id)
win->pd = vop2_find_pd_by_id(vop2, win_data->pd_id);
num_wins++;
if (!vop2->support_multi_area)
@@ -7248,6 +7451,10 @@ static int vop2_bind(struct device *dev, struct device *master, void *data)
vop2->support_multi_area = of_property_read_bool(dev->of_node, "support-multi-area");
vop2->disable_afbc_win = of_property_read_bool(dev->of_node, "disable-afbc-win");
ret = vop2_pd_data_init(vop2);
if (ret)
return ret;
ret = vop2_win_init(vop2);
if (ret)
return ret;

View File

@@ -464,10 +464,13 @@ static const struct vop2_dsc_data rk3588_vop_dsc_data[] = {
{
.id = 0,
.regs = &rk3588_vop_dsc0_regs,
.pd_id = VOP2_PD_DSC_8K,
},
{
.id = 1,
.regs = &rk3588_vop_dsc1_regs,
.pd_id = VOP2_PD_DSC_4K,
},
};
@@ -1783,6 +1786,90 @@ static const struct vop2_win_data rk3568_vop_win_data[] = {
},
};
const struct vop2_power_domain_regs rk3588_cluster0_pd_regs = {
.pd = VOP_REG(RK3568_SYS_PD_CTRL, 0x1, 0),
.status = VOP_REG(RK3568_SYS_STATUS0, 0x1, 8),
};
const struct vop2_power_domain_regs rk3588_cluster1_pd_regs = {
.pd = VOP_REG(RK3568_SYS_PD_CTRL, 0x1, 1),
.status = VOP_REG(RK3568_SYS_STATUS0, 0x1, 9),
};
const struct vop2_power_domain_regs rk3588_cluster2_pd_regs = {
.pd = VOP_REG(RK3568_SYS_PD_CTRL, 0x1, 2),
.status = VOP_REG(RK3568_SYS_STATUS0, 0x1, 10),
};
const struct vop2_power_domain_regs rk3588_cluster3_pd_regs = {
.pd = VOP_REG(RK3568_SYS_PD_CTRL, 0x1, 3),
.status = VOP_REG(RK3568_SYS_STATUS0, 0x1, 11),
};
const struct vop2_power_domain_regs rk3588_esmart_pd_regs = {
.pd = VOP_REG(RK3568_SYS_PD_CTRL, 0x1, 7),
.status = VOP_REG(RK3568_SYS_STATUS0, 0x1, 15),
};
const struct vop2_power_domain_regs rk3588_dsc_8k_pd_regs = {
.pd = VOP_REG(RK3568_SYS_PD_CTRL, 0x1, 5),
.status = VOP_REG(RK3568_SYS_STATUS0, 0x1, 13),
};
const struct vop2_power_domain_regs rk3588_dsc_4k_pd_regs = {
.pd = VOP_REG(RK3568_SYS_PD_CTRL, 0x1, 6),
.status = VOP_REG(RK3568_SYS_STATUS0, 0x1, 14),
};
/*
* There are 7 internal power domains on rk3588 vop,
* Cluster0/1/2/3 each have on pd, and PD_CLUSTER0 as parent,
* that means PD_CLUSTER0 should turn on first before
* PD_CLUSTER1/2/3 turn on.
*
* Esmart0/1/2/3 share one pd PD_ESMART0.
* DSC_8K/DSC_4K each have on pd.
*/
static const struct vop2_power_domain_data rk3588_vop_pd_data[] = {
{
.id = VOP2_PD_CLUSTER0,
.regs = &rk3588_cluster0_pd_regs,
},
{
.id = VOP2_PD_CLUSTER1,
.parent_id = VOP2_PD_CLUSTER0,
.regs = &rk3588_cluster1_pd_regs,
},
{
.id = VOP2_PD_CLUSTER2,
.parent_id = VOP2_PD_CLUSTER0,
.regs = &rk3588_cluster0_pd_regs,
},
{
.id = VOP2_PD_CLUSTER3,
.parent_id = VOP2_PD_CLUSTER0,
.regs = &rk3588_cluster1_pd_regs,
},
{
.id = VOP2_PD_ESMART0,
.regs = &rk3588_esmart_pd_regs,
},
{
.id = VOP2_PD_DSC_8K,
.regs = &rk3588_dsc_8k_pd_regs,
},
{
.id = VOP2_PD_DSC_4K,
.regs = &rk3588_dsc_4k_pd_regs,
},
};
/*
* rk3588 vop with 4 cluster, 4 esmart win.
* Every cluster can work as 4K win or split into two win.
@@ -1817,6 +1904,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = {
.vsu_filter_mode = VOP2_SCALE_UP_BIL,
.vsd_filter_mode = VOP2_SCALE_DOWN_BIL,
.regs = &rk3568_cluster0_win_data,
.pd_id = VOP2_PD_CLUSTER0,
.max_upscale_factor = 4,
.max_downscale_factor = 4,
.dly = { 4, 26, 29 },
@@ -1859,6 +1947,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = {
.vsu_filter_mode = VOP2_SCALE_UP_BIL,
.vsd_filter_mode = VOP2_SCALE_DOWN_BIL,
.regs = &rk3568_cluster1_win_data,
.pd_id = VOP2_PD_CLUSTER1,
.type = DRM_PLANE_TYPE_OVERLAY,
.max_upscale_factor = 4,
.max_downscale_factor = 4,
@@ -1889,6 +1978,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = {
{
.name = "Cluster2-win0",
.phys_id = ROCKCHIP_VOP2_CLUSTER2,
.pd_id = VOP2_PD_CLUSTER2,
.splice_win_id = ROCKCHIP_VOP2_CLUSTER3,
.base = 0x00,
.formats = formats_win_full_10bit,
@@ -1932,6 +2022,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = {
{
.name = "Cluster3-win0",
.phys_id = ROCKCHIP_VOP2_CLUSTER3,
.pd_id = VOP2_PD_CLUSTER3,
.base = 0x00,
.formats = formats_win_full_10bit,
.nformats = ARRAY_SIZE(formats_win_full_10bit),
@@ -1974,6 +2065,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = {
{
.name = "Esmart0-win0",
.phys_id = ROCKCHIP_VOP2_ESMART0,
.pd_id = VOP2_PD_ESMART0,
.splice_win_id = ROCKCHIP_VOP2_ESMART1,
.formats = formats_win_full_10bit,
.nformats = ARRAY_SIZE(formats_win_full_10bit),
@@ -1998,6 +2090,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = {
{
.name = "Esmart2-win0",
.phys_id = ROCKCHIP_VOP2_ESMART2,
.pd_id = VOP2_PD_ESMART0,
.splice_win_id = ROCKCHIP_VOP2_ESMART3,
.base = 0x400,
.formats = formats_win_full_10bit,
@@ -2022,6 +2115,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = {
{
.name = "Esmart1-win0",
.phys_id = ROCKCHIP_VOP2_ESMART1,
.pd_id = VOP2_PD_ESMART0,
.formats = formats_win_full_10bit,
.nformats = ARRAY_SIZE(formats_win_full_10bit),
.format_modifiers = format_modifiers,
@@ -2044,6 +2138,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = {
{
.name = "Esmart3-win0",
.phys_id = ROCKCHIP_VOP2_ESMART3,
.pd_id = VOP2_PD_ESMART0,
.formats = formats_win_full_10bit,
.nformats = ARRAY_SIZE(formats_win_full_10bit),
.format_modifiers = format_modifiers,
@@ -2235,6 +2330,7 @@ static const struct vop2_data rk3588_vop = {
.nr_vps = 4,
.nr_mixers = 7,
.nr_layers = 8,
.nr_pds = 7,
.max_input = { 8192, 4320 },
.max_output = { 4096, 2304 },
.ctrl = &rk3588_vop_ctrl,
@@ -2248,6 +2344,8 @@ static const struct vop2_data rk3588_vop = {
.layer = rk3568_vop_layers,
.win = rk3588_vop_win_data,
.win_size = ARRAY_SIZE(rk3588_vop_win_data),
.pd = rk3588_vop_pd_data,
.nr_pds = ARRAY_SIZE(rk3588_vop_pd_data),
};
static const struct of_device_id vop2_dt_match[] = {

View File

@@ -1055,9 +1055,12 @@
#define RK3568_WB_XSCAL_FACTOR 0x44
#define RK3568_WB_YRGB_MST 0x48
#define RK3568_WB_CBR_MST 0x4C
#define RK3568_OTP_WIN_EN 0x050
#define RK3568_LUT_PORT_SEL 0x058
#define RK3568_SYS_STATUS0 0x060
#define RK3568_OTP_WIN_EN 0x50
#define RK3568_LUT_PORT_SEL 0x58
#define RK3568_SYS_STATUS0 0x60
#define RK3568_SYS_STATUS1 0x64
#define RK3568_SYS_STATUS2 0x68
#define RK3568_SYS_STATUS3 0x6C
#define RK3568_VP0_LINE_FLAG 0x70
#define RK3568_VP1_LINE_FLAG 0x74
#define RK3568_VP2_LINE_FLAG 0x78