1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Copyright (C) 2009 Red Hat <mjg@redhat.com>
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Permission is hereby granted, free of charge, to any person obtaining
5*4882a593Smuzhiyun * a copy of this software and associated documentation files (the
6*4882a593Smuzhiyun * "Software"), to deal in the Software without restriction, including
7*4882a593Smuzhiyun * without limitation the rights to use, copy, modify, merge, publish,
8*4882a593Smuzhiyun * distribute, sublicense, and/or sell copies of the Software, and to
9*4882a593Smuzhiyun * permit persons to whom the Software is furnished to do so, subject to
10*4882a593Smuzhiyun * the following conditions:
11*4882a593Smuzhiyun *
12*4882a593Smuzhiyun * The above copyright notice and this permission notice (including the
13*4882a593Smuzhiyun * next paragraph) shall be included in all copies or substantial
14*4882a593Smuzhiyun * portions of the Software.
15*4882a593Smuzhiyun *
16*4882a593Smuzhiyun * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17*4882a593Smuzhiyun * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18*4882a593Smuzhiyun * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19*4882a593Smuzhiyun * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
20*4882a593Smuzhiyun * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21*4882a593Smuzhiyun * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22*4882a593Smuzhiyun * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23*4882a593Smuzhiyun *
24*4882a593Smuzhiyun */
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun /*
27*4882a593Smuzhiyun * Authors:
28*4882a593Smuzhiyun * Matthew Garrett <mjg@redhat.com>
29*4882a593Smuzhiyun *
30*4882a593Smuzhiyun * Register locations derived from NVClock by Roderick Colenbrander
31*4882a593Smuzhiyun */
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun #include <linux/apple-gmux.h>
34*4882a593Smuzhiyun #include <linux/backlight.h>
35*4882a593Smuzhiyun #include <linux/idr.h>
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun #include "nouveau_drv.h"
38*4882a593Smuzhiyun #include "nouveau_reg.h"
39*4882a593Smuzhiyun #include "nouveau_encoder.h"
40*4882a593Smuzhiyun #include "nouveau_connector.h"
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun static struct ida bl_ida;
43*4882a593Smuzhiyun #define BL_NAME_SIZE 15 // 12 for name + 2 for digits + 1 for '\0'
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun struct nouveau_backlight {
46*4882a593Smuzhiyun struct backlight_device *dev;
47*4882a593Smuzhiyun int id;
48*4882a593Smuzhiyun };
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun static bool
nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE],struct nouveau_backlight * bl)51*4882a593Smuzhiyun nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE],
52*4882a593Smuzhiyun struct nouveau_backlight *bl)
53*4882a593Smuzhiyun {
54*4882a593Smuzhiyun const int nb = ida_alloc_max(&bl_ida, 99, GFP_KERNEL);
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun if (nb < 0)
57*4882a593Smuzhiyun return false;
58*4882a593Smuzhiyun if (nb > 0)
59*4882a593Smuzhiyun snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight%d", nb);
60*4882a593Smuzhiyun else
61*4882a593Smuzhiyun snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight");
62*4882a593Smuzhiyun bl->id = nb;
63*4882a593Smuzhiyun return true;
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun static int
nv40_get_intensity(struct backlight_device * bd)67*4882a593Smuzhiyun nv40_get_intensity(struct backlight_device *bd)
68*4882a593Smuzhiyun {
69*4882a593Smuzhiyun struct nouveau_encoder *nv_encoder = bl_get_data(bd);
70*4882a593Smuzhiyun struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
71*4882a593Smuzhiyun struct nvif_object *device = &drm->client.device.object;
72*4882a593Smuzhiyun int val = (nvif_rd32(device, NV40_PMC_BACKLIGHT) &
73*4882a593Smuzhiyun NV40_PMC_BACKLIGHT_MASK) >> 16;
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun return val;
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun static int
nv40_set_intensity(struct backlight_device * bd)79*4882a593Smuzhiyun nv40_set_intensity(struct backlight_device *bd)
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun struct nouveau_encoder *nv_encoder = bl_get_data(bd);
82*4882a593Smuzhiyun struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
83*4882a593Smuzhiyun struct nvif_object *device = &drm->client.device.object;
84*4882a593Smuzhiyun int val = bd->props.brightness;
85*4882a593Smuzhiyun int reg = nvif_rd32(device, NV40_PMC_BACKLIGHT);
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun nvif_wr32(device, NV40_PMC_BACKLIGHT,
88*4882a593Smuzhiyun (val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK));
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun return 0;
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun static const struct backlight_ops nv40_bl_ops = {
94*4882a593Smuzhiyun .options = BL_CORE_SUSPENDRESUME,
95*4882a593Smuzhiyun .get_brightness = nv40_get_intensity,
96*4882a593Smuzhiyun .update_status = nv40_set_intensity,
97*4882a593Smuzhiyun };
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun static int
nv40_backlight_init(struct nouveau_encoder * encoder,struct backlight_properties * props,const struct backlight_ops ** ops)100*4882a593Smuzhiyun nv40_backlight_init(struct nouveau_encoder *encoder,
101*4882a593Smuzhiyun struct backlight_properties *props,
102*4882a593Smuzhiyun const struct backlight_ops **ops)
103*4882a593Smuzhiyun {
104*4882a593Smuzhiyun struct nouveau_drm *drm = nouveau_drm(encoder->base.base.dev);
105*4882a593Smuzhiyun struct nvif_object *device = &drm->client.device.object;
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun if (!(nvif_rd32(device, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK))
108*4882a593Smuzhiyun return -ENODEV;
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun props->type = BACKLIGHT_RAW;
111*4882a593Smuzhiyun props->max_brightness = 31;
112*4882a593Smuzhiyun *ops = &nv40_bl_ops;
113*4882a593Smuzhiyun return 0;
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun static int
nv50_get_intensity(struct backlight_device * bd)117*4882a593Smuzhiyun nv50_get_intensity(struct backlight_device *bd)
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun struct nouveau_encoder *nv_encoder = bl_get_data(bd);
120*4882a593Smuzhiyun struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
121*4882a593Smuzhiyun struct nvif_object *device = &drm->client.device.object;
122*4882a593Smuzhiyun int or = ffs(nv_encoder->dcb->or) - 1;
123*4882a593Smuzhiyun u32 div = 1025;
124*4882a593Smuzhiyun u32 val;
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun val = nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(or));
127*4882a593Smuzhiyun val &= NV50_PDISP_SOR_PWM_CTL_VAL;
128*4882a593Smuzhiyun return ((val * 100) + (div / 2)) / div;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun static int
nv50_set_intensity(struct backlight_device * bd)132*4882a593Smuzhiyun nv50_set_intensity(struct backlight_device *bd)
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun struct nouveau_encoder *nv_encoder = bl_get_data(bd);
135*4882a593Smuzhiyun struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
136*4882a593Smuzhiyun struct nvif_object *device = &drm->client.device.object;
137*4882a593Smuzhiyun int or = ffs(nv_encoder->dcb->or) - 1;
138*4882a593Smuzhiyun u32 div = 1025;
139*4882a593Smuzhiyun u32 val = (bd->props.brightness * div) / 100;
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or),
142*4882a593Smuzhiyun NV50_PDISP_SOR_PWM_CTL_NEW | val);
143*4882a593Smuzhiyun return 0;
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun static const struct backlight_ops nv50_bl_ops = {
147*4882a593Smuzhiyun .options = BL_CORE_SUSPENDRESUME,
148*4882a593Smuzhiyun .get_brightness = nv50_get_intensity,
149*4882a593Smuzhiyun .update_status = nv50_set_intensity,
150*4882a593Smuzhiyun };
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun static int
nva3_get_intensity(struct backlight_device * bd)153*4882a593Smuzhiyun nva3_get_intensity(struct backlight_device *bd)
154*4882a593Smuzhiyun {
155*4882a593Smuzhiyun struct nouveau_encoder *nv_encoder = bl_get_data(bd);
156*4882a593Smuzhiyun struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
157*4882a593Smuzhiyun struct nvif_object *device = &drm->client.device.object;
158*4882a593Smuzhiyun int or = ffs(nv_encoder->dcb->or) - 1;
159*4882a593Smuzhiyun u32 div, val;
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun div = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or));
162*4882a593Smuzhiyun val = nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(or));
163*4882a593Smuzhiyun val &= NVA3_PDISP_SOR_PWM_CTL_VAL;
164*4882a593Smuzhiyun if (div && div >= val)
165*4882a593Smuzhiyun return ((val * 100) + (div / 2)) / div;
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun return 100;
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun static int
nva3_set_intensity(struct backlight_device * bd)171*4882a593Smuzhiyun nva3_set_intensity(struct backlight_device *bd)
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun struct nouveau_encoder *nv_encoder = bl_get_data(bd);
174*4882a593Smuzhiyun struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
175*4882a593Smuzhiyun struct nvif_object *device = &drm->client.device.object;
176*4882a593Smuzhiyun int or = ffs(nv_encoder->dcb->or) - 1;
177*4882a593Smuzhiyun u32 div, val;
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun div = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or));
180*4882a593Smuzhiyun val = (bd->props.brightness * div) / 100;
181*4882a593Smuzhiyun if (div) {
182*4882a593Smuzhiyun nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or),
183*4882a593Smuzhiyun val |
184*4882a593Smuzhiyun NV50_PDISP_SOR_PWM_CTL_NEW |
185*4882a593Smuzhiyun NVA3_PDISP_SOR_PWM_CTL_UNK);
186*4882a593Smuzhiyun return 0;
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun return -EINVAL;
190*4882a593Smuzhiyun }
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun static const struct backlight_ops nva3_bl_ops = {
193*4882a593Smuzhiyun .options = BL_CORE_SUSPENDRESUME,
194*4882a593Smuzhiyun .get_brightness = nva3_get_intensity,
195*4882a593Smuzhiyun .update_status = nva3_set_intensity,
196*4882a593Smuzhiyun };
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun static int
nv50_backlight_init(struct nouveau_encoder * nv_encoder,struct backlight_properties * props,const struct backlight_ops ** ops)199*4882a593Smuzhiyun nv50_backlight_init(struct nouveau_encoder *nv_encoder,
200*4882a593Smuzhiyun struct backlight_properties *props,
201*4882a593Smuzhiyun const struct backlight_ops **ops)
202*4882a593Smuzhiyun {
203*4882a593Smuzhiyun struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
204*4882a593Smuzhiyun struct nvif_object *device = &drm->client.device.object;
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun if (!nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(ffs(nv_encoder->dcb->or) - 1)))
207*4882a593Smuzhiyun return -ENODEV;
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun if (drm->client.device.info.chipset <= 0xa0 ||
210*4882a593Smuzhiyun drm->client.device.info.chipset == 0xaa ||
211*4882a593Smuzhiyun drm->client.device.info.chipset == 0xac)
212*4882a593Smuzhiyun *ops = &nv50_bl_ops;
213*4882a593Smuzhiyun else
214*4882a593Smuzhiyun *ops = &nva3_bl_ops;
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun props->type = BACKLIGHT_RAW;
217*4882a593Smuzhiyun props->max_brightness = 100;
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun return 0;
220*4882a593Smuzhiyun }
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun int
nouveau_backlight_init(struct drm_connector * connector)223*4882a593Smuzhiyun nouveau_backlight_init(struct drm_connector *connector)
224*4882a593Smuzhiyun {
225*4882a593Smuzhiyun struct nouveau_drm *drm = nouveau_drm(connector->dev);
226*4882a593Smuzhiyun struct nouveau_backlight *bl;
227*4882a593Smuzhiyun struct nouveau_encoder *nv_encoder = NULL;
228*4882a593Smuzhiyun struct nvif_device *device = &drm->client.device;
229*4882a593Smuzhiyun char backlight_name[BL_NAME_SIZE];
230*4882a593Smuzhiyun struct backlight_properties props = {0};
231*4882a593Smuzhiyun const struct backlight_ops *ops;
232*4882a593Smuzhiyun int ret;
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun if (apple_gmux_present()) {
235*4882a593Smuzhiyun NV_INFO_ONCE(drm, "Apple GMUX detected: not registering Nouveau backlight interface\n");
236*4882a593Smuzhiyun return 0;
237*4882a593Smuzhiyun }
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
240*4882a593Smuzhiyun nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS);
241*4882a593Smuzhiyun else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP)
242*4882a593Smuzhiyun nv_encoder = find_encoder(connector, DCB_OUTPUT_DP);
243*4882a593Smuzhiyun else
244*4882a593Smuzhiyun return 0;
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun if (!nv_encoder)
247*4882a593Smuzhiyun return 0;
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun switch (device->info.family) {
250*4882a593Smuzhiyun case NV_DEVICE_INFO_V0_CURIE:
251*4882a593Smuzhiyun ret = nv40_backlight_init(nv_encoder, &props, &ops);
252*4882a593Smuzhiyun break;
253*4882a593Smuzhiyun case NV_DEVICE_INFO_V0_TESLA:
254*4882a593Smuzhiyun case NV_DEVICE_INFO_V0_FERMI:
255*4882a593Smuzhiyun case NV_DEVICE_INFO_V0_KEPLER:
256*4882a593Smuzhiyun case NV_DEVICE_INFO_V0_MAXWELL:
257*4882a593Smuzhiyun case NV_DEVICE_INFO_V0_PASCAL:
258*4882a593Smuzhiyun case NV_DEVICE_INFO_V0_VOLTA:
259*4882a593Smuzhiyun case NV_DEVICE_INFO_V0_TURING:
260*4882a593Smuzhiyun ret = nv50_backlight_init(nv_encoder, &props, &ops);
261*4882a593Smuzhiyun break;
262*4882a593Smuzhiyun default:
263*4882a593Smuzhiyun return 0;
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun if (ret == -ENODEV)
267*4882a593Smuzhiyun return 0;
268*4882a593Smuzhiyun else if (ret)
269*4882a593Smuzhiyun return ret;
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun bl = kzalloc(sizeof(*bl), GFP_KERNEL);
272*4882a593Smuzhiyun if (!bl)
273*4882a593Smuzhiyun return -ENOMEM;
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun if (!nouveau_get_backlight_name(backlight_name, bl)) {
276*4882a593Smuzhiyun NV_ERROR(drm, "Failed to retrieve a unique name for the backlight interface\n");
277*4882a593Smuzhiyun goto fail_alloc;
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun bl->dev = backlight_device_register(backlight_name, connector->kdev,
281*4882a593Smuzhiyun nv_encoder, ops, &props);
282*4882a593Smuzhiyun if (IS_ERR(bl->dev)) {
283*4882a593Smuzhiyun if (bl->id >= 0)
284*4882a593Smuzhiyun ida_free(&bl_ida, bl->id);
285*4882a593Smuzhiyun ret = PTR_ERR(bl->dev);
286*4882a593Smuzhiyun goto fail_alloc;
287*4882a593Smuzhiyun }
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun nouveau_connector(connector)->backlight = bl;
290*4882a593Smuzhiyun bl->dev->props.brightness = bl->dev->ops->get_brightness(bl->dev);
291*4882a593Smuzhiyun backlight_update_status(bl->dev);
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun return 0;
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun fail_alloc:
296*4882a593Smuzhiyun kfree(bl);
297*4882a593Smuzhiyun return ret;
298*4882a593Smuzhiyun }
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun void
nouveau_backlight_fini(struct drm_connector * connector)301*4882a593Smuzhiyun nouveau_backlight_fini(struct drm_connector *connector)
302*4882a593Smuzhiyun {
303*4882a593Smuzhiyun struct nouveau_connector *nv_conn = nouveau_connector(connector);
304*4882a593Smuzhiyun struct nouveau_backlight *bl = nv_conn->backlight;
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun if (!bl)
307*4882a593Smuzhiyun return;
308*4882a593Smuzhiyun
309*4882a593Smuzhiyun if (bl->id >= 0)
310*4882a593Smuzhiyun ida_free(&bl_ida, bl->id);
311*4882a593Smuzhiyun
312*4882a593Smuzhiyun backlight_device_unregister(bl->dev);
313*4882a593Smuzhiyun nv_conn->backlight = NULL;
314*4882a593Smuzhiyun kfree(bl);
315*4882a593Smuzhiyun }
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun void
nouveau_backlight_ctor(void)318*4882a593Smuzhiyun nouveau_backlight_ctor(void)
319*4882a593Smuzhiyun {
320*4882a593Smuzhiyun ida_init(&bl_ida);
321*4882a593Smuzhiyun }
322*4882a593Smuzhiyun
323*4882a593Smuzhiyun void
nouveau_backlight_dtor(void)324*4882a593Smuzhiyun nouveau_backlight_dtor(void)
325*4882a593Smuzhiyun {
326*4882a593Smuzhiyun ida_destroy(&bl_ida);
327*4882a593Smuzhiyun }
328