From f7e3199891ae12eef3a2c58c84c83e5019b168e3 Mon Sep 17 00:00:00 2001 From: Chaoyi Chen Date: Thu, 14 Nov 2024 11:11:17 +0000 Subject: [PATCH] drm/rockchip: vop: Add writeback support RV1126B VOP has a writeback with max 1920 x 1080 output. Writeback work as a connector in drm system. Change-Id: I550601e480630a841c9abfadb9eda2204c074592 Signed-off-by: Chaoyi Chen --- drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 511 +++++++++++++++++++- drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 37 ++ drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 57 +++ drivers/gpu/drm/rockchip/rockchip_vop_reg.h | 8 + 4 files changed, 608 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 0a743b18b8de..85ef072e4065 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -39,6 +39,7 @@ #include #include #include +#include #ifdef CONFIG_DRM_ANALOGIX_DP #include @@ -105,6 +106,9 @@ #define VOP_CTRL_SET(x, name, v) \ REG_SET(x, name, 0, (x)->data->ctrl->name, v, false) +#define VOP_CTRL_SET2(x, ctrl, name, v) \ + REG_SET(x, name, 0, ctrl->regs->name, v, false) + #define VOP_INTR_GET(vop, name) \ vop_read_reg(vop, 0, &vop->data->ctrl->name) @@ -115,6 +119,12 @@ REG_SET_MASK(vop, name, 0, vop->data->intr->name, \ mask, v, false) +#define VOP_INTR_SET2(vop, intr, name, v) \ + REG_SET(vop, name, 0, intr->name, v, false) +#define VOP_INTR_SET_MASK2(vop, intr, name, mask, v) \ + REG_SET_MASK(vop, name, 0, 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) @@ -133,11 +143,28 @@ VOP_INTR_SET_MASK(vop, name, mask, reg); \ } while (0) #define VOP_INTR_GET_TYPE(vop, name, type) \ - vop_get_intr_type(vop, &vop->data->intr->name, type) + vop_get_intr_type(vop, vop->data->intr, &vop->data->intr->name, type) + +#define VOP_INTR_SET_TYPE2(vop, intr, name, type, v) \ + do { \ + int i, reg = 0, mask = 0; \ + for (i = 0; i < intr->nintrs; i++) { \ + if (intr->intrs[i] & type) { \ + reg |= (v) << i; \ + mask |= 1 << i; \ + } \ + } \ + VOP_INTR_SET_MASK2(vop, intr, name, mask, reg); \ + } while (0) +#define VOP_INTR_GET_TYPE2(vop, intr, name, type) \ + vop_get_intr_type(vop, intr, &intr->name, type) #define VOP_CTRL_GET(x, name) \ vop_read_reg(x, 0, &vop->data->ctrl->name) +#define VOP_CTRL_GET2(x, ctrl, name) \ + vop_read_reg(x, 0, &ctrl->regs->name) + #define VOP_WIN_GET(vop, win, name) \ vop_read_reg(vop, win->offset, &VOP_WIN_NAME(win, name)) @@ -156,6 +183,7 @@ #define to_vop_win(x) container_of(x, struct vop_win, base) #define to_vop_plane_state(x) container_of(x, struct vop_plane_state, base) +#define to_wb_state(x) container_of(x, struct vop_wb_connector_state, base) #define AFBC_Y2R_COLOR_TRANSFORM (1 << 4) enum vop_pending { @@ -221,6 +249,54 @@ struct vop_win { struct drm_property *name_prop; }; +/* + * max two jobs a time, one is running(writing back), + * another one will run in next frame. + */ +#define VOP_WB_JOB_MAX 2 + +struct vop_wb_job { + bool pending; + /** + * @fs_vsync_cnt: frame start vysnc counter, + * used to get the write back complete event; + */ + uint32_t fs_vsync_cnt; +}; + +struct vop_wb { + struct drm_writeback_connector conn; + const struct vop_wb_regs *regs; + struct vop_wb_job jobs[VOP_WB_JOB_MAX]; + uint8_t job_index; + + /** + * @job_lock: + * + * spinlock to protect the job between vop_wb_commit and vop_wb_handler in isr. + */ + spinlock_t job_lock; +}; + +enum vop_wb_format { + VOP_WB_ARGB8888, + VOP_WB_RGB888, + VOP_WB_RGB565, + VOP_WB_YUV420SP = 4, + VOP_WB_INVALID = -1, +}; + +struct vop_wb_connector_state { + struct drm_connector_state base; + dma_addr_t yrgb_addr; + dma_addr_t uv_addr; + enum vop_wb_format format; + bool xgt2_en; + uint16_t scale_x_factor; + uint8_t scale_x_en; + uint8_t scale_y_en; +}; + struct vop { struct rockchip_crtc rockchip_crtc; struct device *dev; @@ -306,6 +382,12 @@ struct vop { struct rockchip_mcu_timing mcu_timing; struct rockchip_mcu_timing mcu_bypass_timing; + /** + * @wb_en: write back enabled + */ + bool wb_en; + struct vop_wb wb; + struct vop_win win[]; }; @@ -392,6 +474,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 inline void vop_write_reg_uncached(struct vop *vop, const struct vop_reg *reg, uint32_t v) +{ + uint32_t offset = reg->offset; + uint32_t cached_val = vop->regsbak[offset >> 2]; + + v = (cached_val & ~(reg->mask << reg->shift)) | ((v & reg->mask) << reg->shift); + writel(v, vop->regs + offset); +} + 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) @@ -423,15 +514,15 @@ vop_get_win_phy(struct vop_win *win, const struct vop_reg *reg) return win->phy; } -static inline uint32_t vop_get_intr_type(struct vop *vop, +static inline uint32_t vop_get_intr_type(struct vop *vop, const struct vop_intr *intr, const struct vop_reg *reg, int type) { uint32_t i, ret = 0; uint32_t regs = vop_read_reg(vop, 0, reg); - for (i = 0; i < vop->data->intr->nintrs; i++) { - if ((type & vop->data->intr->intrs[i]) && (regs & 1 << i)) - ret |= vop->data->intr->intrs[i]; + for (i = 0; i < intr->nintrs; i++) { + if ((type & intr->intrs[i]) && (regs & 1 << i)) + ret |= intr->intrs[i]; } return ret; @@ -532,6 +623,13 @@ static inline void vop_cfg_done(struct vop *vop) VOP_CTRL_SET(vop, cfg_done, 1); } +static inline void vop_wb_cfg_done(struct vop *vop) +{ + struct vop_wb *wb = &vop->wb; + + VOP_CTRL_SET2(vop, wb, cfg_done, 1); +} + static bool vop_is_allwin_disabled(struct vop *vop) { int i; @@ -811,6 +909,23 @@ static inline bool rockchip_afbc(struct drm_plane *plane, u64 modifier) return rockchip_drm_is_afbc(plane, modifier); } +static enum vop_wb_format vop_convert_wb_format(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_ARGB8888: + return VOP_WB_ARGB8888; + case DRM_FORMAT_RGB888: + return VOP_WB_RGB888; + case DRM_FORMAT_RGB565: + return VOP_WB_RGB565; + case DRM_FORMAT_NV12: + return VOP_WB_YUV420SP; + default: + DRM_ERROR("unsupported wb format[%p4cc]\n", &format); + return VOP_WB_INVALID; + } +} + static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src, uint32_t dst, bool is_horizontal, int vsu_mode, int *vskiplines) @@ -1458,6 +1573,379 @@ static void vop_core_clks_disable(struct vop *vop) clk_disable(vop->hclk); } +static void vop_wb_connector_reset(struct drm_connector *connector) +{ + struct vop_wb_connector_state *wb_state; + + if (connector->state) { + __drm_atomic_helper_connector_destroy_state(connector->state); + kfree(connector->state); + connector->state = NULL; + } + + wb_state = kzalloc(sizeof(*wb_state), GFP_KERNEL); + if (wb_state) + __drm_atomic_helper_connector_reset(connector, &wb_state->base); +} + +static enum drm_connector_status +vop_wb_connector_detect(struct drm_connector *connector, bool force) +{ + return connector_status_connected; +} + +static void vop_wb_connector_destroy(struct drm_connector *connector) +{ + drm_connector_cleanup(connector); +} + +static struct drm_connector_state * +vop_wb_connector_duplicate_state(struct drm_connector *connector) +{ + struct vop_wb_connector_state *wb_state; + + if (WARN_ON(!connector->state)) + return NULL; + + wb_state = kzalloc(sizeof(*wb_state), GFP_KERNEL); + if (!wb_state) + return NULL; + + __drm_atomic_helper_connector_duplicate_state(connector, &wb_state->base); + + return &wb_state->base; +} + +static const struct drm_connector_funcs vop_wb_connector_funcs = { + .reset = vop_wb_connector_reset, + .detect = vop_wb_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = vop_wb_connector_destroy, + .atomic_duplicate_state = vop_wb_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static int vop_wb_connector_get_modes(struct drm_connector *connector) +{ + return 0; +} + +static enum drm_mode_status +vop_wb_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + + struct drm_writeback_connector *wb_conn; + struct vop_wb *wb; + struct vop *vop; + int w, h; + + wb_conn = container_of(connector, struct drm_writeback_connector, base); + wb = container_of(wb_conn, struct vop_wb, conn); + vop = container_of(wb, struct vop, wb); + w = mode->hdisplay; + h = mode->vdisplay; + + if (w > vop->data->wb->max_output.width) + return MODE_BAD_HVALUE; + + if (h > vop->data->wb->max_output.height) + return MODE_BAD_VVALUE; + + return MODE_OK; +} + +#define VOP_WB_BILI_SCL_FAC_CHECK(src, dst, fac) \ + (fac * (dst - 1) >> 12 < (src - 1)) + +static uint16_t vop_wb_scale_down_factor(uint32_t src, uint32_t dst) +{ + uint32_t fac = 0; + int i = 0; + + /* + * A workaround to avoid zero div. + */ + if (dst < 2) + dst = 2; + + fac = scl_cal_scale2(src, dst); + for (i = 0; i < 100; i++) { + if (VOP_WB_BILI_SCL_FAC_CHECK(src, dst, fac)) + break; + fac -= 1; + DRM_DEBUG("down fac cali: src:%d, dst:%d, fac:0x%x\n", src, dst, fac); + } + + return fac; +} + +static inline bool +vop_wb_connector_changed_only(struct drm_crtc_state *cstate, struct drm_connector *conn) +{ + struct drm_crtc_state *old_state; + u32 changed_connectors; + + old_state = drm_atomic_get_old_crtc_state(cstate->state, cstate->crtc); + changed_connectors = cstate->connector_mask ^ old_state->connector_mask; + + return BIT(drm_connector_index(conn)) == changed_connectors; +} + +static int vop_wb_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *cstate, + struct drm_connector_state *conn_state) +{ + struct vop_wb_connector_state *wb_state = to_wb_state(conn_state); + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(cstate); + struct drm_crtc *crtc = cstate->crtc; + struct vop *vop = to_vop(crtc); + struct drm_framebuffer *fb; + struct drm_gem_object *obj, *uv_obj; + struct rockchip_gem_object *rk_obj, *rk_uv_obj; + uint32_t src_width; + + /* + * No need for a full modested when the only connector changed is the + * writeback connector. + */ + if (cstate->connectors_changed && + vop_wb_connector_changed_only(cstate, conn_state->connector)) { + cstate->connectors_changed = false; + drm_dbg(crtc->dev, + "VOP force change connectors_changed to false when only wb changed\n"); + } + + if (!conn_state->writeback_job || !conn_state->writeback_job->fb) + return 0; + + fb = conn_state->writeback_job->fb; + DRM_DEV_DEBUG(vop->dev, "%d x % d\n", fb->width, fb->height); + + if (!fb->format->is_yuv && is_yuv_output(vcstate->bus_format)) { + DRM_ERROR("YUV2RGB is not supported by writeback\n"); + return -EINVAL; + } + + if ((fb->width > cstate->mode.hdisplay) || (fb->width * 4 < cstate->mode.hdisplay) || + ((fb->height < cstate->mode.vdisplay) && + (fb->height != (cstate->mode.vdisplay >> 1)))) { + drm_dbg_kms(crtc->dev, "Invalid framebuffer size %ux%u, Only support up to 1/4 x scale down and 1/2 y scale down\n", + fb->width, fb->height); + return -EINVAL; + } + + wb_state->xgt2_en = fb->width * 2 < cstate->mode.hdisplay; + src_width = wb_state->xgt2_en ? cstate->mode.hdisplay / 2 : cstate->mode.hdisplay; + wb_state->scale_x_factor = vop_wb_scale_down_factor(src_width, fb->width); + wb_state->scale_x_en = (fb->width < cstate->mode.hdisplay) ? 1 : 0; + wb_state->scale_y_en = (fb->height < cstate->mode.vdisplay) ? 1 : 0; + + wb_state->format = vop_convert_wb_format(fb->format->format); + if (wb_state->format < 0) { + drm_dbg_kms(crtc->dev, "Invalid pixel format %p4cc\n", &fb->format->format); + return -EINVAL; + } + + obj = fb->obj[0]; + rk_obj = to_rockchip_obj(obj); + wb_state->yrgb_addr = rk_obj->dma_addr + fb->offsets[0]; + + if (fb->format->is_yuv) { + uv_obj = fb->obj[1]; + rk_uv_obj = to_rockchip_obj(uv_obj); + + wb_state->uv_addr = rk_uv_obj->dma_addr + fb->offsets[1]; + } + + return 0; +} + +static void vop_wb_encoder_atomic_disable(struct drm_encoder *encoder, + struct drm_atomic_state *state) +{ + struct drm_crtc *crtc = encoder->crtc; + + if (!crtc->state->active_changed && !crtc->state->mode_changed) { + crtc->state->connectors_changed = false; + drm_dbg(crtc->dev, + "VOP force change connectors_changed to false when disable wb\n"); + } +} + +static const struct drm_encoder_helper_funcs vop_wb_encoder_helper_funcs = { + .atomic_check = vop_wb_encoder_atomic_check, + .atomic_disable = vop_wb_encoder_atomic_disable, +}; + +static const struct drm_connector_helper_funcs vop_wb_connector_helper_funcs = { + .get_modes = vop_wb_connector_get_modes, + .mode_valid = vop_wb_connector_mode_valid, +}; + +static int vop_wb_connector_init(struct vop *vop) +{ + const struct vop_data *vop_data = vop->data; + struct drm_crtc *crtc = &vop->rockchip_crtc.crtc; + int ret; + + if (!vop_data->wb) + return 0; + + vop->wb.regs = vop_data->wb->regs; + vop->wb.conn.encoder.possible_crtcs = drm_crtc_mask(crtc); + spin_lock_init(&vop->wb.job_lock); + drm_connector_helper_add(&vop->wb.conn.base, &vop_wb_connector_helper_funcs); + + ret = drm_writeback_connector_init(vop->drm_dev, &vop->wb.conn, + &vop_wb_connector_funcs, + &vop_wb_encoder_helper_funcs, + vop_data->wb->formats, + vop_data->wb->nformats, + vop->wb.conn.encoder.possible_crtcs); + if (ret) + DRM_DEV_ERROR(vop->dev, "writeback connector init failed\n"); + + return ret; +} + +static void vop_wb_connector_destory(struct vop *vop) +{ + const struct vop_data *vop_data = vop->data; + + if (!vop_data->wb) + return; + + drm_encoder_cleanup(&vop->wb.conn.encoder); + drm_connector_cleanup(&vop->wb.conn.base); +} + +static void vop_wb_irqs_enable(struct vop *vop) +{ + const struct vop_data *vop_data = vop->data; + const struct vop_intr *intr = vop_data->wb_intr; + uint32_t irqs = VOPL_WB_UV_FIFO_FULL_INTR | VOPL_WB_YRGB_FIFO_FULL_INTR; + + VOP_INTR_SET_TYPE2(vop, intr, clear, irqs, 1); + VOP_INTR_SET_TYPE2(vop, intr, enable, irqs, 1); +} + +static uint32_t vop_read_and_clear_wb_irqs(struct vop *vop) +{ + const struct vop_data *vop_data = vop->data; + const struct vop_intr *intr = vop_data->wb_intr; + uint32_t irqs = VOPL_WB_UV_FIFO_FULL_INTR | VOPL_WB_YRGB_FIFO_FULL_INTR; + uint32_t val; + + if (!intr) + return 0; + + val = VOP_INTR_GET_TYPE2(vop, intr, status, irqs); + if (val) + VOP_INTR_SET_TYPE2(vop, intr, clear, val, 1); + + return val; +} + +static void vop_wb_commit(struct drm_crtc *crtc) +{ + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); + struct vop *vop = to_vop(crtc); + struct vop_wb *wb = &vop->wb; + struct drm_writeback_connector *wb_conn = &wb->conn; + struct drm_connector_state *conn_state = wb_conn->base.state; + struct vop_wb_connector_state *wb_state; + unsigned long flags; + uint32_t fifo_throd; + uint8_t r2y; + + if (!conn_state) + return; + wb_state = to_wb_state(conn_state); + + if (conn_state->writeback_job && conn_state->writeback_job->fb) { + struct drm_framebuffer *fb = conn_state->writeback_job->fb; + + rockchip_drm_dbg(vop->dev, VOP_DEBUG_WB, + "Enable wb %ux%u fmt: %u pitches: %d addr: %pad", + fb->width, fb->height, wb_state->format, + fb->pitches[0], &wb_state->yrgb_addr); + + drm_writeback_queue_job(wb_conn, conn_state); + conn_state->writeback_job = NULL; + + spin_lock_irqsave(&wb->job_lock, flags); + wb->jobs[wb->job_index].pending = true; + wb->job_index++; + if (wb->job_index >= VOP_WB_JOB_MAX) + wb->job_index = 0; + spin_unlock_irqrestore(&wb->job_lock, flags); + + fifo_throd = fb->pitches[0] >> 4; + if (fifo_throd > vop->data->wb->fifo_depth) + fifo_throd = vop->data->wb->fifo_depth; + r2y = !vcstate->yuv_overlay && fb->format->is_yuv; + + VOP_CTRL_SET2(vop, wb, format, wb_state->format); + VOP_CTRL_SET2(vop, wb, yrgb_mst, wb_state->yrgb_addr); + VOP_CTRL_SET2(vop, wb, uv_mst, wb_state->uv_addr); + VOP_CTRL_SET2(vop, wb, fifo_throd, fifo_throd); + VOP_CTRL_SET2(vop, wb, xgt2_en, wb_state->xgt2_en); + VOP_CTRL_SET2(vop, wb, scale_x_factor, wb_state->scale_x_factor); + VOP_CTRL_SET2(vop, wb, scale_x_en, wb_state->scale_x_en); + VOP_CTRL_SET2(vop, wb, scale_y_en, wb_state->scale_y_en); + VOP_CTRL_SET2(vop, wb, r2y_en, r2y); + + VOP_CTRL_SET2(vop, wb, act_width, fb->width - 1); + VOP_CTRL_SET2(vop, wb, vir_stride, fb->pitches[0] >> 2); + VOP_CTRL_SET2(vop, wb, vir_stride_en, 1); + VOP_CTRL_SET2(vop, wb, post_empty_stop_en, 1); + + /* RV1126B support one-shot mode */ + VOP_CTRL_SET2(vop, wb, one_frame_mode, 1); + + /* In one-shot mode, enable bit will auto clear */ + vop_write_reg_uncached(vop, &wb->regs->enable, 1); + + vop_wb_irqs_enable(vop); + } +} + +static void __maybe_unused vop_wb_disable(struct vop *vop) +{ + struct vop_wb *wb = &vop->wb; + + VOP_CTRL_SET2(vop, wb, enable, 0); + vop_wb_cfg_done(vop); +} + +static void vop_wb_handler(struct vop *vop) +{ + struct vop_wb *wb = &vop->wb; + struct vop_wb_job *job; + unsigned long flags; + uint8_t i; + + if (!wb->regs) + return; + + /* In one shot mode, wb_en is auto disable */ + spin_lock_irqsave(&wb->job_lock, flags); + for (i = 0; i < VOP_WB_JOB_MAX; i++) { + job = &wb->jobs[i]; + if (job->pending) { + job->fs_vsync_cnt++; + + if (job->fs_vsync_cnt == 2) { + job->pending = false; + job->fs_vsync_cnt = 0; + drm_writeback_signal_completion(&vop->wb.conn, 0); + } + } + } + spin_unlock_irqrestore(&wb->job_lock, flags); +} + static void vop_crtc_load_lut(struct drm_crtc *crtc) { struct vop *vop = to_vop(crtc); @@ -4430,6 +4918,7 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc, spin_lock_irqsave(&vop->irq_lock, flags); vop->pre_overlay = s->hdr.pre_overlay; + vop_wb_commit(crtc); vop_cfg_done(vop); rockchip_drm_dbg(vop->dev, VOP_DEBUG_CFG_DONE, "cfg_done\n"); /* @@ -4749,6 +5238,7 @@ static irqreturn_t vop_isr(int irq, void *data) struct vop *vop = data; struct drm_crtc *crtc = &vop->rockchip_crtc.crtc; uint32_t active_irqs; + uint32_t wb_irqs; unsigned long flags; int ret = IRQ_NONE; @@ -4774,6 +5264,7 @@ static irqreturn_t vop_isr(int irq, void *data) /* Clear all active interrupt sources */ if (active_irqs) VOP_INTR_SET_TYPE(vop, clear, active_irqs, 1); + wb_irqs = vop_read_and_clear_wb_irqs(vop); spin_unlock_irqrestore(&vop->irq_lock, flags); @@ -4803,6 +5294,7 @@ static irqreturn_t vop_isr(int irq, void *data) 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); + vop_wb_handler(vop); drm_crtc_handle_vblank(crtc); vop_handle_vblank(vop); active_irqs &= ~(FS_INTR | FS_FIELD_INTR); @@ -4830,6 +5322,12 @@ static irqreturn_t vop_isr(int irq, void *data) if (active_irqs) DRM_ERROR("Unknown VOP IRQs: %#02x\n", active_irqs); + if (wb_irqs) { + active_irqs = wb_irqs; + ERROR_HANDLER(VOPL_WB_UV_FIFO_FULL); + ERROR_HANDLER(VOPL_WB_YRGB_FIFO_FULL); + } + out_disable: vop_core_clks_disable(vop); out: @@ -5470,6 +5968,7 @@ static int vop_bind(struct device *dev, struct device *master, void *data) if (ret) return ret; + vop_wb_connector_init(vop); pm_runtime_enable(&pdev->dev); if (of_count_phandle_with_args(dev->of_node, "power-domains", "#power-domain-cells") > 1) { @@ -5558,6 +6057,8 @@ static void vop_unbind(struct device *dev, struct device *master, void *data) pm_runtime_disable(dev); vop_destroy_crtc(vop); + + vop_wb_connector_destory(vop); } 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 4951eb48c437..d7473994fbab 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h @@ -1404,11 +1404,43 @@ struct vop_grf_ctrl { struct vop_reg grf_mipi_1to4_en; }; +struct vop_wb_regs { + struct vop_reg cfg_done; + struct vop_reg enable; + struct vop_reg format; + struct vop_reg dither_en; + struct vop_reg r2y_en; + struct vop_reg yrgb_mst; + struct vop_reg uv_mst; + struct vop_reg fifo_throd; + struct vop_reg scale_x_factor; + struct vop_reg scale_x_en; + struct vop_reg scale_y_en; + struct vop_reg axi_yrgb_id; + struct vop_reg axi_uv_id; + struct vop_reg vir_stride; + struct vop_reg vir_stride_en; + struct vop_reg act_width; + struct vop_reg post_empty_stop_en; + struct vop_reg one_frame_mode; + struct vop_reg xgt2_en; +}; + +struct vop_wb_data { + uint32_t nformats; + const uint32_t *formats; + struct vop_rect max_output; + const struct vop_wb_regs *regs; + uint32_t fifo_depth; +}; + 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_intr *wb_intr; + const struct vop_wb_data *wb; const struct vop_win_data *win; const struct vop_csc_table *csc_table; const struct vop_hdr_table *hdr_table; @@ -1711,6 +1743,11 @@ struct vop2_data { #define LINE_FLAG_INTR_CLR (1 << (INTR_CLR_SHIFT + 2)) #define BUS_ERROR_INTR_CLR (1 << (INTR_CLR_SHIFT + 3)) +/* RV1126 VOP Lite WB intr define */ +#define VOPL_WB_YRGB_FIFO_FULL_INTR BIT(0) +#define VOPL_WB_UV_FIFO_FULL_INTR BIT(1) +#define VOPL_WB_COMPLETE_INTR BIT(4) + #define DSP_LINE_NUM(x) (((x) & 0x1fff) << 12) #define DSP_LINE_NUM_MASK (0x1fff << 12) diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c index 77ca3fdd2b2a..2a4351bc15fd 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c @@ -159,6 +159,13 @@ static const uint64_t format_modifiers_afbc[] = { DRM_FORMAT_MOD_INVALID, }; +static const u32 formats_wb[] = { + DRM_FORMAT_RGB888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_RGB565, + DRM_FORMAT_NV12, +}; + 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), @@ -1982,6 +1989,54 @@ static const struct vop_grf_ctrl rv1126b_grf_ctrl = { .grf_dclk_inv = VOP_REG(RV1126B_GRF_VOP_LCDC_CON, 0x1, 0), }; +static const struct vop_wb_regs rv1126b_vop_wb_regs = { + .cfg_done = VOP_REG_MASK(0, 0x1, 5), + + .enable = VOP_REG(RV1126B_WB_CTRL, 0x1, 0), + .format = VOP_REG(RV1126B_WB_CTRL, 0x7, 1), + .dither_en = VOP_REG(RV1126B_WB_CTRL, 0x1, 4), + .r2y_en = VOP_REG(RV1126B_WB_CTRL, 0x1, 5), + .scale_x_en = VOP_REG(RV1126B_WB_CTRL, 0x1, 7), + .scale_y_en = VOP_REG(RV1126B_WB_CTRL, 0x1, 8), + .post_empty_stop_en = VOP_REG(RV1126B_WB_CTRL, 0x1, 11), + .one_frame_mode = VOP_REG(RV1126B_WB_CTRL, 0x1, 12), + .xgt2_en = VOP_REG(RV1126B_WB_CTRL, 0x1, 18), + .axi_yrgb_id = VOP_REG(RV1126B_WB_CTRL, 0xff, 20), + .axi_uv_id = VOP_REG(RV1126B_WB_CTRL, 0x1f, 24), + + .fifo_throd = VOP_REG(RV1126B_WB_XSCAL_FACTOR, 0x3ff, 0), + .scale_x_factor = VOP_REG(RV1126B_WB_XSCAL_FACTOR, 0x3fff, 16), + + .yrgb_mst = VOP_REG(RV1126B_WB_YRGB_MST, 0xffffffff, 0), + .uv_mst = VOP_REG(RV1126B_WB_CBR_MST, 0xffffffff, 0), + + .vir_stride = VOP_REG(RV1126B_WB_VIR_STRIDE, 0x1fff, 0), + .vir_stride_en = VOP_REG(RV1126B_WB_VIR_STRIDE, 0x1, 15), + .act_width = VOP_REG(RV1126B_WB_VIR_STRIDE, 0x1fff, 16), +}; + +static const int rv1126_wb_intrs[] = { + VOPL_WB_UV_FIFO_FULL_INTR, + VOPL_WB_YRGB_FIFO_FULL_INTR, + VOPL_WB_COMPLETE_INTR, +}; + +static const struct vop_intr rv1126b_wb_intr = { + .intrs = rv1126_wb_intrs, + .nintrs = ARRAY_SIZE(rv1126_wb_intrs), + .status = VOP_REG_MASK(RV1126B_WB_INTR_STATUS, 0xffff, 0), + .enable = VOP_REG_MASK(RV1126B_WB_INTR_EN, 0xffff, 0), + .clear = VOP_REG_MASK(RV1126B_WB_INTR_CLEAR, 0xffff, 0), +}; + +static const struct vop_wb_data rv1126b_vop_wb_data = { + .formats = formats_wb, + .nformats = ARRAY_SIZE(formats_wb), + .max_output = { 1920, 1080 }, + .fifo_depth = 1920 * 4 / 16, + .regs = &rv1126b_vop_wb_regs, +}; + static const struct vop_data rv1126b_vop = { .soc_id = 0x1126b, .vop_id = 0, @@ -1990,6 +2045,8 @@ static const struct vop_data rv1126b_vop = { .max_output = {1920, 1080}, .ctrl = &rv1126b_ctrl_data, .intr = &rk3366_lit_intr, + .wb = &rv1126b_vop_wb_data, + .wb_intr = &rv1126b_wb_intr, .grf = &rv1126b_grf_ctrl, .win = rv1126_vop_win_data, .win_size = ARRAY_SIZE(rv1126_vop_win_data), diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h index cf4846f25f1d..c1086ab10700 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h @@ -1041,6 +1041,14 @@ #define RV1126B_CLK_CNT 0x0040 #define RV1126B_GRF_VOP_LCDC_CON 0x30b9c +#define RV1126B_WB_CTRL 0x0280 +#define RV1126B_WB_XSCAL_FACTOR 0x0284 +#define RV1126B_WB_YRGB_MST 0x0288 +#define RV1126B_WB_CBR_MST 0x028c +#define RV1126B_WB_VIR_STRIDE 0x0290 +#define RV1126B_WB_INTR_EN 0x02a0 +#define RV1126B_WB_INTR_CLEAR 0x02a4 +#define RV1126B_WB_INTR_STATUS 0x02a8 #define RK3506_GRF_SOC_CON2 0x0008