1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /************************************************************************** |
3 | * Copyright (c) 2011, Intel Corporation. |
4 | * All Rights Reserved. |
5 | * |
6 | **************************************************************************/ |
7 | |
8 | #include <linux/delay.h> |
9 | #include <linux/dmi.h> |
10 | #include <linux/module.h> |
11 | |
12 | #include <drm/drm.h> |
13 | |
14 | #include "intel_bios.h" |
15 | #include "mid_bios.h" |
16 | #include "psb_drv.h" |
17 | #include "psb_intel_reg.h" |
18 | #include "psb_reg.h" |
19 | |
20 | static int oaktrail_output_init(struct drm_device *dev) |
21 | { |
22 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev); |
23 | if (dev_priv->iLVDS_enable) |
24 | oaktrail_lvds_init(dev, mode_dev: &dev_priv->mode_dev); |
25 | else |
26 | dev_err(dev->dev, "DSI is not supported\n" ); |
27 | if (dev_priv->hdmi_priv) |
28 | oaktrail_hdmi_init(dev, mode_dev: &dev_priv->mode_dev); |
29 | |
30 | psb_intel_sdvo_init(dev, SDVOB); |
31 | |
32 | return 0; |
33 | } |
34 | |
35 | /* |
36 | * Provide the low level interfaces for the Moorestown backlight |
37 | */ |
38 | |
39 | #define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF |
40 | #define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */ |
41 | #define BLC_PWM_FREQ_CALC_CONSTANT 32 |
42 | #define MHz 1000000 |
43 | #define BLC_ADJUSTMENT_MAX 100 |
44 | |
45 | static void oaktrail_set_brightness(struct drm_device *dev, int level) |
46 | { |
47 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev); |
48 | u32 blc_pwm_ctl; |
49 | u32 max_pwm_blc; |
50 | |
51 | if (gma_power_begin(dev, force: 0)) { |
52 | /* Calculate and set the brightness value */ |
53 | max_pwm_blc = REG_READ(BLC_PWM_CTL) >> 16; |
54 | blc_pwm_ctl = level * max_pwm_blc / 100; |
55 | |
56 | /* Adjust the backlight level with the percent in |
57 | * dev_priv->blc_adj1; |
58 | */ |
59 | blc_pwm_ctl = blc_pwm_ctl * dev_priv->blc_adj1; |
60 | blc_pwm_ctl = blc_pwm_ctl / 100; |
61 | |
62 | /* Adjust the backlight level with the percent in |
63 | * dev_priv->blc_adj2; |
64 | */ |
65 | blc_pwm_ctl = blc_pwm_ctl * dev_priv->blc_adj2; |
66 | blc_pwm_ctl = blc_pwm_ctl / 100; |
67 | |
68 | /* force PWM bit on */ |
69 | REG_WRITE(BLC_PWM_CTL2, (0x80000000 | REG_READ(BLC_PWM_CTL2))); |
70 | REG_WRITE(BLC_PWM_CTL, (max_pwm_blc << 16) | blc_pwm_ctl); |
71 | gma_power_end(dev); |
72 | } |
73 | } |
74 | |
75 | static int oaktrail_backlight_init(struct drm_device *dev) |
76 | { |
77 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev); |
78 | unsigned long core_clock; |
79 | u16 bl_max_freq; |
80 | uint32_t value; |
81 | uint32_t blc_pwm_precision_factor; |
82 | |
83 | dev_priv->blc_adj1 = BLC_ADJUSTMENT_MAX; |
84 | dev_priv->blc_adj2 = BLC_ADJUSTMENT_MAX; |
85 | bl_max_freq = 256; |
86 | /* this needs to be set elsewhere */ |
87 | blc_pwm_precision_factor = BLC_PWM_PRECISION_FACTOR; |
88 | |
89 | core_clock = dev_priv->core_freq; |
90 | |
91 | value = (core_clock * MHz) / BLC_PWM_FREQ_CALC_CONSTANT; |
92 | value *= blc_pwm_precision_factor; |
93 | value /= bl_max_freq; |
94 | value /= blc_pwm_precision_factor; |
95 | |
96 | if (value > (unsigned long long)MRST_BLC_MAX_PWM_REG_FREQ) |
97 | return -ERANGE; |
98 | |
99 | if (gma_power_begin(dev, force: false)) { |
100 | REG_WRITE(BLC_PWM_CTL2, (0x80000000 | REG_READ(BLC_PWM_CTL2))); |
101 | REG_WRITE(BLC_PWM_CTL, value | (value << 16)); |
102 | gma_power_end(dev); |
103 | } |
104 | |
105 | oaktrail_set_brightness(dev, PSB_MAX_BRIGHTNESS); |
106 | return 0; |
107 | } |
108 | |
109 | /* |
110 | * Provide the Moorestown specific chip logic and low level methods |
111 | * for power management |
112 | */ |
113 | |
114 | /** |
115 | * oaktrail_save_display_registers - save registers lost on suspend |
116 | * @dev: our DRM device |
117 | * |
118 | * Save the state we need in order to be able to restore the interface |
119 | * upon resume from suspend |
120 | */ |
121 | static int oaktrail_save_display_registers(struct drm_device *dev) |
122 | { |
123 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev); |
124 | struct psb_save_area *regs = &dev_priv->regs; |
125 | struct psb_pipe *p = ®s->pipe[0]; |
126 | int i; |
127 | u32 pp_stat; |
128 | |
129 | /* Display arbitration control + watermarks */ |
130 | regs->psb.saveDSPARB = PSB_RVDC32(DSPARB); |
131 | regs->psb.saveDSPFW1 = PSB_RVDC32(DSPFW1); |
132 | regs->psb.saveDSPFW2 = PSB_RVDC32(DSPFW2); |
133 | regs->psb.saveDSPFW3 = PSB_RVDC32(DSPFW3); |
134 | regs->psb.saveDSPFW4 = PSB_RVDC32(DSPFW4); |
135 | regs->psb.saveDSPFW5 = PSB_RVDC32(DSPFW5); |
136 | regs->psb.saveDSPFW6 = PSB_RVDC32(DSPFW6); |
137 | regs->psb.saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); |
138 | |
139 | /* Pipe & plane A info */ |
140 | p->conf = PSB_RVDC32(PIPEACONF); |
141 | p->src = PSB_RVDC32(PIPEASRC); |
142 | p->fp0 = PSB_RVDC32(MRST_FPA0); |
143 | p->fp1 = PSB_RVDC32(MRST_FPA1); |
144 | p->dpll = PSB_RVDC32(MRST_DPLL_A); |
145 | p->htotal = PSB_RVDC32(HTOTAL_A); |
146 | p->hblank = PSB_RVDC32(HBLANK_A); |
147 | p->hsync = PSB_RVDC32(HSYNC_A); |
148 | p->vtotal = PSB_RVDC32(VTOTAL_A); |
149 | p->vblank = PSB_RVDC32(VBLANK_A); |
150 | p->vsync = PSB_RVDC32(VSYNC_A); |
151 | regs->psb.saveBCLRPAT_A = PSB_RVDC32(BCLRPAT_A); |
152 | p->cntr = PSB_RVDC32(DSPACNTR); |
153 | p->stride = PSB_RVDC32(DSPASTRIDE); |
154 | p->addr = PSB_RVDC32(DSPABASE); |
155 | p->surf = PSB_RVDC32(DSPASURF); |
156 | p->linoff = PSB_RVDC32(DSPALINOFF); |
157 | p->tileoff = PSB_RVDC32(DSPATILEOFF); |
158 | |
159 | /* Save cursor regs */ |
160 | regs->psb.saveDSPACURSOR_CTRL = PSB_RVDC32(CURACNTR); |
161 | regs->psb.saveDSPACURSOR_BASE = PSB_RVDC32(CURABASE); |
162 | regs->psb.saveDSPACURSOR_POS = PSB_RVDC32(CURAPOS); |
163 | |
164 | /* Save palette (gamma) */ |
165 | for (i = 0; i < 256; i++) |
166 | p->palette[i] = PSB_RVDC32(PALETTE_A + (i << 2)); |
167 | |
168 | if (dev_priv->hdmi_priv) |
169 | oaktrail_hdmi_save(dev); |
170 | |
171 | /* Save performance state */ |
172 | regs->psb.savePERF_MODE = PSB_RVDC32(MRST_PERF_MODE); |
173 | |
174 | /* LVDS state */ |
175 | regs->psb.savePP_CONTROL = PSB_RVDC32(PP_CONTROL); |
176 | regs->psb.savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS); |
177 | regs->psb.savePFIT_AUTO_RATIOS = PSB_RVDC32(PFIT_AUTO_RATIOS); |
178 | regs->saveBLC_PWM_CTL = PSB_RVDC32(BLC_PWM_CTL); |
179 | regs->saveBLC_PWM_CTL2 = PSB_RVDC32(BLC_PWM_CTL2); |
180 | regs->psb.saveLVDS = PSB_RVDC32(LVDS); |
181 | regs->psb.savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL); |
182 | regs->psb.savePP_ON_DELAYS = PSB_RVDC32(LVDSPP_ON); |
183 | regs->psb.savePP_OFF_DELAYS = PSB_RVDC32(LVDSPP_OFF); |
184 | regs->psb.savePP_DIVISOR = PSB_RVDC32(PP_CYCLE); |
185 | |
186 | /* HW overlay */ |
187 | regs->psb.saveOV_OVADD = PSB_RVDC32(OV_OVADD); |
188 | regs->psb.saveOV_OGAMC0 = PSB_RVDC32(OV_OGAMC0); |
189 | regs->psb.saveOV_OGAMC1 = PSB_RVDC32(OV_OGAMC1); |
190 | regs->psb.saveOV_OGAMC2 = PSB_RVDC32(OV_OGAMC2); |
191 | regs->psb.saveOV_OGAMC3 = PSB_RVDC32(OV_OGAMC3); |
192 | regs->psb.saveOV_OGAMC4 = PSB_RVDC32(OV_OGAMC4); |
193 | regs->psb.saveOV_OGAMC5 = PSB_RVDC32(OV_OGAMC5); |
194 | |
195 | /* DPST registers */ |
196 | regs->psb.saveHISTOGRAM_INT_CONTROL_REG = |
197 | PSB_RVDC32(HISTOGRAM_INT_CONTROL); |
198 | regs->psb.saveHISTOGRAM_LOGIC_CONTROL_REG = |
199 | PSB_RVDC32(HISTOGRAM_LOGIC_CONTROL); |
200 | regs->psb.savePWM_CONTROL_LOGIC = PSB_RVDC32(PWM_CONTROL_LOGIC); |
201 | |
202 | if (dev_priv->iLVDS_enable) { |
203 | /* Shut down the panel */ |
204 | PSB_WVDC32(0, PP_CONTROL); |
205 | |
206 | do { |
207 | pp_stat = PSB_RVDC32(PP_STATUS); |
208 | } while (pp_stat & 0x80000000); |
209 | |
210 | /* Turn off the plane */ |
211 | PSB_WVDC32(0x58000000, DSPACNTR); |
212 | /* Trigger the plane disable */ |
213 | PSB_WVDC32(0, DSPASURF); |
214 | |
215 | /* Wait ~4 ticks */ |
216 | msleep(msecs: 4); |
217 | |
218 | /* Turn off pipe */ |
219 | PSB_WVDC32(0x0, PIPEACONF); |
220 | /* Wait ~8 ticks */ |
221 | msleep(msecs: 8); |
222 | |
223 | /* Turn off PLLs */ |
224 | PSB_WVDC32(0, MRST_DPLL_A); |
225 | } |
226 | return 0; |
227 | } |
228 | |
229 | /** |
230 | * oaktrail_restore_display_registers - restore lost register state |
231 | * @dev: our DRM device |
232 | * |
233 | * Restore register state that was lost during suspend and resume. |
234 | */ |
235 | static int oaktrail_restore_display_registers(struct drm_device *dev) |
236 | { |
237 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev); |
238 | struct psb_save_area *regs = &dev_priv->regs; |
239 | struct psb_pipe *p = ®s->pipe[0]; |
240 | u32 pp_stat; |
241 | int i; |
242 | |
243 | /* Display arbitration + watermarks */ |
244 | PSB_WVDC32(regs->psb.saveDSPARB, DSPARB); |
245 | PSB_WVDC32(regs->psb.saveDSPFW1, DSPFW1); |
246 | PSB_WVDC32(regs->psb.saveDSPFW2, DSPFW2); |
247 | PSB_WVDC32(regs->psb.saveDSPFW3, DSPFW3); |
248 | PSB_WVDC32(regs->psb.saveDSPFW4, DSPFW4); |
249 | PSB_WVDC32(regs->psb.saveDSPFW5, DSPFW5); |
250 | PSB_WVDC32(regs->psb.saveDSPFW6, DSPFW6); |
251 | PSB_WVDC32(regs->psb.saveCHICKENBIT, DSPCHICKENBIT); |
252 | |
253 | /* Make sure VGA plane is off. it initializes to on after reset!*/ |
254 | PSB_WVDC32(0x80000000, VGACNTRL); |
255 | |
256 | /* set the plls */ |
257 | PSB_WVDC32(p->fp0, MRST_FPA0); |
258 | PSB_WVDC32(p->fp1, MRST_FPA1); |
259 | |
260 | /* Actually enable it */ |
261 | PSB_WVDC32(p->dpll, MRST_DPLL_A); |
262 | udelay(150); |
263 | |
264 | /* Restore mode */ |
265 | PSB_WVDC32(p->htotal, HTOTAL_A); |
266 | PSB_WVDC32(p->hblank, HBLANK_A); |
267 | PSB_WVDC32(p->hsync, HSYNC_A); |
268 | PSB_WVDC32(p->vtotal, VTOTAL_A); |
269 | PSB_WVDC32(p->vblank, VBLANK_A); |
270 | PSB_WVDC32(p->vsync, VSYNC_A); |
271 | PSB_WVDC32(p->src, PIPEASRC); |
272 | PSB_WVDC32(regs->psb.saveBCLRPAT_A, BCLRPAT_A); |
273 | |
274 | /* Restore performance mode*/ |
275 | PSB_WVDC32(regs->psb.savePERF_MODE, MRST_PERF_MODE); |
276 | |
277 | /* Enable the pipe*/ |
278 | if (dev_priv->iLVDS_enable) |
279 | PSB_WVDC32(p->conf, PIPEACONF); |
280 | |
281 | /* Set up the plane*/ |
282 | PSB_WVDC32(p->linoff, DSPALINOFF); |
283 | PSB_WVDC32(p->stride, DSPASTRIDE); |
284 | PSB_WVDC32(p->tileoff, DSPATILEOFF); |
285 | |
286 | /* Enable the plane */ |
287 | PSB_WVDC32(p->cntr, DSPACNTR); |
288 | PSB_WVDC32(p->surf, DSPASURF); |
289 | |
290 | /* Enable Cursor A */ |
291 | PSB_WVDC32(regs->psb.saveDSPACURSOR_CTRL, CURACNTR); |
292 | PSB_WVDC32(regs->psb.saveDSPACURSOR_POS, CURAPOS); |
293 | PSB_WVDC32(regs->psb.saveDSPACURSOR_BASE, CURABASE); |
294 | |
295 | /* Restore palette (gamma) */ |
296 | for (i = 0; i < 256; i++) |
297 | PSB_WVDC32(p->palette[i], PALETTE_A + (i << 2)); |
298 | |
299 | if (dev_priv->hdmi_priv) |
300 | oaktrail_hdmi_restore(dev); |
301 | |
302 | if (dev_priv->iLVDS_enable) { |
303 | PSB_WVDC32(regs->saveBLC_PWM_CTL2, BLC_PWM_CTL2); |
304 | PSB_WVDC32(regs->psb.saveLVDS, LVDS); /*port 61180h*/ |
305 | PSB_WVDC32(regs->psb.savePFIT_CONTROL, PFIT_CONTROL); |
306 | PSB_WVDC32(regs->psb.savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS); |
307 | PSB_WVDC32(regs->psb.savePFIT_AUTO_RATIOS, PFIT_AUTO_RATIOS); |
308 | PSB_WVDC32(regs->saveBLC_PWM_CTL, BLC_PWM_CTL); |
309 | PSB_WVDC32(regs->psb.savePP_ON_DELAYS, LVDSPP_ON); |
310 | PSB_WVDC32(regs->psb.savePP_OFF_DELAYS, LVDSPP_OFF); |
311 | PSB_WVDC32(regs->psb.savePP_DIVISOR, PP_CYCLE); |
312 | PSB_WVDC32(regs->psb.savePP_CONTROL, PP_CONTROL); |
313 | } |
314 | |
315 | /* Wait for cycle delay */ |
316 | do { |
317 | pp_stat = PSB_RVDC32(PP_STATUS); |
318 | } while (pp_stat & 0x08000000); |
319 | |
320 | /* Wait for panel power up */ |
321 | do { |
322 | pp_stat = PSB_RVDC32(PP_STATUS); |
323 | } while (pp_stat & 0x10000000); |
324 | |
325 | /* Restore HW overlay */ |
326 | PSB_WVDC32(regs->psb.saveOV_OVADD, OV_OVADD); |
327 | PSB_WVDC32(regs->psb.saveOV_OGAMC0, OV_OGAMC0); |
328 | PSB_WVDC32(regs->psb.saveOV_OGAMC1, OV_OGAMC1); |
329 | PSB_WVDC32(regs->psb.saveOV_OGAMC2, OV_OGAMC2); |
330 | PSB_WVDC32(regs->psb.saveOV_OGAMC3, OV_OGAMC3); |
331 | PSB_WVDC32(regs->psb.saveOV_OGAMC4, OV_OGAMC4); |
332 | PSB_WVDC32(regs->psb.saveOV_OGAMC5, OV_OGAMC5); |
333 | |
334 | /* DPST registers */ |
335 | PSB_WVDC32(regs->psb.saveHISTOGRAM_INT_CONTROL_REG, |
336 | HISTOGRAM_INT_CONTROL); |
337 | PSB_WVDC32(regs->psb.saveHISTOGRAM_LOGIC_CONTROL_REG, |
338 | HISTOGRAM_LOGIC_CONTROL); |
339 | PSB_WVDC32(regs->psb.savePWM_CONTROL_LOGIC, PWM_CONTROL_LOGIC); |
340 | |
341 | return 0; |
342 | } |
343 | |
344 | /** |
345 | * oaktrail_power_down - power down the display island |
346 | * @dev: our DRM device |
347 | * |
348 | * Power down the display interface of our device |
349 | */ |
350 | static int oaktrail_power_down(struct drm_device *dev) |
351 | { |
352 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev); |
353 | u32 pwr_mask ; |
354 | u32 pwr_sts; |
355 | |
356 | pwr_mask = PSB_PWRGT_DISPLAY_MASK; |
357 | outl(value: pwr_mask, port: dev_priv->ospm_base + PSB_PM_SSC); |
358 | |
359 | while (true) { |
360 | pwr_sts = inl(port: dev_priv->ospm_base + PSB_PM_SSS); |
361 | if ((pwr_sts & pwr_mask) == pwr_mask) |
362 | break; |
363 | else |
364 | udelay(10); |
365 | } |
366 | return 0; |
367 | } |
368 | |
369 | /* |
370 | * oaktrail_power_up |
371 | * |
372 | * Restore power to the specified island(s) (powergating) |
373 | */ |
374 | static int oaktrail_power_up(struct drm_device *dev) |
375 | { |
376 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev); |
377 | u32 pwr_mask = PSB_PWRGT_DISPLAY_MASK; |
378 | u32 pwr_sts, pwr_cnt; |
379 | |
380 | pwr_cnt = inl(port: dev_priv->ospm_base + PSB_PM_SSC); |
381 | pwr_cnt &= ~pwr_mask; |
382 | outl(value: pwr_cnt, port: (dev_priv->ospm_base + PSB_PM_SSC)); |
383 | |
384 | while (true) { |
385 | pwr_sts = inl(port: dev_priv->ospm_base + PSB_PM_SSS); |
386 | if ((pwr_sts & pwr_mask) == 0) |
387 | break; |
388 | else |
389 | udelay(10); |
390 | } |
391 | return 0; |
392 | } |
393 | |
394 | /* Oaktrail */ |
395 | static const struct psb_offset oaktrail_regmap[2] = { |
396 | { |
397 | .fp0 = MRST_FPA0, |
398 | .fp1 = MRST_FPA1, |
399 | .cntr = DSPACNTR, |
400 | .conf = PIPEACONF, |
401 | .src = PIPEASRC, |
402 | .dpll = MRST_DPLL_A, |
403 | .htotal = HTOTAL_A, |
404 | .hblank = HBLANK_A, |
405 | .hsync = HSYNC_A, |
406 | .vtotal = VTOTAL_A, |
407 | .vblank = VBLANK_A, |
408 | .vsync = VSYNC_A, |
409 | .stride = DSPASTRIDE, |
410 | .size = DSPASIZE, |
411 | .pos = DSPAPOS, |
412 | .surf = DSPASURF, |
413 | .addr = MRST_DSPABASE, |
414 | .base = MRST_DSPABASE, |
415 | .status = PIPEASTAT, |
416 | .linoff = DSPALINOFF, |
417 | .tileoff = DSPATILEOFF, |
418 | .palette = PALETTE_A, |
419 | }, |
420 | { |
421 | .fp0 = FPB0, |
422 | .fp1 = FPB1, |
423 | .cntr = DSPBCNTR, |
424 | .conf = PIPEBCONF, |
425 | .src = PIPEBSRC, |
426 | .dpll = DPLL_B, |
427 | .htotal = HTOTAL_B, |
428 | .hblank = HBLANK_B, |
429 | .hsync = HSYNC_B, |
430 | .vtotal = VTOTAL_B, |
431 | .vblank = VBLANK_B, |
432 | .vsync = VSYNC_B, |
433 | .stride = DSPBSTRIDE, |
434 | .size = DSPBSIZE, |
435 | .pos = DSPBPOS, |
436 | .surf = DSPBSURF, |
437 | .addr = DSPBBASE, |
438 | .base = DSPBBASE, |
439 | .status = PIPEBSTAT, |
440 | .linoff = DSPBLINOFF, |
441 | .tileoff = DSPBTILEOFF, |
442 | .palette = PALETTE_B, |
443 | }, |
444 | }; |
445 | |
446 | static int oaktrail_chip_setup(struct drm_device *dev) |
447 | { |
448 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev); |
449 | int ret; |
450 | |
451 | dev_priv->use_msi = true; |
452 | dev_priv->regmap = oaktrail_regmap; |
453 | |
454 | ret = mid_chip_setup(dev); |
455 | if (ret < 0) |
456 | return ret; |
457 | if (!dev_priv->has_gct) { |
458 | /* Now pull the BIOS data */ |
459 | psb_intel_opregion_init(dev); |
460 | psb_intel_init_bios(dev); |
461 | } |
462 | gma_intel_setup_gmbus(dev); |
463 | oaktrail_hdmi_setup(dev); |
464 | return 0; |
465 | } |
466 | |
467 | static void oaktrail_teardown(struct drm_device *dev) |
468 | { |
469 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev); |
470 | |
471 | gma_intel_teardown_gmbus(dev); |
472 | oaktrail_hdmi_teardown(dev); |
473 | if (!dev_priv->has_gct) |
474 | psb_intel_destroy_bios(dev); |
475 | } |
476 | |
477 | const struct psb_ops oaktrail_chip_ops = { |
478 | .name = "Oaktrail" , |
479 | .pipes = 2, |
480 | .crtcs = 2, |
481 | .hdmi_mask = (1 << 1), |
482 | .lvds_mask = (1 << 0), |
483 | .sdvo_mask = (1 << 1), |
484 | .cursor_needs_phys = 0, |
485 | .sgx_offset = MRST_SGX_OFFSET, |
486 | |
487 | .chip_setup = oaktrail_chip_setup, |
488 | .chip_teardown = oaktrail_teardown, |
489 | .crtc_helper = &oaktrail_helper_funcs, |
490 | |
491 | .output_init = oaktrail_output_init, |
492 | |
493 | .backlight_init = oaktrail_backlight_init, |
494 | .backlight_set = oaktrail_set_brightness, |
495 | .backlight_name = "oaktrail-bl" , |
496 | |
497 | .save_regs = oaktrail_save_display_registers, |
498 | .restore_regs = oaktrail_restore_display_registers, |
499 | .save_crtc = gma_crtc_save, |
500 | .restore_crtc = gma_crtc_restore, |
501 | .power_down = oaktrail_power_down, |
502 | .power_up = oaktrail_power_up, |
503 | |
504 | .i2c_bus = 1, |
505 | }; |
506 | |