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