summaryrefslogtreecommitdiff
path: root/drivers/video/backlight/cr_bllcd.c
blob: 26add8898605e0bc08789e6bdf4a7961886e575b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
/*
 * Copyright (c) Intel Corp. 2007.
 * All Rights Reserved.
 *
 * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to
 * develop this driver.
 *
 * This file is part of the Carillo Ranch video subsystem driver.
 * The Carillo Ranch video subsystem driver is free software;
 * you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * The Carillo Ranch video subsystem driver is distributed
 * in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this driver; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Authors:
 *   Thomas Hellstrom <thomas-at-tungstengraphics-dot-com>
 *   Alan Hourihane <alanh-at-tungstengraphics-dot-com>
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/lcd.h>
#include <linux/pci.h>

/* The LVDS- and panel power controls sits on the
 * GPIO port of the ISA bridge.
 */

#define CRVML_DEVICE_LPC    0x27B8
#define CRVML_REG_GPIOBAR   0x48
#define CRVML_REG_GPIOEN    0x4C
#define CRVML_GPIOEN_BIT    (1 << 4)
#define CRVML_PANEL_PORT    0x38
#define CRVML_LVDS_ON       0x00000001
#define CRVML_PANEL_ON      0x00000002
#define CRVML_BACKLIGHT_OFF 0x00000004

/* The PLL Clock register sits on Host bridge */
#define CRVML_DEVICE_MCH   0x5001
#define CRVML_REG_MCHBAR   0x44
#define CRVML_REG_MCHEN    0x54
#define CRVML_MCHEN_BIT    (1 << 28)
#define CRVML_MCHMAP_SIZE  4096
#define CRVML_REG_CLOCK    0xc3c
#define CRVML_CLOCK_SHIFT  8
#define CRVML_CLOCK_MASK   0x00000f00

static struct pci_dev *lpc_dev;
static u32 gpio_bar;

struct cr_panel {
	struct backlight_device *cr_backlight_device;
	struct lcd_device *cr_lcd_device;
};

static int cr_backlight_set_intensity(struct backlight_device *bd)
{
	int intensity = bd->props.brightness;
	u32 addr = gpio_bar + CRVML_PANEL_PORT;
	u32 cur = inl(addr);

	if (bd->props.power == FB_BLANK_UNBLANK)
		intensity = FB_BLANK_UNBLANK;
	if (bd->props.fb_blank == FB_BLANK_UNBLANK)
		intensity = FB_BLANK_UNBLANK;
	if (bd->props.power == FB_BLANK_POWERDOWN)
		intensity = FB_BLANK_POWERDOWN;
	if (bd->props.fb_blank == FB_BLANK_POWERDOWN)
		intensity = FB_BLANK_POWERDOWN;

	if (intensity == FB_BLANK_UNBLANK) { /* FULL ON */
		cur &= ~CRVML_BACKLIGHT_OFF;
		outl(cur, addr);
	} else if (intensity == FB_BLANK_POWERDOWN) { /* OFF */
		cur |= CRVML_BACKLIGHT_OFF;
		outl(cur, addr);
	} /* anything else, don't bother */

	return 0;
}

static int cr_backlight_get_intensity(struct backlight_device *bd)
{
	u32 addr = gpio_bar + CRVML_PANEL_PORT;
	u32 cur = inl(addr);
	u8 intensity;

	if (cur & CRVML_BACKLIGHT_OFF)
		intensity = FB_BLANK_POWERDOWN;
	else
		intensity = FB_BLANK_UNBLANK;

	return intensity;
}

static struct backlight_ops cr_backlight_ops = {
	.get_brightness = cr_backlight_get_intensity,
	.update_status = cr_backlight_set_intensity,
};

static void cr_panel_on(void)
{
	u32 addr = gpio_bar + CRVML_PANEL_PORT;
	u32 cur = inl(addr);

	if (!(cur & CRVML_PANEL_ON)) {
		/* Make sure LVDS controller is down. */
		if (cur & 0x00000001) {
			cur &= ~CRVML_LVDS_ON;
			outl(cur, addr);
		}
		/* Power up Panel */
		schedule_timeout(HZ / 10);
		cur |= CRVML_PANEL_ON;
		outl(cur, addr);
	}

	/* Power up LVDS controller */

	if (!(cur & CRVML_LVDS_ON)) {
		schedule_timeout(HZ / 10);
		outl(cur | CRVML_LVDS_ON, addr);
	}
}

static void cr_panel_off(void)
{
	u32 addr = gpio_bar + CRVML_PANEL_PORT;
	u32 cur = inl(addr);

	/* Power down LVDS controller first to avoid high currents */
	if (cur & CRVML_LVDS_ON) {
		cur &= ~CRVML_LVDS_ON;
		outl(cur, addr);
	}
	if (cur & CRVML_PANEL_ON) {
		schedule_timeout(HZ / 10);
		outl(cur & ~CRVML_PANEL_ON, addr);
	}
}

