xref: /OK3568_Linux_fs/kernel/drivers/video/backlight/locomolcd.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Backlight control code for Sharp Zaurus SL-5500
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright 2005 John Lenz <lenz@cs.wisc.edu>
6*4882a593Smuzhiyun  * Maintainer: Pavel Machek <pavel@ucw.cz> (unless John wants to :-)
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * This driver assumes single CPU. That's okay, because collie is
9*4882a593Smuzhiyun  * slightly old hardware, and no one is going to retrofit second CPU to
10*4882a593Smuzhiyun  * old PDA.
11*4882a593Smuzhiyun  */
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun /* LCD power functions */
14*4882a593Smuzhiyun #include <linux/module.h>
15*4882a593Smuzhiyun #include <linux/init.h>
16*4882a593Smuzhiyun #include <linux/delay.h>
17*4882a593Smuzhiyun #include <linux/device.h>
18*4882a593Smuzhiyun #include <linux/interrupt.h>
19*4882a593Smuzhiyun #include <linux/fb.h>
20*4882a593Smuzhiyun #include <linux/backlight.h>
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun #include <asm/hardware/locomo.h>
23*4882a593Smuzhiyun #include <asm/irq.h>
24*4882a593Smuzhiyun #include <asm/mach/sharpsl_param.h>
25*4882a593Smuzhiyun #include <asm/mach-types.h>
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun #include "../../../arch/arm/mach-sa1100/generic.h"
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun static struct backlight_device *locomolcd_bl_device;
30*4882a593Smuzhiyun static struct locomo_dev *locomolcd_dev;
31*4882a593Smuzhiyun static unsigned long locomolcd_flags;
32*4882a593Smuzhiyun #define LOCOMOLCD_SUSPENDED     0x01
33*4882a593Smuzhiyun 
locomolcd_on(int comadj)34*4882a593Smuzhiyun static void locomolcd_on(int comadj)
35*4882a593Smuzhiyun {
36*4882a593Smuzhiyun 	locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHA_ON, 0);
37*4882a593Smuzhiyun 	locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHA_ON, 1);
38*4882a593Smuzhiyun 	mdelay(2);
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun 	locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHD_ON, 0);
41*4882a593Smuzhiyun 	locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHD_ON, 1);
42*4882a593Smuzhiyun 	mdelay(2);
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun 	locomo_m62332_senddata(locomolcd_dev, comadj, 0);
45*4882a593Smuzhiyun 	mdelay(5);
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun 	locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VEE_ON, 0);
48*4882a593Smuzhiyun 	locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VEE_ON, 1);
49*4882a593Smuzhiyun 	mdelay(10);
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun 	/* TFTCRST | CPSOUT=0 | CPSEN */
52*4882a593Smuzhiyun 	locomo_writel(0x01, locomolcd_dev->mapbase + LOCOMO_TC);
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun 	/* Set CPSD */
55*4882a593Smuzhiyun 	locomo_writel(6, locomolcd_dev->mapbase + LOCOMO_CPSD);
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 	/* TFTCRST | CPSOUT=0 | CPSEN */
58*4882a593Smuzhiyun 	locomo_writel((0x04 | 0x01), locomolcd_dev->mapbase + LOCOMO_TC);
59*4882a593Smuzhiyun 	mdelay(10);
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 	locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_MOD, 0);
62*4882a593Smuzhiyun 	locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_MOD, 1);
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun 
locomolcd_off(int comadj)65*4882a593Smuzhiyun static void locomolcd_off(int comadj)
66*4882a593Smuzhiyun {
67*4882a593Smuzhiyun 	/* TFTCRST=1 | CPSOUT=1 | CPSEN = 0 */
68*4882a593Smuzhiyun 	locomo_writel(0x06, locomolcd_dev->mapbase + LOCOMO_TC);
69*4882a593Smuzhiyun 	mdelay(1);
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 	locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHA_ON, 0);
72*4882a593Smuzhiyun 	mdelay(110);
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VEE_ON, 0);
75*4882a593Smuzhiyun 	mdelay(700);
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 	/* TFTCRST=0 | CPSOUT=0 | CPSEN = 0 */
78*4882a593Smuzhiyun 	locomo_writel(0, locomolcd_dev->mapbase + LOCOMO_TC);
79*4882a593Smuzhiyun 	locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_MOD, 0);
80*4882a593Smuzhiyun 	locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHD_ON, 0);
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun 
locomolcd_power(int on)83*4882a593Smuzhiyun void locomolcd_power(int on)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun 	int comadj = sharpsl_param.comadj;
86*4882a593Smuzhiyun 	unsigned long flags;
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	local_irq_save(flags);
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 	if (!locomolcd_dev) {
91*4882a593Smuzhiyun 		local_irq_restore(flags);
92*4882a593Smuzhiyun 		return;
93*4882a593Smuzhiyun 	}
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	/* read comadj */
96*4882a593Smuzhiyun 	if (comadj == -1 && machine_is_collie())
97*4882a593Smuzhiyun 		comadj = 128;
98*4882a593Smuzhiyun 	if (comadj == -1 && machine_is_poodle())
99*4882a593Smuzhiyun 		comadj = 118;
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	if (on)
102*4882a593Smuzhiyun 		locomolcd_on(comadj);
103*4882a593Smuzhiyun 	else
104*4882a593Smuzhiyun 		locomolcd_off(comadj);
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	local_irq_restore(flags);
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun EXPORT_SYMBOL(locomolcd_power);
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun static int current_intensity;
111*4882a593Smuzhiyun 
locomolcd_set_intensity(struct backlight_device * bd)112*4882a593Smuzhiyun static int locomolcd_set_intensity(struct backlight_device *bd)
113*4882a593Smuzhiyun {
114*4882a593Smuzhiyun 	int intensity = backlight_get_brightness(bd);
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 	if (locomolcd_flags & LOCOMOLCD_SUSPENDED)
117*4882a593Smuzhiyun 		intensity = 0;
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	switch (intensity) {
120*4882a593Smuzhiyun 	/*
121*4882a593Smuzhiyun 	 * AC and non-AC are handled differently,
122*4882a593Smuzhiyun 	 * but produce same results in sharp code?
123*4882a593Smuzhiyun 	 */
124*4882a593Smuzhiyun 	case 0:
125*4882a593Smuzhiyun 		locomo_frontlight_set(locomolcd_dev, 0, 0, 161);
126*4882a593Smuzhiyun 		break;
127*4882a593Smuzhiyun 	case 1:
128*4882a593Smuzhiyun 		locomo_frontlight_set(locomolcd_dev, 117, 0, 161);
129*4882a593Smuzhiyun 		break;
130*4882a593Smuzhiyun 	case 2:
131*4882a593Smuzhiyun 		locomo_frontlight_set(locomolcd_dev, 163, 0, 148);
132*4882a593Smuzhiyun 		break;
133*4882a593Smuzhiyun 	case 3:
134*4882a593Smuzhiyun 		locomo_frontlight_set(locomolcd_dev, 194, 0, 161);
135*4882a593Smuzhiyun 		break;
136*4882a593Smuzhiyun 	case 4:
137*4882a593Smuzhiyun 		locomo_frontlight_set(locomolcd_dev, 194, 1, 161);
138*4882a593Smuzhiyun 		break;
139*4882a593Smuzhiyun 	default:
140*4882a593Smuzhiyun 		return -ENODEV;
141*4882a593Smuzhiyun 	}
142*4882a593Smuzhiyun 	current_intensity = intensity;
143*4882a593Smuzhiyun 	return 0;
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun 
locomolcd_get_intensity(struct backlight_device * bd)146*4882a593Smuzhiyun static int locomolcd_get_intensity(struct backlight_device *bd)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun 	return current_intensity;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun static const struct backlight_ops locomobl_data = {
152*4882a593Smuzhiyun 	.get_brightness = locomolcd_get_intensity,
153*4882a593Smuzhiyun 	.update_status  = locomolcd_set_intensity,
154*4882a593Smuzhiyun };
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
locomolcd_suspend(struct device * dev)157*4882a593Smuzhiyun static int locomolcd_suspend(struct device *dev)
158*4882a593Smuzhiyun {
159*4882a593Smuzhiyun 	locomolcd_flags |= LOCOMOLCD_SUSPENDED;
160*4882a593Smuzhiyun 	locomolcd_set_intensity(locomolcd_bl_device);
161*4882a593Smuzhiyun 	return 0;
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun 
locomolcd_resume(struct device * dev)164*4882a593Smuzhiyun static int locomolcd_resume(struct device *dev)
165*4882a593Smuzhiyun {
166*4882a593Smuzhiyun 	locomolcd_flags &= ~LOCOMOLCD_SUSPENDED;
167*4882a593Smuzhiyun 	locomolcd_set_intensity(locomolcd_bl_device);
168*4882a593Smuzhiyun 	return 0;
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun #endif
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(locomolcd_pm_ops, locomolcd_suspend, locomolcd_resume);
173*4882a593Smuzhiyun 
locomolcd_probe(struct locomo_dev * ldev)174*4882a593Smuzhiyun static int locomolcd_probe(struct locomo_dev *ldev)
175*4882a593Smuzhiyun {
176*4882a593Smuzhiyun 	struct backlight_properties props;
177*4882a593Smuzhiyun 	unsigned long flags;
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	local_irq_save(flags);
180*4882a593Smuzhiyun 	locomolcd_dev = ldev;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	locomo_gpio_set_dir(ldev->dev.parent, LOCOMO_GPIO_FL_VR, 0);
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	/*
185*4882a593Smuzhiyun 	 * the poodle_lcd_power function is called for the first time
186*4882a593Smuzhiyun 	 * from fs_initcall, which is before locomo is activated.
187*4882a593Smuzhiyun 	 * We need to recall poodle_lcd_power here
188*4882a593Smuzhiyun 	 */
189*4882a593Smuzhiyun 	if (machine_is_poodle())
190*4882a593Smuzhiyun 		locomolcd_power(1);
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	local_irq_restore(flags);
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	memset(&props, 0, sizeof(struct backlight_properties));
195*4882a593Smuzhiyun 	props.type = BACKLIGHT_RAW;
196*4882a593Smuzhiyun 	props.max_brightness = 4;
197*4882a593Smuzhiyun 	locomolcd_bl_device = backlight_device_register("locomo-bl",
198*4882a593Smuzhiyun 							&ldev->dev, NULL,
199*4882a593Smuzhiyun 							&locomobl_data, &props);
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	if (IS_ERR(locomolcd_bl_device))
202*4882a593Smuzhiyun 		return PTR_ERR(locomolcd_bl_device);
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	/* Set up frontlight so that screen is readable */
205*4882a593Smuzhiyun 	locomolcd_bl_device->props.brightness = 2;
206*4882a593Smuzhiyun 	locomolcd_set_intensity(locomolcd_bl_device);
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun 	return 0;
209*4882a593Smuzhiyun }
210*4882a593Smuzhiyun 
locomolcd_remove(struct locomo_dev * dev)211*4882a593Smuzhiyun static int locomolcd_remove(struct locomo_dev *dev)
212*4882a593Smuzhiyun {
213*4882a593Smuzhiyun 	unsigned long flags;
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun 	locomolcd_bl_device->props.brightness = 0;
216*4882a593Smuzhiyun 	locomolcd_bl_device->props.power = 0;
217*4882a593Smuzhiyun 	locomolcd_set_intensity(locomolcd_bl_device);
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	backlight_device_unregister(locomolcd_bl_device);
220*4882a593Smuzhiyun 	local_irq_save(flags);
221*4882a593Smuzhiyun 	locomolcd_dev = NULL;
222*4882a593Smuzhiyun 	local_irq_restore(flags);
223*4882a593Smuzhiyun 	return 0;
224*4882a593Smuzhiyun }
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun static struct locomo_driver poodle_lcd_driver = {
227*4882a593Smuzhiyun 	.drv = {
228*4882a593Smuzhiyun 		.name	= "locomo-backlight",
229*4882a593Smuzhiyun 		.pm	= &locomolcd_pm_ops,
230*4882a593Smuzhiyun 	},
231*4882a593Smuzhiyun 	.devid	= LOCOMO_DEVID_BACKLIGHT,
232*4882a593Smuzhiyun 	.probe	= locomolcd_probe,
233*4882a593Smuzhiyun 	.remove	= locomolcd_remove,
234*4882a593Smuzhiyun };
235*4882a593Smuzhiyun 
locomolcd_init(void)236*4882a593Smuzhiyun static int __init locomolcd_init(void)
237*4882a593Smuzhiyun {
238*4882a593Smuzhiyun 	return locomo_driver_register(&poodle_lcd_driver);
239*4882a593Smuzhiyun }
240*4882a593Smuzhiyun 
locomolcd_exit(void)241*4882a593Smuzhiyun static void __exit locomolcd_exit(void)
242*4882a593Smuzhiyun {
243*4882a593Smuzhiyun 	locomo_driver_unregister(&poodle_lcd_driver);
244*4882a593Smuzhiyun }
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun module_init(locomolcd_init);
247*4882a593Smuzhiyun module_exit(locomolcd_exit);
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>, Pavel Machek <pavel@ucw.cz>");
250*4882a593Smuzhiyun MODULE_DESCRIPTION("Collie LCD driver");
251*4882a593Smuzhiyun MODULE_LICENSE("GPL");
252