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 "drmP.h" 37 #include "nouveau_drv.h" 38 #include "nouveau_drm.h" 39 #include "nouveau_reg.h" 40 #include "nouveau_encoder.h" 41 42 static int 43 nv40_get_intensity(struct backlight_device *bd) 44 { 45 struct drm_device *dev = bl_get_data(bd); 46 int val = (nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK) 47 >> 16; 48 49 return val; 50 } 51 52 static int 53 nv40_set_intensity(struct backlight_device *bd) 54 { 55 struct drm_device *dev = bl_get_data(bd); 56 int val = bd->props.brightness; 57 int reg = nv_rd32(dev, NV40_PMC_BACKLIGHT); 58 59 nv_wr32(dev, NV40_PMC_BACKLIGHT, 60 (val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK)); 61 62 return 0; 63 } 64 65 static 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 71 static int 72 nv40_backlight_init(struct drm_connector *connector) 73 { 74 struct drm_device *dev = connector->dev; 75 struct drm_nouveau_private *dev_priv = dev->dev_private; 76 struct backlight_properties props; 77 struct backlight_device *bd; 78 79 if (!(nv_rd32(dev, 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, dev, 86 &nv40_bl_ops, &props); 87 if (IS_ERR(bd)) 88 return PTR_ERR(bd); 89 90 dev_priv->backlight = bd; 91 bd->props.brightness = nv40_get_intensity(bd); 92 backlight_update_status(bd); 93 94 return 0; 95 } 96 97 static int 98 nv50_get_intensity(struct backlight_device *bd) 99 { 100 struct nouveau_encoder *nv_encoder = bl_get_data(bd); 101 struct drm_device *dev = nv_encoder->base.base.dev; 102 int or = nv_encoder->or; 103 104 return nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT + (or * 0x800)); 105 } 106 107 static int 108 nv50_set_intensity(struct backlight_device *bd) 109 { 110 struct nouveau_encoder *nv_encoder = bl_get_data(bd); 111 struct drm_device *dev = nv_encoder->base.base.dev; 112 int val = bd->props.brightness; 113 int or = nv_encoder->or; 114 115 nv_wr32(dev, NV50_PDISPLAY_SOR_BACKLIGHT + (or * 0x800), 116 val | NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE); 117 return 0; 118 } 119 120 static const struct backlight_ops nv50_bl_ops = { 121 .options = BL_CORE_SUSPENDRESUME, 122 .get_brightness = nv50_get_intensity, 123 .update_status = nv50_set_intensity, 124 }; 125 126 static int 127 nv50_backlight_init(struct drm_connector *connector) 128 { 129 struct drm_device *dev = connector->dev; 130 struct drm_nouveau_private *dev_priv = dev->dev_private; 131 struct nouveau_encoder *nv_encoder; 132 struct backlight_properties props; 133 struct backlight_device *bd; 134 int or; 135 136 nv_encoder = find_encoder(connector, OUTPUT_LVDS); 137 if (!nv_encoder) { 138 nv_encoder = find_encoder(connector, OUTPUT_DP); 139 if (!nv_encoder) 140 return -ENODEV; 141 } 142 143 or = nv_encoder->or; 144 145 if (!nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT + (or * 0x800))) 146 return 0; 147 148 memset(&props, 0, sizeof(struct backlight_properties)); 149 props.type = BACKLIGHT_RAW; 150 props.max_brightness = 1025; 151 bd = backlight_device_register("nv_backlight", &connector->kdev, 152 nv_encoder, &nv50_bl_ops, &props); 153 if (IS_ERR(bd)) 154 return PTR_ERR(bd); 155 156 dev_priv->backlight = bd; 157 bd->props.brightness = nv50_get_intensity(bd); 158 backlight_update_status(bd); 159 return 0; 160 } 161 162 int 163 nouveau_backlight_init(struct drm_device *dev) 164 { 165 struct drm_nouveau_private *dev_priv = dev->dev_private; 166 struct drm_connector *connector; 167 168 #ifdef CONFIG_ACPI 169 if (acpi_video_backlight_support()) { 170 NV_INFO(dev, "ACPI backlight interface available, " 171 "not registering our own\n"); 172 return 0; 173 } 174 #endif 175 176 list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 177 if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS && 178 connector->connector_type != DRM_MODE_CONNECTOR_eDP) 179 continue; 180 181 switch (dev_priv->card_type) { 182 case NV_40: 183 return nv40_backlight_init(connector); 184 case NV_50: 185 return nv50_backlight_init(connector); 186 default: 187 break; 188 } 189 } 190 191 192 return 0; 193 } 194 195 void 196 nouveau_backlight_exit(struct drm_device *dev) 197 { 198 struct drm_nouveau_private *dev_priv = dev->dev_private; 199 200 if (dev_priv->backlight) { 201 backlight_device_unregister(dev_priv->backlight); 202 dev_priv->backlight = NULL; 203 } 204 } 205