1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright 2015 Freescale Semiconductor, Inc. 4 * 5 * Freescale DCU drm device driver 6 */ 7 8 #include <linux/clk.h> 9 #include <linux/regmap.h> 10 11 #include <drm/drmP.h> 12 #include <drm/drm_atomic.h> 13 #include <drm/drm_atomic_helper.h> 14 #include <drm/drm_crtc.h> 15 #include <drm/drm_probe_helper.h> 16 #include <video/videomode.h> 17 18 #include "fsl_dcu_drm_crtc.h" 19 #include "fsl_dcu_drm_drv.h" 20 #include "fsl_dcu_drm_plane.h" 21 22 static void fsl_dcu_drm_crtc_atomic_flush(struct drm_crtc *crtc, 23 struct drm_crtc_state *old_crtc_state) 24 { 25 struct drm_device *dev = crtc->dev; 26 struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; 27 struct drm_pending_vblank_event *event = crtc->state->event; 28 29 regmap_write(fsl_dev->regmap, 30 DCU_UPDATE_MODE, DCU_UPDATE_MODE_READREG); 31 32 if (event) { 33 crtc->state->event = NULL; 34 35 spin_lock_irq(&crtc->dev->event_lock); 36 if (drm_crtc_vblank_get(crtc) == 0) 37 drm_crtc_arm_vblank_event(crtc, event); 38 else 39 drm_crtc_send_vblank_event(crtc, event); 40 spin_unlock_irq(&crtc->dev->event_lock); 41 } 42 } 43 44 static void fsl_dcu_drm_crtc_atomic_disable(struct drm_crtc *crtc, 45 struct drm_crtc_state *old_crtc_state) 46 { 47 struct drm_device *dev = crtc->dev; 48 struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; 49 50 /* always disable planes on the CRTC */ 51 drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, true); 52 53 drm_crtc_vblank_off(crtc); 54 55 regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE, 56 DCU_MODE_DCU_MODE_MASK, 57 DCU_MODE_DCU_MODE(DCU_MODE_OFF)); 58 regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, 59 DCU_UPDATE_MODE_READREG); 60 clk_disable_unprepare(fsl_dev->pix_clk); 61 } 62 63 static void fsl_dcu_drm_crtc_atomic_enable(struct drm_crtc *crtc, 64 struct drm_crtc_state *old_state) 65 { 66 struct drm_device *dev = crtc->dev; 67 struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; 68 69 clk_prepare_enable(fsl_dev->pix_clk); 70 regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE, 71 DCU_MODE_DCU_MODE_MASK, 72 DCU_MODE_DCU_MODE(DCU_MODE_NORMAL)); 73 regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, 74 DCU_UPDATE_MODE_READREG); 75 76 drm_crtc_vblank_on(crtc); 77 } 78 79 static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc) 80 { 81 struct drm_device *dev = crtc->dev; 82 struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; 83 struct drm_connector *con = &fsl_dev->connector.base; 84 struct drm_display_mode *mode = &crtc->state->mode; 85 unsigned int pol = 0; 86 struct videomode vm; 87 88 clk_set_rate(fsl_dev->pix_clk, mode->clock * 1000); 89 90 drm_display_mode_to_videomode(mode, &vm); 91 92 /* INV_PXCK as default (most display sample data on rising edge) */ 93 if (!(con->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE)) 94 pol |= DCU_SYN_POL_INV_PXCK; 95 96 if (vm.flags & DISPLAY_FLAGS_HSYNC_LOW) 97 pol |= DCU_SYN_POL_INV_HS_LOW; 98 99 if (vm.flags & DISPLAY_FLAGS_VSYNC_LOW) 100 pol |= DCU_SYN_POL_INV_VS_LOW; 101 102 regmap_write(fsl_dev->regmap, DCU_HSYN_PARA, 103 DCU_HSYN_PARA_BP(vm.hback_porch) | 104 DCU_HSYN_PARA_PW(vm.hsync_len) | 105 DCU_HSYN_PARA_FP(vm.hfront_porch)); 106 regmap_write(fsl_dev->regmap, DCU_VSYN_PARA, 107 DCU_VSYN_PARA_BP(vm.vback_porch) | 108 DCU_VSYN_PARA_PW(vm.vsync_len) | 109 DCU_VSYN_PARA_FP(vm.vfront_porch)); 110 regmap_write(fsl_dev->regmap, DCU_DISP_SIZE, 111 DCU_DISP_SIZE_DELTA_Y(vm.vactive) | 112 DCU_DISP_SIZE_DELTA_X(vm.hactive)); 113 regmap_write(fsl_dev->regmap, DCU_SYN_POL, pol); 114 regmap_write(fsl_dev->regmap, DCU_BGND, DCU_BGND_R(0) | 115 DCU_BGND_G(0) | DCU_BGND_B(0)); 116 regmap_write(fsl_dev->regmap, DCU_DCU_MODE, 117 DCU_MODE_BLEND_ITER(1) | DCU_MODE_RASTER_EN); 118 regmap_write(fsl_dev->regmap, DCU_THRESHOLD, 119 DCU_THRESHOLD_LS_BF_VS(BF_VS_VAL) | 120 DCU_THRESHOLD_OUT_BUF_HIGH(BUF_MAX_VAL) | 121 DCU_THRESHOLD_OUT_BUF_LOW(BUF_MIN_VAL)); 122 return; 123 } 124 125 static const struct drm_crtc_helper_funcs fsl_dcu_drm_crtc_helper_funcs = { 126 .atomic_disable = fsl_dcu_drm_crtc_atomic_disable, 127 .atomic_flush = fsl_dcu_drm_crtc_atomic_flush, 128 .atomic_enable = fsl_dcu_drm_crtc_atomic_enable, 129 .mode_set_nofb = fsl_dcu_drm_crtc_mode_set_nofb, 130 }; 131 132 static int fsl_dcu_drm_crtc_enable_vblank(struct drm_crtc *crtc) 133 { 134 struct drm_device *dev = crtc->dev; 135 struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; 136 unsigned int value; 137 138 regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value); 139 value &= ~DCU_INT_MASK_VBLANK; 140 regmap_write(fsl_dev->regmap, DCU_INT_MASK, value); 141 142 return 0; 143 } 144 145 static void fsl_dcu_drm_crtc_disable_vblank(struct drm_crtc *crtc) 146 { 147 struct drm_device *dev = crtc->dev; 148 struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; 149 unsigned int value; 150 151 regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value); 152 value |= DCU_INT_MASK_VBLANK; 153 regmap_write(fsl_dev->regmap, DCU_INT_MASK, value); 154 } 155 156 static const struct drm_crtc_funcs fsl_dcu_drm_crtc_funcs = { 157 .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 158 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 159 .destroy = drm_crtc_cleanup, 160 .page_flip = drm_atomic_helper_page_flip, 161 .reset = drm_atomic_helper_crtc_reset, 162 .set_config = drm_atomic_helper_set_config, 163 .enable_vblank = fsl_dcu_drm_crtc_enable_vblank, 164 .disable_vblank = fsl_dcu_drm_crtc_disable_vblank, 165 }; 166 167 int fsl_dcu_drm_crtc_create(struct fsl_dcu_drm_device *fsl_dev) 168 { 169 struct drm_plane *primary; 170 struct drm_crtc *crtc = &fsl_dev->crtc; 171 int ret; 172 173 fsl_dcu_drm_init_planes(fsl_dev->drm); 174 175 primary = fsl_dcu_drm_primary_create_plane(fsl_dev->drm); 176 if (!primary) 177 return -ENOMEM; 178 179 ret = drm_crtc_init_with_planes(fsl_dev->drm, crtc, primary, NULL, 180 &fsl_dcu_drm_crtc_funcs, NULL); 181 if (ret) { 182 primary->funcs->destroy(primary); 183 return ret; 184 } 185 186 drm_crtc_helper_add(crtc, &fsl_dcu_drm_crtc_helper_funcs); 187 188 return 0; 189 } 190