Merge branches 'clk-ingenic', 'clk-mtk-mux', 'clk-qcom-sdm845-pcie', 'clk-mtk-crit...
[linux-2.6-microblaze.git] / drivers / gpu / drm / nouveau / nouveau_backlight.c
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/apple-gmux.h>
34 #include <linux/backlight.h>
35 #include <linux/idr.h>
36
37 #include "nouveau_drv.h"
38 #include "nouveau_reg.h"
39 #include "nouveau_encoder.h"
40 #include "nouveau_connector.h"
41
42 static struct ida bl_ida;
43 #define BL_NAME_SIZE 15 // 12 for name + 2 for digits + 1 for '\0'
44
45 struct nouveau_backlight {
46         struct backlight_device *dev;
47         int id;
48 };
49
50 static bool
51 nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE],
52                            struct nouveau_backlight *bl)
53 {
54         const int nb = ida_simple_get(&bl_ida, 0, 0, GFP_KERNEL);
55         if (nb < 0 || nb >= 100)
56                 return false;
57         if (nb > 0)
58                 snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight%d", nb);
59         else
60                 snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight");
61         bl->id = nb;
62         return true;
63 }
64
65 static int
66 nv40_get_intensity(struct backlight_device *bd)
67 {
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;
73
74         return val;
75 }
76
77 static int
78 nv40_set_intensity(struct backlight_device *bd)
79 {
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);
85
86         nvif_wr32(device, NV40_PMC_BACKLIGHT,
87                   (val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK));
88
89         return 0;
90 }
91
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,
96 };
97
98 static int
99 nv40_backlight_init(struct nouveau_encoder *encoder,
100                     struct backlight_properties *props,
101                     const struct backlight_ops **ops)
102 {
103         struct nouveau_drm *drm = nouveau_drm(encoder->base.base.dev);
104         struct nvif_object *device = &drm->client.device.object;
105
106         if (!(nvif_rd32(device, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK))
107                 return -ENODEV;
108
109         props->type = BACKLIGHT_RAW;
110         props->max_brightness = 31;
111         *ops = &nv40_bl_ops;
112         return 0;
113 }
114
115 static int
116 nv50_get_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->client.device.object;
121         int or = ffs(nv_encoder->dcb->or) - 1;
122         u32 div = 1025;
123         u32 val;
124
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;
128 }
129
130 static int
131 nv50_set_intensity(struct backlight_device *bd)
132 {
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;
137         u32 div = 1025;
138         u32 val = (bd->props.brightness * div) / 100;
139
140         nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or),
141                   NV50_PDISP_SOR_PWM_CTL_NEW | val);
142         return 0;
143 }
144
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,
149 };
150
151 static int
152 nva3_get_intensity(struct backlight_device *bd)
153 {
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;
158         u32 div, val;
159
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;
165
166         return 100;
167 }
168
169 static int
170 nva3_set_intensity(struct backlight_device *bd)
171 {
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;
176         u32 div, val;
177
178         div = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or));
179         val = (bd->props.brightness * div) / 100;
180         if (div) {
181                 nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or),
182                           val |
183                           NV50_PDISP_SOR_PWM_CTL_NEW |
184                           NVA3_PDISP_SOR_PWM_CTL_UNK);
185                 return 0;
186         }
187
188         return -EINVAL;
189 }
190
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,
195 };
196
197 static int
198 nv50_backlight_init(struct nouveau_encoder *nv_encoder,
199                     struct backlight_properties *props,
200                     const struct backlight_ops **ops)
201 {
202         struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
203         struct nvif_object *device = &drm->client.device.object;
204
205         if (!nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(ffs(nv_encoder->dcb->or) - 1)))
206                 return -ENODEV;
207
208         if (drm->client.device.info.chipset <= 0xa0 ||
209             drm->client.device.info.chipset == 0xaa ||
210             drm->client.device.info.chipset == 0xac)
211                 *ops = &nv50_bl_ops;
212         else
213                 *ops = &nva3_bl_ops;
214
215         props->type = BACKLIGHT_RAW;
216         props->max_brightness = 100;
217
218         return 0;
219 }
220
221 int
222 nouveau_backlight_init(struct drm_connector *connector)
223 {
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;
231         int ret;
232
233         if (apple_gmux_present()) {
234                 NV_INFO_ONCE(drm, "Apple GMUX detected: not registering Nouveau backlight interface\n");
235                 return 0;
236         }
237
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);
242         else
243                 return 0;
244
245         if (!nv_encoder)
246                 return 0;
247
248         switch (device->info.family) {
249         case NV_DEVICE_INFO_V0_CURIE:
250                 ret = nv40_backlight_init(nv_encoder, &props, &ops);
251                 break;
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);
260                 break;
261         default:
262                 return 0;
263         }
264
265         if (ret == -ENODEV)
266                 return 0;
267         else if (ret)
268                 return ret;
269
270         bl = kzalloc(sizeof(*bl), GFP_KERNEL);
271         if (!bl)
272                 return -ENOMEM;
273
274         if (!nouveau_get_backlight_name(backlight_name, bl)) {
275                 NV_ERROR(drm, "Failed to retrieve a unique name for the backlight interface\n");
276                 goto fail_alloc;
277         }
278
279         bl->dev = backlight_device_register(backlight_name, connector->kdev,
280                                             nv_encoder, ops, &props);
281         if (IS_ERR(bl->dev)) {
282                 if (bl->id >= 0)
283                         ida_simple_remove(&bl_ida, bl->id);
284                 ret = PTR_ERR(bl->dev);
285                 goto fail_alloc;
286         }
287
288         nouveau_connector(connector)->backlight = bl;
289         bl->dev->props.brightness = bl->dev->ops->get_brightness(bl->dev);
290         backlight_update_status(bl->dev);
291
292         return 0;
293
294 fail_alloc:
295         kfree(bl);
296         return ret;
297 }
298
299 void
300 nouveau_backlight_fini(struct drm_connector *connector)
301 {
302         struct nouveau_connector *nv_conn = nouveau_connector(connector);
303         struct nouveau_backlight *bl = nv_conn->backlight;
304
305         if (!bl)
306                 return;
307
308         if (bl->id >= 0)
309                 ida_simple_remove(&bl_ida, bl->id);
310
311         backlight_device_unregister(bl->dev);
312         nv_conn->backlight = NULL;
313         kfree(bl);
314 }
315
316 void
317 nouveau_backlight_ctor(void)
318 {
319         ida_init(&bl_ida);
320 }
321
322 void
323 nouveau_backlight_dtor(void)
324 {
325         ida_destroy(&bl_ida);
326 }