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 
60dbe7e429SAlan Hourihane static int cr_backlight_set_intensity(struct backlight_device *bd)
61dbe7e429SAlan Hourihane {
62dbe7e429SAlan Hourihane 	int intensity = bd->props.brightness;
63dbe7e429SAlan Hourihane 	u32 addr = gpio_bar + CRVML_PANEL_PORT;
64dbe7e429SAlan Hourihane 	u32 cur = inl(addr);
65dbe7e429SAlan Hourihane 
6624d34617SSam Ravnborg 	if (backlight_get_brightness(bd) == 0) {
6724d34617SSam Ravnborg 		/* OFF */
68dbe7e429SAlan Hourihane 		cur |= CRVML_BACKLIGHT_OFF;
69dbe7e429SAlan Hourihane 		outl(cur, addr);
7024d34617SSam Ravnborg 	} else {
7124d34617SSam Ravnborg 		/* FULL ON */
7224d34617SSam Ravnborg 		cur &= ~CRVML_BACKLIGHT_OFF;
7324d34617SSam Ravnborg 		outl(cur, addr);
7424d34617SSam Ravnborg 	}
75dbe7e429SAlan Hourihane 
76dbe7e429SAlan Hourihane 	return 0;
77dbe7e429SAlan Hourihane }
78dbe7e429SAlan Hourihane 
79dbe7e429SAlan Hourihane static int cr_backlight_get_intensity(struct backlight_device *bd)
80dbe7e429SAlan Hourihane {
81dbe7e429SAlan Hourihane 	u32 addr = gpio_bar + CRVML_PANEL_PORT;
82dbe7e429SAlan Hourihane 	u32 cur = inl(addr);
83dbe7e429SAlan Hourihane 	u8 intensity;
84dbe7e429SAlan Hourihane 
85dbe7e429SAlan Hourihane 	if (cur & CRVML_BACKLIGHT_OFF)
8624d34617SSam Ravnborg 		intensity = 0;
87dbe7e429SAlan Hourihane 	else
8824d34617SSam Ravnborg 		intensity = 1;
89dbe7e429SAlan Hourihane 
90dbe7e429SAlan Hourihane 	return intensity;
91dbe7e429SAlan Hourihane }
92dbe7e429SAlan Hourihane 
939905a43bSEmese Revfy static const struct backlight_ops cr_backlight_ops = {
94dbe7e429SAlan Hourihane 	.get_brightness = cr_backlight_get_intensity,
95dbe7e429SAlan Hourihane 	.update_status = cr_backlight_set_intensity,
96dbe7e429SAlan Hourihane };
97dbe7e429SAlan Hourihane 
98dbe7e429SAlan Hourihane static void cr_panel_on(void)
99dbe7e429SAlan Hourihane {
100dbe7e429SAlan Hourihane 	u32 addr = gpio_bar + CRVML_PANEL_PORT;
101dbe7e429SAlan Hourihane 	u32 cur = inl(addr);
102dbe7e429SAlan Hourihane 
103dbe7e429SAlan Hourihane 	if (!(cur & CRVML_PANEL_ON)) {
104dbe7e429SAlan Hourihane 		/* Make sure LVDS controller is down. */
105dbe7e429SAlan Hourihane 		if (cur & 0x00000001) {
106dbe7e429SAlan Hourihane 			cur &= ~CRVML_LVDS_ON;
107dbe7e429SAlan Hourihane 			outl(cur, addr);
108dbe7e429SAlan Hourihane 		}
109dbe7e429SAlan Hourihane 		/* Power up Panel */
110dbe7e429SAlan Hourihane 		schedule_timeout(HZ / 10);
111dbe7e429SAlan Hourihane 		cur |= CRVML_PANEL_ON;
112dbe7e429SAlan Hourihane 		outl(cur, addr);
113dbe7e429SAlan Hourihane 	}
114dbe7e429SAlan Hourihane 
115dbe7e429SAlan Hourihane 	/* Power up LVDS controller */
116dbe7e429SAlan Hourihane 
117dbe7e429SAlan Hourihane 	if (!(cur & CRVML_LVDS_ON)) {
118dbe7e429SAlan Hourihane 		schedule_timeout(HZ / 10);
119dbe7e429SAlan Hourihane 		outl(cur | CRVML_LVDS_ON, addr);
120dbe7e429SAlan Hourihane 	}
121dbe7e429SAlan Hourihane }
122dbe7e429SAlan Hourihane 
123dbe7e429SAlan Hourihane static void cr_panel_off(void)
124dbe7e429SAlan Hourihane {
125dbe7e429SAlan Hourihane 	u32 addr = gpio_bar + CRVML_PANEL_PORT;
126dbe7e429SAlan Hourihane 	u32 cur = inl(addr);
127dbe7e429SAlan Hourihane 
128dbe7e429SAlan Hourihane 	/* Power down LVDS controller first to avoid high currents */
129dbe7e429SAlan Hourihane 	if (cur & CRVML_LVDS_ON) {
130dbe7e429SAlan Hourihane 		cur &= ~CRVML_LVDS_ON;
131dbe7e429SAlan Hourihane 		outl(cur, addr);
132dbe7e429SAlan Hourihane 	}
133dbe7e429SAlan Hourihane 	if (cur & CRVML_PANEL_ON) {
134dbe7e429SAlan Hourihane 		schedule_timeout(HZ / 10);
135dbe7e429SAlan Hourihane 		outl(cur & ~CRVML_PANEL_ON, addr);
136dbe7e429SAlan Hourihane 	}
137dbe7e429SAlan Hourihane }
138dbe7e429SAlan Hourihane 
139dbe7e429SAlan Hourihane static int cr_lcd_set_power(struct lcd_device *ld, int power)
140dbe7e429SAlan Hourihane {
141dbe7e429SAlan Hourihane 	if (power == FB_BLANK_UNBLANK)
142dbe7e429SAlan Hourihane 		cr_panel_on();
143dbe7e429SAlan Hourihane 	if (power == FB_BLANK_POWERDOWN)
144dbe7e429SAlan Hourihane 		cr_panel_off();
145dbe7e429SAlan Hourihane 
146dbe7e429SAlan Hourihane 	return 0;
147dbe7e429SAlan Hourihane }
148dbe7e429SAlan Hourihane 
149dbe7e429SAlan Hourihane static struct lcd_ops cr_lcd_ops = {
150dbe7e429SAlan Hourihane 	.set_power = cr_lcd_set_power,
151dbe7e429SAlan Hourihane };
152dbe7e429SAlan Hourihane 
153dbe7e429SAlan Hourihane static int cr_backlight_probe(struct platform_device *pdev)
154dbe7e429SAlan Hourihane {
155a19a6ee6SMatthew Garrett 	struct backlight_properties props;
1560b75f2dfSJesper Juhl 	struct backlight_device *bdp;
1570b75f2dfSJesper Juhl 	struct lcd_device *ldp;
158dbe7e429SAlan Hourihane 	struct cr_panel *crp;
159dbe7e429SAlan Hourihane 	u8 dev_en;
160dbe7e429SAlan Hourihane 
161dbe7e429SAlan Hourihane 	lpc_dev = pci_get_device(PCI_VENDOR_ID_INTEL,
162dbe7e429SAlan Hourihane 					CRVML_DEVICE_LPC, NULL);
163dbe7e429SAlan Hourihane 	if (!lpc_dev) {
16431e6432bSJingoo Han 		pr_err("INTEL CARILLO RANCH LPC not found.\n");
165dbe7e429SAlan Hourihane 		return -ENODEV;
166dbe7e429SAlan Hourihane 	}
167dbe7e429SAlan Hourihane 
168dbe7e429SAlan Hourihane 	pci_read_config_byte(lpc_dev, CRVML_REG_GPIOEN, &dev_en);
169dbe7e429SAlan Hourihane 	if (!(dev_en & CRVML_GPIOEN_BIT)) {
17031e6432bSJingoo Han 		pr_err("Carillo Ranch GPIO device was not enabled.\n");
171dbe7e429SAlan Hourihane 		pci_dev_put(lpc_dev);
172dbe7e429SAlan Hourihane 		return -ENODEV;
173dbe7e429SAlan Hourihane 	}
174dbe7e429SAlan Hourihane 
175a19a6ee6SMatthew Garrett 	memset(&props, 0, sizeof(struct backlight_properties));
176bb7ca747SMatthew Garrett 	props.type = BACKLIGHT_RAW;
177289a65e4SJingoo Han 	bdp = devm_backlight_device_register(&pdev->dev, "cr-backlight",
178289a65e4SJingoo Han 					&pdev->dev, NULL, &cr_backlight_ops,
179289a65e4SJingoo Han 					&props);
1800b75f2dfSJesper Juhl 	if (IS_ERR(bdp)) {
181dbe7e429SAlan Hourihane 		pci_dev_put(lpc_dev);
1820b75f2dfSJesper Juhl 		return PTR_ERR(bdp);
183dbe7e429SAlan Hourihane 	}
184dbe7e429SAlan Hourihane 
185289a65e4SJingoo Han 	ldp = devm_lcd_device_register(&pdev->dev, "cr-lcd", &pdev->dev, NULL,
186289a65e4SJingoo Han 					&cr_lcd_ops);
1870b75f2dfSJesper Juhl 	if (IS_ERR(ldp)) {
188dbe7e429SAlan Hourihane 		pci_dev_put(lpc_dev);
1895b0582eaSRoel Kluin 		return PTR_ERR(ldp);
190dbe7e429SAlan Hourihane 	}
191dbe7e429SAlan Hourihane 
192dbe7e429SAlan Hourihane 	pci_read_config_dword(lpc_dev, CRVML_REG_GPIOBAR,
193dbe7e429SAlan Hourihane 			      &gpio_bar);
194dbe7e429SAlan Hourihane 	gpio_bar &= ~0x3F;
195dbe7e429SAlan Hourihane 
196ce969228SJulia Lawall 	crp = devm_kzalloc(&pdev->dev, sizeof(*crp), GFP_KERNEL);
1970b75f2dfSJesper Juhl 	if (!crp) {
1980b75f2dfSJesper Juhl 		pci_dev_put(lpc_dev);
1990b75f2dfSJesper Juhl 		return -ENOMEM;
2000b75f2dfSJesper Juhl 	}
2010b75f2dfSJesper Juhl 
2020b75f2dfSJesper Juhl 	crp->cr_backlight_device = bdp;
2030b75f2dfSJesper Juhl 	crp->cr_lcd_device = ldp;
204dbe7e429SAlan Hourihane 	crp->cr_backlight_device->props.power = FB_BLANK_UNBLANK;
205dbe7e429SAlan Hourihane 	crp->cr_backlight_device->props.brightness = 0;
206dbe7e429SAlan Hourihane 	cr_backlight_set_intensity(crp->cr_backlight_device);
207dbe7e429SAlan Hourihane 	cr_lcd_set_power(crp->cr_lcd_device, FB_BLANK_UNBLANK);
208dbe7e429SAlan Hourihane 
209dbe7e429SAlan Hourihane 	platform_set_drvdata(pdev, crp);
210dbe7e429SAlan Hourihane 
211dbe7e429SAlan Hourihane 	return 0;
212dbe7e429SAlan Hourihane }
213dbe7e429SAlan Hourihane 
214dbe7e429SAlan Hourihane static int cr_backlight_remove(struct platform_device *pdev)
215dbe7e429SAlan Hourihane {
216dbe7e429SAlan Hourihane 	struct cr_panel *crp = platform_get_drvdata(pdev);
2177beeee46SJingoo Han 
218dbe7e429SAlan Hourihane 	crp->cr_backlight_device->props.power = FB_BLANK_POWERDOWN;
219dbe7e429SAlan Hourihane 	crp->cr_backlight_device->props.brightness = 0;
220dbe7e429SAlan Hourihane 	crp->cr_backlight_device->props.max_brightness = 0;
221dbe7e429SAlan Hourihane 	cr_backlight_set_intensity(crp->cr_backlight_device);
222dbe7e429SAlan Hourihane 	cr_lcd_set_power(crp->cr_lcd_device, FB_BLANK_POWERDOWN);
223dbe7e429SAlan Hourihane 	pci_dev_put(lpc_dev);
224dbe7e429SAlan Hourihane 
225dbe7e429SAlan Hourihane 	return 0;
226dbe7e429SAlan Hourihane }
227dbe7e429SAlan Hourihane 
228dbe7e429SAlan Hourihane static struct platform_driver cr_backlight_driver = {
229dbe7e429SAlan Hourihane 	.probe = cr_backlight_probe,
230dbe7e429SAlan Hourihane 	.remove = cr_backlight_remove,
231dbe7e429SAlan Hourihane 	.driver = {
232dbe7e429SAlan Hourihane 		   .name = "cr_backlight",
233dbe7e429SAlan Hourihane 		   },
234dbe7e429SAlan Hourihane };
235dbe7e429SAlan Hourihane 
236dbe7e429SAlan Hourihane static struct platform_device *crp;
237dbe7e429SAlan Hourihane 
238dbe7e429SAlan Hourihane static int __init cr_backlight_init(void)
239dbe7e429SAlan Hourihane {
240dbe7e429SAlan Hourihane 	int ret = platform_driver_register(&cr_backlight_driver);
241dbe7e429SAlan Hourihane 
242b4a11d3dSAkinobu Mita 	if (ret)
243b4a11d3dSAkinobu Mita 		return ret;
244dbe7e429SAlan Hourihane 
245b4a11d3dSAkinobu Mita 	crp = platform_device_register_simple("cr_backlight", -1, NULL, 0);
246b4a11d3dSAkinobu Mita 	if (IS_ERR(crp)) {
247dbe7e429SAlan Hourihane 		platform_driver_unregister(&cr_backlight_driver);
248b4a11d3dSAkinobu Mita 		return PTR_ERR(crp);
249dbe7e429SAlan Hourihane 	}
250dbe7e429SAlan Hourihane 
25131e6432bSJingoo Han 	pr_info("Carillo Ranch Backlight Driver Initialized.\n");
252dbe7e429SAlan Hourihane 
253b4a11d3dSAkinobu Mita 	return 0;
254dbe7e429SAlan Hourihane }
255dbe7e429SAlan Hourihane 
256dbe7e429SAlan Hourihane static void __exit cr_backlight_exit(void)
257dbe7e429SAlan Hourihane {
258dbe7e429SAlan Hourihane 	platform_device_unregister(crp);
259dbe7e429SAlan Hourihane 	platform_driver_unregister(&cr_backlight_driver);
260dbe7e429SAlan Hourihane }
261dbe7e429SAlan Hourihane 
262dbe7e429SAlan Hourihane module_init(cr_backlight_init);
263dbe7e429SAlan Hourihane module_exit(cr_backlight_exit);
264dbe7e429SAlan Hourihane 
265dbe7e429SAlan Hourihane MODULE_AUTHOR("Tungsten Graphics Inc.");
266dbe7e429SAlan Hourihane MODULE_DESCRIPTION("Carillo Ranch Backlight Driver");
267dbe7e429SAlan Hourihane MODULE_LICENSE("GPL");
268