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 41 static int nv40_get_intensity(struct backlight_device *bd) 42 { 43 struct drm_device *dev = bl_get_data(bd); 44 int val = (nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK) 45 >> 16; 46 47 return val; 48 } 49 50 static int nv40_set_intensity(struct backlight_device *bd) 51 { 52 struct drm_device *dev = bl_get_data(bd); 53 int val = bd->props.brightness; 54 int reg = nv_rd32(dev, NV40_PMC_BACKLIGHT); 55 56 nv_wr32(dev, NV40_PMC_BACKLIGHT, 57 (val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK)); 58 59 return 0; 60 } 61 62 static const struct backlight_ops nv40_bl_ops = { 63 .options = BL_CORE_SUSPENDRESUME, 64 .get_brightness = nv40_get_intensity, 65 .update_status = nv40_set_intensity, 66 }; 67 68 static int nv50_get_intensity(struct backlight_device *bd) 69 { 70 struct drm_device *dev = bl_get_data(bd); 71 72 return nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT); 73 } 74 75 static int nv50_set_intensity(struct backlight_device *bd) 76 { 77 struct drm_device *dev = bl_get_data(bd); 78 int val = bd->props.brightness; 79 80 nv_wr32(dev, NV50_PDISPLAY_SOR_BACKLIGHT, 81 val | NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE); 82 return 0; 83 } 84 85 static const struct backlight_ops nv50_bl_ops = { 86 .options = BL_CORE_SUSPENDRESUME, 87 .get_brightness = nv50_get_intensity, 88 .update_status = nv50_set_intensity, 89 }; 90 91 static int nouveau_nv40_backlight_init(struct drm_connector *connector) 92 { 93 struct drm_device *dev = connector->dev; 94 struct drm_nouveau_private *dev_priv = dev->dev_private; 95 struct backlight_properties props; 96 struct backlight_device *bd; 97 98 if (!(nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK)) 99 return 0; 100 101 memset(&props, 0, sizeof(struct backlight_properties)); 102 props.type = BACKLIGHT_RAW; 103 props.max_brightness = 31; 104 bd = backlight_device_register("nv_backlight", &connector->kdev, dev, 105 &nv40_bl_ops, &props); 106 if (IS_ERR(bd)) 107 return PTR_ERR(bd); 108 109 dev_priv->backlight = bd; 110 bd->props.brightness = nv40_get_intensity(bd); 111 backlight_update_status(bd); 112 113 return 0; 114 } 115 116 static int nouveau_nv50_backlight_init(struct drm_connector *connector) 117 { 118 struct drm_device *dev = connector->dev; 119 struct drm_nouveau_private *dev_priv = dev->dev_private; 120 struct backlight_properties props; 121 struct backlight_device *bd; 122 123 if (!nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT)) 124 return 0; 125 126 memset(&props, 0, sizeof(struct backlight_properties)); 127 props.type = BACKLIGHT_RAW; 128 props.max_brightness = 1025; 129 bd = backlight_device_register("nv_backlight", &connector->kdev, dev, 130 &nv50_bl_ops, &props); 131 if (IS_ERR(bd)) 132 return PTR_ERR(bd); 133 134 dev_priv->backlight = bd; 135 bd->props.brightness = nv50_get_intensity(bd); 136 backlight_update_status(bd); 137 return 0; 138 } 139 140 int nouveau_backlight_init(struct drm_connector *connector) 141 { 142 struct drm_device *dev = connector->dev; 143 struct drm_nouveau_private *dev_priv = dev->dev_private; 144 145 #ifdef CONFIG_ACPI 146 if (acpi_video_backlight_support()) { 147 NV_INFO(dev, "ACPI backlight interface available, " 148 "not registering our own\n"); 149 return 0; 150 } 151 #endif 152 153 switch (dev_priv->card_type) { 154 case NV_40: 155 return nouveau_nv40_backlight_init(connector); 156 case NV_50: 157 return nouveau_nv50_backlight_init(connector); 158 default: 159 break; 160 } 161 162 return 0; 163 } 164 165 void nouveau_backlight_exit(struct drm_connector *connector) 166 { 167 struct drm_device *dev = connector->dev; 168 struct drm_nouveau_private *dev_priv = dev->dev_private; 169 170 if (dev_priv->backlight) { 171 backlight_device_unregister(dev_priv->backlight); 172 dev_priv->backlight = NULL; 173 } 174 } 175