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"
41 static struct ida bl_ida
;
42 #define BL_NAME_SIZE 15 // 12 for name + 2 for digits + 1 for '\0'
44 struct backlight_connector
{
45 struct list_head head
;
50 nouveau_get_backlight_name(char backlight_name
[BL_NAME_SIZE
], struct backlight_connector
53 const int nb
= ida_simple_get(&bl_ida
, 0, 0, GFP_KERNEL
);
54 if (nb
< 0 || nb
>= 100)
57 snprintf(backlight_name
, BL_NAME_SIZE
, "nv_backlight%d", nb
);
59 snprintf(backlight_name
, BL_NAME_SIZE
, "nv_backlight");
65 nv40_get_intensity(struct backlight_device
*bd
)
67 struct nouveau_drm
*drm
= bl_get_data(bd
);
68 struct nvif_object
*device
= &drm
->client
.device
.object
;
69 int val
= (nvif_rd32(device
, NV40_PMC_BACKLIGHT
) &
70 NV40_PMC_BACKLIGHT_MASK
) >> 16;
76 nv40_set_intensity(struct backlight_device
*bd
)
78 struct nouveau_drm
*drm
= bl_get_data(bd
);
79 struct nvif_object
*device
= &drm
->client
.device
.object
;
80 int val
= bd
->props
.brightness
;
81 int reg
= nvif_rd32(device
, NV40_PMC_BACKLIGHT
);
83 nvif_wr32(device
, NV40_PMC_BACKLIGHT
,
84 (val
<< 16) | (reg
& ~NV40_PMC_BACKLIGHT_MASK
));
89 static const struct backlight_ops nv40_bl_ops
= {
90 .options
= BL_CORE_SUSPENDRESUME
,
91 .get_brightness
= nv40_get_intensity
,
92 .update_status
= nv40_set_intensity
,
96 nv40_backlight_init(struct drm_connector
*connector
)
98 struct nouveau_drm
*drm
= nouveau_drm(connector
->dev
);
99 struct nvif_object
*device
= &drm
->client
.device
.object
;
100 struct backlight_properties props
;
101 struct backlight_device
*bd
;
102 struct backlight_connector bl_connector
;
103 char backlight_name
[BL_NAME_SIZE
];
105 if (!(nvif_rd32(device
, NV40_PMC_BACKLIGHT
) & NV40_PMC_BACKLIGHT_MASK
))
108 memset(&props
, 0, sizeof(struct backlight_properties
));
109 props
.type
= BACKLIGHT_RAW
;
110 props
.max_brightness
= 31;
111 if (!nouveau_get_backlight_name(backlight_name
, &bl_connector
)) {
112 NV_ERROR(drm
, "Failed to retrieve a unique name for the backlight interface\n");
115 bd
= backlight_device_register(backlight_name
, connector
->kdev
, drm
,
116 &nv40_bl_ops
, &props
);
119 if (bl_connector
.id
>= 0)
120 ida_simple_remove(&bl_ida
, bl_connector
.id
);
123 list_add(&bl_connector
.head
, &drm
->bl_connectors
);
125 bd
->props
.brightness
= nv40_get_intensity(bd
);
126 backlight_update_status(bd
);
132 nv50_get_intensity(struct backlight_device
*bd
)
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
->client
.device
.object
;
137 int or = ffs(nv_encoder
->dcb
->or) - 1;
141 val
= nvif_rd32(device
, NV50_PDISP_SOR_PWM_CTL(or));
142 val
&= NV50_PDISP_SOR_PWM_CTL_VAL
;
143 return ((val
* 100) + (div
/ 2)) / div
;
147 nv50_set_intensity(struct backlight_device
*bd
)
149 struct nouveau_encoder
*nv_encoder
= bl_get_data(bd
);
150 struct nouveau_drm
*drm
= nouveau_drm(nv_encoder
->base
.base
.dev
);
151 struct nvif_object
*device
= &drm
->client
.device
.object
;
152 int or = ffs(nv_encoder
->dcb
->or) - 1;
154 u32 val
= (bd
->props
.brightness
* div
) / 100;
156 nvif_wr32(device
, NV50_PDISP_SOR_PWM_CTL(or),
157 NV50_PDISP_SOR_PWM_CTL_NEW
| val
);
161 static const struct backlight_ops nv50_bl_ops
= {
162 .options
= BL_CORE_SUSPENDRESUME
,
163 .get_brightness
= nv50_get_intensity
,
164 .update_status
= nv50_set_intensity
,
168 nva3_get_intensity(struct backlight_device
*bd
)
170 struct nouveau_encoder
*nv_encoder
= bl_get_data(bd
);
171 struct nouveau_drm
*drm
= nouveau_drm(nv_encoder
->base
.base
.dev
);
172 struct nvif_object
*device
= &drm
->client
.device
.object
;
173 int or = ffs(nv_encoder
->dcb
->or) - 1;
176 div
= nvif_rd32(device
, NV50_PDISP_SOR_PWM_DIV(or));
177 val
= nvif_rd32(device
, NV50_PDISP_SOR_PWM_CTL(or));
178 val
&= NVA3_PDISP_SOR_PWM_CTL_VAL
;
179 if (div
&& div
>= val
)
180 return ((val
* 100) + (div
/ 2)) / div
;
186 nva3_set_intensity(struct backlight_device
*bd
)
188 struct nouveau_encoder
*nv_encoder
= bl_get_data(bd
);
189 struct nouveau_drm
*drm
= nouveau_drm(nv_encoder
->base
.base
.dev
);
190 struct nvif_object
*device
= &drm
->client
.device
.object
;
191 int or = ffs(nv_encoder
->dcb
->or) - 1;
194 div
= nvif_rd32(device
, NV50_PDISP_SOR_PWM_DIV(or));
195 val
= (bd
->props
.brightness
* div
) / 100;
197 nvif_wr32(device
, NV50_PDISP_SOR_PWM_CTL(or), val
|
198 NV50_PDISP_SOR_PWM_CTL_NEW
|
199 NVA3_PDISP_SOR_PWM_CTL_UNK
);
206 static const struct backlight_ops nva3_bl_ops
= {
207 .options
= BL_CORE_SUSPENDRESUME
,
208 .get_brightness
= nva3_get_intensity
,
209 .update_status
= nva3_set_intensity
,
213 nv50_backlight_init(struct drm_connector
*connector
)
215 struct nouveau_drm
*drm
= nouveau_drm(connector
->dev
);
216 struct nvif_object
*device
= &drm
->client
.device
.object
;
217 struct nouveau_encoder
*nv_encoder
;
218 struct backlight_properties props
;
219 struct backlight_device
*bd
;
220 const struct backlight_ops
*ops
;
221 struct backlight_connector bl_connector
;
222 char backlight_name
[BL_NAME_SIZE
];
224 nv_encoder
= find_encoder(connector
, DCB_OUTPUT_LVDS
);
226 nv_encoder
= find_encoder(connector
, DCB_OUTPUT_DP
);
231 if (!nvif_rd32(device
, NV50_PDISP_SOR_PWM_CTL(ffs(nv_encoder
->dcb
->or) - 1)))
234 if (drm
->client
.device
.info
.chipset
<= 0xa0 ||
235 drm
->client
.device
.info
.chipset
== 0xaa ||
236 drm
->client
.device
.info
.chipset
== 0xac)
241 memset(&props
, 0, sizeof(struct backlight_properties
));
242 props
.type
= BACKLIGHT_RAW
;
243 props
.max_brightness
= 100;
244 if (!nouveau_get_backlight_name(backlight_name
, &bl_connector
)) {
245 NV_ERROR(drm
, "Failed to retrieve a unique name for the backlight interface\n");
248 bd
= backlight_device_register(backlight_name
, connector
->kdev
,
249 nv_encoder
, ops
, &props
);
252 if (bl_connector
.id
>= 0)
253 ida_simple_remove(&bl_ida
, bl_connector
.id
);
257 list_add(&bl_connector
.head
, &drm
->bl_connectors
);
259 bd
->props
.brightness
= bd
->ops
->get_brightness(bd
);
260 backlight_update_status(bd
);
265 nouveau_backlight_init(struct drm_device
*dev
)
267 struct nouveau_drm
*drm
= nouveau_drm(dev
);
268 struct nvif_device
*device
= &drm
->client
.device
;
269 struct drm_connector
*connector
;
270 struct drm_connector_list_iter conn_iter
;
272 INIT_LIST_HEAD(&drm
->bl_connectors
);
274 if (apple_gmux_present()) {
275 NV_INFO(drm
, "Apple GMUX detected: not registering Nouveau backlight interface\n");
279 drm_connector_list_iter_begin(dev
, &conn_iter
);
280 drm_for_each_connector_iter(connector
, &conn_iter
) {
281 if (connector
->connector_type
!= DRM_MODE_CONNECTOR_LVDS
&&
282 connector
->connector_type
!= DRM_MODE_CONNECTOR_eDP
)
285 switch (device
->info
.family
) {
286 case NV_DEVICE_INFO_V0_CURIE
:
287 return nv40_backlight_init(connector
);
288 case NV_DEVICE_INFO_V0_TESLA
:
289 case NV_DEVICE_INFO_V0_FERMI
:
290 case NV_DEVICE_INFO_V0_KEPLER
:
291 case NV_DEVICE_INFO_V0_MAXWELL
:
292 return nv50_backlight_init(connector
);
297 drm_connector_list_iter_end(&conn_iter
);
303 nouveau_backlight_exit(struct drm_device
*dev
)
305 struct nouveau_drm
*drm
= nouveau_drm(dev
);
306 struct backlight_connector
*connector
;
308 list_for_each_entry(connector
, &drm
->bl_connectors
, head
) {
309 if (connector
->id
>= 0)
310 ida_simple_remove(&bl_ida
, connector
->id
);
313 if (drm
->backlight
) {
314 backlight_device_unregister(drm
->backlight
);
315 drm
->backlight
= NULL
;
320 nouveau_backlight_ctor(void)
326 nouveau_backlight_dtor(void)
328 ida_destroy(&bl_ida
);