/* * 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.
*/ staticconst uint32_t bt601_yuv2rgb[] = {
0x4A8, 0x0, 0x662,
0x4A8, 0x1E6F, 0x1CBF,
0x4A8, 0x812, 0x0,
0x321168, 0x0877CF, 0x2EB127
};
/* physical map length of vop register */
uint32_t len;
/* one time only one process allowed to config the register */
spinlock_t reg_lock; /* lock vop irq reg */
spinlock_t irq_lock; /* protects crtc enable/disable */ struct mutex vop_lock;
staticbool has_rb_swapped(uint32_t version, uint32_t format)
{ switch (format) { case DRM_FORMAT_XBGR8888: case DRM_FORMAT_ABGR8888: case DRM_FORMAT_BGR565: returntrue; /* * full framework (IP version 3.x) only need rb swapped for RGB888 and * little framework (IP version 2.x) only need rb swapped for BGR888, * check for 3.x to also only rb swap BGR888 for unknown vop version
*/ case DRM_FORMAT_RGB888: return VOP_MAJOR(version) == 3; case DRM_FORMAT_BGR888: return VOP_MAJOR(version) != 3; default: returnfalse;
}
}
staticbool has_uv_swapped(uint32_t format)
{ switch (format) { case DRM_FORMAT_NV21: case DRM_FORMAT_NV61: case DRM_FORMAT_NV42: returntrue; default: returnfalse;
}
}
staticbool is_fmt_10(uint32_t format)
{ switch (format) { case DRM_FORMAT_NV15: case DRM_FORMAT_NV20: case DRM_FORMAT_NV30: returntrue; default: returnfalse;
}
}
staticenum vop_data_format vop_convert_format(uint32_t format)
{ switch (format) { case DRM_FORMAT_XRGB8888: case DRM_FORMAT_ARGB8888: case DRM_FORMAT_XBGR8888: case DRM_FORMAT_ABGR8888: return VOP_FMT_ARGB8888; case DRM_FORMAT_RGB888: case DRM_FORMAT_BGR888: return VOP_FMT_RGB888; case DRM_FORMAT_RGB565: case DRM_FORMAT_BGR565: return VOP_FMT_RGB565; case DRM_FORMAT_NV12: case DRM_FORMAT_NV15: case DRM_FORMAT_NV21: return VOP_FMT_YUV420SP; case DRM_FORMAT_NV16: case DRM_FORMAT_NV20: case DRM_FORMAT_NV61: return VOP_FMT_YUV422SP; case DRM_FORMAT_NV24: case DRM_FORMAT_NV30: case DRM_FORMAT_NV42: return VOP_FMT_YUV444SP; default:
DRM_ERROR("unsupported format[%08x]\n", format); return -EINVAL;
}
}
staticint vop_convert_afbc_format(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; default:
DRM_DEBUG_KMS("unsupported AFBC format[%08x]\n", format); return -EINVAL;
}
}
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)
{
uint16_t val = 1 << SCL_FT_DEFAULT_FIXPOINT_SHIFT;
if (vskiplines)
*vskiplines = 0;
if (is_horizontal) { if (mode == SCALE_UP)
val = GET_SCL_FT_BIC(src, dst); elseif (mode == SCALE_DOWN)
val = GET_SCL_FT_BILI_DN(src, dst);
} else { if (mode == SCALE_UP) { if (vsu_mode == SCALE_UP_BIL)
val = GET_SCL_FT_BILI_UP(src, dst); else
val = GET_SCL_FT_BIC(src, dst);
} elseif (mode == SCALE_DOWN) { if (vskiplines) {
*vskiplines = scl_get_vskiplines(src, dst);
val = scl_get_bili_dn_vskip(src, dst,
*vskiplines);
} else {
val = GET_SCL_FT_BILI_DN(src, dst);
}
}
}
/* * (1) each frame starts at the start of the Vsync pulse which is signaled by * the "FRAME_SYNC" interrupt. * (2) the active data region of each frame ends at dsp_vact_end * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num, * to get "LINE_FLAG" interrupt at the end of the active on screen data. * * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end * Interrupts * LINE_FLAG -------------------------------+ * FRAME_SYNC ----+ | * | | * v v * | Vsync | Vbp | Vactive | Vfp | * ^ ^ ^ ^ * | | | | * | | | | * dsp_vs_end ------------+ | | | VOP_DSP_VTOTAL_VS_END * dsp_vact_start --------------+ | | VOP_DSP_VACT_ST_END * dsp_vact_end ----------------------------+ | VOP_DSP_VACT_ST_END * dsp_total -------------------------------------+ VOP_DSP_VTOTAL_VS_END
*/ staticbool vop_line_flag_irq_is_enabled(struct vop *vop)
{
uint32_t line_flag_irq; unsignedlong flags;
ret = pm_runtime_resume_and_get(vop->dev); if (ret < 0) {
DRM_DEV_ERROR(vop->dev, "failed to get pm runtime: %d\n", ret); return ret;
}
ret = vop_core_clks_enable(vop); if (WARN_ON(ret < 0)) goto err_put_pm_runtime;
ret = clk_enable(vop->dclk); if (WARN_ON(ret < 0)) goto err_disable_core;
/* * 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); 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);
}
}
if (vop->data->afbc) { struct rockchip_crtc_state *s; /* * Disable AFBC and forget there was a vop window with AFBC
*/
VOP_AFBC_SET(vop, enable, 0);
s = to_rockchip_crtc_state(crtc->state);
s->enable_afbc = false;
}
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;
if (crtc->state->self_refresh_active) {
rockchip_drm_set_win_enabled(crtc, false); goto out;
}
mutex_lock(&vop->vop_lock);
drm_crtc_vblank_off(crtc);
/* * 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(&vop->dsp_hold_completion);
vop_dsp_hold_valid_irq_enable(vop);
spin_lock(&vop->reg_lock);
VOP_REG_SET(vop, common, standby, 1);
spin_unlock(&vop->reg_lock);
if (!wait_for_completion_timeout(&vop->dsp_hold_completion,
msecs_to_jiffies(200)))
WARN(1, "%s: timed out waiting for DSP hold", crtc->name);
vop_dsp_hold_valid_irq_disable(vop);
vop->is_enabled = false;
/* * vop standby complete, so iommu detach is safe.
*/
rockchip_drm_dma_detach_device(vop->drm_dev, vop->dev);
crtc_state = drm_atomic_get_existing_crtc_state(state,
crtc); if (WARN_ON(!crtc_state)) return -EINVAL;
ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
min_scale, max_scale, true, true); if (ret) return ret;
if (!new_plane_state->visible) return 0;
ret = vop_convert_format(fb->format->format); if (ret < 0) return ret;
/* * Src.x1 can be odd when do clip, but yuv plane start point * need align with 2 pixel.
*/ if (fb->format->is_yuv && ((new_plane_state->src.x1 >> 16) % 2)) {
DRM_DEBUG_KMS("Invalid Source: Yuv format not support odd xpos\n"); return -EINVAL;
}
if (fb->format->is_yuv && new_plane_state->rotation & DRM_MODE_REFLECT_Y) {
DRM_DEBUG_KMS("Invalid Source: Yuv format does not support this rotation\n"); return -EINVAL;
}
if (rockchip_afbc(fb->modifier)) { struct vop *vop = to_vop(crtc);
if (!vop->data->afbc) {
DRM_DEBUG_KMS("vop does not support AFBC\n"); return -EINVAL;
}
ret = vop_convert_afbc_format(fb->format->format); if (ret < 0) return ret;
if (new_plane_state->src.x1 || new_plane_state->src.y1) {
DRM_DEBUG_KMS("AFBC does not support offset display, " \ "xpos=%d, ypos=%d, offset=%d\n",
new_plane_state->src.x1, new_plane_state->src.y1,
fb->offsets[0]); return -EINVAL;
}
if (new_plane_state->rotation && new_plane_state->rotation != DRM_MODE_ROTATE_0) {
DRM_DEBUG_KMS("No rotation support in AFBC, rotation=%d\n",
new_plane_state->rotation); return -EINVAL;
}
}
/* * For y-mirroring we need to move address * to the beginning of the last line.
*/ if (new_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);
/* * 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) {
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);
VOP_WIN_SET(vop, win, src_alpha_ctl, val);
if (vop->is_enabled) {
vop_plane_atomic_update(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);
}
}
}
/* * Clock craziness. * * Key points: * * - DRM works 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. Try to set the exact rate first, and confirm the clock framework * can provide it. * * 2. If the clock framework cannot provide the exact rate, we should * add 999 Hz to the requested rate. 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. * * 3. Get the clock framework to round the rate for us to tell us * what it will actually make. * * 4. Store the rounded up rate so that we don't need to worry about * this in the actual clk_set_rate().
*/
rate = clk_round_rate(vop->dclk, adjusted_mode->clock * 1000); if (rate / 1000 != adjusted_mode->clock)
rate = clk_round_rate(vop->dclk,
adjusted_mode->clock * 1000 + 999);
adjusted_mode->clock = DIV_ROUND_UP(rate, 1000);
if (!state->gamma_lut || !VOP_HAS_REG(vop, common, update_gamma_lut)) { /* * 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;
} else { /* * On RK3399 the gamma LUT can updated without clearing dsp_lut_en, * by setting update_gamma_lut then waiting for lut_buffer_index change
*/
old_idx = vop_lut_buffer_index(vop);
}
if (VOP_HAS_REG(vop, common, update_gamma_lut)) {
ret = readx_poll_timeout(vop_lut_buffer_index, vop,
lut_idx, lut_idx != old_idx, 5, 30 * 1000); if (ret) {
DRM_DEV_ERROR(vop->dev, "gamma LUT update timeout!\n"); return;
}
/* * update_gamma_lut is auto cleared by HW, but write 0 to clear the bit * in our backup of the regs.
*/
spin_lock(&vop->reg_lock);
VOP_REG_SET(vop, common, update_gamma_lut, 0);
spin_unlock(&vop->reg_lock);
}
}
/* * Only update GAMMA if the 'active' flag is not changed, * otherwise it's updated by .atomic_enable.
*/ if (crtc_state->color_mgmt_changed &&
!crtc_state->active_changed)
vop_crtc_gamma_set(vop, crtc, old_crtc_state);
}
/* * 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 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.
*/ if (crtc->state->gamma_lut)
vop_crtc_gamma_set(vop, crtc, old_state);
}
staticvoid vop_wait_for_irq_handler(struct vop *vop)
{ bool pending; int ret;
/* * Spin until frame start interrupt status bit goes low, which means * that interrupt handler was invoked and cleared it. The timeout of * 10 msecs is really too long, but it is just a safety measure if * something goes really wrong. The wait will only happen in the very * unlikely case of a vblank happening exactly at the same time and * shouldn't exceed microseconds range.
*/
ret = readx_poll_timeout_atomic(vop_fs_irq_is_pending, vop, pending,
!pending, 0, 10 * 1000); if (ret)
DRM_DEV_ERROR(vop->dev, "VOP vblank IRQ stuck for 10 ms\n");
/* 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);
vop_cfg_done(vop);
/* Ack the DMA transfer of the previous frame (RK3066). */ if (VOP_HAS_REG(vop, common, dma_stop))
VOP_REG_SET(vop, common, dma_stop, 0);
spin_unlock(&vop->reg_lock);
/* * There is a (rather unlikely) possiblity that a vblank interrupt * fired before we set the cfg_done bit. To avoid spuriously * signalling flip completion we need to wait for it to finish.
*/
vop_wait_for_irq_handler(vop);
spin_lock_irq(&crtc->dev->event_lock); if (crtc->state->event) {
WARN_ON(drm_crtc_vblank_get(crtc) != 0);
WARN_ON(vop->event);
connector = vop_get_edp_connector(vop); if (!connector) return -EINVAL;
if (source_name && strcmp(source_name, "auto") == 0)
ret = analogix_dp_start_crc(connector); elseif (!source_name)
ret = analogix_dp_stop_crc(connector); else
ret = -EINVAL;
/* * The irq is shared with the iommu. If the runtime-pm state of the * vop-device is disabled the irq has to be targeted at the iommu.
*/ if (!pm_runtime_get_if_in_use(vop->dev)) return IRQ_NONE;
if (vop_core_clks_enable(vop)) {
DRM_DEV_ERROR_RATELIMITED(vop->dev, "couldn't enable clocks\n"); goto out;
}
/* * 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);
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);
/* This is expected for vop iommu irqs, since the irq is shared */ if (!active_irqs) goto out_disable;
if (active_irqs & DSP_HOLD_VALID_INTR) {
complete(&vop->dsp_hold_completion);
active_irqs &= ~DSP_HOLD_VALID_INTR;
ret = IRQ_HANDLED;
}
if (active_irqs & LINE_FLAG_INTR) {
complete(&vop->line_flag_completion);
active_irqs &= ~LINE_FLAG_INTR;
ret = IRQ_HANDLED;
}
if (active_irqs & FS_INTR) {
drm_crtc_handle_vblank(crtc);
vop_handle_vblank(vop);
active_irqs &= ~FS_INTR;
ret = IRQ_HANDLED;
}
/* Unhandled irqs are spurious. */ if (active_irqs)
DRM_DEV_ERROR(vop->dev, "Unknown VOP IRQs: %#02x\n",
active_irqs);
/* * Create drm_plane for primary and cursor planes first, since we need * 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]; conststruct vop_win_data *win_data = vop_win->data;
if (win_data->type != DRM_PLANE_TYPE_PRIMARY &&
win_data->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); goto err_cleanup_planes;
}
/* * 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]; conststruct vop_win_data *win_data = vop_win->data; unsignedlong possible_crtcs = drm_crtc_mask(crtc);
if (win_data->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); goto err_cleanup_crtc;
}
drm_plane_helper_add(&vop_win->base, &plane_helper_funcs);
vop_plane_add_properties(&vop_win->base, win_data);
}
port = of_get_child_by_name(dev->of_node, "port"); if (!port) {
DRM_DEV_ERROR(vop->dev, "no port node found in %pOF\n",
dev->of_node);
ret = -ENOENT; goto err_cleanup_crtc;
}
/* * We need to cleanup the planes now. Why? * * The planes are "&vop->win[i].base". That means the memory is * all part of the big "struct vop" chunk of memory. That memory * was devm allocated and associated with this component. We need to * free it ourselves before vop_unbind() finishes.
*/
list_for_each_entry_safe(plane, tmp, &drm_dev->mode_config.plane_list,
head)
drm_plane_cleanup(plane);
/* * Destroy CRTC after vop_plane_destroy() since vop_disable_plane() * references the CRTC.
*/
drm_crtc_cleanup(crtc);
drm_flip_work_cleanup(&vop->fb_unref_work);
}
staticint vop_initial(struct vop *vop)
{ 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);
}
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_resume_and_get(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);
/* * 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);
for (i = 0; i < vop_data->win_size; i++) { struct vop_win *vop_win = &vop->win[i]; conststruct vop_win_data *win_data = &vop_data->win[i];
vop_win->data = win_data;
vop_win->vop = vop;
if (vop_data->win_yuv2yuv)
vop_win->yuv2yuv_data = &vop_data->win_yuv2yuv[i];
}
}
/** * rockchip_drm_wait_vact_end * @crtc: CRTC to enable line flag * @mstimeout: millisecond for timeout * * Wait for vact_end line flag irq or timeout. * * Returns: * Zero on success, negative errno on failure.
*/ int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsignedint mstimeout)
{ struct vop *vop = to_vop(crtc); unsignedlong jiffies_left; int ret = 0;
if (!crtc || !vop->is_enabled) return -ENODEV;
mutex_lock(&vop->vop_lock); if (mstimeout <= 0) {
ret = -EINVAL; goto out;
}
if (vop_line_flag_irq_is_enabled(vop)) {
ret = -EBUSY; goto out;
}
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.