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