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
  35#include "nouveau_drm.h"
  36#include "nouveau_reg.h"
  37#include "nouveau_encoder.h"
  38
  39static int
  40nv40_get_intensity(struct backlight_device *bd)
  41{
  42        struct nouveau_drm *drm = bl_get_data(bd);
  43        struct nvif_object *device = &drm->device.object;
  44        int val = (nvif_rd32(device, NV40_PMC_BACKLIGHT) &
  45                                   NV40_PMC_BACKLIGHT_MASK) >> 16;
  46
  47        return val;
  48}
  49
  50static int
  51nv40_set_intensity(struct backlight_device *bd)
  52{
  53        struct nouveau_drm *drm = bl_get_data(bd);
  54        struct nvif_object *device = &drm->device.object;
  55        int val = bd->props.brightness;
  56        int reg = nvif_rd32(device, NV40_PMC_BACKLIGHT);
  57
  58        nvif_wr32(device, NV40_PMC_BACKLIGHT,
  59                 (val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK));
  60
  61        return 0;
  62}
  63
  64static const struct backlight_ops nv40_bl_ops = {
  65        .options = BL_CORE_SUSPENDRESUME,
  66        .get_brightness = nv40_get_intensity,
  67        .update_status = nv40_set_intensity,
  68};
  69
  70static int
  71nv40_backlight_init(struct drm_connector *connector)
  72{
  73        struct nouveau_drm *drm = nouveau_drm(connector->dev);
  74        struct nvif_object *device = &drm->device.object;
  75        struct backlight_properties props;
  76        struct backlight_device *bd;
  77
  78        if (!(nvif_rd32(device, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK))
  79                return 0;
  80
  81        memset(&props, 0, sizeof(struct backlight_properties));
  82        props.type = BACKLIGHT_RAW;
  83        props.max_brightness = 31;
  84        bd = backlight_device_register("nv_backlight", connector->kdev, drm,
  85                                       &nv40_bl_ops, &props);
  86        if (IS_ERR(bd))
  87                return PTR_ERR(bd);
  88        drm->backlight = bd;
  89        bd->props.brightness = nv40_get_intensity(bd);
  90        backlight_update_status(bd);
  91
  92        return 0;
  93}
  94
  95static int
  96nv50_get_intensity(struct backlight_device *bd)
  97{
  98        struct nouveau_encoder *nv_encoder = bl_get_data(bd);
  99        struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
 100        struct nvif_object *device = &drm->device.object;
 101        int or = nv_encoder->or;
 102        u32 div = 1025;
 103        u32 val;
 104
 105        val  = nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(or));
 106        val &= NV50_PDISP_SOR_PWM_CTL_VAL;
 107        return ((val * 100) + (div / 2)) / div;
 108}
 109
 110static int
 111nv50_set_intensity(struct backlight_device *bd)
 112{
 113        struct nouveau_encoder *nv_encoder = bl_get_data(bd);
 114        struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
 115        struct nvif_object *device = &drm->device.object;
 116        int or = nv_encoder->or;
 117        u32 div = 1025;
 118        u32 val = (bd->props.brightness * div) / 100;
 119
 120        nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or),
 121                        NV50_PDISP_SOR_PWM_CTL_NEW | val);
 122        return 0;
 123}
 124
 125static const struct backlight_ops nv50_bl_ops = {
 126        .options = BL_CORE_SUSPENDRESUME,
 127        .get_brightness = nv50_get_intensity,
 128        .update_status = nv50_set_intensity,
 129};
 130
 131static int
 132nva3_get_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->device.object;
 137        int or = nv_encoder->or;
 138        u32 div, val;
 139
 140        div  = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or));
 141        val  = nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(or));
 142        val &= NVA3_PDISP_SOR_PWM_CTL_VAL;
 143        if (div && div >= val)
 144                return ((val * 100) + (div / 2)) / div;
 145
 146        return 100;
 147}
 148
 149static int
 150nva3_set_intensity(struct backlight_device *bd)
 151{
 152        struct nouveau_encoder *nv_encoder = bl_get_data(bd);
 153        struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
 154        struct nvif_object *device = &drm->device.object;
 155        int or = nv_encoder->or;
 156        u32 div, val;
 157
 158        div = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or));
 159        val = (bd->props.brightness * div) / 100;
 160        if (div) {
 161                nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or), val |
 162                                NV50_PDISP_SOR_PWM_CTL_NEW |
 163                                NVA3_PDISP_SOR_PWM_CTL_UNK);
 164                return 0;
 165        }
 166
 167        return -EINVAL;
 168}
 169
 170static const struct backlight_ops nva3_bl_ops = {
 171        .options = BL_CORE_SUSPENDRESUME,
 172        .get_brightness = nva3_get_intensity,
 173        .update_status = nva3_set_intensity,
 174};
 175
 176static int
 177nv50_backlight_init(struct drm_connector *connector)
 178{
 179        struct nouveau_drm *drm = nouveau_drm(connector->dev);
 180        struct nvif_object *device = &drm->device.object;
 181        struct nouveau_encoder *nv_encoder;
 182        struct backlight_properties props;
 183        struct backlight_device *bd;
 184        const struct backlight_ops *ops;
 185
 186        nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS);
 187        if (!nv_encoder) {
 188                nv_encoder = find_encoder(connector, DCB_OUTPUT_DP);
 189                if (!nv_encoder)
 190                        return -ENODEV;
 191        }
 192
 193        if (!nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(nv_encoder->or)))
 194                return 0;
 195
 196        if (drm->device.info.chipset <= 0xa0 ||
 197            drm->device.info.chipset == 0xaa ||
 198            drm->device.info.chipset == 0xac)
 199                ops = &nv50_bl_ops;
 200        else
 201                ops = &nva3_bl_ops;
 202
 203        memset(&props, 0, sizeof(struct backlight_properties));
 204        props.type = BACKLIGHT_RAW;
 205        props.max_brightness = 100;
 206        bd = backlight_device_register("nv_backlight", connector->kdev,
 207                                       nv_encoder, ops, &props);
 208        if (IS_ERR(bd))
 209                return PTR_ERR(bd);
 210
 211        drm->backlight = bd;
 212        bd->props.brightness = bd->ops->get_brightness(bd);
 213        backlight_update_status(bd);
 214        return 0;
 215}
 216
 217int
 218nouveau_backlight_init(struct drm_device *dev)
 219{
 220        struct nouveau_drm *drm = nouveau_drm(dev);
 221        struct nvif_device *device = &drm->device;
 222        struct drm_connector *connector;
 223
 224        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
 225                if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS &&
 226                    connector->connector_type != DRM_MODE_CONNECTOR_eDP)
 227                        continue;
 228
 229                switch (device->info.family) {
 230                case NV_DEVICE_INFO_V0_CURIE:
 231                        return nv40_backlight_init(connector);
 232                case NV_DEVICE_INFO_V0_TESLA:
 233                case NV_DEVICE_INFO_V0_FERMI:
 234                case NV_DEVICE_INFO_V0_KEPLER:
 235                        return nv50_backlight_init(connector);
 236                default:
 237                        break;
 238                }
 239        }
 240
 241
 242        return 0;
 243}
 244
 245void
 246nouveau_backlight_exit(struct drm_device *dev)
 247{
 248        struct nouveau_drm *drm = nouveau_drm(dev);
 249
 250        if (drm->backlight) {
 251                backlight_device_unregister(drm->backlight);
 252                drm->backlight = NULL;
 253        }
 254}
 255