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/apple-gmux.h> 34 #include <linux/backlight.h> 35 #include <linux/idr.h> 36 37 #include "nouveau_drv.h" 38 #include "nouveau_reg.h" 39 #include "nouveau_encoder.h" 40 #include "nouveau_connector.h" 41 42 static struct ida bl_ida; 43 #define BL_NAME_SIZE 15 // 12 for name + 2 for digits + 1 for '\0' 44 45 struct nouveau_backlight { 46 struct backlight_device *dev; 47 int id; 48 }; 49 50 static bool 51 nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE], 52 struct nouveau_backlight *bl) 53 { 54 const int nb = ida_simple_get(&bl_ida, 0, 0, GFP_KERNEL); 55 if (nb < 0 || nb >= 100) 56 return false; 57 if (nb > 0) 58 snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight%d", nb); 59 else 60 snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight"); 61 bl->id = nb; 62 return true; 63 } 64 65 static int 66 nv40_get_intensity(struct backlight_device *bd) 67 { 68 struct nouveau_encoder *nv_encoder = bl_get_data(bd); 69 struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); 70 struct nvif_object *device = &drm->client.device.object; 71 int val = (nvif_rd32(device, NV40_PMC_BACKLIGHT) & 72 NV40_PMC_BACKLIGHT_MASK) >> 16; 73 74 return val; 75 } 76 77 static int 78 nv40_set_intensity(struct backlight_device *bd) 79 { 80 struct nouveau_encoder *nv_encoder = bl_get_data(bd); 81 struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); 82 struct nvif_object *device = &drm->client.device.object; 83 int val = bd->props.brightness; 84 int reg = nvif_rd32(device, NV40_PMC_BACKLIGHT); 85 86 nvif_wr32(device, NV40_PMC_BACKLIGHT, 87 (val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK)); 88 89 return 0; 90 } 91 92 static const struct backlight_ops nv40_bl_ops = { 93 .options = BL_CORE_SUSPENDRESUME, 94 .get_brightness = nv40_get_intensity, 95 .update_status = nv40_set_intensity, 96 }; 97 98 static int 99 nv40_backlight_init(struct nouveau_encoder *encoder, 100 struct backlight_properties *props, 101 const struct backlight_ops **ops) 102 { 103 struct nouveau_drm *drm = nouveau_drm(encoder->base.base.dev); 104 struct nvif_object *device = &drm->client.device.object; 105 106 if (!(nvif_rd32(device, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK)) 107 return -ENODEV; 108 109 props->type = BACKLIGHT_RAW; 110 props->max_brightness = 31; 111 *ops = &nv40_bl_ops; 112 return 0; 113 } 114 115 static int 116 nv50_get_intensity(struct backlight_device *bd) 117 { 118 struct nouveau_encoder *nv_encoder = bl_get_data(bd); 119 struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); 120 struct nvif_object *device = &drm->client.device.object; 121 int or = ffs(nv_encoder->dcb->or) - 1; 122 u32 div = 1025; 123 u32 val; 124 125 val = nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(or)); 126 val &= NV50_PDISP_SOR_PWM_CTL_VAL; 127 return ((val * 100) + (div / 2)) / div; 128 } 129 130 static int 131 nv50_set_intensity(struct backlight_device *bd) 132 { 133 struct nouveau_encoder *nv_encoder = bl_get_data(bd); 134 struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); 135 struct nvif_object *device = &drm->client.device.object; 136 int or = ffs(nv_encoder->dcb->or) - 1; 137 u32 div = 1025; 138 u32 val = (bd->props.brightness * div) / 100; 139 140 nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or), 141 NV50_PDISP_SOR_PWM_CTL_NEW | val); 142 return 0; 143 } 144 145 static const struct backlight_ops nv50_bl_ops = { 146 .options = BL_CORE_SUSPENDRESUME, 147 .get_brightness = nv50_get_intensity, 148 .update_status = nv50_set_intensity, 149 }; 150 151 static int 152 nva3_get_intensity(struct backlight_device *bd) 153 { 154 struct nouveau_encoder *nv_encoder = bl_get_data(bd); 155 struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); 156 struct nvif_object *device = &drm->client.device.object; 157 int or = ffs(nv_encoder->dcb->or) - 1; 158 u32 div, val; 159 160 div = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or)); 161 val = nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(or)); 162 val &= NVA3_PDISP_SOR_PWM_CTL_VAL; 163 if (div && div >= val) 164 return ((val * 100) + (div / 2)) / div; 165 166 return 100; 167 } 168 169 static int 170 nva3_set_intensity(struct backlight_device *bd) 171 { 172 struct nouveau_encoder *nv_encoder = bl_get_data(bd); 173 struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); 174 struct nvif_object *device = &drm->client.device.object; 175 int or = ffs(nv_encoder->dcb->or) - 1; 176 u32 div, val; 177 178 div = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or)); 179 val = (bd->props.brightness * div) / 100; 180 if (div) { 181 nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or), 182 val | 183 NV50_PDISP_SOR_PWM_CTL_NEW | 184 NVA3_PDISP_SOR_PWM_CTL_UNK); 185 return 0; 186 } 187 188 return -EINVAL; 189 } 190 191 static const struct backlight_ops nva3_bl_ops = { 192 .options = BL_CORE_SUSPENDRESUME, 193 .get_brightness = nva3_get_intensity, 194 .update_status = nva3_set_intensity, 195 }; 196 197 static int 198 nv50_backlight_init(struct nouveau_encoder *nv_encoder, 199 struct backlight_properties *props, 200 const struct backlight_ops **ops) 201 { 202 struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); 203 struct nvif_object *device = &drm->client.device.object; 204 205 if (!nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(ffs(nv_encoder->dcb->or) - 1))) 206 return -ENODEV; 207 208 if (drm->client.device.info.chipset <= 0xa0 || 209 drm->client.device.info.chipset == 0xaa || 210 drm->client.device.info.chipset == 0xac) 211 *ops = &nv50_bl_ops; 212 else 213 *ops = &nva3_bl_ops; 214 215 props->type = BACKLIGHT_RAW; 216 props->max_brightness = 100; 217 218 return 0; 219 } 220 221 int 222 nouveau_backlight_init(struct drm_connector *connector) 223 { 224 struct nouveau_drm *drm = nouveau_drm(connector->dev); 225 struct nouveau_backlight *bl; 226 struct nouveau_encoder *nv_encoder = NULL; 227 struct nvif_device *device = &drm->client.device; 228 char backlight_name[BL_NAME_SIZE]; 229 struct backlight_properties props = {0}; 230 const struct backlight_ops *ops; 231 int ret; 232 233 if (apple_gmux_present()) { 234 NV_INFO_ONCE(drm, "Apple GMUX detected: not registering Nouveau backlight interface\n"); 235 return 0; 236 } 237 238 if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) 239 nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS); 240 else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) 241 nv_encoder = find_encoder(connector, DCB_OUTPUT_DP); 242 else 243 return 0; 244 245 if (!nv_encoder) 246 return 0; 247 248 switch (device->info.family) { 249 case NV_DEVICE_INFO_V0_CURIE: 250 ret = nv40_backlight_init(nv_encoder, &props, &ops); 251 break; 252 case NV_DEVICE_INFO_V0_TESLA: 253 case NV_DEVICE_INFO_V0_FERMI: 254 case NV_DEVICE_INFO_V0_KEPLER: 255 case NV_DEVICE_INFO_V0_MAXWELL: 256 case NV_DEVICE_INFO_V0_PASCAL: 257 case NV_DEVICE_INFO_V0_VOLTA: 258 case NV_DEVICE_INFO_V0_TURING: 259 ret = nv50_backlight_init(nv_encoder, &props, &ops); 260 break; 261 default: 262 return 0; 263 } 264 265 if (ret == -ENODEV) 266 return 0; 267 else if (ret) 268 return ret; 269 270 bl = kzalloc(sizeof(*bl), GFP_KERNEL); 271 if (!bl) 272 return -ENOMEM; 273 274 if (!nouveau_get_backlight_name(backlight_name, bl)) { 275 NV_ERROR(drm, "Failed to retrieve a unique name for the backlight interface\n"); 276 goto fail_alloc; 277 } 278 279 bl->dev = backlight_device_register(backlight_name, connector->kdev, 280 nv_encoder, ops, &props); 281 if (IS_ERR(bl->dev)) { 282 if (bl->id >= 0) 283 ida_simple_remove(&bl_ida, bl->id); 284 ret = PTR_ERR(bl->dev); 285 goto fail_alloc; 286 } 287 288 nouveau_connector(connector)->backlight = bl; 289 bl->dev->props.brightness = bl->dev->ops->get_brightness(bl->dev); 290 backlight_update_status(bl->dev); 291 292 return 0; 293 294 fail_alloc: 295 kfree(bl); 296 return ret; 297 } 298 299 void 300 nouveau_backlight_fini(struct drm_connector *connector) 301 { 302 struct nouveau_connector *nv_conn = nouveau_connector(connector); 303 struct nouveau_backlight *bl = nv_conn->backlight; 304 305 if (!bl) 306 return; 307 308 if (bl->id >= 0) 309 ida_simple_remove(&bl_ida, bl->id); 310 311 backlight_device_unregister(bl->dev); 312 nv_conn->backlight = NULL; 313 kfree(bl); 314 } 315 316 void 317 nouveau_backlight_ctor(void) 318 { 319 ida_init(&bl_ida); 320 } 321 322 void 323 nouveau_backlight_dtor(void) 324 { 325 ida_destroy(&bl_ida); 326 } 327