Home | History | Annotate | Line # | Download | only in nouveau
nouveau_backlight.c revision 1.2
      1 /*	$NetBSD: nouveau_backlight.c,v 1.2 2018/08/27 04:58:24 riastradh Exp $	*/
      2 
      3 /*
      4  * Copyright (C) 2009 Red Hat <mjg (at) redhat.com>
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining
      7  * a copy of this software and associated documentation files (the
      8  * "Software"), to deal in the Software without restriction, including
      9  * without limitation the rights to use, copy, modify, merge, publish,
     10  * distribute, sublicense, and/or sell copies of the Software, and to
     11  * permit persons to whom the Software is furnished to do so, subject to
     12  * the following conditions:
     13  *
     14  * The above copyright notice and this permission notice (including the
     15  * next paragraph) shall be included in all copies or substantial
     16  * portions of the Software.
     17  *
     18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     21  * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
     22  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     23  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     24  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     25  *
     26  */
     27 
     28 /*
     29  * Authors:
     30  *  Matthew Garrett <mjg (at) redhat.com>
     31  *
     32  * Register locations derived from NVClock by Roderick Colenbrander
     33  */
     34 
     35 #include <sys/cdefs.h>
     36 __KERNEL_RCSID(0, "$NetBSD: nouveau_backlight.c,v 1.2 2018/08/27 04:58:24 riastradh Exp $");
     37 
     38 #include <linux/backlight.h>
     39 
     40 #include "nouveau_drm.h"
     41 #include "nouveau_reg.h"
     42 #include "nouveau_encoder.h"
     43 
     44 static int
     45 nv40_get_intensity(struct backlight_device *bd)
     46 {
     47 	struct nouveau_drm *drm = bl_get_data(bd);
     48 	struct nvif_object *device = &drm->device.object;
     49 	int val = (nvif_rd32(device, NV40_PMC_BACKLIGHT) &
     50 				   NV40_PMC_BACKLIGHT_MASK) >> 16;
     51 
     52 	return val;
     53 }
     54 
     55 static int
     56 nv40_set_intensity(struct backlight_device *bd)
     57 {
     58 	struct nouveau_drm *drm = bl_get_data(bd);
     59 	struct nvif_object *device = &drm->device.object;
     60 	int val = bd->props.brightness;
     61 	int reg = nvif_rd32(device, NV40_PMC_BACKLIGHT);
     62 
     63 	nvif_wr32(device, NV40_PMC_BACKLIGHT,
     64 		 (val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK));
     65 
     66 	return 0;
     67 }
     68 
     69 static const struct backlight_ops nv40_bl_ops = {
     70 	.options = BL_CORE_SUSPENDRESUME,
     71 	.get_brightness = nv40_get_intensity,
     72 	.update_status = nv40_set_intensity,
     73 };
     74 
     75 static int
     76 nv40_backlight_init(struct drm_connector *connector)
     77 {
     78 	struct nouveau_drm *drm = nouveau_drm(connector->dev);
     79 	struct nvif_object *device = &drm->device.object;
     80 	struct backlight_properties props;
     81 	struct backlight_device *bd;
     82 
     83 	if (!(nvif_rd32(device, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK))
     84 		return 0;
     85 
     86 	memset(&props, 0, sizeof(struct backlight_properties));
     87 	props.type = BACKLIGHT_RAW;
     88 	props.max_brightness = 31;
     89 	bd = backlight_device_register("nv_backlight", connector->kdev, drm,
     90 				       &nv40_bl_ops, &props);
     91 	if (IS_ERR(bd))
     92 		return PTR_ERR(bd);
     93 	drm->backlight = bd;
     94 	bd->props.brightness = nv40_get_intensity(bd);
     95 	backlight_update_status(bd);
     96 
     97 	return 0;
     98 }
     99 
    100 static int
    101 nv50_get_intensity(struct backlight_device *bd)
    102 {
    103 	struct nouveau_encoder *nv_encoder = bl_get_data(bd);
    104 	struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
    105 	struct nvif_object *device = &drm->device.object;
    106 	int or = nv_encoder->or;
    107 	u32 div = 1025;
    108 	u32 val;
    109 
    110 	val  = nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(or));
    111 	val &= NV50_PDISP_SOR_PWM_CTL_VAL;
    112 	return ((val * 100) + (div / 2)) / div;
    113 }
    114 
    115 static int
    116 nv50_set_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->device.object;
    121 	int or = nv_encoder->or;
    122 	u32 div = 1025;
    123 	u32 val = (bd->props.brightness * div) / 100;
    124 
    125 	nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or),
    126 			NV50_PDISP_SOR_PWM_CTL_NEW | val);
    127 	return 0;
    128 }
    129 
    130 static const struct backlight_ops nv50_bl_ops = {
    131 	.options = BL_CORE_SUSPENDRESUME,
    132 	.get_brightness = nv50_get_intensity,
    133 	.update_status = nv50_set_intensity,
    134 };
    135 
    136 static int
    137 nva3_get_intensity(struct backlight_device *bd)
    138 {
    139 	struct nouveau_encoder *nv_encoder = bl_get_data(bd);
    140 	struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
    141 	struct nvif_object *device = &drm->device.object;
    142 	int or = nv_encoder->or;
    143 	u32 div, val;
    144 
    145 	div  = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or));
    146 	val  = nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(or));
    147 	val &= NVA3_PDISP_SOR_PWM_CTL_VAL;
    148 	if (div && div >= val)
    149 		return ((val * 100) + (div / 2)) / div;
    150 
    151 	return 100;
    152 }
    153 
    154 static int
    155 nva3_set_intensity(struct backlight_device *bd)
    156 {
    157 	struct nouveau_encoder *nv_encoder = bl_get_data(bd);
    158 	struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
    159 	struct nvif_object *device = &drm->device.object;
    160 	int or = nv_encoder->or;
    161 	u32 div, val;
    162 
    163 	div = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or));
    164 	val = (bd->props.brightness * div) / 100;
    165 	if (div) {
    166 		nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or), val |
    167 				NV50_PDISP_SOR_PWM_CTL_NEW |
    168 				NVA3_PDISP_SOR_PWM_CTL_UNK);
    169 		return 0;
    170 	}
    171 
    172 	return -EINVAL;
    173 }
    174 
    175 static const struct backlight_ops nva3_bl_ops = {
    176 	.options = BL_CORE_SUSPENDRESUME,
    177 	.get_brightness = nva3_get_intensity,
    178 	.update_status = nva3_set_intensity,
    179 };
    180 
    181 static int
    182 nv50_backlight_init(struct drm_connector *connector)
    183 {
    184 	struct nouveau_drm *drm = nouveau_drm(connector->dev);
    185 	struct nvif_object *device = &drm->device.object;
    186 	struct nouveau_encoder *nv_encoder;
    187 	struct backlight_properties props;
    188 	struct backlight_device *bd;
    189 	const struct backlight_ops *ops;
    190 
    191 	nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS);
    192 	if (!nv_encoder) {
    193 		nv_encoder = find_encoder(connector, DCB_OUTPUT_DP);
    194 		if (!nv_encoder)
    195 			return -ENODEV;
    196 	}
    197 
    198 	if (!nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(nv_encoder->or)))
    199 		return 0;
    200 
    201 	if (drm->device.info.chipset <= 0xa0 ||
    202 	    drm->device.info.chipset == 0xaa ||
    203 	    drm->device.info.chipset == 0xac)
    204 		ops = &nv50_bl_ops;
    205 	else
    206 		ops = &nva3_bl_ops;
    207 
    208 	memset(&props, 0, sizeof(struct backlight_properties));
    209 	props.type = BACKLIGHT_RAW;
    210 	props.max_brightness = 100;
    211 	bd = backlight_device_register("nv_backlight", connector->kdev,
    212 				       nv_encoder, ops, &props);
    213 	if (IS_ERR(bd))
    214 		return PTR_ERR(bd);
    215 
    216 	drm->backlight = bd;
    217 	bd->props.brightness = bd->ops->get_brightness(bd);
    218 	backlight_update_status(bd);
    219 	return 0;
    220 }
    221 
    222 int
    223 nouveau_backlight_init(struct drm_device *dev)
    224 {
    225 	struct nouveau_drm *drm = nouveau_drm(dev);
    226 	struct nvif_device *device = &drm->device;
    227 	struct drm_connector *connector;
    228 
    229 	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
    230 		if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS &&
    231 		    connector->connector_type != DRM_MODE_CONNECTOR_eDP)
    232 			continue;
    233 
    234 		switch (device->info.family) {
    235 		case NV_DEVICE_INFO_V0_CURIE:
    236 			return nv40_backlight_init(connector);
    237 		case NV_DEVICE_INFO_V0_TESLA:
    238 		case NV_DEVICE_INFO_V0_FERMI:
    239 		case NV_DEVICE_INFO_V0_KEPLER:
    240 			return nv50_backlight_init(connector);
    241 		default:
    242 			break;
    243 		}
    244 	}
    245 
    246 
    247 	return 0;
    248 }
    249 
    250 void
    251 nouveau_backlight_exit(struct drm_device *dev)
    252 {
    253 	struct nouveau_drm *drm = nouveau_drm(dev);
    254 
    255 	if (drm->backlight) {
    256 		backlight_device_unregister(drm->backlight);
    257 		drm->backlight = NULL;
    258 	}
    259 }
    260