linux/drivers/gpu/drm/nouveau/nouveau_backlight.c
<<
>>
Prefs
   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
  40static int
  41nv40_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
  51static int
  52nv40_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
  65static 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
  71static int
  72nv40_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
  96static int
  97nv50_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
 111static int
 112nv50_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
 126static 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
 132static int
 133nva3_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
 150static int
 151nva3_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
 171static 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
 177static int
 178nv50_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
 218int
 219nouveau_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
 255void
 256nouveau_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