From e09b41010ba33a20a87472ee821fa407a5b8da36 Mon Sep 17 00:00:00 2001 From: José Pekkarinen Date: Mon, 11 Apr 2016 10:41:07 +0300 Subject: These changes are the raw update to linux-4.4.6-rt14. Kernel sources are taken from kernel.org, and rt patch from the rt wiki download page. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During the rebasing, the following patch collided: Force tick interrupt and get rid of softirq magic(I70131fb85). Collisions have been removed because its logic was found on the source already. Change-Id: I7f57a4081d9deaa0d9ccfc41a6c8daccdee3b769 Signed-off-by: José Pekkarinen --- kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 362 ++++++++++++++++++--- 1 file changed, 315 insertions(+), 47 deletions(-) (limited to 'kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop.c') diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 4557f335a..03c47eead 100644 --- a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -49,6 +50,8 @@ #define VOP_WIN_SET(x, win, name, v) \ REG_SET(x, win->base, win->phy->name, v, RELAXED) +#define VOP_SCL_SET(x, win, name, v) \ + REG_SET(x, win->base, win->phy->scl->name, v, RELAXED) #define VOP_CTRL_SET(x, name, v) \ REG_SET(x, 0, (x)->data->ctrl->name, v, NORMAL) @@ -163,12 +166,43 @@ struct vop_ctrl { struct vop_reg vpost_st_end; }; +struct vop_scl_regs { + struct vop_reg cbcr_vsd_mode; + struct vop_reg cbcr_vsu_mode; + struct vop_reg cbcr_hsd_mode; + struct vop_reg cbcr_ver_scl_mode; + struct vop_reg cbcr_hor_scl_mode; + struct vop_reg yrgb_vsd_mode; + struct vop_reg yrgb_vsu_mode; + struct vop_reg yrgb_hsd_mode; + struct vop_reg yrgb_ver_scl_mode; + struct vop_reg yrgb_hor_scl_mode; + struct vop_reg line_load_mode; + struct vop_reg cbcr_axi_gather_num; + struct vop_reg yrgb_axi_gather_num; + struct vop_reg vsd_cbcr_gt2; + struct vop_reg vsd_cbcr_gt4; + struct vop_reg vsd_yrgb_gt2; + struct vop_reg vsd_yrgb_gt4; + struct vop_reg bic_coe_sel; + struct vop_reg cbcr_axi_gather_en; + struct vop_reg yrgb_axi_gather_en; + + struct vop_reg lb_mode; + struct vop_reg scale_yrgb_x; + struct vop_reg scale_yrgb_y; + struct vop_reg scale_cbcr_x; + struct vop_reg scale_cbcr_y; +}; + struct vop_win_phy { + const struct vop_scl_regs *scl; const uint32_t *data_formats; uint32_t nformats; struct vop_reg enable; struct vop_reg format; + struct vop_reg rb_swap; struct vop_reg act_info; struct vop_reg dsp_info; struct vop_reg dsp_st; @@ -198,8 +232,12 @@ struct vop_data { static const uint32_t formats_01[] = { 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, @@ -208,15 +246,49 @@ static const uint32_t formats_01[] = { static const uint32_t formats_234[] = { DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ABGR8888, DRM_FORMAT_RGB888, + DRM_FORMAT_BGR888, DRM_FORMAT_RGB565, + DRM_FORMAT_BGR565, +}; + +static const struct vop_scl_regs win_full_scl = { + .cbcr_vsd_mode = VOP_REG(WIN0_CTRL1, 0x1, 31), + .cbcr_vsu_mode = VOP_REG(WIN0_CTRL1, 0x1, 30), + .cbcr_hsd_mode = VOP_REG(WIN0_CTRL1, 0x3, 28), + .cbcr_ver_scl_mode = VOP_REG(WIN0_CTRL1, 0x3, 26), + .cbcr_hor_scl_mode = VOP_REG(WIN0_CTRL1, 0x3, 24), + .yrgb_vsd_mode = VOP_REG(WIN0_CTRL1, 0x1, 23), + .yrgb_vsu_mode = VOP_REG(WIN0_CTRL1, 0x1, 22), + .yrgb_hsd_mode = VOP_REG(WIN0_CTRL1, 0x3, 20), + .yrgb_ver_scl_mode = VOP_REG(WIN0_CTRL1, 0x3, 18), + .yrgb_hor_scl_mode = VOP_REG(WIN0_CTRL1, 0x3, 16), + .line_load_mode = VOP_REG(WIN0_CTRL1, 0x1, 15), + .cbcr_axi_gather_num = VOP_REG(WIN0_CTRL1, 0x7, 12), + .yrgb_axi_gather_num = VOP_REG(WIN0_CTRL1, 0xf, 8), + .vsd_cbcr_gt2 = VOP_REG(WIN0_CTRL1, 0x1, 7), + .vsd_cbcr_gt4 = VOP_REG(WIN0_CTRL1, 0x1, 6), + .vsd_yrgb_gt2 = VOP_REG(WIN0_CTRL1, 0x1, 5), + .vsd_yrgb_gt4 = VOP_REG(WIN0_CTRL1, 0x1, 4), + .bic_coe_sel = VOP_REG(WIN0_CTRL1, 0x3, 2), + .cbcr_axi_gather_en = VOP_REG(WIN0_CTRL1, 0x1, 1), + .yrgb_axi_gather_en = VOP_REG(WIN0_CTRL1, 0x1, 0), + .lb_mode = VOP_REG(WIN0_CTRL0, 0x7, 5), + .scale_yrgb_x = VOP_REG(WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0), + .scale_yrgb_y = VOP_REG(WIN0_SCL_FACTOR_YRGB, 0xffff, 16), + .scale_cbcr_x = VOP_REG(WIN0_SCL_FACTOR_CBR, 0xffff, 0x0), + .scale_cbcr_y = VOP_REG(WIN0_SCL_FACTOR_CBR, 0xffff, 16), }; static const struct vop_win_phy win01_data = { + .scl = &win_full_scl, .data_formats = formats_01, .nformats = ARRAY_SIZE(formats_01), .enable = VOP_REG(WIN0_CTRL0, 0x1, 0), .format = VOP_REG(WIN0_CTRL0, 0x7, 1), + .rb_swap = VOP_REG(WIN0_CTRL0, 0x1, 12), .act_info = VOP_REG(WIN0_ACT_INFO, 0x1fff1fff, 0), .dsp_info = VOP_REG(WIN0_DSP_INFO, 0x0fff0fff, 0), .dsp_st = VOP_REG(WIN0_DSP_ST, 0x1fff1fff, 0), @@ -233,6 +305,7 @@ static const struct vop_win_phy win23_data = { .nformats = ARRAY_SIZE(formats_234), .enable = VOP_REG(WIN2_CTRL0, 0x1, 0), .format = VOP_REG(WIN2_CTRL0, 0x7, 1), + .rb_swap = VOP_REG(WIN2_CTRL0, 0x1, 12), .dsp_info = VOP_REG(WIN2_DSP_INFO0, 0x0fff0fff, 0), .dsp_st = VOP_REG(WIN2_DSP_ST0, 0x1fff1fff, 0), .yrgb_mst = VOP_REG(WIN2_MST0, 0xffffffff, 0), @@ -241,15 +314,6 @@ static const struct vop_win_phy win23_data = { .dst_alpha_ctl = VOP_REG(WIN2_DST_ALPHA_CTRL, 0xff, 0), }; -static const struct vop_win_phy cursor_data = { - .data_formats = formats_234, - .nformats = ARRAY_SIZE(formats_234), - .enable = VOP_REG(HWC_CTRL0, 0x1, 0), - .format = VOP_REG(HWC_CTRL0, 0x7, 1), - .dsp_st = VOP_REG(HWC_DSP_ST, 0x1fff1fff, 0), - .yrgb_mst = VOP_REG(HWC_MST, 0xffffffff, 0), -}; - static const struct vop_ctrl ctrl_data = { .standby = VOP_REG(SYS_CTRL, 0x1, 22), .gate_en = VOP_REG(SYS_CTRL, 0x1, 23), @@ -276,19 +340,25 @@ static const struct vop_reg_data vop_init_reg_table[] = { {DSP_CTRL0, 0x00000000}, {WIN0_CTRL0, 0x00000080}, {WIN1_CTRL0, 0x00000080}, + /* TODO: Win2/3 support multiple area function, but we haven't found + * a suitable way to use it yet, so let's just use them as other windows + * with only area 0 enabled. + */ + {WIN2_CTRL0, 0x00000010}, + {WIN3_CTRL0, 0x00000010}, }; /* * Note: rk3288 has a dedicated 'cursor' window, however, that window requires * special support to get alpha blending working. For now, just use overlay - * window 1 for the drm cursor. + * window 3 for the drm cursor. + * */ static const struct vop_win_data rk3288_vop_win_data[] = { { .base = 0x00, .phy = &win01_data, .type = DRM_PLANE_TYPE_PRIMARY }, - { .base = 0x40, .phy = &win01_data, .type = DRM_PLANE_TYPE_CURSOR }, + { .base = 0x40, .phy = &win01_data, .type = DRM_PLANE_TYPE_OVERLAY }, { .base = 0x00, .phy = &win23_data, .type = DRM_PLANE_TYPE_OVERLAY }, - { .base = 0x50, .phy = &win23_data, .type = DRM_PLANE_TYPE_OVERLAY }, - { .base = 0x00, .phy = &cursor_data, .type = DRM_PLANE_TYPE_OVERLAY }, + { .base = 0x50, .phy = &win23_data, .type = DRM_PLANE_TYPE_CURSOR }, }; static const struct vop_data rk3288_vop = { @@ -304,6 +374,7 @@ static const struct of_device_id vop_driver_dt_match[] = { .data = &rk3288_vop }, {}, }; +MODULE_DEVICE_TABLE(of, vop_driver_dt_match); static inline void vop_writel(struct vop *vop, uint32_t offset, uint32_t v) { @@ -351,15 +422,32 @@ static inline void vop_mask_write_relaxed(struct vop *vop, uint32_t offset, } } +static bool has_rb_swapped(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_BGR888: + case DRM_FORMAT_BGR565: + return true; + default: + return false; + } +} + static enum 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: return VOP_FMT_YUV420SP; @@ -373,16 +461,149 @@ static enum vop_data_format vop_convert_format(uint32_t format) } } +static bool is_yuv_support(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV24: + 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, + uint32_t dst, bool is_horizontal, + int vsu_mode, int *vskiplines) +{ + uint16_t val = 1 << SCL_FT_DEFAULT_FIXPOINT_SHIFT; + + if (is_horizontal) { + if (mode == SCALE_UP) + val = GET_SCL_FT_BIC(src, dst); + else if (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); + } else if (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); + } + } + } + + 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, 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; + int hsub = drm_format_horz_chroma_subsampling(pixel_format); + int vsub = drm_format_vert_chroma_subsampling(pixel_format); + bool is_yuv = is_yuv_support(pixel_format); + 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; + int vskiplines; + + if (dst_w > 3840) { + DRM_ERROR("Maximum destination width (3840) exceeded\n"); + return; + } + + yrgb_hor_scl_mode = scl_get_scl_mode(src_w, dst_w); + yrgb_ver_scl_mode = scl_get_scl_mode(src_h, dst_h); + + if (is_yuv) { + cbcr_hor_scl_mode = scl_get_scl_mode(cbcr_src_w, dst_w); + cbcr_ver_scl_mode = scl_get_scl_mode(cbcr_src_h, dst_h); + if (cbcr_hor_scl_mode == SCALE_DOWN) + lb_mode = scl_vop_cal_lb_mode(dst_w, true); + else + lb_mode = scl_vop_cal_lb_mode(cbcr_src_w, true); + } else { + if (yrgb_hor_scl_mode == SCALE_DOWN) + lb_mode = scl_vop_cal_lb_mode(dst_w, false); + else + lb_mode = scl_vop_cal_lb_mode(src_w, false); + } + + VOP_SCL_SET(vop, win, lb_mode, lb_mode); + if (lb_mode == LB_RGB_3840X2) { + if (yrgb_ver_scl_mode != SCALE_NONE) { + DRM_ERROR("ERROR : not allow yrgb ver scale\n"); + return; + } + if (cbcr_ver_scl_mode != SCALE_NONE) { + DRM_ERROR("ERROR : not allow cbcr ver scale\n"); + return; + } + vsu_mode = SCALE_UP_BIL; + } else if (lb_mode == LB_RGB_2560X4) { + vsu_mode = SCALE_UP_BIL; + } else { + vsu_mode = SCALE_UP_BIC; + } + + val = scl_vop_cal_scale(yrgb_hor_scl_mode, src_w, dst_w, + true, 0, NULL); + VOP_SCL_SET(vop, win, scale_yrgb_x, val); + val = scl_vop_cal_scale(yrgb_ver_scl_mode, src_h, dst_h, + false, vsu_mode, &vskiplines); + VOP_SCL_SET(vop, win, scale_yrgb_y, val); + + VOP_SCL_SET(vop, win, vsd_yrgb_gt4, vskiplines == 4); + VOP_SCL_SET(vop, win, vsd_yrgb_gt2, vskiplines == 2); + + VOP_SCL_SET(vop, win, yrgb_hor_scl_mode, yrgb_hor_scl_mode); + VOP_SCL_SET(vop, win, yrgb_ver_scl_mode, yrgb_ver_scl_mode); + VOP_SCL_SET(vop, win, yrgb_hsd_mode, SCALE_DOWN_BIL); + VOP_SCL_SET(vop, win, yrgb_vsd_mode, SCALE_DOWN_BIL); + VOP_SCL_SET(vop, win, yrgb_vsu_mode, vsu_mode); + if (is_yuv) { + val = scl_vop_cal_scale(cbcr_hor_scl_mode, cbcr_src_w, + dst_w, true, 0, NULL); + VOP_SCL_SET(vop, win, scale_cbcr_x, val); + val = scl_vop_cal_scale(cbcr_ver_scl_mode, cbcr_src_h, + dst_h, false, vsu_mode, &vskiplines); + VOP_SCL_SET(vop, win, scale_cbcr_y, val); + + VOP_SCL_SET(vop, win, vsd_cbcr_gt4, vskiplines == 4); + VOP_SCL_SET(vop, win, vsd_cbcr_gt2, vskiplines == 2); + VOP_SCL_SET(vop, win, cbcr_hor_scl_mode, cbcr_hor_scl_mode); + VOP_SCL_SET(vop, win, cbcr_ver_scl_mode, cbcr_ver_scl_mode); + VOP_SCL_SET(vop, win, cbcr_hsd_mode, SCALE_DOWN_BIL); + VOP_SCL_SET(vop, win, cbcr_vsd_mode, SCALE_DOWN_BIL); + VOP_SCL_SET(vop, win, cbcr_vsu_mode, vsu_mode); + } +} + static void vop_dsp_hold_valid_irq_enable(struct vop *vop) { unsigned long flags; @@ -457,6 +678,7 @@ static void vop_enable(struct drm_crtc *crtc) goto err_disable_aclk; } + memcpy(vop->regs, vop->regsbak, vop->len); /* * At here, vop clock & iommu is enable, R/W vop regs would be safe. */ @@ -577,16 +799,22 @@ static int vop_update_plane_event(struct drm_plane *plane, struct vop *vop = to_vop(crtc); struct drm_gem_object *obj; struct rockchip_gem_object *rk_obj; + struct drm_gem_object *uv_obj; + struct rockchip_gem_object *rk_uv_obj; unsigned long offset; unsigned int actual_w; unsigned int actual_h; unsigned int dsp_stx; unsigned int dsp_sty; unsigned int y_vir_stride; + unsigned int uv_vir_stride = 0; dma_addr_t yrgb_mst; + dma_addr_t uv_mst = 0; enum vop_data_format format; uint32_t val; bool is_alpha; + bool rb_swap; + bool is_yuv; bool visible; int ret; struct drm_rect dest = { @@ -607,11 +835,15 @@ static int vop_update_plane_event(struct drm_plane *plane, .y2 = crtc->mode.vdisplay, }; bool can_position = plane->type != DRM_PLANE_TYPE_PRIMARY; + 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; ret = drm_plane_helper_check_update(plane, crtc, fb, &src, &dest, &clip, - DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, + min_scale, + max_scale, can_position, false, &visible); if (ret) return ret; @@ -620,6 +852,9 @@ static int vop_update_plane_event(struct drm_plane *plane, return 0; is_alpha = is_alpha_support(fb->pixel_format); + rb_swap = has_rb_swapped(fb->pixel_format); + is_yuv = is_yuv_support(fb->pixel_format); + format = vop_convert_format(fb->pixel_format); if (format < 0) return format; @@ -632,19 +867,46 @@ static int vop_update_plane_event(struct drm_plane *plane, rk_obj = to_rockchip_obj(obj); + if (is_yuv) { + /* + * Src.x1 can be odd when do clip, but yuv plane start point + * need align with 2 pixel. + */ + val = (src.x1 >> 16) % 2; + src.x1 += val << 16; + src.x2 += val << 16; + } + actual_w = (src.x2 - src.x1) >> 16; actual_h = (src.y2 - src.y1) >> 16; - crtc_x = max(0, crtc_x); - crtc_y = max(0, crtc_y); - dsp_stx = crtc_x + crtc->mode.htotal - crtc->mode.hsync_start; - dsp_sty = crtc_y + crtc->mode.vtotal - crtc->mode.vsync_start; + dsp_stx = dest.x1 + crtc->mode.htotal - crtc->mode.hsync_start; + dsp_sty = dest.y1 + crtc->mode.vtotal - crtc->mode.vsync_start; - offset = (src.x1 >> 16) * (fb->bits_per_pixel >> 3); + offset = (src.x1 >> 16) * drm_format_plane_cpp(fb->pixel_format, 0); offset += (src.y1 >> 16) * fb->pitches[0]; - yrgb_mst = rk_obj->dma_addr + offset; - y_vir_stride = fb->pitches[0] / (fb->bits_per_pixel >> 3); + yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0]; + y_vir_stride = fb->pitches[0] >> 2; + + if (is_yuv) { + int hsub = drm_format_horz_chroma_subsampling(fb->pixel_format); + int vsub = drm_format_vert_chroma_subsampling(fb->pixel_format); + int bpp = drm_format_plane_cpp(fb->pixel_format, 1); + + uv_obj = rockchip_fb_get_gem_obj(fb, 1); + if (!uv_obj) { + DRM_ERROR("fail to get uv object from framebuffer\n"); + return -EINVAL; + } + rk_uv_obj = to_rockchip_obj(uv_obj); + uv_vir_stride = fb->pitches[1] >> 2; + + offset = (src.x1 >> 16) * bpp / hsub; + offset += (src.y1 >> 16) * fb->pitches[1] / vsub; + + uv_mst = rk_uv_obj->dma_addr + offset + fb->offsets[1]; + } /* * If this plane update changes the plane's framebuffer, (or more @@ -681,13 +943,27 @@ static int vop_update_plane_event(struct drm_plane *plane, VOP_WIN_SET(vop, win, format, format); VOP_WIN_SET(vop, win, yrgb_vir, y_vir_stride); VOP_WIN_SET(vop, win, yrgb_mst, yrgb_mst); + if (is_yuv) { + VOP_WIN_SET(vop, win, uv_vir, uv_vir_stride); + VOP_WIN_SET(vop, win, uv_mst, uv_mst); + } + + if (win->phy->scl) + scl_vop_cal_scl_fac(vop, win, actual_w, actual_h, + dest.x2 - dest.x1, dest.y2 - dest.y1, + fb->pixel_format); + val = (actual_h - 1) << 16; val |= (actual_w - 1) & 0xffff; VOP_WIN_SET(vop, win, act_info, val); + + val = (dest.y2 - dest.y1 - 1) << 16; + val |= (dest.x2 - dest.x1 - 1) & 0xffff; VOP_WIN_SET(vop, win, dsp_info, val); - val = (dsp_sty - 1) << 16; - val |= (dsp_stx - 1) & 0xffff; + val = dsp_sty << 16; + val |= dsp_stx & 0xffff; VOP_WIN_SET(vop, win, dsp_st, val); + VOP_WIN_SET(vop, win, rb_swap, rb_swap); if (is_alpha) { VOP_WIN_SET(vop, win, dst_alpha_ctl, @@ -1014,7 +1290,7 @@ static void vop_win_state_complete(struct vop_win *vop_win, if (state->event) { spin_lock_irqsave(&drm->event_lock, flags); - drm_send_vblank_event(drm, -1, state->event); + drm_crtc_send_vblank_event(crtc, state->event); spin_unlock_irqrestore(&drm->event_lock, flags); } @@ -1300,32 +1576,25 @@ static int vop_initial(struct vop *vop) return PTR_ERR(vop->dclk); } - ret = clk_prepare(vop->hclk); - if (ret < 0) { - dev_err(vop->dev, "failed to prepare hclk\n"); - return ret; - } - ret = clk_prepare(vop->dclk); if (ret < 0) { dev_err(vop->dev, "failed to prepare dclk\n"); - goto err_unprepare_hclk; + return ret; } - ret = clk_prepare(vop->aclk); + /* Enable both the hclk and aclk to setup the vop */ + ret = clk_prepare_enable(vop->hclk); if (ret < 0) { - dev_err(vop->dev, "failed to prepare aclk\n"); + dev_err(vop->dev, "failed to prepare/enable hclk\n"); goto err_unprepare_dclk; } - /* - * enable hclk, so that we can config vop register. - */ - ret = clk_enable(vop->hclk); + ret = clk_prepare_enable(vop->aclk); if (ret < 0) { - dev_err(vop->dev, "failed to prepare aclk\n"); - goto err_unprepare_aclk; + dev_err(vop->dev, "failed to prepare/enable aclk\n"); + goto err_disable_hclk; } + /* * do hclk_reset, reset all vop registers. */ @@ -1333,7 +1602,7 @@ static int vop_initial(struct vop *vop) if (IS_ERR(ahb_rst)) { dev_err(vop->dev, "failed to get ahb reset\n"); ret = PTR_ERR(ahb_rst); - goto err_disable_hclk; + goto err_disable_aclk; } reset_control_assert(ahb_rst); usleep_range(10, 20); @@ -1359,26 +1628,25 @@ static int vop_initial(struct vop *vop) if (IS_ERR(vop->dclk_rst)) { dev_err(vop->dev, "failed to get dclk reset\n"); ret = PTR_ERR(vop->dclk_rst); - goto err_unprepare_aclk; + 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; return 0; +err_disable_aclk: + clk_disable_unprepare(vop->aclk); err_disable_hclk: - clk_disable(vop->hclk); -err_unprepare_aclk: - clk_unprepare(vop->aclk); + clk_disable_unprepare(vop->hclk); err_unprepare_dclk: clk_unprepare(vop->dclk); -err_unprepare_hclk: - clk_unprepare(vop->hclk); return ret; } -- cgit 1.2.3-korg