blob: 2bebfeeb4f1d4e774c372f2218225644db281599 [file] [log] [blame]
Michael Hanselmann5474c122006-06-25 05:47:08 -07001/*
2 * Backlight code for nVidia based graphic cards
3 *
4 * Copyright 2004 Antonino Daplas <adaplas@pol.net>
5 * Copyright (c) 2006 Michael Hanselmann <linux-kernel@hansmi.ch>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/backlight.h>
13#include <linux/fb.h>
14#include <linux/pci.h>
15#include "nv_local.h"
16#include "nv_type.h"
17#include "nv_proto.h"
18
19#ifdef CONFIG_PMAC_BACKLIGHT
20#include <asm/backlight.h>
21#include <asm/machdep.h>
22#endif
23
24/* We do not have any information about which values are allowed, thus
25 * we used safe values.
26 */
27#define MIN_LEVEL 0x158
28#define MAX_LEVEL 0x534
Michael Hanselmanne01af032006-07-10 04:44:45 -070029#define LEVEL_STEP ((MAX_LEVEL - MIN_LEVEL) / FB_BACKLIGHT_MAX)
Michael Hanselmann5474c122006-06-25 05:47:08 -070030
31static struct backlight_properties nvidia_bl_data;
32
33static int nvidia_bl_get_level_brightness(struct nvidia_par *par,
34 int level)
35{
36 struct fb_info *info = pci_get_drvdata(par->pci_dev);
37 int nlevel;
38
39 /* Get and convert the value */
Richard Purdie37ce69a2007-02-10 14:10:33 +000040 /* No locking of bl_curve since we read a single value */
Michael Hanselmanne01af032006-07-10 04:44:45 -070041 nlevel = MIN_LEVEL + info->bl_curve[level] * LEVEL_STEP;
Michael Hanselmann5474c122006-06-25 05:47:08 -070042
43 if (nlevel < 0)
44 nlevel = 0;
45 else if (nlevel < MIN_LEVEL)
46 nlevel = MIN_LEVEL;
47 else if (nlevel > MAX_LEVEL)
48 nlevel = MAX_LEVEL;
49
50 return nlevel;
51}
52
Richard Purdie37ce69a2007-02-10 14:10:33 +000053static int nvidia_bl_update_status(struct backlight_device *bd)
Michael Hanselmann5474c122006-06-25 05:47:08 -070054{
55 struct nvidia_par *par = class_get_devdata(&bd->class_dev);
56 u32 tmp_pcrt, tmp_pmc, fpcontrol;
57 int level;
58
59 if (!par->FlatPanel)
60 return 0;
61
62 if (bd->props->power != FB_BLANK_UNBLANK ||
63 bd->props->fb_blank != FB_BLANK_UNBLANK)
64 level = 0;
65 else
66 level = bd->props->brightness;
67
68 tmp_pmc = NV_RD32(par->PMC, 0x10F0) & 0x0000FFFF;
69 tmp_pcrt = NV_RD32(par->PCRTC0, 0x081C) & 0xFFFFFFFC;
70 fpcontrol = NV_RD32(par->PRAMDAC, 0x0848) & 0xCFFFFFCC;
71
72 if (level > 0) {
73 tmp_pcrt |= 0x1;
74 tmp_pmc |= (1 << 31); /* backlight bit */
75 tmp_pmc |= nvidia_bl_get_level_brightness(par, level) << 16;
76 fpcontrol |= par->fpSyncs;
77 } else
78 fpcontrol |= 0x20000022;
79
80 NV_WR32(par->PCRTC0, 0x081C, tmp_pcrt);
81 NV_WR32(par->PMC, 0x10F0, tmp_pmc);
82 NV_WR32(par->PRAMDAC, 0x848, fpcontrol);
83
84 return 0;
85}
86
87static int nvidia_bl_get_brightness(struct backlight_device *bd)
88{
89 return bd->props->brightness;
90}
91
92static struct backlight_properties nvidia_bl_data = {
Michael Hanselmann5474c122006-06-25 05:47:08 -070093 .get_brightness = nvidia_bl_get_brightness,
94 .update_status = nvidia_bl_update_status,
95 .max_brightness = (FB_BACKLIGHT_LEVELS - 1),
96};
97
Michael Hanselmann5474c122006-06-25 05:47:08 -070098void nvidia_bl_init(struct nvidia_par *par)
99{
100 struct fb_info *info = pci_get_drvdata(par->pci_dev);
101 struct backlight_device *bd;
102 char name[12];
103
104 if (!par->FlatPanel)
105 return;
106
107#ifdef CONFIG_PMAC_BACKLIGHT
108 if (!machine_is(powermac) ||
109 !pmac_has_backlight_type("mnca"))
110 return;
111#endif
112
113 snprintf(name, sizeof(name), "nvidiabl%d", info->node);
114
James Simmonsa8274d52006-12-19 12:56:16 -0800115 bd = backlight_device_register(name, info->dev, par, &nvidia_bl_data);
Michael Hanselmann5474c122006-06-25 05:47:08 -0700116 if (IS_ERR(bd)) {
117 info->bl_dev = NULL;
Benjamin Herrenschmidt98a3c782006-08-31 14:04:34 +1000118 printk(KERN_WARNING "nvidia: Backlight registration failed\n");
Michael Hanselmann5474c122006-06-25 05:47:08 -0700119 goto error;
120 }
121
Michael Hanselmann5474c122006-06-25 05:47:08 -0700122 info->bl_dev = bd;
123 fb_bl_default_curve(info, 0,
124 0x158 * FB_BACKLIGHT_MAX / MAX_LEVEL,
125 0x534 * FB_BACKLIGHT_MAX / MAX_LEVEL);
Michael Hanselmann5474c122006-06-25 05:47:08 -0700126
Michael Hanselmann5474c122006-06-25 05:47:08 -0700127 bd->props->brightness = nvidia_bl_data.max_brightness;
128 bd->props->power = FB_BLANK_UNBLANK;
Richard Purdie28ee0862007-02-08 22:25:09 +0000129 backlight_update_status(bd);
Michael Hanselmann5474c122006-06-25 05:47:08 -0700130
131#ifdef CONFIG_PMAC_BACKLIGHT
132 mutex_lock(&pmac_backlight_mutex);
133 if (!pmac_backlight)
134 pmac_backlight = bd;
135 mutex_unlock(&pmac_backlight_mutex);
136#endif
137
138 printk("nvidia: Backlight initialized (%s)\n", name);
139
140 return;
141
142error:
143 return;
144}
145
146void nvidia_bl_exit(struct nvidia_par *par)
147{
148 struct fb_info *info = pci_get_drvdata(par->pci_dev);
Richard Purdie37ce69a2007-02-10 14:10:33 +0000149 struct backlight_device *bd = info->bl_dev;
Michael Hanselmann5474c122006-06-25 05:47:08 -0700150
Richard Purdie37ce69a2007-02-10 14:10:33 +0000151 if (bd) {
Michael Hanselmann5474c122006-06-25 05:47:08 -0700152#ifdef CONFIG_PMAC_BACKLIGHT
Richard Purdie37ce69a2007-02-10 14:10:33 +0000153 mutex_lock(&pmac_backlight_mutex);
154 if (pmac_backlight == bd)
Michael Hanselmann5474c122006-06-25 05:47:08 -0700155 pmac_backlight = NULL;
Richard Purdie37ce69a2007-02-10 14:10:33 +0000156 mutex_unlock(&pmac_backlight_mutex);
Michael Hanselmann5474c122006-06-25 05:47:08 -0700157#endif
Richard Purdie37ce69a2007-02-10 14:10:33 +0000158 backlight_device_unregister(bd);
Michael Hanselmann5474c122006-06-25 05:47:08 -0700159
160 printk("nvidia: Backlight unloaded\n");
161 }
Michael Hanselmann5474c122006-06-25 05:47:08 -0700162}