if (vop2_cluster_window(win))
vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 0);
}
static u32 vop2_get_bpp(conststruct drm_format_info *format)
{ switch (format->format) { case DRM_FORMAT_YUV420_8BIT: return 12; case DRM_FORMAT_YUV420_10BIT: return 15; case DRM_FORMAT_VUY101010: return 30; default: return drm_format_info_bpp(format, 0);
}
}
staticenum vop2_data_format vop2_convert_format(u32 format)
{ switch (format) { case DRM_FORMAT_XRGB2101010: case DRM_FORMAT_ARGB2101010: case DRM_FORMAT_XBGR2101010: case DRM_FORMAT_ABGR2101010: return VOP2_FMT_XRGB101010; case DRM_FORMAT_XRGB8888: case DRM_FORMAT_ARGB8888: case DRM_FORMAT_XBGR8888: case DRM_FORMAT_ABGR8888: return VOP2_FMT_ARGB8888; case DRM_FORMAT_RGB888: case DRM_FORMAT_BGR888: return VOP2_FMT_RGB888; case DRM_FORMAT_RGB565: case DRM_FORMAT_BGR565: return VOP2_FMT_RGB565; case DRM_FORMAT_NV12: case DRM_FORMAT_NV21: case DRM_FORMAT_YUV420_8BIT: return VOP2_FMT_YUV420SP; case DRM_FORMAT_NV15: case DRM_FORMAT_YUV420_10BIT: return VOP2_FMT_YUV420SP_10; case DRM_FORMAT_NV16: case DRM_FORMAT_NV61: return VOP2_FMT_YUV422SP; case DRM_FORMAT_NV20: case DRM_FORMAT_Y210: return VOP2_FMT_YUV422SP_10; case DRM_FORMAT_NV24: case DRM_FORMAT_NV42: return VOP2_FMT_YUV444SP; case DRM_FORMAT_NV30: return VOP2_FMT_YUV444SP_10; case DRM_FORMAT_YUYV: case DRM_FORMAT_YVYU: return VOP2_FMT_VYUY422; case DRM_FORMAT_VYUY: case DRM_FORMAT_UYVY: return VOP2_FMT_YUYV422; default:
DRM_ERROR("unsupported format[%08x]\n", format); return -EINVAL;
}
}
staticenum vop2_afbc_format vop2_convert_afbc_format(u32 format)
{ switch (format) { case DRM_FORMAT_XRGB2101010: case DRM_FORMAT_ARGB2101010: case DRM_FORMAT_XBGR2101010: case DRM_FORMAT_ABGR2101010: return VOP2_AFBC_FMT_ARGB2101010; case DRM_FORMAT_XRGB8888: case DRM_FORMAT_ARGB8888: case DRM_FORMAT_XBGR8888: case DRM_FORMAT_ABGR8888: return VOP2_AFBC_FMT_ARGB8888; case DRM_FORMAT_RGB888: case DRM_FORMAT_BGR888: return VOP2_AFBC_FMT_RGB888; case DRM_FORMAT_RGB565: case DRM_FORMAT_BGR565: return VOP2_AFBC_FMT_RGB565; case DRM_FORMAT_YUV420_8BIT: return VOP2_AFBC_FMT_YUV420; case DRM_FORMAT_YUV420_10BIT: return VOP2_AFBC_FMT_YUV420_10BIT; case DRM_FORMAT_YVYU: case DRM_FORMAT_YUYV: case DRM_FORMAT_VYUY: case DRM_FORMAT_UYVY: return VOP2_AFBC_FMT_YUV422; case DRM_FORMAT_Y210: return VOP2_AFBC_FMT_YUV422_10BIT; default: return VOP2_AFBC_FMT_INVALID;
}
return VOP2_AFBC_FMT_INVALID;
}
staticbool vop2_win_rb_swap(u32 format)
{ switch (format) { case DRM_FORMAT_XBGR2101010: case DRM_FORMAT_ABGR2101010: case DRM_FORMAT_XBGR8888: case DRM_FORMAT_ABGR8888: case DRM_FORMAT_BGR888: case DRM_FORMAT_BGR565: returntrue; default: returnfalse;
}
}
staticbool vop2_afbc_uv_swap(u32 format)
{ switch (format) { case DRM_FORMAT_YUYV: case DRM_FORMAT_Y210: case DRM_FORMAT_YUV420_8BIT: case DRM_FORMAT_YUV420_10BIT: returntrue; default: returnfalse;
}
}
staticbool vop2_win_uv_swap(u32 format)
{ switch (format) { case DRM_FORMAT_NV12: case DRM_FORMAT_NV16: case DRM_FORMAT_NV24: case DRM_FORMAT_NV15: case DRM_FORMAT_NV20: case DRM_FORMAT_NV30: case DRM_FORMAT_YUYV: case DRM_FORMAT_UYVY: returntrue; default: returnfalse;
}
}
staticbool vop2_win_dither_up(u32 format)
{ switch (format) { case DRM_FORMAT_BGR565: case DRM_FORMAT_RGB565: returntrue; default: returnfalse;
}
}
staticbool vop2_output_uv_swap(u32 bus_format, u32 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))) returntrue; else returnfalse;
}
staticbool is_yuv_output(u32 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: returntrue; default: returnfalse;
}
}
staticbool rockchip_afbc(struct drm_plane *plane, u64 modifier)
{ int i;
if (modifier == DRM_FORMAT_MOD_LINEAR) returnfalse;
for (i = 0 ; i < plane->modifier_count; i++) if (plane->modifiers[i] == modifier) returntrue;
if (modifier == DRM_FORMAT_MOD_INVALID) returnfalse;
if (vop2->version == VOP_VERSION_RK3568) { if (vop2_cluster_window(win)) { if (modifier == DRM_FORMAT_MOD_LINEAR) {
drm_dbg_kms(vop2->drm, "Cluster window only supports format with afbc\n"); returnfalse;
}
}
}
if (format == DRM_FORMAT_XRGB2101010 || format == DRM_FORMAT_XBGR2101010) { if (vop2->version == VOP_VERSION_RK3588) { if (!rockchip_afbc(plane, modifier)) {
drm_dbg_kms(vop2->drm, "Only support 32 bpp format with afbc\n"); returnfalse;
}
}
}
if (modifier == DRM_FORMAT_MOD_LINEAR) returntrue;
if (!rockchip_afbc(plane, modifier)) {
drm_dbg_kms(vop2->drm, "Unsupported format modifier 0x%llx\n",
modifier);
returnfalse;
}
return vop2_convert_afbc_format(format) >= 0;
}
/* * 0: Full mode, 16 lines for one tail * 1: half block mode, 8 lines one tail
*/ staticbool vop2_half_block_enable(struct drm_plane_state *pstate)
{ if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90)) returnfalse; else returntrue;
}
/* * A Cluster window has 2048 x 16 line buffer, which can * works at 2048 x 16(Full) or 4096 x 8 (Half) mode. * for Cluster_lb_mode register: * 0: half mode, for plane input width range 2048 ~ 4096 * 1: half mode, for cluster work at 2 * 2048 plane mode * 2: half mode, for rotate_90/270 mode *
*/ staticint vop2_get_cluster_lb_mode(struct vop2_win *win, struct drm_plane_state *pstate)
{ if ((pstate->rotation & DRM_MODE_ROTATE_270) ||
(pstate->rotation & DRM_MODE_ROTATE_90)) return 2; else return 0;
}
/* * RK3568 VOP Esmart/Smart dsp_w should be even pixel * at scale down mode
*/ if (!(win->data->feature & WIN_FEATURE_AFBDC)) { if ((hor_scl_mode == SCALE_DOWN) && (dst_w & 0x1)) {
drm_dbg(vop2->drm, "%s dst_w[%d] should align as 2 pixel\n",
win->data->name, dst_w);
dst_w++;
}
}
val = vop2_scale_factor(src_w, dst_w);
vop2_win_write(win, VOP2_WIN_SCALE_YRGB_X, val);
val = vop2_scale_factor(src_h, dst_h);
vop2_win_write(win, VOP2_WIN_SCALE_YRGB_Y, val);
staticint vop2_convert_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;
}
}
staticvoid vop2_enable(struct vop2 *vop2)
{ int ret;
u32 version;
ret = pm_runtime_resume_and_get(vop2->dev); if (ret < 0) {
drm_err(vop2->drm, "failed to get pm runtime: %d\n", ret); return;
}
ret = vop2_core_clks_prepare_enable(vop2); if (ret) {
pm_runtime_put_sync(vop2->dev); return;
}
ret = rockchip_drm_dma_attach_device(vop2->drm, vop2->dev); if (ret) {
drm_err(vop2->drm, "failed to attach dma mapping, %d\n", ret); return;
}
version = vop2_readl(vop2, RK3568_VERSION_INFO); if (version != vop2->version) {
drm_err(vop2->drm, "Hardware version(0x%08x) mismatch\n", version); return;
}
/* * rk3566 share the same vop version with rk3568, so * we need to use soc_id for identification here.
*/ if (vop2->data->soc_id == 3566)
vop2_writel(vop2, RK3568_OTP_WIN_EN, 1);
if (vop2->version == VOP_VERSION_RK3588)
rk3588_vop2_power_domain_enable_all(vop2);
/* * Disable auto gating, this is a workaround to * avoid display image shift when a window enabled.
*/
regmap_clear_bits(vop2->map, RK3568_SYS_AUTO_GATING_CTRL,
RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN);
/* * Vop standby will take effect at end of current frame, * if dsp hold valid irq happen, it means standby complete. * * we must wait standby complete when we want to disable aclk, * if not, memory bus maybe dead.
*/
reinit_completion(&vp->dsp_hold_completion);
ret = wait_for_completion_timeout(&vp->dsp_hold_completion,
msecs_to_jiffies(50)); if (!ret)
drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id);
vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID);
if (vp->dclk_src)
clk_set_parent(vp->dclk, vp->dclk_src);
clk_disable_unprepare(vp->dclk);
vop2->enable_count--;
if (!vop2->enable_count)
vop2_disable(vop2);
vop2_unlock(vop2);
if (crtc->state->event && !crtc->state->active) {
spin_lock_irq(&crtc->dev->event_lock);
drm_crtc_send_vblank_event(crtc, crtc->state->event);
spin_unlock_irq(&crtc->dev->event_lock);
/* * Src.x1 can be odd when do clip, but yuv plane start point * need align with 2 pixel.
*/ if (fb->format->is_yuv && ((pstate->src.x1 >> 16) % 2)) {
drm_err(vop2->drm, "Invalid Source: Yuv format not support odd xpos\n"); return -EINVAL;
}
/* * The color key is 10 bit, so all format should * convert to 10 bit here.
*/ staticvoid vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key)
{ struct drm_plane_state *pstate = plane->state; struct drm_framebuffer *fb = pstate->fb; struct vop2_win *win = to_vop2_win(plane);
u32 color_key_en = 0;
u32 r = 0;
u32 g = 0;
u32 b = 0;
switch (fb->format->format) { case DRM_FORMAT_RGB565: case DRM_FORMAT_BGR565:
r = (color_key & 0xf800) >> 11;
g = (color_key & 0x7e0) >> 5;
b = (color_key & 0x1f);
r <<= 5;
g <<= 4;
b <<= 5;
color_key_en = 1; break; case DRM_FORMAT_XRGB8888: case DRM_FORMAT_ARGB8888: case DRM_FORMAT_XBGR8888: case DRM_FORMAT_ABGR8888: case DRM_FORMAT_RGB888: case DRM_FORMAT_BGR888:
r = (color_key & 0xff0000) >> 16;
g = (color_key & 0xff00) >> 8;
b = (color_key & 0xff);
r <<= 2;
g <<= 2;
b <<= 2;
color_key_en = 1; break;
}
/* * can't update plane when vop2 is disabled.
*/ if (WARN_ON(!crtc)) return;
if (!pstate->visible) {
vop2_plane_atomic_disable(plane, state); return;
}
afbc_en = rockchip_afbc(plane, fb->modifier);
offset = (src->x1 >> 16) * fb->format->cpp[0];
/* * AFBC HDR_PTR must set to the zero offset of the framebuffer.
*/ if (afbc_en)
offset = 0; elseif (pstate->rotation & DRM_MODE_REFLECT_Y)
offset += ((src->y2 >> 16) - 1) * fb->pitches[0]; else
offset += (src->y1 >> 16) * fb->pitches[0];
rk_obj = to_rockchip_obj(fb->obj[0]);
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;
/* * AFBC pic_vir_width is count by pixel, this is different * with WIN_VIR_STRIDE.
*/
stride = (fb->pitches[0] << 3) / bpp; if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270))
drm_dbg_kms(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligned\n",
vp->id, win->data->name, stride);
/* It's for head stride, each head size is 16 byte */
stride = ALIGN(stride, block_w) / block_w * 16;
uv_swap = vop2_afbc_uv_swap(fb->format->format); /* * This is a workaround for crazy IC design, Cluster * and Esmart/Smart use different format configuration map: * YUV420_10BIT: 0x10 for Cluster, 0x14 for Esmart/Smart. * * This is one thing we can make the convert simple: * AFBCD decode all the YUV data to YUV444. So we just * set all the yuv 10 bit to YUV444_10.
*/ if (fb->format->is_yuv && bpp == 10)
format = VOP2_CLUSTER_YUV444_10;
if (vop2_cluster_window(win))
vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1);
vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format);
vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap); /* * On rk3566/8, this bit is auto gating enable, * but this function is not work well so we need * to disable it for these two platform. * On rk3588, and the following new soc(rk3528/rk3576), * this bit is gating disable, we should write 1 to * disable gating when enable afbc.
*/ if (vop2->version == VOP_VERSION_RK3568)
vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0); else
vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 1);
val = 0; if (hdisplay != hsize)
val |= RK3568_VP_POST_SCL_CTRL__HSCALEDOWN; if (vdisplay != vsize)
val |= RK3568_VP_POST_SCL_CTRL__VSCALEDOWN;
vop2_vp_write(vp, RK3568_VP_POST_SCL_CTRL, val);
ret = clk_prepare_enable(vp->dclk); if (ret < 0) {
drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n",
vp->id, ret);
vop2_unlock(vop2); return;
}
/* * for drive a high resolution(4KP120, 8K), vop on rk3588/rk3576 need * process multi(1/2/4/8) pixels per cycle, so the dclk feed by the * system cru may be the 1/2 or 1/4 of mode->clock.
*/
clock = vop2->ops->setup_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags);
}
/* * Switch to HDMI PHY PLL as DCLK source for display modes up * to 4K@60Hz, if available, otherwise keep using the system CRU.
*/ if ((vop2->pll_hdmiphy0 || vop2->pll_hdmiphy1) && clock <= VOP2_MAX_DCLK_RATE) {
drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) { struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
if (rkencoder->crtc_endpoint_id == ROCKCHIP_VOP2_EP_HDMI0) { if (!vop2->pll_hdmiphy0) break;
if (!vp->dclk_src)
vp->dclk_src = clk_get_parent(vp->dclk);
ret = clk_set_parent(vp->dclk, vop2->pll_hdmiphy0); if (ret < 0)
drm_warn(vop2->drm, "Could not switch to HDMI0 PHY PLL: %d\n", ret); break;
}
if (rkencoder->crtc_endpoint_id == ROCKCHIP_VOP2_EP_HDMI1) { if (!vop2->pll_hdmiphy1) break;
if (!vp->dclk_src)
vp->dclk_src = clk_get_parent(vp->dclk);
ret = clk_set_parent(vp->dclk, vop2->pll_hdmiphy1); if (ret < 0)
drm_warn(vop2->drm, "Could not switch to HDMI1 PHY PLL: %d\n", ret); break;
}
}
}
if (!vop2_supports_seamless_gamma_lut_update(vop2) && vop2_gamma_lut_in_use(vop2, vp)) {
drm_info(vop2->drm, "Gamma LUT can be enabled for only one CRTC at a time\n"); return -EINVAL;
}
/* In case of modeset, gamma lut update already happened in atomic enable */ if (!drm_atomic_crtc_needs_modeset(crtc_state) && crtc_state->color_mgmt_changed)
vop2_crtc_atomic_try_set_gamma_locked(vop2, vp, crtc, crtc_state);
root = debugfs_create_dir("vop2", minor->debugfs_root); if (!IS_ERR(root)) { for (i = 0; i < ARRAY_SIZE(vop2_debugfs_list); i++)
vop2_debugfs_list[i].data = vop2;
if (irqs & VP_INT_DSP_HOLD_VALID) {
complete(&vp->dsp_hold_completion);
ret = IRQ_HANDLED;
}
if (irqs & VP_INT_FS_FIELD) {
drm_crtc_handle_vblank(crtc);
spin_lock(&crtc->dev->event_lock); if (vp->event) {
u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
if (irqs & VP_INT_POST_BUF_EMPTY) {
drm_err_ratelimited(vop2->drm, "POST_BUF_EMPTY irq err at vp%d\n", vp->id);
ret = IRQ_HANDLED;
}
pm_runtime_put(vop2->dev);
return ret;
}
static irqreturn_t vop2_isr(int irq, void *data)
{ struct vop2 *vop2 = data; conststruct vop2_data *vop2_data = vop2->data;
u32 axi_irqs[VOP2_SYS_AXI_BUS_NUM]; int ret = IRQ_NONE; int i;
/* * The irq is shared with the iommu. If the runtime-pm state of the * vop2-device is disabled the irq has to be targeted at the iommu.
*/ if (!pm_runtime_get_if_in_use(vop2->dev)) return IRQ_NONE;
if (vop2->version < VOP_VERSION_RK3576) { for (i = 0; i < vop2_data->nr_vps; i++) { struct vop2_video_port *vp = &vop2->vps[i]; struct drm_crtc *crtc = &vp->crtc;
u32 irqs;
if (irqs & VP_INT_DSP_HOLD_VALID) {
complete(&vp->dsp_hold_completion);
ret = IRQ_HANDLED;
}
if (irqs & VP_INT_FS_FIELD) {
drm_crtc_handle_vblank(crtc);
spin_lock(&crtc->dev->event_lock); if (vp->event) {
u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
for (i = 0; i < ARRAY_SIZE(axi_irqs); i++) { if (axi_irqs[i] & VOP2_INT_BUS_ERRPR) {
drm_err_ratelimited(vop2->drm, "BUS_ERROR irq err\n");
ret = IRQ_HANDLED;
}
}
/* * On RK3566 these windows don't have an independent * framebuffer. They can only share/mirror the framebuffer * with smart0, esmart0 and cluster0 respectively. * And RK3566 share the same vop version with Rk3568, so we * need to use soc_id for identification here.
*/ staticbool vop2_is_mirror_win(struct vop2_win *win)
{ struct vop2 *vop2 = win->vop2;
if (vop2->data->soc_id == 3566) { switch (win->data->phys_id) { case ROCKCHIP_VOP2_SMART1: case ROCKCHIP_VOP2_ESMART1: case ROCKCHIP_VOP2_CLUSTER1:
--> --------------------
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.