Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 1 | /************************************************************************** |
| 2 | * Copyright (c) 2011, Intel Corporation. |
| 3 | * All Rights Reserved. |
| 4 | * |
| 5 | * This program is free software; you can redistribute it and/or modify it |
| 6 | * under the terms and conditions of the GNU General Public License, |
| 7 | * version 2, as published by the Free Software Foundation. |
| 8 | * |
| 9 | * This program is distributed in the hope it will be useful, but WITHOUT |
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 12 | * more details. |
| 13 | * |
| 14 | * You should have received a copy of the GNU General Public License along with |
| 15 | * this program; if not, write to the Free Software Foundation, Inc., |
| 16 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. |
| 17 | * |
| 18 | **************************************************************************/ |
| 19 | |
| 20 | #include <linux/backlight.h> |
| 21 | #include <drm/drmP.h> |
| 22 | #include <drm/drm.h> |
David Howells | 760285e | 2012-10-02 18:01:07 +0100 | [diff] [blame] | 23 | #include <drm/gma_drm.h> |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 24 | #include "psb_drv.h" |
| 25 | #include "psb_reg.h" |
| 26 | #include "psb_intel_reg.h" |
| 27 | #include "intel_bios.h" |
Patrik Jakobsson | 7f67c06 | 2013-07-02 17:07:59 +0200 | [diff] [blame] | 28 | #include "psb_device.h" |
Patrik Jakobsson | f35257a | 2014-03-11 22:53:43 +0100 | [diff] [blame] | 29 | #include "gma_device.h" |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 30 | |
| 31 | static int psb_output_init(struct drm_device *dev) |
| 32 | { |
| 33 | struct drm_psb_private *dev_priv = dev->dev_private; |
| 34 | psb_intel_lvds_init(dev, &dev_priv->mode_dev); |
| 35 | psb_intel_sdvo_init(dev, SDVOB); |
| 36 | return 0; |
| 37 | } |
| 38 | |
| 39 | #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE |
| 40 | |
| 41 | /* |
| 42 | * Poulsbo Backlight Interfaces |
| 43 | */ |
| 44 | |
| 45 | #define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */ |
| 46 | #define BLC_PWM_FREQ_CALC_CONSTANT 32 |
| 47 | #define MHz 1000000 |
| 48 | |
| 49 | #define PSB_BLC_PWM_PRECISION_FACTOR 10 |
| 50 | #define PSB_BLC_MAX_PWM_REG_FREQ 0xFFFE |
| 51 | #define PSB_BLC_MIN_PWM_REG_FREQ 0x2 |
| 52 | |
| 53 | #define PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE) |
| 54 | #define PSB_BACKLIGHT_PWM_CTL_SHIFT (16) |
| 55 | |
| 56 | static int psb_brightness; |
| 57 | static struct backlight_device *psb_backlight_device; |
| 58 | |
| 59 | static int psb_get_brightness(struct backlight_device *bd) |
| 60 | { |
| 61 | /* return locally cached var instead of HW read (due to DPST etc.) */ |
| 62 | /* FIXME: ideally return actual value in case firmware fiddled with |
| 63 | it */ |
| 64 | return psb_brightness; |
| 65 | } |
| 66 | |
| 67 | |
| 68 | static int psb_backlight_setup(struct drm_device *dev) |
| 69 | { |
| 70 | struct drm_psb_private *dev_priv = dev->dev_private; |
| 71 | unsigned long core_clock; |
| 72 | /* u32 bl_max_freq; */ |
| 73 | /* unsigned long value; */ |
| 74 | u16 bl_max_freq; |
| 75 | uint32_t value; |
| 76 | uint32_t blc_pwm_precision_factor; |
| 77 | |
| 78 | /* get bl_max_freq and pol from dev_priv*/ |
| 79 | if (!dev_priv->lvds_bl) { |
| 80 | dev_err(dev->dev, "Has no valid LVDS backlight info\n"); |
| 81 | return -ENOENT; |
| 82 | } |
| 83 | bl_max_freq = dev_priv->lvds_bl->freq; |
| 84 | blc_pwm_precision_factor = PSB_BLC_PWM_PRECISION_FACTOR; |
| 85 | |
| 86 | core_clock = dev_priv->core_freq; |
| 87 | |
| 88 | value = (core_clock * MHz) / BLC_PWM_FREQ_CALC_CONSTANT; |
| 89 | value *= blc_pwm_precision_factor; |
| 90 | value /= bl_max_freq; |
| 91 | value /= blc_pwm_precision_factor; |
| 92 | |
| 93 | if (value > (unsigned long long)PSB_BLC_MAX_PWM_REG_FREQ || |
| 94 | value < (unsigned long long)PSB_BLC_MIN_PWM_REG_FREQ) |
| 95 | return -ERANGE; |
| 96 | else { |
| 97 | value &= PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR; |
| 98 | REG_WRITE(BLC_PWM_CTL, |
| 99 | (value << PSB_BACKLIGHT_PWM_CTL_SHIFT) | (value)); |
| 100 | } |
| 101 | return 0; |
| 102 | } |
| 103 | |
| 104 | static int psb_set_brightness(struct backlight_device *bd) |
| 105 | { |
| 106 | struct drm_device *dev = bl_get_data(psb_backlight_device); |
| 107 | int level = bd->props.brightness; |
| 108 | |
| 109 | /* Percentage 1-100% being valid */ |
| 110 | if (level < 1) |
| 111 | level = 1; |
| 112 | |
| 113 | psb_intel_lvds_set_brightness(dev, level); |
| 114 | psb_brightness = level; |
| 115 | return 0; |
| 116 | } |
| 117 | |
| 118 | static const struct backlight_ops psb_ops = { |
| 119 | .get_brightness = psb_get_brightness, |
| 120 | .update_status = psb_set_brightness, |
| 121 | }; |
| 122 | |
| 123 | static int psb_backlight_init(struct drm_device *dev) |
| 124 | { |
| 125 | struct drm_psb_private *dev_priv = dev->dev_private; |
| 126 | int ret; |
| 127 | struct backlight_properties props; |
| 128 | |
| 129 | memset(&props, 0, sizeof(struct backlight_properties)); |
| 130 | props.max_brightness = 100; |
| 131 | props.type = BACKLIGHT_PLATFORM; |
| 132 | |
| 133 | psb_backlight_device = backlight_device_register("psb-bl", |
| 134 | NULL, (void *)dev, &psb_ops, &props); |
| 135 | if (IS_ERR(psb_backlight_device)) |
| 136 | return PTR_ERR(psb_backlight_device); |
| 137 | |
| 138 | ret = psb_backlight_setup(dev); |
| 139 | if (ret < 0) { |
| 140 | backlight_device_unregister(psb_backlight_device); |
| 141 | psb_backlight_device = NULL; |
| 142 | return ret; |
| 143 | } |
| 144 | psb_backlight_device->props.brightness = 100; |
| 145 | psb_backlight_device->props.max_brightness = 100; |
| 146 | backlight_update_status(psb_backlight_device); |
| 147 | dev_priv->backlight_device = psb_backlight_device; |
Alan Cox | f507598 | 2012-07-16 17:51:50 +0100 | [diff] [blame] | 148 | |
| 149 | /* This must occur after the backlight is properly initialised */ |
| 150 | psb_lid_timer_init(dev_priv); |
| 151 | |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 152 | return 0; |
| 153 | } |
| 154 | |
| 155 | #endif |
| 156 | |
| 157 | /* |
| 158 | * Provide the Poulsbo specific chip logic and low level methods |
| 159 | * for power management |
| 160 | */ |
| 161 | |
| 162 | static void psb_init_pm(struct drm_device *dev) |
| 163 | { |
| 164 | struct drm_psb_private *dev_priv = dev->dev_private; |
| 165 | |
| 166 | u32 gating = PSB_RSGX32(PSB_CR_CLKGATECTL); |
| 167 | gating &= ~3; /* Disable 2D clock gating */ |
| 168 | gating |= 1; |
| 169 | PSB_WSGX32(gating, PSB_CR_CLKGATECTL); |
| 170 | PSB_RSGX32(PSB_CR_CLKGATECTL); |
| 171 | } |
| 172 | |
| 173 | /** |
| 174 | * psb_save_display_registers - save registers lost on suspend |
| 175 | * @dev: our DRM device |
| 176 | * |
| 177 | * Save the state we need in order to be able to restore the interface |
| 178 | * upon resume from suspend |
| 179 | */ |
| 180 | static int psb_save_display_registers(struct drm_device *dev) |
| 181 | { |
| 182 | struct drm_psb_private *dev_priv = dev->dev_private; |
| 183 | struct drm_crtc *crtc; |
| 184 | struct drm_connector *connector; |
Alan Cox | c6265ff | 2012-03-08 16:02:05 +0000 | [diff] [blame] | 185 | struct psb_state *regs = &dev_priv->regs.psb; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 186 | |
| 187 | /* Display arbitration control + watermarks */ |
Alan Cox | 648a8e3 | 2012-03-08 16:00:31 +0000 | [diff] [blame] | 188 | regs->saveDSPARB = PSB_RVDC32(DSPARB); |
| 189 | regs->saveDSPFW1 = PSB_RVDC32(DSPFW1); |
| 190 | regs->saveDSPFW2 = PSB_RVDC32(DSPFW2); |
| 191 | regs->saveDSPFW3 = PSB_RVDC32(DSPFW3); |
| 192 | regs->saveDSPFW4 = PSB_RVDC32(DSPFW4); |
| 193 | regs->saveDSPFW5 = PSB_RVDC32(DSPFW5); |
| 194 | regs->saveDSPFW6 = PSB_RVDC32(DSPFW6); |
| 195 | regs->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 196 | |
| 197 | /* Save crtc and output state */ |
Daniel Vetter | 0a81951 | 2012-12-02 01:33:17 +0100 | [diff] [blame] | 198 | drm_modeset_lock_all(dev); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 199 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
| 200 | if (drm_helper_crtc_in_use(crtc)) |
| 201 | crtc->funcs->save(crtc); |
| 202 | } |
| 203 | |
| 204 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
Alan Cox | 7beff62 | 2012-05-21 15:27:44 +0100 | [diff] [blame] | 205 | if (connector->funcs->save) |
| 206 | connector->funcs->save(connector); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 207 | |
Daniel Vetter | 0a81951 | 2012-12-02 01:33:17 +0100 | [diff] [blame] | 208 | drm_modeset_unlock_all(dev); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 209 | return 0; |
| 210 | } |
| 211 | |
| 212 | /** |
| 213 | * psb_restore_display_registers - restore lost register state |
| 214 | * @dev: our DRM device |
| 215 | * |
| 216 | * Restore register state that was lost during suspend and resume. |
| 217 | */ |
| 218 | static int psb_restore_display_registers(struct drm_device *dev) |
| 219 | { |
| 220 | struct drm_psb_private *dev_priv = dev->dev_private; |
| 221 | struct drm_crtc *crtc; |
| 222 | struct drm_connector *connector; |
Alan Cox | c6265ff | 2012-03-08 16:02:05 +0000 | [diff] [blame] | 223 | struct psb_state *regs = &dev_priv->regs.psb; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 224 | |
| 225 | /* Display arbitration + watermarks */ |
Alan Cox | 648a8e3 | 2012-03-08 16:00:31 +0000 | [diff] [blame] | 226 | PSB_WVDC32(regs->saveDSPARB, DSPARB); |
| 227 | PSB_WVDC32(regs->saveDSPFW1, DSPFW1); |
| 228 | PSB_WVDC32(regs->saveDSPFW2, DSPFW2); |
| 229 | PSB_WVDC32(regs->saveDSPFW3, DSPFW3); |
| 230 | PSB_WVDC32(regs->saveDSPFW4, DSPFW4); |
| 231 | PSB_WVDC32(regs->saveDSPFW5, DSPFW5); |
| 232 | PSB_WVDC32(regs->saveDSPFW6, DSPFW6); |
| 233 | PSB_WVDC32(regs->saveCHICKENBIT, DSPCHICKENBIT); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 234 | |
| 235 | /*make sure VGA plane is off. it initializes to on after reset!*/ |
| 236 | PSB_WVDC32(0x80000000, VGACNTRL); |
| 237 | |
Daniel Vetter | 0a81951 | 2012-12-02 01:33:17 +0100 | [diff] [blame] | 238 | drm_modeset_lock_all(dev); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 239 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) |
| 240 | if (drm_helper_crtc_in_use(crtc)) |
| 241 | crtc->funcs->restore(crtc); |
| 242 | |
| 243 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
Alan Cox | 7beff62 | 2012-05-21 15:27:44 +0100 | [diff] [blame] | 244 | if (connector->funcs->restore) |
| 245 | connector->funcs->restore(connector); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 246 | |
Daniel Vetter | 0a81951 | 2012-12-02 01:33:17 +0100 | [diff] [blame] | 247 | drm_modeset_unlock_all(dev); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 248 | return 0; |
| 249 | } |
| 250 | |
| 251 | static int psb_power_down(struct drm_device *dev) |
| 252 | { |
| 253 | return 0; |
| 254 | } |
| 255 | |
| 256 | static int psb_power_up(struct drm_device *dev) |
| 257 | { |
| 258 | return 0; |
| 259 | } |
| 260 | |
Alan Cox | 8512e07 | 2012-05-11 11:30:53 +0100 | [diff] [blame] | 261 | /* Poulsbo */ |
| 262 | static const struct psb_offset psb_regmap[2] = { |
| 263 | { |
| 264 | .fp0 = FPA0, |
| 265 | .fp1 = FPA1, |
| 266 | .cntr = DSPACNTR, |
| 267 | .conf = PIPEACONF, |
| 268 | .src = PIPEASRC, |
| 269 | .dpll = DPLL_A, |
| 270 | .htotal = HTOTAL_A, |
| 271 | .hblank = HBLANK_A, |
| 272 | .hsync = HSYNC_A, |
| 273 | .vtotal = VTOTAL_A, |
| 274 | .vblank = VBLANK_A, |
| 275 | .vsync = VSYNC_A, |
| 276 | .stride = DSPASTRIDE, |
| 277 | .size = DSPASIZE, |
| 278 | .pos = DSPAPOS, |
| 279 | .base = DSPABASE, |
| 280 | .surf = DSPASURF, |
| 281 | .addr = DSPABASE, |
| 282 | .status = PIPEASTAT, |
| 283 | .linoff = DSPALINOFF, |
| 284 | .tileoff = DSPATILEOFF, |
| 285 | .palette = PALETTE_A, |
| 286 | }, |
| 287 | { |
| 288 | .fp0 = FPB0, |
| 289 | .fp1 = FPB1, |
| 290 | .cntr = DSPBCNTR, |
| 291 | .conf = PIPEBCONF, |
| 292 | .src = PIPEBSRC, |
| 293 | .dpll = DPLL_B, |
| 294 | .htotal = HTOTAL_B, |
| 295 | .hblank = HBLANK_B, |
| 296 | .hsync = HSYNC_B, |
| 297 | .vtotal = VTOTAL_B, |
| 298 | .vblank = VBLANK_B, |
| 299 | .vsync = VSYNC_B, |
| 300 | .stride = DSPBSTRIDE, |
| 301 | .size = DSPBSIZE, |
| 302 | .pos = DSPBPOS, |
| 303 | .base = DSPBBASE, |
| 304 | .surf = DSPBSURF, |
| 305 | .addr = DSPBBASE, |
| 306 | .status = PIPEBSTAT, |
| 307 | .linoff = DSPBLINOFF, |
| 308 | .tileoff = DSPBTILEOFF, |
| 309 | .palette = PALETTE_B, |
| 310 | } |
| 311 | }; |
| 312 | |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 313 | static int psb_chip_setup(struct drm_device *dev) |
| 314 | { |
Alan Cox | 8512e07 | 2012-05-11 11:30:53 +0100 | [diff] [blame] | 315 | struct drm_psb_private *dev_priv = dev->dev_private; |
| 316 | dev_priv->regmap = psb_regmap; |
Patrik Jakobsson | f35257a | 2014-03-11 22:53:43 +0100 | [diff] [blame] | 317 | gma_get_core_freq(dev); |
Patrik Jakobsson | 5c0c1d5 | 2011-12-19 21:40:58 +0000 | [diff] [blame] | 318 | gma_intel_setup_gmbus(dev); |
Alan Cox | d839ede | 2012-05-03 15:06:18 +0100 | [diff] [blame] | 319 | psb_intel_opregion_init(dev); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 320 | psb_intel_init_bios(dev); |
| 321 | return 0; |
| 322 | } |
| 323 | |
Patrik Jakobsson | 5c0c1d5 | 2011-12-19 21:40:58 +0000 | [diff] [blame] | 324 | static void psb_chip_teardown(struct drm_device *dev) |
| 325 | { |
Alan Cox | 6607e02 | 2012-05-14 12:03:34 +0100 | [diff] [blame] | 326 | struct drm_psb_private *dev_priv = dev->dev_private; |
| 327 | psb_lid_timer_takedown(dev_priv); |
Patrik Jakobsson | 5c0c1d5 | 2011-12-19 21:40:58 +0000 | [diff] [blame] | 328 | gma_intel_teardown_gmbus(dev); |
| 329 | } |
| 330 | |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 331 | const struct psb_ops psb_chip_ops = { |
| 332 | .name = "Poulsbo", |
| 333 | .accel_2d = 1, |
| 334 | .pipes = 2, |
| 335 | .crtcs = 2, |
Alan Cox | d235e64 | 2012-04-25 14:38:07 +0100 | [diff] [blame] | 336 | .hdmi_mask = (1 << 0), |
| 337 | .lvds_mask = (1 << 1), |
Patrik Jakobsson | cf8efd3 | 2013-09-16 17:54:54 +0200 | [diff] [blame] | 338 | .sdvo_mask = (1 << 0), |
Patrik Jakobsson | bc79482 | 2012-05-21 15:27:30 +0100 | [diff] [blame] | 339 | .cursor_needs_phys = 1, |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 340 | .sgx_offset = PSB_SGX_OFFSET, |
| 341 | .chip_setup = psb_chip_setup, |
Patrik Jakobsson | 5c0c1d5 | 2011-12-19 21:40:58 +0000 | [diff] [blame] | 342 | .chip_teardown = psb_chip_teardown, |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 343 | |
| 344 | .crtc_helper = &psb_intel_helper_funcs, |
| 345 | .crtc_funcs = &psb_intel_crtc_funcs, |
Patrik Jakobsson | 7f67c06 | 2013-07-02 17:07:59 +0200 | [diff] [blame] | 346 | .clock_funcs = &psb_clock_funcs, |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 347 | |
| 348 | .output_init = psb_output_init, |
| 349 | |
| 350 | #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE |
| 351 | .backlight_init = psb_backlight_init, |
| 352 | #endif |
| 353 | |
| 354 | .init_pm = psb_init_pm, |
| 355 | .save_regs = psb_save_display_registers, |
| 356 | .restore_regs = psb_restore_display_registers, |
| 357 | .power_down = psb_power_down, |
| 358 | .power_up = psb_power_up, |
| 359 | }; |
| 360 | |