14ad91750SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2dbe7e429SAlan Hourihane /*
3dbe7e429SAlan Hourihane  * Copyright (c) Intel Corp. 2007.
4dbe7e429SAlan Hourihane  * All Rights Reserved.
5dbe7e429SAlan Hourihane  *
6dbe7e429SAlan Hourihane  * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to
7dbe7e429SAlan Hourihane  * develop this driver.
8dbe7e429SAlan Hourihane  *
9dbe7e429SAlan Hourihane  * This file is part of the Carillo Ranch video subsystem driver.
10dbe7e429SAlan Hourihane  *
11dbe7e429SAlan Hourihane  * Authors:
12dbe7e429SAlan Hourihane  *   Thomas Hellstrom <thomas-at-tungstengraphics-dot-com>
13dbe7e429SAlan Hourihane  *   Alan Hourihane <alanh-at-tungstengraphics-dot-com>
14dbe7e429SAlan Hourihane  */
15dbe7e429SAlan Hourihane 
1631e6432bSJingoo Han #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1731e6432bSJingoo Han 
18dbe7e429SAlan Hourihane #include <linux/module.h>
19dbe7e429SAlan Hourihane #include <linux/kernel.h>
20dbe7e429SAlan Hourihane #include <linux/init.h>
21dbe7e429SAlan Hourihane #include <linux/platform_device.h>
22dbe7e429SAlan Hourihane #include <linux/mutex.h>
23dbe7e429SAlan Hourihane #include <linux/fb.h>
24dbe7e429SAlan Hourihane #include <linux/backlight.h>
25dbe7e429SAlan Hourihane #include <linux/lcd.h>
26dbe7e429SAlan Hourihane #include <linux/pci.h>
275a0e3ad6STejun Heo #include <linux/slab.h>
28dbe7e429SAlan Hourihane 
29dbe7e429SAlan Hourihane /* The LVDS- and panel power controls sits on the
30dbe7e429SAlan Hourihane  * GPIO port of the ISA bridge.
31dbe7e429SAlan Hourihane  */
32dbe7e429SAlan Hourihane 
33dbe7e429SAlan Hourihane #define CRVML_DEVICE_LPC    0x27B8
34dbe7e429SAlan Hourihane #define CRVML_REG_GPIOBAR   0x48
35dbe7e429SAlan Hourihane #define CRVML_REG_GPIOEN    0x4C
36dbe7e429SAlan Hourihane #define CRVML_GPIOEN_BIT    (1 << 4)
37dbe7e429SAlan Hourihane #define CRVML_PANEL_PORT    0x38
38dbe7e429SAlan Hourihane #define CRVML_LVDS_ON       0x00000001
39dbe7e429SAlan Hourihane #define CRVML_PANEL_ON      0x00000002
40dbe7e429SAlan Hourihane #define CRVML_BACKLIGHT_OFF 0x00000004
41dbe7e429SAlan Hourihane 
42dbe7e429SAlan Hourihane /* The PLL Clock register sits on Host bridge */
43dbe7e429SAlan Hourihane #define CRVML_DEVICE_MCH   0x5001
44dbe7e429SAlan Hourihane #define CRVML_REG_MCHBAR   0x44
45dbe7e429SAlan Hourihane #define CRVML_REG_MCHEN    0x54
46dbe7e429SAlan Hourihane #define CRVML_MCHEN_BIT    (1 << 28)
47dbe7e429SAlan Hourihane #define CRVML_MCHMAP_SIZE  4096
48dbe7e429SAlan Hourihane #define CRVML_REG_CLOCK    0xc3c
49dbe7e429SAlan Hourihane #define CRVML_CLOCK_SHIFT  8
50dbe7e429SAlan Hourihane #define CRVML_CLOCK_MASK   0x00000f00
51dbe7e429SAlan Hourihane 
52dbe7e429SAlan Hourihane static struct pci_dev *lpc_dev;
53dbe7e429SAlan Hourihane static u32 gpio_bar;
54dbe7e429SAlan Hourihane 
55dbe7e429SAlan Hourihane struct cr_panel {
56dbe7e429SAlan Hourihane 	struct backlight_device *cr_backlight_device;
57dbe7e429SAlan Hourihane 	struct lcd_device *cr_lcd_device;
58dbe7e429SAlan Hourihane };
59dbe7e429SAlan Hourihane 
cr_backlight_set_intensity(struct backlight_device * bd)60dbe7e429SAlan Hourihane static int cr_backlight_set_intensity(struct backlight_device *bd)
61dbe7e429SAlan Hourihane {
62dbe7e429SAlan Hourihane 	u32 addr = gpio_bar + CRVML_PANEL_PORT;
63dbe7e429SAlan Hourihane 	u32 cur = inl(addr);
64dbe7e429SAlan Hourihane 
6524d34617SSam Ravnborg 	if (backlight_get_brightness(bd) == 0) {
6624d34617SSam Ravnborg 		/* OFF */
67dbe7e429SAlan Hourihane 		cur |= CRVML_BACKLIGHT_OFF;
68dbe7e429SAlan Hourihane 		outl(cur, addr);
6924d34617SSam Ravnborg 	} else {
7024d34617SSam Ravnborg 		/* FULL ON */
7124d34617SSam Ravnborg 		cur &= ~CRVML_BACKLIGHT_OFF;
7224d34617SSam Ravnborg 		outl(cur, addr);
7324d34617SSam Ravnborg 	}
74dbe7e429SAlan Hourihane 
75dbe7e429SAlan Hourihane 	return 0;
76dbe7e429SAlan Hourihane }
77dbe7e429SAlan Hourihane 
cr_backlight_get_intensity(struct backlight_device * bd)78dbe7e429SAlan Hourihane static int cr_backlight_get_intensity(struct backlight_device *bd)
79dbe7e429SAlan Hourihane {
80dbe7e429SAlan Hourihane 	u32 addr = gpio_bar + CRVML_PANEL_PORT;
81dbe7e429SAlan Hourihane 	u32 cur = inl(addr);
82dbe7e429SAlan Hourihane 	u8 intensity;
83dbe7e429SAlan Hourihane 
84dbe7e429SAlan Hourihane 	if (cur & CRVML_BACKLIGHT_OFF)
8524d34617SSam Ravnborg 		intensity = 0;
86dbe7e429SAlan Hourihane 	else
8724d34617SSam Ravnborg 		intensity = 1;
88dbe7e429SAlan Hourihane 
89dbe7e429SAlan Hourihane 	return intensity;
90dbe7e429SAlan Hourihane }
91dbe7e429SAlan Hourihane 
929905a43bSEmese Revfy static const struct backlight_ops cr_backlight_ops = {
93dbe7e429SAlan Hourihane 	.get_brightness = cr_backlight_get_intensity,
94dbe7e429SAlan Hourihane 	.update_status = cr_backlight_set_intensity,
95dbe7e429SAlan Hourihane };
96dbe7e429SAlan Hourihane 
cr_panel_on(void)97dbe7e429SAlan Hourihane static void cr_panel_on(void)
98dbe7e429SAlan Hourihane {
99dbe7e429SAlan Hourihane 	u32 addr = gpio_bar + CRVML_PANEL_PORT;
100dbe7e429SAlan Hourihane 	u32 cur = inl(addr);
101dbe7e429SAlan Hourihane 
102dbe7e429SAlan Hourihane 	if (!(cur & CRVML_PANEL_ON)) {
103dbe7e429SAlan Hourihane 		/* Make sure LVDS controller is down. */
104dbe7e429SAlan Hourihane 		if (cur & 0x00000001) {
105dbe7e429SAlan Hourihane 			cur &= ~CRVML_LVDS_ON;
106dbe7e429SAlan Hourihane 			outl(cur, addr);
107dbe7e429SAlan Hourihane 		}
108dbe7e429SAlan Hourihane 		/* Power up Panel */
109dbe7e429SAlan Hourihane 		schedule_timeout(HZ / 10);
110dbe7e429SAlan Hourihane 		cur |= CRVML_PANEL_ON;
111dbe7e429SAlan Hourihane 		outl(cur, addr);
112dbe7e429SAlan Hourihane 	}
113dbe7e429SAlan Hourihane 
114dbe7e429SAlan Hourihane 	/* Power up LVDS controller */
115dbe7e429SAlan Hourihane 
116dbe7e429SAlan Hourihane 	if (!(cur & CRVML_LVDS_ON)) {
117dbe7e429SAlan Hourihane 		schedule_timeout(HZ / 10);
118dbe7e429SAlan Hourihane 		outl(cur | CRVML_LVDS_ON, addr);
119dbe7e429SAlan Hourihane 	}
120dbe7e429SAlan Hourihane }
121dbe7e429SAlan Hourihane 
cr_panel_off(void)122dbe7e429SAlan Hourihane static void cr_panel_off(void)
123dbe7e429SAlan Hourihane {
124dbe7e429SAlan Hourihane 	u32 addr = gpio_bar + CRVML_PANEL_PORT;
125dbe7e429SAlan Hourihane 	u32 cur = inl(addr);
126dbe7e429SAlan Hourihane 
127dbe7e429SAlan Hourihane 	/* Power down LVDS controller first to avoid high currents */
128dbe7e429SAlan Hourihane 	if (cur & CRVML_LVDS_ON) {
129dbe7e429SAlan Hourihane 		cur &= ~CRVML_LVDS_ON;
130dbe7e429SAlan Hourihane 		outl(cur, addr);
131dbe7e429SAlan Hourihane 	}
132dbe7e429SAlan Hourihane 	if (cur & CRVML_PANEL_ON) {
133dbe7e429SAlan Hourihane 		schedule_timeout(HZ / 10);
134dbe7e429SAlan Hourihane 		outl(cur & ~CRVML_PANEL_ON, addr);
135dbe7e429SAlan Hourihane 	}
136dbe7e429SAlan Hourihane }
137dbe7e429SAlan Hourihane 
cr_lcd_set_power(struct lcd_device * ld,int power)138dbe7e429SAlan Hourihane static int cr_lcd_set_power(struct lcd_device *ld, int power)
139dbe7e429SAlan Hourihane {
140dbe7e429SAlan Hourihane 	if (power == FB_BLANK_UNBLANK)
141dbe7e429SAlan Hourihane 		cr_panel_on();
142dbe7e429SAlan Hourihane 	if (power == FB_BLANK_POWERDOWN)
143dbe7e429SAlan Hourihane 		cr_panel_off();
144dbe7e429SAlan Hourihane 
145dbe7e429SAlan Hourihane 	return 0;
146dbe7e429SAlan Hourihane }
147dbe7e429SAlan Hourihane 
148dbe7e429SAlan Hourihane static struct lcd_ops cr_lcd_ops = {
149dbe7e429SAlan Hourihane 	.set_power = cr_lcd_set_power,
150dbe7e429SAlan Hourihane };
151dbe7e429SAlan Hourihane 
cr_backlight_probe(struct platform_device * pdev)152dbe7e429SAlan Hourihane static int cr_backlight_probe(struct platform_device *pdev)
153dbe7e429SAlan Hourihane {
154a19a6ee6SMatthew Garrett 	struct backlight_properties props;
1550b75f2dfSJesper Juhl 	struct backlight_device *bdp;
1560b75f2dfSJesper Juhl 	struct lcd_device *ldp;
157dbe7e429SAlan Hourihane 	struct cr_panel *crp;
158dbe7e429SAlan Hourihane 	u8 dev_en;
159dbe7e429SAlan Hourihane 
160dbe7e429SAlan Hourihane 	lpc_dev = pci_get_device(PCI_VENDOR_ID_INTEL,
161dbe7e429SAlan Hourihane 					CRVML_DEVICE_LPC, NULL);
162dbe7e429SAlan Hourihane 	if (!lpc_dev) {
16331e6432bSJingoo Han 		pr_err("INTEL CARILLO RANCH LPC not found.\n");
164dbe7e429SAlan Hourihane 		return -ENODEV;
165dbe7e429SAlan Hourihane 	}
166dbe7e429SAlan Hourihane 
167dbe7e429SAlan Hourihane 	pci_read_config_byte(lpc_dev, CRVML_REG_GPIOEN, &dev_en);
168dbe7e429SAlan Hourihane 	if (!(dev_en & CRVML_GPIOEN_BIT)) {
16931e6432bSJingoo Han 		pr_err("Carillo Ranch GPIO device was not enabled.\n");
170dbe7e429SAlan Hourihane 		pci_dev_put(lpc_dev);
171dbe7e429SAlan Hourihane 		return -ENODEV;
172dbe7e429SAlan Hourihane 	}
173dbe7e429SAlan Hourihane 
174a19a6ee6SMatthew Garrett 	memset(&props, 0, sizeof(struct backlight_properties));
175bb7ca747SMatthew Garrett 	props.type = BACKLIGHT_RAW;
176289a65e4SJingoo Han 	bdp = devm_backlight_device_register(&pdev->dev, "cr-backlight",
177289a65e4SJingoo Han 					&pdev->dev, NULL, &cr_backlight_ops,
178289a65e4SJingoo Han 					&props);
1790b75f2dfSJesper Juhl 	if (IS_ERR(bdp)) {
180dbe7e429SAlan Hourihane 		pci_dev_put(lpc_dev);
1810b75f2dfSJesper Juhl 		return PTR_ERR(bdp);
182dbe7e429SAlan Hourihane 	}
183dbe7e429SAlan Hourihane 
184289a65e4SJingoo Han 	ldp = devm_lcd_device_register(&pdev->dev, "cr-lcd", &pdev->dev, NULL,
185289a65e4SJingoo Han 					&cr_lcd_ops);
1860b75f2dfSJesper Juhl 	if (IS_ERR(ldp)) {
187dbe7e429SAlan Hourihane 		pci_dev_put(lpc_dev);
1885b0582eaSRoel Kluin 		return PTR_ERR(ldp);
189dbe7e429SAlan Hourihane 	}
190dbe7e429SAlan Hourihane 
191dbe7e429SAlan Hourihane 	pci_read_config_dword(lpc_dev, CRVML_REG_GPIOBAR,
192dbe7e429SAlan Hourihane 			      &gpio_bar);
193dbe7e429SAlan Hourihane 	gpio_bar &= ~0x3F;
194dbe7e429SAlan Hourihane 
195ce969228SJulia Lawall 	crp = devm_kzalloc(&pdev->dev, sizeof(*crp), GFP_KERNEL);
1960b75f2dfSJesper Juhl 	if (!crp) {
1970b75f2dfSJesper Juhl 		pci_dev_put(lpc_dev);
1980b75f2dfSJesper Juhl 		return -ENOMEM;
1990b75f2dfSJesper Juhl 	}
2000b75f2dfSJesper Juhl 
2010b75f2dfSJesper Juhl 	crp->cr_backlight_device = bdp;
2020b75f2dfSJesper Juhl 	crp->cr_lcd_device = ldp;
203dbe7e429SAlan Hourihane 	crp->cr_backlight_device->props.power = FB_BLANK_UNBLANK;
204dbe7e429SAlan Hourihane 	crp->cr_backlight_device->props.brightness = 0;
205dbe7e429SAlan Hourihane 	cr_backlight_set_intensity(crp->cr_backlight_device);
206dbe7e429SAlan Hourihane 	cr_lcd_set_power(crp->cr_lcd_device, FB_BLANK_UNBLANK);
207dbe7e429SAlan Hourihane 
208dbe7e429SAlan Hourihane 	platform_set_drvdata(pdev, crp);
209dbe7e429SAlan Hourihane 
210dbe7e429SAlan Hourihane 	return 0;
211dbe7e429SAlan Hourihane }
212dbe7e429SAlan Hourihane 
cr_backlight_remove(struct platform_device * pdev)213*f6672d7fSUwe Kleine-König static void cr_backlight_remove(struct platform_device *pdev)
214dbe7e429SAlan Hourihane {
215dbe7e429SAlan Hourihane 	struct cr_panel *crp = platform_get_drvdata(pdev);
2167beeee46SJingoo Han 
217dbe7e429SAlan Hourihane 	crp->cr_backlight_device->props.power = FB_BLANK_POWERDOWN;
218dbe7e429SAlan Hourihane 	crp->cr_backlight_device->props.brightness = 0;
219dbe7e429SAlan Hourihane 	crp->cr_backlight_device->props.max_brightness = 0;
220dbe7e429SAlan Hourihane 	cr_backlight_set_intensity(crp->cr_backlight_device);
221dbe7e429SAlan Hourihane 	cr_lcd_set_power(crp->cr_lcd_device, FB_BLANK_POWERDOWN);
222dbe7e429SAlan Hourihane 	pci_dev_put(lpc_dev);
223dbe7e429SAlan Hourihane }
224dbe7e429SAlan Hourihane 
225dbe7e429SAlan Hourihane static struct platform_driver cr_backlight_driver = {
226dbe7e429SAlan Hourihane 	.probe = cr_backlight_probe,
227*f6672d7fSUwe Kleine-König 	.remove_new = cr_backlight_remove,
228dbe7e429SAlan Hourihane 	.driver = {
229dbe7e429SAlan Hourihane 		   .name = "cr_backlight",
230dbe7e429SAlan Hourihane 		   },
231dbe7e429SAlan Hourihane };
232dbe7e429SAlan Hourihane 
233dbe7e429SAlan Hourihane static struct platform_device *crp;
234dbe7e429SAlan Hourihane 
cr_backlight_init(void)235dbe7e429SAlan Hourihane static int __init cr_backlight_init(void)
236dbe7e429SAlan Hourihane {
237dbe7e429SAlan Hourihane 	int ret = platform_driver_register(&cr_backlight_driver);
238dbe7e429SAlan Hourihane 
239b4a11d3dSAkinobu Mita 	if (ret)
240b4a11d3dSAkinobu Mita 		return ret;
241dbe7e429SAlan Hourihane 
242b4a11d3dSAkinobu Mita 	crp = platform_device_register_simple("cr_backlight", -1, NULL, 0);
243b4a11d3dSAkinobu Mita 	if (IS_ERR(crp)) {
244dbe7e429SAlan Hourihane 		platform_driver_unregister(&cr_backlight_driver);
245b4a11d3dSAkinobu Mita 		return PTR_ERR(crp);
246dbe7e429SAlan Hourihane 	}
247dbe7e429SAlan Hourihane 
24831e6432bSJingoo Han 	pr_info("Carillo Ranch Backlight Driver Initialized.\n");
249dbe7e429SAlan Hourihane 
250b4a11d3dSAkinobu Mita 	return 0;
251dbe7e429SAlan Hourihane }
252dbe7e429SAlan Hourihane 
cr_backlight_exit(void)253dbe7e429SAlan Hourihane static void __exit cr_backlight_exit(void)
254dbe7e429SAlan Hourihane {
255dbe7e429SAlan Hourihane 	platform_device_unregister(crp);
256dbe7e429SAlan Hourihane 	platform_driver_unregister(&cr_backlight_driver);
257dbe7e429SAlan Hourihane }
258dbe7e429SAlan Hourihane 
259dbe7e429SAlan Hourihane module_init(cr_backlight_init);
260dbe7e429SAlan Hourihane module_exit(cr_backlight_exit);
261dbe7e429SAlan Hourihane 
262dbe7e429SAlan Hourihane MODULE_AUTHOR("Tungsten Graphics Inc.");
263dbe7e429SAlan Hourihane MODULE_DESCRIPTION("Carillo Ranch Backlight Driver");
264dbe7e429SAlan Hourihane MODULE_LICENSE("GPL");
265