static int cr_lcd_set_power(struct lcd_device *ld, int power)
{
	if (power == FB_BLANK_UNBLANK)
		cr_panel_on();
	if (power == FB_BLANK_POWERDOWN)
		cr_panel_off();

	return 0;
}

static struct lcd_ops cr_lcd_ops = {
	.set_power = cr_lcd_set_power,
};

static int cr_backlight_probe(struct platform_device *pdev)
{
	struct backlight_device *bdp;
	struct lcd_device *ldp;
	struct cr_panel *crp;
	u8 dev_en;

	lpc_dev = pci_get_device(PCI_VENDOR_ID_INTEL,
					CRVML_DEVICE_LPC, NULL);
	if (!lpc_dev) {
		printk("INTEL CARILLO RANCH LPC not found.\n");
		return -ENODEV;
	}

	pci_read_config_byte(lpc_dev, CRVML_REG_GPIOEN, &dev_en);
	if (!(dev_en & CRVML_GPIOEN_BIT)) {
		printk(KERN_ERR
		       "Carillo Ranch GPIO device was not enabled.\n");
		pci_dev_put(lpc_dev);
		return -ENODEV;
	}

	bdp = backlight_device_register("cr-backlight",
					&pdev->dev, NULL, &cr_backlight_ops);
	if (IS_ERR(bdp)) {
		pci_dev_put(lpc_dev);
		return PTR_ERR(bdp);
	}

	ldp = lcd_device_register("cr-lcd", &pdev->dev, NULL, &cr_lcd_ops);
	if (IS_ERR(ldp)) {
		backlight_device_unregister(bdp);
		pci_dev_put(lpc_dev);
		return PTR_ERR(bdp);
	}

	pci_read_config_dword(lpc_dev, CRVML_REG_GPIOBAR,
			      &gpio_bar);
	gpio_bar &= ~0x3F;

	crp = kzalloc(sizeof(*crp), GFP_KERNEL);
	if (!crp) {
		lcd_device_unregister(ldp);
		backlight_device_unregister(bdp);
		pci_dev_put(lpc_dev);
		return -ENOMEM;
	}

	crp->cr_backlight_device = bdp;
	crp->cr_lcd_device = ldp;
	crp->cr_backlight_device->props.power = FB_BLANK_UNBLANK;
	crp->cr_backlight_device->props.brightness = 0;
	crp->cr_backlight_device->props.max_brightness = 0;
	cr_backlight_set_intensity(crp->cr_backlight_device);

	cr_lcd_set_power(crp->cr_lcd_device, FB_BLANK_UNBLANK);

	platform_set_drvdata(pdev, crp);

	return 0;
}

static int cr_backlight_remove(struct platform_device *pdev)
{
	struct cr_panel *crp = platform_get_drvdata(pdev);
	crp->cr_backlight_device->props.power = FB_BLANK_POWERDOWN;
	crp->cr_backlight_device->props.brightness = 0;
	crp->cr_backlight_device->props.max_brightness = 0;
	cr_backlight_set_intensity(crp->cr_backlight_device);
	cr_lcd_set_power(crp->cr_lcd_device, FB_BLANK_POWERDOWN);
	backlight_device_unregister(crp->cr_backlight_device);
	lcd_device_unregister(crp->cr_lcd_device);
	pci_dev_put(lpc_dev);

	return 0;
}

static struct platform_driver cr_backlight_driver = {
	.probe = cr_backlight_probe,
	.remove = cr_backlight_remove,
	.driver = {
		   .name = "cr_backlight",
		   },
};

static struct platform_device *crp;

static int __init cr_backlight_init(void)
{
	int ret = platform_driver_register(&cr_backlight_driver);

	if (!ret) {
		crp = platform_device_alloc("cr_backlight", -1);
		if (!crp)
			return -ENOMEM;

		ret = platform_device_add(crp);

		if (ret) {
			platform_device_put(crp);
			platform_driver_unregister(&cr_backlight_driver);
		}
	}

	printk("Carillo Ranch Backlight Driver Initialized.\n");

	return ret;
}

static void __exit cr_backlight_exit(void)
{
	platform_device_unregister(crp);
	platform_driver_unregister(&cr_backlight_driver);
}

module_init(cr_backlight_init);
module_exit(cr_backlight_exit);

MODULE_AUTHOR("Tungsten Graphics Inc.");
MODULE_DESCRIPTION("Carillo Ranch Backlight Driver");
MODULE_LICENSE("GPL");