1 /* 2 * Copyright (C) 2009 Red Hat <mjg@redhat.com> 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining 5 * a copy of this software and associated documentation files (the 6 * "Software"), to deal in the Software without restriction, including 7 * without limitation the rights to use, copy, modify, merge, publish, 8 * distribute, sublicense, and/or sell copies of the Software, and to 9 * permit persons to whom the Software is furnished to do so, subject to 10 * the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the 13 * next paragraph) shall be included in all copies or substantial 14 * portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE 20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 * 24 */ 25 26 /* 27 * Authors: 28 * Matthew Garrett <mjg@redhat.com> 29 * 30 * Register locations derived from NVClock by Roderick Colenbrander 31 */ 32 33 #include <linux/backlight.h> 34 #include <linux/acpi.h> 35 36 #include "nouveau_drm.h" 37 #include "nouveau_reg.h" 38 #include "nouveau_encoder.h" 39 40 static int 41 nv40_get_intensity(struct backlight_device *bd) 42 { 43 struct nouveau_drm *drm = bl_get_data(bd); 44 struct nouveau_device *device = nv_device(drm->device); 45 int val = (nv_rd32(device, NV40_PMC_BACKLIGHT) & 46 NV40_PMC_BACKLIGHT_MASK) >> 16; 47 48 return val; 49 } 50 51 static int 52 nv40_set_intensity(struct backlight_device *bd) 53 { 54 struct nouveau_drm *drm = bl_get_data(bd); 55 struct nouveau_device *device = nv_device(drm->device); 56 int val = bd->props.brightness; 57 int reg = nv_rd32(device, NV40_PMC_BACKLIGHT); 58 59 nv_wr32(device, NV40_PMC_BACKLIGHT, 60 (val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK)); 61 62 return 0; 63 } 64 65 static const struct backlight_ops nv40_bl_ops = { 66 .options = BL_CORE_SUSPENDRESUME, 67 .get_brightness = nv40_get_intensity, 68 .update_status = nv40_set_intensity, 69 }; 70 71 static int 72 nv40_backlight_init(struct drm_connector *connector) 73 { 74 struct nouveau_drm *drm = nouveau_drm(connector->dev); 75 struct nouveau_device *device = nv_device(drm->device); 76 struct backlight_properties props; 77 struct backlight_device *bd; 78 79 if (!(nv_rd32(device, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK)) 80 return 0; 81 82 memset(&props, 0, sizeof(struct backlight_properties)); 83 props.type = BACKLIGHT_RAW; 84 props.max_brightness = 31; 85 bd = backlight_device_register("nv_backlight", connector->kdev, drm, 86 &nv40_bl_ops, &props); 87 if (IS_ERR(bd)) 88 return PTR_ERR(bd); 89 drm->backlight = bd; 90 bd->props.brightness = nv40_get_intensity(bd); 91 backlight_update_status(bd); 92 93 return 0; 94 } 95 96 static int 97 nv50_get_intensity(struct backlight_device *bd) 98 { 99 struct nouveau_encoder *nv_encoder = bl_get_data(bd); 100 struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); 101 struct nouveau_device *device = nv_device(drm->device); 102 int or = nv_encoder->or; 103 u32 div = 1025; 104 u32 val; 105 106 val = nv_rd32(device, NV50_PDISP_SOR_PWM_CTL(or)); 107 val &= NV50_PDISP_SOR_PWM_CTL_VAL; 108 return ((val * 100) + (div / 2)) / div; 109 } 110 111 static int 112 nv50_set_intensity(struct backlight_device *bd) 113 { 114 struct nouveau_encoder *nv_encoder = bl_get_data(bd); 115 struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); 116 struct nouveau_device *device = nv_device(drm->device); 117 int or = nv_encoder->or; 118 u32 div = 1025; 119 u32 val = (bd->props.brightness * div) / 100; 120 121 nv_wr32(device, NV50_PDISP_SOR_PWM_CTL(or), 122 NV50_PDISP_SOR_PWM_CTL_NEW | val); 123 return 0; 124 } 125 126 static const struct backlight_ops nv50_bl_ops = { 127 .options = BL_CORE_SUSPENDRESUME, 128 .get_brightness = nv50_get_intensity, 129 .update_status = nv50_set_intensity, 130 }; 131 132 static int 133 nva3_get_intensity(struct backlight_device *bd) 134 { 135 struct nouveau_encoder *nv_encoder = bl_get_data(bd); 136 struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); 137 struct nouveau_device *device = nv_device(drm->device); 138 int or = nv_encoder->or; 139 u32 div, val; 140 141 div = nv_rd32(device, NV50_PDISP_SOR_PWM_DIV(or)); 142 val = nv_rd32(device, NV50_PDISP_SOR_PWM_CTL(or)); 143 val &= NVA3_PDISP_SOR_PWM_CTL_VAL; 144 if (div && div >= val) 145 return ((val * 100) + (div / 2)) / div; 146 147 return 100; 148 } 149 150 static int 151 nva3_set_intensity(struct backlight_device *bd) 152 { 153 struct nouveau_encoder *nv_encoder = bl_get_data(bd); 154 struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); 155 struct nouveau_device *device = nv_device(drm->device); 156 int or = nv_encoder->or; 157 u32 div, val; 158 159 div = nv_rd32(device, NV50_PDISP_SOR_PWM_DIV(or)); 160 val = (bd->props.brightness * div) / 100; 161 if (div) { 162 nv_wr32(device, NV50_PDISP_SOR_PWM_CTL(or), val | 163 NV50_PDISP_SOR_PWM_CTL_NEW | 164 NVA3_PDISP_SOR_PWM_CTL_UNK); 165 return 0; 166 } 167 168 return -EINVAL; 169 } 170 171 static const struct backlight_ops nva3_bl_ops = { 172 .options = BL_CORE_SUSPENDRESUME, 173 .get_brightness = nva3_get_intensity, 174 .update_status = nva3_set_intensity, 175 }; 176 177 static int 178 nv50_backlight_init(struct drm_connector *connector) 179 { 180 struct nouveau_drm *drm = nouveau_drm(connector->dev); 181 struct nouveau_device *device = nv_device(drm->device); 182 struct nouveau_encoder *nv_encoder; 183 struct backlight_properties props; 184 struct backlight_device *bd; 185 const struct backlight_ops *ops; 186 187 nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS); 188 if (!nv_encoder) { 189 nv_encoder = find_encoder(connector, DCB_OUTPUT_DP); 190 if (!nv_encoder) 191 return -ENODEV; 192 } 193 194 if (!nv_rd32(device, NV50_PDISP_SOR_PWM_CTL(nv_encoder->or))) 195 return 0; 196 197 if (device->chipset <= 0xa0 || 198 device->chipset == 0xaa || 199 device->chipset == 0xac) 200 ops = &nv50_bl_ops; 201 else 202 ops = &nva3_bl_ops; 203 204 memset(&props, 0, sizeof(struct backlight_properties)); 205 props.type = BACKLIGHT_RAW; 206 props.max_brightness = 100; 207 bd = backlight_device_register("nv_backlight", connector->kdev, 208 nv_encoder, ops, &props); 209 if (IS_ERR(bd)) 210 return PTR_ERR(bd); 211 212 drm->backlight = bd; 213 bd->props.brightness = bd->ops->get_brightness(bd); 214 backlight_update_status(bd); 215 return 0; 216 } 217 218 int 219 nouveau_backlight_init(struct drm_device *dev) 220 { 221 struct nouveau_drm *drm = nouveau_drm(dev); 222 struct nouveau_device *device = nv_device(drm->device); 223 struct drm_connector *connector; 224 225 #ifdef CONFIG_ACPI 226 if (acpi_video_backlight_support()) { 227 NV_INFO(drm, "ACPI backlight interface available, " 228 "not registering our own\n"); 229 return 0; 230 } 231 #endif 232 233 list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 234 if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS && 235 connector->connector_type != DRM_MODE_CONNECTOR_eDP) 236 continue; 237 238 switch (device->card_type) { 239 case NV_40: 240 return nv40_backlight_init(connector); 241 case NV_50: 242 case NV_C0: 243 case NV_D0: 244 case NV_E0: 245 return nv50_backlight_init(connector); 246 default: 247 break; 248 } 249 } 250 251 252 return 0; 253 } 254 255 void 256 nouveau_backlight_exit(struct drm_device *dev) 257 { 258 struct nouveau_drm *drm = nouveau_drm(dev); 259 260 if (drm->backlight) { 261 backlight_device_unregister(drm->backlight); 262 drm->backlight = NULL; 263 } 264 } 265