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