diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index e8487b0ec403..b5c05cf595a7 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -157,6 +157,11 @@ struct rockchip_crtc_funcs { void (*crtc_send_mcu_cmd)(struct drm_crtc *crtc, u32 type, u32 value); }; +struct rockchip_dclk_pll { + struct clk *pll; + unsigned int use_count; +}; + /* * Rockchip drm private structure. * @@ -187,6 +192,8 @@ struct rockchip_drm_private { const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC]; + struct rockchip_dclk_pll default_pll; + struct rockchip_dclk_pll hdmi_pll; struct rockchip_drm_vcnt vcnt[ROCKCHIP_MAX_CRTC]; /** * @loader_protect diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index c80f7d9fd13f..a6ef67de5b3b 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -6,21 +6,28 @@ #include #include +#include #include +#include #include #include +#include #include #include #include #include #include #include +#include #include +#include #include #include #include #include +#include +#include #include #include #include @@ -32,6 +39,12 @@ #ifdef CONFIG_DRM_ANALOGIX_DP #include #endif +#include + +#include +#include +#include +#include "../drm_crtc_internal.h" #include "rockchip_drm_drv.h" #include "rockchip_drm_gem.h" @@ -39,28 +52,65 @@ #include "rockchip_drm_vop.h" #include "rockchip_rgb.h" -#define VOP_WIN_SET(vop, win, name, v) \ - vop_reg_set(vop, &win->phy->name, win->base, ~0, v, #name) -#define VOP_SCL_SET(vop, win, name, v) \ - vop_reg_set(vop, &win->phy->scl->name, win->base, ~0, v, #name) -#define VOP_SCL_SET_EXT(vop, win, name, v) \ - vop_reg_set(vop, &win->phy->scl->ext->name, \ - win->base, ~0, v, #name) +#define VOP_REG_SUPPORT(vop, reg) \ + (reg.mask && \ + (!reg.major || \ + (reg.major == VOP_MAJOR(vop->version) && \ + reg.begin_minor <= VOP_MINOR(vop->version) && \ + reg.end_minor >= VOP_MINOR(vop->version)))) -#define VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, name, v) \ +#define VOP_WIN_SUPPORT(vop, win, name) \ + VOP_REG_SUPPORT(vop, win->phy->name) + +#define VOP_WIN_SCL_EXT_SUPPORT(vop, win, name) \ + (win->phy->scl->ext && \ + VOP_REG_SUPPORT(vop, win->phy->scl->ext->name)) + +#define VOP_CTRL_SUPPORT(vop, name) \ + VOP_REG_SUPPORT(vop, vop->data->ctrl->name) + +#define VOP_INTR_SUPPORT(vop, name) \ + VOP_REG_SUPPORT(vop, vop->data->intr->name) + +#define __REG_SET(x, off, mask, shift, v, write_mask, relaxed) \ + vop_mask_write(x, off, mask, shift, v, write_mask, relaxed) + +#define _REG_SET(vop, name, off, reg, mask, v, relaxed) \ do { \ - if (win_yuv2yuv && win_yuv2yuv->name.mask) \ - vop_reg_set(vop, &win_yuv2yuv->name, 0, ~0, v, #name); \ + if (VOP_REG_SUPPORT(vop, reg)) \ + __REG_SET(vop, off + reg.offset, mask, reg.shift, \ + v, reg.write_mask, relaxed); \ + else \ + dev_dbg(vop->dev, "Warning: not support "#name"\n"); \ } while (0) -#define VOP_WIN_YUV2YUV_COEFFICIENT_SET(vop, win_yuv2yuv, name, v) \ - do { \ - if (win_yuv2yuv && win_yuv2yuv->phy->name.mask) \ - vop_reg_set(vop, &win_yuv2yuv->phy->name, win_yuv2yuv->base, ~0, v, #name); \ - } while (0) +#define REG_SET(x, name, off, reg, v, relaxed) \ + _REG_SET(x, name, off, reg, reg.mask, v, relaxed) +#define REG_SET_MASK(x, name, off, reg, mask, v, relaxed) \ + _REG_SET(x, name, off, reg, reg.mask & mask, v, relaxed) +#define VOP_WIN_SET(x, win, name, v) \ + REG_SET(x, name, win->offset, VOP_WIN_NAME(win, name), v, true) +#define VOP_WIN_SET_EXT(x, win, ext, name, v) \ + REG_SET(x, name, 0, win->ext->name, v, true) +#define VOP_SCL_SET(x, win, name, v) \ + REG_SET(x, name, win->offset, win->phy->scl->name, v, true) +#define VOP_SCL_SET_EXT(x, win, name, v) \ + REG_SET(x, name, win->offset, win->phy->scl->ext->name, v, true) + +#define VOP_CTRL_SET(x, name, v) \ + REG_SET(x, name, 0, (x)->data->ctrl->name, v, false) + +#define VOP_INTR_GET(vop, name) \ + vop_read_reg(vop, 0, &vop->data->ctrl->name) + +#define VOP_INTR_SET(vop, name, v) \ + REG_SET(vop, name, 0, vop->data->intr->name, \ + v, false) #define VOP_INTR_SET_MASK(vop, name, mask, v) \ - vop_reg_set(vop, &vop->data->intr->name, 0, mask, v, #name) + REG_SET_MASK(vop, name, 0, vop->data->intr->name, \ + mask, v, false) + #define VOP_REG_SET(vop, group, name, v) \ vop_reg_set(vop, &vop->data->group->name, 0, ~0, v, #name) @@ -79,66 +129,110 @@ #define VOP_INTR_GET_TYPE(vop, name, type) \ vop_get_intr_type(vop, &vop->data->intr->name, type) +#define VOP_CTRL_GET(x, name) \ + vop_read_reg(x, 0, &vop->data->ctrl->name) + #define VOP_WIN_GET(vop, win, name) \ - vop_read_reg(vop, win->base, &win->phy->name) + vop_read_reg(vop, win->offset, &VOP_WIN_NAME(win, name)) -#define VOP_WIN_HAS_REG(win, name) \ - (!!(win->phy->name.mask)) - -#define VOP_WIN_GET_YRGBADDR(vop, win) \ - vop_readl(vop, win->base + win->phy->yrgb_mst.offset) +#define VOP_WIN_NAME(win, name) \ + (vop_get_win_phy(win, &win->phy->name)->name) #define VOP_WIN_TO_INDEX(vop_win) \ ((vop_win) - (vop_win)->vop->win) -#define VOP_AFBC_SET(vop, name, v) \ +#define VOP_GRF_SET(vop, reg, v) \ do { \ - if ((vop)->data->afbc) \ - vop_reg_set((vop), &(vop)->data->afbc->name, \ - 0, ~0, v, #name); \ + if (vop->data->grf_ctrl) { \ + vop_grf_writel(vop, vop->data->grf_ctrl->reg, v); \ + } \ } while (0) #define to_vop(x) container_of(x, struct vop, crtc) #define to_vop_win(x) container_of(x, struct vop_win, base) - -#define AFBC_FMT_RGB565 0x0 -#define AFBC_FMT_U8U8U8U8 0x5 -#define AFBC_FMT_U8U8U8 0x4 - -#define AFBC_TILE_16x16 BIT(4) - -/* - * The coefficients of the following matrix are all fixed points. - * The format is S2.10 for the 3x3 part of the matrix, and S9.12 for the offsets. - * They are all represented in two's complement. - */ -static const uint32_t bt601_yuv2rgb[] = { - 0x4A8, 0x0, 0x662, - 0x4A8, 0x1E6F, 0x1CBF, - 0x4A8, 0x812, 0x0, - 0x321168, 0x0877CF, 0x2EB127 -}; +#define to_vop_plane_state(x) container_of(x, struct vop_plane_state, base) enum vop_pending { VOP_PENDING_FB_UNREF, }; -struct vop_win { - struct drm_plane base; - const struct vop_win_data *data; - const struct vop_win_yuv2yuv_data *yuv2yuv_data; - struct vop *vop; +struct vop_zpos { + int win_id; + int zpos; +}; + +struct vop_plane_state { + struct drm_plane_state base; + int format; + int zpos; + struct drm_rect src; + struct drm_rect dest; + dma_addr_t yrgb_mst; + dma_addr_t uv_mst; + const uint32_t *y2r_table; + const uint32_t *r2r_table; + const uint32_t *r2y_table; + int eotf; + bool y2r_en; + bool r2r_en; + bool r2y_en; + int color_space; + unsigned int csc_mode; + int global_alpha; + int blend_mode; + unsigned long offset; + int pdaf_data_type; + bool async_commit; + struct vop_dump_list *planlist; +}; + +struct rockchip_mcu_timing { + int mcu_pix_total; + int mcu_cs_pst; + int mcu_cs_pend; + int mcu_rw_pst; + int mcu_rw_pend; + int mcu_hold_mode; +}; + +struct vop_win { + struct vop_win *parent; + struct drm_plane base; + + int win_id; + int area_id; + int zpos; + uint32_t offset; + enum drm_plane_type type; + const struct vop_win_phy *phy; + const struct vop_csc *csc; + const uint32_t *data_formats; + uint32_t nformats; + u64 feature; + struct vop *vop; + struct vop_plane_state state; }; -struct rockchip_rgb; struct vop { struct drm_crtc crtc; struct device *dev; struct drm_device *drm_dev; + struct dentry *debugfs; + struct drm_info_list *debugfs_files; + struct drm_property *plane_zpos_prop; + struct drm_property *plane_feature_prop; + struct drm_property *feature_prop; + bool is_iommu_enabled; + bool is_iommu_needed; bool is_enabled; + bool support_multi_area; + u32 version; + + struct drm_tv_connector_state active_tv_state; + bool pre_overlay; + bool loader_protect; struct completion dsp_hold_completion; - unsigned int win_enabled; /* protected by dev->event_lock */ struct drm_pending_vblank_event *event; @@ -149,14 +243,22 @@ struct vop { struct completion line_flag_completion; const struct vop_data *data; + int num_wins; uint32_t *regsbak; void __iomem *regs; - void __iomem *lut_regs; + struct regmap *grf; /* physical map length of vop register */ uint32_t len; + void __iomem *lut_regs; + u32 *lut; + u32 lut_len; + bool lut_active; + /* gamma look up table */ + struct drm_color_lut *gamma_lut; + bool dual_channel_swap; /* one time only one process allowed to config the register */ spinlock_t reg_lock; /* lock vop irq reg */ @@ -172,16 +274,74 @@ struct vop { struct clk *dclk; /* vop share memory frequency */ struct clk *aclk; + /* vop source handling, optional */ + struct clk *dclk_source; /* vop dclk reset */ struct reset_control *dclk_rst; - /* optional internal rgb encoder */ - struct rockchip_rgb *rgb; + struct rockchip_dclk_pll *pll; + + struct rockchip_mcu_timing mcu_timing; struct vop_win win[]; }; +/* + * bus-format types. + */ +struct drm_bus_format_enum_list { + int type; + const char *name; +}; + +static const struct drm_bus_format_enum_list drm_bus_format_enum_list[] = { + { DRM_MODE_CONNECTOR_Unknown, "Unknown" }, + { MEDIA_BUS_FMT_RGB565_1X16, "RGB565_1X16" }, + { MEDIA_BUS_FMT_RGB666_1X18, "RGB666_1X18" }, + { MEDIA_BUS_FMT_RGB666_1X24_CPADHI, "RGB666_1X24_CPADHI" }, + { MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, "RGB666_1X7X3_SPWG" }, + { MEDIA_BUS_FMT_YUV8_1X24, "YUV8_1X24" }, + { MEDIA_BUS_FMT_UYYVYY8_0_5X24, "UYYVYY8_0_5X24" }, + { MEDIA_BUS_FMT_YUV10_1X30, "YUV10_1X30" }, + { MEDIA_BUS_FMT_UYYVYY10_0_5X30, "UYYVYY10_0_5X30" }, + { MEDIA_BUS_FMT_RGB888_3X8, "RGB888_3X8" }, + { MEDIA_BUS_FMT_RGB888_DUMMY_4X8, "RGB888_DUMMY_4X8" }, + { MEDIA_BUS_FMT_RGB888_1X24, "RGB888_1X24" }, + { MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, "RGB888_1X7X4_SPWG" }, + { MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, "RGB888_1X7X4_JEIDA" }, + { MEDIA_BUS_FMT_UYVY8_2X8, "UYVY8_2X8" }, + { MEDIA_BUS_FMT_YUYV8_1X16, "YUYV8_1X16" }, + { MEDIA_BUS_FMT_UYVY8_1X16, "UYVY8_1X16" }, +}; + +static DRM_ENUM_NAME_FN(drm_get_bus_format_name, drm_bus_format_enum_list) + +static void vop_lock(struct vop *vop) +{ + mutex_lock(&vop->vop_lock); + rockchip_dmcfreq_lock(); +} + +static void vop_unlock(struct vop *vop) +{ + rockchip_dmcfreq_unlock(); + mutex_unlock(&vop->vop_lock); +} + +static inline void vop_grf_writel(struct vop *vop, struct vop_reg reg, u32 v) +{ + u32 val = 0; + + if (IS_ERR_OR_NULL(vop->grf)) + return; + + if (VOP_REG_SUPPORT(vop, reg)) { + val = (v << reg.shift) | (reg.mask << (reg.shift + 16)); + regmap_write(vop->grf, reg.offset, val); + } +} + static inline void vop_writel(struct vop *vop, uint32_t offset, uint32_t v) { writel(v, vop->regs + offset); @@ -199,23 +359,15 @@ static inline uint32_t vop_read_reg(struct vop *vop, uint32_t base, return (vop_readl(vop, base + reg->offset) >> reg->shift) & reg->mask; } -static void vop_reg_set(struct vop *vop, const struct vop_reg *reg, - uint32_t _offset, uint32_t _mask, uint32_t v, - const char *reg_name) +static inline void vop_mask_write(struct vop *vop, uint32_t offset, + uint32_t mask, uint32_t shift, uint32_t v, + bool write_mask, bool relaxed) { - int offset, mask, shift; - - if (!reg || !reg->mask) { - DRM_DEV_DEBUG(vop->dev, "Warning: not support %s\n", reg_name); + if (!mask) return; - } - offset = reg->offset + _offset; - mask = reg->mask & _mask; - shift = reg->shift; - - if (reg->write_mask) { - v = ((v << shift) & 0xffff) | (mask << (shift + 16)); + if (write_mask) { + v = ((v & mask) << shift) | (mask << (shift + 16)); } else { uint32_t cached_val = vop->regsbak[offset >> 2]; @@ -223,12 +375,21 @@ static void vop_reg_set(struct vop *vop, const struct vop_reg *reg, vop->regsbak[offset >> 2] = v; } - if (reg->relaxed) + if (relaxed) writel_relaxed(v, vop->regs + offset); else writel(v, vop->regs + offset); } +static inline const struct vop_win_phy * +vop_get_win_phy(struct vop_win *win, const struct vop_reg *reg) +{ + if (!reg->mask && win->parent) + return win->parent->phy; + + return win->phy; +} + static inline uint32_t vop_get_intr_type(struct vop *vop, const struct vop_reg *reg, int type) { @@ -243,9 +404,147 @@ static inline uint32_t vop_get_intr_type(struct vop *vop, return ret; } +static void vop_load_hdr2sdr_table(struct vop *vop) +{ + int i; + const struct vop_hdr_table *table = vop->data->hdr_table; + uint32_t hdr2sdr_eetf_oetf_yn[33]; + + for (i = 0; i < 33; i++) + hdr2sdr_eetf_oetf_yn[i] = table->hdr2sdr_eetf_yn[i] + + (table->hdr2sdr_bt1886oetf_yn[i] << 16); + + vop_writel(vop, table->hdr2sdr_eetf_oetf_y0_offset, + hdr2sdr_eetf_oetf_yn[0]); + for (i = 1; i < 33; i++) + vop_writel(vop, + table->hdr2sdr_eetf_oetf_y1_offset + (i - 1) * 4, + hdr2sdr_eetf_oetf_yn[i]); + + vop_writel(vop, table->hdr2sdr_sat_y0_offset, + table->hdr2sdr_sat_yn[0]); + for (i = 1; i < 9; i++) + vop_writel(vop, table->hdr2sdr_sat_y1_offset + (i - 1) * 4, + table->hdr2sdr_sat_yn[i]); + + VOP_CTRL_SET(vop, hdr2sdr_src_min, table->hdr2sdr_src_range_min); + VOP_CTRL_SET(vop, hdr2sdr_src_max, table->hdr2sdr_src_range_max); + VOP_CTRL_SET(vop, hdr2sdr_normfaceetf, table->hdr2sdr_normfaceetf); + VOP_CTRL_SET(vop, hdr2sdr_dst_min, table->hdr2sdr_dst_range_min); + VOP_CTRL_SET(vop, hdr2sdr_dst_max, table->hdr2sdr_dst_range_max); + VOP_CTRL_SET(vop, hdr2sdr_normfacgamma, table->hdr2sdr_normfacgamma); +} + +static void vop_load_sdr2hdr_table(struct vop *vop, uint32_t cmd) +{ + int i; + const struct vop_hdr_table *table = vop->data->hdr_table; + uint32_t sdr2hdr_eotf_oetf_yn[65]; + uint32_t sdr2hdr_oetf_dx_dxpow[64]; + + for (i = 0; i < 65; i++) { + if (cmd == SDR2HDR_FOR_BT2020) + sdr2hdr_eotf_oetf_yn[i] = + table->sdr2hdr_bt1886eotf_yn_for_bt2020[i] + + (table->sdr2hdr_st2084oetf_yn_for_bt2020[i] << 18); + else if (cmd == SDR2HDR_FOR_HDR) + sdr2hdr_eotf_oetf_yn[i] = + table->sdr2hdr_bt1886eotf_yn_for_hdr[i] + + (table->sdr2hdr_st2084oetf_yn_for_hdr[i] << 18); + else if (cmd == SDR2HDR_FOR_HLG_HDR) + sdr2hdr_eotf_oetf_yn[i] = + table->sdr2hdr_bt1886eotf_yn_for_hlg_hdr[i] + + (table->sdr2hdr_st2084oetf_yn_for_hlg_hdr[i] << 18); + } + vop_writel(vop, table->sdr2hdr_eotf_oetf_y0_offset, + sdr2hdr_eotf_oetf_yn[0]); + for (i = 1; i < 65; i++) + vop_writel(vop, table->sdr2hdr_eotf_oetf_y1_offset + + (i - 1) * 4, sdr2hdr_eotf_oetf_yn[i]); + + for (i = 0; i < 64; i++) { + sdr2hdr_oetf_dx_dxpow[i] = table->sdr2hdr_st2084oetf_dxn[i] + + (table->sdr2hdr_st2084oetf_dxn_pow2[i] << 16); + vop_writel(vop, table->sdr2hdr_oetf_dx_dxpow1_offset + i * 4, + sdr2hdr_oetf_dx_dxpow[i]); + } + + for (i = 0; i < 63; i++) + vop_writel(vop, table->sdr2hdr_oetf_xn1_offset + i * 4, + table->sdr2hdr_st2084oetf_xn[i]); +} + +static void vop_load_csc_table(struct vop *vop, u32 offset, const u32 *table) +{ + int i; + + /* + * so far the csc offset is not 0 and in the feature the csc offset + * impossible be 0, so when the offset is 0, should return here. + */ + if (!table || offset == 0) + return; + + for (i = 0; i < 8; i++) + vop_writel(vop, offset + i * 4, table[i]); +} + static inline void vop_cfg_done(struct vop *vop) { - VOP_REG_SET(vop, common, cfg_done, 1); + VOP_CTRL_SET(vop, cfg_done, 1); +} + +static bool vop_is_allwin_disabled(struct vop *vop) +{ + int i; + + for (i = 0; i < vop->num_wins; i++) { + struct vop_win *win = &vop->win[i]; + + if (VOP_WIN_GET(vop, win, enable) != 0) + return false; + } + + return true; +} + +static void vop_win_disable(struct vop *vop, struct vop_win *win) +{ + /* + * FIXUP: some of the vop scale would be abnormal after windows power + * on/off so deinit scale to scale_none mode. + */ + if (win->phy->scl && win->phy->scl->ext) { + VOP_SCL_SET_EXT(vop, win, yrgb_hor_scl_mode, SCALE_NONE); + VOP_SCL_SET_EXT(vop, win, yrgb_ver_scl_mode, SCALE_NONE); + VOP_SCL_SET_EXT(vop, win, cbcr_hor_scl_mode, SCALE_NONE); + VOP_SCL_SET_EXT(vop, win, cbcr_ver_scl_mode, SCALE_NONE); + } + + VOP_WIN_SET(vop, win, enable, 0); + if (win->area_id == 0) + VOP_WIN_SET(vop, win, gate, 0); +} + +static void vop_disable_allwin(struct vop *vop) +{ + int i; + + for (i = 0; i < vop->num_wins; i++) { + struct vop_win *win = &vop->win[i]; + + vop_win_disable(vop, win); + } +} + +static inline void vop_write_lut(struct vop *vop, uint32_t offset, uint32_t v) +{ + writel(v, vop->lut_regs + offset); +} + +static inline uint32_t vop_read_lut(struct vop *vop, uint32_t offset) +{ + return readl(vop->lut_regs + offset); } static bool has_rb_swapped(uint32_t format) @@ -276,38 +575,127 @@ static enum vop_data_format vop_convert_format(uint32_t format) case DRM_FORMAT_BGR565: return VOP_FMT_RGB565; case DRM_FORMAT_NV12: + case DRM_FORMAT_NV15: return VOP_FMT_YUV420SP; case DRM_FORMAT_NV16: + case DRM_FORMAT_NV20: return VOP_FMT_YUV422SP; case DRM_FORMAT_NV24: + case DRM_FORMAT_NV30: return VOP_FMT_YUV444SP; + case DRM_FORMAT_YUYV: + return VOP_FMT_YUYV; default: DRM_ERROR("unsupported format[%08x]\n", format); return -EINVAL; } } -static int vop_convert_afbc_format(uint32_t format) +static bool is_uv_swap(uint32_t bus_format, uint32_t output_mode) +{ + /* + * FIXME: + * + * There is no media type for YUV444 output, + * so when out_mode is AAAA or P888, assume output is YUV444 on + * yuv format. + * + * From H/W testing, YUV444 mode need a rb swap. + */ + if (bus_format == MEDIA_BUS_FMT_YVYU8_1X16 || + bus_format == MEDIA_BUS_FMT_VYUY8_1X16 || + bus_format == MEDIA_BUS_FMT_YVYU8_2X8 || + bus_format == MEDIA_BUS_FMT_VYUY8_2X8 || + ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 || + bus_format == MEDIA_BUS_FMT_YUV10_1X30) && + (output_mode == ROCKCHIP_OUT_MODE_AAAA || + output_mode == ROCKCHIP_OUT_MODE_P888))) + return true; + else + return false; +} + +static bool is_yc_swap(uint32_t bus_format) +{ + switch (bus_format) { + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YVYU8_1X16: + case MEDIA_BUS_FMT_YUYV8_2X8: + case MEDIA_BUS_FMT_YVYU8_2X8: + return true; + default: + return false; + } +} + +static bool is_yuv_output(uint32_t bus_format) +{ + switch (bus_format) { + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + case MEDIA_BUS_FMT_YUYV8_2X8: + case MEDIA_BUS_FMT_YVYU8_2X8: + case MEDIA_BUS_FMT_UYVY8_2X8: + case MEDIA_BUS_FMT_VYUY8_2X8: + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YVYU8_1X16: + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_VYUY8_1X16: + return true; + default: + return false; + } +} + +static bool is_yuv_support(uint32_t format) { switch (format) { - case DRM_FORMAT_XRGB8888: - case DRM_FORMAT_ARGB8888: - case DRM_FORMAT_XBGR8888: - case DRM_FORMAT_ABGR8888: - return AFBC_FMT_U8U8U8U8; - case DRM_FORMAT_RGB888: - case DRM_FORMAT_BGR888: - return AFBC_FMT_U8U8U8; - case DRM_FORMAT_RGB565: - case DRM_FORMAT_BGR565: - return AFBC_FMT_RGB565; - /* either of the below should not be reachable */ + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV15: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV20: + case DRM_FORMAT_NV24: + case DRM_FORMAT_NV30: + case DRM_FORMAT_YUYV: + return true; default: - DRM_WARN_ONCE("unsupported AFBC format[%08x]\n", format); - return -EINVAL; + return false; } +} - return -EINVAL; +static bool is_yuyv_format(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_YUYV: + return true; + default: + return false; + } +} + +static bool is_yuv_10bit(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_NV15: + case DRM_FORMAT_NV20: + case DRM_FORMAT_NV30: + return true; + default: + return false; + } +} + +static bool is_alpha_support(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_ABGR8888: + return true; + default: + return false; + } } static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src, @@ -344,29 +732,37 @@ static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src, return val; } -static void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win_data *win, - uint32_t src_w, uint32_t src_h, uint32_t dst_w, - uint32_t dst_h, const struct drm_format_info *info) +static void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win *win, + uint32_t src_w, uint32_t src_h, uint32_t dst_w, + uint32_t dst_h, uint32_t pixel_format) { uint16_t yrgb_hor_scl_mode, yrgb_ver_scl_mode; uint16_t cbcr_hor_scl_mode = SCALE_NONE; uint16_t cbcr_ver_scl_mode = SCALE_NONE; + const struct drm_format_info *info = drm_format_info(pixel_format); + uint8_t hsub = info->hsub; + uint8_t vsub = info->vsub; bool is_yuv = false; - uint16_t cbcr_src_w = src_w / info->hsub; - uint16_t cbcr_src_h = src_h / info->vsub; + uint16_t cbcr_src_w = src_w / hsub; + uint16_t cbcr_src_h = src_h / vsub; uint16_t vsu_mode; uint16_t lb_mode; uint32_t val; + const struct vop_data *vop_data = vop->data; int vskiplines; + if (!win->phy->scl) + return; + + if (!(vop_data->feature & VOP_FEATURE_ALPHA_SCALE)) { + if (is_alpha_support(pixel_format) && + (src_w != dst_w || src_h != dst_h)) + DRM_ERROR("ERROR: unsupported ppixel alpha&scale\n"); + } + if (info->is_yuv) is_yuv = true; - if (dst_w > 3840) { - DRM_DEV_ERROR(vop->dev, "Maximum dst width (3840) exceeded\n"); - return; - } - if (!win->phy->scl->ext) { VOP_SCL_SET(vop, win, scale_yrgb_x, scl_cal_scale2(src_w, dst_w)); @@ -448,6 +844,372 @@ static void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win_data *win, } } +/* + * rk3328 HDR/CSC path + * + * HDR/SDR --> win0 --> HDR2SDR ----\ + * \ MUX --\ + * \ --> SDR2HDR/CSC--/ \ + * \ + * SDR --> win1 -->pre_overlay ->SDR2HDR/CSC --> post_ovrlay-->post CSC-->output + * SDR --> win2 -/ + * + */ + +static int vop_hdr_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *crtc_state) +{ + struct drm_atomic_state *state = crtc_state->state; + struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); + struct drm_plane_state *pstate; + struct drm_plane *plane; + struct vop *vop = to_vop(crtc); + int pre_sdr2hdr_state = 0, post_sdr2hdr_state = 0; + int pre_sdr2hdr_mode = 0, post_sdr2hdr_mode = 0, sdr2hdr_func = 0; + bool pre_overlay = false; + int hdr2sdr_en = 0, plane_id = 0; + + if (!vop->data->hdr_table) + return 0; + /* hdr cover */ + drm_atomic_crtc_state_for_each_plane(plane, crtc_state) { + struct vop_plane_state *vop_plane_state; + struct vop_win *win = to_vop_win(plane); + + pstate = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(pstate)) + return PTR_ERR(pstate); + vop_plane_state = to_vop_plane_state(pstate); + if (!pstate->fb) + continue; + + if (vop_plane_state->eotf > s->eotf) + if (win->feature & WIN_FEATURE_HDR2SDR) + hdr2sdr_en = 1; + if (vop_plane_state->eotf < s->eotf) { + if (win->feature & WIN_FEATURE_PRE_OVERLAY) + pre_sdr2hdr_state |= BIT(plane_id); + else + post_sdr2hdr_state |= BIT(plane_id); + } + plane_id++; + } + + if (pre_sdr2hdr_state || post_sdr2hdr_state || hdr2sdr_en) { + pre_overlay = true; + pre_sdr2hdr_mode = BT709_TO_BT2020; + post_sdr2hdr_mode = BT709_TO_BT2020; + sdr2hdr_func = SDR2HDR_FOR_HDR; + goto exit_hdr_convert; + } + + /* overlay mode */ + plane_id = 0; + pre_overlay = false; + pre_sdr2hdr_mode = 0; + post_sdr2hdr_mode = 0; + pre_sdr2hdr_state = 0; + post_sdr2hdr_state = 0; + drm_atomic_crtc_state_for_each_plane(plane, crtc_state) { + struct vop_plane_state *vop_plane_state; + struct vop_win *win = to_vop_win(plane); + + pstate = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(pstate)) + return PTR_ERR(pstate); + vop_plane_state = to_vop_plane_state(pstate); + if (!pstate->fb) + continue; + + if (vop_plane_state->color_space == V4L2_COLORSPACE_BT2020 && + vop_plane_state->color_space > s->color_space) { + if (win->feature & WIN_FEATURE_PRE_OVERLAY) { + pre_sdr2hdr_mode = BT2020_TO_BT709; + pre_sdr2hdr_state |= BIT(plane_id); + } else { + post_sdr2hdr_mode = BT2020_TO_BT709; + post_sdr2hdr_state |= BIT(plane_id); + } + } + if (s->color_space == V4L2_COLORSPACE_BT2020 && + vop_plane_state->color_space < s->color_space) { + if (win->feature & WIN_FEATURE_PRE_OVERLAY) { + pre_sdr2hdr_mode = BT709_TO_BT2020; + pre_sdr2hdr_state |= BIT(plane_id); + } else { + post_sdr2hdr_mode = BT709_TO_BT2020; + post_sdr2hdr_state |= BIT(plane_id); + } + } + plane_id++; + } + + if (pre_sdr2hdr_state || post_sdr2hdr_state) { + pre_overlay = true; + sdr2hdr_func = SDR2HDR_FOR_BT2020; + } + +exit_hdr_convert: + s->hdr.pre_overlay = pre_overlay; + s->hdr.hdr2sdr_en = hdr2sdr_en; + if (s->hdr.pre_overlay) + s->yuv_overlay = 0; + + s->hdr.sdr2hdr_state.bt1886eotf_pre_conv_en = !!pre_sdr2hdr_state; + s->hdr.sdr2hdr_state.rgb2rgb_pre_conv_en = !!pre_sdr2hdr_state; + s->hdr.sdr2hdr_state.rgb2rgb_pre_conv_mode = pre_sdr2hdr_mode; + s->hdr.sdr2hdr_state.st2084oetf_pre_conv_en = !!pre_sdr2hdr_state; + + s->hdr.sdr2hdr_state.bt1886eotf_post_conv_en = !!post_sdr2hdr_state; + s->hdr.sdr2hdr_state.rgb2rgb_post_conv_en = !!post_sdr2hdr_state; + s->hdr.sdr2hdr_state.rgb2rgb_post_conv_mode = post_sdr2hdr_mode; + s->hdr.sdr2hdr_state.st2084oetf_post_conv_en = !!post_sdr2hdr_state; + s->hdr.sdr2hdr_state.sdr2hdr_func = sdr2hdr_func; + + return 0; +} + +static int to_vop_csc_mode(int csc_mode) +{ + switch (csc_mode) { + case V4L2_COLORSPACE_SMPTE170M: + case V4L2_COLORSPACE_470_SYSTEM_M: + case V4L2_COLORSPACE_470_SYSTEM_BG: + return CSC_BT601L; + case V4L2_COLORSPACE_REC709: + case V4L2_COLORSPACE_SMPTE240M: + case V4L2_COLORSPACE_DEFAULT: + return CSC_BT709L; + case V4L2_COLORSPACE_JPEG: + return CSC_BT601F; + case V4L2_COLORSPACE_BT2020: + return CSC_BT2020; + default: + return CSC_BT709L; + } +} + +static void vop_disable_all_planes(struct vop *vop) +{ + bool active; + int ret; + + vop_disable_allwin(vop); + vop_cfg_done(vop); + ret = readx_poll_timeout_atomic(vop_is_allwin_disabled, + vop, active, active, + 0, 500 * 1000); + if (ret) + dev_err(vop->dev, "wait win close timeout\n"); +} + +/* + * rk3399 colorspace path: + * Input Win csc Output + * 1. YUV(2020) --> Y2R->2020To709->R2Y --> YUV_OUTPUT(601/709) + * RGB --> R2Y __/ + * + * 2. YUV(2020) --> bypasss --> YUV_OUTPUT(2020) + * RGB --> 709To2020->R2Y __/ + * + * 3. YUV(2020) --> Y2R->2020To709 --> RGB_OUTPUT(709) + * RGB --> R2Y __/ + * + * 4. YUV(601/709)-> Y2R->709To2020->R2Y --> YUV_OUTPUT(2020) + * RGB --> 709To2020->R2Y __/ + * + * 5. YUV(601/709)-> bypass --> YUV_OUTPUT(709) + * RGB --> R2Y __/ + * + * 6. YUV(601/709)-> bypass --> YUV_OUTPUT(601) + * RGB --> R2Y(601) __/ + * + * 7. YUV --> Y2R(709) --> RGB_OUTPUT(709) + * RGB --> bypass __/ + * + * 8. RGB --> 709To2020->R2Y --> YUV_OUTPUT(2020) + * + * 9. RGB --> R2Y(709) --> YUV_OUTPUT(709) + * + * 10. RGB --> R2Y(601) --> YUV_OUTPUT(601) + * + * 11. RGB --> bypass --> RGB_OUTPUT(709) + */ +static int vop_setup_csc_table(const struct vop_csc_table *csc_table, + bool is_input_yuv, bool is_output_yuv, + int input_csc, int output_csc, + const uint32_t **y2r_table, + const uint32_t **r2r_table, + const uint32_t **r2y_table) +{ + *y2r_table = NULL; + *r2r_table = NULL; + *r2y_table = NULL; + + if (!csc_table) + return 0; + + if (is_output_yuv) { + if (output_csc == V4L2_COLORSPACE_BT2020) { + if (is_input_yuv) { + if (input_csc == V4L2_COLORSPACE_BT2020) + return 0; + *y2r_table = csc_table->y2r_bt709; + } + if (input_csc != V4L2_COLORSPACE_BT2020) + *r2r_table = csc_table->r2r_bt709_to_bt2020; + *r2y_table = csc_table->r2y_bt2020; + } else { + if (is_input_yuv && input_csc == V4L2_COLORSPACE_BT2020) + *y2r_table = csc_table->y2r_bt2020; + if (input_csc == V4L2_COLORSPACE_BT2020) + *r2r_table = csc_table->r2r_bt2020_to_bt709; + if (!is_input_yuv || *y2r_table) { + if (output_csc == V4L2_COLORSPACE_REC709 || + output_csc == V4L2_COLORSPACE_SMPTE240M || + output_csc == V4L2_COLORSPACE_DEFAULT) + *r2y_table = csc_table->r2y_bt709; + else if (output_csc == V4L2_COLORSPACE_SMPTE170M || + output_csc == V4L2_COLORSPACE_470_SYSTEM_M || + output_csc == V4L2_COLORSPACE_470_SYSTEM_BG) + *r2y_table = csc_table->r2y_bt601_12_235; /* bt601 limit */ + else + *r2y_table = csc_table->r2y_bt601; /* bt601 full */ + } + } + } else { + if (!is_input_yuv) + return 0; + + /* + * is possible use bt2020 on rgb mode? + */ + if (WARN_ON(output_csc == V4L2_COLORSPACE_BT2020)) + return -EINVAL; + + if (input_csc == V4L2_COLORSPACE_BT2020) + *y2r_table = csc_table->y2r_bt2020; + else if (input_csc == V4L2_COLORSPACE_REC709 || + input_csc == V4L2_COLORSPACE_SMPTE240M || + input_csc == V4L2_COLORSPACE_DEFAULT) + *y2r_table = csc_table->y2r_bt709; + else if (input_csc == V4L2_COLORSPACE_SMPTE170M || + input_csc == V4L2_COLORSPACE_470_SYSTEM_M || + input_csc == V4L2_COLORSPACE_470_SYSTEM_BG) + *y2r_table = csc_table->y2r_bt601_12_235; /* bt601 limit */ + else + *y2r_table = csc_table->y2r_bt601; /* bt601 full */ + + if (input_csc == V4L2_COLORSPACE_BT2020) + /* + * We don't have bt601 to bt709 table, force use bt709. + */ + *r2r_table = csc_table->r2r_bt2020_to_bt709; + } + + return 0; +} + +static void vop_setup_csc_mode(bool is_input_yuv, bool is_output_yuv, + int input_csc, int output_csc, + bool *y2r_en, bool *r2y_en, int *csc_mode) +{ + if (is_input_yuv && !is_output_yuv) { + *y2r_en = true; + *csc_mode = to_vop_csc_mode(input_csc); + } else if (!is_input_yuv && is_output_yuv) { + *r2y_en = true; + *csc_mode = to_vop_csc_mode(output_csc); + } +} + +static int vop_csc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *crtc_state) +{ + struct vop *vop = to_vop(crtc); + struct drm_atomic_state *state = crtc_state->state; + struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); + const struct vop_csc_table *csc_table = vop->data->csc_table; + struct drm_plane_state *pstate; + struct drm_plane *plane; + bool is_input_yuv, is_output_yuv; + int ret; + + is_output_yuv = is_yuv_output(s->bus_format); + + drm_atomic_crtc_state_for_each_plane(plane, crtc_state) { + struct vop_plane_state *vop_plane_state; + struct vop_win *win = to_vop_win(plane); + + pstate = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(pstate)) + return PTR_ERR(pstate); + vop_plane_state = to_vop_plane_state(pstate); + + if (!pstate->fb) + continue; + is_input_yuv = is_yuv_support(pstate->fb->format->format); + vop_plane_state->y2r_en = false; + vop_plane_state->r2r_en = false; + vop_plane_state->r2y_en = false; + + ret = vop_setup_csc_table(csc_table, is_input_yuv, + is_output_yuv, + vop_plane_state->color_space, + s->color_space, + &vop_plane_state->y2r_table, + &vop_plane_state->r2r_table, + &vop_plane_state->r2y_table); + if (ret) + return ret; + + vop_setup_csc_mode(is_input_yuv, s->yuv_overlay, + vop_plane_state->color_space, s->color_space, + &vop_plane_state->y2r_en, + &vop_plane_state->r2y_en, + &vop_plane_state->csc_mode); + + if (csc_table) { + vop_plane_state->y2r_en = !!vop_plane_state->y2r_table; + vop_plane_state->r2r_en = !!vop_plane_state->r2r_table; + vop_plane_state->r2y_en = !!vop_plane_state->r2y_table; + continue; + } + + /* + * This is update for IC design not reasonable, when enable + * hdr2sdr on rk3328, vop can't support per-pixel alpha * global + * alpha,so we must back to gpu, but gpu can't support hdr2sdr, + * gpu output hdr UI, vop will do: + * UI(rgbx) -> yuv -> rgb ->hdr2sdr -> overlay -> output. + */ + if (s->hdr.hdr2sdr_en && + vop_plane_state->eotf == HDMI_EOTF_SMPTE_ST2084 && + !is_yuv_support(pstate->fb->format->format)) + vop_plane_state->r2y_en = true; + if (win->feature & WIN_FEATURE_PRE_OVERLAY) + vop_plane_state->r2r_en = + s->hdr.sdr2hdr_state.rgb2rgb_pre_conv_en; + else if (win->feature & WIN_FEATURE_HDR2SDR) + vop_plane_state->r2r_en = + s->hdr.sdr2hdr_state.rgb2rgb_post_conv_en; + } + + return 0; +} + +static void vop_enable_debug_irq(struct drm_crtc *crtc) +{ + struct vop *vop = to_vop(crtc); + uint32_t irqs; + + irqs = BUS_ERROR_INTR | WIN0_EMPTY_INTR | WIN1_EMPTY_INTR | + WIN2_EMPTY_INTR | WIN3_EMPTY_INTR | HWC_EMPTY_INTR | + POST_BUF_EMPTY_INTR; + VOP_INTR_SET_TYPE(vop, clear, irqs, 1); + VOP_INTR_SET_TYPE(vop, enable, irqs, 1); +} + static void vop_dsp_hold_valid_irq_enable(struct vop *vop) { unsigned long flags; @@ -567,147 +1329,217 @@ static void vop_core_clks_disable(struct vop *vop) clk_disable(vop->hclk); } -static void vop_win_disable(struct vop *vop, const struct vop_win *vop_win) -{ - const struct vop_win_data *win = vop_win->data; - - if (win->phy->scl && win->phy->scl->ext) { - VOP_SCL_SET_EXT(vop, win, yrgb_hor_scl_mode, SCALE_NONE); - VOP_SCL_SET_EXT(vop, win, yrgb_ver_scl_mode, SCALE_NONE); - VOP_SCL_SET_EXT(vop, win, cbcr_hor_scl_mode, SCALE_NONE); - VOP_SCL_SET_EXT(vop, win, cbcr_ver_scl_mode, SCALE_NONE); - } - - VOP_WIN_SET(vop, win, enable, 0); - vop->win_enabled &= ~BIT(VOP_WIN_TO_INDEX(vop_win)); -} - -static int vop_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) +static void vop_crtc_load_lut(struct drm_crtc *crtc) { struct vop *vop = to_vop(crtc); - int ret, i; + int i, dle, lut_idx = 0; - ret = pm_runtime_get_sync(vop->dev); - if (ret < 0) { - DRM_DEV_ERROR(vop->dev, "failed to get pm runtime: %d\n", ret); - return ret; + if (!vop->is_enabled || !vop->lut || !vop->lut_regs) + return; + + if (WARN_ON(!drm_modeset_is_locked(&crtc->mutex))) + return; + + if (!VOP_CTRL_SUPPORT(vop, update_gamma_lut)) { + spin_lock(&vop->reg_lock); + VOP_CTRL_SET(vop, dsp_lut_en, 0); + vop_cfg_done(vop); + spin_unlock(&vop->reg_lock); + +#define CTRL_GET(name) VOP_CTRL_GET(vop, name) + readx_poll_timeout(CTRL_GET, dsp_lut_en, + dle, !dle, 5, 33333); + } else { + lut_idx = CTRL_GET(lut_buffer_index); } - ret = vop_core_clks_enable(vop); - if (WARN_ON(ret < 0)) - goto err_put_pm_runtime; + for (i = 0; i < vop->lut_len; i++) + vop_write_lut(vop, i << 2, vop->lut[i]); - ret = clk_enable(vop->dclk); - if (WARN_ON(ret < 0)) - goto err_disable_core; + spin_lock(&vop->reg_lock); - /* - * Slave iommu shares power, irq and clock with vop. It was associated - * automatically with this master device via common driver code. - * Now that we have enabled the clock we attach it to the shared drm - * mapping. - */ - ret = rockchip_drm_dma_attach_device(vop->drm_dev, vop->dev); - if (ret) { - DRM_DEV_ERROR(vop->dev, - "failed to attach dma mapping, %d\n", ret); + VOP_CTRL_SET(vop, dsp_lut_en, 1); + VOP_CTRL_SET(vop, update_gamma_lut, 1); + vop_cfg_done(vop); + vop->lut_active = true; + + spin_unlock(&vop->reg_lock); + + if (VOP_CTRL_SUPPORT(vop, update_gamma_lut)) { + readx_poll_timeout(CTRL_GET, lut_buffer_index, + dle, dle != lut_idx, 5, 33333); + /* FIXME: + * update_gamma value auto clean to 0 by HW, should not + * bakeup it. + */ + VOP_CTRL_SET(vop, update_gamma_lut, 0); + } +#undef CTRL_GET +} + +static void rockchip_vop_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, + u16 green, u16 blue, int regno) +{ + struct vop *vop = to_vop(crtc); + u32 lut_len = vop->lut_len; + u32 r, g, b; + + if (regno >= lut_len || !vop->lut) + return; + + r = red * (lut_len - 1) / 0xffff; + g = green * (lut_len - 1) / 0xffff; + b = blue * (lut_len - 1) / 0xffff; + vop->lut[regno] = r * lut_len * lut_len + g * lut_len + b; +} + +static void rockchip_vop_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, + u16 *green, u16 *blue, int regno) +{ + struct vop *vop = to_vop(crtc); + u32 lut_len = vop->lut_len; + u32 r, g, b; + + if (regno >= lut_len || !vop->lut) + return; + + r = (vop->lut[regno] / lut_len / lut_len) & (lut_len - 1); + g = (vop->lut[regno] / lut_len) & (lut_len - 1); + b = vop->lut[regno] & (lut_len - 1); + *red = r * 0xffff / (lut_len - 1); + *green = g * 0xffff / (lut_len - 1); + *blue = b * 0xffff / (lut_len - 1); +} + +static int vop_crtc_legacy_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) +{ + struct vop *vop = to_vop(crtc); + int len = min(size, vop->lut_len); + int i; + + if (!vop->lut) + return -EINVAL; + + for (i = 0; i < len; i++) + rockchip_vop_crtc_fb_gamma_set(crtc, red[i], green[i], blue[i], i); + + vop_crtc_load_lut(crtc); + + return 0; +} + +static int vop_crtc_atomic_gamma_set(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct vop *vop = to_vop(crtc); + struct drm_color_lut *lut = vop->gamma_lut; + unsigned int i; + + for (i = 0; i < vop->lut_len; i++) + rockchip_vop_crtc_fb_gamma_set(crtc, lut[i].red, lut[i].green, + lut[i].blue, i); + vop_crtc_load_lut(crtc); + + return 0; +} + +static void vop_power_enable(struct drm_crtc *crtc) +{ + struct vop *vop = to_vop(crtc); + int ret; + + ret = clk_prepare_enable(vop->hclk); + if (ret < 0) { + dev_err(vop->dev, "failed to enable hclk - %d\n", ret); + return; + } + + ret = clk_prepare_enable(vop->dclk); + if (ret < 0) { + dev_err(vop->dev, "failed to enable dclk - %d\n", ret); + goto err_disable_hclk; + } + + ret = clk_prepare_enable(vop->aclk); + if (ret < 0) { + dev_err(vop->dev, "failed to enable aclk - %d\n", ret); goto err_disable_dclk; } - spin_lock(&vop->reg_lock); - for (i = 0; i < vop->len; i += 4) - writel_relaxed(vop->regsbak[i / 4], vop->regs + i); - - /* - * We need to make sure that all windows are disabled before we - * enable the crtc. Otherwise we might try to scan from a destroyed - * buffer later. - * - * In the case of enable-after-PSR, we don't need to worry about this - * case since the buffer is guaranteed to be valid and disabling the - * window will result in screen glitches on PSR exit. - */ - if (!old_state || !old_state->self_refresh_active) { - for (i = 0; i < vop->data->win_size; i++) { - struct vop_win *vop_win = &vop->win[i]; - - vop_win_disable(vop, vop_win); - } + ret = pm_runtime_get_sync(vop->dev); + if (ret < 0) { + dev_err(vop->dev, "failed to get pm runtime: %d\n", ret); + return; } - if (vop->data->afbc) { - struct rockchip_crtc_state *s; + memcpy(vop->regsbak, vop->regs, vop->len); + + if (VOP_CTRL_SUPPORT(vop, version)) { + uint32_t version = VOP_CTRL_GET(vop, version); + /* - * Disable AFBC and forget there was a vop window with AFBC + * Fixup rk3288w version. */ - VOP_AFBC_SET(vop, enable, 0); - s = to_rockchip_crtc_state(crtc->state); - s->enable_afbc = false; + if (version && version == 0x0a05) + vop->version = VOP_VERSION(3, 1); } - vop_cfg_done(vop); - - spin_unlock(&vop->reg_lock); - - /* - * At here, vop clock & iommu is enable, R/W vop regs would be safe. - */ vop->is_enabled = true; - spin_lock(&vop->reg_lock); - - VOP_REG_SET(vop, common, standby, 1); - - spin_unlock(&vop->reg_lock); - - drm_crtc_vblank_on(crtc); - - return 0; + return; err_disable_dclk: - clk_disable(vop->dclk); -err_disable_core: - vop_core_clks_disable(vop); -err_put_pm_runtime: - pm_runtime_put_sync(vop->dev); - return ret; + clk_disable_unprepare(vop->dclk); +err_disable_hclk: + clk_disable_unprepare(vop->hclk); } -static void rockchip_drm_set_win_enabled(struct drm_crtc *crtc, bool enabled) +static void vop_initial(struct drm_crtc *crtc) { - struct vop *vop = to_vop(crtc); - int i; + struct vop *vop = to_vop(crtc); + int i; - spin_lock(&vop->reg_lock); + vop_power_enable(crtc); - for (i = 0; i < vop->data->win_size; i++) { - struct vop_win *vop_win = &vop->win[i]; - const struct vop_win_data *win = vop_win->data; + VOP_CTRL_SET(vop, global_regdone_en, 1); + VOP_CTRL_SET(vop, dsp_blank, 0); + VOP_CTRL_SET(vop, axi_outstanding_max_num, 30); + VOP_CTRL_SET(vop, axi_max_outstanding_en, 1); + VOP_CTRL_SET(vop, dither_up_en, 1); - VOP_WIN_SET(vop, win, enable, - enabled && (vop->win_enabled & BIT(i))); - } - vop_cfg_done(vop); + /* + * We need to make sure that all windows are disabled before resume + * the crtc. Otherwise we might try to scan from a destroyed + * buffer later. + */ + for (i = 0; i < vop->num_wins; i++) { + struct vop_win *win = &vop->win[i]; + int channel = i * 2 + 1; - spin_unlock(&vop->reg_lock); + VOP_WIN_SET(vop, win, channel, (channel + 1) << 4 | channel); + } + VOP_CTRL_SET(vop, afbdc_en, 0); + vop_enable_debug_irq(crtc); } static void vop_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { struct vop *vop = to_vop(crtc); + int sys_status = drm_crtc_index(crtc) ? + SYS_STATUS_LCDC1 : SYS_STATUS_LCDC0; WARN_ON(vop->event); - if (crtc->state->self_refresh_active) - rockchip_drm_set_win_enabled(crtc, false); - - mutex_lock(&vop->vop_lock); - + vop_lock(vop); + VOP_CTRL_SET(vop, reg_done_frm, 1); + VOP_CTRL_SET(vop, dsp_interlace, 0); drm_crtc_vblank_off(crtc); - - if (crtc->state->self_refresh_active) - goto out; + VOP_CTRL_SET(vop, out_mode, ROCKCHIP_OUT_MODE_P888); + VOP_CTRL_SET(vop, afbdc_en, 0); + vop_disable_all_planes(vop); /* * Vop standby will take effect at end of current frame, @@ -721,27 +1553,32 @@ static void vop_crtc_atomic_disable(struct drm_crtc *crtc, spin_lock(&vop->reg_lock); - VOP_REG_SET(vop, common, standby, 1); + VOP_CTRL_SET(vop, standby, 1); spin_unlock(&vop->reg_lock); - wait_for_completion(&vop->dsp_hold_completion); + WARN_ON(!wait_for_completion_timeout(&vop->dsp_hold_completion, + msecs_to_jiffies(50))); vop_dsp_hold_valid_irq_disable(vop); vop->is_enabled = false; + if (vop->is_iommu_enabled) { + /* + * vop standby complete, so iommu detach is safe. + */ + VOP_CTRL_SET(vop, dma_stop, 1); + rockchip_drm_dma_detach_device(vop->drm_dev, vop->dev); + vop->is_iommu_enabled = false; + } - /* - * vop standby complete, so iommu detach is safe. - */ - rockchip_drm_dma_detach_device(vop->drm_dev, vop->dev); + pm_runtime_put_sync(vop->dev); + clk_disable_unprepare(vop->dclk); + clk_disable_unprepare(vop->aclk); + clk_disable_unprepare(vop->hclk); + vop_unlock(vop); - clk_disable(vop->dclk); - vop_core_clks_disable(vop); - pm_runtime_put(vop->dev); - -out: - mutex_unlock(&vop->vop_lock); + rockchip_clear_system_status(sys_status); if (crtc->state->event && !crtc->state->active) { spin_lock_irq(&crtc->dev->event_lock); @@ -752,29 +1589,20 @@ out: } } -static void vop_plane_destroy(struct drm_plane *plane) +static int vop_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) { - drm_plane_cleanup(plane); + if (plane->state->fb) + drm_framebuffer_get(plane->state->fb); + + return 0; } -static inline bool rockchip_afbc(u64 modifier) +static void vop_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) { - return modifier == ROCKCHIP_AFBC_MOD; -} - -static bool rockchip_mod_supported(struct drm_plane *plane, - u32 format, u64 modifier) -{ - if (modifier == DRM_FORMAT_MOD_LINEAR) - return true; - - if (!rockchip_afbc(modifier)) { - DRM_DEBUG_KMS("Unsupported format modifier 0x%llx\n", modifier); - - return false; - } - - return vop_convert_afbc_format(format) >= 0; + if (old_state->fb) + drm_framebuffer_put(old_state->fb); } static int vop_plane_atomic_check(struct drm_plane *plane, @@ -783,21 +1611,41 @@ static int vop_plane_atomic_check(struct drm_plane *plane, struct drm_crtc *crtc = state->crtc; struct drm_crtc_state *crtc_state; struct drm_framebuffer *fb = state->fb; - struct vop_win *vop_win = to_vop_win(plane); - const struct vop_win_data *win = vop_win->data; + struct vop_win *win = to_vop_win(plane); + struct vop_plane_state *vop_plane_state = to_vop_plane_state(state); + const struct vop_data *vop_data; + struct vop *vop; int ret; + struct drm_rect *dest = &vop_plane_state->dest; + struct drm_rect *src = &vop_plane_state->src; + struct drm_gem_object *obj, *uv_obj; + struct rockchip_gem_object *rk_obj, *rk_uv_obj; int min_scale = win->phy->scl ? FRAC_16_16(1, 8) : DRM_PLANE_HELPER_NO_SCALING; int max_scale = win->phy->scl ? FRAC_16_16(8, 1) : DRM_PLANE_HELPER_NO_SCALING; + unsigned long offset; + dma_addr_t dma_addr; - if (!crtc || WARN_ON(!fb)) + crtc = crtc ? crtc : plane->state->crtc; + if (!crtc || !fb) { + plane->state->visible = false; return 0; + } crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); if (WARN_ON(!crtc_state)) return -EINVAL; + src->x1 = state->src_x; + src->y1 = state->src_y; + src->x2 = state->src_x + state->src_w; + src->y2 = state->src_y + state->src_h; + dest->x1 = state->crtc_x; + dest->y1 = state->crtc_y; + dest->x2 = state->crtc_x + state->crtc_w; + dest->y2 = state->crtc_y + state->crtc_h; + ret = drm_atomic_helper_check_plane_state(state, crtc_state, min_scale, max_scale, true, true); @@ -807,9 +1655,30 @@ static int vop_plane_atomic_check(struct drm_plane *plane, if (!state->visible) return 0; - ret = vop_convert_format(fb->format->format); - if (ret < 0) - return ret; + vop_plane_state->format = vop_convert_format(fb->format->format); + if (vop_plane_state->format < 0) + return vop_plane_state->format; + + vop = to_vop(crtc); + vop_data = vop->data; + + if (state->src_w >> 16 < 4 || state->src_h >> 16 < 4 || + state->crtc_w < 4 || state->crtc_h < 4) { + DRM_ERROR("Invalid size: %dx%d->%dx%d, min size is 4x4\n", + state->src_w >> 16, state->src_h >> 16, + state->crtc_w, state->crtc_h); + return -EINVAL; + } + + if (drm_rect_width(src) >> 16 > vop_data->max_input.width || + drm_rect_height(src) >> 16 > vop_data->max_input.height) { + DRM_ERROR("Invalid source: %dx%d. max input: %dx%d\n", + drm_rect_width(src) >> 16, + drm_rect_height(src) >> 16, + vop_data->max_input.width, + vop_data->max_input.height); + return -EINVAL; + } /* * Src.x1 can be odd when do clip, but yuv plane start point @@ -825,28 +1694,28 @@ static int vop_plane_atomic_check(struct drm_plane *plane, return -EINVAL; } - if (rockchip_afbc(fb->modifier)) { - struct vop *vop = to_vop(crtc); + offset = (src->x1 >> 16) * fb->format->cpp[0]; + vop_plane_state->offset = offset + fb->offsets[0]; + if (state->rotation & DRM_MODE_REFLECT_Y) + offset += ((src->y2 >> 16) - 1) * fb->pitches[0]; + else + offset += (src->y1 >> 16) * fb->pitches[0]; - if (!vop->data->afbc) { - DRM_ERROR("vop does not support AFBC\n"); - return -EINVAL; - } + obj = fb->obj[0]; + rk_obj = to_rockchip_obj(obj); + vop_plane_state->yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0]; + if (fb->format->is_yuv) { + int hsub = fb->format->hsub; + int vsub = fb->format->vsub; - ret = vop_convert_afbc_format(fb->format->format); - if (ret < 0) - return ret; + offset = (src->x1 >> 16) * fb->format->cpp[1] / hsub; + offset += (src->y1 >> 16) * fb->pitches[1] / vsub; - if (state->src.x1 || state->src.y1) { - DRM_ERROR("AFBC does not support offset display, xpos=%d, ypos=%d, offset=%d\n", state->src.x1, state->src.y1, fb->offsets[0]); - return -EINVAL; - } + uv_obj = fb->obj[1]; + rk_uv_obj = to_rockchip_obj(uv_obj); - if (state->rotation && state->rotation != DRM_MODE_ROTATE_0) { - DRM_ERROR("No rotation support in AFBC, rotation=%d\n", - state->rotation); - return -EINVAL; - } + dma_addr = rk_uv_obj->dma_addr + offset + fb->offsets[1]; + vop_plane_state->uv_mst = dma_addr; } return 0; @@ -855,15 +1724,33 @@ static int vop_plane_atomic_check(struct drm_plane *plane, static void vop_plane_atomic_disable(struct drm_plane *plane, struct drm_plane_state *old_state) { - struct vop_win *vop_win = to_vop_win(plane); + struct vop_win *win = to_vop_win(plane); struct vop *vop = to_vop(old_state->crtc); +#if defined(CONFIG_ROCKCHIP_DRM_DEBUG) + struct vop_plane_state *vop_plane_state = + to_vop_plane_state(plane->state); +#endif if (!old_state->crtc) return; spin_lock(&vop->reg_lock); - vop_win_disable(vop, vop_win); + vop_win_disable(vop, win); + + /* + * IC design bug: in the bandwidth tension environment when close win2, + * vop will access the freed memory lead to iommu pagefault. + * so we add this reset to workaround. + */ + if (VOP_MAJOR(vop->version) == 2 && VOP_MINOR(vop->version) == 5 && + win->win_id == 2) + VOP_WIN_SET(vop, win, yrgb_mst, 0); + +#if defined(CONFIG_ROCKCHIP_DRM_DEBUG) + kfree(vop_plane_state->planlist); + vop_plane_state->planlist = NULL; +#endif spin_unlock(&vop->reg_lock); } @@ -873,26 +1760,48 @@ static void vop_plane_atomic_update(struct drm_plane *plane, { struct drm_plane_state *state = plane->state; struct drm_crtc *crtc = state->crtc; - struct vop_win *vop_win = to_vop_win(plane); - const struct vop_win_data *win = vop_win->data; - const struct vop_win_yuv2yuv_data *win_yuv2yuv = vop_win->yuv2yuv_data; + struct drm_display_mode *mode = NULL; + struct vop_win *win = to_vop_win(plane); + struct vop_plane_state *vop_plane_state = to_vop_plane_state(state); + struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; + struct rockchip_crtc_state *s; struct vop *vop = to_vop(state->crtc); struct drm_framebuffer *fb = state->fb; - unsigned int actual_w, actual_h; + unsigned int actual_w, actual_h, dsp_w, dsp_h; unsigned int dsp_stx, dsp_sty; uint32_t act_info, dsp_info, dsp_st; - struct drm_rect *src = &state->src; - struct drm_rect *dest = &state->dst; - struct drm_gem_object *obj, *uv_obj; - struct rockchip_gem_object *rk_obj, *rk_uv_obj; - unsigned long offset; - dma_addr_t dma_addr; + struct drm_rect *src = &vop_plane_state->src; + struct drm_rect *dest = &vop_plane_state->dest; + const uint32_t *y2r_table = vop_plane_state->y2r_table; + const uint32_t *r2r_table = vop_plane_state->r2r_table; + const uint32_t *r2y_table = vop_plane_state->r2y_table; uint32_t val; - bool rb_swap; - int win_index = VOP_WIN_TO_INDEX(vop_win); - int format; + bool rb_swap, global_alpha_en; int is_yuv = fb->format->is_yuv; - int i; + +#if defined(CONFIG_ROCKCHIP_DRM_DEBUG) + bool AFBC_flag = false; + struct vop_dump_list *planlist; + unsigned long num_pages; + struct page **pages; + struct rockchip_drm_fb *rk_fb; + struct drm_gem_object *obj; + struct rockchip_gem_object *rk_obj; + + num_pages = 0; + pages = NULL; + rk_fb = to_rockchip_fb(fb); + obj = rk_fb->obj[0]; + rk_obj = to_rockchip_obj(obj); + if (rk_obj) { + num_pages = rk_obj->num_pages; + pages = rk_obj->pages; + } + if (fb->modifier == DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16)) + AFBC_flag = true; + else + AFBC_flag = false; +#endif /* * can't update plane when vop is disabled. @@ -908,205 +1817,447 @@ static void vop_plane_atomic_update(struct drm_plane *plane, return; } - obj = fb->obj[0]; - rk_obj = to_rockchip_obj(obj); - + mode = &crtc->state->adjusted_mode; actual_w = drm_rect_width(src) >> 16; actual_h = drm_rect_height(src) >> 16; - act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff); - dsp_info = (drm_rect_height(dest) - 1) << 16; - dsp_info |= (drm_rect_width(dest) - 1) & 0xffff; - - dsp_stx = dest->x1 + crtc->mode.htotal - crtc->mode.hsync_start; - dsp_sty = dest->y1 + crtc->mode.vtotal - crtc->mode.vsync_start; - dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff); - - offset = (src->x1 >> 16) * fb->format->cpp[0]; - offset += (src->y1 >> 16) * fb->pitches[0]; - dma_addr = rk_obj->dma_addr + offset + fb->offsets[0]; - - /* - * For y-mirroring we need to move address - * to the beginning of the last line. - */ - if (state->rotation & DRM_MODE_REFLECT_Y) - dma_addr += (actual_h - 1) * fb->pitches[0]; - - format = vop_convert_format(fb->format->format); - - spin_lock(&vop->reg_lock); - - if (rockchip_afbc(fb->modifier)) { - int afbc_format = vop_convert_afbc_format(fb->format->format); - - VOP_AFBC_SET(vop, format, afbc_format | AFBC_TILE_16x16); - VOP_AFBC_SET(vop, hreg_block_split, 0); - VOP_AFBC_SET(vop, win_sel, VOP_WIN_TO_INDEX(vop_win)); - VOP_AFBC_SET(vop, hdr_ptr, dma_addr); - VOP_AFBC_SET(vop, pic_size, act_info); + dsp_w = drm_rect_width(dest); + if (dest->x1 + dsp_w > adjusted_mode->hdisplay) { + DRM_ERROR("%s win%d dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n", + crtc->name, win->win_id, dest->x1, dsp_w, adjusted_mode->hdisplay); + dsp_w = adjusted_mode->hdisplay - dest->x1; + if (dsp_w < 4) + dsp_w = 4; + actual_w = dsp_w * actual_w / drm_rect_width(dest); + } + dsp_h = drm_rect_height(dest); + if (dest->y1 + dsp_h > adjusted_mode->vdisplay) { + DRM_ERROR("%s win%d dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n", + crtc->name, win->win_id, dest->y1, dsp_h, adjusted_mode->vdisplay); + dsp_h = adjusted_mode->vdisplay - dest->y1; + if (dsp_h < 4) + dsp_h = 4; + actual_h = dsp_h * actual_h / drm_rect_height(dest); } - VOP_WIN_SET(vop, win, format, format); + act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff); + + dsp_info = (dsp_h - 1) << 16; + dsp_info |= (dsp_w - 1) & 0xffff; + + dsp_stx = dest->x1 + mode->crtc_htotal - mode->crtc_hsync_start; + dsp_sty = dest->y1 + mode->crtc_vtotal - mode->crtc_vsync_start; + dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff); + + s = to_rockchip_crtc_state(crtc->state); + spin_lock(&vop->reg_lock); + + VOP_WIN_SET(vop, win, format, vop_plane_state->format); VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4)); - VOP_WIN_SET(vop, win, yrgb_mst, dma_addr); - VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, y2r_en, is_yuv); - VOP_WIN_SET(vop, win, y_mir_en, + VOP_WIN_SET(vop, win, yrgb_mst, vop_plane_state->yrgb_mst); + + VOP_WIN_SET(vop, win, ymirror, (state->rotation & DRM_MODE_REFLECT_Y) ? 1 : 0); - VOP_WIN_SET(vop, win, x_mir_en, + VOP_WIN_SET(vop, win, xmirror, (state->rotation & DRM_MODE_REFLECT_X) ? 1 : 0); if (is_yuv) { - int hsub = fb->format->hsub; - int vsub = fb->format->vsub; - int bpp = fb->format->cpp[1]; - - uv_obj = fb->obj[1]; - rk_uv_obj = to_rockchip_obj(uv_obj); - - offset = (src->x1 >> 16) * bpp / hsub; - offset += (src->y1 >> 16) * fb->pitches[1] / vsub; - - dma_addr = rk_uv_obj->dma_addr + offset + fb->offsets[1]; VOP_WIN_SET(vop, win, uv_vir, DIV_ROUND_UP(fb->pitches[1], 4)); - VOP_WIN_SET(vop, win, uv_mst, dma_addr); - - for (i = 0; i < NUM_YUV2YUV_COEFFICIENTS; i++) { - VOP_WIN_YUV2YUV_COEFFICIENT_SET(vop, - win_yuv2yuv, - y2r_coefficients[i], - bt601_yuv2rgb[i]); - } + VOP_WIN_SET(vop, win, uv_mst, vop_plane_state->uv_mst); } + VOP_WIN_SET(vop, win, fmt_10, is_yuv_10bit(fb->format->format)); + VOP_WIN_SET(vop, win, fmt_yuyv, is_yuyv_format(fb->format->format)); if (win->phy->scl) scl_vop_cal_scl_fac(vop, win, actual_w, actual_h, drm_rect_width(dest), drm_rect_height(dest), - fb->format); + fb->format->format); VOP_WIN_SET(vop, win, act_info, act_info); VOP_WIN_SET(vop, win, dsp_info, dsp_info); VOP_WIN_SET(vop, win, dsp_st, dsp_st); rb_swap = has_rb_swapped(fb->format->format); + /* + * VOP full need to do rb swap to show rgb888/bgr888 format color correctly + */ + if ((fb->format->format == DRM_FORMAT_RGB888 || fb->format->format == DRM_FORMAT_BGR888) && + VOP_MAJOR(vop->version) == 3) + rb_swap = !rb_swap; VOP_WIN_SET(vop, win, rb_swap, rb_swap); - /* - * Blending win0 with the background color doesn't seem to work - * correctly. We only get the background color, no matter the contents - * of the win0 framebuffer. However, blending pre-multiplied color - * with the default opaque black default background color is a no-op, - * so we can just disable blending to get the correct result. - */ - if (fb->format->has_alpha && win_index > 0) { + global_alpha_en = (vop_plane_state->global_alpha == 0xff) ? 0 : 1; + if ((is_alpha_support(fb->format->format) || global_alpha_en) && + (s->dsp_layer_sel & 0x3) != win->win_id) { + int src_blend_m0; + + if (is_alpha_support(fb->format->format) && global_alpha_en) + src_blend_m0 = ALPHA_PER_PIX_GLOBAL; + else if (is_alpha_support(fb->format->format)) + src_blend_m0 = ALPHA_PER_PIX; + else + src_blend_m0 = ALPHA_GLOBAL; + VOP_WIN_SET(vop, win, dst_alpha_ctl, DST_FACTOR_M0(ALPHA_SRC_INVERSE)); val = SRC_ALPHA_EN(1) | SRC_COLOR_M0(ALPHA_SRC_PRE_MUL) | SRC_ALPHA_M0(ALPHA_STRAIGHT) | - SRC_BLEND_M0(ALPHA_PER_PIX) | - SRC_ALPHA_CAL_M0(ALPHA_NO_SATURATION) | - SRC_FACTOR_M0(ALPHA_ONE); + SRC_BLEND_M0(src_blend_m0) | + SRC_ALPHA_CAL_M0(ALPHA_SATURATION) | + SRC_FACTOR_M0(global_alpha_en ? + ALPHA_SRC_GLOBAL : ALPHA_ONE); VOP_WIN_SET(vop, win, src_alpha_ctl, val); - - VOP_WIN_SET(vop, win, alpha_pre_mul, ALPHA_SRC_PRE_MUL); - VOP_WIN_SET(vop, win, alpha_mode, ALPHA_PER_PIX); + VOP_WIN_SET(vop, win, alpha_pre_mul, + vop_plane_state->blend_mode); + VOP_WIN_SET(vop, win, alpha_mode, 1); VOP_WIN_SET(vop, win, alpha_en, 1); } else { VOP_WIN_SET(vop, win, src_alpha_ctl, SRC_ALPHA_EN(0)); + VOP_WIN_SET(vop, win, alpha_en, 0); } + VOP_WIN_SET(vop, win, global_alpha_val, vop_plane_state->global_alpha); + VOP_WIN_SET(vop, win, csc_mode, vop_plane_state->csc_mode); + if (win->csc) { + vop_load_csc_table(vop, win->csc->y2r_offset, y2r_table); + vop_load_csc_table(vop, win->csc->r2r_offset, r2r_table); + vop_load_csc_table(vop, win->csc->r2y_offset, r2y_table); + VOP_WIN_SET_EXT(vop, win, csc, y2r_en, vop_plane_state->y2r_en); + VOP_WIN_SET_EXT(vop, win, csc, r2r_en, vop_plane_state->r2r_en); + VOP_WIN_SET_EXT(vop, win, csc, r2y_en, vop_plane_state->r2y_en); + VOP_WIN_SET_EXT(vop, win, csc, csc_mode, vop_plane_state->csc_mode); + } VOP_WIN_SET(vop, win, enable, 1); - vop->win_enabled |= BIT(win_index); + VOP_WIN_SET(vop, win, gate, 1); spin_unlock(&vop->reg_lock); -} + /* + * spi interface(vop_plane_state->yrgb_kvaddr, fb->pixel_format, + * actual_w, actual_h) + */ + vop->is_iommu_needed = true; +#if defined(CONFIG_ROCKCHIP_DRM_DEBUG) + kfree(vop_plane_state->planlist); + vop_plane_state->planlist = NULL; -static int vop_plane_atomic_async_check(struct drm_plane *plane, - struct drm_plane_state *state) -{ - struct vop_win *vop_win = to_vop_win(plane); - const struct vop_win_data *win = vop_win->data; - int min_scale = win->phy->scl ? FRAC_16_16(1, 8) : - DRM_PLANE_HELPER_NO_SCALING; - int max_scale = win->phy->scl ? FRAC_16_16(8, 1) : - DRM_PLANE_HELPER_NO_SCALING; - struct drm_crtc_state *crtc_state; - - if (plane != state->crtc->cursor) - return -EINVAL; - - if (!plane->state) - return -EINVAL; - - if (!plane->state->fb) - return -EINVAL; - - if (state->state) - crtc_state = drm_atomic_get_existing_crtc_state(state->state, - state->crtc); - else /* Special case for asynchronous cursor updates. */ - crtc_state = plane->crtc->state; - - return drm_atomic_helper_check_plane_state(plane->state, crtc_state, - min_scale, max_scale, - true, true); -} - -static void vop_plane_atomic_async_update(struct drm_plane *plane, - struct drm_plane_state *new_state) -{ - struct vop *vop = to_vop(plane->state->crtc); - struct drm_framebuffer *old_fb = plane->state->fb; - - plane->state->crtc_x = new_state->crtc_x; - plane->state->crtc_y = new_state->crtc_y; - plane->state->crtc_h = new_state->crtc_h; - plane->state->crtc_w = new_state->crtc_w; - plane->state->src_x = new_state->src_x; - plane->state->src_y = new_state->src_y; - plane->state->src_h = new_state->src_h; - plane->state->src_w = new_state->src_w; - swap(plane->state->fb, new_state->fb); - - if (vop->is_enabled) { - vop_plane_atomic_update(plane, plane->state); - spin_lock(&vop->reg_lock); - vop_cfg_done(vop); - spin_unlock(&vop->reg_lock); - - /* - * A scanout can still be occurring, so we can't drop the - * reference to the old framebuffer. To solve this we get a - * reference to old_fb and set a worker to release it later. - * FIXME: if we perform 500 async_update calls before the - * vblank, then we can have 500 different framebuffers waiting - * to be released. - */ - if (old_fb && plane->state->fb != old_fb) { - drm_framebuffer_get(old_fb); - WARN_ON(drm_crtc_vblank_get(plane->state->crtc) != 0); - drm_flip_work_queue(&vop->fb_unref_work, old_fb); - set_bit(VOP_PENDING_FB_UNREF, &vop->pending); - } + planlist = kmalloc(sizeof(*planlist), GFP_KERNEL); + if (planlist) { + planlist->dump_info.AFBC_flag = AFBC_flag; + planlist->dump_info.area_id = win->area_id; + planlist->dump_info.win_id = win->win_id; + planlist->dump_info.yuv_format = + is_yuv_support(fb->format->format); + planlist->dump_info.num_pages = num_pages; + planlist->dump_info.pages = pages; + planlist->dump_info.offset = vop_plane_state->offset; + planlist->dump_info.pitches = fb->pitches[0]; + planlist->dump_info.height = actual_h; + planlist->dump_info.pixel_format = fb->format->format; + list_add_tail(&planlist->entry, &crtc->vop_dump_list_head); + vop_plane_state->planlist = planlist; + } else { + DRM_ERROR("can't alloc a node of planlist %p\n", planlist); + return; } + if (crtc->vop_dump_status == DUMP_KEEP || + crtc->vop_dump_times > 0) { + vop_plane_dump(&planlist->dump_info, crtc->frame_count); + crtc->vop_dump_times--; + } +#endif } static const struct drm_plane_helper_funcs plane_helper_funcs = { + .prepare_fb = vop_plane_prepare_fb, + .cleanup_fb = vop_plane_cleanup_fb, .atomic_check = vop_plane_atomic_check, .atomic_update = vop_plane_atomic_update, .atomic_disable = vop_plane_atomic_disable, - .atomic_async_check = vop_plane_atomic_async_check, - .atomic_async_update = vop_plane_atomic_async_update, - .prepare_fb = drm_gem_fb_prepare_fb, }; +/** + * rockchip_atomic_helper_update_plane copy from drm_atomic_helper_update_plane + * be designed to support async commit at ioctl DRM_IOCTL_MODE_SETPLANE. + * @plane: plane object to update + * @crtc: owning CRTC of owning plane + * @fb: framebuffer to flip onto plane + * @crtc_x: x offset of primary plane on crtc + * @crtc_y: y offset of primary plane on crtc + * @crtc_w: width of primary plane rectangle on crtc + * @crtc_h: height of primary plane rectangle on crtc + * @src_x: x offset of @fb for panning + * @src_y: y offset of @fb for panning + * @src_w: width of source rectangle in @fb + * @src_h: height of source rectangle in @fb + * @ctx: lock acquire context + * + * Provides a default plane update handler using the atomic driver interface. + * + * RETURNS: + * Zero on success, error code on failure + */ +static int __maybe_unused +rockchip_atomic_helper_update_plane(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_atomic_state *state; + struct drm_plane_state *plane_state; + struct vop_plane_state *vop_plane_state; + int ret = 0; + + state = drm_atomic_state_alloc(plane->dev); + if (!state) + return -ENOMEM; + + state->acquire_ctx = ctx; + plane_state = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(plane_state)) { + ret = PTR_ERR(plane_state); + goto fail; + } + + vop_plane_state = to_vop_plane_state(plane_state); + + ret = drm_atomic_set_crtc_for_plane(plane_state, crtc); + if (ret != 0) + goto fail; + drm_atomic_set_fb_for_plane(plane_state, fb); + plane_state->crtc_x = crtc_x; + plane_state->crtc_y = crtc_y; + plane_state->crtc_w = crtc_w; + plane_state->crtc_h = crtc_h; + plane_state->src_x = src_x; + plane_state->src_y = src_y; + plane_state->src_w = src_w; + plane_state->src_h = src_h; + + if (plane == crtc->cursor || vop_plane_state->async_commit) + state->legacy_cursor_update = true; + + ret = drm_atomic_commit(state); +fail: + drm_atomic_state_put(state); + return ret; +} + +/** + * drm_atomic_helper_disable_plane copy from drm_atomic_helper_disable_plane + * be designed to support async commit at ioctl DRM_IOCTL_MODE_SETPLANE. + * + * @plane: plane to disable + * @ctx: lock acquire context + * + * Provides a default plane disable handler using the atomic driver interface. + * + * RETURNS: + * Zero on success, error code on failure + */ +static int __maybe_unused +rockchip_atomic_helper_disable_plane(struct drm_plane *plane, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_atomic_state *state; + struct drm_plane_state *plane_state; + struct vop_plane_state *vop_plane_state; + int ret = 0; + + state = drm_atomic_state_alloc(plane->dev); + if (!state) + return -ENOMEM; + + state->acquire_ctx = ctx; + plane_state = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(plane_state)) { + ret = PTR_ERR(plane_state); + goto fail; + } + vop_plane_state = to_vop_plane_state(plane_state); + + if ((plane_state->crtc && plane_state->crtc->cursor == plane) || + vop_plane_state->async_commit) + plane_state->state->legacy_cursor_update = true; + + ret = __drm_atomic_helper_disable_plane(plane, plane_state); + if (ret != 0) + goto fail; + + ret = drm_atomic_commit(state); +fail: + drm_atomic_state_put(state); + return ret; +} + +static void vop_plane_destroy(struct drm_plane *plane) +{ + drm_plane_cleanup(plane); +} + +static void vop_atomic_plane_reset(struct drm_plane *plane) +{ + struct vop_plane_state *vop_plane_state = + to_vop_plane_state(plane->state); + struct vop_win *win = to_vop_win(plane); + + if (plane->state && plane->state->fb) + __drm_atomic_helper_plane_destroy_state(plane->state); + kfree(vop_plane_state); + vop_plane_state = kzalloc(sizeof(*vop_plane_state), GFP_KERNEL); + if (!vop_plane_state) + return; + + win->state.zpos = win->zpos; + vop_plane_state->global_alpha = 0xff; + plane->state = &vop_plane_state->base; + plane->state->plane = plane; +} + +static struct drm_plane_state * +vop_atomic_plane_duplicate_state(struct drm_plane *plane) +{ + struct vop_plane_state *old_vop_plane_state; + struct vop_plane_state *vop_plane_state; + + if (WARN_ON(!plane->state)) + return NULL; + + old_vop_plane_state = to_vop_plane_state(plane->state); + vop_plane_state = kmemdup(old_vop_plane_state, + sizeof(*vop_plane_state), GFP_KERNEL); + if (!vop_plane_state) + return NULL; + + __drm_atomic_helper_plane_duplicate_state(plane, + &vop_plane_state->base); + + return &vop_plane_state->base; +} + +static void vop_atomic_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct vop_plane_state *vop_state = to_vop_plane_state(state); + + __drm_atomic_helper_plane_destroy_state(state); + + kfree(vop_state); +} + +static int vop_atomic_plane_set_property(struct drm_plane *plane, + struct drm_plane_state *state, + struct drm_property *property, + uint64_t val) +{ + struct rockchip_drm_private *private = plane->dev->dev_private; + struct vop_win *win = to_vop_win(plane); + struct vop_plane_state *plane_state = to_vop_plane_state(state); + + if (property == win->vop->plane_zpos_prop) { + plane_state->zpos = val; + return 0; + } + + if (property == private->eotf_prop) { + plane_state->eotf = val; + return 0; + } + + if (property == private->color_space_prop) { + plane_state->color_space = val; + return 0; + } + + if (property == private->global_alpha_prop) { + plane_state->global_alpha = val; + return 0; + } + + if (property == private->blend_mode_prop) { + plane_state->blend_mode = val; + return 0; + } + + if (property == private->async_commit_prop) { + plane_state->async_commit = val; + return 0; + } + + DRM_ERROR("failed to set vop plane property id:%d, name:%s\n", + property->base.id, property->name); + + return -EINVAL; +} + +static int vop_atomic_plane_get_property(struct drm_plane *plane, + const struct drm_plane_state *state, + struct drm_property *property, + uint64_t *val) +{ + struct vop_plane_state *plane_state = to_vop_plane_state(state); + struct vop_win *win = to_vop_win(plane); + struct rockchip_drm_private *private = plane->dev->dev_private; + + if (property == win->vop->plane_zpos_prop) { + *val = plane_state->zpos; + return 0; + } + + if (property == private->eotf_prop) { + *val = plane_state->eotf; + return 0; + } + + if (property == private->color_space_prop) { + *val = plane_state->color_space; + return 0; + } + + if (property == private->global_alpha_prop) { + *val = plane_state->global_alpha; + return 0; + } + + if (property == private->blend_mode_prop) { + *val = plane_state->blend_mode; + return 0; + } + + if (property == private->async_commit_prop) { + *val = plane_state->async_commit; + return 0; + } + + if (property == private->share_id_prop) { + int i; + struct drm_mode_object *obj = &plane->base; + + for (i = 0; i < obj->properties->count; i++) { + if (obj->properties->properties[i] == property) { + *val = obj->properties->values[i]; + return 0; + } + } + } + + DRM_ERROR("failed to get vop plane property id:%d, name:%s\n", + property->base.id, property->name); + + return -EINVAL; +} + static const struct drm_plane_funcs vop_plane_funcs = { - .update_plane = drm_atomic_helper_update_plane, - .disable_plane = drm_atomic_helper_disable_plane, + .update_plane = rockchip_atomic_helper_update_plane, + .disable_plane = rockchip_atomic_helper_disable_plane, .destroy = vop_plane_destroy, - .reset = drm_atomic_helper_plane_reset, - .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, - .format_mod_supported = rockchip_mod_supported, + .reset = vop_atomic_plane_reset, + .atomic_duplicate_state = vop_atomic_plane_duplicate_state, + .atomic_destroy_state = vop_atomic_plane_destroy_state, + .atomic_set_property = vop_atomic_plane_set_property, + .atomic_get_property = vop_atomic_plane_get_property, }; static int vop_crtc_enable_vblank(struct drm_crtc *crtc) @@ -1119,8 +2270,13 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc) spin_lock_irqsave(&vop->irq_lock, flags); - VOP_INTR_SET_TYPE(vop, clear, FS_INTR, 1); - VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 1); + if (VOP_MAJOR(vop->version) == 3 && VOP_MINOR(vop->version) >= 7) { + VOP_INTR_SET_TYPE(vop, clear, FS_FIELD_INTR, 1); + VOP_INTR_SET_TYPE(vop, enable, FS_FIELD_INTR, 1); + } else { + VOP_INTR_SET_TYPE(vop, clear, FS_INTR, 1); + VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 1); + } spin_unlock_irqrestore(&vop->irq_lock, flags); @@ -1137,258 +2293,1395 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc) spin_lock_irqsave(&vop->irq_lock, flags); - VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0); + if (VOP_MAJOR(vop->version) == 3 && VOP_MINOR(vop->version) >= 7) + VOP_INTR_SET_TYPE(vop, enable, FS_FIELD_INTR, 0); + else + VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0); spin_unlock_irqrestore(&vop->irq_lock, flags); } -static bool vop_crtc_mode_fixup(struct drm_crtc *crtc, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static void vop_crtc_cancel_pending_vblank(struct drm_crtc *crtc, + struct drm_file *file_priv) +{ + struct drm_device *drm = crtc->dev; + struct vop *vop = to_vop(crtc); + struct drm_pending_vblank_event *e; + unsigned long flags; + + spin_lock_irqsave(&drm->event_lock, flags); + e = vop->event; + if (e && e->base.file_priv == file_priv) { + vop->event = NULL; + + /* e->base.destroy(&e->base);//todo */ + file_priv->event_space += sizeof(e->event); + } + spin_unlock_irqrestore(&drm->event_lock, flags); +} + +static int vop_crtc_loader_protect(struct drm_crtc *crtc, bool on) +{ + struct rockchip_drm_private *private = crtc->dev->dev_private; + struct vop *vop = to_vop(crtc); + int sys_status = drm_crtc_index(crtc) ? + SYS_STATUS_LCDC1 : SYS_STATUS_LCDC0; + + if (on == vop->loader_protect) + return 0; + + if (on) { + if (vop->dclk_source) { + struct clk *parent; + + parent = clk_get_parent(vop->dclk_source); + if (parent) { + if (clk_is_match(private->default_pll.pll, parent)) + vop->pll = &private->default_pll; + else if (clk_is_match(private->hdmi_pll.pll, parent)) + vop->pll = &private->hdmi_pll; + if (vop->pll) + vop->pll->use_count++; + } + } + + rockchip_set_system_status(sys_status); + vop_initial(crtc); + drm_crtc_vblank_on(crtc); + vop->loader_protect = true; + } else { + vop_crtc_atomic_disable(crtc, NULL); + + if (vop->dclk_source && vop->pll) { + vop->pll->use_count--; + vop->pll = NULL; + } + vop->loader_protect = false; + } + + return 0; +} + +#define DEBUG_PRINT(args...) \ + do { \ + if (s) \ + seq_printf(s, args); \ + else \ + pr_err(args); \ + } while (0) + +static int vop_plane_info_dump(struct seq_file *s, struct drm_plane *plane) +{ + struct vop_win *win = to_vop_win(plane); + struct drm_plane_state *state = plane->state; + struct vop_plane_state *pstate = to_vop_plane_state(state); + struct drm_rect *src, *dest; + struct drm_framebuffer *fb = state->fb; + struct drm_format_name_buf format_name; + int i; + struct drm_gem_object *obj; + struct rockchip_gem_object *rk_obj; + dma_addr_t fb_addr; + u64 afbdc_format = + DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16); + + DEBUG_PRINT(" win%d-%d: %s\n", win->win_id, win->area_id, + state->crtc ? "ACTIVE" : "DISABLED"); + if (!fb) + return 0; + + src = &pstate->src; + dest = &pstate->dest; + + drm_get_format_name(fb->format->format, &format_name); + DEBUG_PRINT("\tformat: %s%s%s[%d] color_space[%d]\n", + format_name.str, + fb->modifier == afbdc_format ? "[AFBC]" : "", + pstate->eotf ? " HDR" : " SDR", pstate->eotf, + pstate->color_space); + DEBUG_PRINT("\tcsc: y2r[%d] r2r[%d] r2y[%d] csc mode[%d]\n", + pstate->y2r_en, pstate->r2r_en, pstate->r2y_en, + pstate->csc_mode); + DEBUG_PRINT("\tzpos: %d\n", pstate->zpos); + DEBUG_PRINT("\tsrc: pos[%dx%d] rect[%dx%d]\n", src->x1 >> 16, + src->y1 >> 16, drm_rect_width(src) >> 16, + drm_rect_height(src) >> 16); + DEBUG_PRINT("\tdst: pos[%dx%d] rect[%dx%d]\n", dest->x1, dest->y1, + drm_rect_width(dest), drm_rect_height(dest)); + + for (i = 0; i < fb->format->num_planes; i++) { + obj = fb->obj[0]; + rk_obj = to_rockchip_obj(obj); + fb_addr = rk_obj->dma_addr + fb->offsets[0]; + + DEBUG_PRINT("\tbuf[%d]: addr: %pad pitch: %d offset: %d\n", + i, &fb_addr, fb->pitches[i], fb->offsets[i]); + } + + return 0; +} + +static void vop_dump_connector_on_crtc(struct drm_crtc *crtc, struct seq_file *s) +{ + struct drm_connector_list_iter conn_iter; + struct drm_connector *connector; + + drm_connector_list_iter_begin(crtc->dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + if (crtc->state->connector_mask & drm_connector_mask(connector)) + DEBUG_PRINT(" Connector: %s\n", connector->name); + + } + drm_connector_list_iter_end(&conn_iter); +} + +static int vop_crtc_debugfs_dump(struct drm_crtc *crtc, struct seq_file *s) { struct vop *vop = to_vop(crtc); - unsigned long rate; + struct drm_crtc_state *crtc_state = crtc->state; + struct drm_display_mode *mode = &crtc->state->adjusted_mode; + struct rockchip_crtc_state *state = to_rockchip_crtc_state(crtc->state); + bool interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); + struct drm_plane *plane; + int i; + + DEBUG_PRINT("VOP [%s]: %s\n", dev_name(vop->dev), + crtc_state->active ? "ACTIVE" : "DISABLED"); + + if (!crtc_state->active) + return 0; + + vop_dump_connector_on_crtc(crtc, s); + DEBUG_PRINT("\tbus_format[%x]: %s\n", state->bus_format, + drm_get_bus_format_name(state->bus_format)); + DEBUG_PRINT("\toverlay_mode[%d] output_mode[%x]", + state->yuv_overlay, state->output_mode); + DEBUG_PRINT(" color_space[%d]\n", + state->color_space); + DEBUG_PRINT(" Display mode: %dx%d%s%d\n", + mode->hdisplay, mode->vdisplay, interlaced ? "i" : "p", + drm_mode_vrefresh(mode)); + DEBUG_PRINT("\tclk[%d] real_clk[%d] type[%x] flag[%x]\n", + mode->clock, mode->crtc_clock, mode->type, mode->flags); + DEBUG_PRINT("\tH: %d %d %d %d\n", mode->hdisplay, mode->hsync_start, + mode->hsync_end, mode->htotal); + DEBUG_PRINT("\tV: %d %d %d %d\n", mode->vdisplay, mode->vsync_start, + mode->vsync_end, mode->vtotal); + + for (i = 0; i < vop->num_wins; i++) { + plane = &vop->win[i].base; + vop_plane_info_dump(s, plane); + } + DEBUG_PRINT(" post: sdr2hdr[%d] hdr2sdr[%d]\n", + state->hdr.sdr2hdr_state.bt1886eotf_post_conv_en, + state->hdr.hdr2sdr_en); + DEBUG_PRINT(" pre : sdr2hdr[%d]\n", + state->hdr.sdr2hdr_state.bt1886eotf_pre_conv_en); + DEBUG_PRINT(" post CSC: r2y[%d] y2r[%d] CSC mode[%d]\n", + state->post_r2y_en, state->post_y2r_en, + state->post_csc_mode); + + return 0; +} + +static void vop_crtc_regs_dump(struct drm_crtc *crtc, struct seq_file *s) +{ + struct vop *vop = to_vop(crtc); + struct drm_crtc_state *crtc_state = crtc->state; + int dump_len = vop->len > 0x400 ? 0x400 : vop->len; + int i; + + if (!crtc_state->active) + return; + + for (i = 0; i < dump_len; i += 16) { + DEBUG_PRINT("0x%08x: %08x %08x %08x %08x\n", i, + vop_readl(vop, i), vop_readl(vop, i + 4), + vop_readl(vop, i + 8), vop_readl(vop, i + 12)); + } +} + +static int vop_gamma_show(struct seq_file *s, void *data) +{ + struct drm_info_node *node = s->private; + struct vop *vop = node->info_ent->data; + int i; + + if (!vop->lut || !vop->lut_active || !vop->lut_regs) + return 0; + + for (i = 0; i < vop->lut_len; i++) { + if (i % 8 == 0) + DEBUG_PRINT("\n"); + DEBUG_PRINT("0x%08x ", vop->lut[i]); + } + DEBUG_PRINT("\n"); + + return 0; +} + +#undef DEBUG_PRINT + +static struct drm_info_list vop_debugfs_files[] = { + { "gamma_lut", vop_gamma_show, 0, NULL }, +}; + +static int vop_crtc_debugfs_init(struct drm_minor *minor, struct drm_crtc *crtc) +{ + struct vop *vop = to_vop(crtc); + int ret, i; + + vop->debugfs = debugfs_create_dir(dev_name(vop->dev), + minor->debugfs_root); + + if (!vop->debugfs) + return -ENOMEM; + + vop->debugfs_files = kmemdup(vop_debugfs_files, + sizeof(vop_debugfs_files), + GFP_KERNEL); + if (!vop->debugfs_files) { + ret = -ENOMEM; + goto remove; + } +#if defined(CONFIG_ROCKCHIP_DRM_DEBUG) + drm_debugfs_vop_add(crtc, vop->debugfs); +#endif + for (i = 0; i < ARRAY_SIZE(vop_debugfs_files); i++) + vop->debugfs_files[i].data = vop; + + drm_debugfs_create_files(vop->debugfs_files, ARRAY_SIZE(vop_debugfs_files), + vop->debugfs, minor); + + return 0; +remove: + debugfs_remove(vop->debugfs); + vop->debugfs = NULL; + return ret; +} + +static enum drm_mode_status +vop_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode, + int output_type) +{ + struct vop *vop = to_vop(crtc); + const struct vop_data *vop_data = vop->data; + int request_clock = mode->clock; + int clock; + + if (mode->hdisplay > vop_data->max_output.width) + return MODE_BAD_HVALUE; + + if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && + VOP_MAJOR(vop->version) == 3 && + VOP_MINOR(vop->version) <= 2) + return MODE_BAD; + + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + request_clock *= 2; + clock = clk_round_rate(vop->dclk, request_clock * 1000) / 1000; /* - * Clock craziness. - * - * Key points: - * - * - DRM works in in kHz. - * - Clock framework works in Hz. - * - Rockchip's clock driver picks the clock rate that is the - * same _OR LOWER_ than the one requested. - * - * Action plan: - * - * 1. When DRM gives us a mode, we should add 999 Hz to it. That way - * if the clock we need is 60000001 Hz (~60 MHz) and DRM tells us to - * make 60000 kHz then the clock framework will actually give us - * the right clock. - * - * NOTE: if the PLL (maybe through a divider) could actually make - * a clock rate 999 Hz higher instead of the one we want then this - * could be a problem. Unfortunately there's not much we can do - * since it's baked into DRM to use kHz. It shouldn't matter in - * practice since Rockchip PLLs are controlled by tables and - * even if there is a divider in the middle I wouldn't expect PLL - * rates in the table that are just a few kHz different. - * - * 2. Get the clock framework to round the rate for us to tell us - * what it will actually make. - * - * 3. Store the rounded up rate so that we don't need to worry about - * this in the actual clk_set_rate(). + * Hdmi or DisplayPort request a Accurate clock. */ - rate = clk_round_rate(vop->dclk, adjusted_mode->clock * 1000 + 999); - adjusted_mode->clock = DIV_ROUND_UP(rate, 1000); + if (output_type == DRM_MODE_CONNECTOR_HDMIA || + output_type == DRM_MODE_CONNECTOR_DisplayPort) + if (clock != request_clock) + return MODE_CLOCK_RANGE; + + return MODE_OK; +} + +struct vop_bandwidth { + size_t bandwidth; + int y1; + int y2; +}; + +static int vop_bandwidth_cmp(const void *a, const void *b) +{ + struct vop_bandwidth *pa = (struct vop_bandwidth *)a; + struct vop_bandwidth *pb = (struct vop_bandwidth *)b; + + return pa->y1 - pb->y2; +} + +static size_t vop_plane_line_bandwidth(struct drm_plane_state *pstate) +{ + struct vop_plane_state *vop_plane_state = to_vop_plane_state(pstate); + struct vop_win *win = to_vop_win(pstate->plane); + struct drm_crtc *crtc = pstate->crtc; + struct vop *vop = to_vop(crtc); + struct drm_framebuffer *fb = pstate->fb; + struct drm_rect *dest = &vop_plane_state->dest; + struct drm_rect *src = &vop_plane_state->src; + int bpp = fb->format->cpp[0] << 3; + int src_width = drm_rect_width(src) >> 16; + int src_height = drm_rect_height(src) >> 16; + int dest_width = drm_rect_width(dest); + int dest_height = drm_rect_height(dest); + int vskiplines = scl_get_vskiplines(src_height, dest_height); + size_t bandwidth; + + if (src_width <= 0 || src_height <= 0 || dest_width <= 0 || + dest_height <= 0) + return 0; + + bandwidth = src_width * bpp / 8; + + bandwidth = bandwidth * src_width / dest_width; + bandwidth = bandwidth * src_height / dest_height; + if (vskiplines == 2 && VOP_WIN_SCL_EXT_SUPPORT(vop, win, vsd_yrgb_gt2)) + bandwidth /= 2; + else if (vskiplines == 4 && + VOP_WIN_SCL_EXT_SUPPORT(vop, win, vsd_yrgb_gt4)) + bandwidth /= 4; + + return bandwidth; +} + +static u64 vop_calc_max_bandwidth(struct vop_bandwidth *bw, int start, + int count, int y2) +{ + u64 max_bandwidth = 0; + int i; + + for (i = start; i < count; i++) { + u64 bandwidth = 0; + + if (bw[i].y1 > y2) + continue; + bandwidth = bw[i].bandwidth; + bandwidth += vop_calc_max_bandwidth(bw, i + 1, count, + min(bw[i].y2, y2)); + + if (bandwidth > max_bandwidth) + max_bandwidth = bandwidth; + } + + return max_bandwidth; +} + +static size_t vop_crtc_bandwidth(struct drm_crtc *crtc, + struct drm_crtc_state *crtc_state, + unsigned int *plane_num_total) +{ + struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; + u16 htotal = adjusted_mode->crtc_htotal; + u16 vdisplay = adjusted_mode->crtc_vdisplay; + int clock = adjusted_mode->crtc_clock; + struct vop_plane_state *vop_plane_state; + struct drm_plane_state *pstate; + struct vop_bandwidth *pbandwidth; + struct drm_plane *plane; + u64 bandwidth; + int cnt = 0, plane_num = 0; + struct drm_atomic_state *state = crtc_state->state; +#if defined(CONFIG_ROCKCHIP_DRM_DEBUG) + struct vop_dump_list *pos, *n; +#endif + + if (!htotal || !vdisplay) + return 0; + +#if defined(CONFIG_ROCKCHIP_DRM_DEBUG) + if (!crtc->vop_dump_list_init_flag) { + INIT_LIST_HEAD(&crtc->vop_dump_list_head); + crtc->vop_dump_list_init_flag = true; + } + list_for_each_entry_safe(pos, n, &crtc->vop_dump_list_head, entry) { + list_del(&pos->entry); + } + if (crtc->vop_dump_status == DUMP_KEEP || + crtc->vop_dump_times > 0) { + crtc->frame_count++; + } +#endif + + drm_atomic_crtc_state_for_each_plane(plane, crtc_state) + plane_num++; + + if (plane_num_total) + *plane_num_total += plane_num; + pbandwidth = kmalloc_array(plane_num, sizeof(*pbandwidth), + GFP_KERNEL); + if (!pbandwidth) + return -ENOMEM; + + drm_atomic_crtc_state_for_each_plane(plane, crtc_state) { + pstate = drm_atomic_get_existing_plane_state(state, plane); + if (pstate->crtc != crtc || !pstate->fb) + continue; + + vop_plane_state = to_vop_plane_state(pstate); + pbandwidth[cnt].y1 = vop_plane_state->dest.y1; + pbandwidth[cnt].y2 = vop_plane_state->dest.y2; + pbandwidth[cnt++].bandwidth = vop_plane_line_bandwidth(pstate); + } + + sort(pbandwidth, cnt, sizeof(pbandwidth[0]), vop_bandwidth_cmp, NULL); + + bandwidth = vop_calc_max_bandwidth(pbandwidth, 0, cnt, vdisplay); + kfree(pbandwidth); + /* + * bandwidth(MB/s) + * = line_bandwidth / line_time + * = line_bandwidth(Byte) * clock(KHZ) / 1000 / htotal + */ + bandwidth *= clock; + do_div(bandwidth, htotal * 1000); + + return bandwidth; +} + +static void vop_crtc_close(struct drm_crtc *crtc) +{ + struct vop *vop = NULL; + + if (!crtc) + return; + vop = to_vop(crtc); + mutex_lock(&vop->vop_lock); + if (!vop->is_enabled) { + mutex_unlock(&vop->vop_lock); + return; + } + + vop_disable_all_planes(vop); + mutex_unlock(&vop->vop_lock); +} + +static u32 vop_mode_done(struct vop *vop) +{ + return VOP_CTRL_GET(vop, out_mode); +} + +static void vop_set_out_mode(struct vop *vop, u32 mode) +{ + int ret; + u32 val; + + VOP_CTRL_SET(vop, out_mode, mode); + vop_cfg_done(vop); + ret = readx_poll_timeout(vop_mode_done, vop, val, val == mode, + 1000, 500 * 1000); + if (ret) + dev_err(vop->dev, "wait mode 0x%x timeout\n", mode); + +} + +static void vop_crtc_send_mcu_cmd(struct drm_crtc *crtc, u32 type, u32 value) +{ + struct rockchip_crtc_state *state; + struct vop *vop = NULL; + + if (!crtc) + return; + + vop = to_vop(crtc); + state = to_rockchip_crtc_state(crtc->state); + + /* + * set output mode to P888 when start send cmd. + */ + if ((type == MCU_SETBYPASS) && value) + vop_set_out_mode(vop, ROCKCHIP_OUT_MODE_P888); + mutex_lock(&vop->vop_lock); + if (vop && vop->is_enabled) { + switch (type) { + case MCU_WRCMD: + VOP_CTRL_SET(vop, mcu_rs, 0); + VOP_CTRL_SET(vop, mcu_rw_bypass_port, value); + VOP_CTRL_SET(vop, mcu_rs, 1); + break; + case MCU_WRDATA: + VOP_CTRL_SET(vop, mcu_rs, 1); + VOP_CTRL_SET(vop, mcu_rw_bypass_port, value); + break; + case MCU_SETBYPASS: + VOP_CTRL_SET(vop, mcu_bypass, value ? 1 : 0); + break; + default: + break; + } + } + mutex_unlock(&vop->vop_lock); + + /* + * restore output mode at the end + */ + if ((type == MCU_SETBYPASS) && !value) + vop_set_out_mode(vop, state->output_mode); +} + +static const struct rockchip_crtc_funcs private_crtc_funcs = { + .loader_protect = vop_crtc_loader_protect, + .cancel_pending_vblank = vop_crtc_cancel_pending_vblank, + .debugfs_init = vop_crtc_debugfs_init, + .debugfs_dump = vop_crtc_debugfs_dump, + .regs_dump = vop_crtc_regs_dump, + .mode_valid = vop_crtc_mode_valid, + .bandwidth = vop_crtc_bandwidth, + .crtc_close = vop_crtc_close, + .crtc_send_mcu_cmd = vop_crtc_send_mcu_cmd, +}; + +static bool vop_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ + struct vop *vop = to_vop(crtc); + const struct vop_data *vop_data = vop->data; + + if (mode->hdisplay > vop_data->max_output.width) + return false; + + drm_mode_set_crtcinfo(adj_mode, + CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE); + + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + adj_mode->crtc_clock *= 2; + + adj_mode->crtc_clock = + DIV_ROUND_UP(clk_round_rate(vop->dclk, adj_mode->crtc_clock * 1000), + 1000); return true; } -static bool vop_dsp_lut_is_enabled(struct vop *vop) -{ - return vop_read_reg(vop, 0, &vop->data->common->dsp_lut_en); -} - -static void vop_crtc_write_gamma_lut(struct vop *vop, struct drm_crtc *crtc) -{ - struct drm_color_lut *lut = crtc->state->gamma_lut->data; - unsigned int i; - - for (i = 0; i < crtc->gamma_size; i++) { - u32 word; - - word = (drm_color_lut_extract(lut[i].red, 10) << 20) | - (drm_color_lut_extract(lut[i].green, 10) << 10) | - drm_color_lut_extract(lut[i].blue, 10); - writel(word, vop->lut_regs + i * 4); - } -} - -static void vop_crtc_gamma_set(struct vop *vop, struct drm_crtc *crtc, - struct drm_crtc_state *old_state) -{ - struct drm_crtc_state *state = crtc->state; - unsigned int idle; - int ret; - - if (!vop->lut_regs) - return; - /* - * To disable gamma (gamma_lut is null) or to write - * an update to the LUT, clear dsp_lut_en. - */ - spin_lock(&vop->reg_lock); - VOP_REG_SET(vop, common, dsp_lut_en, 0); - vop_cfg_done(vop); - spin_unlock(&vop->reg_lock); - - /* - * In order to write the LUT to the internal memory, - * we need to first make sure the dsp_lut_en bit is cleared. - */ - ret = readx_poll_timeout(vop_dsp_lut_is_enabled, vop, - idle, !idle, 5, 30 * 1000); - if (ret) { - DRM_DEV_ERROR(vop->dev, "display LUT RAM enable timeout!\n"); - return; - } - - if (!state->gamma_lut) - return; - - spin_lock(&vop->reg_lock); - vop_crtc_write_gamma_lut(vop, crtc); - VOP_REG_SET(vop, common, dsp_lut_en, 1); - vop_cfg_done(vop); - spin_unlock(&vop->reg_lock); -} - -static void vop_crtc_atomic_begin(struct drm_crtc *crtc, - struct drm_crtc_state *old_crtc_state) +static void vop_dither_setup(struct drm_crtc *crtc) { + struct rockchip_crtc_state *s = + to_rockchip_crtc_state(crtc->state); struct vop *vop = to_vop(crtc); /* - * Only update GAMMA if the 'active' flag is not changed, - * otherwise it's updated by .atomic_enable. + * VOP MCU interface can't work right when dither enabled. + * (1) the MCU CMD will be treated as data then changed by dither algorithm + * (2) the dither algorithm works wrong in mcu mode */ - if (crtc->state->color_mgmt_changed && - !crtc->state->active_changed) - vop_crtc_gamma_set(vop, crtc, old_crtc_state); + if (vop->mcu_timing.mcu_pix_total) + return; + + switch (s->bus_format) { + case MEDIA_BUS_FMT_RGB565_1X16: + VOP_CTRL_SET(vop, dither_down_en, 1); + VOP_CTRL_SET(vop, dither_down_mode, RGB888_TO_RGB565); + break; + case MEDIA_BUS_FMT_RGB666_1X18: + case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: + case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: + VOP_CTRL_SET(vop, dither_down_en, 1); + VOP_CTRL_SET(vop, dither_down_mode, RGB888_TO_RGB666); + break; + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + VOP_CTRL_SET(vop, dither_down_en, 0); + VOP_CTRL_SET(vop, pre_dither_down_en, 1); + break; + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + VOP_CTRL_SET(vop, dither_down_en, 0); + VOP_CTRL_SET(vop, pre_dither_down_en, 0); + break; + case MEDIA_BUS_FMT_RGB888_3X8: + case MEDIA_BUS_FMT_RGB888_DUMMY_4X8: + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: + case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: + default: + VOP_CTRL_SET(vop, dither_down_en, 0); + VOP_CTRL_SET(vop, pre_dither_down_en, 0); + break; + } + + VOP_CTRL_SET(vop, pre_dither_down_en, + s->output_mode == ROCKCHIP_OUT_MODE_AAAA ? 0 : 1); + VOP_CTRL_SET(vop, dither_down_sel, DITHER_DOWN_ALLEGRO); +} + +static void vop_update_csc(struct drm_crtc *crtc) +{ + struct rockchip_crtc_state *s = + to_rockchip_crtc_state(crtc->state); + struct vop *vop = to_vop(crtc); + u32 val; + + if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA && + !(vop->data->feature & VOP_FEATURE_OUTPUT_10BIT)) + s->output_mode = ROCKCHIP_OUT_MODE_P888; + + if (is_uv_swap(s->bus_format, s->output_mode)) + VOP_CTRL_SET(vop, dsp_data_swap, DSP_RB_SWAP); + else + VOP_CTRL_SET(vop, dsp_data_swap, 0); + + VOP_CTRL_SET(vop, out_mode, s->output_mode); + + vop_dither_setup(crtc); + VOP_CTRL_SET(vop, dclk_ddr, + s->output_mode == ROCKCHIP_OUT_MODE_YUV420 ? 1 : 0); + VOP_CTRL_SET(vop, hdmi_dclk_out_en, + s->output_mode == ROCKCHIP_OUT_MODE_YUV420 ? 1 : 0); + + VOP_CTRL_SET(vop, overlay_mode, s->yuv_overlay); + VOP_CTRL_SET(vop, dsp_out_yuv, is_yuv_output(s->bus_format)); + + /* + * Background color is 10bit depth if vop version >= 3.5 + */ + if (!is_yuv_output(s->bus_format)) + val = 0; + else if (VOP_MAJOR(vop->version) == 3 && VOP_MINOR(vop->version) == 8 && + s->hdr.pre_overlay) + val = 0; + else if (VOP_MAJOR(vop->version) == 3 && VOP_MINOR(vop->version) >= 5) + val = 0x20010200; + else + val = 0x801080; + VOP_CTRL_SET(vop, dsp_background, val); +} + +/* + * if adjusted mode update, return true, else return false + */ +static bool vop_crtc_mode_update(struct drm_crtc *crtc) +{ + struct vop *vop = to_vop(crtc); + struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; + u16 hsync_len = adjusted_mode->crtc_hsync_end - + adjusted_mode->crtc_hsync_start; + u16 hdisplay = adjusted_mode->crtc_hdisplay; + u16 htotal = adjusted_mode->crtc_htotal; + u16 hact_st = adjusted_mode->crtc_htotal - + adjusted_mode->crtc_hsync_start; + u16 hact_end = hact_st + hdisplay; + u16 vdisplay = adjusted_mode->crtc_vdisplay; + u16 vtotal = adjusted_mode->crtc_vtotal; + u16 vsync_len = adjusted_mode->crtc_vsync_end - + adjusted_mode->crtc_vsync_start; + u16 vact_st = adjusted_mode->crtc_vtotal - + adjusted_mode->crtc_vsync_start; + u16 vact_end = vact_st + vdisplay; + u32 htotal_sync = htotal << 16 | hsync_len; + u32 hactive_st_end = hact_st << 16 | hact_end; + u32 vtotal_sync = vtotal << 16 | vsync_len; + u32 vactive_st_end = vact_st << 16 | vact_end; + u32 crtc_clock = adjusted_mode->crtc_clock * 100; + + if (htotal_sync != VOP_CTRL_GET(vop, htotal_pw) || + hactive_st_end != VOP_CTRL_GET(vop, hact_st_end) || + vtotal_sync != VOP_CTRL_GET(vop, vtotal_pw) || + vactive_st_end != VOP_CTRL_GET(vop, vact_st_end) || + crtc_clock != clk_get_rate(vop->dclk)) + return true; + + return false; +} + +static void vop_mcu_mode(struct drm_crtc *crtc) +{ + struct vop *vop = to_vop(crtc); + + VOP_CTRL_SET(vop, mcu_clk_sel, 1); + VOP_CTRL_SET(vop, mcu_type, 1); + + VOP_CTRL_SET(vop, mcu_hold_mode, 1); + VOP_CTRL_SET(vop, mcu_pix_total, vop->mcu_timing.mcu_pix_total); + VOP_CTRL_SET(vop, mcu_cs_pst, vop->mcu_timing.mcu_cs_pst); + VOP_CTRL_SET(vop, mcu_cs_pend, vop->mcu_timing.mcu_cs_pend); + VOP_CTRL_SET(vop, mcu_rw_pst, vop->mcu_timing.mcu_rw_pst); + VOP_CTRL_SET(vop, mcu_rw_pend, vop->mcu_timing.mcu_rw_pend); } static void vop_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { struct vop *vop = to_vop(crtc); - const struct vop_data *vop_data = vop->data; struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state); struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; - u16 hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start; - u16 hdisplay = adjusted_mode->hdisplay; - u16 htotal = adjusted_mode->htotal; - u16 hact_st = adjusted_mode->htotal - adjusted_mode->hsync_start; + u16 hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start; + u16 hdisplay = adjusted_mode->crtc_hdisplay; + u16 htotal = adjusted_mode->crtc_htotal; + u16 hact_st = adjusted_mode->crtc_htotal - adjusted_mode->crtc_hsync_start; u16 hact_end = hact_st + hdisplay; - u16 vdisplay = adjusted_mode->vdisplay; - u16 vtotal = adjusted_mode->vtotal; - u16 vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start; - u16 vact_st = adjusted_mode->vtotal - adjusted_mode->vsync_start; + u16 vdisplay = adjusted_mode->crtc_vdisplay; + u16 vtotal = adjusted_mode->crtc_vtotal; + u16 vsync_len = adjusted_mode->crtc_vsync_end - adjusted_mode->crtc_vsync_start; + u16 vact_st = adjusted_mode->crtc_vtotal - adjusted_mode->crtc_vsync_start; u16 vact_end = vact_st + vdisplay; - uint32_t pin_pol, val; - int dither_bpc = s->output_bpc ? s->output_bpc : 10; - int ret; - - if (old_state && old_state->self_refresh_active) { - drm_crtc_vblank_on(crtc); - rockchip_drm_set_win_enabled(crtc, true); - return; - } + int sys_status = drm_crtc_index(crtc) ? + SYS_STATUS_LCDC1 : SYS_STATUS_LCDC0; + uint32_t val; + int act_end; + bool interlaced = !!(adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE); + int for_ddr_freq = 0; + bool dclk_inv, yc_swap = false; + rockchip_set_system_status(sys_status); + vop_lock(vop); + DRM_DEV_INFO(vop->dev, "Update mode to %dx%d%s%d, type: %d\n", + hdisplay, vdisplay, interlaced ? "i" : "p", + drm_mode_vrefresh(adjusted_mode), s->output_type); + vop_initial(crtc); + vop_disable_allwin(vop); + VOP_CTRL_SET(vop, standby, 0); + s->mode_update = vop_crtc_mode_update(crtc); + if (s->mode_update) + vop_disable_all_planes(vop); /* - * If we have a GAMMA LUT in the state, then let's make sure - * it's updated. We might be coming out of suspend, - * which means the LUT internal memory needs to be re-written. + * restore the lut table. */ - if (crtc->state->gamma_lut) - vop_crtc_gamma_set(vop, crtc, old_state); + if (vop->lut_active) + vop_crtc_load_lut(crtc); - mutex_lock(&vop->vop_lock); + if (vop->mcu_timing.mcu_pix_total) + vop_mcu_mode(crtc); - WARN_ON(vop->event); + dclk_inv = (s->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) ? 1 : 0; - ret = vop_enable(crtc, old_state); - if (ret) { - mutex_unlock(&vop->vop_lock); - DRM_DEV_ERROR(vop->dev, "Failed to enable vop (%d)\n", ret); - return; + VOP_CTRL_SET(vop, dclk_pol, dclk_inv); + val = (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? + 0 : BIT(HSYNC_POSITIVE); + val |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? + 0 : BIT(VSYNC_POSITIVE); + VOP_CTRL_SET(vop, pin_pol, val); + + if (vop->dclk_source && vop->pll && vop->pll->pll) { + if (clk_set_parent(vop->dclk_source, vop->pll->pll)) + DRM_DEV_ERROR(vop->dev, + "failed to set dclk's parents\n"); } - pin_pol = (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) ? - BIT(HSYNC_POSITIVE) : 0; - pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) ? - BIT(VSYNC_POSITIVE) : 0; - VOP_REG_SET(vop, output, pin_pol, pin_pol); - VOP_REG_SET(vop, output, mipi_dual_channel_en, 0); switch (s->output_type) { + case DRM_MODE_CONNECTOR_DPI: case DRM_MODE_CONNECTOR_LVDS: - VOP_REG_SET(vop, output, rgb_dclk_pol, 1); - VOP_REG_SET(vop, output, rgb_pin_pol, pin_pol); - VOP_REG_SET(vop, output, rgb_en, 1); + VOP_CTRL_SET(vop, rgb_en, 1); + VOP_CTRL_SET(vop, rgb_pin_pol, val); + VOP_CTRL_SET(vop, rgb_dclk_pol, dclk_inv); + VOP_CTRL_SET(vop, lvds_en, 1); + VOP_CTRL_SET(vop, lvds_pin_pol, val); + VOP_CTRL_SET(vop, lvds_dclk_pol, dclk_inv); + VOP_GRF_SET(vop, grf_dclk_inv, dclk_inv); + if (s->output_if & VOP_OUTPUT_IF_BT1120) { + VOP_CTRL_SET(vop, bt1120_en, 1); + yc_swap = is_yc_swap(s->bus_format); + VOP_CTRL_SET(vop, bt1120_yc_swap, yc_swap); + VOP_CTRL_SET(vop, yuv_clip, 1); + } break; case DRM_MODE_CONNECTOR_eDP: - VOP_REG_SET(vop, output, edp_dclk_pol, 1); - VOP_REG_SET(vop, output, edp_pin_pol, pin_pol); - VOP_REG_SET(vop, output, edp_en, 1); + VOP_CTRL_SET(vop, edp_en, 1); + VOP_CTRL_SET(vop, edp_pin_pol, val); + VOP_CTRL_SET(vop, edp_dclk_pol, dclk_inv); break; case DRM_MODE_CONNECTOR_HDMIA: - VOP_REG_SET(vop, output, hdmi_dclk_pol, 1); - VOP_REG_SET(vop, output, hdmi_pin_pol, pin_pol); - VOP_REG_SET(vop, output, hdmi_en, 1); + VOP_CTRL_SET(vop, hdmi_en, 1); + VOP_CTRL_SET(vop, hdmi_pin_pol, val); + VOP_CTRL_SET(vop, hdmi_dclk_pol, 1); break; case DRM_MODE_CONNECTOR_DSI: - VOP_REG_SET(vop, output, mipi_dclk_pol, 1); - VOP_REG_SET(vop, output, mipi_pin_pol, pin_pol); - VOP_REG_SET(vop, output, mipi_en, 1); - VOP_REG_SET(vop, output, mipi_dual_channel_en, - !!(s->output_flags & ROCKCHIP_OUTPUT_DSI_DUAL)); + VOP_CTRL_SET(vop, mipi_en, 1); + VOP_CTRL_SET(vop, mipi_pin_pol, val); + VOP_CTRL_SET(vop, mipi_dclk_pol, dclk_inv); + VOP_CTRL_SET(vop, mipi_dual_channel_en, + !!(s->output_flags & ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE)); + VOP_CTRL_SET(vop, data01_swap, + !!(s->output_flags & ROCKCHIP_OUTPUT_DATA_SWAP) || + vop->dual_channel_swap); break; case DRM_MODE_CONNECTOR_DisplayPort: - VOP_REG_SET(vop, output, dp_dclk_pol, 0); - VOP_REG_SET(vop, output, dp_pin_pol, pin_pol); - VOP_REG_SET(vop, output, dp_en, 1); + VOP_CTRL_SET(vop, dp_dclk_pol, 0); + VOP_CTRL_SET(vop, dp_pin_pol, val); + VOP_CTRL_SET(vop, dp_en, 1); + break; + case DRM_MODE_CONNECTOR_TV: + if (vdisplay == CVBS_PAL_VDISPLAY) + VOP_CTRL_SET(vop, tve_sw_mode, 1); + else + VOP_CTRL_SET(vop, tve_sw_mode, 0); + + VOP_CTRL_SET(vop, tve_dclk_pol, 1); + VOP_CTRL_SET(vop, tve_dclk_en, 1); + /* use the same pol reg with hdmi */ + VOP_CTRL_SET(vop, hdmi_pin_pol, val); + VOP_CTRL_SET(vop, sw_genlock, 1); + VOP_CTRL_SET(vop, sw_uv_offset_en, 1); + VOP_CTRL_SET(vop, dither_up_en, 1); break; default: - DRM_DEV_ERROR(vop->dev, "unsupported connector_type [%d]\n", - s->output_type); + DRM_ERROR("unsupported connector_type[%d]\n", s->output_type); } - - /* - * if vop is not support RGB10 output, need force RGB10 to RGB888. - */ - if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA && - !(vop_data->feature & VOP_FEATURE_OUTPUT_RGB10)) - s->output_mode = ROCKCHIP_OUT_MODE_P888; - - if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA && dither_bpc <= 8) - VOP_REG_SET(vop, common, pre_dither_down, 1); - else - VOP_REG_SET(vop, common, pre_dither_down, 0); - - if (dither_bpc == 6) { - VOP_REG_SET(vop, common, dither_down_sel, DITHER_DOWN_ALLEGRO); - VOP_REG_SET(vop, common, dither_down_mode, RGB888_TO_RGB666); - VOP_REG_SET(vop, common, dither_down_en, 1); - } else { - VOP_REG_SET(vop, common, dither_down_en, 0); - } - - VOP_REG_SET(vop, common, out_mode, s->output_mode); - - VOP_REG_SET(vop, modeset, htotal_pw, (htotal << 16) | hsync_len); + vop_update_csc(crtc); + VOP_CTRL_SET(vop, htotal_pw, (htotal << 16) | hsync_len); val = hact_st << 16; val |= hact_end; - VOP_REG_SET(vop, modeset, hact_st_end, val); - VOP_REG_SET(vop, modeset, hpost_st_end, val); + VOP_CTRL_SET(vop, hact_st_end, val); + VOP_CTRL_SET(vop, hpost_st_end, val); - VOP_REG_SET(vop, modeset, vtotal_pw, (vtotal << 16) | vsync_len); val = vact_st << 16; val |= vact_end; - VOP_REG_SET(vop, modeset, vact_st_end, val); - VOP_REG_SET(vop, modeset, vpost_st_end, val); + VOP_CTRL_SET(vop, vact_st_end, val); + VOP_CTRL_SET(vop, vpost_st_end, val); - VOP_REG_SET(vop, intr, line_flag_num[0], vact_end); + if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { + u16 vact_st_f1 = vtotal + vact_st + 1; + u16 vact_end_f1 = vact_st_f1 + vdisplay; - clk_set_rate(vop->dclk, adjusted_mode->clock * 1000); + val = vact_st_f1 << 16 | vact_end_f1; + VOP_CTRL_SET(vop, vact_st_end_f1, val); + VOP_CTRL_SET(vop, vpost_st_end_f1, val); - VOP_REG_SET(vop, common, standby, 0); - mutex_unlock(&vop->vop_lock); + val = vtotal << 16 | (vtotal + vsync_len); + VOP_CTRL_SET(vop, vs_st_end_f1, val); + VOP_CTRL_SET(vop, dsp_interlace, 1); + VOP_CTRL_SET(vop, p2i_en, 1); + vtotal += vtotal + 1; + act_end = vact_end_f1; + } else { + VOP_CTRL_SET(vop, dsp_interlace, 0); + VOP_CTRL_SET(vop, p2i_en, 0); + act_end = vact_end; + } + + if (VOP_MAJOR(vop->version) == 3 && + (VOP_MINOR(vop->version) == 2 || VOP_MINOR(vop->version) == 8)) + for_ddr_freq = 1000; + VOP_INTR_SET(vop, line_flag_num[0], act_end); + VOP_INTR_SET(vop, line_flag_num[1], + act_end - us_to_vertical_line(adjusted_mode, for_ddr_freq)); + + VOP_CTRL_SET(vop, vtotal_pw, vtotal << 16 | vsync_len); + + VOP_CTRL_SET(vop, core_dclk_div, + !!(adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK)); + + VOP_CTRL_SET(vop, win_csc_mode_sel, 1); + + clk_set_rate(vop->dclk, adjusted_mode->crtc_clock * 1000); + + + vop_cfg_done(vop); + + drm_crtc_vblank_on(crtc); + vop_unlock(vop); +} + +static int vop_zpos_cmp(const void *a, const void *b) +{ + struct vop_zpos *pa = (struct vop_zpos *)a; + struct vop_zpos *pb = (struct vop_zpos *)b; + + return pa->zpos - pb->zpos; +} + +static int vop_afbdc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *crtc_state) +{ + struct vop *vop = to_vop(crtc); + const struct vop_data *vop_data = vop->data; + struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); + struct drm_atomic_state *state = crtc_state->state; + struct drm_plane *plane; + struct drm_plane_state *pstate; + struct vop_plane_state *plane_state; + struct drm_framebuffer *fb; + struct drm_rect *src; + struct vop_win *win; + int afbdc_format; + + s->afbdc_en = 0; + + drm_atomic_crtc_state_for_each_plane(plane, crtc_state) { + pstate = drm_atomic_get_existing_plane_state(state, plane); + /* + * plane might not have changed, in which case take + * current state: + */ + if (!pstate) + pstate = plane->state; + + fb = pstate->fb; + + if (pstate->crtc != crtc || !fb) + continue; + if (fb->modifier != + DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16)) + continue; + + if (!(vop_data->feature & VOP_FEATURE_AFBDC)) { + DRM_ERROR("not support afbdc\n"); + return -EINVAL; + } + + plane_state = to_vop_plane_state(pstate); + + switch (plane_state->format) { + case VOP_FMT_ARGB8888: + afbdc_format = AFBDC_FMT_U8U8U8U8; + break; + case VOP_FMT_RGB888: + afbdc_format = AFBDC_FMT_U8U8U8; + break; + case VOP_FMT_RGB565: + afbdc_format = AFBDC_FMT_RGB565; + break; + default: + return -EINVAL; + } + + if (s->afbdc_en) { + DRM_ERROR("vop only support one afbc layer\n"); + return -EINVAL; + } + + win = to_vop_win(plane); + src = &plane_state->src; + if (!(win->feature & WIN_FEATURE_AFBDC)) { + DRM_ERROR("win[%d] feature:0x%llx, not support afbdc\n", + win->win_id, win->feature); + return -EINVAL; + } + if (!IS_ALIGNED(fb->width, 16)) { + DRM_ERROR("win[%d] afbdc must 16 align, width: %d\n", + win->win_id, fb->width); + return -EINVAL; + } + + if (VOP_CTRL_SUPPORT(vop, afbdc_pic_vir_width)) { + u32 align_x1, align_x2, align_y1, align_y2, align_val; + struct drm_gem_object *obj; + struct rockchip_gem_object *rk_obj; + dma_addr_t fb_addr; + + obj = fb->obj[0]; + rk_obj = to_rockchip_obj(obj); + fb_addr = rk_obj->dma_addr + fb->offsets[0]; + + s->afbdc_win_format = afbdc_format; + s->afbdc_win_id = win->win_id; + s->afbdc_win_ptr = fb_addr; + s->afbdc_win_vir_width = fb->width; + s->afbdc_win_xoffset = (src->x1 >> 16); + s->afbdc_win_yoffset = (src->y1 >> 16); + + align_x1 = (src->x1 >> 16) - ((src->x1 >> 16) % 16); + align_y1 = (src->y1 >> 16) - ((src->y1 >> 16) % 16); + + align_val = (src->x2 >> 16) % 16; + if (align_val) + align_x2 = (src->x2 >> 16) + (16 - align_val); + else + align_x2 = src->x2 >> 16; + + align_val = (src->y2 >> 16) % 16; + if (align_val) + align_y2 = (src->y2 >> 16) + (16 - align_val); + else + align_y2 = src->y2 >> 16; + + s->afbdc_win_width = align_x2 - align_x1 - 1; + s->afbdc_win_height = align_y2 - align_y1 - 1; + + s->afbdc_en = 1; + + break; + } + if (src->x1 || src->y1 || fb->offsets[0]) { + DRM_ERROR("win[%d] afbdc not support offset display\n", + win->win_id); + DRM_ERROR("xpos=%d, ypos=%d, offset=%d\n", + src->x1, src->y1, fb->offsets[0]); + return -EINVAL; + } + s->afbdc_win_format = afbdc_format; + s->afbdc_win_width = fb->width - 1; + s->afbdc_win_height = (drm_rect_height(src) >> 16) - 1; + s->afbdc_win_id = win->win_id; + s->afbdc_win_ptr = plane_state->yrgb_mst; + s->afbdc_en = 1; + } + + return 0; +} + +static void vop_dclk_source_generate(struct drm_crtc *crtc, + struct drm_crtc_state *crtc_state) +{ + struct rockchip_drm_private *private = crtc->dev->dev_private; + struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); + struct rockchip_crtc_state *old_s = to_rockchip_crtc_state(crtc->state); + struct vop *vop = to_vop(crtc); + struct rockchip_dclk_pll *old_pll = vop->pll; + + if (!vop->dclk_source) + return; + + if (crtc_state->active) { + WARN_ON(vop->pll && !vop->pll->use_count); + if (!vop->pll || vop->pll->use_count > 1 || + s->output_type != old_s->output_type) { + if (vop->pll) + vop->pll->use_count--; + + if (s->output_type != DRM_MODE_CONNECTOR_HDMIA && + !private->default_pll.use_count) + vop->pll = &private->default_pll; + else + vop->pll = &private->hdmi_pll; + + vop->pll->use_count++; + } + } else if (vop->pll) { + vop->pll->use_count--; + vop->pll = NULL; + } + if (vop->pll != old_pll) + crtc_state->mode_changed = true; +} + +static int vop_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *crtc_state) +{ + struct drm_atomic_state *state = crtc_state->state; + struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); + struct vop *vop = to_vop(crtc); + const struct vop_data *vop_data = vop->data; + struct drm_plane *plane; + struct drm_plane_state *pstate; + struct vop_plane_state *plane_state; + struct vop_zpos *pzpos; + int dsp_layer_sel = 0; + int i, j, cnt = 0, ret = 0; + + ret = vop_afbdc_atomic_check(crtc, crtc_state); + if (ret) + return ret; + + s->yuv_overlay = 0; + if (VOP_CTRL_SUPPORT(vop, overlay_mode)) + s->yuv_overlay = is_yuv_output(s->bus_format); + + ret = vop_hdr_atomic_check(crtc, crtc_state); + if (ret) + return ret; + ret = vop_csc_atomic_check(crtc, crtc_state); + if (ret) + return ret; + + pzpos = kmalloc_array(vop_data->win_size, sizeof(*pzpos), GFP_KERNEL); + if (!pzpos) + return -ENOMEM; + + for (i = 0; i < vop_data->win_size; i++) { + const struct vop_win_data *win_data = &vop_data->win[i]; + struct vop_win *win; + + if (!win_data->phy) + continue; + + for (j = 0; j < vop->num_wins; j++) { + win = &vop->win[j]; + + if (win->win_id == i && !win->area_id) + break; + } + if (WARN_ON(j >= vop->num_wins)) { + ret = -EINVAL; + goto err_free_pzpos; + } + + plane = &win->base; + pstate = state->planes[drm_plane_index(plane)].state; + /* + * plane might not have changed, in which case take + * current state: + */ + if (!pstate) + pstate = plane->state; + plane_state = to_vop_plane_state(pstate); + + if (!pstate->visible) + pzpos[cnt].zpos = INT_MAX; + else + pzpos[cnt].zpos = plane_state->zpos; + pzpos[cnt++].win_id = win->win_id; + } + + sort(pzpos, cnt, sizeof(pzpos[0]), vop_zpos_cmp, NULL); + + for (i = 0, cnt = 0; i < vop_data->win_size; i++) { + const struct vop_win_data *win_data = &vop_data->win[i]; + int shift = i * 2; + + if (win_data->phy) { + struct vop_zpos *zpos = &pzpos[cnt++]; + + dsp_layer_sel |= zpos->win_id << shift; + } else { + dsp_layer_sel |= i << shift; + } + } + + s->dsp_layer_sel = dsp_layer_sel; + + vop_dclk_source_generate(crtc, crtc_state); + +err_free_pzpos: + kfree(pzpos); + return ret; +} + +static void vop_post_config(struct drm_crtc *crtc) +{ + struct vop *vop = to_vop(crtc); + struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state); + struct drm_display_mode *mode = &crtc->state->adjusted_mode; + u16 vtotal = mode->crtc_vtotal; + u16 hdisplay = mode->crtc_hdisplay; + u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start; + u16 vdisplay = mode->crtc_vdisplay; + u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start; + u16 hsize = hdisplay * (s->left_margin + s->right_margin) / 200; + u16 vsize = vdisplay * (s->top_margin + s->bottom_margin) / 200; + u16 hact_end, vact_end; + u32 val; + + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + vsize = rounddown(vsize, 2); + + hact_st += hdisplay * (100 - s->left_margin) / 200; + hact_end = hact_st + hsize; + val = hact_st << 16; + val |= hact_end; + VOP_CTRL_SET(vop, hpost_st_end, val); + vact_st += vdisplay * (100 - s->top_margin) / 200; + vact_end = vact_st + vsize; + val = vact_st << 16; + val |= vact_end; + VOP_CTRL_SET(vop, vpost_st_end, val); + val = scl_cal_scale2(vdisplay, vsize) << 16; + val |= scl_cal_scale2(hdisplay, hsize); + VOP_CTRL_SET(vop, post_scl_factor, val); + +#define POST_HORIZONTAL_SCALEDOWN_EN(x) ((x) << 0) +#define POST_VERTICAL_SCALEDOWN_EN(x) ((x) << 1) + VOP_CTRL_SET(vop, post_scl_ctrl, + POST_HORIZONTAL_SCALEDOWN_EN(hdisplay != hsize) | + POST_VERTICAL_SCALEDOWN_EN(vdisplay != vsize)); + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + u16 vact_st_f1 = vtotal + vact_st + 1; + u16 vact_end_f1 = vact_st_f1 + vsize; + + val = vact_st_f1 << 16 | vact_end_f1; + VOP_CTRL_SET(vop, vpost_st_end_f1, val); + } +} + +static void vop_update_hdr(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct rockchip_crtc_state *s = + to_rockchip_crtc_state(crtc->state); + struct vop *vop = to_vop(crtc); + struct rockchip_sdr2hdr_state *sdr2hdr_state = &s->hdr.sdr2hdr_state; + + if (!vop->data->hdr_table) + return; + + if (s->hdr.hdr2sdr_en) { + vop_load_hdr2sdr_table(vop); + /* This is ic design bug, when in hdr2sdr mode, the overlay mode + * is rgb domain, so the win0 is do yuv2rgb, but in this case, + * we must close win0 y2r. + */ + VOP_CTRL_SET(vop, hdr2sdr_en_win0_csc, 0); + } + VOP_CTRL_SET(vop, hdr2sdr_en, s->hdr.hdr2sdr_en); + + VOP_CTRL_SET(vop, bt1886eotf_pre_conv_en, + sdr2hdr_state->bt1886eotf_pre_conv_en); + VOP_CTRL_SET(vop, bt1886eotf_post_conv_en, + sdr2hdr_state->bt1886eotf_post_conv_en); + + VOP_CTRL_SET(vop, rgb2rgb_pre_conv_en, + sdr2hdr_state->rgb2rgb_pre_conv_en); + VOP_CTRL_SET(vop, rgb2rgb_pre_conv_mode, + sdr2hdr_state->rgb2rgb_pre_conv_mode); + VOP_CTRL_SET(vop, st2084oetf_pre_conv_en, + sdr2hdr_state->st2084oetf_pre_conv_en); + + VOP_CTRL_SET(vop, rgb2rgb_post_conv_en, + sdr2hdr_state->rgb2rgb_post_conv_en); + VOP_CTRL_SET(vop, rgb2rgb_post_conv_mode, + sdr2hdr_state->rgb2rgb_post_conv_mode); + VOP_CTRL_SET(vop, st2084oetf_post_conv_en, + sdr2hdr_state->st2084oetf_post_conv_en); + + if (sdr2hdr_state->bt1886eotf_pre_conv_en || + sdr2hdr_state->bt1886eotf_post_conv_en) + vop_load_sdr2hdr_table(vop, sdr2hdr_state->sdr2hdr_func); + VOP_CTRL_SET(vop, win_csc_mode_sel, 1); +} + +static void vop_tv_config_update(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct rockchip_crtc_state *s = + to_rockchip_crtc_state(crtc->state); + struct rockchip_crtc_state *old_s = + to_rockchip_crtc_state(old_crtc_state); + int brightness, contrast, saturation, hue, sin_hue, cos_hue; + struct vop *vop = to_vop(crtc); + const struct vop_data *vop_data = vop->data; + + if (!s->tv_state) + return; + + /* + * The BCSH only need to config once except one of the following + * condition changed: + * 1. tv_state: include brightness,contrast,saturation and hue; + * 2. yuv_overlay: it is related to BCSH r2y module; + * 3. mode_update: it is indicate mode change and resume from suspend; + * 4. bcsh_en: control the BCSH module enable or disable state; + * 5. bus_format: it is related to BCSH y2r module; + */ + if (!memcmp(s->tv_state, + &vop->active_tv_state, sizeof(*s->tv_state)) && + s->yuv_overlay == old_s->yuv_overlay && s->mode_update && + s->bcsh_en == old_s->bcsh_en && s->bus_format == old_s->bus_format) + return; + + memcpy(&vop->active_tv_state, s->tv_state, sizeof(*s->tv_state)); + /* post BCSH CSC */ + s->post_r2y_en = 0; + s->post_y2r_en = 0; + s->bcsh_en = 0; + if (s->tv_state) { + if (s->tv_state->brightness != 50 || + s->tv_state->contrast != 50 || + s->tv_state->saturation != 50 || s->tv_state->hue != 50) + s->bcsh_en = 1; + } + + if (s->bcsh_en) { + if (!s->yuv_overlay) + s->post_r2y_en = 1; + if (!is_yuv_output(s->bus_format)) + s->post_y2r_en = 1; + } else { + if (!s->yuv_overlay && is_yuv_output(s->bus_format)) + s->post_r2y_en = 1; + if (s->yuv_overlay && !is_yuv_output(s->bus_format)) + s->post_y2r_en = 1; + } + + s->post_csc_mode = to_vop_csc_mode(s->color_space); + VOP_CTRL_SET(vop, bcsh_r2y_en, s->post_r2y_en); + VOP_CTRL_SET(vop, bcsh_y2r_en, s->post_y2r_en); + VOP_CTRL_SET(vop, bcsh_r2y_csc_mode, s->post_csc_mode); + VOP_CTRL_SET(vop, bcsh_y2r_csc_mode, s->post_csc_mode); + if (!s->bcsh_en) { + VOP_CTRL_SET(vop, bcsh_en, s->bcsh_en); + return; + } + + if (vop_data->feature & VOP_FEATURE_OUTPUT_10BIT) + brightness = interpolate(0, -128, 100, 127, s->tv_state->brightness); + else if (VOP_MAJOR(vop->version) == 2 && VOP_MINOR(vop->version) == 6) /* px30 vopb */ + brightness = interpolate(0, -64, 100, 63, s->tv_state->brightness); + else + brightness = interpolate(0, -32, 100, 31, s->tv_state->brightness); + + if ((VOP_MAJOR(vop->version) == 3) || + (VOP_MAJOR(vop->version) == 2 && VOP_MINOR(vop->version) == 6)) { /* px30 vopb */ + contrast = interpolate(0, 0, 100, 511, s->tv_state->contrast); + saturation = interpolate(0, 0, 100, 511, s->tv_state->saturation); + /* + * a:[-30~0]: + * sin_hue = 0x100 - sin(a)*256; + * cos_hue = cos(a)*256; + * a:[0~30] + * sin_hue = sin(a)*256; + * cos_hue = cos(a)*256; + */ + hue = interpolate(0, -30, 100, 30, s->tv_state->hue); + sin_hue = fixp_sin32(hue) >> 23; + cos_hue = fixp_cos32(hue) >> 23; + VOP_CTRL_SET(vop, bcsh_sat_con, saturation * contrast / 0x100); + + } else { + contrast = interpolate(0, 0, 100, 255, s->tv_state->contrast); + saturation = interpolate(0, 0, 100, 255, s->tv_state->saturation); + /* + * a:[-30~0]: + * sin_hue = 0x100 - sin(a)*128; + * cos_hue = cos(a)*128; + * a:[0~30] + * sin_hue = sin(a)*128; + * cos_hue = cos(a)*128; + */ + hue = interpolate(0, -30, 100, 30, s->tv_state->hue); + sin_hue = fixp_sin32(hue) >> 24; + cos_hue = fixp_cos32(hue) >> 24; + VOP_CTRL_SET(vop, bcsh_sat_con, saturation * contrast / 0x80); + } + + VOP_CTRL_SET(vop, bcsh_brightness, brightness); + VOP_CTRL_SET(vop, bcsh_contrast, contrast); + VOP_CTRL_SET(vop, bcsh_sin_hue, sin_hue); + VOP_CTRL_SET(vop, bcsh_cos_hue, cos_hue); + VOP_CTRL_SET(vop, bcsh_out_mode, BCSH_OUT_MODE_NORMAL_VIDEO); + if (VOP_MAJOR(vop->version) == 3 && VOP_MINOR(vop->version) == 0) + VOP_CTRL_SET(vop, auto_gate_en, 0); + VOP_CTRL_SET(vop, bcsh_en, s->bcsh_en); +} + +static void vop_cfg_update(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct rockchip_crtc_state *s = + to_rockchip_crtc_state(crtc->state); + struct vop *vop = to_vop(crtc); + + spin_lock(&vop->reg_lock); + + vop_update_csc(crtc); + + vop_tv_config_update(crtc, old_crtc_state); + + if (s->afbdc_en) { + u32 pic_size, pic_offset; + + VOP_CTRL_SET(vop, afbdc_format, s->afbdc_win_format | 1 << 4); + VOP_CTRL_SET(vop, afbdc_hreg_block_split, 0); + VOP_CTRL_SET(vop, afbdc_sel, s->afbdc_win_id); + VOP_CTRL_SET(vop, afbdc_hdr_ptr, s->afbdc_win_ptr); + pic_size = (s->afbdc_win_width & 0xffff); + pic_size |= s->afbdc_win_height << 16; + VOP_CTRL_SET(vop, afbdc_pic_size, pic_size); + + VOP_CTRL_SET(vop, afbdc_pic_vir_width, s->afbdc_win_vir_width); + pic_offset = (s->afbdc_win_xoffset & 0xffff); + pic_offset |= s->afbdc_win_yoffset << 16; + VOP_CTRL_SET(vop, afbdc_pic_offset, pic_offset); + } + + VOP_CTRL_SET(vop, afbdc_en, s->afbdc_en); + + VOP_CTRL_SET(vop, dsp_layer_sel, s->dsp_layer_sel); + vop_post_config(crtc); + + spin_unlock(&vop->reg_lock); } static bool vop_fs_irq_is_pending(struct vop *vop) { - return VOP_INTR_GET_TYPE(vop, status, FS_INTR); + if (VOP_MAJOR(vop->version) == 3 && VOP_MINOR(vop->version) >= 7) + return VOP_INTR_GET_TYPE(vop, status, FS_FIELD_INTR); + else + return VOP_INTR_GET_TYPE(vop, status, FS_INTR); } static void vop_wait_for_irq_handler(struct vop *vop) @@ -1412,72 +3705,66 @@ static void vop_wait_for_irq_handler(struct vop *vop) synchronize_irq(vop->irq); } -static int vop_crtc_atomic_check(struct drm_crtc *crtc, - struct drm_crtc_state *crtc_state) -{ - struct vop *vop = to_vop(crtc); - struct drm_plane *plane; - struct drm_plane_state *plane_state; - struct rockchip_crtc_state *s; - int afbc_planes = 0; - - if (vop->lut_regs && crtc_state->color_mgmt_changed && - crtc_state->gamma_lut) { - unsigned int len; - - len = drm_color_lut_size(crtc_state->gamma_lut); - if (len != crtc->gamma_size) { - DRM_DEBUG_KMS("Invalid LUT size; got %d, expected %d\n", - len, crtc->gamma_size); - return -EINVAL; - } - } - - drm_atomic_crtc_state_for_each_plane(plane, crtc_state) { - plane_state = - drm_atomic_get_plane_state(crtc_state->state, plane); - if (IS_ERR(plane_state)) { - DRM_DEBUG_KMS("Cannot get plane state for plane %s\n", - plane->name); - return PTR_ERR(plane_state); - } - - if (drm_is_afbc(plane_state->fb->modifier)) - ++afbc_planes; - } - - if (afbc_planes > 1) { - DRM_DEBUG_KMS("Invalid number of AFBC planes; got %d, expected at most 1\n", afbc_planes); - return -EINVAL; - } - - s = to_rockchip_crtc_state(crtc_state); - s->enable_afbc = afbc_planes > 0; - - return 0; -} - static void vop_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { struct drm_atomic_state *old_state = old_crtc_state->state; - struct drm_plane_state *old_plane_state, *new_plane_state; + struct drm_plane_state *old_plane_state; struct vop *vop = to_vop(crtc); struct drm_plane *plane; - struct rockchip_crtc_state *s; int i; + unsigned long flags; + struct rockchip_crtc_state *s = + to_rockchip_crtc_state(crtc->state); - if (WARN_ON(!vop->is_enabled)) - return; + vop_cfg_update(crtc, old_crtc_state); - spin_lock(&vop->reg_lock); + if (!vop->is_iommu_enabled && vop->is_iommu_needed) { + int ret; - /* Enable AFBC if there is some AFBC window, disable otherwise. */ - s = to_rockchip_crtc_state(crtc->state); - VOP_AFBC_SET(vop, enable, s->enable_afbc); + if (s->mode_update) + VOP_CTRL_SET(vop, dma_stop, 1); + + ret = rockchip_drm_dma_attach_device(vop->drm_dev, vop->dev); + if (ret) { + vop->is_iommu_enabled = false; + vop_disable_all_planes(vop); + dev_err(vop->dev, "failed to attach dma mapping, %d\n", + ret); + } else { + vop->is_iommu_enabled = true; + VOP_CTRL_SET(vop, dma_stop, 0); + } + } + + vop_update_hdr(crtc, old_crtc_state); + if (old_crtc_state->color_mgmt_changed || old_crtc_state->active_changed) { + if (crtc->state->gamma_lut || vop->gamma_lut) { + if (old_crtc_state->gamma_lut) + vop->gamma_lut = old_crtc_state->gamma_lut->data; + vop_crtc_atomic_gamma_set(crtc, old_crtc_state); + } + } + + spin_lock_irqsave(&vop->irq_lock, flags); + vop->pre_overlay = s->hdr.pre_overlay; vop_cfg_done(vop); + /* + * rk322x and rk332x odd-even field will mistake when in interlace mode. + * we must switch to frame effect before switch screen and switch to + * field effect after switch screen complete. + */ + if (VOP_MAJOR(vop->version) == 3 && + (VOP_MINOR(vop->version) == 7 || VOP_MINOR(vop->version) == 8)) { + if (!s->mode_update && VOP_CTRL_GET(vop, reg_done_frm)) + VOP_CTRL_SET(vop, reg_done_frm, 0); + } else { + VOP_CTRL_SET(vop, reg_done_frm, 0); + } + if (vop->mcu_timing.mcu_pix_total) + VOP_CTRL_SET(vop, mcu_hold_mode, 0); - spin_unlock(&vop->reg_lock); + spin_unlock_irqrestore(&vop->irq_lock, flags); /* * There is a (rather unlikely) possiblity that a vblank interrupt @@ -1495,13 +3782,11 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc, crtc->state->event = NULL; } spin_unlock_irq(&crtc->dev->event_lock); - - for_each_oldnew_plane_in_state(old_state, plane, old_plane_state, - new_plane_state, i) { + for_each_old_plane_in_state(old_state, plane, old_plane_state, i) { if (!old_plane_state->fb) continue; - if (old_plane_state->fb == new_plane_state->fb) + if (old_plane_state->fb == plane->state->fb) continue; drm_framebuffer_get(old_plane_state->fb); @@ -1514,7 +3799,6 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc, static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = { .mode_fixup = vop_crtc_mode_fixup, .atomic_check = vop_crtc_atomic_check, - .atomic_begin = vop_crtc_atomic_begin, .atomic_flush = vop_crtc_atomic_flush, .atomic_enable = vop_crtc_atomic_enable, .atomic_disable = vop_crtc_atomic_disable, @@ -1525,11 +3809,33 @@ static void vop_crtc_destroy(struct drm_crtc *crtc) drm_crtc_cleanup(crtc); } +static void vop_crtc_reset(struct drm_crtc *crtc) +{ + struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state); + + if (crtc->state) { + __drm_atomic_helper_crtc_destroy_state(crtc->state); + kfree(s); + } + + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (!s) + return; + crtc->state = &s->base; + crtc->state->crtc = crtc; + + s->left_margin = 100; + s->right_margin = 100; + s->top_margin = 100; + s->bottom_margin = 100; +} + static struct drm_crtc_state *vop_crtc_duplicate_state(struct drm_crtc *crtc) { - struct rockchip_crtc_state *rockchip_state; + struct rockchip_crtc_state *rockchip_state, *old_state; - rockchip_state = kzalloc(sizeof(*rockchip_state), GFP_KERNEL); + old_state = to_rockchip_crtc_state(crtc->state); + rockchip_state = kmemdup(old_state, sizeof(*old_state), GFP_KERNEL); if (!rockchip_state) return NULL; @@ -1546,17 +3852,6 @@ static void vop_crtc_destroy_state(struct drm_crtc *crtc, kfree(s); } -static void vop_crtc_reset(struct drm_crtc *crtc) -{ - struct rockchip_crtc_state *crtc_state = - kzalloc(sizeof(*crtc_state), GFP_KERNEL); - - if (crtc->state) - vop_crtc_destroy_state(crtc, crtc->state); - - __drm_atomic_helper_crtc_reset(crtc, &crtc_state->base); -} - #ifdef CONFIG_DRM_ANALOGIX_DP static struct drm_connector *vop_get_edp_connector(struct vop *vop) { @@ -1622,18 +3917,94 @@ vop_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name, } #endif +static int vop_crtc_atomic_get_property(struct drm_crtc *crtc, + const struct drm_crtc_state *state, + struct drm_property *property, + uint64_t *val) +{ + struct drm_device *drm_dev = crtc->dev; + struct rockchip_drm_private *private = drm_dev->dev_private; + struct drm_mode_config *mode_config = &drm_dev->mode_config; + struct rockchip_crtc_state *s = to_rockchip_crtc_state(state); + struct vop *vop = to_vop(crtc); + + if (property == mode_config->tv_left_margin_property) { + *val = s->left_margin; + return 0; + } + + if (property == mode_config->tv_right_margin_property) { + *val = s->right_margin; + return 0; + } + + if (property == mode_config->tv_top_margin_property) { + *val = s->top_margin; + return 0; + } + + if (property == mode_config->tv_bottom_margin_property) { + *val = s->bottom_margin; + return 0; + } + + if (property == private->alpha_scale_prop) { + *val = (vop->data->feature & VOP_FEATURE_ALPHA_SCALE) ? 1 : 0; + return 0; + } + + DRM_ERROR("failed to get vop crtc property\n"); + return -EINVAL; +} + +static int vop_crtc_atomic_set_property(struct drm_crtc *crtc, + struct drm_crtc_state *state, + struct drm_property *property, + uint64_t val) +{ + struct drm_device *drm_dev = crtc->dev; + struct drm_mode_config *mode_config = &drm_dev->mode_config; + struct rockchip_crtc_state *s = to_rockchip_crtc_state(state); + //struct vop *vop = to_vop(crtc); + + if (property == mode_config->tv_left_margin_property) { + s->left_margin = val; + return 0; + } + + if (property == mode_config->tv_right_margin_property) { + s->right_margin = val; + return 0; + } + + if (property == mode_config->tv_top_margin_property) { + s->top_margin = val; + return 0; + } + + if (property == mode_config->tv_bottom_margin_property) { + s->bottom_margin = val; + return 0; + } + + DRM_ERROR("failed to set vop crtc property\n"); + return -EINVAL; +} + static const struct drm_crtc_funcs vop_crtc_funcs = { + .gamma_set = vop_crtc_legacy_gamma_set, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, .destroy = vop_crtc_destroy, .reset = vop_crtc_reset, + .atomic_get_property = vop_crtc_atomic_get_property, + .atomic_set_property = vop_crtc_atomic_set_property, .atomic_duplicate_state = vop_crtc_duplicate_state, .atomic_destroy_state = vop_crtc_destroy_state, .enable_vblank = vop_crtc_enable_vblank, .disable_vblank = vop_crtc_disable_vblank, .set_crc_source = vop_crtc_set_crc_source, .verify_crc_source = vop_crtc_verify_crc_source, - .gamma_set = drm_atomic_helper_legacy_gamma_set, }; static void vop_fb_unref_worker(struct drm_flip_work *work, void *val) @@ -1649,14 +4020,15 @@ static void vop_handle_vblank(struct vop *vop) { struct drm_device *drm = vop->drm_dev; struct drm_crtc *crtc = &vop->crtc; + unsigned long flags; - spin_lock(&drm->event_lock); + spin_lock_irqsave(&drm->event_lock, flags); if (vop->event) { drm_crtc_send_vblank_event(crtc, vop->event); drm_crtc_vblank_put(crtc); vop->event = NULL; } - spin_unlock(&drm->event_lock); + spin_unlock_irqrestore(&drm->event_lock, flags); if (test_and_clear_bit(VOP_PENDING_FB_UNREF, &vop->pending)) drm_flip_work_commit(&vop->fb_unref_work, system_unbound_wq); @@ -1667,6 +4039,7 @@ static irqreturn_t vop_isr(int irq, void *data) struct vop *vop = data; struct drm_crtc *crtc = &vop->crtc; uint32_t active_irqs; + unsigned long flags; int ret = IRQ_NONE; /* @@ -1685,14 +4058,14 @@ static irqreturn_t vop_isr(int irq, void *data) * interrupt register has interrupt status, enable and clear bits, we * must hold irq_lock to avoid a race with enable/disable_vblank(). */ - spin_lock(&vop->irq_lock); + spin_lock_irqsave(&vop->irq_lock, flags); active_irqs = VOP_INTR_GET_TYPE(vop, status, INTR_MASK); /* Clear all active interrupt sources */ if (active_irqs) VOP_INTR_SET_TYPE(vop, clear, active_irqs, 1); - spin_unlock(&vop->irq_lock); + spin_unlock_irqrestore(&vop->irq_lock, flags); /* This is expected for vop iommu irqs, since the irq is shared */ if (!active_irqs) @@ -1710,17 +4083,41 @@ static irqreturn_t vop_isr(int irq, void *data) ret = IRQ_HANDLED; } - if (active_irqs & FS_INTR) { + if ((active_irqs & FS_INTR) || (active_irqs & FS_FIELD_INTR)) { + /* This is IC design not reasonable, this two register bit need + * frame effective, but actually it's effective immediately, so + * we config this register at frame start. + */ + spin_lock_irqsave(&vop->irq_lock, flags); + VOP_CTRL_SET(vop, level2_overlay_en, vop->pre_overlay); + VOP_CTRL_SET(vop, alpha_hard_calc, vop->pre_overlay); + spin_unlock_irqrestore(&vop->irq_lock, flags); drm_crtc_handle_vblank(crtc); vop_handle_vblank(vop); - active_irqs &= ~FS_INTR; + active_irqs &= ~(FS_INTR | FS_FIELD_INTR); ret = IRQ_HANDLED; } +#define ERROR_HANDLER(x) \ + do { \ + if (active_irqs & x##_INTR) {\ + DRM_DEV_ERROR_RATELIMITED(vop->dev, #x " irq err\n"); \ + active_irqs &= ~x##_INTR; \ + ret = IRQ_HANDLED; \ + } \ + } while (0) + + ERROR_HANDLER(BUS_ERROR); + ERROR_HANDLER(WIN0_EMPTY); + ERROR_HANDLER(WIN1_EMPTY); + ERROR_HANDLER(WIN2_EMPTY); + ERROR_HANDLER(WIN3_EMPTY); + ERROR_HANDLER(HWC_EMPTY); + ERROR_HANDLER(POST_BUF_EMPTY); + /* Unhandled irqs are spurious. */ if (active_irqs) - DRM_DEV_ERROR(vop->dev, "Unknown VOP IRQs: %#02x\n", - active_irqs); + DRM_ERROR("Unknown VOP IRQs: %#02x\n", active_irqs); out_disable: vop_core_clks_disable(vop); @@ -1729,27 +4126,139 @@ out: return ret; } -static void vop_plane_add_properties(struct drm_plane *plane, - const struct vop_win_data *win_data) +static void vop_plane_add_properties(struct vop *vop, + struct drm_plane *plane, + const struct vop_win *win) { unsigned int flags = 0; - flags |= VOP_WIN_HAS_REG(win_data, x_mir_en) ? DRM_MODE_REFLECT_X : 0; - flags |= VOP_WIN_HAS_REG(win_data, y_mir_en) ? DRM_MODE_REFLECT_Y : 0; + flags |= (VOP_WIN_SUPPORT(vop, win, xmirror)) ? DRM_MODE_REFLECT_X : 0; + flags |= (VOP_WIN_SUPPORT(vop, win, ymirror)) ? DRM_MODE_REFLECT_Y : 0; if (flags) drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0, DRM_MODE_ROTATE_0 | flags); } +static int vop_plane_init(struct vop *vop, struct vop_win *win, + unsigned long possible_crtcs) +{ + struct rockchip_drm_private *private = vop->drm_dev->dev_private; + uint64_t feature = 0; + int ret; + + ret = drm_universal_plane_init(vop->drm_dev, &win->base, possible_crtcs, &vop_plane_funcs, + win->data_formats, win->nformats, NULL, win->type, NULL); + if (ret) { + DRM_ERROR("failed to initialize plane %d\n", ret); + return ret; + } + drm_plane_helper_add(&win->base, &plane_helper_funcs); + drm_object_attach_property(&win->base.base, + vop->plane_zpos_prop, win->win_id); + + if (win->phy->scl) + feature |= BIT(ROCKCHIP_DRM_PLANE_FEATURE_SCALE); + if (VOP_WIN_SUPPORT(vop, win, src_alpha_ctl) || + VOP_WIN_SUPPORT(vop, win, alpha_en)) + feature |= BIT(ROCKCHIP_DRM_PLANE_FEATURE_ALPHA); + if (win->feature & WIN_FEATURE_HDR2SDR) + feature |= BIT(ROCKCHIP_DRM_PLANE_FEATURE_HDR2SDR); + if (win->feature & WIN_FEATURE_SDR2HDR) + feature |= BIT(ROCKCHIP_DRM_PLANE_FEATURE_SDR2HDR); + if (win->feature & WIN_FEATURE_AFBDC) + feature |= BIT(ROCKCHIP_DRM_PLANE_FEATURE_AFBDC); + + drm_object_attach_property(&win->base.base, vop->plane_feature_prop, + feature); + drm_object_attach_property(&win->base.base, private->eotf_prop, 0); + drm_object_attach_property(&win->base.base, + private->color_space_prop, 0); + if (VOP_WIN_SUPPORT(vop, win, global_alpha_val)) + drm_object_attach_property(&win->base.base, + private->global_alpha_prop, 0xff); + drm_object_attach_property(&win->base.base, + private->blend_mode_prop, 0); + drm_object_attach_property(&win->base.base, + private->async_commit_prop, 0); + + if (win->parent) + drm_object_attach_property(&win->base.base, private->share_id_prop, + win->parent->base.base.id); + else + drm_object_attach_property(&win->base.base, private->share_id_prop, + win->base.base.id); + + return 0; +} + +static int vop_of_init_display_lut(struct vop *vop) +{ + struct device_node *node = vop->dev->of_node; + struct device_node *dsp_lut; + u32 lut_len = vop->lut_len; + struct property *prop; + int length, i, j; + int ret; + + if (!vop->lut) + return -ENOMEM; + + dsp_lut = of_parse_phandle(node, "dsp-lut", 0); + if (!dsp_lut) + return -ENXIO; + + prop = of_find_property(dsp_lut, "gamma-lut", &length); + if (!prop) { + dev_err(vop->dev, "failed to find gamma_lut\n"); + return -ENXIO; + } + + length >>= 2; + + if (length != lut_len) { + u32 r, g, b; + u32 *lut = kmalloc_array(length, sizeof(*lut), GFP_KERNEL); + + if (!lut) + return -ENOMEM; + ret = of_property_read_u32_array(dsp_lut, "gamma-lut", lut, + length); + if (ret) { + dev_err(vop->dev, "load gamma-lut failed\n"); + kfree(lut); + return -EINVAL; + } + + for (i = 0; i < lut_len; i++) { + j = i * length / lut_len; + r = lut[j] / length / length * lut_len / length; + g = lut[j] / length % length * lut_len / length; + b = lut[j] % length * lut_len / length; + + vop->lut[i] = r * lut_len * lut_len + g * lut_len + b; + } + + kfree(lut); + } else { + of_property_read_u32_array(dsp_lut, "gamma-lut", + vop->lut, vop->lut_len); + } + vop->lut_active = true; + + return 0; +} + static int vop_create_crtc(struct vop *vop) { - const struct vop_data *vop_data = vop->data; struct device *dev = vop->dev; + const struct vop_data *vop_data = vop->data; struct drm_device *drm_dev = vop->drm_dev; + struct rockchip_drm_private *private = drm_dev->dev_private; struct drm_plane *primary = NULL, *cursor = NULL, *plane, *tmp; struct drm_crtc *crtc = &vop->crtc; struct device_node *port; - int ret; + uint64_t feature = 0; + int ret = 0; int i; /* @@ -1757,29 +4266,19 @@ static int vop_create_crtc(struct vop *vop) * to pass them to drm_crtc_init_with_planes, which sets the * "possible_crtcs" to the newly initialized crtc. */ - for (i = 0; i < vop_data->win_size; i++) { - struct vop_win *vop_win = &vop->win[i]; - const struct vop_win_data *win_data = vop_win->data; + for (i = 0; i < vop->num_wins; i++) { + struct vop_win *win = &vop->win[i]; - if (win_data->type != DRM_PLANE_TYPE_PRIMARY && - win_data->type != DRM_PLANE_TYPE_CURSOR) + if (win->type != DRM_PLANE_TYPE_PRIMARY && + win->type != DRM_PLANE_TYPE_CURSOR) continue; - ret = drm_universal_plane_init(vop->drm_dev, &vop_win->base, - 0, &vop_plane_funcs, - win_data->phy->data_formats, - win_data->phy->nformats, - win_data->phy->format_modifiers, - win_data->type, NULL); - if (ret) { - DRM_DEV_ERROR(vop->dev, "failed to init plane %d\n", - ret); + if (vop_plane_init(vop, win, 0)) { + DRM_DEV_ERROR(vop->dev, "failed to init plane\n"); goto err_cleanup_planes; } - plane = &vop_win->base; - drm_plane_helper_add(plane, &plane_helper_funcs); - vop_plane_add_properties(plane, win_data); + plane = &win->base; if (plane->type == DRM_PLANE_TYPE_PRIMARY) primary = plane; else if (plane->type == DRM_PLANE_TYPE_CURSOR) @@ -1792,37 +4291,23 @@ static int vop_create_crtc(struct vop *vop) goto err_cleanup_planes; drm_crtc_helper_add(crtc, &vop_crtc_helper_funcs); - if (vop->lut_regs) { - drm_mode_crtc_set_gamma_size(crtc, vop_data->lut_size); - drm_crtc_enable_color_mgmt(crtc, 0, false, vop_data->lut_size); - } /* * Create drm_planes for overlay windows with possible_crtcs restricted * to the newly created crtc. */ - for (i = 0; i < vop_data->win_size; i++) { - struct vop_win *vop_win = &vop->win[i]; - const struct vop_win_data *win_data = vop_win->data; + for (i = 0; i < vop->num_wins; i++) { + struct vop_win *win = &vop->win[i]; unsigned long possible_crtcs = drm_crtc_mask(crtc); - if (win_data->type != DRM_PLANE_TYPE_OVERLAY) + if (win->type != DRM_PLANE_TYPE_OVERLAY) continue; - ret = drm_universal_plane_init(vop->drm_dev, &vop_win->base, - possible_crtcs, - &vop_plane_funcs, - win_data->phy->data_formats, - win_data->phy->nformats, - win_data->phy->format_modifiers, - win_data->type, NULL); - if (ret) { - DRM_DEV_ERROR(vop->dev, "failed to init overlay %d\n", - ret); + if (vop_plane_init(vop, win, possible_crtcs)) { + DRM_DEV_ERROR(vop->dev, "failed to init overlay\n"); goto err_cleanup_crtc; } - drm_plane_helper_add(&vop_win->base, &plane_helper_funcs); - vop_plane_add_properties(&vop_win->base, win_data); + vop_plane_add_properties(vop, &win->base, win); } port = of_get_child_by_name(dev->of_node, "port"); @@ -1839,15 +4324,57 @@ static int vop_create_crtc(struct vop *vop) init_completion(&vop->dsp_hold_completion); init_completion(&vop->line_flag_completion); crtc->port = port; + rockchip_register_crtc_funcs(crtc, &private_crtc_funcs); - ret = drm_self_refresh_helper_init(crtc); - if (ret) - DRM_DEV_DEBUG_KMS(vop->dev, - "Failed to init %s with SR helpers %d, ignoring\n", - crtc->name, ret); +#define VOP_ATTACH_MODE_CONFIG_PROP(prop, v) \ + drm_object_attach_property(&crtc->base, drm_dev->mode_config.prop, v) + VOP_ATTACH_MODE_CONFIG_PROP(tv_left_margin_property, 100); + VOP_ATTACH_MODE_CONFIG_PROP(tv_right_margin_property, 100); + VOP_ATTACH_MODE_CONFIG_PROP(tv_top_margin_property, 100); + VOP_ATTACH_MODE_CONFIG_PROP(tv_bottom_margin_property, 100); + +#undef VOP_ATTACH_MODE_CONFIG_PROP + drm_object_attach_property(&crtc->base, private->alpha_scale_prop, 0); + if (vop_data->feature & VOP_FEATURE_AFBDC) + feature |= BIT(ROCKCHIP_DRM_CRTC_FEATURE_AFBDC); + drm_object_attach_property(&crtc->base, vop->feature_prop, + feature); + if (vop->lut_regs) { + u16 *r_base, *g_base, *b_base; + u32 lut_len = vop->lut_len; + + vop->lut = devm_kmalloc_array(dev, lut_len, sizeof(*vop->lut), + GFP_KERNEL); + if (!vop->lut) + goto err_unregister_crtc_funcs; + + if (vop_of_init_display_lut(vop)) { + for (i = 0; i < lut_len; i++) { + u32 r = i * lut_len * lut_len; + u32 g = i * lut_len; + u32 b = i; + + vop->lut[i] = r | g | b; + } + } + + drm_mode_crtc_set_gamma_size(crtc, lut_len); + drm_crtc_enable_color_mgmt(crtc, 0, false, lut_len); + r_base = crtc->gamma_store; + g_base = r_base + crtc->gamma_size; + b_base = g_base + crtc->gamma_size; + + for (i = 0; i < lut_len; i++) { + rockchip_vop_crtc_fb_gamma_get(crtc, &r_base[i], + &g_base[i], &b_base[i], + i); + } + } return 0; +err_unregister_crtc_funcs: + rockchip_unregister_crtc_funcs(crtc); err_cleanup_crtc: drm_crtc_cleanup(crtc); err_cleanup_planes: @@ -1863,8 +4390,6 @@ static void vop_destroy_crtc(struct vop *vop) struct drm_device *drm_dev = vop->drm_dev; struct drm_plane *plane, *tmp; - drm_self_refresh_helper_cleanup(crtc); - of_node_put(crtc->port); /* @@ -1887,137 +4412,121 @@ static void vop_destroy_crtc(struct vop *vop) drm_flip_work_cleanup(&vop->fb_unref_work); } -static int vop_initial(struct vop *vop) +/* + * Win_id is the order in vop_win_data array. + * This is related to the actual hardware plane. + * But in the Linux platform, such as video hardware and camera preview, + * it can only be played on the nv12 plane. + * So set the order of zpos to PRIMARY < OVERLAY (if have) < CURSOR (if have). + */ +static int vop_plane_get_zpos(enum drm_plane_type type, unsigned int size) { - struct reset_control *ahb_rst; - int i, ret; - - vop->hclk = devm_clk_get(vop->dev, "hclk_vop"); - if (IS_ERR(vop->hclk)) { - DRM_DEV_ERROR(vop->dev, "failed to get hclk source\n"); - return PTR_ERR(vop->hclk); + switch (type) { + case DRM_PLANE_TYPE_PRIMARY: + return 0; + case DRM_PLANE_TYPE_OVERLAY: + return 1; + case DRM_PLANE_TYPE_CURSOR: + return size - 1; } - vop->aclk = devm_clk_get(vop->dev, "aclk_vop"); - if (IS_ERR(vop->aclk)) { - DRM_DEV_ERROR(vop->dev, "failed to get aclk source\n"); - return PTR_ERR(vop->aclk); - } - vop->dclk = devm_clk_get(vop->dev, "dclk_vop"); - if (IS_ERR(vop->dclk)) { - DRM_DEV_ERROR(vop->dev, "failed to get dclk source\n"); - return PTR_ERR(vop->dclk); - } - - ret = pm_runtime_get_sync(vop->dev); - if (ret < 0) { - DRM_DEV_ERROR(vop->dev, "failed to get pm runtime: %d\n", ret); - return ret; - } - - ret = clk_prepare(vop->dclk); - if (ret < 0) { - DRM_DEV_ERROR(vop->dev, "failed to prepare dclk\n"); - goto err_put_pm_runtime; - } - - /* Enable both the hclk and aclk to setup the vop */ - ret = clk_prepare_enable(vop->hclk); - if (ret < 0) { - DRM_DEV_ERROR(vop->dev, "failed to prepare/enable hclk\n"); - goto err_unprepare_dclk; - } - - ret = clk_prepare_enable(vop->aclk); - if (ret < 0) { - DRM_DEV_ERROR(vop->dev, "failed to prepare/enable aclk\n"); - goto err_disable_hclk; - } - - /* - * do hclk_reset, reset all vop registers. - */ - ahb_rst = devm_reset_control_get(vop->dev, "ahb"); - if (IS_ERR(ahb_rst)) { - DRM_DEV_ERROR(vop->dev, "failed to get ahb reset\n"); - ret = PTR_ERR(ahb_rst); - goto err_disable_aclk; - } - reset_control_assert(ahb_rst); - usleep_range(10, 20); - reset_control_deassert(ahb_rst); - - VOP_INTR_SET_TYPE(vop, clear, INTR_MASK, 1); - VOP_INTR_SET_TYPE(vop, enable, INTR_MASK, 0); - - for (i = 0; i < vop->len; i += sizeof(u32)) - vop->regsbak[i / 4] = readl_relaxed(vop->regs + i); - - VOP_REG_SET(vop, misc, global_regdone_en, 1); - VOP_REG_SET(vop, common, dsp_blank, 0); - - for (i = 0; i < vop->data->win_size; i++) { - struct vop_win *vop_win = &vop->win[i]; - const struct vop_win_data *win = vop_win->data; - int channel = i * 2 + 1; - - VOP_WIN_SET(vop, win, channel, (channel + 1) << 4 | channel); - vop_win_disable(vop, vop_win); - VOP_WIN_SET(vop, win, gate, 1); - } - - vop_cfg_done(vop); - - /* - * do dclk_reset, let all config take affect. - */ - vop->dclk_rst = devm_reset_control_get(vop->dev, "dclk"); - if (IS_ERR(vop->dclk_rst)) { - DRM_DEV_ERROR(vop->dev, "failed to get dclk reset\n"); - ret = PTR_ERR(vop->dclk_rst); - goto err_disable_aclk; - } - reset_control_assert(vop->dclk_rst); - usleep_range(10, 20); - reset_control_deassert(vop->dclk_rst); - - clk_disable(vop->hclk); - clk_disable(vop->aclk); - - vop->is_enabled = false; - - pm_runtime_put_sync(vop->dev); - return 0; - -err_disable_aclk: - clk_disable_unprepare(vop->aclk); -err_disable_hclk: - clk_disable_unprepare(vop->hclk); -err_unprepare_dclk: - clk_unprepare(vop->dclk); -err_put_pm_runtime: - pm_runtime_put_sync(vop->dev); - return ret; } /* * Initialize the vop->win array elements. */ -static void vop_win_init(struct vop *vop) +static int vop_win_init(struct vop *vop) { const struct vop_data *vop_data = vop->data; - unsigned int i; + unsigned int i, j; + unsigned int num_wins = 0; + struct drm_property *prop; + static const struct drm_prop_enum_list props[] = { + { ROCKCHIP_DRM_PLANE_FEATURE_SCALE, "scale" }, + { ROCKCHIP_DRM_PLANE_FEATURE_ALPHA, "alpha" }, + { ROCKCHIP_DRM_PLANE_FEATURE_HDR2SDR, "hdr2sdr" }, + { ROCKCHIP_DRM_PLANE_FEATURE_SDR2HDR, "sdr2hdr" }, + { ROCKCHIP_DRM_PLANE_FEATURE_AFBDC, "afbdc" }, + }; + static const struct drm_prop_enum_list crtc_props[] = { + { ROCKCHIP_DRM_CRTC_FEATURE_AFBDC, "afbdc" }, + }; for (i = 0; i < vop_data->win_size; i++) { - struct vop_win *vop_win = &vop->win[i]; + struct vop_win *vop_win = &vop->win[num_wins]; const struct vop_win_data *win_data = &vop_data->win[i]; - vop_win->data = win_data; - vop_win->vop = vop; + if (!win_data->phy) + continue; - if (vop_data->win_yuv2yuv) - vop_win->yuv2yuv_data = &vop_data->win_yuv2yuv[i]; + vop_win->phy = win_data->phy; + vop_win->csc = win_data->csc; + vop_win->offset = win_data->base; + vop_win->type = win_data->type; + vop_win->data_formats = win_data->phy->data_formats; + vop_win->nformats = win_data->phy->nformats; + vop_win->feature = win_data->feature; + vop_win->vop = vop; + vop_win->win_id = i; + vop_win->area_id = 0; + vop_win->zpos = vop_plane_get_zpos(win_data->type, + vop_data->win_size); + + num_wins++; + + if (!vop->support_multi_area) + continue; + + for (j = 0; j < win_data->area_size; j++) { + struct vop_win *vop_area = &vop->win[num_wins]; + const struct vop_win_phy *area = win_data->area[j]; + + vop_area->parent = vop_win; + vop_area->offset = vop_win->offset; + vop_area->phy = area; + vop_area->type = DRM_PLANE_TYPE_OVERLAY; + vop_area->data_formats = vop_win->data_formats; + vop_area->nformats = vop_win->nformats; + vop_area->vop = vop; + vop_area->win_id = i; + vop_area->area_id = j + 1; + num_wins++; + } } + + vop->num_wins = num_wins; + + prop = drm_property_create_range(vop->drm_dev, DRM_MODE_PROP_ATOMIC, + "ZPOS", 0, vop->data->win_size - 1); + if (!prop) { + DRM_ERROR("failed to create zpos property\n"); + return -EINVAL; + } + vop->plane_zpos_prop = prop; + + vop->plane_feature_prop = drm_property_create_bitmask(vop->drm_dev, + DRM_MODE_PROP_IMMUTABLE, "FEATURE", + props, ARRAY_SIZE(props), + BIT(ROCKCHIP_DRM_PLANE_FEATURE_SCALE) | + BIT(ROCKCHIP_DRM_PLANE_FEATURE_ALPHA) | + BIT(ROCKCHIP_DRM_PLANE_FEATURE_HDR2SDR) | + BIT(ROCKCHIP_DRM_PLANE_FEATURE_SDR2HDR) | + BIT(ROCKCHIP_DRM_PLANE_FEATURE_AFBDC)); + if (!vop->plane_feature_prop) { + DRM_ERROR("failed to create feature property\n"); + return -EINVAL; + } + + vop->feature_prop = drm_property_create_bitmask(vop->drm_dev, + DRM_MODE_PROP_IMMUTABLE, "FEATURE", + crtc_props, ARRAY_SIZE(crtc_props), + BIT(ROCKCHIP_DRM_CRTC_FEATURE_AFBDC)); + if (!vop->feature_prop) { + DRM_ERROR("failed to create vop feature property\n"); + return -EINVAL; + } + + return 0; } /** @@ -2076,46 +4585,95 @@ static int vop_bind(struct device *dev, struct device *master, void *data) struct drm_device *drm_dev = data; struct vop *vop; struct resource *res; - int ret, irq; + size_t alloc_size; + int ret, irq, i; + int num_wins = 0; + bool dual_channel_swap = false; + struct device_node *mcu = NULL; vop_data = of_device_get_match_data(dev); if (!vop_data) return -ENODEV; + for (i = 0; i < vop_data->win_size; i++) { + const struct vop_win_data *win_data = &vop_data->win[i]; + + num_wins += win_data->area_size + 1; + } + /* Allocate vop struct and its vop_win array */ - vop = devm_kzalloc(dev, struct_size(vop, win, vop_data->win_size), - GFP_KERNEL); + alloc_size = sizeof(*vop) + sizeof(*vop->win) * num_wins; + vop = devm_kzalloc(dev, alloc_size, GFP_KERNEL); if (!vop) return -ENOMEM; vop->dev = dev; vop->data = vop_data; vop->drm_dev = drm_dev; + vop->num_wins = num_wins; + vop->version = vop_data->version; dev_set_drvdata(dev, vop); + vop->support_multi_area = of_property_read_bool(dev->of_node, "support-multi-area"); - vop_win_init(vop); + ret = vop_win_init(vop); + if (ret) + return ret; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - vop->len = resource_size(res); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); + if (!res) { + dev_warn(vop->dev, "failed to get vop register byname\n"); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + } vop->regs = devm_ioremap_resource(dev, res); if (IS_ERR(vop->regs)) return PTR_ERR(vop->regs); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (res) { - if (!vop_data->lut_size) { - DRM_DEV_ERROR(dev, "no gamma LUT size defined\n"); - return -EINVAL; - } - vop->lut_regs = devm_ioremap_resource(dev, res); - if (IS_ERR(vop->lut_regs)) - return PTR_ERR(vop->lut_regs); - } + vop->len = resource_size(res); vop->regsbak = devm_kzalloc(dev, vop->len, GFP_KERNEL); if (!vop->regsbak) return -ENOMEM; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gamma_lut"); + if (res) { + vop->lut_len = resource_size(res) / sizeof(*vop->lut); + if (vop->lut_len != 256 && vop->lut_len != 1024) { + dev_err(vop->dev, "unsupported lut sizes %d\n", + vop->lut_len); + return -EINVAL; + } + + vop->lut_regs = devm_ioremap_resource(dev, res); + if (IS_ERR(vop->lut_regs)) + return PTR_ERR(vop->lut_regs); + } + vop->grf = syscon_regmap_lookup_by_phandle(dev->of_node, + "rockchip,grf"); + if (IS_ERR(vop->grf)) + dev_err(dev, "missing rockchip,grf property\n"); + vop->hclk = devm_clk_get(vop->dev, "hclk_vop"); + if (IS_ERR(vop->hclk)) { + dev_err(vop->dev, "failed to get hclk source\n"); + return PTR_ERR(vop->hclk); + } + vop->aclk = devm_clk_get(vop->dev, "aclk_vop"); + if (IS_ERR(vop->aclk)) { + dev_err(vop->dev, "failed to get aclk source\n"); + return PTR_ERR(vop->aclk); + } + vop->dclk = devm_clk_get(vop->dev, "dclk_vop"); + if (IS_ERR(vop->dclk)) { + dev_err(vop->dev, "failed to get dclk source\n"); + return PTR_ERR(vop->dclk); + } + vop->dclk_source = devm_clk_get(vop->dev, "dclk_source"); + if (PTR_ERR(vop->dclk_source) == -ENOENT) { + vop->dclk_source = NULL; + } else if (PTR_ERR(vop->dclk_source) == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else if (IS_ERR(vop->dclk_source)) { + dev_err(vop->dev, "failed to get dclk source parent\n"); + return PTR_ERR(vop->dclk_source); + } irq = platform_get_irq(pdev, 0); if (irq < 0) { DRM_DEV_ERROR(dev, "cannot find irq for vop\n"); @@ -2127,53 +4685,51 @@ static int vop_bind(struct device *dev, struct device *master, void *data) spin_lock_init(&vop->irq_lock); mutex_init(&vop->vop_lock); + ret = devm_request_irq(dev, vop->irq, vop_isr, + IRQF_SHARED, dev_name(dev), vop); + if (ret) + return ret; ret = vop_create_crtc(vop); if (ret) return ret; pm_runtime_enable(&pdev->dev); - ret = vop_initial(vop); - if (ret < 0) { - DRM_DEV_ERROR(&pdev->dev, - "cannot initial vop dev - err %d\n", ret); - goto err_disable_pm_runtime; + + mcu = of_get_child_by_name(dev->of_node, "mcu-timing"); + if (!mcu) { + dev_dbg(dev, "no mcu-timing node found in %s\n", + dev->of_node->full_name); + } else { + u32 val; + + if (!of_property_read_u32(mcu, "mcu-pix-total", &val)) + vop->mcu_timing.mcu_pix_total = val; + if (!of_property_read_u32(mcu, "mcu-cs-pst", &val)) + vop->mcu_timing.mcu_cs_pst = val; + if (!of_property_read_u32(mcu, "mcu-cs-pend", &val)) + vop->mcu_timing.mcu_cs_pend = val; + if (!of_property_read_u32(mcu, "mcu-rw-pst", &val)) + vop->mcu_timing.mcu_rw_pst = val; + if (!of_property_read_u32(mcu, "mcu-rw-pend", &val)) + vop->mcu_timing.mcu_rw_pend = val; + if (!of_property_read_u32(mcu, "mcu-hold-mode", &val)) + vop->mcu_timing.mcu_hold_mode = val; } - ret = devm_request_irq(dev, vop->irq, vop_isr, - IRQF_SHARED, dev_name(dev), vop); - if (ret) - goto err_disable_pm_runtime; - - if (vop->data->feature & VOP_FEATURE_INTERNAL_RGB) { - vop->rgb = rockchip_rgb_init(dev, &vop->crtc, vop->drm_dev); - if (IS_ERR(vop->rgb)) { - ret = PTR_ERR(vop->rgb); - goto err_disable_pm_runtime; - } - } + dual_channel_swap = of_property_read_bool(dev->of_node, + "rockchip,dual-channel-swap"); + vop->dual_channel_swap = dual_channel_swap; return 0; - -err_disable_pm_runtime: - pm_runtime_disable(&pdev->dev); - vop_destroy_crtc(vop); - return ret; } static void vop_unbind(struct device *dev, struct device *master, void *data) { struct vop *vop = dev_get_drvdata(dev); - if (vop->rgb) - rockchip_rgb_fini(vop->rgb); - pm_runtime_disable(dev); vop_destroy_crtc(vop); - - clk_unprepare(vop->aclk); - clk_unprepare(vop->hclk); - clk_unprepare(vop->dclk); } const struct component_ops vop_component_ops = { diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h index f78fbf29311d..a23549e2af0e 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h @@ -18,10 +18,13 @@ #define VOP_MAJOR(version) ((version) >> 8) #define VOP_MINOR(version) ((version) & 0xff) -#define VOP2_SOC_VARIANT 4 - -#define NUM_YUV2YUV_COEFFICIENTS 12 +#define ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE BIT(0) +#define ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE BIT(1) +#define ROCKCHIP_OUTPUT_DATA_SWAP BIT(2) +#define AFBDC_FMT_RGB565 0x0 +#define AFBDC_FMT_U8U8U8U8 0x5 +#define AFBDC_FMT_U8U8U8 0x4 #define VOP_FEATURE_OUTPUT_10BIT BIT(0) #define VOP_FEATURE_AFBDC BIT(1) #define VOP_FEATURE_ALPHA_SCALE BIT(2) @@ -43,53 +46,7 @@ #define WIN_FEATURE_MULTI_AREA BIT(7) -#define DSP_BG_SWAP 0x1 -#define DSP_RB_SWAP 0x2 -#define DSP_RG_SWAP 0x4 -#define DSP_DELTA_SWAP 0x8 - -/* AFBC supports a number of configurable modes. Relevant to us is block size - * (16x16 or 32x8), storage modifiers (SPARSE, SPLIT), and the YUV-like - * colourspace transform (YTR). 16x16 SPARSE mode is always used. SPLIT mode - * could be enabled via the hreg_block_split register, but is not currently - * handled. The colourspace transform is implicitly always assumed by the - * decoder, so consumers must use this transform as well. - * - * Failure to match modifiers will cause errors displaying AFBC buffers - * produced by conformant AFBC producers, including Mesa. - */ -#define ROCKCHIP_AFBC_MOD \ - DRM_FORMAT_MOD_ARM_AFBC( \ - AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | AFBC_FORMAT_MOD_SPARSE \ - | AFBC_FORMAT_MOD_YTR \ - ) - -enum vop_data_format { - VOP_FMT_ARGB8888 = 0, - VOP_FMT_RGB888, - VOP_FMT_RGB565, - VOP_FMT_YUV420SP = 4, - VOP_FMT_YUV422SP, - VOP_FMT_YUV444SP, -}; - -enum _vop_sdr2hdr_func { - SDR2HDR_FOR_BT2020, - SDR2HDR_FOR_HDR, - SDR2HDR_FOR_HLG_HDR, -}; - -enum _vop_rgb2rgb_conv_mode { - BT709_TO_BT2020, - BT2020_TO_BT709, -}; - -enum vop_csc_format { - CSC_BT601L, - CSC_BT709L, - CSC_BT601F, - CSC_BT2020, -}; +#define VOP2_SOC_VARIANT 4 enum bcsh_out_mode { BCSH_OUT_MODE_BLACK, @@ -98,6 +55,17 @@ enum bcsh_out_mode { BCSH_OUT_MODE_NORMAL_VIDEO, }; +enum cabc_stage_mode { + LAST_FRAME_PWM_VAL = 0x0, + CUR_FRAME_PWM_VAL = 0x1, + STAGE_BY_STAGE = 0x2 +}; + +enum cabc_stage_up_mode { + MUL_MODE, + ADD_MODE, +}; + /* * the delay number of a window in different mode. */ @@ -108,128 +76,216 @@ enum vop2_win_dly_mode { VOP2_DLY_MODE_MAX, }; +#define DSP_BG_SWAP 0x1 +#define DSP_RB_SWAP 0x2 +#define DSP_RG_SWAP 0x4 +#define DSP_DELTA_SWAP 0x8 + +enum vop_csc_format { + CSC_BT601L, + CSC_BT709L, + CSC_BT601F, + CSC_BT2020, +}; + +enum vop_csc_mode { + CSC_RGB, + CSC_YUV, +}; + +enum vop_data_format { + VOP_FMT_ARGB8888 = 0, + VOP_FMT_RGB888, + VOP_FMT_RGB565 = 2, + VOP_FMT_YUYV = 2, + VOP_FMT_YUV420SP = 4, + VOP_FMT_YUV422SP, + VOP_FMT_YUV444SP, +}; + +struct vop_reg_data { + uint32_t offset; + uint32_t value; +}; + +struct vop_reg { + uint32_t mask; + uint32_t offset:17; + uint32_t shift:5; + uint32_t begin_minor:4; + uint32_t end_minor:4; + uint32_t reserved:2; + uint32_t major:3; + uint32_t write_mask:1; +}; + +struct vop_csc { + struct vop_reg y2r_en; + struct vop_reg r2r_en; + struct vop_reg r2y_en; + struct vop_reg csc_mode; + + uint32_t y2r_offset; + uint32_t r2r_offset; + uint32_t r2y_offset; +}; + struct vop_rect { int width; int height; }; -struct vop_hdr_table { - const uint32_t hdr2sdr_eetf_oetf_y0_offset; - const uint32_t hdr2sdr_eetf_oetf_y1_offset; - const uint32_t *hdr2sdr_eetf_yn; - const uint32_t *hdr2sdr_bt1886oetf_yn; - const uint32_t hdr2sdr_sat_y0_offset; - const uint32_t hdr2sdr_sat_y1_offset; - const uint32_t *hdr2sdr_sat_yn; - - const uint32_t hdr2sdr_src_range_min; - const uint32_t hdr2sdr_src_range_max; - const uint32_t hdr2sdr_normfaceetf; - const uint32_t hdr2sdr_dst_range_min; - const uint32_t hdr2sdr_dst_range_max; - const uint32_t hdr2sdr_normfacgamma; - - const uint32_t sdr2hdr_eotf_oetf_y0_offset; - const uint32_t sdr2hdr_eotf_oetf_y1_offset; - const uint32_t *sdr2hdr_bt1886eotf_yn_for_hlg_hdr; - const uint32_t *sdr2hdr_bt1886eotf_yn_for_bt2020; - const uint32_t *sdr2hdr_bt1886eotf_yn_for_hdr; - const uint32_t *sdr2hdr_st2084oetf_yn_for_hlg_hdr; - const uint32_t *sdr2hdr_st2084oetf_yn_for_bt2020; - const uint32_t *sdr2hdr_st2084oetf_yn_for_hdr; - const uint32_t sdr2hdr_oetf_dx_dxpow1_offset; - const uint32_t *sdr2hdr_st2084oetf_dxn_pow2; - const uint32_t *sdr2hdr_st2084oetf_dxn; - const uint32_t sdr2hdr_oetf_xn1_offset; - const uint32_t *sdr2hdr_st2084oetf_xn; -}; - -struct vop_reg { - uint32_t mask; - uint16_t offset; - uint8_t shift; - bool write_mask; - bool relaxed; -}; - -struct vop_afbc { - struct vop_reg enable; - struct vop_reg win_sel; - struct vop_reg format; - struct vop_reg hreg_block_split; - struct vop_reg rb_swap; - struct vop_reg uv_swap; - struct vop_reg auto_gating_en; - struct vop_reg rotate; - struct vop_reg block_split_en; - struct vop_reg pic_vir_width; - struct vop_reg tile_num; - struct vop_reg pic_offset; - struct vop_reg pic_size; - struct vop_reg dsp_offset; - struct vop_reg transform_offset; - struct vop_reg hdr_ptr; - struct vop_reg half_block_en; - struct vop_reg xmirror; - struct vop_reg ymirror; - struct vop_reg rotate_270; - struct vop_reg rotate_90; - struct vop_reg rstn; -}; - -struct vop_modeset { +struct vop_ctrl { + struct vop_reg version; + struct vop_reg standby; + struct vop_reg dma_stop; + struct vop_reg axi_outstanding_max_num; + struct vop_reg axi_max_outstanding_en; struct vop_reg htotal_pw; struct vop_reg hact_st_end; - struct vop_reg hpost_st_end; struct vop_reg vtotal_pw; struct vop_reg vact_st_end; + struct vop_reg vact_st_end_f1; + struct vop_reg vs_st_end_f1; + struct vop_reg hpost_st_end; struct vop_reg vpost_st_end; -}; - -struct vop_output { - struct vop_reg pin_pol; - struct vop_reg dp_pin_pol; - struct vop_reg dp_dclk_pol; - struct vop_reg edp_pin_pol; - struct vop_reg edp_dclk_pol; - struct vop_reg hdmi_pin_pol; - struct vop_reg hdmi_dclk_pol; - struct vop_reg mipi_pin_pol; - struct vop_reg mipi_dclk_pol; - struct vop_reg rgb_pin_pol; - struct vop_reg rgb_dclk_pol; - struct vop_reg dp_en; + struct vop_reg vpost_st_end_f1; + struct vop_reg post_scl_factor; + struct vop_reg post_scl_ctrl; + struct vop_reg dsp_interlace; + struct vop_reg global_regdone_en; + struct vop_reg auto_gate_en; + struct vop_reg post_lb_mode; + struct vop_reg dsp_layer_sel; + struct vop_reg overlay_mode; + struct vop_reg core_dclk_div; + struct vop_reg dclk_ddr; + struct vop_reg p2i_en; + struct vop_reg hdmi_dclk_out_en; + struct vop_reg rgb_en; + struct vop_reg lvds_en; struct vop_reg edp_en; struct vop_reg hdmi_en; struct vop_reg mipi_en; + struct vop_reg data01_swap; struct vop_reg mipi_dual_channel_en; - struct vop_reg rgb_en; -}; - -struct vop_common { - struct vop_reg cfg_done; - struct vop_reg dsp_blank; - struct vop_reg data_blank; - struct vop_reg pre_dither_down; + struct vop_reg dp_en; + struct vop_reg dclk_pol; + struct vop_reg pin_pol; + struct vop_reg rgb_dclk_pol; + struct vop_reg rgb_pin_pol; + struct vop_reg lvds_dclk_pol; + struct vop_reg lvds_pin_pol; + struct vop_reg hdmi_dclk_pol; + struct vop_reg hdmi_pin_pol; + struct vop_reg edp_dclk_pol; + struct vop_reg edp_pin_pol; + struct vop_reg mipi_dclk_pol; + struct vop_reg mipi_pin_pol; + struct vop_reg dp_dclk_pol; + struct vop_reg dp_pin_pol; struct vop_reg dither_down_sel; struct vop_reg dither_down_mode; struct vop_reg dither_down_en; - struct vop_reg dither_up; - struct vop_reg dsp_lut_en; - struct vop_reg gate_en; - struct vop_reg mmu_en; - struct vop_reg out_mode; - struct vop_reg standby; -}; + struct vop_reg pre_dither_down_en; + struct vop_reg dither_up_en; -struct vop_misc { - struct vop_reg global_regdone_en; + struct vop_reg sw_dac_sel; + struct vop_reg tve_sw_mode; + struct vop_reg tve_dclk_pol; + struct vop_reg tve_dclk_en; + struct vop_reg sw_genlock; + struct vop_reg sw_uv_offset_en; + struct vop_reg dsp_out_yuv; + struct vop_reg dsp_data_swap; + struct vop_reg yuv_clip; + struct vop_reg dsp_ccir656_avg; + struct vop_reg dsp_black; + struct vop_reg dsp_blank; + struct vop_reg dsp_outzero; + struct vop_reg update_gamma_lut; + struct vop_reg lut_buffer_index; + struct vop_reg dsp_lut_en; + + struct vop_reg out_mode; + + struct vop_reg xmirror; + struct vop_reg ymirror; + struct vop_reg dsp_background; + + /* AFBDC */ + struct vop_reg afbdc_en; + struct vop_reg afbdc_sel; + struct vop_reg afbdc_format; + struct vop_reg afbdc_hreg_block_split; + struct vop_reg afbdc_pic_size; + struct vop_reg afbdc_hdr_ptr; + struct vop_reg afbdc_rstn; + struct vop_reg afbdc_pic_vir_width; + struct vop_reg afbdc_pic_offset; + struct vop_reg afbdc_axi_ctrl; + + /* BCSH */ + struct vop_reg bcsh_brightness; + struct vop_reg bcsh_contrast; + struct vop_reg bcsh_sat_con; + struct vop_reg bcsh_sin_hue; + struct vop_reg bcsh_cos_hue; + struct vop_reg bcsh_r2y_csc_mode; + struct vop_reg bcsh_r2y_en; + struct vop_reg bcsh_y2r_csc_mode; + struct vop_reg bcsh_y2r_en; + struct vop_reg bcsh_color_bar; + struct vop_reg bcsh_out_mode; + struct vop_reg bcsh_en; + + /* HDR */ + struct vop_reg level2_overlay_en; + struct vop_reg alpha_hard_calc; + struct vop_reg hdr2sdr_en; + struct vop_reg hdr2sdr_en_win0_csc; + struct vop_reg hdr2sdr_src_min; + struct vop_reg hdr2sdr_src_max; + struct vop_reg hdr2sdr_normfaceetf; + struct vop_reg hdr2sdr_dst_min; + struct vop_reg hdr2sdr_dst_max; + struct vop_reg hdr2sdr_normfacgamma; + + struct vop_reg bt1886eotf_pre_conv_en; + struct vop_reg rgb2rgb_pre_conv_en; + struct vop_reg rgb2rgb_pre_conv_mode; + struct vop_reg st2084oetf_pre_conv_en; + struct vop_reg bt1886eotf_post_conv_en; + struct vop_reg rgb2rgb_post_conv_en; + struct vop_reg rgb2rgb_post_conv_mode; + struct vop_reg st2084oetf_post_conv_en; + struct vop_reg win_csc_mode_sel; + + /* MCU OUTPUT */ + struct vop_reg mcu_pix_total; + struct vop_reg mcu_cs_pst; + struct vop_reg mcu_cs_pend; + struct vop_reg mcu_rw_pst; + struct vop_reg mcu_rw_pend; + struct vop_reg mcu_clk_sel; + struct vop_reg mcu_hold_mode; + struct vop_reg mcu_frame_st; + struct vop_reg mcu_rs; + struct vop_reg mcu_bypass; + struct vop_reg mcu_type; + struct vop_reg mcu_rw_bypass_port; + + /* bt1120 */ + struct vop_reg bt1120_yc_swap; + struct vop_reg bt1120_en; + + struct vop_reg reg_done_frm; + struct vop_reg cfg_done; }; struct vop_intr { const int *intrs; uint32_t nintrs; - struct vop_reg line_flag_num[2]; struct vop_reg enable; struct vop_reg clear; @@ -269,19 +325,129 @@ struct vop_scl_regs { struct vop_reg scale_cbcr_y; }; -struct vop_yuv2yuv_phy { - struct vop_reg y2r_coefficients[NUM_YUV2YUV_COEFFICIENTS]; +struct vop_afbc { + struct vop_reg enable; + struct vop_reg win_sel; + struct vop_reg format; + struct vop_reg rb_swap; + struct vop_reg uv_swap; + struct vop_reg auto_gating_en; + struct vop_reg rotate; + struct vop_reg block_split_en; + struct vop_reg pic_vir_width; + struct vop_reg tile_num; + struct vop_reg pic_offset; + struct vop_reg pic_size; + struct vop_reg dsp_offset; + struct vop_reg transform_offset; + struct vop_reg hdr_ptr; + struct vop_reg half_block_en; + struct vop_reg xmirror; + struct vop_reg ymirror; + struct vop_reg rotate_270; + struct vop_reg rotate_90; + struct vop_reg rstn; +}; + +struct vop_csc_table { + const uint32_t *y2r_bt601; + const uint32_t *y2r_bt601_12_235; + const uint32_t *y2r_bt601_10bit; + const uint32_t *y2r_bt601_10bit_12_235; + const uint32_t *r2y_bt601; + const uint32_t *r2y_bt601_12_235; + const uint32_t *r2y_bt601_10bit; + const uint32_t *r2y_bt601_10bit_12_235; + + const uint32_t *y2r_bt709; + const uint32_t *y2r_bt709_10bit; + const uint32_t *r2y_bt709; + const uint32_t *r2y_bt709_10bit; + + const uint32_t *y2r_bt2020; + const uint32_t *r2y_bt2020; + + const uint32_t *r2r_bt709_to_bt2020; + const uint32_t *r2r_bt2020_to_bt709; +}; + +struct vop_hdr_table { + const uint32_t hdr2sdr_eetf_oetf_y0_offset; + const uint32_t hdr2sdr_eetf_oetf_y1_offset; + const uint32_t *hdr2sdr_eetf_yn; + const uint32_t *hdr2sdr_bt1886oetf_yn; + const uint32_t hdr2sdr_sat_y0_offset; + const uint32_t hdr2sdr_sat_y1_offset; + const uint32_t *hdr2sdr_sat_yn; + + const uint32_t hdr2sdr_src_range_min; + const uint32_t hdr2sdr_src_range_max; + const uint32_t hdr2sdr_normfaceetf; + const uint32_t hdr2sdr_dst_range_min; + const uint32_t hdr2sdr_dst_range_max; + const uint32_t hdr2sdr_normfacgamma; + + const uint32_t sdr2hdr_eotf_oetf_y0_offset; + const uint32_t sdr2hdr_eotf_oetf_y1_offset; + const uint32_t *sdr2hdr_bt1886eotf_yn_for_hlg_hdr; + const uint32_t *sdr2hdr_bt1886eotf_yn_for_bt2020; + const uint32_t *sdr2hdr_bt1886eotf_yn_for_hdr; + const uint32_t *sdr2hdr_st2084oetf_yn_for_hlg_hdr; + const uint32_t *sdr2hdr_st2084oetf_yn_for_bt2020; + const uint32_t *sdr2hdr_st2084oetf_yn_for_hdr; + const uint32_t sdr2hdr_oetf_dx_dxpow1_offset; + const uint32_t *sdr2hdr_st2084oetf_dxn_pow2; + const uint32_t *sdr2hdr_st2084oetf_dxn; + const uint32_t sdr2hdr_oetf_xn1_offset; + const uint32_t *sdr2hdr_st2084oetf_xn; +}; + +enum { + VOP_CSC_Y2R_BT601, + VOP_CSC_Y2R_BT709, + VOP_CSC_Y2R_BT2020, + VOP_CSC_R2Y_BT601, + VOP_CSC_R2Y_BT709, + VOP_CSC_R2Y_BT2020, + VOP_CSC_R2R_BT2020_TO_BT709, + VOP_CSC_R2R_BT709_TO_2020, +}; + +enum _vop_overlay_mode { + VOP_RGB_DOMAIN, + VOP_YUV_DOMAIN +}; + +enum _vop_sdr2hdr_func { + SDR2HDR_FOR_BT2020, + SDR2HDR_FOR_HDR, + SDR2HDR_FOR_HLG_HDR, +}; + +enum _vop_rgb2rgb_conv_mode { + BT709_TO_BT2020, + BT2020_TO_BT709, +}; + +enum _MCU_IOCTL { + MCU_WRCMD = 0, + MCU_WRDATA, + MCU_SETBYPASS, }; struct vop_win_phy { const struct vop_scl_regs *scl; const uint32_t *data_formats; uint32_t nformats; - const uint64_t *format_modifiers; - struct vop_reg enable; struct vop_reg gate; + struct vop_reg enable; struct vop_reg format; + struct vop_reg fmt_10; + struct vop_reg fmt_yuyv; + struct vop_reg csc_mode; + struct vop_reg xmirror; + struct vop_reg ymirror; struct vop_reg rb_swap; struct vop_reg act_info; struct vop_reg dsp_info; @@ -290,50 +456,25 @@ struct vop_win_phy { struct vop_reg uv_mst; struct vop_reg yrgb_vir; struct vop_reg uv_vir; - struct vop_reg y_mir_en; - struct vop_reg x_mir_en; + struct vop_reg channel; struct vop_reg dst_alpha_ctl; struct vop_reg src_alpha_ctl; - struct vop_reg alpha_pre_mul; struct vop_reg alpha_mode; struct vop_reg alpha_en; - struct vop_reg channel; -}; - -struct vop_win_yuv2yuv_data { - uint32_t base; - const struct vop_yuv2yuv_phy *phy; - struct vop_reg y2r_en; + struct vop_reg alpha_pre_mul; + struct vop_reg global_alpha_val; + struct vop_reg key_color; + struct vop_reg key_en; }; struct vop_win_data { uint32_t base; - const struct vop_win_phy *phy; enum drm_plane_type type; -}; - -struct vop_grf_ctrl { - struct vop_reg grf_dclk_inv; - struct vop_reg grf_bt1120_clk_inv; - struct vop_reg grf_bt656_clk_inv; -}; - -struct vop_data { - uint32_t version; - const struct vop_intr *intr; - const struct vop_common *common; - const struct vop_misc *misc; - const struct vop_modeset *modeset; - const struct vop_output *output; - const struct vop_afbc *afbc; - const struct vop_win_yuv2yuv_data *win_yuv2yuv; - const struct vop_win_data *win; - unsigned int win_size; - unsigned int lut_size; - -#define VOP_FEATURE_OUTPUT_RGB10 BIT(0) -#define VOP_FEATURE_INTERNAL_RGB BIT(1) + const struct vop_win_phy *phy; + const struct vop_win_phy **area; + const struct vop_csc *csc; + unsigned int area_size; u64 feature; }; @@ -466,6 +607,7 @@ struct vop2_video_port_regs { struct vop_reg hdr_dst_color_ctrl; struct vop_reg hdr_src_alpha_ctrl; struct vop_reg hdr_dst_alpha_ctrl; + struct vop_reg bg_mix_ctrl; /* BCSH */ struct vop_reg bcsh_brightness; @@ -581,7 +723,7 @@ struct vop2_layer_regs { * * The pipeline in vop2: * - * win-->layer-->mixer-->vp-->connector(RGB/LVDS/HDMI/MIPI) + * win-->layer-->mixer-->vp--->connector(RGB/LVDS/HDMI/MIPI) * */ struct vop2_layer_data { @@ -589,6 +731,28 @@ struct vop2_layer_data { const struct vop2_layer_regs *regs; }; +struct vop_grf_ctrl { + struct vop_reg grf_dclk_inv; + struct vop_reg grf_bt1120_clk_inv; + struct vop_reg grf_bt656_clk_inv; +}; + +struct vop_data { + const struct vop_reg_data *init_table; + unsigned int table_size; + const struct vop_ctrl *ctrl; + const struct vop_intr *intr; + const struct vop_win_data *win; + const struct vop_csc_table *csc_table; + const struct vop_hdr_table *hdr_table; + const struct vop_grf_ctrl *grf_ctrl; + unsigned int win_size; + uint32_t version; + struct vop_rect max_input; + struct vop_rect max_output; + u64 feature; +}; + struct vop2_ctrl { struct vop_reg cfg_done_en; struct vop_reg wb_cfg_done; @@ -699,11 +863,13 @@ struct vop2_data { unsigned int win_size; }; +#define CVBS_PAL_VDISPLAY 288 + /* interrupt define */ -#define DSP_HOLD_VALID_INTR (1 << 0) -#define FS_INTR (1 << 1) -#define LINE_FLAG_INTR (1 << 2) -#define BUS_ERROR_INTR (1 << 3) +#define DSP_HOLD_VALID_INTR BIT(0) +#define FS_INTR BIT(1) +#define LINE_FLAG_INTR BIT(2) +#define BUS_ERROR_INTR BIT(3) #define FS_NEW_INTR BIT(4) #define ADDR_SAME_INTR BIT(5) #define LINE_FLAG1_INTR BIT(6) @@ -730,7 +896,6 @@ struct vop2_data { POST_BUF_EMPTY_INTR | \ DMA_FINISH_INTR | FS_FIELD_INTR | \ FE_INTR) - #define DSP_HOLD_VALID_INTR_EN(x) ((x) << 4) #define FS_INTR_EN(x) ((x) << 5) #define LINE_FLAG_INTR_EN(x) ((x) << 6) @@ -764,10 +929,10 @@ struct vop2_data { /* * display output interface supported by rockchip lcdc */ -#define ROCKCHIP_OUT_MODE_P888 0 +#define ROCKCHIP_OUT_MODE_P888 0 #define ROCKCHIP_OUT_MODE_BT1120 0 -#define ROCKCHIP_OUT_MODE_P666 1 -#define ROCKCHIP_OUT_MODE_P565 2 +#define ROCKCHIP_OUT_MODE_P666 1 +#define ROCKCHIP_OUT_MODE_P565 2 #define ROCKCHIP_OUT_MODE_BT656 5 #define ROCKCHIP_OUT_MODE_S888 8 #define ROCKCHIP_OUT_MODE_S888_DUMMY 12 @@ -775,13 +940,8 @@ struct vop2_data { /* for use special outface */ #define ROCKCHIP_OUT_MODE_AAAA 15 - -/* output flags */ -#define ROCKCHIP_OUTPUT_DSI_DUAL BIT(0) - -#define ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE BIT(0) -#define ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE BIT(1) -#define ROCKCHIP_OUTPUT_DATA_SWAP BIT(2) +#define ROCKCHIP_OUT_MODE_TYPE(x) ((x) >> 16) +#define ROCKCHIP_OUT_MODE(x) ((x) & 0xffff) enum alpha_mode { ALPHA_STRAIGHT, @@ -810,6 +970,7 @@ enum factor_mode { ALPHA_SRC, ALPHA_SRC_INVERSE, ALPHA_SRC_GLOBAL, + ALPHA_DST_GLOBAL, }; enum src_factor_mode { @@ -880,7 +1041,8 @@ enum dither_down_mode_sel { enum vop_pol { HSYNC_POSITIVE = 0, VSYNC_POSITIVE = 1, - DEN_NEGATIVE = 2 + DEN_NEGATIVE = 2, + DCLK_INVERT = 3 }; @@ -908,7 +1070,7 @@ static inline uint16_t scl_get_bili_dn_vskip(int src_h, int dst_h, { int act_height; - act_height = DIV_ROUND_UP(src_h, vskiplines); + act_height = (src_h + vskiplines - 1) / vskiplines; if (act_height == dst_h) return GET_SCL_FT_BILI_DN(src_h, dst_h) / vskiplines; diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c index 80053d91a301..66e9a4c8e798 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c @@ -5,36 +5,36 @@ */ #include -#include -#include +#include #include #include #include -#include #include #include "rockchip_drm_vop.h" #include "rockchip_vop_reg.h" -#include "rockchip_drm_drv.h" -#define _VOP_REG(off, _mask, _shift, _write_mask, _relaxed) \ - { \ - .offset = off, \ +#define VOP_REG_VER_MASK(off, _mask, s, _write_mask, _major, \ + _begin_minor, _end_minor) \ + {.offset = off, \ .mask = _mask, \ - .shift = _shift, \ + .shift = s, \ .write_mask = _write_mask, \ - .relaxed = _relaxed, \ - } + .major = _major, \ + .begin_minor = _begin_minor, \ + .end_minor = _end_minor,} -#define VOP_REG(off, _mask, _shift) \ - _VOP_REG(off, _mask, _shift, false, true) +#define VOP_REG(off, _mask, s) \ + VOP_REG_VER_MASK(off, _mask, s, false, 0, 0, -1) -#define VOP_REG_SYNC(off, _mask, _shift) \ - _VOP_REG(off, _mask, _shift, false, false) +#define VOP_REG_MASK(off, _mask, s) \ + VOP_REG_VER_MASK(off, _mask, s, true, 0, 0, -1) + +#define VOP_REG_VER(off, _mask, s, _major, _begin_minor, _end_minor) \ + VOP_REG_VER_MASK(off, _mask, s, false, \ + _major, _begin_minor, _end_minor) -#define VOP_REG_MASK_SYNC(off, _mask, _shift) \ - _VOP_REG(off, _mask, _shift, true, false) static const uint32_t formats_win_full[] = { DRM_FORMAT_XRGB8888, @@ -50,15 +50,39 @@ static const uint32_t formats_win_full[] = { DRM_FORMAT_NV24, }; -static const uint64_t format_modifiers_win_full[] = { - DRM_FORMAT_MOD_LINEAR, - DRM_FORMAT_MOD_INVALID, +static const uint32_t formats_win_full_10bit[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_RGB888, + DRM_FORMAT_BGR888, + DRM_FORMAT_RGB565, + DRM_FORMAT_BGR565, + DRM_FORMAT_NV12, + DRM_FORMAT_NV16, + DRM_FORMAT_NV24, + ///DRM_FORMAT_NV12_10, + //DRM_FORMAT_NV16_10, + //DRM_FORMAT_NV24_10, }; -static const uint64_t format_modifiers_win_full_afbc[] = { - ROCKCHIP_AFBC_MOD, - DRM_FORMAT_MOD_LINEAR, - DRM_FORMAT_MOD_INVALID, +static const uint32_t formats_win_full_10bit_yuyv[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_RGB888, + DRM_FORMAT_BGR888, + DRM_FORMAT_RGB565, + DRM_FORMAT_BGR565, + DRM_FORMAT_NV12, + DRM_FORMAT_NV16, + DRM_FORMAT_NV24, + //DRM_FORMAT_NV12_10, + //DRM_FORMAT_NV16_10, + //DRM_FORMAT_NV24_10, + DRM_FORMAT_YUYV, }; static const uint32_t formats_win_lite[] = { @@ -72,479 +96,6 @@ static const uint32_t formats_win_lite[] = { DRM_FORMAT_BGR565, }; -static const uint64_t format_modifiers_win_lite[] = { - DRM_FORMAT_MOD_LINEAR, - DRM_FORMAT_MOD_INVALID, -}; - -static const struct vop_scl_regs rk3036_win_scl = { - .scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0), - .scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16), - .scale_cbcr_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0), - .scale_cbcr_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 16), -}; - -static const struct vop_win_phy rk3036_win0_data = { - .scl = &rk3036_win_scl, - .data_formats = formats_win_full, - .nformats = ARRAY_SIZE(formats_win_full), - .format_modifiers = format_modifiers_win_full, - .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 0), - .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 3), - .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 15), - .act_info = VOP_REG(RK3036_WIN0_ACT_INFO, 0x1fff1fff, 0), - .dsp_info = VOP_REG(RK3036_WIN0_DSP_INFO, 0x0fff0fff, 0), - .dsp_st = VOP_REG(RK3036_WIN0_DSP_ST, 0x1fff1fff, 0), - .yrgb_mst = VOP_REG(RK3036_WIN0_YRGB_MST, 0xffffffff, 0), - .uv_mst = VOP_REG(RK3036_WIN0_CBR_MST, 0xffffffff, 0), - .yrgb_vir = VOP_REG(RK3036_WIN0_VIR, 0xffff, 0), - .uv_vir = VOP_REG(RK3036_WIN0_VIR, 0x1fff, 16), -}; - -static const struct vop_win_phy rk3036_win1_data = { - .data_formats = formats_win_lite, - .nformats = ARRAY_SIZE(formats_win_lite), - .format_modifiers = format_modifiers_win_lite, - .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1), - .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6), - .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19), - .act_info = VOP_REG(RK3036_WIN1_ACT_INFO, 0x1fff1fff, 0), - .dsp_info = VOP_REG(RK3036_WIN1_DSP_INFO, 0x0fff0fff, 0), - .dsp_st = VOP_REG(RK3036_WIN1_DSP_ST, 0x1fff1fff, 0), - .yrgb_mst = VOP_REG(RK3036_WIN1_MST, 0xffffffff, 0), - .yrgb_vir = VOP_REG(RK3036_WIN1_VIR, 0xffff, 0), -}; - -static const struct vop_win_data rk3036_vop_win_data[] = { - { .base = 0x00, .phy = &rk3036_win0_data, - .type = DRM_PLANE_TYPE_PRIMARY }, - { .base = 0x00, .phy = &rk3036_win1_data, - .type = DRM_PLANE_TYPE_CURSOR }, -}; - -static const int rk3036_vop_intrs[] = { - DSP_HOLD_VALID_INTR, - FS_INTR, - LINE_FLAG_INTR, - BUS_ERROR_INTR, -}; - -static const struct vop_intr rk3036_intr = { - .intrs = rk3036_vop_intrs, - .nintrs = ARRAY_SIZE(rk3036_vop_intrs), - .line_flag_num[0] = VOP_REG(RK3036_INT_STATUS, 0xfff, 12), - .status = VOP_REG_SYNC(RK3036_INT_STATUS, 0xf, 0), - .enable = VOP_REG_SYNC(RK3036_INT_STATUS, 0xf, 4), - .clear = VOP_REG_SYNC(RK3036_INT_STATUS, 0xf, 8), -}; - -static const struct vop_modeset rk3036_modeset = { - .htotal_pw = VOP_REG(RK3036_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), - .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0), - .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), - .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0), -}; - -static const struct vop_output rk3036_output = { - .pin_pol = VOP_REG(RK3036_DSP_CTRL0, 0xf, 4), -}; - -static const struct vop_common rk3036_common = { - .standby = VOP_REG_SYNC(RK3036_SYS_CTRL, 0x1, 30), - .out_mode = VOP_REG(RK3036_DSP_CTRL0, 0xf, 0), - .dsp_blank = VOP_REG(RK3036_DSP_CTRL1, 0x1, 24), - .dither_down_sel = VOP_REG(RK3036_DSP_CTRL0, 0x1, 27), - .dither_down_en = VOP_REG(RK3036_DSP_CTRL0, 0x1, 11), - .dither_down_mode = VOP_REG(RK3036_DSP_CTRL0, 0x1, 10), - .cfg_done = VOP_REG_SYNC(RK3036_REG_CFG_DONE, 0x1, 0), -}; - -static const struct vop_data rk3036_vop = { - .intr = &rk3036_intr, - .common = &rk3036_common, - .modeset = &rk3036_modeset, - .output = &rk3036_output, - .win = rk3036_vop_win_data, - .win_size = ARRAY_SIZE(rk3036_vop_win_data), -}; - -static const struct vop_win_phy rk3126_win1_data = { - .data_formats = formats_win_lite, - .nformats = ARRAY_SIZE(formats_win_lite), - .format_modifiers = format_modifiers_win_lite, - .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1), - .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6), - .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19), - .dsp_info = VOP_REG(RK3126_WIN1_DSP_INFO, 0x0fff0fff, 0), - .dsp_st = VOP_REG(RK3126_WIN1_DSP_ST, 0x1fff1fff, 0), - .yrgb_mst = VOP_REG(RK3126_WIN1_MST, 0xffffffff, 0), - .yrgb_vir = VOP_REG(RK3036_WIN1_VIR, 0xffff, 0), -}; - -static const struct vop_win_data rk3126_vop_win_data[] = { - { .base = 0x00, .phy = &rk3036_win0_data, - .type = DRM_PLANE_TYPE_PRIMARY }, - { .base = 0x00, .phy = &rk3126_win1_data, - .type = DRM_PLANE_TYPE_CURSOR }, -}; - -static const struct vop_data rk3126_vop = { - .intr = &rk3036_intr, - .common = &rk3036_common, - .modeset = &rk3036_modeset, - .output = &rk3036_output, - .win = rk3126_vop_win_data, - .win_size = ARRAY_SIZE(rk3126_vop_win_data), -}; - -static const int px30_vop_intrs[] = { - FS_INTR, - 0, 0, - LINE_FLAG_INTR, - 0, - BUS_ERROR_INTR, - 0, 0, - DSP_HOLD_VALID_INTR, -}; - -static const struct vop_intr px30_intr = { - .intrs = px30_vop_intrs, - .nintrs = ARRAY_SIZE(px30_vop_intrs), - .line_flag_num[0] = VOP_REG(PX30_LINE_FLAG, 0xfff, 0), - .status = VOP_REG_MASK_SYNC(PX30_INTR_STATUS, 0xffff, 0), - .enable = VOP_REG_MASK_SYNC(PX30_INTR_EN, 0xffff, 0), - .clear = VOP_REG_MASK_SYNC(PX30_INTR_CLEAR, 0xffff, 0), -}; - -static const struct vop_common px30_common = { - .standby = VOP_REG_SYNC(PX30_SYS_CTRL2, 0x1, 1), - .out_mode = VOP_REG(PX30_DSP_CTRL2, 0xf, 16), - .dsp_blank = VOP_REG(PX30_DSP_CTRL2, 0x1, 14), - .dither_down_en = VOP_REG(PX30_DSP_CTRL2, 0x1, 8), - .dither_down_sel = VOP_REG(PX30_DSP_CTRL2, 0x1, 7), - .dither_down_mode = VOP_REG(PX30_DSP_CTRL2, 0x1, 6), - .cfg_done = VOP_REG_SYNC(PX30_REG_CFG_DONE, 0x1, 0), -}; - -static const struct vop_modeset px30_modeset = { - .htotal_pw = VOP_REG(PX30_DSP_HTOTAL_HS_END, 0x0fff0fff, 0), - .hact_st_end = VOP_REG(PX30_DSP_HACT_ST_END, 0x0fff0fff, 0), - .vtotal_pw = VOP_REG(PX30_DSP_VTOTAL_VS_END, 0x0fff0fff, 0), - .vact_st_end = VOP_REG(PX30_DSP_VACT_ST_END, 0x0fff0fff, 0), -}; - -static const struct vop_output px30_output = { - .rgb_dclk_pol = VOP_REG(PX30_DSP_CTRL0, 0x1, 1), - .rgb_pin_pol = VOP_REG(PX30_DSP_CTRL0, 0x7, 2), - .rgb_en = VOP_REG(PX30_DSP_CTRL0, 0x1, 0), - .mipi_dclk_pol = VOP_REG(PX30_DSP_CTRL0, 0x1, 25), - .mipi_pin_pol = VOP_REG(PX30_DSP_CTRL0, 0x7, 26), - .mipi_en = VOP_REG(PX30_DSP_CTRL0, 0x1, 24), -}; - -static const struct vop_scl_regs px30_win_scl = { - .scale_yrgb_x = VOP_REG(PX30_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0), - .scale_yrgb_y = VOP_REG(PX30_WIN0_SCL_FACTOR_YRGB, 0xffff, 16), - .scale_cbcr_x = VOP_REG(PX30_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0), - .scale_cbcr_y = VOP_REG(PX30_WIN0_SCL_FACTOR_CBR, 0xffff, 16), -}; - -static const struct vop_win_phy px30_win0_data = { - .scl = &px30_win_scl, - .data_formats = formats_win_full, - .nformats = ARRAY_SIZE(formats_win_full), - .format_modifiers = format_modifiers_win_full, - .enable = VOP_REG(PX30_WIN0_CTRL0, 0x1, 0), - .format = VOP_REG(PX30_WIN0_CTRL0, 0x7, 1), - .rb_swap = VOP_REG(PX30_WIN0_CTRL0, 0x1, 12), - .act_info = VOP_REG(PX30_WIN0_ACT_INFO, 0xffffffff, 0), - .dsp_info = VOP_REG(PX30_WIN0_DSP_INFO, 0xffffffff, 0), - .dsp_st = VOP_REG(PX30_WIN0_DSP_ST, 0xffffffff, 0), - .yrgb_mst = VOP_REG(PX30_WIN0_YRGB_MST0, 0xffffffff, 0), - .uv_mst = VOP_REG(PX30_WIN0_CBR_MST0, 0xffffffff, 0), - .yrgb_vir = VOP_REG(PX30_WIN0_VIR, 0x1fff, 0), - .uv_vir = VOP_REG(PX30_WIN0_VIR, 0x1fff, 16), - .alpha_pre_mul = VOP_REG(PX30_WIN0_ALPHA_CTRL, 0x1, 2), - .alpha_mode = VOP_REG(PX30_WIN0_ALPHA_CTRL, 0x1, 1), - .alpha_en = VOP_REG(PX30_WIN0_ALPHA_CTRL, 0x1, 0), -}; - -static const struct vop_win_phy px30_win1_data = { - .data_formats = formats_win_lite, - .nformats = ARRAY_SIZE(formats_win_lite), - .format_modifiers = format_modifiers_win_lite, - .enable = VOP_REG(PX30_WIN1_CTRL0, 0x1, 0), - .format = VOP_REG(PX30_WIN1_CTRL0, 0x7, 4), - .rb_swap = VOP_REG(PX30_WIN1_CTRL0, 0x1, 12), - .dsp_info = VOP_REG(PX30_WIN1_DSP_INFO, 0xffffffff, 0), - .dsp_st = VOP_REG(PX30_WIN1_DSP_ST, 0xffffffff, 0), - .yrgb_mst = VOP_REG(PX30_WIN1_MST, 0xffffffff, 0), - .yrgb_vir = VOP_REG(PX30_WIN1_VIR, 0x1fff, 0), - .alpha_pre_mul = VOP_REG(PX30_WIN1_ALPHA_CTRL, 0x1, 2), - .alpha_mode = VOP_REG(PX30_WIN1_ALPHA_CTRL, 0x1, 1), - .alpha_en = VOP_REG(PX30_WIN1_ALPHA_CTRL, 0x1, 0), -}; - -static const struct vop_win_phy px30_win2_data = { - .data_formats = formats_win_lite, - .nformats = ARRAY_SIZE(formats_win_lite), - .format_modifiers = format_modifiers_win_lite, - .gate = VOP_REG(PX30_WIN2_CTRL0, 0x1, 4), - .enable = VOP_REG(PX30_WIN2_CTRL0, 0x1, 0), - .format = VOP_REG(PX30_WIN2_CTRL0, 0x3, 5), - .rb_swap = VOP_REG(PX30_WIN2_CTRL0, 0x1, 20), - .dsp_info = VOP_REG(PX30_WIN2_DSP_INFO0, 0x0fff0fff, 0), - .dsp_st = VOP_REG(PX30_WIN2_DSP_ST0, 0x1fff1fff, 0), - .yrgb_mst = VOP_REG(PX30_WIN2_MST0, 0xffffffff, 0), - .yrgb_vir = VOP_REG(PX30_WIN2_VIR0_1, 0x1fff, 0), - .alpha_pre_mul = VOP_REG(PX30_WIN2_ALPHA_CTRL, 0x1, 2), - .alpha_mode = VOP_REG(PX30_WIN2_ALPHA_CTRL, 0x1, 1), - .alpha_en = VOP_REG(PX30_WIN2_ALPHA_CTRL, 0x1, 0), -}; - -static const struct vop_win_data px30_vop_big_win_data[] = { - { .base = 0x00, .phy = &px30_win0_data, - .type = DRM_PLANE_TYPE_PRIMARY }, - { .base = 0x00, .phy = &px30_win1_data, - .type = DRM_PLANE_TYPE_OVERLAY }, - { .base = 0x00, .phy = &px30_win2_data, - .type = DRM_PLANE_TYPE_CURSOR }, -}; - -static const struct vop_data px30_vop_big = { - .intr = &px30_intr, - .feature = VOP_FEATURE_INTERNAL_RGB, - .common = &px30_common, - .modeset = &px30_modeset, - .output = &px30_output, - .win = px30_vop_big_win_data, - .win_size = ARRAY_SIZE(px30_vop_big_win_data), -}; - -static const struct vop_win_data px30_vop_lit_win_data[] = { - { .base = 0x00, .phy = &px30_win1_data, - .type = DRM_PLANE_TYPE_PRIMARY }, -}; - -static const struct vop_data px30_vop_lit = { - .intr = &px30_intr, - .feature = VOP_FEATURE_INTERNAL_RGB, - .common = &px30_common, - .modeset = &px30_modeset, - .output = &px30_output, - .win = px30_vop_lit_win_data, - .win_size = ARRAY_SIZE(px30_vop_lit_win_data), -}; - -static const struct vop_scl_regs rk3066_win_scl = { - .scale_yrgb_x = VOP_REG(RK3066_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0), - .scale_yrgb_y = VOP_REG(RK3066_WIN0_SCL_FACTOR_YRGB, 0xffff, 16), - .scale_cbcr_x = VOP_REG(RK3066_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0), - .scale_cbcr_y = VOP_REG(RK3066_WIN0_SCL_FACTOR_CBR, 0xffff, 16), -}; - -static const struct vop_win_phy rk3066_win0_data = { - .scl = &rk3066_win_scl, - .data_formats = formats_win_full, - .nformats = ARRAY_SIZE(formats_win_full), - .format_modifiers = format_modifiers_win_full, - .enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 0), - .format = VOP_REG(RK3066_SYS_CTRL0, 0x7, 4), - .rb_swap = VOP_REG(RK3066_SYS_CTRL0, 0x1, 19), - .act_info = VOP_REG(RK3066_WIN0_ACT_INFO, 0x1fff1fff, 0), - .dsp_info = VOP_REG(RK3066_WIN0_DSP_INFO, 0x0fff0fff, 0), - .dsp_st = VOP_REG(RK3066_WIN0_DSP_ST, 0x1fff1fff, 0), - .yrgb_mst = VOP_REG(RK3066_WIN0_YRGB_MST0, 0xffffffff, 0), - .uv_mst = VOP_REG(RK3066_WIN0_CBR_MST0, 0xffffffff, 0), - .yrgb_vir = VOP_REG(RK3066_WIN0_VIR, 0xffff, 0), - .uv_vir = VOP_REG(RK3066_WIN0_VIR, 0x1fff, 16), -}; - -static const struct vop_win_phy rk3066_win1_data = { - .scl = &rk3066_win_scl, - .data_formats = formats_win_full, - .nformats = ARRAY_SIZE(formats_win_full), - .format_modifiers = format_modifiers_win_full, - .enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 1), - .format = VOP_REG(RK3066_SYS_CTRL0, 0x7, 7), - .rb_swap = VOP_REG(RK3066_SYS_CTRL0, 0x1, 23), - .act_info = VOP_REG(RK3066_WIN1_ACT_INFO, 0x1fff1fff, 0), - .dsp_info = VOP_REG(RK3066_WIN1_DSP_INFO, 0x0fff0fff, 0), - .dsp_st = VOP_REG(RK3066_WIN1_DSP_ST, 0x1fff1fff, 0), - .yrgb_mst = VOP_REG(RK3066_WIN1_YRGB_MST, 0xffffffff, 0), - .uv_mst = VOP_REG(RK3066_WIN1_CBR_MST, 0xffffffff, 0), - .yrgb_vir = VOP_REG(RK3066_WIN1_VIR, 0xffff, 0), - .uv_vir = VOP_REG(RK3066_WIN1_VIR, 0x1fff, 16), -}; - -static const struct vop_win_phy rk3066_win2_data = { - .data_formats = formats_win_lite, - .nformats = ARRAY_SIZE(formats_win_lite), - .format_modifiers = format_modifiers_win_lite, - .enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 2), - .format = VOP_REG(RK3066_SYS_CTRL0, 0x7, 10), - .rb_swap = VOP_REG(RK3066_SYS_CTRL0, 0x1, 27), - .dsp_info = VOP_REG(RK3066_WIN2_DSP_INFO, 0x0fff0fff, 0), - .dsp_st = VOP_REG(RK3066_WIN2_DSP_ST, 0x1fff1fff, 0), - .yrgb_mst = VOP_REG(RK3066_WIN2_MST, 0xffffffff, 0), - .yrgb_vir = VOP_REG(RK3066_WIN2_VIR, 0xffff, 0), -}; - -static const struct vop_modeset rk3066_modeset = { - .htotal_pw = VOP_REG(RK3066_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), - .hact_st_end = VOP_REG(RK3066_DSP_HACT_ST_END, 0x1fff1fff, 0), - .vtotal_pw = VOP_REG(RK3066_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), - .vact_st_end = VOP_REG(RK3066_DSP_VACT_ST_END, 0x1fff1fff, 0), -}; - -static const struct vop_output rk3066_output = { - .pin_pol = VOP_REG(RK3066_DSP_CTRL0, 0x7, 4), -}; - -static const struct vop_common rk3066_common = { - .standby = VOP_REG(RK3066_SYS_CTRL0, 0x1, 1), - .out_mode = VOP_REG(RK3066_DSP_CTRL0, 0xf, 0), - .cfg_done = VOP_REG(RK3066_REG_CFG_DONE, 0x1, 0), - .dither_down_en = VOP_REG(RK3066_DSP_CTRL0, 0x1, 11), - .dither_down_mode = VOP_REG(RK3066_DSP_CTRL0, 0x1, 10), - .dsp_blank = VOP_REG(RK3066_DSP_CTRL1, 0x1, 24), -}; - -static const struct vop_win_data rk3066_vop_win_data[] = { - { .base = 0x00, .phy = &rk3066_win0_data, - .type = DRM_PLANE_TYPE_PRIMARY }, - { .base = 0x00, .phy = &rk3066_win1_data, - .type = DRM_PLANE_TYPE_OVERLAY }, - { .base = 0x00, .phy = &rk3066_win2_data, - .type = DRM_PLANE_TYPE_CURSOR }, -}; - -static const int rk3066_vop_intrs[] = { - /* - * hs_start interrupt fires at frame-start, so serves - * the same purpose as dsp_hold in the driver. - */ - DSP_HOLD_VALID_INTR, - FS_INTR, - LINE_FLAG_INTR, - BUS_ERROR_INTR, -}; - -static const struct vop_intr rk3066_intr = { - .intrs = rk3066_vop_intrs, - .nintrs = ARRAY_SIZE(rk3066_vop_intrs), - .line_flag_num[0] = VOP_REG(RK3066_INT_STATUS, 0xfff, 12), - .status = VOP_REG(RK3066_INT_STATUS, 0xf, 0), - .enable = VOP_REG(RK3066_INT_STATUS, 0xf, 4), - .clear = VOP_REG(RK3066_INT_STATUS, 0xf, 8), -}; - -static const struct vop_data rk3066_vop = { - .version = VOP_VERSION(2, 1), - .intr = &rk3066_intr, - .common = &rk3066_common, - .modeset = &rk3066_modeset, - .output = &rk3066_output, - .win = rk3066_vop_win_data, - .win_size = ARRAY_SIZE(rk3066_vop_win_data), -}; - -static const struct vop_scl_regs rk3188_win_scl = { - .scale_yrgb_x = VOP_REG(RK3188_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0), - .scale_yrgb_y = VOP_REG(RK3188_WIN0_SCL_FACTOR_YRGB, 0xffff, 16), - .scale_cbcr_x = VOP_REG(RK3188_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0), - .scale_cbcr_y = VOP_REG(RK3188_WIN0_SCL_FACTOR_CBR, 0xffff, 16), -}; - -static const struct vop_win_phy rk3188_win0_data = { - .scl = &rk3188_win_scl, - .data_formats = formats_win_full, - .nformats = ARRAY_SIZE(formats_win_full), - .format_modifiers = format_modifiers_win_full, - .enable = VOP_REG(RK3188_SYS_CTRL, 0x1, 0), - .format = VOP_REG(RK3188_SYS_CTRL, 0x7, 3), - .rb_swap = VOP_REG(RK3188_SYS_CTRL, 0x1, 15), - .act_info = VOP_REG(RK3188_WIN0_ACT_INFO, 0x1fff1fff, 0), - .dsp_info = VOP_REG(RK3188_WIN0_DSP_INFO, 0x0fff0fff, 0), - .dsp_st = VOP_REG(RK3188_WIN0_DSP_ST, 0x1fff1fff, 0), - .yrgb_mst = VOP_REG(RK3188_WIN0_YRGB_MST0, 0xffffffff, 0), - .uv_mst = VOP_REG(RK3188_WIN0_CBR_MST0, 0xffffffff, 0), - .yrgb_vir = VOP_REG(RK3188_WIN_VIR, 0x1fff, 0), -}; - -static const struct vop_win_phy rk3188_win1_data = { - .data_formats = formats_win_lite, - .nformats = ARRAY_SIZE(formats_win_lite), - .format_modifiers = format_modifiers_win_lite, - .enable = VOP_REG(RK3188_SYS_CTRL, 0x1, 1), - .format = VOP_REG(RK3188_SYS_CTRL, 0x7, 6), - .rb_swap = VOP_REG(RK3188_SYS_CTRL, 0x1, 19), - /* no act_info on window1 */ - .dsp_info = VOP_REG(RK3188_WIN1_DSP_INFO, 0x07ff07ff, 0), - .dsp_st = VOP_REG(RK3188_WIN1_DSP_ST, 0x0fff0fff, 0), - .yrgb_mst = VOP_REG(RK3188_WIN1_MST, 0xffffffff, 0), - .yrgb_vir = VOP_REG(RK3188_WIN_VIR, 0x1fff, 16), -}; - -static const struct vop_modeset rk3188_modeset = { - .htotal_pw = VOP_REG(RK3188_DSP_HTOTAL_HS_END, 0x0fff0fff, 0), - .hact_st_end = VOP_REG(RK3188_DSP_HACT_ST_END, 0x0fff0fff, 0), - .vtotal_pw = VOP_REG(RK3188_DSP_VTOTAL_VS_END, 0x0fff0fff, 0), - .vact_st_end = VOP_REG(RK3188_DSP_VACT_ST_END, 0x0fff0fff, 0), -}; - -static const struct vop_output rk3188_output = { - .pin_pol = VOP_REG(RK3188_DSP_CTRL0, 0xf, 4), -}; - -static const struct vop_common rk3188_common = { - .gate_en = VOP_REG(RK3188_SYS_CTRL, 0x1, 31), - .standby = VOP_REG(RK3188_SYS_CTRL, 0x1, 30), - .out_mode = VOP_REG(RK3188_DSP_CTRL0, 0xf, 0), - .cfg_done = VOP_REG(RK3188_REG_CFG_DONE, 0x1, 0), - .dither_down_sel = VOP_REG(RK3188_DSP_CTRL0, 0x1, 27), - .dither_down_en = VOP_REG(RK3188_DSP_CTRL0, 0x1, 11), - .dither_down_mode = VOP_REG(RK3188_DSP_CTRL0, 0x1, 10), - .dsp_blank = VOP_REG(RK3188_DSP_CTRL1, 0x3, 24), -}; - -static const struct vop_win_data rk3188_vop_win_data[] = { - { .base = 0x00, .phy = &rk3188_win0_data, - .type = DRM_PLANE_TYPE_PRIMARY }, - { .base = 0x00, .phy = &rk3188_win1_data, - .type = DRM_PLANE_TYPE_CURSOR }, -}; - -static const int rk3188_vop_intrs[] = { - /* - * hs_start interrupt fires at frame-start, so serves - * the same purpose as dsp_hold in the driver. - */ - DSP_HOLD_VALID_INTR, - FS_INTR, - LINE_FLAG_INTR, - BUS_ERROR_INTR, -}; - -static const struct vop_intr rk3188_vop_intr = { - .intrs = rk3188_vop_intrs, - .nintrs = ARRAY_SIZE(rk3188_vop_intrs), - .line_flag_num[0] = VOP_REG(RK3188_INT_STATUS, 0xfff, 12), - .status = VOP_REG(RK3188_INT_STATUS, 0xf, 0), - .enable = VOP_REG(RK3188_INT_STATUS, 0xf, 4), - .clear = VOP_REG(RK3188_INT_STATUS, 0xf, 8), -}; - -static const struct vop_data rk3188_vop = { - .intr = &rk3188_vop_intr, - .common = &rk3188_common, - .modeset = &rk3188_modeset, - .output = &rk3188_output, - .win = rk3188_vop_win_data, - .win_size = ARRAY_SIZE(rk3188_vop_win_data), - .feature = VOP_FEATURE_INTERNAL_RGB, -}; - static const struct vop_scl_extension rk3288_win_full_scl_ext = { .cbcr_vsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 31), .cbcr_vsu_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 30), @@ -579,12 +130,15 @@ static const struct vop_scl_regs rk3288_win_full_scl = { static const struct vop_win_phy rk3288_win01_data = { .scl = &rk3288_win_full_scl, - .data_formats = formats_win_full, - .nformats = ARRAY_SIZE(formats_win_full), - .format_modifiers = format_modifiers_win_full, + .data_formats = formats_win_full_10bit, + .nformats = ARRAY_SIZE(formats_win_full_10bit), .enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0), .format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1), + .fmt_10 = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 4), + .csc_mode = VOP_REG_VER(RK3288_WIN0_CTRL0, 0x3, 10, 3, 2, -1), .rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12), + .xmirror = VOP_REG_VER(RK3368_WIN0_CTRL0, 0x1, 21, 3, 2, -1), + .ymirror = VOP_REG_VER(RK3368_WIN0_CTRL0, 0x1, 22, 3, 2, -1), .act_info = VOP_REG(RK3288_WIN0_ACT_INFO, 0x1fff1fff, 0), .dsp_info = VOP_REG(RK3288_WIN0_DSP_INFO, 0x0fff0fff, 0), .dsp_st = VOP_REG(RK3288_WIN0_DSP_ST, 0x1fff1fff, 0), @@ -592,58 +146,156 @@ static const struct vop_win_phy rk3288_win01_data = { .uv_mst = VOP_REG(RK3288_WIN0_CBR_MST, 0xffffffff, 0), .yrgb_vir = VOP_REG(RK3288_WIN0_VIR, 0x3fff, 0), .uv_vir = VOP_REG(RK3288_WIN0_VIR, 0x3fff, 16), - .src_alpha_ctl = VOP_REG(RK3288_WIN0_SRC_ALPHA_CTRL, 0xff, 0), - .dst_alpha_ctl = VOP_REG(RK3288_WIN0_DST_ALPHA_CTRL, 0xff, 0), - .channel = VOP_REG(RK3288_WIN0_CTRL2, 0xff, 0), + .src_alpha_ctl = VOP_REG(RK3288_WIN0_SRC_ALPHA_CTRL, 0xffff, 0), + .global_alpha_val = VOP_REG(RK3288_WIN0_SRC_ALPHA_CTRL, 0xff, 16), + .dst_alpha_ctl = VOP_REG(RK3288_WIN0_DST_ALPHA_CTRL, 0xffffffff, 0), + .channel = VOP_REG_VER(RK3288_WIN0_CTRL2, 0xff, 0, 3, 8, 8), }; static const struct vop_win_phy rk3288_win23_data = { .data_formats = formats_win_lite, .nformats = ARRAY_SIZE(formats_win_lite), - .format_modifiers = format_modifiers_win_lite, - .enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 4), .gate = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 0), + .enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 4), .format = VOP_REG(RK3288_WIN2_CTRL0, 0x7, 1), .rb_swap = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 12), .dsp_info = VOP_REG(RK3288_WIN2_DSP_INFO0, 0x0fff0fff, 0), .dsp_st = VOP_REG(RK3288_WIN2_DSP_ST0, 0x1fff1fff, 0), .yrgb_mst = VOP_REG(RK3288_WIN2_MST0, 0xffffffff, 0), .yrgb_vir = VOP_REG(RK3288_WIN2_VIR0_1, 0x1fff, 0), - .src_alpha_ctl = VOP_REG(RK3288_WIN2_SRC_ALPHA_CTRL, 0xff, 0), - .dst_alpha_ctl = VOP_REG(RK3288_WIN2_DST_ALPHA_CTRL, 0xff, 0), + .src_alpha_ctl = VOP_REG(RK3288_WIN2_SRC_ALPHA_CTRL, 0xffff, 0), + .global_alpha_val = VOP_REG(RK3288_WIN2_SRC_ALPHA_CTRL, 0xff, 16), + .dst_alpha_ctl = VOP_REG(RK3288_WIN2_DST_ALPHA_CTRL, 0xffffffff, 0), }; -static const struct vop_modeset rk3288_modeset = { +static const struct vop_win_phy rk3288_area1_data = { + .enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 5), + .dsp_info = VOP_REG(RK3288_WIN2_DSP_INFO1, 0x0fff0fff, 0), + .dsp_st = VOP_REG(RK3288_WIN2_DSP_ST1, 0x1fff1fff, 0), + .yrgb_mst = VOP_REG(RK3288_WIN2_MST1, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3288_WIN2_VIR0_1, 0x1fff, 16), +}; + +static const struct vop_win_phy rk3288_area2_data = { + .enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 6), + .dsp_info = VOP_REG(RK3288_WIN2_DSP_INFO2, 0x0fff0fff, 0), + .dsp_st = VOP_REG(RK3288_WIN2_DSP_ST2, 0x1fff1fff, 0), + .yrgb_mst = VOP_REG(RK3288_WIN2_MST2, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3288_WIN2_VIR2_3, 0x1fff, 0), +}; + +static const struct vop_win_phy rk3288_area3_data = { + .enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 7), + .dsp_info = VOP_REG(RK3288_WIN2_DSP_INFO3, 0x0fff0fff, 0), + .dsp_st = VOP_REG(RK3288_WIN2_DSP_ST3, 0x1fff1fff, 0), + .yrgb_mst = VOP_REG(RK3288_WIN2_MST3, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3288_WIN2_VIR2_3, 0x1fff, 16), +}; + +static const struct vop_win_phy *rk3288_area_data[] = { + &rk3288_area1_data, + &rk3288_area2_data, + &rk3288_area3_data +}; + +static const struct vop_ctrl rk3288_ctrl_data = { + .version = VOP_REG(RK3288_VERSION_INFO, 0xffff, 16), + .standby = VOP_REG(RK3288_SYS_CTRL, 0x1, 22), + .dma_stop = VOP_REG(RK3288_SYS_CTRL, 0x1, 21), + .axi_outstanding_max_num = VOP_REG(RK3288_SYS_CTRL1, 0x1f, 13), + .axi_max_outstanding_en = VOP_REG(RK3288_SYS_CTRL1, 0x1, 12), + .reg_done_frm = VOP_REG_VER(RK3288_SYS_CTRL1, 0x1, 24, 3, 5, -1), .htotal_pw = VOP_REG(RK3288_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), .hact_st_end = VOP_REG(RK3288_DSP_HACT_ST_END, 0x1fff1fff, 0), .vtotal_pw = VOP_REG(RK3288_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), .vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0), + .vact_st_end_f1 = VOP_REG(RK3288_DSP_VACT_ST_END_F1, 0x1fff1fff, 0), + .vs_st_end_f1 = VOP_REG(RK3288_DSP_VS_ST_END_F1, 0x1fff1fff, 0), .hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0), .vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0), -}; + .vpost_st_end_f1 = VOP_REG(RK3288_POST_DSP_VACT_INFO_F1, 0x1fff1fff, 0), + .post_scl_factor = VOP_REG(RK3288_POST_SCL_FACTOR_YRGB, 0xffffffff, 0), + .post_scl_ctrl = VOP_REG(RK3288_POST_SCL_CTRL, 0x3, 0), -static const struct vop_output rk3288_output = { - .pin_pol = VOP_REG(RK3288_DSP_CTRL0, 0xf, 4), + .dsp_interlace = VOP_REG(RK3288_DSP_CTRL0, 0x1, 10), + .auto_gate_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 23), + .dsp_layer_sel = VOP_REG(RK3288_DSP_CTRL1, 0xff, 8), + .post_lb_mode = VOP_REG_VER(RK3288_SYS_CTRL, 0x1, 18, 3, 2, -1), + .global_regdone_en = VOP_REG_VER(RK3288_SYS_CTRL, 0x1, 11, 3, 2, -1), + .overlay_mode = VOP_REG_VER(RK3288_SYS_CTRL, 0x1, 16, 3, 2, -1), + .core_dclk_div = VOP_REG_VER(RK3366_DSP_CTRL0, 0x1, 4, 3, 4, -1), + .p2i_en = VOP_REG_VER(RK3366_DSP_CTRL0, 0x1, 5, 3, 4, -1), + .dclk_ddr = VOP_REG_VER(RK3288_DSP_CTRL0, 0x1, 8, 3, 1, -1), + .dp_en = VOP_REG_VER(RK3399_SYS_CTRL, 0x1, 11, 3, 5, -1), + .hdmi_dclk_out_en = VOP_REG_VER(RK3288_SYS_CTRL, 0x1, 11, 3, 1, 1), .rgb_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 12), .hdmi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 13), .edp_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 14), .mipi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 15), -}; + .mipi_dual_channel_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 3), + .data01_swap = VOP_REG_VER(RK3288_SYS_CTRL, 0x1, 17, 3, 5, -1), + .dclk_pol = VOP_REG_VER(RK3288_DSP_CTRL0, 0x1, 7, 3, 0, 1), + .pin_pol = VOP_REG_VER(RK3288_DSP_CTRL0, 0x7, 4, 3, 0, 1), + .dp_dclk_pol = VOP_REG_VER(RK3399_DSP_CTRL1, 0x1, 19, 3, 5, -1), + .dp_pin_pol = VOP_REG_VER(RK3399_DSP_CTRL1, 0x7, 16, 3, 5, -1), + .rgb_dclk_pol = VOP_REG_VER(RK3368_DSP_CTRL1, 0x1, 19, 3, 2, -1), + .rgb_pin_pol = VOP_REG_VER(RK3368_DSP_CTRL1, 0x7, 16, 3, 2, -1), + .tve_dclk_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 24), + .tve_dclk_pol = VOP_REG(RK3288_SYS_CTRL, 0x1, 25), + .tve_sw_mode = VOP_REG(RK3288_SYS_CTRL, 0x1, 26), + .sw_uv_offset_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 27), + .sw_genlock = VOP_REG(RK3288_SYS_CTRL, 0x1, 28), + .hdmi_dclk_pol = VOP_REG_VER(RK3368_DSP_CTRL1, 0x1, 23, 3, 2, -1), + .hdmi_pin_pol = VOP_REG_VER(RK3368_DSP_CTRL1, 0x7, 20, 3, 2, -1), + .edp_dclk_pol = VOP_REG_VER(RK3368_DSP_CTRL1, 0x1, 27, 3, 2, -1), + .edp_pin_pol = VOP_REG_VER(RK3368_DSP_CTRL1, 0x7, 24, 3, 2, -1), + .mipi_dclk_pol = VOP_REG_VER(RK3368_DSP_CTRL1, 0x1, 31, 3, 2, -1), + .mipi_pin_pol = VOP_REG_VER(RK3368_DSP_CTRL1, 0x7, 28, 3, 2, -1), -static const struct vop_common rk3288_common = { - .standby = VOP_REG_SYNC(RK3288_SYS_CTRL, 0x1, 22), - .gate_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 23), - .mmu_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 20), .dither_down_sel = VOP_REG(RK3288_DSP_CTRL1, 0x1, 4), .dither_down_mode = VOP_REG(RK3288_DSP_CTRL1, 0x1, 3), .dither_down_en = VOP_REG(RK3288_DSP_CTRL1, 0x1, 2), - .pre_dither_down = VOP_REG(RK3288_DSP_CTRL1, 0x1, 1), - .dither_up = VOP_REG(RK3288_DSP_CTRL1, 0x1, 6), - .dsp_lut_en = VOP_REG(RK3288_DSP_CTRL1, 0x1, 0), - .data_blank = VOP_REG(RK3288_DSP_CTRL0, 0x1, 19), + .pre_dither_down_en = VOP_REG(RK3288_DSP_CTRL1, 0x1, 1), + .dither_up_en = VOP_REG(RK3288_DSP_CTRL1, 0x1, 6), + + .dsp_out_yuv = VOP_REG_VER(RK3399_POST_SCL_CTRL, 0x1, 2, 3, 5, -1), + .dsp_data_swap = VOP_REG(RK3288_DSP_CTRL0, 0x1f, 12), + .dsp_ccir656_avg = VOP_REG(RK3288_DSP_CTRL0, 0x1, 20), .dsp_blank = VOP_REG(RK3288_DSP_CTRL0, 0x3, 18), + .update_gamma_lut = VOP_REG_VER(RK3288_DSP_CTRL1, 0x1, 7, 3, 5, -1), + .lut_buffer_index = VOP_REG_VER(RK3399_DBG_POST_REG1, 0x1, 1, 3, 5, -1), + .dsp_lut_en = VOP_REG(RK3288_DSP_CTRL1, 0x1, 0), .out_mode = VOP_REG(RK3288_DSP_CTRL0, 0xf, 0), - .cfg_done = VOP_REG_SYNC(RK3288_REG_CFG_DONE, 0x1, 0), + + .afbdc_rstn = VOP_REG_VER(RK3399_AFBCD0_CTRL, 0x1, 3, 3, 5, -1), + .afbdc_en = VOP_REG_VER(RK3399_AFBCD0_CTRL, 0x1, 0, 3, 5, -1), + .afbdc_sel = VOP_REG_VER(RK3399_AFBCD0_CTRL, 0x3, 1, 3, 5, -1), + .afbdc_format = VOP_REG_VER(RK3399_AFBCD0_CTRL, 0x1f, 16, 3, 5, -1), + .afbdc_hreg_block_split = VOP_REG_VER(RK3399_AFBCD0_CTRL, + 0x1, 21, 3, 5, -1), + .afbdc_hdr_ptr = VOP_REG_VER(RK3399_AFBCD0_HDR_PTR, 0xffffffff, + 0, 3, 5, -1), + .afbdc_pic_size = VOP_REG_VER(RK3399_AFBCD0_PIC_SIZE, 0xffffffff, + 0, 3, 5, -1), + .bcsh_brightness = VOP_REG(RK3288_BCSH_BCS, 0xff, 0), + .bcsh_contrast = VOP_REG(RK3288_BCSH_BCS, 0x1ff, 8), + .bcsh_sat_con = VOP_REG(RK3288_BCSH_BCS, 0x3ff, 20), + .bcsh_out_mode = VOP_REG(RK3288_BCSH_BCS, 0x3, 30), + .bcsh_sin_hue = VOP_REG(RK3288_BCSH_H, 0x1ff, 0), + .bcsh_cos_hue = VOP_REG(RK3288_BCSH_H, 0x1ff, 16), + .bcsh_r2y_csc_mode = VOP_REG_VER(RK3368_BCSH_CTRL, 0x1, 6, 3, 1, -1), + .bcsh_r2y_en = VOP_REG_VER(RK3368_BCSH_CTRL, 0x1, 4, 3, 1, -1), + .bcsh_y2r_csc_mode = VOP_REG_VER(RK3368_BCSH_CTRL, 0x3, 2, 3, 1, -1), + .bcsh_y2r_en = VOP_REG_VER(RK3368_BCSH_CTRL, 0x1, 0, 3, 1, -1), + .bcsh_color_bar = VOP_REG(RK3288_BCSH_COLOR_BAR, 0xffffff, 8), + .bcsh_en = VOP_REG(RK3288_BCSH_COLOR_BAR, 0x1, 0), + + .xmirror = VOP_REG(RK3288_DSP_CTRL0, 0x1, 22), + .ymirror = VOP_REG(RK3288_DSP_CTRL0, 0x1, 23), + + .dsp_background = VOP_REG(RK3288_DSP_BG, 0xffffffff, 0), + + .cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0), }; /* @@ -658,9 +310,13 @@ static const struct vop_win_data rk3288_vop_win_data[] = { { .base = 0x40, .phy = &rk3288_win01_data, .type = DRM_PLANE_TYPE_OVERLAY }, { .base = 0x00, .phy = &rk3288_win23_data, - .type = DRM_PLANE_TYPE_OVERLAY }, + .type = DRM_PLANE_TYPE_OVERLAY, + .area = rk3288_area_data, + .area_size = ARRAY_SIZE(rk3288_area_data), }, { .base = 0x50, .phy = &rk3288_win23_data, - .type = DRM_PLANE_TYPE_CURSOR }, + .type = DRM_PLANE_TYPE_CURSOR, + .area = rk3288_area_data, + .area_size = ARRAY_SIZE(rk3288_area_data), }, }; static const int rk3288_vop_intrs[] = { @@ -679,25 +335,52 @@ static const struct vop_intr rk3288_vop_intr = { .clear = VOP_REG(RK3288_INTR_CTRL0, 0xf, 8), }; -static const struct vop_data rk3288_vop = { - .version = VOP_VERSION(3, 1), - .feature = VOP_FEATURE_OUTPUT_RGB10, +static const struct vop_grf_ctrl rk3288_vop_big_grf_ctrl = { + .grf_dclk_inv = VOP_REG(RK3288_GRF_SOC_CON15, 0x1, 13), +}; + +static const struct vop_grf_ctrl rk3288_vop_lit_grf_ctrl = { + .grf_dclk_inv = VOP_REG(RK3288_GRF_SOC_CON15, 0x1, 15), +}; + +static const struct vop_data rk3288_vop_big = { + .version = VOP_VERSION(3, 0), + .feature = VOP_FEATURE_OUTPUT_10BIT | VOP_FEATURE_ALPHA_SCALE, + .max_input = {4096, 8192}, + .max_output = {3840, 2160}, .intr = &rk3288_vop_intr, - .common = &rk3288_common, - .modeset = &rk3288_modeset, - .output = &rk3288_output, + .grf_ctrl = &rk3288_vop_big_grf_ctrl, + .ctrl = &rk3288_ctrl_data, + .win = rk3288_vop_win_data, + .win_size = ARRAY_SIZE(rk3288_vop_win_data), +}; + +static const struct vop_data rk3288_vop_lit = { + .version = VOP_VERSION(3, 0), + .feature = VOP_FEATURE_OUTPUT_10BIT | VOP_FEATURE_ALPHA_SCALE, + .max_input = {4096, 8192}, + .max_output = {2560, 1600}, + .intr = &rk3288_vop_intr, + .grf_ctrl = &rk3288_vop_lit_grf_ctrl, + .ctrl = &rk3288_ctrl_data, .win = rk3288_vop_win_data, .win_size = ARRAY_SIZE(rk3288_vop_win_data), - .lut_size = 1024, }; static const int rk3368_vop_intrs[] = { FS_INTR, - 0, 0, + FS_NEW_INTR, + ADDR_SAME_INTR, LINE_FLAG_INTR, - 0, + LINE_FLAG1_INTR, BUS_ERROR_INTR, - 0, 0, 0, 0, 0, 0, 0, + WIN0_EMPTY_INTR, + WIN1_EMPTY_INTR, + WIN2_EMPTY_INTR, + WIN3_EMPTY_INTR, + HWC_EMPTY_INTR, + POST_BUF_EMPTY_INTR, + FS_FIELD_INTR, DSP_HOLD_VALID_INTR, }; @@ -706,87 +389,86 @@ static const struct vop_intr rk3368_vop_intr = { .nintrs = ARRAY_SIZE(rk3368_vop_intrs), .line_flag_num[0] = VOP_REG(RK3368_LINE_FLAG, 0xffff, 0), .line_flag_num[1] = VOP_REG(RK3368_LINE_FLAG, 0xffff, 16), - .status = VOP_REG_MASK_SYNC(RK3368_INTR_STATUS, 0x3fff, 0), - .enable = VOP_REG_MASK_SYNC(RK3368_INTR_EN, 0x3fff, 0), - .clear = VOP_REG_MASK_SYNC(RK3368_INTR_CLEAR, 0x3fff, 0), -}; - -static const struct vop_win_phy rk3368_win01_data = { - .scl = &rk3288_win_full_scl, - .data_formats = formats_win_full, - .nformats = ARRAY_SIZE(formats_win_full), - .format_modifiers = format_modifiers_win_full, - .enable = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 0), - .format = VOP_REG(RK3368_WIN0_CTRL0, 0x7, 1), - .rb_swap = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 12), - .x_mir_en = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 21), - .y_mir_en = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 22), - .act_info = VOP_REG(RK3368_WIN0_ACT_INFO, 0x1fff1fff, 0), - .dsp_info = VOP_REG(RK3368_WIN0_DSP_INFO, 0x0fff0fff, 0), - .dsp_st = VOP_REG(RK3368_WIN0_DSP_ST, 0x1fff1fff, 0), - .yrgb_mst = VOP_REG(RK3368_WIN0_YRGB_MST, 0xffffffff, 0), - .uv_mst = VOP_REG(RK3368_WIN0_CBR_MST, 0xffffffff, 0), - .yrgb_vir = VOP_REG(RK3368_WIN0_VIR, 0x3fff, 0), - .uv_vir = VOP_REG(RK3368_WIN0_VIR, 0x3fff, 16), - .src_alpha_ctl = VOP_REG(RK3368_WIN0_SRC_ALPHA_CTRL, 0xff, 0), - .dst_alpha_ctl = VOP_REG(RK3368_WIN0_DST_ALPHA_CTRL, 0xff, 0), - .channel = VOP_REG(RK3368_WIN0_CTRL2, 0xff, 0), + .status = VOP_REG_MASK(RK3368_INTR_STATUS, 0x3fff, 0), + .enable = VOP_REG_MASK(RK3368_INTR_EN, 0x3fff, 0), + .clear = VOP_REG_MASK(RK3368_INTR_CLEAR, 0x3fff, 0), }; static const struct vop_win_phy rk3368_win23_data = { .data_formats = formats_win_lite, .nformats = ARRAY_SIZE(formats_win_lite), - .format_modifiers = format_modifiers_win_lite, .gate = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 0), .enable = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 4), .format = VOP_REG(RK3368_WIN2_CTRL0, 0x3, 5), + .ymirror = VOP_REG(RK3368_WIN2_CTRL1, 0x1, 15), .rb_swap = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 20), - .y_mir_en = VOP_REG(RK3368_WIN2_CTRL1, 0x1, 15), .dsp_info = VOP_REG(RK3368_WIN2_DSP_INFO0, 0x0fff0fff, 0), .dsp_st = VOP_REG(RK3368_WIN2_DSP_ST0, 0x1fff1fff, 0), .yrgb_mst = VOP_REG(RK3368_WIN2_MST0, 0xffffffff, 0), .yrgb_vir = VOP_REG(RK3368_WIN2_VIR0_1, 0x1fff, 0), - .src_alpha_ctl = VOP_REG(RK3368_WIN2_SRC_ALPHA_CTRL, 0xff, 0), - .dst_alpha_ctl = VOP_REG(RK3368_WIN2_DST_ALPHA_CTRL, 0xff, 0), + .src_alpha_ctl = VOP_REG(RK3368_WIN2_SRC_ALPHA_CTRL, 0xffff, 0), + .global_alpha_val = VOP_REG(RK3368_WIN2_SRC_ALPHA_CTRL, 0xff, 16), + .dst_alpha_ctl = VOP_REG(RK3368_WIN2_DST_ALPHA_CTRL, 0xffffffff, 0), +}; + +static const struct vop_win_phy rk3368_area1_data = { + .enable = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 8), + .format = VOP_REG(RK3368_WIN2_CTRL0, 0x3, 9), + .rb_swap = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 23), + .dsp_info = VOP_REG(RK3368_WIN2_DSP_INFO1, 0x0fff0fff, 0), + .dsp_st = VOP_REG(RK3368_WIN2_DSP_ST1, 0x1fff1fff, 0), + .yrgb_mst = VOP_REG(RK3368_WIN2_MST1, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3368_WIN2_VIR0_1, 0x1fff, 16), +}; + +static const struct vop_win_phy rk3368_area2_data = { + .enable = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 12), + .format = VOP_REG(RK3368_WIN2_CTRL0, 0x3, 13), + .rb_swap = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 26), + .dsp_info = VOP_REG(RK3368_WIN2_DSP_INFO2, 0x0fff0fff, 0), + .dsp_st = VOP_REG(RK3368_WIN2_DSP_ST2, 0x1fff1fff, 0), + .yrgb_mst = VOP_REG(RK3368_WIN2_MST2, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3368_WIN2_VIR2_3, 0x1fff, 0), +}; + +static const struct vop_win_phy rk3368_area3_data = { + .enable = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 16), + .format = VOP_REG(RK3368_WIN2_CTRL0, 0x3, 17), + .rb_swap = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 29), + .dsp_info = VOP_REG(RK3368_WIN2_DSP_INFO3, 0x0fff0fff, 0), + .dsp_st = VOP_REG(RK3368_WIN2_DSP_ST3, 0x1fff1fff, 0), + .yrgb_mst = VOP_REG(RK3368_WIN2_MST3, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3368_WIN2_VIR2_3, 0x1fff, 16), +}; + +static const struct vop_win_phy *rk3368_area_data[] = { + &rk3368_area1_data, + &rk3368_area2_data, + &rk3368_area3_data }; static const struct vop_win_data rk3368_vop_win_data[] = { - { .base = 0x00, .phy = &rk3368_win01_data, + { .base = 0x00, .phy = &rk3288_win01_data, .type = DRM_PLANE_TYPE_PRIMARY }, - { .base = 0x40, .phy = &rk3368_win01_data, + { .base = 0x40, .phy = &rk3288_win01_data, .type = DRM_PLANE_TYPE_OVERLAY }, { .base = 0x00, .phy = &rk3368_win23_data, - .type = DRM_PLANE_TYPE_OVERLAY }, + .type = DRM_PLANE_TYPE_OVERLAY, + .area = rk3368_area_data, + .area_size = ARRAY_SIZE(rk3368_area_data), }, { .base = 0x50, .phy = &rk3368_win23_data, - .type = DRM_PLANE_TYPE_CURSOR }, -}; - -static const struct vop_output rk3368_output = { - .rgb_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 19), - .hdmi_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 23), - .edp_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 27), - .mipi_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 31), - .rgb_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 16), - .hdmi_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 20), - .edp_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 24), - .mipi_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 28), - .rgb_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 12), - .hdmi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 13), - .edp_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 14), - .mipi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 15), -}; - -static const struct vop_misc rk3368_misc = { - .global_regdone_en = VOP_REG(RK3368_SYS_CTRL, 0x1, 11), + .type = DRM_PLANE_TYPE_CURSOR, + .area = rk3368_area_data, + .area_size = ARRAY_SIZE(rk3368_area_data), }, }; static const struct vop_data rk3368_vop = { .version = VOP_VERSION(3, 2), + .feature = VOP_FEATURE_ALPHA_SCALE, + .max_input = {4096, 8192}, + .max_output = {4096, 2160}, .intr = &rk3368_vop_intr, - .common = &rk3288_common, - .modeset = &rk3288_modeset, - .output = &rk3368_output, - .misc = &rk3368_misc, + .ctrl = &rk3288_ctrl_data, .win = rk3368_vop_win_data, .win_size = ARRAY_SIZE(rk3368_vop_win_data), }; @@ -796,79 +478,137 @@ static const struct vop_intr rk3366_vop_intr = { .nintrs = ARRAY_SIZE(rk3368_vop_intrs), .line_flag_num[0] = VOP_REG(RK3366_LINE_FLAG, 0xffff, 0), .line_flag_num[1] = VOP_REG(RK3366_LINE_FLAG, 0xffff, 16), - .status = VOP_REG_MASK_SYNC(RK3366_INTR_STATUS0, 0xffff, 0), - .enable = VOP_REG_MASK_SYNC(RK3366_INTR_EN0, 0xffff, 0), - .clear = VOP_REG_MASK_SYNC(RK3366_INTR_CLEAR0, 0xffff, 0), + .status = VOP_REG_MASK(RK3366_INTR_STATUS0, 0xffff, 0), + .enable = VOP_REG_MASK(RK3366_INTR_EN0, 0xffff, 0), + .clear = VOP_REG_MASK(RK3366_INTR_CLEAR0, 0xffff, 0), +}; + +static const struct vop_grf_ctrl rk3368_vop_grf_ctrl = { + .grf_dclk_inv = VOP_REG(RK3368_GRF_SOC_CON6, 0x1, 5), }; static const struct vop_data rk3366_vop = { .version = VOP_VERSION(3, 4), + .feature = VOP_FEATURE_ALPHA_SCALE, + .max_input = {4096, 8192}, + .max_output = {4096, 2160}, .intr = &rk3366_vop_intr, - .common = &rk3288_common, - .modeset = &rk3288_modeset, - .output = &rk3368_output, - .misc = &rk3368_misc, + .grf_ctrl = &rk3368_vop_grf_ctrl, + .ctrl = &rk3288_ctrl_data, .win = rk3368_vop_win_data, .win_size = ARRAY_SIZE(rk3368_vop_win_data), }; -static const struct vop_output rk3399_output = { - .dp_dclk_pol = VOP_REG(RK3399_DSP_CTRL1, 0x1, 19), - .rgb_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 19), - .hdmi_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 23), - .edp_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 27), - .mipi_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 31), - .dp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0x7, 16), - .rgb_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 16), - .hdmi_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 20), - .edp_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 24), - .mipi_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 28), - .dp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11), - .rgb_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 12), - .hdmi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 13), - .edp_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 14), - .mipi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 15), - .mipi_dual_channel_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 3), +static const uint32_t vop_csc_y2r_bt601[] = { + 0x00000400, 0x0400059c, 0xfd25fea0, 0x07170400, + 0x00000000, 0xfff4cab4, 0x00087932, 0xfff1d4f2, }; -static const struct vop_yuv2yuv_phy rk3399_yuv2yuv_win01_data = { - .y2r_coefficients = { - VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 0, 0xffff, 0), - VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 0, 0xffff, 16), - VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 4, 0xffff, 0), - VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 4, 0xffff, 16), - VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 8, 0xffff, 0), - VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 8, 0xffff, 16), - VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 12, 0xffff, 0), - VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 12, 0xffff, 16), - VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 16, 0xffff, 0), - VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 20, 0xffffffff, 0), - VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 24, 0xffffffff, 0), - VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 28, 0xffffffff, 0), - }, +static const uint32_t vop_csc_y2r_bt601_12_235[] = { + 0x000004a8, 0x04a80662, 0xfcbffe6f, 0x081204a8, + 0x00000000, 0xfff2134e, 0x00087b58, 0xffeeb4b0, }; -static const struct vop_yuv2yuv_phy rk3399_yuv2yuv_win23_data = { }; +static const uint32_t vop_csc_r2y_bt601[] = { + 0x02590132, 0xff530075, 0x0200fead, 0xfe530200, + 0x0000ffad, 0x00000200, 0x00080200, 0x00080200, +}; -static const struct vop_win_yuv2yuv_data rk3399_vop_big_win_yuv2yuv_data[] = { - { .base = 0x00, .phy = &rk3399_yuv2yuv_win01_data, - .y2r_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 1) }, - { .base = 0x60, .phy = &rk3399_yuv2yuv_win01_data, - .y2r_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 9) }, - { .base = 0xC0, .phy = &rk3399_yuv2yuv_win23_data }, - { .base = 0x120, .phy = &rk3399_yuv2yuv_win23_data }, +static const uint32_t vop_csc_r2y_bt601_12_235[] = { + 0x02040107, 0xff680064, 0x01c2fed6, 0xfe8701c2, + 0x0000ffb7, 0x00010200, 0x00080200, 0x00080200, +}; +static const uint32_t vop_csc_y2r_bt709[] = { + 0x000004a8, 0x04a8072c, 0xfddeff26, 0x087304a8, + 0x00000000, 0xfff08077, 0x0004cfed, 0xffedf1b8, +}; + +static const uint32_t vop_csc_r2y_bt709[] = { + 0x027500bb, 0xff99003f, 0x01c2fea5, 0xfe6801c2, + 0x0000ffd7, 0x00010200, 0x00080200, 0x00080200, +}; + +static const uint32_t vop_csc_y2r_bt2020[] = { + 0x000004a8, 0x04a806b6, 0xfd66ff40, 0x089004a8, + 0x00000000, 0xfff16bfc, 0x00058ae9, 0xffedb828, +}; + +static const uint32_t vop_csc_r2y_bt2020[] = { + 0x025300e6, 0xff830034, 0x01c1febd, 0xfe6401c1, + 0x0000ffdc, 0x00010200, 0x00080200, 0x00080200, +}; + +static const uint32_t vop_csc_r2r_bt709_to_bt2020[] = { + 0xfda606a4, 0xff80ffb5, 0xfff80488, 0xff99ffed, + 0x0000047a, 0x00000200, 0x00000200, 0x00000200, +}; + +static const uint32_t vop_csc_r2r_bt2020_to_bt709[] = { + 0x01510282, 0x0047002c, 0x000c03ae, 0x005a0011, + 0x00000394, 0x00000200, 0x00000200, 0x00000200, +}; + +static const struct vop_csc_table rk3399_csc_table = { + .y2r_bt601 = vop_csc_y2r_bt601, + .y2r_bt601_12_235 = vop_csc_y2r_bt601_12_235, + .r2y_bt601 = vop_csc_r2y_bt601, + .r2y_bt601_12_235 = vop_csc_r2y_bt601_12_235, + + .y2r_bt709 = vop_csc_y2r_bt709, + .r2y_bt709 = vop_csc_r2y_bt709, + + .y2r_bt2020 = vop_csc_y2r_bt2020, + .r2y_bt2020 = vop_csc_r2y_bt2020, + + .r2r_bt709_to_bt2020 = vop_csc_r2r_bt709_to_bt2020, + .r2r_bt2020_to_bt709 = vop_csc_r2r_bt2020_to_bt709, +}; + +static const struct vop_csc rk3399_win0_csc = { + .r2r_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 0), + .y2r_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 1), + .r2y_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 2), + .y2r_offset = RK3399_WIN0_YUV2YUV_Y2R, + .r2r_offset = RK3399_WIN0_YUV2YUV_3X3, + .r2y_offset = RK3399_WIN0_YUV2YUV_R2Y, +}; + +static const struct vop_csc rk3399_win1_csc = { + .r2r_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 8), + .y2r_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 9), + .r2y_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 10), + .y2r_offset = RK3399_WIN1_YUV2YUV_Y2R, + .r2r_offset = RK3399_WIN1_YUV2YUV_3X3, + .r2y_offset = RK3399_WIN1_YUV2YUV_R2Y, +}; + +static const struct vop_csc rk3399_win2_csc = { + .r2r_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 16), + .r2y_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 18), + .r2r_offset = RK3399_WIN2_YUV2YUV_3X3, + .csc_mode = VOP_REG(RK3399_YUV2YUV_WIN, 0x3, 22), +}; + +static const struct vop_csc rk3399_win3_csc = { + .r2r_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 24), + .r2y_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 26), + .r2r_offset = RK3399_WIN3_YUV2YUV_3X3, + .csc_mode = VOP_REG(RK3399_YUV2YUV_WIN, 0x3, 30), }; static const struct vop_win_phy rk3399_win01_data = { .scl = &rk3288_win_full_scl, - .data_formats = formats_win_full, - .nformats = ARRAY_SIZE(formats_win_full), - .format_modifiers = format_modifiers_win_full_afbc, + .data_formats = formats_win_full_10bit_yuyv, + .nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv), .enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0), .format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1), + .fmt_10 = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 4), + .fmt_yuyv = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 17), + .csc_mode = VOP_REG_VER(RK3288_WIN0_CTRL0, 0x3, 10, 3, 2, -1), .rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12), - .y_mir_en = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 22), + .xmirror = VOP_REG_VER(RK3368_WIN0_CTRL0, 0x1, 21, 3, 2, -1), + .ymirror = VOP_REG_VER(RK3368_WIN0_CTRL0, 0x1, 22, 3, 2, -1), .act_info = VOP_REG(RK3288_WIN0_ACT_INFO, 0x1fff1fff, 0), .dsp_info = VOP_REG(RK3288_WIN0_DSP_INFO, 0x0fff0fff, 0), .dsp_st = VOP_REG(RK3288_WIN0_DSP_ST, 0x1fff1fff, 0), @@ -876,76 +616,71 @@ static const struct vop_win_phy rk3399_win01_data = { .uv_mst = VOP_REG(RK3288_WIN0_CBR_MST, 0xffffffff, 0), .yrgb_vir = VOP_REG(RK3288_WIN0_VIR, 0x3fff, 0), .uv_vir = VOP_REG(RK3288_WIN0_VIR, 0x3fff, 16), - .src_alpha_ctl = VOP_REG(RK3288_WIN0_SRC_ALPHA_CTRL, 0xff, 0), - .dst_alpha_ctl = VOP_REG(RK3288_WIN0_DST_ALPHA_CTRL, 0xff, 0), + .src_alpha_ctl = VOP_REG(RK3288_WIN0_SRC_ALPHA_CTRL, 0xffff, 0), + .global_alpha_val = VOP_REG(RK3288_WIN0_SRC_ALPHA_CTRL, 0xff, 16), + .dst_alpha_ctl = VOP_REG(RK3288_WIN0_DST_ALPHA_CTRL, 0xffffffff, 0), + .channel = VOP_REG_VER(RK3288_WIN0_CTRL2, 0xff, 0, 3, 8, 8), }; -/* - * rk3399 vop big windows register layout is same as rk3288, but we - * have a separate rk3399 win data array here so that we can advertise - * AFBC on the primary plane. - */ static const struct vop_win_data rk3399_vop_win_data[] = { - { .base = 0x00, .phy = &rk3399_win01_data, - .type = DRM_PLANE_TYPE_PRIMARY }, - { .base = 0x40, .phy = &rk3288_win01_data, - .type = DRM_PLANE_TYPE_OVERLAY }, - { .base = 0x00, .phy = &rk3288_win23_data, - .type = DRM_PLANE_TYPE_OVERLAY }, - { .base = 0x50, .phy = &rk3288_win23_data, - .type = DRM_PLANE_TYPE_CURSOR }, -}; - -static const struct vop_afbc rk3399_vop_afbc = { - .rstn = VOP_REG(RK3399_AFBCD0_CTRL, 0x1, 3), - .enable = VOP_REG(RK3399_AFBCD0_CTRL, 0x1, 0), - .win_sel = VOP_REG(RK3399_AFBCD0_CTRL, 0x3, 1), - .format = VOP_REG(RK3399_AFBCD0_CTRL, 0x1f, 16), - .hreg_block_split = VOP_REG(RK3399_AFBCD0_CTRL, 0x1, 21), - .hdr_ptr = VOP_REG(RK3399_AFBCD0_HDR_PTR, 0xffffffff, 0), - .pic_size = VOP_REG(RK3399_AFBCD0_PIC_SIZE, 0xffffffff, 0), + { .base = 0x00, .phy = &rk3399_win01_data, .csc = &rk3399_win0_csc, + .type = DRM_PLANE_TYPE_PRIMARY, + .feature = WIN_FEATURE_AFBDC }, + { .base = 0x40, .phy = &rk3399_win01_data, .csc = &rk3399_win1_csc, + .type = DRM_PLANE_TYPE_OVERLAY, + .feature = WIN_FEATURE_AFBDC }, + { .base = 0x00, .phy = &rk3368_win23_data, .csc = &rk3399_win2_csc, + .type = DRM_PLANE_TYPE_OVERLAY, + .feature = WIN_FEATURE_AFBDC, + .area = rk3368_area_data, + .area_size = ARRAY_SIZE(rk3368_area_data), }, + { .base = 0x50, .phy = &rk3368_win23_data, .csc = &rk3399_win3_csc, + .type = DRM_PLANE_TYPE_CURSOR, + .feature = WIN_FEATURE_AFBDC, + .area = rk3368_area_data, + .area_size = ARRAY_SIZE(rk3368_area_data), }, }; static const struct vop_data rk3399_vop_big = { .version = VOP_VERSION(3, 5), - .feature = VOP_FEATURE_OUTPUT_RGB10, + .csc_table = &rk3399_csc_table, + .feature = VOP_FEATURE_OUTPUT_10BIT | VOP_FEATURE_AFBDC | + VOP_FEATURE_ALPHA_SCALE, + .max_input = {4096, 8192}, + .max_output = {4096, 2160}, .intr = &rk3366_vop_intr, - .common = &rk3288_common, - .modeset = &rk3288_modeset, - .output = &rk3399_output, - .afbc = &rk3399_vop_afbc, - .misc = &rk3368_misc, + .ctrl = &rk3288_ctrl_data, .win = rk3399_vop_win_data, .win_size = ARRAY_SIZE(rk3399_vop_win_data), - .win_yuv2yuv = rk3399_vop_big_win_yuv2yuv_data, }; static const struct vop_win_data rk3399_vop_lit_win_data[] = { - { .base = 0x00, .phy = &rk3368_win01_data, - .type = DRM_PLANE_TYPE_PRIMARY }, - { .base = 0x00, .phy = &rk3368_win23_data, - .type = DRM_PLANE_TYPE_CURSOR}, + { .base = 0x00, .phy = &rk3399_win01_data, .csc = &rk3399_win0_csc, + .type = DRM_PLANE_TYPE_OVERLAY, + .feature = WIN_FEATURE_AFBDC }, + { .phy = NULL }, + { .base = 0x00, .phy = &rk3368_win23_data, .csc = &rk3399_win2_csc, + .type = DRM_PLANE_TYPE_PRIMARY, + .feature = WIN_FEATURE_AFBDC, + .area = rk3368_area_data, + .area_size = ARRAY_SIZE(rk3368_area_data), }, + { .phy = NULL }, }; -static const struct vop_win_yuv2yuv_data rk3399_vop_lit_win_yuv2yuv_data[] = { - { .base = 0x00, .phy = &rk3399_yuv2yuv_win01_data, - .y2r_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 1)}, - { .base = 0x60, .phy = &rk3399_yuv2yuv_win23_data }, -}; static const struct vop_data rk3399_vop_lit = { .version = VOP_VERSION(3, 6), + .feature = VOP_FEATURE_ALPHA_SCALE, + .csc_table = &rk3399_csc_table, + .max_input = {4096, 8192}, + .max_output = {2560, 1600}, .intr = &rk3366_vop_intr, - .common = &rk3288_common, - .modeset = &rk3288_modeset, - .output = &rk3399_output, - .misc = &rk3368_misc, + .ctrl = &rk3288_ctrl_data, .win = rk3399_vop_lit_win_data, .win_size = ARRAY_SIZE(rk3399_vop_lit_win_data), - .win_yuv2yuv = rk3399_vop_lit_win_yuv2yuv_data, }; -static const struct vop_win_data rk3228_vop_win_data[] = { +static const struct vop_win_data rk322x_vop_win_data[] = { { .base = 0x00, .phy = &rk3288_win01_data, .type = DRM_PLANE_TYPE_PRIMARY }, { .base = 0x40, .phy = &rk3288_win01_data, @@ -954,54 +689,353 @@ static const struct vop_win_data rk3228_vop_win_data[] = { static const struct vop_data rk3228_vop = { .version = VOP_VERSION(3, 7), - .feature = VOP_FEATURE_OUTPUT_RGB10, + .feature = VOP_FEATURE_OUTPUT_10BIT | VOP_FEATURE_ALPHA_SCALE, + .max_input = {4096, 8192}, + .max_output = {4096, 2160}, .intr = &rk3366_vop_intr, - .common = &rk3288_common, - .modeset = &rk3288_modeset, - .output = &rk3399_output, - .misc = &rk3368_misc, - .win = rk3228_vop_win_data, - .win_size = ARRAY_SIZE(rk3228_vop_win_data), + .ctrl = &rk3288_ctrl_data, + .win = rk322x_vop_win_data, + .win_size = ARRAY_SIZE(rk322x_vop_win_data), }; -static const struct vop_modeset rk3328_modeset = { +static const u32 sdr2hdr_bt1886eotf_yn_for_hlg_hdr[65] = { + 0, + 1, 7, 17, 35, + 60, 92, 134, 184, + 244, 315, 396, 487, + 591, 706, 833, 915, + 1129, 1392, 1717, 2118, + 2352, 2612, 2900, 3221, + 3577, 3972, 4411, 4899, + 5441, 6042, 6710, 7452, + 7853, 8276, 8721, 9191, + 9685, 10207, 10756, 11335, + 11945, 12588, 13266, 13980, + 14732, 15525, 16361, 17241, + 17699, 18169, 18652, 19147, + 19656, 20178, 20714, 21264, + 21829, 22408, 23004, 23615, + 24242, 24886, 25547, 26214, +}; + +static const u32 sdr2hdr_bt1886eotf_yn_for_bt2020[65] = { + 0, + 1820, 3640, 5498, 7674, + 10256, 13253, 16678, 20539, + 24847, 29609, 34833, 40527, + 46699, 53354, 60499, 68141, + 76285, 84937, 94103, 103787, + 108825, 113995, 119296, 124731, + 130299, 136001, 141837, 147808, + 153915, 160158, 166538, 173055, + 176365, 179709, 183089, 186502, + 189951, 193434, 196952, 200505, + 204093, 207715, 211373, 215066, + 218795, 222558, 226357, 230191, + 232121, 234060, 236008, 237965, + 239931, 241906, 243889, 245882, + 247883, 249894, 251913, 253941, + 255978, 258024, 260079, 262143, +}; + +static u32 sdr2hdr_bt1886eotf_yn_for_hdr[65] = { + /* dst_range 425int */ + 0, + 5, 21, 49, 91, + 150, 225, 320, 434, + 569, 726, 905, 1108, + 1336, 1588, 1866, 2171, + 2502, 2862, 3250, 3667, + 3887, 4114, 4349, 4591, + 4841, 5099, 5364, 5638, + 5920, 6209, 6507, 6812, + 6968, 7126, 7287, 7449, + 7613, 7779, 7948, 8118, + 8291, 8466, 8643, 8822, + 9003, 9187, 9372, 9560, + 9655, 9750, 9846, 9942, + 10039, 10136, 10234, 10333, + 10432, 10531, 10631, 10732, + 10833, 10935, 11038, 11141, +}; + +static const u32 sdr2hdr_st2084oetf_yn_for_hlg_hdr[65] = { + 0, + 668, 910, 1217, 1600, + 2068, 2384, 2627, 3282, + 3710, 4033, 4879, 5416, + 5815, 6135, 6401, 6631, + 6833, 7176, 7462, 7707, + 7921, 8113, 8285, 8442, + 8586, 8843, 9068, 9268, + 9447, 9760, 10027, 10259, + 10465, 10650, 10817, 10971, + 11243, 11480, 11689, 11877, + 12047, 12202, 12345, 12477, + 12601, 12716, 12926, 13115, + 13285, 13441, 13583, 13716, + 13839, 13953, 14163, 14350, + 14519, 14673, 14945, 15180, + 15570, 15887, 16153, 16383, +}; + +static const u32 sdr2hdr_st2084oetf_yn_for_bt2020[65] = { + 0, + 0, 0, 1, 2, + 4, 6, 9, 18, + 27, 36, 72, 108, + 144, 180, 216, 252, + 288, 360, 432, 504, + 576, 648, 720, 792, + 864, 1008, 1152, 1296, + 1444, 1706, 1945, 2166, + 2372, 2566, 2750, 2924, + 3251, 3553, 3834, 4099, + 4350, 4588, 4816, 5035, + 5245, 5447, 5832, 6194, + 6536, 6862, 7173, 7471, + 7758, 8035, 8560, 9055, + 9523, 9968, 10800, 11569, + 12963, 14210, 15347, 16383, +}; + +static u32 sdr2hdr_st2084oetf_yn_for_hdr[65] = { + 0, + 281, 418, 610, 871, + 1217, 1464, 1662, 2218, + 2599, 2896, 3699, 4228, + 4628, 4953, 5227, 5466, + 5676, 6038, 6341, 6602, + 6833, 7039, 7226, 7396, + 7554, 7835, 8082, 8302, + 8501, 8848, 9145, 9405, + 9635, 9842, 10031, 10204, + 10512, 10779, 11017, 11230, + 11423, 11599, 11762, 11913, + 12054, 12185, 12426, 12641, + 12835, 13013, 13177, 13328, + 13469, 13600, 13840, 14055, + 14248, 14425, 14737, 15006, + 15453, 15816, 16121, 16383, +}; + +static const u32 sdr2hdr_st2084oetf_dxn_pow2[64] = { + 0, 0, 1, 2, + 3, 3, 3, 5, + 5, 5, 7, 7, + 7, 7, 7, 7, + 7, 8, 8, 8, + 8, 8, 8, 8, + 8, 9, 9, 9, + 9, 10, 10, 10, + 10, 10, 10, 10, + 11, 11, 11, 11, + 11, 11, 11, 11, + 11, 11, 12, 12, + 12, 12, 12, 12, + 12, 12, 13, 13, + 13, 13, 14, 14, + 15, 15, 15, 15, +}; + +static const u32 sdr2hdr_st2084oetf_dxn[64] = { + 1, 1, 2, 4, + 8, 8, 8, 32, + 32, 32, 128, 128, + 128, 128, 128, 128, + 128, 256, 256, 256, + 256, 256, 256, 256, + 256, 512, 512, 512, + 512, 1024, 1024, 1024, + 1024, 1024, 1024, 1024, + 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, + 2048, 2048, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 8192, 8192, + 8192, 8192, 16384, 16384, + 32768, 32768, 32768, 32768, +}; + +static const u32 sdr2hdr_st2084oetf_xn[63] = { + 1, 2, 4, 8, + 16, 24, 32, 64, + 96, 128, 256, 384, + 512, 640, 768, 896, + 1024, 1280, 1536, 1792, + 2048, 2304, 2560, 2816, + 3072, 3584, 4096, 4608, + 5120, 6144, 7168, 8192, + 9216, 10240, 11264, 12288, + 14336, 16384, 18432, 20480, + 22528, 24576, 26624, 28672, + 30720, 32768, 36864, 40960, + 45056, 49152, 53248, 57344, + 61440, 65536, 73728, 81920, + 90112, 98304, 114688, 131072, + 163840, 196608, 229376, +}; + +static u32 hdr2sdr_eetf_yn[33] = { + 1716, + 1880, 2067, 2277, 2508, + 2758, 3026, 3310, 3609, + 3921, 4246, 4581, 4925, + 5279, 5640, 6007, 6380, + 6758, 7140, 7526, 7914, + 8304, 8694, 9074, 9438, + 9779, 10093, 10373, 10615, + 10812, 10960, 11053, 11084, +}; + +static u32 hdr2sdr_bt1886oetf_yn[33] = { + 0, + 0, 0, 0, 0, + 0, 0, 0, 314, + 746, 1323, 2093, 2657, + 3120, 3519, 3874, 4196, + 4492, 5024, 5498, 5928, + 6323, 7034, 7666, 8239, + 8766, 9716, 10560, 11325, + 12029, 13296, 14422, 16383, +}; + +static const u32 hdr2sdr_sat_yn[9] = { + 0, + 1792, 3584, 3472, 2778, + 2083, 1389, 694, 0, +}; + +static const struct vop_hdr_table rk3328_hdr_table = { + .hdr2sdr_eetf_oetf_y0_offset = RK3328_HDR2SDR_EETF_OETF_Y0, + .hdr2sdr_eetf_oetf_y1_offset = RK3328_HDR2SDR_EETF_OETF_Y1, + .hdr2sdr_eetf_yn = hdr2sdr_eetf_yn, + .hdr2sdr_bt1886oetf_yn = hdr2sdr_bt1886oetf_yn, + .hdr2sdr_sat_y0_offset = RK3328_HDR2DR_SAT_Y0, + .hdr2sdr_sat_y1_offset = RK3328_HDR2DR_SAT_Y1, + .hdr2sdr_sat_yn = hdr2sdr_sat_yn, + + .hdr2sdr_src_range_min = 494, + .hdr2sdr_src_range_max = 12642, + .hdr2sdr_normfaceetf = 1327, + .hdr2sdr_dst_range_min = 4, + .hdr2sdr_dst_range_max = 3276, + .hdr2sdr_normfacgamma = 5120, + + .sdr2hdr_eotf_oetf_y0_offset = RK3328_SDR2HDR_EOTF_OETF_Y0, + .sdr2hdr_eotf_oetf_y1_offset = RK3328_SDR2HDR_EOTF_OETF_Y1, + .sdr2hdr_bt1886eotf_yn_for_hlg_hdr = sdr2hdr_bt1886eotf_yn_for_hlg_hdr, + .sdr2hdr_bt1886eotf_yn_for_bt2020 = sdr2hdr_bt1886eotf_yn_for_bt2020, + .sdr2hdr_bt1886eotf_yn_for_hdr = sdr2hdr_bt1886eotf_yn_for_hdr, + .sdr2hdr_st2084oetf_yn_for_hlg_hdr = sdr2hdr_st2084oetf_yn_for_hlg_hdr, + .sdr2hdr_st2084oetf_yn_for_bt2020 = sdr2hdr_st2084oetf_yn_for_bt2020, + .sdr2hdr_st2084oetf_yn_for_hdr = sdr2hdr_st2084oetf_yn_for_hdr, + .sdr2hdr_oetf_dx_dxpow1_offset = RK3328_SDR2HDR_OETF_DX_DXPOW1, + .sdr2hdr_oetf_xn1_offset = RK3328_SDR2HDR_OETF_XN1, + .sdr2hdr_st2084oetf_dxn_pow2 = sdr2hdr_st2084oetf_dxn_pow2, + .sdr2hdr_st2084oetf_dxn = sdr2hdr_st2084oetf_dxn, + .sdr2hdr_st2084oetf_xn = sdr2hdr_st2084oetf_xn, +}; + +static const struct vop_ctrl rk3328_ctrl_data = { + .standby = VOP_REG(RK3328_SYS_CTRL, 0x1, 22), + .dma_stop = VOP_REG(RK3328_SYS_CTRL, 0x1, 21), + .axi_outstanding_max_num = VOP_REG(RK3328_SYS_CTRL1, 0x1f, 13), + .axi_max_outstanding_en = VOP_REG(RK3328_SYS_CTRL1, 0x1, 12), + .reg_done_frm = VOP_REG(RK3328_SYS_CTRL1, 0x1, 24), + .auto_gate_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 23), .htotal_pw = VOP_REG(RK3328_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), .hact_st_end = VOP_REG(RK3328_DSP_HACT_ST_END, 0x1fff1fff, 0), .vtotal_pw = VOP_REG(RK3328_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), .vact_st_end = VOP_REG(RK3328_DSP_VACT_ST_END, 0x1fff1fff, 0), + .vact_st_end_f1 = VOP_REG(RK3328_DSP_VACT_ST_END_F1, 0x1fff1fff, 0), + .vs_st_end_f1 = VOP_REG(RK3328_DSP_VS_ST_END_F1, 0x1fff1fff, 0), .hpost_st_end = VOP_REG(RK3328_POST_DSP_HACT_INFO, 0x1fff1fff, 0), .vpost_st_end = VOP_REG(RK3328_POST_DSP_VACT_INFO, 0x1fff1fff, 0), -}; - -static const struct vop_output rk3328_output = { - .rgb_dclk_pol = VOP_REG(RK3328_DSP_CTRL1, 0x1, 19), - .hdmi_dclk_pol = VOP_REG(RK3328_DSP_CTRL1, 0x1, 23), - .edp_dclk_pol = VOP_REG(RK3328_DSP_CTRL1, 0x1, 27), - .mipi_dclk_pol = VOP_REG(RK3328_DSP_CTRL1, 0x1, 31), + .vpost_st_end_f1 = VOP_REG(RK3328_POST_DSP_VACT_INFO_F1, 0x1fff1fff, 0), + .post_scl_factor = VOP_REG(RK3328_POST_SCL_FACTOR_YRGB, 0xffffffff, 0), + .post_scl_ctrl = VOP_REG(RK3328_POST_SCL_CTRL, 0x3, 0), + .dsp_out_yuv = VOP_REG(RK3328_POST_SCL_CTRL, 0x1, 2), + .dsp_interlace = VOP_REG(RK3328_DSP_CTRL0, 0x1, 10), + .dsp_layer_sel = VOP_REG(RK3328_DSP_CTRL1, 0xff, 8), + .post_lb_mode = VOP_REG(RK3328_SYS_CTRL, 0x1, 18), + .global_regdone_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 11), + .overlay_mode = VOP_REG(RK3328_SYS_CTRL, 0x1, 16), + .core_dclk_div = VOP_REG(RK3328_DSP_CTRL0, 0x1, 4), + .dclk_ddr = VOP_REG(RK3328_DSP_CTRL0, 0x1, 8), + .p2i_en = VOP_REG(RK3328_DSP_CTRL0, 0x1, 5), .rgb_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 12), .hdmi_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 13), .edp_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 14), .mipi_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 15), + .tve_dclk_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 24), + .tve_dclk_pol = VOP_REG(RK3328_SYS_CTRL, 0x1, 25), + .tve_sw_mode = VOP_REG(RK3328_SYS_CTRL, 0x1, 26), + .sw_uv_offset_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 27), + .sw_genlock = VOP_REG(RK3328_SYS_CTRL, 0x1, 28), + .sw_dac_sel = VOP_REG(RK3328_SYS_CTRL, 0x1, 29), .rgb_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0x7, 16), .hdmi_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0x7, 20), .edp_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0x7, 24), .mipi_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0x7, 28), -}; + .rgb_dclk_pol = VOP_REG(RK3328_DSP_CTRL1, 0x1, 19), + .hdmi_dclk_pol = VOP_REG(RK3328_DSP_CTRL1, 0x1, 23), + .edp_dclk_pol = VOP_REG(RK3328_DSP_CTRL1, 0x1, 27), + .mipi_dclk_pol = VOP_REG(RK3328_DSP_CTRL1, 0x1, 31), -static const struct vop_misc rk3328_misc = { - .global_regdone_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 11), -}; - -static const struct vop_common rk3328_common = { - .standby = VOP_REG_SYNC(RK3328_SYS_CTRL, 0x1, 22), .dither_down_sel = VOP_REG(RK3328_DSP_CTRL1, 0x1, 4), .dither_down_mode = VOP_REG(RK3328_DSP_CTRL1, 0x1, 3), .dither_down_en = VOP_REG(RK3328_DSP_CTRL1, 0x1, 2), - .pre_dither_down = VOP_REG(RK3328_DSP_CTRL1, 0x1, 1), - .dither_up = VOP_REG(RK3328_DSP_CTRL1, 0x1, 6), + .pre_dither_down_en = VOP_REG(RK3328_DSP_CTRL1, 0x1, 1), + .dither_up_en = VOP_REG(RK3328_DSP_CTRL1, 0x1, 6), + + .dsp_data_swap = VOP_REG(RK3328_DSP_CTRL0, 0x1f, 12), + .dsp_ccir656_avg = VOP_REG(RK3328_DSP_CTRL0, 0x1, 20), .dsp_blank = VOP_REG(RK3328_DSP_CTRL0, 0x3, 18), + .dsp_lut_en = VOP_REG(RK3328_DSP_CTRL1, 0x1, 0), .out_mode = VOP_REG(RK3328_DSP_CTRL0, 0xf, 0), - .cfg_done = VOP_REG_SYNC(RK3328_REG_CFG_DONE, 0x1, 0), + + .xmirror = VOP_REG(RK3328_DSP_CTRL0, 0x1, 22), + .ymirror = VOP_REG(RK3328_DSP_CTRL0, 0x1, 23), + + .dsp_background = VOP_REG(RK3328_DSP_BG, 0xffffffff, 0), + + .alpha_hard_calc = VOP_REG(RK3328_SYS_CTRL1, 0x1, 27), + .level2_overlay_en = VOP_REG(RK3328_SYS_CTRL1, 0x1, 28), + + .hdr2sdr_en = VOP_REG(RK3328_HDR2DR_CTRL, 0x1, 0), + .hdr2sdr_en_win0_csc = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 9), + .hdr2sdr_src_min = VOP_REG(RK3328_HDR2DR_SRC_RANGE, 0x3fff, 0), + .hdr2sdr_src_max = VOP_REG(RK3328_HDR2DR_SRC_RANGE, 0x3fff, 16), + .hdr2sdr_normfaceetf = VOP_REG(RK3328_HDR2DR_NORMFACEETF, 0x7ff, 0), + .hdr2sdr_dst_min = VOP_REG(RK3328_HDR2DR_DST_RANGE, 0x3fff, 0), + .hdr2sdr_dst_max = VOP_REG(RK3328_HDR2DR_DST_RANGE, 0x3fff, 16), + .hdr2sdr_normfacgamma = VOP_REG(RK3328_HDR2DR_NORMFACGAMMA, 0xffff, 0), + + .bt1886eotf_pre_conv_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 0), + .rgb2rgb_pre_conv_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 1), + .rgb2rgb_pre_conv_mode = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 2), + .st2084oetf_pre_conv_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 3), + .bt1886eotf_post_conv_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 4), + .rgb2rgb_post_conv_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 5), + .rgb2rgb_post_conv_mode = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 6), + .st2084oetf_post_conv_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 7), + .win_csc_mode_sel = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 31), + + .bcsh_brightness = VOP_REG(RK3328_BCSH_BCS, 0xff, 0), + .bcsh_contrast = VOP_REG(RK3328_BCSH_BCS, 0x1ff, 8), + .bcsh_sat_con = VOP_REG(RK3328_BCSH_BCS, 0x3ff, 20), + .bcsh_out_mode = VOP_REG(RK3328_BCSH_BCS, 0x3, 30), + .bcsh_sin_hue = VOP_REG(RK3328_BCSH_H, 0x1ff, 0), + .bcsh_cos_hue = VOP_REG(RK3328_BCSH_H, 0x1ff, 16), + .bcsh_r2y_csc_mode = VOP_REG(RK3328_BCSH_CTRL, 0x3, 6), + .bcsh_r2y_en = VOP_REG(RK3328_BCSH_CTRL, 0x1, 4), + .bcsh_y2r_csc_mode = VOP_REG(RK3328_BCSH_CTRL, 0x3, 2), + .bcsh_y2r_en = VOP_REG(RK3328_BCSH_CTRL, 0x1, 0), + .bcsh_color_bar = VOP_REG(RK3328_BCSH_COLOR_BAR, 0xffffff, 8), + .bcsh_en = VOP_REG(RK3328_BCSH_COLOR_BAR, 0x1, 0), + + .cfg_done = VOP_REG(RK3328_REG_CFG_DONE, 0x1, 0), }; static const struct vop_intr rk3328_vop_intr = { @@ -1009,47 +1043,742 @@ static const struct vop_intr rk3328_vop_intr = { .nintrs = ARRAY_SIZE(rk3368_vop_intrs), .line_flag_num[0] = VOP_REG(RK3328_LINE_FLAG, 0xffff, 0), .line_flag_num[1] = VOP_REG(RK3328_LINE_FLAG, 0xffff, 16), - .status = VOP_REG_MASK_SYNC(RK3328_INTR_STATUS0, 0xffff, 0), - .enable = VOP_REG_MASK_SYNC(RK3328_INTR_EN0, 0xffff, 0), - .clear = VOP_REG_MASK_SYNC(RK3328_INTR_CLEAR0, 0xffff, 0), + .status = VOP_REG_MASK(RK3328_INTR_STATUS0, 0xffff, 0), + .enable = VOP_REG_MASK(RK3328_INTR_EN0, 0xffff, 0), + .clear = VOP_REG_MASK(RK3328_INTR_CLEAR0, 0xffff, 0), +}; + +static const struct vop_csc rk3328_win0_csc = { + .r2y_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 8), + .r2r_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 5), + .y2r_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 9), +}; + +static const struct vop_csc rk3328_win1_csc = { + .r2y_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 10), + .r2r_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 1), + .y2r_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 11), +}; + +static const struct vop_csc rk3328_win2_csc = { + .r2y_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 12), + .r2r_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 1), + .y2r_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 13), }; static const struct vop_win_data rk3328_vop_win_data[] = { - { .base = 0xd0, .phy = &rk3368_win01_data, - .type = DRM_PLANE_TYPE_PRIMARY }, - { .base = 0x1d0, .phy = &rk3368_win01_data, - .type = DRM_PLANE_TYPE_OVERLAY }, - { .base = 0x2d0, .phy = &rk3368_win01_data, - .type = DRM_PLANE_TYPE_CURSOR }, + { .base = 0xd0, .phy = &rk3288_win01_data, .csc = &rk3328_win0_csc, + .type = DRM_PLANE_TYPE_PRIMARY, + .feature = WIN_FEATURE_HDR2SDR | WIN_FEATURE_SDR2HDR }, + { .base = 0x1d0, .phy = &rk3288_win01_data, .csc = &rk3328_win1_csc, + .type = DRM_PLANE_TYPE_OVERLAY, + .feature = WIN_FEATURE_SDR2HDR | WIN_FEATURE_PRE_OVERLAY }, + { .base = 0x2d0, .phy = &rk3288_win01_data, .csc = &rk3328_win2_csc, + .type = DRM_PLANE_TYPE_CURSOR, + .feature = WIN_FEATURE_SDR2HDR | WIN_FEATURE_PRE_OVERLAY }, }; static const struct vop_data rk3328_vop = { .version = VOP_VERSION(3, 8), - .feature = VOP_FEATURE_OUTPUT_RGB10, + .feature = VOP_FEATURE_OUTPUT_10BIT | VOP_FEATURE_ALPHA_SCALE, + .hdr_table = &rk3328_hdr_table, + .max_input = {4096, 8192}, + .max_output = {4096, 2160}, .intr = &rk3328_vop_intr, - .common = &rk3328_common, - .modeset = &rk3328_modeset, - .output = &rk3328_output, - .misc = &rk3328_misc, + .ctrl = &rk3328_ctrl_data, .win = rk3328_vop_win_data, .win_size = ARRAY_SIZE(rk3328_vop_win_data), }; +static const struct vop_scl_regs rk3036_win0_scl = { + .scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0), + .scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16), + .scale_cbcr_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0), + .scale_cbcr_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 16), +}; + +static const struct vop_scl_regs rk3036_win1_scl = { + .scale_yrgb_x = VOP_REG(RK3036_WIN1_SCL_FACTOR_YRGB, 0xffff, 0x0), + .scale_yrgb_y = VOP_REG(RK3036_WIN1_SCL_FACTOR_YRGB, 0xffff, 16), +}; + +static const struct vop_win_phy rk3036_win0_data = { + .scl = &rk3036_win0_scl, + .data_formats = formats_win_full, + .nformats = ARRAY_SIZE(formats_win_full), + .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 0), + .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 3), + .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 15), + .act_info = VOP_REG(RK3036_WIN0_ACT_INFO, 0x1fff1fff, 0), + .dsp_info = VOP_REG(RK3036_WIN0_DSP_INFO, 0x0fff0fff, 0), + .dsp_st = VOP_REG(RK3036_WIN0_DSP_ST, 0x1fff1fff, 0), + .yrgb_mst = VOP_REG(RK3036_WIN0_YRGB_MST, 0xffffffff, 0), + .uv_mst = VOP_REG(RK3036_WIN0_CBR_MST, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3036_WIN0_VIR, 0xffff, 0), + .uv_vir = VOP_REG(RK3036_WIN0_VIR, 0x1fff, 16), + .alpha_mode = VOP_REG(RK3036_DSP_CTRL0, 0x1, 18), + .alpha_en = VOP_REG(RK3036_ALPHA_CTRL, 0x1, 0), + .alpha_pre_mul = VOP_REG(RK3036_DSP_CTRL0, 0x1, 29), +}; + +static const struct vop_win_phy rk3036_win1_data = { + .scl = &rk3036_win1_scl, + .data_formats = formats_win_lite, + .nformats = ARRAY_SIZE(formats_win_lite), + .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1), + .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6), + .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19), + .act_info = VOP_REG(RK3036_WIN1_ACT_INFO, 0x1fff1fff, 0), + .dsp_info = VOP_REG(RK3036_WIN1_DSP_INFO, 0x0fff0fff, 0), + .dsp_st = VOP_REG(RK3036_WIN1_DSP_ST, 0x1fff1fff, 0), + .yrgb_mst = VOP_REG(RK3036_WIN1_MST, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3036_WIN1_VIR, 0xffff, 0), + .alpha_mode = VOP_REG(RK3036_DSP_CTRL0, 0x1, 19), + .alpha_en = VOP_REG(RK3036_ALPHA_CTRL, 0x1, 1) +}; + +static const struct vop_win_data rk3036_vop_win_data[] = { + { .base = 0x00, .phy = &rk3036_win0_data, + .type = DRM_PLANE_TYPE_PRIMARY }, + { .base = 0x00, .phy = &rk3036_win1_data, + .type = DRM_PLANE_TYPE_OVERLAY }, +}; + +static const int rk3036_vop_intrs[] = { + DSP_HOLD_VALID_INTR, + FS_INTR, + LINE_FLAG_INTR, + BUS_ERROR_INTR, +}; + +static const struct vop_intr rk3036_intr = { + .intrs = rk3036_vop_intrs, + .nintrs = ARRAY_SIZE(rk3036_vop_intrs), + .line_flag_num[0] = VOP_REG(RK3036_INT_STATUS, 0xfff, 12), + .status = VOP_REG(RK3036_INT_STATUS, 0xf, 0), + .enable = VOP_REG(RK3036_INT_STATUS, 0xf, 4), + .clear = VOP_REG(RK3036_INT_STATUS, 0xf, 8), +}; + +static const struct vop_ctrl rk3036_ctrl_data = { + .standby = VOP_REG(RK3036_SYS_CTRL, 0x1, 30), + .out_mode = VOP_REG(RK3036_DSP_CTRL0, 0xf, 0), + .dsp_blank = VOP_REG(RK3036_DSP_CTRL1, 0x1, 24), + .dclk_pol = VOP_REG(RK3036_DSP_CTRL0, 0x1, 7), + .pin_pol = VOP_REG(RK3036_DSP_CTRL0, 0x7, 4), + .dither_down_sel = VOP_REG(RK3036_DSP_CTRL0, 0x1, 27), + .dither_down_en = VOP_REG(RK3036_DSP_CTRL0, 0x1, 11), + .dither_down_mode = VOP_REG(RK3036_DSP_CTRL0, 0x1, 10), + .dither_up_en = VOP_REG(RK3036_DSP_CTRL0, 0x1, 9), + .dsp_layer_sel = VOP_REG(RK3036_DSP_CTRL0, 0x1, 8), + .htotal_pw = VOP_REG(RK3036_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), + .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0), + .hdmi_en = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 22), + .hdmi_dclk_pol = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 23), + .hdmi_pin_pol = VOP_REG(RK3036_INT_SCALER, 0x7, 4), + .rgb_en = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 24), + .rgb_dclk_pol = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 25), + .lvds_en = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 26), + .lvds_dclk_pol = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 27), + .mipi_en = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 28), + .mipi_dclk_pol = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 29), + .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), + .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0), + .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0), +}; + +static const struct vop_data rk3036_vop = { + .version = VOP_VERSION(2, 2), + .max_input = {1920, 1080}, + .max_output = {1920, 1080}, + .ctrl = &rk3036_ctrl_data, + .intr = &rk3036_intr, + .win = rk3036_vop_win_data, + .win_size = ARRAY_SIZE(rk3036_vop_win_data), +}; + +static const struct vop_scl_regs rk3066_win_scl = { + .scale_yrgb_x = VOP_REG(RK3066_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0), + .scale_yrgb_y = VOP_REG(RK3066_WIN0_SCL_FACTOR_YRGB, 0xffff, 16), + .scale_cbcr_x = VOP_REG(RK3066_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0), + .scale_cbcr_y = VOP_REG(RK3066_WIN0_SCL_FACTOR_CBR, 0xffff, 16), +}; + +static const struct vop_win_phy rk3066_win0_data = { + .scl = &rk3066_win_scl, + .data_formats = formats_win_full, + .nformats = ARRAY_SIZE(formats_win_full), + .enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 0), + .format = VOP_REG(RK3066_SYS_CTRL0, 0x7, 4), + .rb_swap = VOP_REG(RK3066_SYS_CTRL0, 0x1, 19), + .act_info = VOP_REG(RK3066_WIN0_ACT_INFO, 0x1fff1fff, 0), + .dsp_info = VOP_REG(RK3066_WIN0_DSP_INFO, 0x0fff0fff, 0), + .dsp_st = VOP_REG(RK3066_WIN0_DSP_ST, 0x1fff1fff, 0), + .yrgb_mst = VOP_REG(RK3066_WIN0_YRGB_MST0, 0xffffffff, 0), + .uv_mst = VOP_REG(RK3066_WIN0_CBR_MST0, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3066_WIN0_VIR, 0xffff, 0), + .uv_vir = VOP_REG(RK3066_WIN0_VIR, 0x1fff, 16), + .alpha_mode = VOP_REG(RK3066_DSP_CTRL0, 0x1, 21), + .alpha_en = VOP_REG(RK3066_BLEND_CTRL, 0x1, 0) +}; + +static const struct vop_win_phy rk3066_win1_data = { + .scl = &rk3066_win_scl, + .data_formats = formats_win_full, + .nformats = ARRAY_SIZE(formats_win_full), + .enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 1), + .format = VOP_REG(RK3066_SYS_CTRL0, 0x7, 7), + .rb_swap = VOP_REG(RK3066_SYS_CTRL0, 0x1, 23), + .act_info = VOP_REG(RK3066_WIN1_ACT_INFO, 0x1fff1fff, 0), + .dsp_info = VOP_REG(RK3066_WIN1_DSP_INFO, 0x0fff0fff, 0), + .dsp_st = VOP_REG(RK3066_WIN1_DSP_ST, 0x1fff1fff, 0), + .yrgb_mst = VOP_REG(RK3066_WIN1_YRGB_MST, 0xffffffff, 0), + .uv_mst = VOP_REG(RK3066_WIN1_CBR_MST, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3066_WIN1_VIR, 0xffff, 0), + .uv_vir = VOP_REG(RK3066_WIN1_VIR, 0x1fff, 16), + .alpha_mode = VOP_REG(RK3066_DSP_CTRL0, 0x1, 22), + .alpha_en = VOP_REG(RK3066_BLEND_CTRL, 0x1, 1) +}; + +static const struct vop_win_phy rk3066_win2_data = { + .data_formats = formats_win_lite, + .nformats = ARRAY_SIZE(formats_win_lite), + .enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 2), + .format = VOP_REG(RK3066_SYS_CTRL0, 0x7, 10), + .rb_swap = VOP_REG(RK3066_SYS_CTRL0, 0x1, 27), + .dsp_info = VOP_REG(RK3066_WIN2_DSP_INFO, 0x0fff0fff, 0), + .dsp_st = VOP_REG(RK3066_WIN2_DSP_ST, 0x1fff1fff, 0), + .yrgb_mst = VOP_REG(RK3066_WIN2_MST, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3066_WIN2_VIR, 0xffff, 0), + .alpha_mode = VOP_REG(RK3066_DSP_CTRL0, 0x1, 23), + .alpha_en = VOP_REG(RK3066_BLEND_CTRL, 0x1, 2) +}; + +static const struct vop_win_data rk3066_vop_win_data[] = { + { .base = 0x00, .phy = &rk3066_win0_data, + .type = DRM_PLANE_TYPE_PRIMARY }, + { .base = 0x00, .phy = &rk3066_win1_data, + .type = DRM_PLANE_TYPE_OVERLAY }, + { .base = 0x00, .phy = &rk3066_win2_data, + .type = DRM_PLANE_TYPE_CURSOR }, +}; + +static const int rk3066_vop_intrs[] = { + 0, + FS_INTR, + LINE_FLAG_INTR, + BUS_ERROR_INTR, +}; + +static const struct vop_intr rk3066_intr = { + .intrs = rk3066_vop_intrs, + .nintrs = ARRAY_SIZE(rk3066_vop_intrs), + .line_flag_num[0] = VOP_REG(RK3066_INT_STATUS, 0xfff, 12), + .status = VOP_REG(RK3066_INT_STATUS, 0xf, 0), + .enable = VOP_REG(RK3066_INT_STATUS, 0xf, 4), + .clear = VOP_REG(RK3066_INT_STATUS, 0xf, 8), +}; + +static const struct vop_ctrl rk3066_ctrl_data = { + .standby = VOP_REG(RK3066_SYS_CTRL0, 0x1, 1), + .out_mode = VOP_REG(RK3066_DSP_CTRL0, 0xf, 0), + .dsp_blank = VOP_REG(RK3066_DSP_CTRL1, 0x1, 24), + .dclk_pol = VOP_REG(RK3066_DSP_CTRL0, 0x1, 7), + .pin_pol = VOP_REG(RK3066_DSP_CTRL0, 0x7, 4), + .dsp_layer_sel = VOP_REG(RK3066_DSP_CTRL0, 0x1, 8), + .htotal_pw = VOP_REG(RK3066_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), + .hact_st_end = VOP_REG(RK3066_DSP_HACT_ST_END, 0x1fff1fff, 0), + .vtotal_pw = VOP_REG(RK3066_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), + .vact_st_end = VOP_REG(RK3066_DSP_VACT_ST_END, 0x1fff1fff, 0), + .cfg_done = VOP_REG(RK3066_REG_CFG_DONE, 0x1, 0), +}; + +static const struct vop_data rk3066_vop = { + .version = VOP_VERSION(2, 1), + .max_input = {1920, 4096}, + .max_output = {1920, 1080}, + .ctrl = &rk3066_ctrl_data, + .intr = &rk3066_intr, + .win = rk3066_vop_win_data, + .win_size = ARRAY_SIZE(rk3066_vop_win_data), +}; + +static const int rk3366_vop_lit_intrs[] = { + FS_INTR, + FS_NEW_INTR, + ADDR_SAME_INTR, + LINE_FLAG_INTR, + LINE_FLAG1_INTR, + BUS_ERROR_INTR, + WIN0_EMPTY_INTR, + WIN1_EMPTY_INTR, + DSP_HOLD_VALID_INTR, + DMA_FINISH_INTR, + WIN2_EMPTY_INTR, + POST_BUF_EMPTY_INTR +}; + +static const struct vop_scl_regs rk3366_lit_win_scl = { + .scale_yrgb_x = VOP_REG(RK3366_LIT_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0), + .scale_yrgb_y = VOP_REG(RK3366_LIT_WIN0_SCL_FACTOR_YRGB, 0xffff, 16), + .scale_cbcr_x = VOP_REG(RK3366_LIT_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0), + .scale_cbcr_y = VOP_REG(RK3366_LIT_WIN0_SCL_FACTOR_CBR, 0xffff, 16), +}; + +static const struct vop_win_phy rk3366_lit_win0_data = { + .scl = &rk3366_lit_win_scl, + .data_formats = formats_win_full, + .nformats = ARRAY_SIZE(formats_win_full), + + .enable = VOP_REG(RK3366_LIT_WIN0_CTRL0, 0x1, 0), + .format = VOP_REG(RK3366_LIT_WIN0_CTRL0, 0x7, 1), + .rb_swap = VOP_REG(RK3366_LIT_WIN0_CTRL0, 0x1, 12), + .act_info = VOP_REG(RK3366_LIT_WIN0_ACT_INFO, 0xffffffff, 0), + .dsp_info = VOP_REG(RK3366_LIT_WIN0_DSP_INFO, 0xffffffff, 0), + .dsp_st = VOP_REG(RK3366_LIT_WIN0_DSP_ST, 0xffffffff, 0), + .yrgb_mst = VOP_REG(RK3366_LIT_WIN0_YRGB_MST0, 0xffffffff, 0), + .uv_mst = VOP_REG(RK3366_LIT_WIN0_CBR_MST0, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3366_LIT_WIN0_VIR, 0x1fff, 0), + .uv_vir = VOP_REG(RK3366_LIT_WIN0_VIR, 0x1fff, 16), + + .alpha_pre_mul = VOP_REG(RK3366_LIT_WIN0_ALPHA_CTRL, 0x1, 2), + .alpha_mode = VOP_REG(RK3366_LIT_WIN0_ALPHA_CTRL, 0x1, 1), + .alpha_en = VOP_REG(RK3366_LIT_WIN0_ALPHA_CTRL, 0x1, 0), + .global_alpha_val = VOP_REG(RK3366_LIT_WIN0_ALPHA_CTRL, 0xff, 4), + .key_color = VOP_REG(RK3366_LIT_WIN0_COLOR_KEY, 0xffffff, 0), + .key_en = VOP_REG(RK3366_LIT_WIN0_COLOR_KEY, 0x1, 24), +}; + +static const struct vop_win_phy rk3366_lit_win1_data = { + .data_formats = formats_win_lite, + .nformats = ARRAY_SIZE(formats_win_lite), + + .enable = VOP_REG(RK3366_LIT_WIN1_CTRL0, 0x1, 0), + .format = VOP_REG(RK3366_LIT_WIN1_CTRL0, 0x7, 4), + .rb_swap = VOP_REG(RK3366_LIT_WIN1_CTRL0, 0x1, 12), + .dsp_info = VOP_REG(RK3366_LIT_WIN1_DSP_INFO, 0xffffffff, 0), + .dsp_st = VOP_REG(RK3366_LIT_WIN1_DSP_ST, 0xffffffff, 0), + .yrgb_mst = VOP_REG(RK3366_LIT_WIN1_MST, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3366_LIT_WIN1_VIR, 0x1fff, 0), + + .alpha_pre_mul = VOP_REG(RK3366_LIT_WIN1_ALPHA_CTRL, 0x1, 2), + .alpha_mode = VOP_REG(RK3366_LIT_WIN1_ALPHA_CTRL, 0x1, 1), + .alpha_en = VOP_REG(RK3366_LIT_WIN1_ALPHA_CTRL, 0x1, 0), + .global_alpha_val = VOP_REG(RK3366_LIT_WIN1_ALPHA_CTRL, 0xff, 4), + .key_color = VOP_REG(RK3366_LIT_WIN1_COLOR_KEY, 0xffffff, 0), + .key_en = VOP_REG(RK3366_LIT_WIN1_COLOR_KEY, 0x1, 24), +}; + +static const struct vop_win_data rk3366_vop_lit_win_data[] = { + { .base = 0x00, .phy = &rk3366_lit_win0_data, + .type = DRM_PLANE_TYPE_PRIMARY }, + { .base = 0x00, .phy = &rk3366_lit_win1_data, + .type = DRM_PLANE_TYPE_CURSOR }, +}; + +static const struct vop_intr rk3366_lit_intr = { + .intrs = rk3366_vop_lit_intrs, + .nintrs = ARRAY_SIZE(rk3366_vop_lit_intrs), + .line_flag_num[0] = VOP_REG(RK3366_LIT_LINE_FLAG, 0xfff, 0), + .line_flag_num[1] = VOP_REG(RK3366_LIT_LINE_FLAG, 0xfff, 16), + .status = VOP_REG_MASK(RK3366_LIT_INTR_STATUS, 0xffff, 0), + .enable = VOP_REG_MASK(RK3366_LIT_INTR_EN, 0xffff, 0), + .clear = VOP_REG_MASK(RK3366_LIT_INTR_CLEAR, 0xffff, 0), +}; + +static const struct vop_win_phy rk3126_win1_data = { + .data_formats = formats_win_lite, + .nformats = ARRAY_SIZE(formats_win_lite), + .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1), + .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6), + .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19), + .dsp_info = VOP_REG(RK3126_WIN1_DSP_INFO, 0x0fff0fff, 0), + .dsp_st = VOP_REG(RK3126_WIN1_DSP_ST, 0x1fff1fff, 0), + .yrgb_mst = VOP_REG(RK3126_WIN1_MST, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3036_WIN1_VIR, 0xffff, 0), + .alpha_mode = VOP_REG(RK3036_DSP_CTRL0, 0x1, 19), + .alpha_en = VOP_REG(RK3036_ALPHA_CTRL, 0x1, 1), + .alpha_pre_mul = VOP_REG(RK3036_DSP_CTRL0, 0x1, 29), +}; + +static const struct vop_win_data rk3126_vop_win_data[] = { + { .base = 0x00, .phy = &rk3036_win0_data, + .type = DRM_PLANE_TYPE_OVERLAY }, + { .base = 0x00, .phy = &rk3126_win1_data, + .type = DRM_PLANE_TYPE_PRIMARY }, +}; + +static const struct vop_data rk3126_vop = { + .version = VOP_VERSION(2, 4), + .max_input = {1920, 8192}, + .max_output = {1920, 1080}, + .ctrl = &rk3036_ctrl_data, + .intr = &rk3036_intr, + .win = rk3126_vop_win_data, + .win_size = ARRAY_SIZE(rk3126_vop_win_data), +}; + +/* PX30 VOPB win2 is same with RK3368, + * but RK3368 win2 register offset is 0xb0 and px30 is 0x190, + * so we set the PX30 VOPB win2 base = 0x190 - 0xb0 = 0xe0 + */ + +static const struct vop_ctrl px30_ctrl_data = { + .standby = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 1), + .axi_outstanding_max_num = VOP_REG(RK3366_LIT_SYS_CTRL1, 0x1f, 16), + .axi_max_outstanding_en = VOP_REG(RK3366_LIT_SYS_CTRL1, 0x1, 12), + .htotal_pw = VOP_REG(RK3366_LIT_DSP_HTOTAL_HS_END, 0x0fff0fff, 0), + .hact_st_end = VOP_REG(RK3366_LIT_DSP_HACT_ST_END, 0x0fff0fff, 0), + .vtotal_pw = VOP_REG(RK3366_LIT_DSP_VTOTAL_VS_END, 0x0fff0fff, 0), + .vact_st_end = VOP_REG(RK3366_LIT_DSP_VACT_ST_END, 0x0fff0fff, 0), + .vact_st_end_f1 = VOP_REG(RK3366_LIT_DSP_VACT_ST_END_F1, 0x0fff0fff, 0), + .vs_st_end_f1 = VOP_REG(RK3366_LIT_DSP_VS_ST_END_F1, 0x0fff0fff, 0), + .dsp_interlace = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 0), + .global_regdone_en = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 13), + .auto_gate_en = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 0), + .dsp_layer_sel = VOP_REG(RK3366_LIT_DSP_CTRL2, 0xff, 22), + .overlay_mode = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 4), + .core_dclk_div = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 13), + .dclk_ddr = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 14), + .rgb_en = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 0), + .rgb_pin_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x7, 2), + .hdmi_en = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 8), + .hdmi_pin_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x7, 10), + .lvds_en = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 16), + .lvds_pin_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x7, 18), + .mipi_en = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 24), + .mipi_pin_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x7, 26), + .mipi_dclk_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 25), + .lvds_dclk_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 17), + .hdmi_dclk_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 9), + .rgb_dclk_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 1), + .dither_down_en = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 8), + .dither_down_sel = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 7), + .dither_down_mode = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 6), + .dither_up_en = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 2), + .dsp_data_swap = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1f, 9), + .dsp_ccir656_avg = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 5), + .dsp_black = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 15), + .dsp_blank = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 14), + .dsp_outzero = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 3), + .dsp_lut_en = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 5), + .out_mode = VOP_REG(RK3366_LIT_DSP_CTRL2, 0xf, 16), + .dsp_background = VOP_REG(RK3366_LIT_DSP_BG, 0x00ffffff, 0), + .cfg_done = VOP_REG(RK3366_LIT_REG_CFG_DONE, 0x1, 0), + + .bcsh_en = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 0), + .bcsh_r2y_csc_mode = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 1), + .bcsh_out_mode = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x3, 2), + .bcsh_y2r_csc_mode = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x3, 4), + .bcsh_y2r_en = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 6), + .bcsh_r2y_en = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 7), + .bcsh_color_bar = VOP_REG(RK3366_LIT_BCSH_COL_BAR, 0xffffff, 0), + .bcsh_brightness = VOP_REG(RK3366_LIT_BCSH_BCS, 0xff, 0), + .bcsh_contrast = VOP_REG(RK3366_LIT_BCSH_BCS, 0x1ff, 8), + .bcsh_sat_con = VOP_REG(RK3366_LIT_BCSH_BCS, 0x3ff, 20), + .bcsh_sin_hue = VOP_REG(RK3366_LIT_BCSH_H, 0x1ff, 0), + .bcsh_cos_hue = VOP_REG(RK3366_LIT_BCSH_H, 0x1ff, 16), + + .afbdc_en = VOP_REG(PX30_AFBCD0_CTRL, 0x1, 0), + .afbdc_format = VOP_REG(PX30_AFBCD0_CTRL, 0x1f, 4), + .afbdc_pic_vir_width = VOP_REG(PX30_AFBCD0_CTRL, 0xffff, 16), + .afbdc_hdr_ptr = VOP_REG(PX30_AFBCD0_HDR_PTR, 0xffffffff, 0), + .afbdc_pic_size = VOP_REG(PX30_AFBCD0_PIC_SIZE, 0xffffffff, 0), + .afbdc_pic_offset = VOP_REG(PX30_AFBCD0_PIC_OFFSET, 0xffffffff, 0), + .afbdc_axi_ctrl = VOP_REG(PX30_AFBCD0_AXI_CTRL, 0xffffffff, 0), + + .mcu_pix_total = VOP_REG(RK3366_LIT_MCU_CTRL, 0x3f, 0), + .mcu_cs_pst = VOP_REG(RK3366_LIT_MCU_CTRL, 0xf, 6), + .mcu_cs_pend = VOP_REG(RK3366_LIT_MCU_CTRL, 0x3f, 10), + .mcu_rw_pst = VOP_REG(RK3366_LIT_MCU_CTRL, 0xf, 16), + .mcu_rw_pend = VOP_REG(RK3366_LIT_MCU_CTRL, 0x3f, 20), + .mcu_clk_sel = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 26), + .mcu_hold_mode = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 27), + .mcu_frame_st = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 28), + .mcu_rs = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 29), + .mcu_bypass = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 30), + .mcu_type = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 31), + .mcu_rw_bypass_port = VOP_REG(RK3366_LIT_MCU_RW_BYPASS_PORT, + 0xffffffff, 0), +}; + +static const struct vop_win_phy px30_win23_data = { + .data_formats = formats_win_lite, + .nformats = ARRAY_SIZE(formats_win_lite), + .gate = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 0), + .enable = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 4), + .format = VOP_REG(RK3368_WIN2_CTRL0, 0x3, 5), + .rb_swap = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 20), + .dsp_info = VOP_REG(RK3368_WIN2_DSP_INFO0, 0x0fff0fff, 0), + .dsp_st = VOP_REG(RK3368_WIN2_DSP_ST0, 0x1fff1fff, 0), + .yrgb_mst = VOP_REG(RK3368_WIN2_MST0, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3368_WIN2_VIR0_1, 0x1fff, 0), + .alpha_pre_mul = VOP_REG(RK3368_WIN2_SRC_ALPHA_CTRL, 0x1, 2), + .alpha_mode = VOP_REG(RK3368_WIN2_SRC_ALPHA_CTRL, 0x1, 1), + .alpha_en = VOP_REG(RK3368_WIN2_SRC_ALPHA_CTRL, 0x1, 0), + .global_alpha_val = VOP_REG(RK3368_WIN2_SRC_ALPHA_CTRL, 0xff, 4), + .channel = VOP_REG(RK3368_WIN2_CTRL1, 0xf, 8), +}; + +static const struct vop_win_data px30_vop_big_win_data[] = { + { .base = 0x00, .phy = &rk3366_lit_win0_data, + .type = DRM_PLANE_TYPE_OVERLAY }, + { .base = 0x00, .phy = &rk3366_lit_win1_data, + .type = DRM_PLANE_TYPE_PRIMARY, + .feature = WIN_FEATURE_AFBDC }, + { .base = 0xe0, .phy = &px30_win23_data, + .type = DRM_PLANE_TYPE_CURSOR, + .area = rk3368_area_data, + .area_size = ARRAY_SIZE(rk3368_area_data), }, +}; + +static const struct vop_win_data px30_vop_lit_win_data[] = { + { .phy = NULL }, + { .base = 0x00, .phy = &rk3366_lit_win1_data, + .type = DRM_PLANE_TYPE_PRIMARY }, + { .phy = NULL }, +}; + +static const struct vop_grf_ctrl px30_grf_ctrl = { + .grf_dclk_inv = VOP_REG(PX30_GRF_PD_VO_CON1, 0x1, 4), +}; + +static const struct vop_data px30_vop_lit = { + .version = VOP_VERSION(2, 5), + .max_input = {1920, 8192}, + .max_output = {1920, 1080}, + .ctrl = &px30_ctrl_data, + .intr = &rk3366_lit_intr, + .grf_ctrl = &px30_grf_ctrl, + .win = px30_vop_lit_win_data, + .win_size = ARRAY_SIZE(px30_vop_lit_win_data), +}; + +static const struct vop_data px30_vop_big = { + .version = VOP_VERSION(2, 6), + .feature = VOP_FEATURE_AFBDC, + .max_input = {1920, 8192}, + .max_output = {1920, 1080}, + .ctrl = &px30_ctrl_data, + .intr = &rk3366_lit_intr, + .grf_ctrl = &px30_grf_ctrl, + .win = px30_vop_big_win_data, + .win_size = ARRAY_SIZE(px30_vop_big_win_data), +}; + +static const struct vop_ctrl rk3308_ctrl_data = { + .standby = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 1), + .axi_outstanding_max_num = VOP_REG(RK3366_LIT_SYS_CTRL1, 0x1f, 16), + .axi_max_outstanding_en = VOP_REG(RK3366_LIT_SYS_CTRL1, 0x1, 12), + .htotal_pw = VOP_REG(RK3366_LIT_DSP_HTOTAL_HS_END, 0x0fff0fff, 0), + .hact_st_end = VOP_REG(RK3366_LIT_DSP_HACT_ST_END, 0x0fff0fff, 0), + .vtotal_pw = VOP_REG(RK3366_LIT_DSP_VTOTAL_VS_END, 0x0fff0fff, 0), + .vact_st_end = VOP_REG(RK3366_LIT_DSP_VACT_ST_END, 0x0fff0fff, 0), + .vact_st_end_f1 = VOP_REG(RK3366_LIT_DSP_VACT_ST_END_F1, 0x0fff0fff, 0), + .vs_st_end_f1 = VOP_REG(RK3366_LIT_DSP_VS_ST_END_F1, 0x0fff0fff, 0), + .global_regdone_en = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 13), + .auto_gate_en = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 0), + .dsp_layer_sel = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 3), + .overlay_mode = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 4), + .dclk_ddr = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 14), + .rgb_en = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 0), + .rgb_pin_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x7, 2), + .rgb_dclk_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 1), + .dither_down_en = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 8), + .dither_down_sel = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 7), + .dither_down_mode = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 6), + .dither_up_en = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 2), + .dsp_data_swap = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1f, 9), + .dsp_ccir656_avg = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 5), + .dsp_black = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 15), + .dsp_blank = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 14), + .dsp_outzero = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 3), + .dsp_lut_en = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 5), + .out_mode = VOP_REG(RK3366_LIT_DSP_CTRL2, 0xf, 16), + .dsp_background = VOP_REG(RK3366_LIT_DSP_BG, 0x00ffffff, 0), + .cfg_done = VOP_REG(RK3366_LIT_REG_CFG_DONE, 0x1, 0), + + .bcsh_en = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 0), + .bcsh_r2y_csc_mode = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 1), + .bcsh_out_mode = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x3, 2), + .bcsh_y2r_csc_mode = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x3, 4), + .bcsh_y2r_en = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 6), + .bcsh_r2y_en = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 7), + .bcsh_color_bar = VOP_REG(RK3366_LIT_BCSH_COL_BAR, 0xffffff, 0), + .bcsh_brightness = VOP_REG(RK3366_LIT_BCSH_BCS, 0x3f, 0), + .bcsh_contrast = VOP_REG(RK3366_LIT_BCSH_BCS, 0xff, 8), + .bcsh_sat_con = VOP_REG(RK3366_LIT_BCSH_BCS, 0x1ff, 16), + .bcsh_sin_hue = VOP_REG(RK3366_LIT_BCSH_H, 0xff, 0), + .bcsh_cos_hue = VOP_REG(RK3366_LIT_BCSH_H, 0xff, 8), + + .mcu_pix_total = VOP_REG(RK3366_LIT_MCU_CTRL, 0x3f, 0), + .mcu_cs_pst = VOP_REG(RK3366_LIT_MCU_CTRL, 0xf, 6), + .mcu_cs_pend = VOP_REG(RK3366_LIT_MCU_CTRL, 0x3f, 10), + .mcu_rw_pst = VOP_REG(RK3366_LIT_MCU_CTRL, 0xf, 16), + .mcu_rw_pend = VOP_REG(RK3366_LIT_MCU_CTRL, 0x3f, 20), + .mcu_clk_sel = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 26), + .mcu_hold_mode = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 27), + .mcu_frame_st = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 28), + .mcu_rs = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 29), + .mcu_bypass = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 30), + .mcu_type = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 31), + .mcu_rw_bypass_port = VOP_REG(RK3366_LIT_MCU_RW_BYPASS_PORT, + 0xffffffff, 0), +}; + +static const int rk3308_vop_intrs[] = { + FS_INTR, + FS_NEW_INTR, + ADDR_SAME_INTR, + LINE_FLAG_INTR, + LINE_FLAG1_INTR, + BUS_ERROR_INTR, + 0, + 0, + DSP_HOLD_VALID_INTR, + DMA_FINISH_INTR, + 0, + POST_BUF_EMPTY_INTR +}; + +static const struct vop_intr rk3308_vop_intr = { + .intrs = rk3308_vop_intrs, + .nintrs = ARRAY_SIZE(rk3308_vop_intrs), + .line_flag_num[0] = VOP_REG(RK3366_LIT_LINE_FLAG, 0xfff, 0), + .line_flag_num[1] = VOP_REG(RK3366_LIT_LINE_FLAG, 0xfff, 16), + .status = VOP_REG_MASK(RK3366_LIT_INTR_STATUS, 0xffff, 0), + .enable = VOP_REG_MASK(RK3366_LIT_INTR_EN, 0xffff, 0), + .clear = VOP_REG_MASK(RK3366_LIT_INTR_CLEAR, 0xffff, 0), +}; + +static const struct vop_data rk3308_vop = { + .version = VOP_VERSION(2, 7), + .max_input = {1920, 8192}, + .max_output = {1920, 1080}, + .ctrl = &rk3308_ctrl_data, + .intr = &rk3308_vop_intr, + .win = rk3366_vop_lit_win_data, + .win_size = ARRAY_SIZE(rk3366_vop_lit_win_data), +}; + +static const struct vop_ctrl rv1126_ctrl_data = { + .standby = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 1), + .axi_outstanding_max_num = VOP_REG(RK3366_LIT_SYS_CTRL1, 0x1f, 16), + .axi_max_outstanding_en = VOP_REG(RK3366_LIT_SYS_CTRL1, 0x1, 12), + .htotal_pw = VOP_REG(RK3366_LIT_DSP_HTOTAL_HS_END, 0x0fff0fff, 0), + .hact_st_end = VOP_REG(RK3366_LIT_DSP_HACT_ST_END, 0x0fff0fff, 0), + .vtotal_pw = VOP_REG(RK3366_LIT_DSP_VTOTAL_VS_END, 0x0fff0fff, 0), + .vact_st_end = VOP_REG(RK3366_LIT_DSP_VACT_ST_END, 0x0fff0fff, 0), + .vact_st_end_f1 = VOP_REG(RK3366_LIT_DSP_VACT_ST_END_F1, 0x0fff0fff, 0), + .vs_st_end_f1 = VOP_REG(RK3366_LIT_DSP_VS_ST_END_F1, 0x0fff0fff, 0), + .dsp_interlace = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 0), + .global_regdone_en = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 13), + .auto_gate_en = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 0), + .dsp_layer_sel = VOP_REG(RK3366_LIT_DSP_CTRL2, 0xff, 22), + .overlay_mode = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 4), + .core_dclk_div = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 13), + .dclk_ddr = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 14), + .rgb_en = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 0), + .rgb_pin_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x7, 2), + .hdmi_en = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 8), + .hdmi_pin_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x7, 10), + .lvds_en = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 16), + .lvds_pin_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x7, 18), + .mipi_en = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 24), + .mipi_pin_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x7, 26), + .mipi_dclk_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 25), + .lvds_dclk_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 17), + .hdmi_dclk_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 9), + .rgb_dclk_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 1), + .dither_down_en = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 8), + .dither_down_sel = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 7), + .dither_down_mode = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 6), + .dither_up_en = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 2), + .dsp_data_swap = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1f, 9), + .yuv_clip = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 4), + .dsp_ccir656_avg = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 5), + .dsp_black = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 15), + .dsp_blank = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 14), + .dsp_outzero = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 3), + .dsp_lut_en = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 5), + .out_mode = VOP_REG(RK3366_LIT_DSP_CTRL2, 0xf, 16), + .dsp_background = VOP_REG(RK3366_LIT_DSP_BG, 0x00ffffff, 0), + .cfg_done = VOP_REG(RK3366_LIT_REG_CFG_DONE, 0x1, 0), + + .bcsh_en = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 0), + .bcsh_r2y_csc_mode = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 1), + .bcsh_out_mode = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x3, 2), + .bcsh_y2r_csc_mode = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x3, 4), + .bcsh_y2r_en = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 6), + .bcsh_r2y_en = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 7), + .bcsh_color_bar = VOP_REG(RK3366_LIT_BCSH_COL_BAR, 0xffffff, 0), + .bcsh_brightness = VOP_REG(RK3366_LIT_BCSH_BCS, 0xff, 0), + .bcsh_contrast = VOP_REG(RK3366_LIT_BCSH_BCS, 0x1ff, 8), + .bcsh_sat_con = VOP_REG(RK3366_LIT_BCSH_BCS, 0x3ff, 20), + .bcsh_sin_hue = VOP_REG(RK3366_LIT_BCSH_H, 0x1ff, 0), + .bcsh_cos_hue = VOP_REG(RK3366_LIT_BCSH_H, 0x1ff, 16), + + .mcu_pix_total = VOP_REG(RK3366_LIT_MCU_CTRL, 0x3f, 0), + .mcu_cs_pst = VOP_REG(RK3366_LIT_MCU_CTRL, 0xf, 6), + .mcu_cs_pend = VOP_REG(RK3366_LIT_MCU_CTRL, 0x3f, 10), + .mcu_rw_pst = VOP_REG(RK3366_LIT_MCU_CTRL, 0xf, 16), + .mcu_rw_pend = VOP_REG(RK3366_LIT_MCU_CTRL, 0x3f, 20), + .mcu_clk_sel = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 26), + .mcu_hold_mode = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 27), + .mcu_frame_st = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 28), + .mcu_rs = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 29), + .mcu_bypass = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 30), + .mcu_type = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 31), + .mcu_rw_bypass_port = VOP_REG(RK3366_LIT_MCU_RW_BYPASS_PORT, + 0xffffffff, 0), + .bt1120_yc_swap = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 30), + .bt1120_en = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 31), +}; + +static const struct vop_win_data rv1126_vop_win_data[] = { + { .base = 0x00, .phy = &rk3366_lit_win0_data, + .type = DRM_PLANE_TYPE_OVERLAY }, + { .phy = NULL }, + { .base = 0xe0, .phy = &px30_win23_data, + .type = DRM_PLANE_TYPE_PRIMARY, + .area = rk3368_area_data, + .area_size = ARRAY_SIZE(rk3368_area_data), }, +}; + +static const struct vop_grf_ctrl rv1126_grf_ctrl = { + .grf_dclk_inv = VOP_REG(RV1126_GRF_IOFUNC_CON3, 0x1, 2), +}; + +static const struct vop_data rv1126_vop = { + .version = VOP_VERSION(2, 0xb), + .max_input = {1920, 1920}, + .max_output = {1920, 1080}, + .ctrl = &rv1126_ctrl_data, + .intr = &rk3366_lit_intr, + .grf_ctrl = &rv1126_grf_ctrl, + .win = rv1126_vop_win_data, + .win_size = ARRAY_SIZE(rv1126_vop_win_data), +}; + static const struct of_device_id vop_driver_dt_match[] = { { .compatible = "rockchip,rk3036-vop", .data = &rk3036_vop }, - { .compatible = "rockchip,rk3126-vop", - .data = &rk3126_vop }, - { .compatible = "rockchip,px30-vop-big", - .data = &px30_vop_big }, - { .compatible = "rockchip,px30-vop-lit", - .data = &px30_vop_lit }, { .compatible = "rockchip,rk3066-vop", .data = &rk3066_vop }, - { .compatible = "rockchip,rk3188-vop", - .data = &rk3188_vop }, - { .compatible = "rockchip,rk3288-vop", - .data = &rk3288_vop }, + { .compatible = "rockchip,rk3126-vop", + .data = &rk3126_vop }, + { .compatible = "rockchip,px30-vop-lit", + .data = &px30_vop_lit }, + { .compatible = "rockchip,px30-vop-big", + .data = &px30_vop_big }, + { .compatible = "rockchip,rk3308-vop", + .data = &rk3308_vop }, + { .compatible = "rockchip,rv1126-vop", + .data = &rv1126_vop }, + { .compatible = "rockchip,rk3288-vop-big", + .data = &rk3288_vop_big }, + { .compatible = "rockchip,rk3288-vop-lit", + .data = &rk3288_vop_lit }, { .compatible = "rockchip,rk3368-vop", .data = &rk3368_vop }, { .compatible = "rockchip,rk3366-vop", diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h index 8923c8b1577e..db75e014a00b 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h @@ -113,6 +113,11 @@ #define RK3288_DSP_VACT_ST_END 0x0194 #define RK3288_DSP_VS_ST_END_F1 0x0198 #define RK3288_DSP_VACT_ST_END_F1 0x019c + +#define RK3288_BCSH_COLOR_BAR 0x01b0 +#define RK3288_BCSH_BCS 0x01b4 +#define RK3288_BCSH_H 0x01b8 +#define RK3288_GRF_SOC_CON15 0x03a4 /* register definition end */ /* rk3368 register definition */ @@ -300,6 +305,7 @@ #define RK3368_CABC_GAMMA_LUT_ADDR 0x1800 #define RK3368_MCU_BYPASS_WPORT 0x2200 #define RK3368_MCU_BYPASS_RPORT 0x2300 +#define RK3368_GRF_SOC_CON6 0x0418 /* rk3368 register definition end */ #define RK3366_REG_CFG_DONE 0x0000 @@ -628,6 +634,7 @@ #define RK3399_YUV2YUV_WIN 0x02c0 #define RK3399_YUV2YUV_POST 0x02c4 #define RK3399_AUTO_GATING_EN 0x02cc +#define RK3399_DBG_POST_REG1 0x036c #define RK3399_WIN0_CSC_COE 0x03a0 #define RK3399_WIN1_CSC_COE 0x03c0 #define RK3399_WIN2_CSC_COE 0x03e0 @@ -798,6 +805,21 @@ #define RK3328_DBG_POST_RESERVED 0x000006ec #define RK3328_DBG_DATAO 0x000006f0 #define RK3328_DBG_DATAO_2 0x000006f4 +#define RK3328_SDR2HDR_CTRL 0x00000700 +#define RK3328_SDR2HDR_EOTF_OETF_Y0 0x00000704 +#define RK3328_SDR2HDR_EOTF_OETF_Y1 0x00000710 +#define RK3328_SDR2HDR_OETF_DX_DXPOW1 0x00000810 +#define RK3328_SDR2HDR_OETF_XN1 0x00000910 + +#define RK3328_HDR2DR_CTRL 0x00000a10 +#define RK3328_HDR2DR_SRC_RANGE 0x00000a14 +#define RK3328_HDR2DR_NORMFACEETF 0x00000a18 +#define RK3328_HDR2DR_DST_RANGE 0x00000a20 +#define RK3328_HDR2DR_NORMFACGAMMA 0x00000a24 +#define RK3328_HDR2SDR_EETF_OETF_Y0 0x00000a28 +#define RK3328_HDR2DR_SAT_Y0 0x00000a2C +#define RK3328_HDR2SDR_EETF_OETF_Y1 0x00000a30 +#define RK3328_HDR2DR_SAT_Y1 0x00000ab0 /* sdr to hdr */ #define RK3328_SDR2HDR_CTRL 0x00000700 @@ -830,6 +852,7 @@ #define RK3036_SYS_CTRL 0x00 #define RK3036_DSP_CTRL0 0x04 #define RK3036_DSP_CTRL1 0x08 +#define RK3036_INT_SCALER 0x0c #define RK3036_INT_STATUS 0x10 #define RK3036_ALPHA_CTRL 0x14 #define RK3036_WIN0_COLOR_KEY 0x18 @@ -870,112 +893,6 @@ #define RK3036_HWC_LUT_ADDR 0x800 /* rk3036 register definition end */ -/* rk3126 register definition */ -#define RK3126_WIN1_MST 0x4c -#define RK3126_WIN1_DSP_INFO 0x50 -#define RK3126_WIN1_DSP_ST 0x54 -/* rk3126 register definition end */ - -/* px30 register definition */ -#define PX30_REG_CFG_DONE 0x00000 -#define PX30_VERSION 0x00004 -#define PX30_DSP_BG 0x00008 -#define PX30_MCU_CTRL 0x0000c -#define PX30_SYS_CTRL0 0x00010 -#define PX30_SYS_CTRL1 0x00014 -#define PX30_SYS_CTRL2 0x00018 -#define PX30_DSP_CTRL0 0x00020 -#define PX30_DSP_CTRL2 0x00028 -#define PX30_VOP_STATUS 0x0002c -#define PX30_LINE_FLAG 0x00030 -#define PX30_INTR_EN 0x00034 -#define PX30_INTR_CLEAR 0x00038 -#define PX30_INTR_STATUS 0x0003c -#define PX30_WIN0_CTRL0 0x00050 -#define PX30_WIN0_CTRL1 0x00054 -#define PX30_WIN0_COLOR_KEY 0x00058 -#define PX30_WIN0_VIR 0x0005c -#define PX30_WIN0_YRGB_MST0 0x00060 -#define PX30_WIN0_CBR_MST0 0x00064 -#define PX30_WIN0_ACT_INFO 0x00068 -#define PX30_WIN0_DSP_INFO 0x0006c -#define PX30_WIN0_DSP_ST 0x00070 -#define PX30_WIN0_SCL_FACTOR_YRGB 0x00074 -#define PX30_WIN0_SCL_FACTOR_CBR 0x00078 -#define PX30_WIN0_SCL_OFFSET 0x0007c -#define PX30_WIN0_ALPHA_CTRL 0x00080 -#define PX30_WIN1_CTRL0 0x00090 -#define PX30_WIN1_CTRL1 0x00094 -#define PX30_WIN1_VIR 0x00098 -#define PX30_WIN1_MST 0x000a0 -#define PX30_WIN1_DSP_INFO 0x000a4 -#define PX30_WIN1_DSP_ST 0x000a8 -#define PX30_WIN1_COLOR_KEY 0x000ac -#define PX30_WIN1_ALPHA_CTRL 0x000bc -#define PX30_HWC_CTRL0 0x000e0 -#define PX30_HWC_CTRL1 0x000e4 -#define PX30_HWC_MST 0x000e8 -#define PX30_HWC_DSP_ST 0x000ec -#define PX30_HWC_ALPHA_CTRL 0x000f0 -#define PX30_DSP_HTOTAL_HS_END 0x00100 -#define PX30_DSP_HACT_ST_END 0x00104 -#define PX30_DSP_VTOTAL_VS_END 0x00108 -#define PX30_DSP_VACT_ST_END 0x0010c -#define PX30_DSP_VS_ST_END_F1 0x00110 -#define PX30_DSP_VACT_ST_END_F1 0x00114 -#define PX30_BCSH_CTRL 0x00160 -#define PX30_BCSH_COL_BAR 0x00164 -#define PX30_BCSH_BCS 0x00168 -#define PX30_BCSH_H 0x0016c -#define PX30_FRC_LOWER01_0 0x00170 -#define PX30_FRC_LOWER01_1 0x00174 -#define PX30_FRC_LOWER10_0 0x00178 -#define PX30_FRC_LOWER10_1 0x0017c -#define PX30_FRC_LOWER11_0 0x00180 -#define PX30_FRC_LOWER11_1 0x00184 -#define PX30_MCU_RW_BYPASS_PORT 0x0018c -#define PX30_WIN2_CTRL0 0x00190 -#define PX30_WIN2_CTRL1 0x00194 -#define PX30_WIN2_VIR0_1 0x00198 -#define PX30_WIN2_VIR2_3 0x0019c -#define PX30_WIN2_MST0 0x001a0 -#define PX30_WIN2_DSP_INFO0 0x001a4 -#define PX30_WIN2_DSP_ST0 0x001a8 -#define PX30_WIN2_COLOR_KEY 0x001ac -#define PX30_WIN2_ALPHA_CTRL 0x001bc -#define PX30_BLANKING_VALUE 0x001f4 -#define PX30_FLAG_REG_FRM_VALID 0x001f8 -#define PX30_FLAG_REG 0x001fc -#define PX30_HWC_LUT_ADDR 0x00600 -#define PX30_GAMMA_LUT_ADDR 0x00a00 -/* px30 register definition end */ - -/* rk3188 register definition */ -#define RK3188_SYS_CTRL 0x00 -#define RK3188_DSP_CTRL0 0x04 -#define RK3188_DSP_CTRL1 0x08 -#define RK3188_INT_STATUS 0x10 -#define RK3188_WIN0_YRGB_MST0 0x20 -#define RK3188_WIN0_CBR_MST0 0x24 -#define RK3188_WIN0_YRGB_MST1 0x28 -#define RK3188_WIN0_CBR_MST1 0x2c -#define RK3188_WIN_VIR 0x30 -#define RK3188_WIN0_ACT_INFO 0x34 -#define RK3188_WIN0_DSP_INFO 0x38 -#define RK3188_WIN0_DSP_ST 0x3c -#define RK3188_WIN0_SCL_FACTOR_YRGB 0x40 -#define RK3188_WIN0_SCL_FACTOR_CBR 0x44 -#define RK3188_WIN1_MST 0x4c -#define RK3188_WIN1_DSP_INFO 0x50 -#define RK3188_WIN1_DSP_ST 0x54 -#define RK3188_DSP_HTOTAL_HS_END 0x6c -#define RK3188_DSP_HACT_ST_END 0x70 -#define RK3188_DSP_VTOTAL_VS_END 0x74 -#define RK3188_DSP_VACT_ST_END 0x78 -#define RK3188_REG_CFG_DONE 0x90 -/* rk3188 register definition end */ - -/* rk3066 register definition */ #define RK3066_SYS_CTRL0 0x00 #define RK3066_SYS_CTRL1 0x04 #define RK3066_DSP_CTRL0 0x08 @@ -1026,7 +943,101 @@ #define RK3066_MCU_BYPASS_RPORT 0x200 #define RK3066_WIN2_LUT_ADDR 0x400 #define RK3066_DSP_LUT_ADDR 0x800 -/* rk3066 register definition end */ + +/* rk3366 register definition */ +#define RK3366_LIT_REG_CFG_DONE 0x00000 +#define RK3366_LIT_VERSION 0x00004 +#define RK3366_LIT_DSP_BG 0x00008 +#define RK3366_LIT_MCU_CTRL 0x0000c +#define RK3366_LIT_SYS_CTRL0 0x00010 +#define RK3366_LIT_SYS_CTRL1 0x00014 +#define RK3366_LIT_SYS_CTRL2 0x00018 +#define RK3366_LIT_DSP_CTRL0 0x00020 +#define RK3366_LIT_DSP_CTRL2 0x00028 +#define RK3366_LIT_VOP_STATUS 0x0002c +#define RK3366_LIT_LINE_FLAG 0x00030 +#define RK3366_LIT_INTR_EN 0x00034 +#define RK3366_LIT_INTR_CLEAR 0x00038 +#define RK3366_LIT_INTR_STATUS 0x0003c +#define RK3366_LIT_WIN0_CTRL0 0x00050 +#define RK3366_LIT_WIN0_CTRL1 0x00054 +#define RK3366_LIT_WIN0_COLOR_KEY 0x00058 +#define RK3366_LIT_WIN0_VIR 0x0005c +#define RK3366_LIT_WIN0_YRGB_MST0 0x00060 +#define RK3366_LIT_WIN0_CBR_MST0 0x00064 +#define RK3366_LIT_WIN0_ACT_INFO 0x00068 +#define RK3366_LIT_WIN0_DSP_INFO 0x0006c +#define RK3366_LIT_WIN0_DSP_ST 0x00070 +#define RK3366_LIT_WIN0_SCL_FACTOR_YRGB 0x00074 +#define RK3366_LIT_WIN0_SCL_FACTOR_CBR 0x00078 +#define RK3366_LIT_WIN0_SCL_OFFSET 0x0007c +#define RK3366_LIT_WIN0_ALPHA_CTRL 0x00080 +#define RK3366_LIT_WIN1_CTRL0 0x00090 +#define RK3366_LIT_WIN1_CTRL1 0x00094 +#define RK3366_LIT_WIN1_VIR 0x00098 +#define RK3366_LIT_WIN1_MST 0x000a0 +#define RK3366_LIT_WIN1_DSP_INFO 0x000a4 +#define RK3366_LIT_WIN1_DSP_ST 0x000a8 +#define RK3366_LIT_WIN1_COLOR_KEY 0x000ac +#define RK3366_LIT_WIN1_ALPHA_CTRL 0x000bc +#define RK3366_LIT_HWC_CTRL0 0x000e0 +#define RK3366_LIT_HWC_CTRL1 0x000e4 +#define RK3366_LIT_HWC_MST 0x000e8 +#define RK3366_LIT_HWC_DSP_ST 0x000ec +#define RK3366_LIT_HWC_ALPHA_CTRL 0x000f0 +#define RK3366_LIT_DSP_HTOTAL_HS_END 0x00100 +#define RK3366_LIT_DSP_HACT_ST_END 0x00104 +#define RK3366_LIT_DSP_VTOTAL_VS_END 0x00108 +#define RK3366_LIT_DSP_VACT_ST_END 0x0010c +#define RK3366_LIT_DSP_VS_ST_END_F1 0x00110 +#define RK3366_LIT_DSP_VACT_ST_END_F1 0x00114 +#define RK3366_LIT_BCSH_CTRL 0x00160 +#define RK3366_LIT_BCSH_COL_BAR 0x00164 +#define RK3366_LIT_BCSH_BCS 0x00168 +#define RK3366_LIT_BCSH_H 0x0016c +#define RK3366_LIT_FRC_LOWER01_0 0x00170 +#define RK3366_LIT_FRC_LOWER01_1 0x00174 +#define RK3366_LIT_FRC_LOWER10_0 0x00178 +#define RK3366_LIT_FRC_LOWER10_1 0x0017c +#define RK3366_LIT_FRC_LOWER11_0 0x00180 +#define RK3366_LIT_FRC_LOWER11_1 0x00184 +#define RK3366_LIT_MCU_RW_BYPASS_PORT 0x0018c +#define RK3366_LIT_DBG_REG_000 0x00190 +#define RK3366_LIT_BLANKING_VALUE 0x001f4 +#define RK3366_LIT_FLAG_REG_FRM_VALID 0x001f8 +#define RK3366_LIT_FLAG_REG 0x001fc +#define RK3366_LIT_HWC_LUT_ADDR 0x00600 +#define RK3366_LIT_GAMMA_LUT_ADDR 0x00a00 +/* rk3366 register definition end */ + +/* rk3126 register definition */ +#define RK3126_WIN1_MST 0x0004c +#define RK3126_WIN1_DSP_INFO 0x00050 +#define RK3126_WIN1_DSP_ST 0x00054 +/* rk3126 register definition end */ + +/* px30 register definition */ +#define PX30_CABC_CTRL0 0x00200 +#define PX30_CABC_CTRL1 0x00204 +#define PX30_CABC_CTRL2 0x00208 +#define PX30_CABC_CTRL3 0x0020c +#define PX30_CABC_GAUSS_LINE0_0 0x00210 +#define PX30_CABC_GAUSS_LINE0_1 0x00214 +#define PX30_CABC_GAUSS_LINE1_0 0x00218 +#define PX30_CABC_GAUSS_LINE1_1 0x0021c +#define PX30_CABC_GAUSS_LINE2_0 0x00220 +#define PX30_CABC_GAUSS_LINE2_1 0x00224 +#define PX30_AFBCD0_CTRL 0x00240 +#define PX30_AFBCD0_HDR_PTR 0x00244 +#define PX30_AFBCD0_PIC_SIZE 0x00248 +#define PX30_AFBCD0_PIC_OFFSET 0x0024c +#define PX30_AFBCD0_AXI_CTRL 0x00250 +#define PX30_GRF_PD_VO_CON1 0x00438 +/* px30 register definition end */ + +#define RV1126_GRF_IOFUNC_CON3 0x1026c + +/* rk3568 vop registers definition */ #define RK3568_GRF_VO_CON1 0x0364 /* System registers definition */ @@ -1443,5 +1454,4 @@ #define RK3568_HDR_EOTF_OETF_Y0 0x20F0 #define RK3568_HDR_OETF_DX_POW1 0x2200 #define RK3568_HDR_OETF_XN1 0x2300 - #endif /* _ROCKCHIP_VOP_REG_H */