root/drivers/gpu/drm/nouveau/nouveau_backlight.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. nouveau_get_backlight_name
  2. nv40_get_intensity
  3. nv40_set_intensity
  4. nv40_backlight_init
  5. nv50_get_intensity
  6. nv50_set_intensity
  7. nva3_get_intensity
  8. nva3_set_intensity
  9. nv50_backlight_init
  10. nouveau_backlight_init
  11. nouveau_backlight_fini
  12. nouveau_backlight_ctor
  13. nouveau_backlight_dtor

   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 }

/* [<][>][^][v][top][bottom][index][help] */