18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Backlight control code for Sharp Zaurus SL-5500
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2005 John Lenz <lenz@cs.wisc.edu>
68c2ecf20Sopenharmony_ci * Maintainer: Pavel Machek <pavel@ucw.cz> (unless John wants to :-)
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This driver assumes single CPU. That's okay, because collie is
98c2ecf20Sopenharmony_ci * slightly old hardware, and no one is going to retrofit second CPU to
108c2ecf20Sopenharmony_ci * old PDA.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/* LCD power functions */
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/delay.h>
178c2ecf20Sopenharmony_ci#include <linux/device.h>
188c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
198c2ecf20Sopenharmony_ci#include <linux/fb.h>
208c2ecf20Sopenharmony_ci#include <linux/backlight.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <asm/hardware/locomo.h>
238c2ecf20Sopenharmony_ci#include <asm/irq.h>
248c2ecf20Sopenharmony_ci#include <asm/mach/sharpsl_param.h>
258c2ecf20Sopenharmony_ci#include <asm/mach-types.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include "../../../arch/arm/mach-sa1100/generic.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic struct backlight_device *locomolcd_bl_device;
308c2ecf20Sopenharmony_cistatic struct locomo_dev *locomolcd_dev;
318c2ecf20Sopenharmony_cistatic unsigned long locomolcd_flags;
328c2ecf20Sopenharmony_ci#define LOCOMOLCD_SUSPENDED     0x01
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic void locomolcd_on(int comadj)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHA_ON, 0);
378c2ecf20Sopenharmony_ci	locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHA_ON, 1);
388c2ecf20Sopenharmony_ci	mdelay(2);
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHD_ON, 0);
418c2ecf20Sopenharmony_ci	locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHD_ON, 1);
428c2ecf20Sopenharmony_ci	mdelay(2);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	locomo_m62332_senddata(locomolcd_dev, comadj, 0);
458c2ecf20Sopenharmony_ci	mdelay(5);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VEE_ON, 0);
488c2ecf20Sopenharmony_ci	locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VEE_ON, 1);
498c2ecf20Sopenharmony_ci	mdelay(10);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	/* TFTCRST | CPSOUT=0 | CPSEN */
528c2ecf20Sopenharmony_ci	locomo_writel(0x01, locomolcd_dev->mapbase + LOCOMO_TC);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	/* Set CPSD */
558c2ecf20Sopenharmony_ci	locomo_writel(6, locomolcd_dev->mapbase + LOCOMO_CPSD);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/* TFTCRST | CPSOUT=0 | CPSEN */
588c2ecf20Sopenharmony_ci	locomo_writel((0x04 | 0x01), locomolcd_dev->mapbase + LOCOMO_TC);
598c2ecf20Sopenharmony_ci	mdelay(10);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_MOD, 0);
628c2ecf20Sopenharmony_ci	locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_MOD, 1);
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic void locomolcd_off(int comadj)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	/* TFTCRST=1 | CPSOUT=1 | CPSEN = 0 */
688c2ecf20Sopenharmony_ci	locomo_writel(0x06, locomolcd_dev->mapbase + LOCOMO_TC);
698c2ecf20Sopenharmony_ci	mdelay(1);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHA_ON, 0);
728c2ecf20Sopenharmony_ci	mdelay(110);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VEE_ON, 0);
758c2ecf20Sopenharmony_ci	mdelay(700);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	/* TFTCRST=0 | CPSOUT=0 | CPSEN = 0 */
788c2ecf20Sopenharmony_ci	locomo_writel(0, locomolcd_dev->mapbase + LOCOMO_TC);
798c2ecf20Sopenharmony_ci	locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_MOD, 0);
808c2ecf20Sopenharmony_ci	locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHD_ON, 0);
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_civoid locomolcd_power(int on)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	int comadj = sharpsl_param.comadj;
868c2ecf20Sopenharmony_ci	unsigned long flags;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	local_irq_save(flags);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if (!locomolcd_dev) {
918c2ecf20Sopenharmony_ci		local_irq_restore(flags);
928c2ecf20Sopenharmony_ci		return;
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	/* read comadj */
968c2ecf20Sopenharmony_ci	if (comadj == -1 && machine_is_collie())
978c2ecf20Sopenharmony_ci		comadj = 128;
988c2ecf20Sopenharmony_ci	if (comadj == -1 && machine_is_poodle())
998c2ecf20Sopenharmony_ci		comadj = 118;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (on)
1028c2ecf20Sopenharmony_ci		locomolcd_on(comadj);
1038c2ecf20Sopenharmony_ci	else
1048c2ecf20Sopenharmony_ci		locomolcd_off(comadj);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	local_irq_restore(flags);
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(locomolcd_power);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic int current_intensity;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic int locomolcd_set_intensity(struct backlight_device *bd)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	int intensity = backlight_get_brightness(bd);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (locomolcd_flags & LOCOMOLCD_SUSPENDED)
1178c2ecf20Sopenharmony_ci		intensity = 0;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	switch (intensity) {
1208c2ecf20Sopenharmony_ci	/*
1218c2ecf20Sopenharmony_ci	 * AC and non-AC are handled differently,
1228c2ecf20Sopenharmony_ci	 * but produce same results in sharp code?
1238c2ecf20Sopenharmony_ci	 */
1248c2ecf20Sopenharmony_ci	case 0:
1258c2ecf20Sopenharmony_ci		locomo_frontlight_set(locomolcd_dev, 0, 0, 161);
1268c2ecf20Sopenharmony_ci		break;
1278c2ecf20Sopenharmony_ci	case 1:
1288c2ecf20Sopenharmony_ci		locomo_frontlight_set(locomolcd_dev, 117, 0, 161);
1298c2ecf20Sopenharmony_ci		break;
1308c2ecf20Sopenharmony_ci	case 2:
1318c2ecf20Sopenharmony_ci		locomo_frontlight_set(locomolcd_dev, 163, 0, 148);
1328c2ecf20Sopenharmony_ci		break;
1338c2ecf20Sopenharmony_ci	case 3:
1348c2ecf20Sopenharmony_ci		locomo_frontlight_set(locomolcd_dev, 194, 0, 161);
1358c2ecf20Sopenharmony_ci		break;
1368c2ecf20Sopenharmony_ci	case 4:
1378c2ecf20Sopenharmony_ci		locomo_frontlight_set(locomolcd_dev, 194, 1, 161);
1388c2ecf20Sopenharmony_ci		break;
1398c2ecf20Sopenharmony_ci	default:
1408c2ecf20Sopenharmony_ci		return -ENODEV;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci	current_intensity = intensity;
1438c2ecf20Sopenharmony_ci	return 0;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic int locomolcd_get_intensity(struct backlight_device *bd)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	return current_intensity;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic const struct backlight_ops locomobl_data = {
1528c2ecf20Sopenharmony_ci	.get_brightness = locomolcd_get_intensity,
1538c2ecf20Sopenharmony_ci	.update_status  = locomolcd_set_intensity,
1548c2ecf20Sopenharmony_ci};
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
1578c2ecf20Sopenharmony_cistatic int locomolcd_suspend(struct device *dev)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	locomolcd_flags |= LOCOMOLCD_SUSPENDED;
1608c2ecf20Sopenharmony_ci	locomolcd_set_intensity(locomolcd_bl_device);
1618c2ecf20Sopenharmony_ci	return 0;
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic int locomolcd_resume(struct device *dev)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	locomolcd_flags &= ~LOCOMOLCD_SUSPENDED;
1678c2ecf20Sopenharmony_ci	locomolcd_set_intensity(locomolcd_bl_device);
1688c2ecf20Sopenharmony_ci	return 0;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci#endif
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(locomolcd_pm_ops, locomolcd_suspend, locomolcd_resume);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic int locomolcd_probe(struct locomo_dev *ldev)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	struct backlight_properties props;
1778c2ecf20Sopenharmony_ci	unsigned long flags;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	local_irq_save(flags);
1808c2ecf20Sopenharmony_ci	locomolcd_dev = ldev;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	locomo_gpio_set_dir(ldev->dev.parent, LOCOMO_GPIO_FL_VR, 0);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	/*
1858c2ecf20Sopenharmony_ci	 * the poodle_lcd_power function is called for the first time
1868c2ecf20Sopenharmony_ci	 * from fs_initcall, which is before locomo is activated.
1878c2ecf20Sopenharmony_ci	 * We need to recall poodle_lcd_power here
1888c2ecf20Sopenharmony_ci	 */
1898c2ecf20Sopenharmony_ci	if (machine_is_poodle())
1908c2ecf20Sopenharmony_ci		locomolcd_power(1);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	local_irq_restore(flags);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	memset(&props, 0, sizeof(struct backlight_properties));
1958c2ecf20Sopenharmony_ci	props.type = BACKLIGHT_RAW;
1968c2ecf20Sopenharmony_ci	props.max_brightness = 4;
1978c2ecf20Sopenharmony_ci	locomolcd_bl_device = backlight_device_register("locomo-bl",
1988c2ecf20Sopenharmony_ci							&ldev->dev, NULL,
1998c2ecf20Sopenharmony_ci							&locomobl_data, &props);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (IS_ERR(locomolcd_bl_device))
2028c2ecf20Sopenharmony_ci		return PTR_ERR(locomolcd_bl_device);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	/* Set up frontlight so that screen is readable */
2058c2ecf20Sopenharmony_ci	locomolcd_bl_device->props.brightness = 2;
2068c2ecf20Sopenharmony_ci	locomolcd_set_intensity(locomolcd_bl_device);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	return 0;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic int locomolcd_remove(struct locomo_dev *dev)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	unsigned long flags;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	locomolcd_bl_device->props.brightness = 0;
2168c2ecf20Sopenharmony_ci	locomolcd_bl_device->props.power = 0;
2178c2ecf20Sopenharmony_ci	locomolcd_set_intensity(locomolcd_bl_device);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	backlight_device_unregister(locomolcd_bl_device);
2208c2ecf20Sopenharmony_ci	local_irq_save(flags);
2218c2ecf20Sopenharmony_ci	locomolcd_dev = NULL;
2228c2ecf20Sopenharmony_ci	local_irq_restore(flags);
2238c2ecf20Sopenharmony_ci	return 0;
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic struct locomo_driver poodle_lcd_driver = {
2278c2ecf20Sopenharmony_ci	.drv = {
2288c2ecf20Sopenharmony_ci		.name	= "locomo-backlight",
2298c2ecf20Sopenharmony_ci		.pm	= &locomolcd_pm_ops,
2308c2ecf20Sopenharmony_ci	},
2318c2ecf20Sopenharmony_ci	.devid	= LOCOMO_DEVID_BACKLIGHT,
2328c2ecf20Sopenharmony_ci	.probe	= locomolcd_probe,
2338c2ecf20Sopenharmony_ci	.remove	= locomolcd_remove,
2348c2ecf20Sopenharmony_ci};
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic int __init locomolcd_init(void)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	return locomo_driver_register(&poodle_lcd_driver);
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic void __exit locomolcd_exit(void)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	locomo_driver_unregister(&poodle_lcd_driver);
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cimodule_init(locomolcd_init);
2478c2ecf20Sopenharmony_cimodule_exit(locomolcd_exit);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ciMODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>, Pavel Machek <pavel@ucw.cz>");
2508c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Collie LCD driver");
2518c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
252