| 1 |
/* |
| 2 |
* LCD / Backlight control code for Sharp SL-6000x (tosa) |
| 3 |
* |
| 4 |
* Copyright (c) 2005 Dirk Opfer |
| 5 |
* Copyright (c) 2007,2008 Dmitry Baryshkov |
| 6 |
* |
| 7 |
* This program is free software; you can redistribute it and/or modify |
| 8 |
* it under the terms of the GNU General Public License version 2 as |
| 9 |
* published by the Free Software Foundation. |
| 10 |
* |
| 11 |
*/ |
| 12 |
|
| 13 |
#include <linux/kernel.h> |
| 14 |
#include <linux/module.h> |
| 15 |
#include <linux/device.h> |
| 16 |
#include <linux/spi/spi.h> |
| 17 |
#include <linux/i2c.h> |
| 18 |
#include <linux/gpio.h> |
| 19 |
#include <linux/delay.h> |
| 20 |
#include <linux/lcd.h> |
| 21 |
#include <linux/fb.h> |
| 22 |
|
| 23 |
#include <asm/mach/sharpsl_param.h> |
| 24 |
|
| 25 |
#include <mach/tosa.h> |
| 26 |
|
| 27 |
#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) |
| 28 |
|
| 29 |
#define TG_REG0_VQV 0x0001 |
| 30 |
#define TG_REG0_COLOR 0x0002 |
| 31 |
#define TG_REG0_UD 0x0004 |
| 32 |
#define TG_REG0_LR 0x0008 |
| 33 |
|
| 34 |
#define DAC_BASE 0x4e |
| 35 |
|
| 36 |
struct tosa_lcd_data { |
| 37 |
struct spi_device *spi; |
| 38 |
struct lcd_device *lcd; |
| 39 |
struct i2c_client *i2c; |
| 40 |
|
| 41 |
int lcd_power; |
| 42 |
bool is_vga; |
| 43 |
}; |
| 44 |
|
| 45 |
static int tosa_tg_send(struct spi_device *spi, int adrs, uint8_t data) |
| 46 |
{ |
| 47 |
u8 buf[1]; |
| 48 |
struct spi_message msg; |
| 49 |
struct spi_transfer xfer = { |
| 50 |
.len = 1, |
| 51 |
.cs_change = 1, |
| 52 |
.tx_buf = buf, |
| 53 |
}; |
| 54 |
|
| 55 |
buf[0] = ((adrs & 0x07) << 5) | (data & 0x1f); |
| 56 |
spi_message_init(&msg); |
| 57 |
spi_message_add_tail(&xfer, &msg); |
| 58 |
|
| 59 |
return spi_sync(spi, &msg); |
| 60 |
} |
| 61 |
|
| 62 |
int tosa_bl_enable(struct spi_device *spi, int enable) |
| 63 |
{ |
| 64 |
/* bl_enable GP04=1 otherwise GP04=0*/ |
| 65 |
return tosa_tg_send(spi, TG_GPODR2, enable? 0x01 : 0x00); |
| 66 |
} |
| 67 |
EXPORT_SYMBOL(tosa_bl_enable); |
| 68 |
|
| 69 |
static void tosa_lcd_tg_init(struct tosa_lcd_data *data) |
| 70 |
{ |
| 71 |
/* TG on */ |
| 72 |
gpio_set_value(TOSA_GPIO_TG_ON, 0); |
| 73 |
|
| 74 |
mdelay(60); |
| 75 |
|
| 76 |
/* delayed 0clk TCTL signal for VGA */ |
| 77 |
tosa_tg_send(data->spi, TG_TPOSCTL, 0x00); |
| 78 |
/* GPOS0=powercontrol, GPOS1=GPIO, GPOS2=TCTL */ |
| 79 |
tosa_tg_send(data->spi, TG_GPOSR, 0x02); |
| 80 |
} |
| 81 |
|
| 82 |
static void tosa_lcd_tg_on(struct tosa_lcd_data *data) |
| 83 |
{ |
| 84 |
struct spi_device *spi = data->spi; |
| 85 |
int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR; |
| 86 |
|
| 87 |
if (data->is_vga) |
| 88 |
value |= TG_REG0_VQV; |
| 89 |
|
| 90 |
tosa_tg_send(spi, TG_PNLCTL, value); |
| 91 |
|
| 92 |
/* TG LCD pannel power up */ |
| 93 |
tosa_tg_send(spi, TG_PINICTL,0x4); |
| 94 |
mdelay(50); |
| 95 |
|
| 96 |
/* TG LCD GVSS */ |
| 97 |
tosa_tg_send(spi, TG_PINICTL,0x0); |
| 98 |
|
| 99 |
if (!data->i2c) { |
| 100 |
/* after the pannel is powered up the first time, we can access the i2c bus */ |
| 101 |
/* so probe for the DAC */ |
| 102 |
struct i2c_adapter *adap = i2c_get_adapter(0); |
| 103 |
struct i2c_board_info info = { |
| 104 |
.type = "tosa-bl", |
| 105 |
.addr = DAC_BASE, |
| 106 |
.platform_data = data->spi, |
| 107 |
}; |
| 108 |
data->i2c = i2c_new_device(adap, &info); |
| 109 |
} |
| 110 |
} |
| 111 |
|
| 112 |
static void tosa_lcd_tg_off(struct tosa_lcd_data *data) |
| 113 |
{ |
| 114 |
struct spi_device *spi = data->spi; |
| 115 |
|
| 116 |
/* TG LCD VHSA off */ |
| 117 |
tosa_tg_send(spi, TG_PINICTL,0x4); |
| 118 |
mdelay(50); |
| 119 |
|
| 120 |
/* TG LCD signal off */ |
| 121 |
tosa_tg_send(spi, TG_PINICTL,0x6); |
| 122 |
mdelay(50); |
| 123 |
|
| 124 |
/* TG Off */ |
| 125 |
gpio_set_value(TOSA_GPIO_TG_ON, 1); |
| 126 |
mdelay(100); |
| 127 |
} |
| 128 |
|
| 129 |
int tosa_lcd_set_power(struct lcd_device *lcd, int power) |
| 130 |
{ |
| 131 |
struct tosa_lcd_data *data = lcd_get_data(lcd); |
| 132 |
|
| 133 |
if (POWER_IS_ON(power) && !POWER_IS_ON(data->lcd_power)) |
| 134 |
tosa_lcd_tg_on(data); |
| 135 |
|
| 136 |
if (!POWER_IS_ON(power) && POWER_IS_ON(data->lcd_power)) |
| 137 |
tosa_lcd_tg_off(data); |
| 138 |
|
| 139 |
data->lcd_power = power; |
| 140 |
return 0; |
| 141 |
} |
| 142 |
|
| 143 |
static int tosa_lcd_get_power(struct lcd_device *lcd) |
| 144 |
{ |
| 145 |
struct tosa_lcd_data *data = lcd_get_data(lcd); |
| 146 |
|
| 147 |
return data->lcd_power; |
| 148 |
} |
| 149 |
|
| 150 |
static int tosa_lcd_set_mode(struct lcd_device *lcd, struct fb_videomode *mode) |
| 151 |
{ |
| 152 |
struct tosa_lcd_data *data = lcd_get_data(lcd); |
| 153 |
|
| 154 |
if (mode->xres == 320 || mode->yres == 320) |
| 155 |
data->is_vga = false; |
| 156 |
else |
| 157 |
data->is_vga = true; |
| 158 |
|
| 159 |
if (POWER_IS_ON(data->lcd_power)) |
| 160 |
tosa_lcd_tg_on(data); |
| 161 |
|
| 162 |
return 0; |
| 163 |
} |
| 164 |
|
| 165 |
static struct lcd_ops tosa_lcd_ops = { |
| 166 |
.set_power = tosa_lcd_set_power, |
| 167 |
.get_power = tosa_lcd_get_power, |
| 168 |
.set_mode = tosa_lcd_set_mode, |
| 169 |
}; |
| 170 |
|
| 171 |
static int __devinit tosa_lcd_probe(struct spi_device *spi) |
| 172 |
{ |
| 173 |
int ret; |
| 174 |
struct tosa_lcd_data *data; |
| 175 |
|
| 176 |
data = kzalloc(sizeof(struct tosa_lcd_data), GFP_KERNEL); |
| 177 |
if (!data) |
| 178 |
return -ENOMEM; |
| 179 |
|
| 180 |
data->is_vga = true; /* defaut to VGA mode */ |
| 181 |
|
| 182 |
/* |
| 183 |
* bits_per_word cannot be configured in platform data |
| 184 |
*/ |
| 185 |
spi->bits_per_word = 8; |
| 186 |
|
| 187 |
ret = spi_setup(spi); |
| 188 |
if (ret < 0) |
| 189 |
goto err_spi; |
| 190 |
|
| 191 |
data->spi = spi; |
| 192 |
dev_set_drvdata(&spi->dev, data); |
| 193 |
|
| 194 |
ret = gpio_request(TOSA_GPIO_TG_ON, "tg #pwr"); |
| 195 |
if (ret < 0) |
| 196 |
goto err_gpio_tg; |
| 197 |
|
| 198 |
mdelay(60); |
| 199 |
|
| 200 |
ret = gpio_direction_output(TOSA_GPIO_TG_ON, 0); |
| 201 |
if (ret < 0) |
| 202 |
goto err_gpio_dir; |
| 203 |
|
| 204 |
mdelay(60); |
| 205 |
tosa_lcd_tg_init(data); |
| 206 |
|
| 207 |
tosa_lcd_tg_on(data); |
| 208 |
|
| 209 |
data->lcd = lcd_device_register("tosa-lcd", &spi->dev, data, |
| 210 |
&tosa_lcd_ops); |
| 211 |
|
| 212 |
if (IS_ERR(data->lcd)) { |
| 213 |
ret = PTR_ERR(data->lcd); |
| 214 |
data->lcd = NULL; |
| 215 |
goto err_register; |
| 216 |
} |
| 217 |
|
| 218 |
return 0; |
| 219 |
|
| 220 |
err_register: |
| 221 |
tosa_lcd_tg_off(data); |
| 222 |
err_gpio_dir: |
| 223 |
gpio_free(TOSA_GPIO_TG_ON); |
| 224 |
err_gpio_tg: |
| 225 |
dev_set_drvdata(&spi->dev, NULL); |
| 226 |
err_spi: |
| 227 |
kfree(data); |
| 228 |
return ret; |
| 229 |
} |
| 230 |
|
| 231 |
static int __devexit tosa_lcd_remove(struct spi_device *spi) |
| 232 |
{ |
| 233 |
struct tosa_lcd_data *data = dev_get_drvdata(&spi->dev); |
| 234 |
|
| 235 |
lcd_device_unregister(data->lcd); |
| 236 |
|
| 237 |
if (data->i2c) |
| 238 |
i2c_unregister_device(data->i2c); |
| 239 |
|
| 240 |
tosa_lcd_tg_off(data); |
| 241 |
|
| 242 |
gpio_free(TOSA_GPIO_TG_ON); |
| 243 |
dev_set_drvdata(&spi->dev, NULL); |
| 244 |
kfree(data); |
| 245 |
|
| 246 |
return 0; |
| 247 |
} |
| 248 |
|
| 249 |
#ifdef CONFIG_PM |
| 250 |
static int tosa_lcd_suspend(struct spi_device *spi, pm_message_t state) |
| 251 |
{ |
| 252 |
struct tosa_lcd_data *data = dev_get_drvdata(&spi->dev); |
| 253 |
|
| 254 |
tosa_lcd_tg_off(data); |
| 255 |
|
| 256 |
return 0; |
| 257 |
} |
| 258 |
|
| 259 |
static int tosa_lcd_resume(struct spi_device *spi) |
| 260 |
{ |
| 261 |
struct tosa_lcd_data *data = dev_get_drvdata(&spi->dev); |
| 262 |
|
| 263 |
tosa_lcd_tg_init(data); |
| 264 |
if (POWER_IS_ON(data->lcd_power)) |
| 265 |
tosa_lcd_tg_on(data); |
| 266 |
else |
| 267 |
tosa_lcd_tg_off(data); |
| 268 |
|
| 269 |
return 0; |
| 270 |
} |
| 271 |
#else |
| 272 |
#define tosa_lcd_suspend NULL |
| 273 |
#define tosa_lcd_reume NULL |
| 274 |
#endif |
| 275 |
|
| 276 |
static struct spi_driver tosa_lcd_driver = { |
| 277 |
.driver = { |
| 278 |
.name = "tosa-lcd", |
| 279 |
.owner = THIS_MODULE, |
| 280 |
}, |
| 281 |
.probe = tosa_lcd_probe, |
| 282 |
.remove = __devexit_p(tosa_lcd_remove), |
| 283 |
.suspend = tosa_lcd_suspend, |
| 284 |
.resume = tosa_lcd_resume, |
| 285 |
}; |
| 286 |
|
| 287 |
static int __init tosa_lcd_init(void) |
| 288 |
{ |
| 289 |
return spi_register_driver(&tosa_lcd_driver); |
| 290 |
} |
| 291 |
|
| 292 |
static void __exit tosa_lcd_exit(void) |
| 293 |
{ |
| 294 |
spi_unregister_driver(&tosa_lcd_driver); |
| 295 |
} |
| 296 |
|
| 297 |
module_init(tosa_lcd_init); |
| 298 |
module_exit(tosa_lcd_exit); |
| 299 |
|
| 300 |
MODULE_AUTHOR("Dmitry Baryshkov"); |
| 301 |
MODULE_LICENSE("GPL v2"); |
| 302 |
MODULE_DESCRIPTION("LCD/Backlight control for Sharp SL-6000 PDA"); |
| 303 |
MODULE_ALIAS("spi:tosa-lcd"); |