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