2 * Copyright (C) 2009 Red Hat <mjg@redhat.com>
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:
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.
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.
28 * Matthew Garrett <mjg@redhat.com>
30 * Register locations derived from NVClock by Roderick Colenbrander
33 #include <linux/apple-gmux.h>
34 #include <linux/backlight.h>
35 #include <linux/idr.h>
37 #include "nouveau_drv.h"
38 #include "nouveau_reg.h"
39 #include "nouveau_encoder.h"
40 #include "nouveau_connector.h"
42 static struct ida bl_ida
;
43 #define BL_NAME_SIZE 15 // 12 for name + 2 for digits + 1 for '\0'
45 struct nouveau_backlight
{
46 struct backlight_device
*dev
;
51 nouveau_get_backlight_name(char backlight_name
[BL_NAME_SIZE
],
52 struct nouveau_backlight
*bl
)
54 const int nb
= ida_simple_get(&bl_ida
, 0, 0, GFP_KERNEL
);
55 if (nb
< 0 || nb
>= 100)
58 snprintf(backlight_name
, BL_NAME_SIZE
, "nv_backlight%d", nb
);
60 snprintf(backlight_name
, BL_NAME_SIZE
, "nv_backlight");
66 nv40_get_intensity(struct backlight_device
*bd
)
68 struct nouveau_encoder
*nv_encoder
= bl_get_data(bd
);
69 struct nouveau_drm
*drm
= nouveau_drm(nv_encoder
->base
.base
.dev
);
70 struct nvif_object
*device
= &drm
->client
.device
.object
;
71 int val
= (nvif_rd32(device
, NV40_PMC_BACKLIGHT
) &
72 NV40_PMC_BACKLIGHT_MASK
) >> 16;
78 nv40_set_intensity(struct backlight_device
*bd
)
80 struct nouveau_encoder
*nv_encoder
= bl_get_data(bd
);
81 struct nouveau_drm
*drm
= nouveau_drm(nv_encoder
->base
.base
.dev
);
82 struct nvif_object
*device
= &drm
->client
.device
.object
;
83 int val
= bd
->props
.brightness
;
84 int reg
= nvif_rd32(device
, NV40_PMC_BACKLIGHT
);
86 nvif_wr32(device
, NV40_PMC_BACKLIGHT
,
87 (val
<< 16) | (reg
& ~NV40_PMC_BACKLIGHT_MASK
));
92 static const struct backlight_ops nv40_bl_ops
= {
93 .options
= BL_CORE_SUSPENDRESUME
,
94 .get_brightness
= nv40_get_intensity
,
95 .update_status
= nv40_set_intensity
,
99 nv40_backlight_init(struct nouveau_encoder
*encoder
,
100 struct backlight_properties
*props
,
101 const struct backlight_ops
**ops
)
103 struct nouveau_drm
*drm
= nouveau_drm(encoder
->base
.base
.dev
);
104 struct nvif_object
*device
= &drm
->client
.device
.object
;
106 if (!(nvif_rd32(device
, NV40_PMC_BACKLIGHT
) & NV40_PMC_BACKLIGHT_MASK
))
109 props
->type
= BACKLIGHT_RAW
;
110 props
->max_brightness
= 31;
116 nv50_get_intensity(struct backlight_device
*bd
)
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
->client
.device
.object
;
121 int or = ffs(nv_encoder
->dcb
->or) - 1;
125 val
= nvif_rd32(device
, NV50_PDISP_SOR_PWM_CTL(or));
126 val
&= NV50_PDISP_SOR_PWM_CTL_VAL
;
127 return ((val
* 100) + (div
/ 2)) / div
;
131 nv50_set_intensity(struct backlight_device
*bd
)
133 struct nouveau_encoder
*nv_encoder
= bl_get_data(bd
);
134 struct nouveau_drm
*drm
= nouveau_drm(nv_encoder
->base
.base
.dev
);
135 struct nvif_object
*device
= &drm
->client
.device
.object
;
136 int or = ffs(nv_encoder
->dcb
->or) - 1;
138 u32 val
= (bd
->props
.brightness
* div
) / 100;
140 nvif_wr32(device
, NV50_PDISP_SOR_PWM_CTL(or),
141 NV50_PDISP_SOR_PWM_CTL_NEW
| val
);
145 static const struct backlight_ops nv50_bl_ops
= {
146 .options
= BL_CORE_SUSPENDRESUME
,
147 .get_brightness
= nv50_get_intensity
,
148 .update_status
= nv50_set_intensity
,
152 nva3_get_intensity(struct backlight_device
*bd
)
154 struct nouveau_encoder
*nv_encoder
= bl_get_data(bd
);
155 struct nouveau_drm
*drm
= nouveau_drm(nv_encoder
->base
.base
.dev
);
156 struct nvif_object
*device
= &drm
->client
.device
.object
;
157 int or = ffs(nv_encoder
->dcb
->or) - 1;
160 div
= nvif_rd32(device
, NV50_PDISP_SOR_PWM_DIV(or));
161 val
= nvif_rd32(device
, NV50_PDISP_SOR_PWM_CTL(or));
162 val
&= NVA3_PDISP_SOR_PWM_CTL_VAL
;
163 if (div
&& div
>= val
)
164 return ((val
* 100) + (div
/ 2)) / div
;
170 nva3_set_intensity(struct backlight_device
*bd
)
172 struct nouveau_encoder
*nv_encoder
= bl_get_data(bd
);
173 struct nouveau_drm
*drm
= nouveau_drm(nv_encoder
->base
.base
.dev
);
174 struct nvif_object
*device
= &drm
->client
.device
.object
;
175 int or = ffs(nv_encoder
->dcb
->or) - 1;
178 div
= nvif_rd32(device
, NV50_PDISP_SOR_PWM_DIV(or));
179 val
= (bd
->props
.brightness
* div
) / 100;
181 nvif_wr32(device
, NV50_PDISP_SOR_PWM_CTL(or),
183 NV50_PDISP_SOR_PWM_CTL_NEW
|
184 NVA3_PDISP_SOR_PWM_CTL_UNK
);
191 static const struct backlight_ops nva3_bl_ops
= {
192 .options
= BL_CORE_SUSPENDRESUME
,
193 .get_brightness
= nva3_get_intensity
,
194 .update_status
= nva3_set_intensity
,
198 nv50_backlight_init(struct nouveau_encoder
*nv_encoder
,
199 struct backlight_properties
*props
,
200 const struct backlight_ops
**ops
)
202 struct nouveau_drm
*drm
= nouveau_drm(nv_encoder
->base
.base
.dev
);
203 struct nvif_object
*device
= &drm
->client
.device
.object
;
205 if (!nvif_rd32(device
, NV50_PDISP_SOR_PWM_CTL(ffs(nv_encoder
->dcb
->or) - 1)))
208 if (drm
->client
.device
.info
.chipset
<= 0xa0 ||
209 drm
->client
.device
.info
.chipset
== 0xaa ||
210 drm
->client
.device
.info
.chipset
== 0xac)
215 props
->type
= BACKLIGHT_RAW
;
216 props
->max_brightness
= 100;
222 nouveau_backlight_init(struct drm_connector
*connector
)
224 struct nouveau_drm
*drm
= nouveau_drm(connector
->dev
);
225 struct nouveau_backlight
*bl
;
226 struct nouveau_encoder
*nv_encoder
= NULL
;
227 struct nvif_device
*device
= &drm
->client
.device
;
228 char backlight_name
[BL_NAME_SIZE
];
229 struct backlight_properties props
= {0};
230 const struct backlight_ops
*ops
;
233 if (apple_gmux_present()) {
234 NV_INFO_ONCE(drm
, "Apple GMUX detected: not registering Nouveau backlight interface\n");
238 if (connector
->connector_type
== DRM_MODE_CONNECTOR_LVDS
)
239 nv_encoder
= find_encoder(connector
, DCB_OUTPUT_LVDS
);
240 else if (connector
->connector_type
== DRM_MODE_CONNECTOR_eDP
)
241 nv_encoder
= find_encoder(connector
, DCB_OUTPUT_DP
);
248 switch (device
->info
.family
) {
249 case NV_DEVICE_INFO_V0_CURIE
:
250 ret
= nv40_backlight_init(nv_encoder
, &props
, &ops
);
252 case NV_DEVICE_INFO_V0_TESLA
:
253 case NV_DEVICE_INFO_V0_FERMI
:
254 case NV_DEVICE_INFO_V0_KEPLER
:
255 case NV_DEVICE_INFO_V0_MAXWELL
:
256 case NV_DEVICE_INFO_V0_PASCAL
:
257 case NV_DEVICE_INFO_V0_VOLTA
:
258 case NV_DEVICE_INFO_V0_TURING
:
259 ret
= nv50_backlight_init(nv_encoder
, &props
, &ops
);
270 bl
= kzalloc(sizeof(*bl
), GFP_KERNEL
);
274 if (!nouveau_get_backlight_name(backlight_name
, bl
)) {
275 NV_ERROR(drm
, "Failed to retrieve a unique name for the backlight interface\n");
279 bl
->dev
= backlight_device_register(backlight_name
, connector
->kdev
,
280 nv_encoder
, ops
, &props
);
281 if (IS_ERR(bl
->dev
)) {
283 ida_simple_remove(&bl_ida
, bl
->id
);
284 ret
= PTR_ERR(bl
->dev
);
288 nouveau_connector(connector
)->backlight
= bl
;
289 bl
->dev
->props
.brightness
= bl
->dev
->ops
->get_brightness(bl
->dev
);
290 backlight_update_status(bl
->dev
);
300 nouveau_backlight_fini(struct drm_connector
*connector
)
302 struct nouveau_connector
*nv_conn
= nouveau_connector(connector
);
303 struct nouveau_backlight
*bl
= nv_conn
->backlight
;
309 ida_simple_remove(&bl_ida
, bl
->id
);
311 backlight_device_unregister(bl
->dev
);
312 nv_conn
->backlight
= NULL
;
317 nouveau_backlight_ctor(void)
323 nouveau_backlight_dtor(void)
325 ida_destroy(&bl_ida
);