| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * Copyright © 2018-2020 Intel Corporation | |
| */ | |
| #include <drm/drm_atomic.h> | |
| #include <drm/drm_atomic_helper.h> | |
| #include <drm/drm_blend.h> | |
| #include <drm/drm_crtc.h> | |
| #include <drm/drm_fb_dma_helper.h> | |
| #include <drm/drm_fourcc.h> | |
| #include <drm/drm_framebuffer.h> | |
| #include <drm/drm_gem_dma_helper.h> | |
| #include <drm/drm_managed.h> | |
| #include <drm/drm_print.h> | |
| #include "kmb_drv.h" | |
| #include "kmb_plane.h" | |
| #include "kmb_regs.h" | |
| const u32 layer_irqs[] = { | |
| LCD_INT_VL0, | |
| LCD_INT_VL1, | |
| LCD_INT_GL0, | |
| LCD_INT_GL1 | |
| }; | |
| /* Conversion (yuv->rgb) matrix from myriadx */ | |
| static const u32 csc_coef_lcd[] = { | |
| 1024, 0, 1436, | |
| 1024, -352, -731, | |
| 1024, 1814, 0, | |
| -179, 125, -226 | |
| }; | |
| /* Graphics layer (layers 2 & 3) formats, only packed formats are supported */ | |
| static const u32 kmb_formats_g[] = { | |
| DRM_FORMAT_RGB332, | |
| DRM_FORMAT_XRGB4444, DRM_FORMAT_XBGR4444, | |
| DRM_FORMAT_ARGB4444, DRM_FORMAT_ABGR4444, | |
| DRM_FORMAT_XRGB1555, DRM_FORMAT_XBGR1555, | |
| DRM_FORMAT_ARGB1555, DRM_FORMAT_ABGR1555, | |
| DRM_FORMAT_RGB565, DRM_FORMAT_BGR565, | |
| DRM_FORMAT_RGB888, DRM_FORMAT_BGR888, | |
| DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888, | |
| DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888, | |
| }; | |
| /* Video layer ( 0 & 1) formats, packed and planar formats are supported */ | |
| static const u32 kmb_formats_v[] = { | |
| /* packed formats */ | |
| DRM_FORMAT_RGB332, | |
| DRM_FORMAT_XRGB4444, DRM_FORMAT_XBGR4444, | |
| DRM_FORMAT_ARGB4444, DRM_FORMAT_ABGR4444, | |
| DRM_FORMAT_XRGB1555, DRM_FORMAT_XBGR1555, | |
| DRM_FORMAT_ARGB1555, DRM_FORMAT_ABGR1555, | |
| DRM_FORMAT_RGB565, DRM_FORMAT_BGR565, | |
| DRM_FORMAT_RGB888, DRM_FORMAT_BGR888, | |
| DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888, | |
| DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888, | |
| /*planar formats */ | |
| DRM_FORMAT_YUV420, DRM_FORMAT_YVU420, | |
| DRM_FORMAT_YUV422, DRM_FORMAT_YVU422, | |
| DRM_FORMAT_YUV444, DRM_FORMAT_YVU444, | |
| DRM_FORMAT_NV12, DRM_FORMAT_NV21, | |
| }; | |
| static unsigned int check_pixel_format(struct drm_plane *plane, u32 format) | |
| { | |
| struct kmb_drm_private *kmb; | |
| struct kmb_plane *kmb_plane = to_kmb_plane(plane); | |
| int i; | |
| int plane_id = kmb_plane->id; | |
| struct disp_cfg init_disp_cfg; | |
| kmb = to_kmb(plane->dev); | |
| init_disp_cfg = kmb->init_disp_cfg[plane_id]; | |
| /* Due to HW limitations, changing pixel format after initial | |
| * plane configuration is not supported. | |
| */ | |
| if (init_disp_cfg.format && init_disp_cfg.format != format) { | |
| drm_dbg(&kmb->drm, "Cannot change format after initial plane configuration"); | |
| return -EINVAL; | |
| } | |
| for (i = 0; i < plane->format_count; i++) { | |
| if (plane->format_types[i] == format) | |
| return 0; | |
| } | |
| return -EINVAL; | |
| } | |
| static int kmb_plane_atomic_check(struct drm_plane *plane, | |
| struct drm_atomic_commit *state) | |
| { | |
| struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, | |
| plane); | |
| struct kmb_drm_private *kmb; | |
| struct kmb_plane *kmb_plane = to_kmb_plane(plane); | |
| int plane_id = kmb_plane->id; | |
| struct disp_cfg init_disp_cfg; | |
| struct drm_framebuffer *fb; | |
| int ret; | |
| struct drm_crtc_state *crtc_state; | |
| bool can_position; | |
| kmb = to_kmb(plane->dev); | |
| init_disp_cfg = kmb->init_disp_cfg[plane_id]; | |
| fb = new_plane_state->fb; | |
| if (!fb || !new_plane_state->crtc) | |
| return 0; | |
| ret = check_pixel_format(plane, fb->format->format); | |
| if (ret) | |
| return ret; | |
| if (new_plane_state->crtc_w > KMB_FB_MAX_WIDTH || | |
| new_plane_state->crtc_h > KMB_FB_MAX_HEIGHT || | |
| new_plane_state->crtc_w < KMB_FB_MIN_WIDTH || | |
| new_plane_state->crtc_h < KMB_FB_MIN_HEIGHT) | |
| return -EINVAL; | |
| /* Due to HW limitations, changing plane height or width after | |
| * initial plane configuration is not supported. | |
| */ | |
| if ((init_disp_cfg.width && init_disp_cfg.height) && | |
| (init_disp_cfg.width != fb->width || | |
| init_disp_cfg.height != fb->height)) { | |
| drm_dbg(&kmb->drm, "Cannot change plane height or width after initial configuration"); | |
| return -EINVAL; | |
| } | |
| can_position = (plane->type == DRM_PLANE_TYPE_OVERLAY); | |
| crtc_state = | |
| drm_atomic_get_new_crtc_state(state, new_plane_state->crtc); | |
| return drm_atomic_helper_check_plane_state(new_plane_state, | |
| crtc_state, | |
| DRM_PLANE_NO_SCALING, | |
| DRM_PLANE_NO_SCALING, | |
| can_position, true); | |
| } | |
| static void kmb_plane_atomic_disable(struct drm_plane *plane, | |
| struct drm_atomic_commit *state) | |
| { | |
| struct kmb_plane *kmb_plane = to_kmb_plane(plane); | |
| int plane_id = kmb_plane->id; | |
| struct kmb_drm_private *kmb; | |
| kmb = to_kmb(plane->dev); | |
| if (WARN_ON(plane_id >= KMB_MAX_PLANES)) | |
| return; | |
| switch (plane_id) { | |
| case LAYER_0: | |
| kmb->plane_status[plane_id].ctrl = LCD_CTRL_VL1_ENABLE; | |
| break; | |
| case LAYER_1: | |
| kmb->plane_status[plane_id].ctrl = LCD_CTRL_VL2_ENABLE; | |
| break; | |
| } | |
| kmb->plane_status[plane_id].disable = true; | |
| } | |
| static unsigned int get_pixel_format(u32 format) | |
| { | |
| unsigned int val = 0; | |
| switch (format) { | |
| /* planar formats */ | |
| case DRM_FORMAT_YUV444: | |
| val = LCD_LAYER_FORMAT_YCBCR444PLAN | LCD_LAYER_PLANAR_STORAGE; | |
| break; | |
| case DRM_FORMAT_YVU444: | |
| val = LCD_LAYER_FORMAT_YCBCR444PLAN | LCD_LAYER_PLANAR_STORAGE | |
| | LCD_LAYER_CRCB_ORDER; | |
| break; | |
| case DRM_FORMAT_YUV422: | |
| val = LCD_LAYER_FORMAT_YCBCR422PLAN | LCD_LAYER_PLANAR_STORAGE; | |
| break; | |
| case DRM_FORMAT_YVU422: | |
| val = LCD_LAYER_FORMAT_YCBCR422PLAN | LCD_LAYER_PLANAR_STORAGE | |
| | LCD_LAYER_CRCB_ORDER; | |
| break; | |
| case DRM_FORMAT_YUV420: | |
| val = LCD_LAYER_FORMAT_YCBCR420PLAN | LCD_LAYER_PLANAR_STORAGE; | |
| break; | |
| case DRM_FORMAT_YVU420: | |
| val = LCD_LAYER_FORMAT_YCBCR420PLAN | LCD_LAYER_PLANAR_STORAGE | |
| | LCD_LAYER_CRCB_ORDER; | |
| break; | |
| case DRM_FORMAT_NV12: | |
| val = LCD_LAYER_FORMAT_NV12 | LCD_LAYER_PLANAR_STORAGE; | |
| break; | |
| case DRM_FORMAT_NV21: | |
| val = LCD_LAYER_FORMAT_NV12 | LCD_LAYER_PLANAR_STORAGE | |
| | LCD_LAYER_CRCB_ORDER; | |
| break; | |
| /* packed formats */ | |
| /* looks hw requires B & G to be swapped when RGB */ | |
| case DRM_FORMAT_RGB332: | |
| val = LCD_LAYER_FORMAT_RGB332 | LCD_LAYER_BGR_ORDER; | |
| break; | |
| case DRM_FORMAT_XBGR4444: | |
| val = LCD_LAYER_FORMAT_RGBX4444; | |
| break; | |
| case DRM_FORMAT_ARGB4444: | |
| val = LCD_LAYER_FORMAT_RGBA4444 | LCD_LAYER_BGR_ORDER; | |
| break; | |
| case DRM_FORMAT_ABGR4444: | |
| val = LCD_LAYER_FORMAT_RGBA4444; | |
| break; | |
| case DRM_FORMAT_XRGB1555: | |
| val = LCD_LAYER_FORMAT_XRGB1555 | LCD_LAYER_BGR_ORDER; | |
| break; | |
| case DRM_FORMAT_XBGR1555: | |
| val = LCD_LAYER_FORMAT_XRGB1555; | |
| break; | |
| case DRM_FORMAT_ARGB1555: | |
| val = LCD_LAYER_FORMAT_RGBA1555 | LCD_LAYER_BGR_ORDER; | |
| break; | |
| case DRM_FORMAT_ABGR1555: | |
| val = LCD_LAYER_FORMAT_RGBA1555; | |
| break; | |
| case DRM_FORMAT_RGB565: | |
| val = LCD_LAYER_FORMAT_RGB565 | LCD_LAYER_BGR_ORDER; | |
| break; | |
| case DRM_FORMAT_BGR565: | |
| val = LCD_LAYER_FORMAT_RGB565; | |
| break; | |
| case DRM_FORMAT_RGB888: | |
| val = LCD_LAYER_FORMAT_RGB888 | LCD_LAYER_BGR_ORDER; | |
| break; | |
| case DRM_FORMAT_BGR888: | |
| val = LCD_LAYER_FORMAT_RGB888; | |
| break; | |
| case DRM_FORMAT_XRGB8888: | |
| val = LCD_LAYER_FORMAT_RGBX8888 | LCD_LAYER_BGR_ORDER; | |
| break; | |
| case DRM_FORMAT_XBGR8888: | |
| val = LCD_LAYER_FORMAT_RGBX8888; | |
| break; | |
| case DRM_FORMAT_ARGB8888: | |
| val = LCD_LAYER_FORMAT_RGBA8888 | LCD_LAYER_BGR_ORDER; | |
| break; | |
| case DRM_FORMAT_ABGR8888: | |
| val = LCD_LAYER_FORMAT_RGBA8888; | |
| break; | |
| } | |
| DRM_INFO_ONCE("%s : %d format=0x%x val=0x%x\n", | |
| __func__, __LINE__, format, val); | |
| return val; | |
| } | |
| static unsigned int get_bits_per_pixel(const struct drm_format_info *format) | |
| { | |
| u32 bpp = 0; | |
| unsigned int val = 0; | |
| if (format->num_planes > 1) { | |
| val = LCD_LAYER_8BPP; | |
| return val; | |
| } | |
| bpp += 8 * format->cpp[0]; | |
| switch (bpp) { | |
| case 8: | |
| val = LCD_LAYER_8BPP; | |
| break; | |
| case 16: | |
| val = LCD_LAYER_16BPP; | |
| break; | |
| case 24: | |
| val = LCD_LAYER_24BPP; | |
| break; | |
| case 32: | |
| val = LCD_LAYER_32BPP; | |
| break; | |
| } | |
| DRM_DEBUG("bpp=%d val=0x%x\n", bpp, val); | |
| return val; | |
| } | |
| static void config_csc(struct kmb_drm_private *kmb, int plane_id) | |
| { | |
| /* YUV to RGB conversion using the fixed matrix csc_coef_lcd */ | |
| kmb_write_lcd(kmb, LCD_LAYERn_CSC_COEFF11(plane_id), csc_coef_lcd[0]); | |
| kmb_write_lcd(kmb, LCD_LAYERn_CSC_COEFF12(plane_id), csc_coef_lcd[1]); | |
| kmb_write_lcd(kmb, LCD_LAYERn_CSC_COEFF13(plane_id), csc_coef_lcd[2]); | |
| kmb_write_lcd(kmb, LCD_LAYERn_CSC_COEFF21(plane_id), csc_coef_lcd[3]); | |
| kmb_write_lcd(kmb, LCD_LAYERn_CSC_COEFF22(plane_id), csc_coef_lcd[4]); | |
| kmb_write_lcd(kmb, LCD_LAYERn_CSC_COEFF23(plane_id), csc_coef_lcd[5]); | |
| kmb_write_lcd(kmb, LCD_LAYERn_CSC_COEFF31(plane_id), csc_coef_lcd[6]); | |
| kmb_write_lcd(kmb, LCD_LAYERn_CSC_COEFF32(plane_id), csc_coef_lcd[7]); | |
| kmb_write_lcd(kmb, LCD_LAYERn_CSC_COEFF33(plane_id), csc_coef_lcd[8]); | |
| kmb_write_lcd(kmb, LCD_LAYERn_CSC_OFF1(plane_id), csc_coef_lcd[9]); | |
| kmb_write_lcd(kmb, LCD_LAYERn_CSC_OFF2(plane_id), csc_coef_lcd[10]); | |
| kmb_write_lcd(kmb, LCD_LAYERn_CSC_OFF3(plane_id), csc_coef_lcd[11]); | |
| } | |
| static void kmb_plane_set_alpha(struct kmb_drm_private *kmb, | |
| const struct drm_plane_state *state, | |
| unsigned char plane_id, | |
| unsigned int *val) | |
| { | |
| u16 plane_alpha = state->alpha; | |
| u16 pixel_blend_mode = state->pixel_blend_mode; | |
| int has_alpha = state->fb->format->has_alpha; | |
| if (plane_alpha != DRM_BLEND_ALPHA_OPAQUE) | |
| *val |= LCD_LAYER_ALPHA_STATIC; | |
| if (has_alpha) { | |
| switch (pixel_blend_mode) { | |
| case DRM_MODE_BLEND_PIXEL_NONE: | |
| break; | |
| case DRM_MODE_BLEND_PREMULTI: | |
| *val |= LCD_LAYER_ALPHA_EMBED | LCD_LAYER_ALPHA_PREMULT; | |
| break; | |
| case DRM_MODE_BLEND_COVERAGE: | |
| *val |= LCD_LAYER_ALPHA_EMBED; | |
| break; | |
| default: | |
| DRM_DEBUG("Missing pixel blend mode case (%s == %ld)\n", | |
| __stringify(pixel_blend_mode), | |
| (long)pixel_blend_mode); | |
| break; | |
| } | |
| } | |
| if (plane_alpha == DRM_BLEND_ALPHA_OPAQUE && !has_alpha) { | |
| *val &= LCD_LAYER_ALPHA_DISABLED; | |
| return; | |
| } | |
| kmb_write_lcd(kmb, LCD_LAYERn_ALPHA(plane_id), plane_alpha); | |
| } | |
| static void kmb_plane_atomic_update(struct drm_plane *plane, | |
| struct drm_atomic_commit *state) | |
| { | |
| struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, | |
| plane); | |
| struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, | |
| plane); | |
| struct drm_framebuffer *fb; | |
| struct kmb_drm_private *kmb; | |
| unsigned int width; | |
| unsigned int height; | |
| unsigned int dma_len; | |
| struct kmb_plane *kmb_plane; | |
| unsigned int dma_cfg; | |
| unsigned int ctrl = 0, val = 0, out_format = 0; | |
| unsigned int src_w, src_h, crtc_x, crtc_y; | |
| unsigned char plane_id; | |
| int num_planes; | |
| static dma_addr_t addr[MAX_SUB_PLANES]; | |
| struct disp_cfg *init_disp_cfg; | |
| if (!plane || !new_plane_state || !old_plane_state) | |
| return; | |
| fb = new_plane_state->fb; | |
| if (!fb) | |
| return; | |
| num_planes = fb->format->num_planes; | |
| kmb_plane = to_kmb_plane(plane); | |
| kmb = to_kmb(plane->dev); | |
| plane_id = kmb_plane->id; | |
| spin_lock_irq(&kmb->irq_lock); | |
| if (kmb->kmb_under_flow || kmb->kmb_flush_done) { | |
| spin_unlock_irq(&kmb->irq_lock); | |
| drm_dbg(&kmb->drm, "plane_update:underflow!!!! returning"); | |
| return; | |
| } | |
| spin_unlock_irq(&kmb->irq_lock); | |
| init_disp_cfg = &kmb->init_disp_cfg[plane_id]; | |
| src_w = new_plane_state->src_w >> 16; | |
| src_h = new_plane_state->src_h >> 16; | |
| crtc_x = new_plane_state->crtc_x; | |
| crtc_y = new_plane_state->crtc_y; | |
| drm_dbg(&kmb->drm, | |
| "src_w=%d src_h=%d, fb->format->format=0x%x fb->flags=0x%x\n", | |
| src_w, src_h, fb->format->format, fb->flags); | |
| width = fb->width; | |
| height = fb->height; | |
| dma_len = (width * height * fb->format->cpp[0]); | |
| drm_dbg(&kmb->drm, "dma_len=%d ", dma_len); | |
| kmb_write_lcd(kmb, LCD_LAYERn_DMA_LEN(plane_id), dma_len); | |
| kmb_write_lcd(kmb, LCD_LAYERn_DMA_LEN_SHADOW(plane_id), dma_len); | |
| kmb_write_lcd(kmb, LCD_LAYERn_DMA_LINE_VSTRIDE(plane_id), | |
| fb->pitches[0]); | |
| kmb_write_lcd(kmb, LCD_LAYERn_DMA_LINE_WIDTH(plane_id), | |
| (width * fb->format->cpp[0])); | |
| addr[Y_PLANE] = drm_fb_dma_get_gem_addr(fb, new_plane_state, 0); | |
| kmb_write_lcd(kmb, LCD_LAYERn_DMA_START_ADDR(plane_id), | |
| addr[Y_PLANE] + fb->offsets[0]); | |
| val = get_pixel_format(fb->format->format); | |
| val |= get_bits_per_pixel(fb->format); | |
| /* Program Cb/Cr for planar formats */ | |
| if (num_planes > 1) { | |
| kmb_write_lcd(kmb, LCD_LAYERn_DMA_CB_LINE_VSTRIDE(plane_id), | |
| width * fb->format->cpp[0]); | |
| kmb_write_lcd(kmb, LCD_LAYERn_DMA_CB_LINE_WIDTH(plane_id), | |
| (width * fb->format->cpp[0])); | |
| addr[U_PLANE] = drm_fb_dma_get_gem_addr(fb, new_plane_state, | |
| U_PLANE); | |
| /* check if Cb/Cr is swapped*/ | |
| if (num_planes == 3 && (val & LCD_LAYER_CRCB_ORDER)) | |
| kmb_write_lcd(kmb, | |
| LCD_LAYERn_DMA_START_CR_ADR(plane_id), | |
| addr[U_PLANE]); | |
| else | |
| kmb_write_lcd(kmb, | |
| LCD_LAYERn_DMA_START_CB_ADR(plane_id), | |
| addr[U_PLANE]); | |
| if (num_planes == 3) { | |
| kmb_write_lcd(kmb, | |
| LCD_LAYERn_DMA_CR_LINE_VSTRIDE(plane_id), | |
| ((width) * fb->format->cpp[0])); | |
| kmb_write_lcd(kmb, | |
| LCD_LAYERn_DMA_CR_LINE_WIDTH(plane_id), | |
| ((width) * fb->format->cpp[0])); | |
| addr[V_PLANE] = drm_fb_dma_get_gem_addr(fb, | |
| new_plane_state, | |
| V_PLANE); | |
| /* check if Cb/Cr is swapped*/ | |
| if (val & LCD_LAYER_CRCB_ORDER) | |
| kmb_write_lcd(kmb, | |
| LCD_LAYERn_DMA_START_CB_ADR(plane_id), | |
| addr[V_PLANE]); | |
| else | |
| kmb_write_lcd(kmb, | |
| LCD_LAYERn_DMA_START_CR_ADR(plane_id), | |
| addr[V_PLANE]); | |
| } | |
| } | |
| kmb_write_lcd(kmb, LCD_LAYERn_WIDTH(plane_id), src_w - 1); | |
| kmb_write_lcd(kmb, LCD_LAYERn_HEIGHT(plane_id), src_h - 1); | |
| kmb_write_lcd(kmb, LCD_LAYERn_COL_START(plane_id), crtc_x); | |
| kmb_write_lcd(kmb, LCD_LAYERn_ROW_START(plane_id), crtc_y); | |
| val |= LCD_LAYER_FIFO_100; | |
| if (val & LCD_LAYER_PLANAR_STORAGE) { | |
| val |= LCD_LAYER_CSC_EN; | |
| /* Enable CSC if input is planar and output is RGB */ | |
| config_csc(kmb, plane_id); | |
| } | |
| kmb_plane_set_alpha(kmb, plane->state, plane_id, &val); | |
| kmb_write_lcd(kmb, LCD_LAYERn_CFG(plane_id), val); | |
| /* Configure LCD_CONTROL */ | |
| ctrl = kmb_read_lcd(kmb, LCD_CONTROL); | |
| /* Set layer blending config */ | |
| ctrl &= ~LCD_CTRL_ALPHA_ALL; | |
| ctrl |= LCD_CTRL_ALPHA_BOTTOM_VL1 | | |
| LCD_CTRL_ALPHA_BLEND_VL2; | |
| ctrl &= ~LCD_CTRL_ALPHA_BLEND_BKGND_DISABLE; | |
| switch (plane_id) { | |
| case LAYER_0: | |
| ctrl |= LCD_CTRL_VL1_ENABLE; | |
| break; | |
| case LAYER_1: | |
| ctrl |= LCD_CTRL_VL2_ENABLE; | |
| break; | |
| case LAYER_2: | |
| ctrl |= LCD_CTRL_GL1_ENABLE; | |
| break; | |
| case LAYER_3: | |
| ctrl |= LCD_CTRL_GL2_ENABLE; | |
| break; | |
| } | |
| ctrl |= LCD_CTRL_PROGRESSIVE | LCD_CTRL_TIM_GEN_ENABLE | |
| | LCD_CTRL_CONTINUOUS | LCD_CTRL_OUTPUT_ENABLED; | |
| /* LCD is connected to MIPI on kmb | |
| * Therefore this bit is required for DSI Tx | |
| */ | |
| ctrl |= LCD_CTRL_VHSYNC_IDLE_LVL; | |
| kmb_write_lcd(kmb, LCD_CONTROL, ctrl); | |
| /* Enable pipeline AXI read transactions for the DMA | |
| * after setting graphics layers. This must be done | |
| * in a separate write cycle. | |
| */ | |
| kmb_set_bitmask_lcd(kmb, LCD_CONTROL, LCD_CTRL_PIPELINE_DMA); | |
| /* FIXME no doc on how to set output format, these values are taken | |
| * from the Myriadx tests | |
| */ | |
| out_format |= LCD_OUTF_FORMAT_RGB888; | |
| /* Leave RGB order,conversion mode and clip mode to default */ | |
| /* do not interleave RGB channels for mipi Tx compatibility */ | |
| out_format |= LCD_OUTF_MIPI_RGB_MODE; | |
| kmb_write_lcd(kmb, LCD_OUT_FORMAT_CFG, out_format); | |
| dma_cfg = LCD_DMA_LAYER_ENABLE | LCD_DMA_LAYER_VSTRIDE_EN | | |
| LCD_DMA_LAYER_CONT_UPDATE | LCD_DMA_LAYER_AXI_BURST_16; | |
| /* Enable DMA */ | |
| kmb_write_lcd(kmb, LCD_LAYERn_DMA_CFG(plane_id), dma_cfg); | |
| /* Save initial display config */ | |
| if (!init_disp_cfg->width || | |
| !init_disp_cfg->height || | |
| !init_disp_cfg->format) { | |
| init_disp_cfg->width = width; | |
| init_disp_cfg->height = height; | |
| init_disp_cfg->format = fb->format->format; | |
| } | |
| drm_dbg(&kmb->drm, "dma_cfg=0x%x LCD_DMA_CFG=0x%x\n", dma_cfg, | |
| kmb_read_lcd(kmb, LCD_LAYERn_DMA_CFG(plane_id))); | |
| kmb_set_bitmask_lcd(kmb, LCD_INT_CLEAR, LCD_INT_EOF | | |
| LCD_INT_DMA_ERR); | |
| kmb_set_bitmask_lcd(kmb, LCD_INT_ENABLE, LCD_INT_EOF | | |
| LCD_INT_DMA_ERR); | |
| } | |
| static const struct drm_plane_helper_funcs kmb_plane_helper_funcs = { | |
| .atomic_check = kmb_plane_atomic_check, | |
| .atomic_update = kmb_plane_atomic_update, | |
| .atomic_disable = kmb_plane_atomic_disable | |
| }; | |
| void kmb_plane_destroy(struct drm_plane *plane) | |
| { | |
| struct kmb_plane *kmb_plane = to_kmb_plane(plane); | |
| drm_plane_cleanup(plane); | |
| kfree(kmb_plane); | |
| } | |
| static const struct drm_plane_funcs kmb_plane_funcs = { | |
| .update_plane = drm_atomic_helper_update_plane, | |
| .disable_plane = drm_atomic_helper_disable_plane, | |
| .destroy = kmb_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, | |
| }; | |
| struct kmb_plane *kmb_plane_init(struct drm_device *drm) | |
| { | |
| struct kmb_drm_private *kmb = to_kmb(drm); | |
| struct kmb_plane *plane = NULL; | |
| struct kmb_plane *primary = NULL; | |
| int i = 0; | |
| int ret = 0; | |
| enum drm_plane_type plane_type; | |
| const u32 *plane_formats; | |
| int num_plane_formats; | |
| unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) | | |
| BIT(DRM_MODE_BLEND_PREMULTI) | | |
| BIT(DRM_MODE_BLEND_COVERAGE); | |
| for (i = 0; i < KMB_MAX_PLANES; i++) { | |
| plane = drmm_kzalloc(drm, sizeof(*plane), GFP_KERNEL); | |
| if (!plane) { | |
| drm_err(drm, "Failed to allocate plane\n"); | |
| return ERR_PTR(-ENOMEM); | |
| } | |
| plane_type = (i == 0) ? DRM_PLANE_TYPE_PRIMARY : | |
| DRM_PLANE_TYPE_OVERLAY; | |
| if (i < 2) { | |
| plane_formats = kmb_formats_v; | |
| num_plane_formats = ARRAY_SIZE(kmb_formats_v); | |
| } else { | |
| plane_formats = kmb_formats_g; | |
| num_plane_formats = ARRAY_SIZE(kmb_formats_g); | |
| } | |
| ret = drm_universal_plane_init(drm, &plane->base_plane, | |
| POSSIBLE_CRTCS, &kmb_plane_funcs, | |
| plane_formats, num_plane_formats, | |
| NULL, plane_type, "plane %d", i); | |
| if (ret < 0) { | |
| drm_err(drm, "drm_universal_plane_init failed (ret=%d)", | |
| ret); | |
| goto cleanup; | |
| } | |
| drm_dbg(drm, "%s : %d i=%d type=%d", | |
| __func__, __LINE__, | |
| i, plane_type); | |
| drm_plane_create_alpha_property(&plane->base_plane); | |
| drm_plane_create_blend_mode_property(&plane->base_plane, | |
| blend_caps); | |
| drm_plane_create_zpos_immutable_property(&plane->base_plane, i); | |
| drm_plane_helper_add(&plane->base_plane, | |
| &kmb_plane_helper_funcs); | |
| if (plane_type == DRM_PLANE_TYPE_PRIMARY) { | |
| primary = plane; | |
| kmb->plane = plane; | |
| } | |
| drm_dbg(drm, "%s : %d primary=%p\n", __func__, __LINE__, | |
| &primary->base_plane); | |
| plane->id = i; | |
| } | |
| /* Disable pipeline AXI read transactions for the DMA | |
| * prior to setting graphics layers | |
| */ | |
| kmb_clr_bitmask_lcd(kmb, LCD_CONTROL, LCD_CTRL_PIPELINE_DMA); | |
| return primary; | |
| cleanup: | |
| drmm_kfree(drm, plane); | |
| return ERR_PTR(ret); | |
| } |