1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /************************************************************************** |
3 | * Copyright (c) 2011, Intel Corporation. |
4 | * All Rights Reserved. |
5 | * |
6 | **************************************************************************/ |
7 | |
8 | #include <drm/drm.h> |
9 | #include <drm/drm_crtc_helper.h> |
10 | |
11 | #include "gma_device.h" |
12 | #include "intel_bios.h" |
13 | #include "psb_device.h" |
14 | #include "psb_drv.h" |
15 | #include "psb_intel_reg.h" |
16 | #include "psb_reg.h" |
17 | |
18 | static int psb_output_init(struct drm_device *dev) |
19 | { |
20 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev); |
21 | psb_intel_lvds_init(dev, mode_dev: &dev_priv->mode_dev); |
22 | psb_intel_sdvo_init(dev, SDVOB); |
23 | return 0; |
24 | } |
25 | |
26 | /* |
27 | * Poulsbo Backlight Interfaces |
28 | */ |
29 | |
30 | #define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */ |
31 | #define BLC_PWM_FREQ_CALC_CONSTANT 32 |
32 | #define MHz 1000000 |
33 | |
34 | #define PSB_BLC_PWM_PRECISION_FACTOR 10 |
35 | #define PSB_BLC_MAX_PWM_REG_FREQ 0xFFFE |
36 | #define PSB_BLC_MIN_PWM_REG_FREQ 0x2 |
37 | |
38 | #define PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE) |
39 | #define PSB_BACKLIGHT_PWM_CTL_SHIFT (16) |
40 | |
41 | static int psb_backlight_setup(struct drm_device *dev) |
42 | { |
43 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev); |
44 | unsigned long core_clock; |
45 | /* u32 bl_max_freq; */ |
46 | /* unsigned long value; */ |
47 | u16 bl_max_freq; |
48 | uint32_t value; |
49 | uint32_t blc_pwm_precision_factor; |
50 | |
51 | /* get bl_max_freq and pol from dev_priv*/ |
52 | if (!dev_priv->lvds_bl) { |
53 | dev_err(dev->dev, "Has no valid LVDS backlight info\n" ); |
54 | return -ENOENT; |
55 | } |
56 | bl_max_freq = dev_priv->lvds_bl->freq; |
57 | blc_pwm_precision_factor = PSB_BLC_PWM_PRECISION_FACTOR; |
58 | |
59 | core_clock = dev_priv->core_freq; |
60 | |
61 | value = (core_clock * MHz) / BLC_PWM_FREQ_CALC_CONSTANT; |
62 | value *= blc_pwm_precision_factor; |
63 | value /= bl_max_freq; |
64 | value /= blc_pwm_precision_factor; |
65 | |
66 | if (value > (unsigned long long)PSB_BLC_MAX_PWM_REG_FREQ || |
67 | value < (unsigned long long)PSB_BLC_MIN_PWM_REG_FREQ) |
68 | return -ERANGE; |
69 | else { |
70 | value &= PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR; |
71 | REG_WRITE(BLC_PWM_CTL, |
72 | (value << PSB_BACKLIGHT_PWM_CTL_SHIFT) | (value)); |
73 | } |
74 | |
75 | psb_intel_lvds_set_brightness(dev, PSB_MAX_BRIGHTNESS); |
76 | /* This must occur after the backlight is properly initialised */ |
77 | psb_lid_timer_init(dev_priv); |
78 | return 0; |
79 | } |
80 | |
81 | /* |
82 | * Provide the Poulsbo specific chip logic and low level methods |
83 | * for power management |
84 | */ |
85 | |
86 | static void psb_init_pm(struct drm_device *dev) |
87 | { |
88 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev); |
89 | |
90 | u32 gating = PSB_RSGX32(PSB_CR_CLKGATECTL); |
91 | gating &= ~3; /* Disable 2D clock gating */ |
92 | gating |= 1; |
93 | PSB_WSGX32(gating, PSB_CR_CLKGATECTL); |
94 | PSB_RSGX32(PSB_CR_CLKGATECTL); |
95 | } |
96 | |
97 | /** |
98 | * psb_save_display_registers - save registers lost on suspend |
99 | * @dev: our DRM device |
100 | * |
101 | * Save the state we need in order to be able to restore the interface |
102 | * upon resume from suspend |
103 | */ |
104 | static int psb_save_display_registers(struct drm_device *dev) |
105 | { |
106 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev); |
107 | struct gma_connector *gma_connector; |
108 | struct drm_crtc *crtc; |
109 | struct drm_connector_list_iter conn_iter; |
110 | struct drm_connector *connector; |
111 | struct psb_state *regs = &dev_priv->regs.psb; |
112 | |
113 | /* Display arbitration control + watermarks */ |
114 | regs->saveDSPARB = PSB_RVDC32(DSPARB); |
115 | regs->saveDSPFW1 = PSB_RVDC32(DSPFW1); |
116 | regs->saveDSPFW2 = PSB_RVDC32(DSPFW2); |
117 | regs->saveDSPFW3 = PSB_RVDC32(DSPFW3); |
118 | regs->saveDSPFW4 = PSB_RVDC32(DSPFW4); |
119 | regs->saveDSPFW5 = PSB_RVDC32(DSPFW5); |
120 | regs->saveDSPFW6 = PSB_RVDC32(DSPFW6); |
121 | regs->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); |
122 | |
123 | /* Save crtc and output state */ |
124 | drm_modeset_lock_all(dev); |
125 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
126 | if (drm_helper_crtc_in_use(crtc)) |
127 | dev_priv->ops->save_crtc(crtc); |
128 | } |
129 | |
130 | drm_connector_list_iter_begin(dev, iter: &conn_iter); |
131 | drm_for_each_connector_iter(connector, &conn_iter) { |
132 | gma_connector = to_gma_connector(connector); |
133 | if (gma_connector->save) |
134 | gma_connector->save(connector); |
135 | } |
136 | drm_connector_list_iter_end(iter: &conn_iter); |
137 | |
138 | drm_modeset_unlock_all(dev); |
139 | return 0; |
140 | } |
141 | |
142 | /** |
143 | * psb_restore_display_registers - restore lost register state |
144 | * @dev: our DRM device |
145 | * |
146 | * Restore register state that was lost during suspend and resume. |
147 | */ |
148 | static int psb_restore_display_registers(struct drm_device *dev) |
149 | { |
150 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev); |
151 | struct gma_connector *gma_connector; |
152 | struct drm_crtc *crtc; |
153 | struct drm_connector_list_iter conn_iter; |
154 | struct drm_connector *connector; |
155 | struct psb_state *regs = &dev_priv->regs.psb; |
156 | |
157 | /* Display arbitration + watermarks */ |
158 | PSB_WVDC32(regs->saveDSPARB, DSPARB); |
159 | PSB_WVDC32(regs->saveDSPFW1, DSPFW1); |
160 | PSB_WVDC32(regs->saveDSPFW2, DSPFW2); |
161 | PSB_WVDC32(regs->saveDSPFW3, DSPFW3); |
162 | PSB_WVDC32(regs->saveDSPFW4, DSPFW4); |
163 | PSB_WVDC32(regs->saveDSPFW5, DSPFW5); |
164 | PSB_WVDC32(regs->saveDSPFW6, DSPFW6); |
165 | PSB_WVDC32(regs->saveCHICKENBIT, DSPCHICKENBIT); |
166 | |
167 | /*make sure VGA plane is off. it initializes to on after reset!*/ |
168 | PSB_WVDC32(0x80000000, VGACNTRL); |
169 | |
170 | drm_modeset_lock_all(dev); |
171 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) |
172 | if (drm_helper_crtc_in_use(crtc)) |
173 | dev_priv->ops->restore_crtc(crtc); |
174 | |
175 | drm_connector_list_iter_begin(dev, iter: &conn_iter); |
176 | drm_for_each_connector_iter(connector, &conn_iter) { |
177 | gma_connector = to_gma_connector(connector); |
178 | if (gma_connector->restore) |
179 | gma_connector->restore(connector); |
180 | } |
181 | drm_connector_list_iter_end(iter: &conn_iter); |
182 | |
183 | drm_modeset_unlock_all(dev); |
184 | return 0; |
185 | } |
186 | |
187 | static int psb_power_down(struct drm_device *dev) |
188 | { |
189 | return 0; |
190 | } |
191 | |
192 | static int psb_power_up(struct drm_device *dev) |
193 | { |
194 | return 0; |
195 | } |
196 | |
197 | /* Poulsbo */ |
198 | static const struct psb_offset psb_regmap[2] = { |
199 | { |
200 | .fp0 = FPA0, |
201 | .fp1 = FPA1, |
202 | .cntr = DSPACNTR, |
203 | .conf = PIPEACONF, |
204 | .src = PIPEASRC, |
205 | .dpll = DPLL_A, |
206 | .htotal = HTOTAL_A, |
207 | .hblank = HBLANK_A, |
208 | .hsync = HSYNC_A, |
209 | .vtotal = VTOTAL_A, |
210 | .vblank = VBLANK_A, |
211 | .vsync = VSYNC_A, |
212 | .stride = DSPASTRIDE, |
213 | .size = DSPASIZE, |
214 | .pos = DSPAPOS, |
215 | .base = DSPABASE, |
216 | .surf = DSPASURF, |
217 | .addr = DSPABASE, |
218 | .status = PIPEASTAT, |
219 | .linoff = DSPALINOFF, |
220 | .tileoff = DSPATILEOFF, |
221 | .palette = PALETTE_A, |
222 | }, |
223 | { |
224 | .fp0 = FPB0, |
225 | .fp1 = FPB1, |
226 | .cntr = DSPBCNTR, |
227 | .conf = PIPEBCONF, |
228 | .src = PIPEBSRC, |
229 | .dpll = DPLL_B, |
230 | .htotal = HTOTAL_B, |
231 | .hblank = HBLANK_B, |
232 | .hsync = HSYNC_B, |
233 | .vtotal = VTOTAL_B, |
234 | .vblank = VBLANK_B, |
235 | .vsync = VSYNC_B, |
236 | .stride = DSPBSTRIDE, |
237 | .size = DSPBSIZE, |
238 | .pos = DSPBPOS, |
239 | .base = DSPBBASE, |
240 | .surf = DSPBSURF, |
241 | .addr = DSPBBASE, |
242 | .status = PIPEBSTAT, |
243 | .linoff = DSPBLINOFF, |
244 | .tileoff = DSPBTILEOFF, |
245 | .palette = PALETTE_B, |
246 | } |
247 | }; |
248 | |
249 | static int psb_chip_setup(struct drm_device *dev) |
250 | { |
251 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev); |
252 | dev_priv->regmap = psb_regmap; |
253 | gma_get_core_freq(dev); |
254 | gma_intel_setup_gmbus(dev); |
255 | psb_intel_opregion_init(dev); |
256 | psb_intel_init_bios(dev); |
257 | return 0; |
258 | } |
259 | |
260 | static void psb_chip_teardown(struct drm_device *dev) |
261 | { |
262 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev); |
263 | psb_lid_timer_takedown(dev_priv); |
264 | gma_intel_teardown_gmbus(dev); |
265 | } |
266 | |
267 | const struct psb_ops psb_chip_ops = { |
268 | .name = "Poulsbo" , |
269 | .pipes = 2, |
270 | .crtcs = 2, |
271 | .hdmi_mask = (1 << 0), |
272 | .lvds_mask = (1 << 1), |
273 | .sdvo_mask = (1 << 0), |
274 | .cursor_needs_phys = 1, |
275 | .sgx_offset = PSB_SGX_OFFSET, |
276 | .chip_setup = psb_chip_setup, |
277 | .chip_teardown = psb_chip_teardown, |
278 | |
279 | .crtc_helper = &psb_intel_helper_funcs, |
280 | .clock_funcs = &psb_clock_funcs, |
281 | |
282 | .output_init = psb_output_init, |
283 | |
284 | .backlight_init = psb_backlight_setup, |
285 | .backlight_set = psb_intel_lvds_set_brightness, |
286 | .backlight_name = "psb-bl" , |
287 | |
288 | .init_pm = psb_init_pm, |
289 | .save_regs = psb_save_display_registers, |
290 | .restore_regs = psb_restore_display_registers, |
291 | .save_crtc = gma_crtc_save, |
292 | .restore_crtc = gma_crtc_restore, |
293 | .power_down = psb_power_down, |
294 | .power_up = psb_power_up, |
295 | }; |
296 | |
297 | |